duke@1: /* ohair@158: * Copyright (c) 1998, 2009, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@158: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@158: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@158: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@158: * or visit www.oracle.com if you need additional information or have any ohair@158: * questions. duke@1: */ duke@1: /* duke@1: * Licensed Materials - Property of IBM duke@1: * RMI-IIOP v1.0 duke@1: * Copyright IBM Corp. 1998 1999 All Rights Reserved duke@1: * duke@1: */ duke@1: duke@1: package com.sun.corba.se.impl.io; duke@1: duke@1: import java.security.MessageDigest; duke@1: import java.security.NoSuchAlgorithmException; duke@1: import java.security.DigestOutputStream; duke@1: import java.security.AccessController; duke@1: import java.security.PrivilegedExceptionAction; duke@1: import java.security.PrivilegedActionException; duke@1: import java.security.PrivilegedAction; duke@1: duke@1: import java.lang.reflect.Modifier; duke@1: import java.lang.reflect.Array; duke@1: import java.lang.reflect.Field; duke@1: import java.lang.reflect.Member; duke@1: import java.lang.reflect.Method; duke@1: import java.lang.reflect.Constructor; duke@1: import java.lang.reflect.Proxy; duke@1: import java.lang.reflect.InvocationTargetException; duke@1: duke@1: import java.io.IOException; duke@1: import java.io.DataOutputStream; duke@1: import java.io.ByteArrayOutputStream; duke@1: import java.io.InvalidClassException; duke@1: import java.io.Serializable; duke@1: duke@1: import java.util.Arrays; duke@1: import java.util.Comparator; duke@1: import java.util.Hashtable; duke@1: duke@1: import com.sun.corba.se.impl.util.RepositoryId; duke@1: duke@1: import org.omg.CORBA.ValueMember; duke@1: duke@1: import sun.corba.Bridge; duke@1: duke@1: /** duke@1: * A ObjectStreamClass describes a class that can be serialized to a stream duke@1: * or a class that was serialized to a stream. It contains the name duke@1: * and the serialVersionUID of the class. duke@1: *
duke@1: * The ObjectStreamClass for a specific class loaded in this Java VM can duke@1: * be found using the lookup method. duke@1: * duke@1: * @author Roger Riggs duke@1: * @since JDK1.1 duke@1: */ duke@1: public class ObjectStreamClass implements java.io.Serializable { duke@1: private static final boolean DEBUG_SVUID = false ; duke@1: duke@1: public static final long kDefaultUID = -1; duke@1: duke@1: private static Object noArgsList[] = {}; duke@1: private static Class noTypesList[] = {}; duke@1: duke@1: private static Hashtable translatedFields; duke@1: miroslawzn@235: /** true if represents enum type */ miroslawzn@235: private boolean isEnum; miroslawzn@235: duke@1: private static final Bridge bridge = duke@1: (Bridge)AccessController.doPrivileged( duke@1: new PrivilegedAction() { duke@1: public Object run() { duke@1: return Bridge.get() ; duke@1: } duke@1: } duke@1: ) ; duke@1: duke@1: /** Find the descriptor for a class that can be serialized. Null duke@1: * is returned if the specified class does not implement duke@1: * java.io.Serializable or java.io.Externalizable. duke@1: */ duke@1: static final ObjectStreamClass lookup(Class cl) duke@1: { duke@1: ObjectStreamClass desc = lookupInternal(cl); duke@1: if (desc.isSerializable() || desc.isExternalizable()) duke@1: return desc; duke@1: return null; duke@1: } duke@1: duke@1: /* duke@1: * Find the class descriptor for the specified class. duke@1: * Package access only so it can be called from ObjectIn/OutStream. duke@1: */ duke@1: static ObjectStreamClass lookupInternal(Class cl) duke@1: { duke@1: /* Synchronize on the hashtable so no two threads will do duke@1: * this at the same time. duke@1: */ duke@1: ObjectStreamClass desc = null; duke@1: synchronized (descriptorFor) { duke@1: /* Find the matching descriptor if it already known */ duke@1: desc = findDescriptorFor(cl); duke@1: if (desc == null) { duke@1: /* Check if it's serializable */ duke@1: boolean serializable = classSerializable.isAssignableFrom(cl); duke@1: duke@1: /* If the class is only Serializable, duke@1: * lookup the descriptor for the superclass. duke@1: */ duke@1: ObjectStreamClass superdesc = null; duke@1: if (serializable) { duke@1: Class superclass = cl.getSuperclass(); duke@1: if (superclass != null) duke@1: superdesc = lookup(superclass); duke@1: } duke@1: duke@1: /* Check if its' externalizable. duke@1: * If it's Externalizable, clear the serializable flag. duke@1: * Only one or the other may be set in the protocol. duke@1: */ duke@1: boolean externalizable = false; duke@1: if (serializable) { duke@1: externalizable = duke@1: ((superdesc != null) && superdesc.isExternalizable()) || duke@1: classExternalizable.isAssignableFrom(cl); duke@1: if (externalizable) { duke@1: serializable = false; duke@1: } duke@1: } duke@1: duke@1: /* Create a new version descriptor, duke@1: * it put itself in the known table. duke@1: */ duke@1: desc = new ObjectStreamClass(cl, superdesc, duke@1: serializable, externalizable); duke@1: } tbell@68: // Must always call init. See bug 4488137. This code was tbell@68: // incorrectly changed to return immediately on a non-null tbell@68: // cache result. That allowed threads to gain access to tbell@68: // unintialized instances. tbell@68: // tbell@68: // History: Note, the following init() call was originally within tbell@68: // the synchronization block, as it currently is now. Later, the tbell@68: // init() call was moved outside the synchronization block, and tbell@68: // the init() method used a private member variable lock, to tbell@68: // avoid performance problems. See bug 4165204. But that lead to tbell@68: // a deadlock situation, see bug 5104239. Hence, the init() method tbell@68: // has now been moved back into the synchronization block. The tbell@68: // right approach to solving these problems would be to rewrite tbell@68: // this class, based on the latest java.io.ObjectStreamClass. tbell@68: desc.init(); duke@1: } duke@1: return desc; duke@1: } duke@1: duke@1: /** duke@1: * The name of the class described by this descriptor. duke@1: */ duke@1: public final String getName() { duke@1: return name; duke@1: } duke@1: duke@1: /** duke@1: * Return the serialVersionUID for this class. duke@1: * The serialVersionUID defines a set of classes all with the same name duke@1: * that have evolved from a common root class and agree to be serialized duke@1: * and deserialized using a common format. duke@1: */ duke@1: public static final long getSerialVersionUID( java.lang.Class clazz) { duke@1: ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz ); duke@1: if( theosc != null ) duke@1: { duke@1: return theosc.getSerialVersionUID( ); duke@1: } duke@1: return 0; duke@1: } duke@1: duke@1: /** duke@1: * Return the serialVersionUID for this class. duke@1: * The serialVersionUID defines a set of classes all with the same name duke@1: * that have evolved from a common root class and agree to be serialized duke@1: * and deserialized using a common format. duke@1: */ duke@1: public final long getSerialVersionUID() { duke@1: return suid; duke@1: } duke@1: duke@1: /** duke@1: * Return the serialVersionUID string for this class. duke@1: * The serialVersionUID defines a set of classes all with the same name duke@1: * that have evolved from a common root class and agree to be serialized duke@1: * and deserialized using a common format. duke@1: */ duke@1: public final String getSerialVersionUIDStr() { duke@1: if (suidStr == null) duke@1: suidStr = Long.toHexString(suid).toUpperCase(); duke@1: return suidStr; duke@1: } duke@1: duke@1: /** duke@1: * Return the actual (computed) serialVersionUID for this class. duke@1: */ duke@1: public static final long getActualSerialVersionUID( java.lang.Class clazz ) duke@1: { duke@1: ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz ); duke@1: if( theosc != null ) duke@1: { duke@1: return theosc.getActualSerialVersionUID( ); duke@1: } duke@1: return 0; duke@1: } duke@1: duke@1: /** duke@1: * Return the actual (computed) serialVersionUID for this class. duke@1: */ duke@1: public final long getActualSerialVersionUID() { duke@1: return actualSuid; duke@1: } duke@1: duke@1: /** duke@1: * Return the actual (computed) serialVersionUID for this class. duke@1: */ duke@1: public final String getActualSerialVersionUIDStr() { duke@1: if (actualSuidStr == null) duke@1: actualSuidStr = Long.toHexString(actualSuid).toUpperCase(); duke@1: return actualSuidStr; duke@1: } duke@1: duke@1: /** duke@1: * Return the class in the local VM that this version is mapped to. duke@1: * Null is returned if there is no corresponding local class. duke@1: */ duke@1: public final Class forClass() { duke@1: return ofClass; duke@1: } duke@1: duke@1: /** duke@1: * Return an array of the fields of this serializable class. duke@1: * @return an array containing an element for each persistent duke@1: * field of this class. Returns an array of length zero if duke@1: * there are no fields. duke@1: * @since JDK1.2 duke@1: */ duke@1: public ObjectStreamField[] getFields() { duke@1: // Return a copy so the caller can't change the fields. duke@1: if (fields.length > 0) { duke@1: ObjectStreamField[] dup = new ObjectStreamField[fields.length]; duke@1: System.arraycopy(fields, 0, dup, 0, fields.length); duke@1: return dup; duke@1: } else { duke@1: return fields; duke@1: } duke@1: } duke@1: duke@1: public boolean hasField(ValueMember field) duke@1: { duke@1: try { duke@1: for (int i = 0; i < fields.length; i++) { duke@1: if (fields[i].getName().equals(field.name)) { duke@1: if (fields[i].getSignature().equals( duke@1: ValueUtility.getSignature(field))) duke@1: return true; duke@1: } duke@1: } duke@1: } catch (Exception exc) { duke@1: // Ignore this; all we want to do is return false duke@1: // Note that ValueUtility.getSignature can throw checked exceptions. duke@1: } duke@1: duke@1: return false; duke@1: } duke@1: duke@1: /* Avoid unnecessary allocations. */ duke@1: final ObjectStreamField[] getFieldsNoCopy() { duke@1: return fields; duke@1: } duke@1: duke@1: /** duke@1: * Get the field of this class by name. duke@1: * @return The ObjectStreamField object of the named field or null if there duke@1: * is no such named field. duke@1: */ duke@1: public final ObjectStreamField getField(String name) { duke@1: /* Binary search of fields by name. duke@1: */ duke@1: for (int i = fields.length-1; i >= 0; i--) { duke@1: if (name.equals(fields[i].getName())) { duke@1: return fields[i]; duke@1: } duke@1: } duke@1: return null; duke@1: } duke@1: duke@1: public Serializable writeReplace(Serializable value) { duke@1: if (writeReplaceObjectMethod != null) { duke@1: try { duke@1: return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList); duke@1: } catch(Throwable t) { duke@1: throw new RuntimeException(t); duke@1: } duke@1: } duke@1: else return value; duke@1: } duke@1: duke@1: public Object readResolve(Object value) { duke@1: if (readResolveObjectMethod != null) { duke@1: try { duke@1: return readResolveObjectMethod.invoke(value,noArgsList); duke@1: } catch(Throwable t) { duke@1: throw new RuntimeException(t); duke@1: } duke@1: } duke@1: else return value; duke@1: } duke@1: duke@1: /** duke@1: * Return a string describing this ObjectStreamClass. duke@1: */ duke@1: public final String toString() { duke@1: StringBuffer sb = new StringBuffer(); duke@1: duke@1: sb.append(name); duke@1: sb.append(": static final long serialVersionUID = "); duke@1: sb.append(Long.toString(suid)); duke@1: sb.append("L;"); duke@1: return sb.toString(); duke@1: } duke@1: duke@1: /* duke@1: * Create a new ObjectStreamClass from a loaded class. duke@1: * Don't call this directly, call lookup instead. duke@1: */ duke@1: private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc, duke@1: boolean serial, boolean extern) duke@1: { duke@1: ofClass = cl; /* created from this class */ duke@1: duke@1: if (Proxy.isProxyClass(cl)) { duke@1: forProxyClass = true; duke@1: } duke@1: duke@1: name = cl.getName(); miroslawzn@235: isEnum = Enum.class.isAssignableFrom(cl); duke@1: superclass = superdesc; duke@1: serializable = serial; duke@1: if (!forProxyClass) { duke@1: // proxy classes are never externalizable duke@1: externalizable = extern; duke@1: } duke@1: duke@1: /* duke@1: * Enter this class in the table of known descriptors. duke@1: * Otherwise, when the fields are read it may recurse duke@1: * trying to find the descriptor for itself. duke@1: */ duke@1: insertDescriptorFor(this); duke@1: duke@1: /* duke@1: * The remainder of initialization occurs in init(), which is called duke@1: * after the lock on the global class descriptor table has been duke@1: * released. duke@1: */ duke@1: } duke@1: duke@1: /* duke@1: * Initialize class descriptor. This method is only invoked on class duke@1: * descriptors created via calls to lookupInternal(). This method is kept duke@1: * separate from the ObjectStreamClass constructor so that lookupInternal duke@1: * does not have to hold onto a global class descriptor table lock while the duke@1: * class descriptor is being initialized (see bug 4165204). duke@1: */ duke@1: duke@1: duke@1: private void init() { duke@1: synchronized (lock) { duke@1: duke@1: // See description at definition of initialized. duke@1: if (initialized) duke@1: return; duke@1: duke@1: final Class cl = ofClass; duke@1: duke@1: if (!serializable || duke@1: externalizable || duke@1: forProxyClass || miroslawzn@235: name.equals("java.lang.String") || miroslawzn@235: isEnum) { duke@1: fields = NO_FIELDS; duke@1: } else if (serializable) { duke@1: /* Ask for permission to override field access checks. duke@1: */ duke@1: AccessController.doPrivileged(new PrivilegedAction() { duke@1: public Object run() { duke@1: /* Fill in the list of persistent fields. duke@1: * If it is declared, use the declared serialPersistentFields. duke@1: * Otherwise, extract the fields from the class itself. duke@1: */ duke@1: try { duke@1: Field pf = cl.getDeclaredField("serialPersistentFields"); duke@1: // serial bug 7; the serialPersistentFields were not duke@1: // being read and stored as Accessible bit was not set duke@1: pf.setAccessible(true); duke@1: // serial bug 7; need to find if the field is of type duke@1: // java.io.ObjectStreamField duke@1: java.io.ObjectStreamField[] f = duke@1: (java.io.ObjectStreamField[])pf.get(cl); duke@1: int mods = pf.getModifiers(); duke@1: if ((Modifier.isPrivate(mods)) && duke@1: (Modifier.isStatic(mods)) && duke@1: (Modifier.isFinal(mods))) duke@1: { duke@1: fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl)); duke@1: } duke@1: } catch (NoSuchFieldException e) { duke@1: fields = null; duke@1: } catch (IllegalAccessException e) { duke@1: fields = null; duke@1: } catch (IllegalArgumentException e) { duke@1: fields = null; duke@1: } catch (ClassCastException e) { duke@1: /* Thrown if a field serialPersistentField exists duke@1: * but it is not of type ObjectStreamField. duke@1: */ duke@1: fields = null; duke@1: } duke@1: duke@1: duke@1: if (fields == null) { duke@1: /* Get all of the declared fields for this duke@1: * Class. setAccessible on all fields so they duke@1: * can be accessed later. Create a temporary duke@1: * ObjectStreamField array to hold each duke@1: * non-static, non-transient field. Then copy the duke@1: * temporary array into an array of the correct duke@1: * size once the number of fields is known. duke@1: */ duke@1: Field[] actualfields = cl.getDeclaredFields(); duke@1: duke@1: int numFields = 0; duke@1: ObjectStreamField[] tempFields = duke@1: new ObjectStreamField[actualfields.length]; duke@1: for (int i = 0; i < actualfields.length; i++) { duke@1: Field fld = actualfields[i] ; duke@1: int modifiers = fld.getModifiers(); duke@1: if (!Modifier.isStatic(modifiers) && duke@1: !Modifier.isTransient(modifiers)) { duke@1: fld.setAccessible(true) ; duke@1: tempFields[numFields++] = new ObjectStreamField(fld); duke@1: } duke@1: } duke@1: duke@1: fields = new ObjectStreamField[numFields]; duke@1: System.arraycopy(tempFields, 0, fields, 0, numFields); duke@1: duke@1: } else { duke@1: // For each declared persistent field, look for an actual duke@1: // reflected Field. If there is one, make sure it's the correct duke@1: // type and cache it in the ObjectStreamClass for that field. duke@1: for (int j = fields.length-1; j >= 0; j--) { duke@1: try { duke@1: Field reflField = cl.getDeclaredField(fields[j].getName()); duke@1: if (fields[j].getType() == reflField.getType()) { duke@1: reflField.setAccessible(true); duke@1: fields[j].setField(reflField); duke@1: } duke@1: } catch (NoSuchFieldException e) { duke@1: // Nothing to do duke@1: } duke@1: } duke@1: } duke@1: return null; duke@1: } duke@1: }); duke@1: duke@1: if (fields.length > 1) duke@1: Arrays.sort(fields); duke@1: duke@1: /* Set up field data for use while writing using the API api. */ duke@1: computeFieldInfo(); duke@1: } duke@1: duke@1: /* Get the serialVersionUID from the class. duke@1: * It uses the access override mechanism so make sure duke@1: * the field objects is only used here. duke@1: * duke@1: * NonSerializable classes have a serialVerisonUID of 0L. duke@1: */ miroslawzn@235: if (isNonSerializable() || isEnum) { duke@1: suid = 0L; duke@1: } else { duke@1: // Lookup special Serializable members using reflection. duke@1: AccessController.doPrivileged(new PrivilegedAction() { duke@1: public Object run() { duke@1: if (forProxyClass) { duke@1: // proxy classes always have serialVersionUID of 0L duke@1: suid = 0L; duke@1: } else { duke@1: try { duke@1: final Field f = cl.getDeclaredField("serialVersionUID"); duke@1: int mods = f.getModifiers(); duke@1: // SerialBug 5: static final SUID should be read duke@1: if (Modifier.isStatic(mods) && Modifier.isFinal(mods) ) { duke@1: f.setAccessible(true); duke@1: suid = f.getLong(cl); duke@1: // SerialBug 2: should be computed after writeObject duke@1: // actualSuid = computeStructuralUID(cl); duke@1: } else { duke@1: suid = _computeSerialVersionUID(cl); duke@1: // SerialBug 2: should be computed after writeObject duke@1: // actualSuid = computeStructuralUID(cl); duke@1: } duke@1: } catch (NoSuchFieldException ex) { duke@1: suid = _computeSerialVersionUID(cl); duke@1: // SerialBug 2: should be computed after writeObject duke@1: // actualSuid = computeStructuralUID(cl); duke@1: } catch (IllegalAccessException ex) { duke@1: suid = _computeSerialVersionUID(cl); duke@1: } duke@1: } duke@1: duke@1: writeReplaceObjectMethod = ObjectStreamClass.getInheritableMethod(cl, duke@1: "writeReplace", noTypesList, Object.class); duke@1: duke@1: readResolveObjectMethod = ObjectStreamClass.getInheritableMethod(cl, duke@1: "readResolve", noTypesList, Object.class); duke@1: duke@1: if (externalizable) duke@1: cons = getExternalizableConstructor(cl) ; duke@1: else duke@1: cons = getSerializableConstructor(cl) ; duke@1: duke@1: if (serializable && !forProxyClass) { duke@1: /* Look for the writeObject method duke@1: * Set the accessible flag on it here. ObjectOutputStream duke@1: * will call it as necessary. duke@1: */ duke@1: writeObjectMethod = getPrivateMethod( cl, "writeObject", duke@1: new Class[] { java.io.ObjectOutputStream.class }, Void.TYPE ) ; duke@1: readObjectMethod = getPrivateMethod( cl, "readObject", duke@1: new Class[] { java.io.ObjectInputStream.class }, Void.TYPE ) ; duke@1: } duke@1: return null; duke@1: } duke@1: }); duke@1: } duke@1: duke@1: // This call depends on a lot of information computed above! duke@1: actualSuid = ObjectStreamClass.computeStructuralUID(this, cl); duke@1: duke@1: // If we have a write object method, precompute the duke@1: // RMI-IIOP stream format version 2 optional data duke@1: // repository ID. duke@1: if (hasWriteObject()) duke@1: rmiiiopOptionalDataRepId = computeRMIIIOPOptionalDataRepId(); duke@1: duke@1: // This must be done last. duke@1: initialized = true; duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Returns non-static private method with given signature defined by given duke@1: * class, or null if none found. Access checks are disabled on the duke@1: * returned method (if any). duke@1: */ duke@1: private static Method getPrivateMethod(Class cl, String name, duke@1: Class[] argTypes, duke@1: Class returnType) duke@1: { duke@1: try { duke@1: Method meth = cl.getDeclaredMethod(name, argTypes); duke@1: meth.setAccessible(true); duke@1: int mods = meth.getModifiers(); duke@1: return ((meth.getReturnType() == returnType) && duke@1: ((mods & Modifier.STATIC) == 0) && duke@1: ((mods & Modifier.PRIVATE) != 0)) ? meth : null; duke@1: } catch (NoSuchMethodException ex) { duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: // Specific to RMI-IIOP duke@1: /** duke@1: * Java to IDL ptc-02-01-12 1.5.1 duke@1: * duke@1: * "The rep_id string passed to the start_value method must be duke@1: * 'RMI:org.omg.custom.class:hashcode:suid' where class is the duke@1: * fully-qualified name of the class whose writeObject method duke@1: * is being invoked and hashcode and suid are the class's hashcode duke@1: * and SUID." duke@1: */ duke@1: private String computeRMIIIOPOptionalDataRepId() { duke@1: duke@1: StringBuffer sbuf = new StringBuffer("RMI:org.omg.custom."); duke@1: sbuf.append(RepositoryId.convertToISOLatin1(this.getName())); duke@1: sbuf.append(':'); duke@1: sbuf.append(this.getActualSerialVersionUIDStr()); duke@1: sbuf.append(':'); duke@1: sbuf.append(this.getSerialVersionUIDStr()); duke@1: duke@1: return sbuf.toString(); duke@1: } duke@1: duke@1: /** duke@1: * This will return null if there is no writeObject method. duke@1: */ duke@1: public final String getRMIIIOPOptionalDataRepId() { duke@1: return rmiiiopOptionalDataRepId; duke@1: } duke@1: duke@1: /* duke@1: * Create an empty ObjectStreamClass for a class about to be read. duke@1: * This is separate from read so ObjectInputStream can assign the duke@1: * wire handle early, before any nested ObjectStreamClass might duke@1: * be read. duke@1: */ duke@1: ObjectStreamClass(String n, long s) { duke@1: name = n; duke@1: suid = s; duke@1: superclass = null; duke@1: } duke@1: duke@1: private static Object[] translateFields(Object objs[]) duke@1: throws NoSuchFieldException { duke@1: try{ duke@1: java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs; duke@1: Object translation[] = null; duke@1: duke@1: if (translatedFields == null) duke@1: translatedFields = new Hashtable(); duke@1: duke@1: translation = (Object[])translatedFields.get(fields); duke@1: duke@1: if (translation != null) duke@1: return translation; duke@1: else { duke@1: Class osfClass = Class.forName("com.sun.corba.se.impl.io.ObjectStreamField"); duke@1: translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length); duke@1: Object arg[] = new Object[2]; duke@1: Class types[] = {String.class, Class.class}; duke@1: Constructor constructor = osfClass.getDeclaredConstructor(types); duke@1: for (int i = fields.length -1; i >= 0; i--){ duke@1: arg[0] = fields[i].getName(); duke@1: arg[1] = fields[i].getType(); duke@1: duke@1: translation[i] = constructor.newInstance(arg); duke@1: } duke@1: translatedFields.put(fields, translation); duke@1: duke@1: } duke@1: duke@1: return (Object[])translation; duke@1: } duke@1: catch(Throwable t){ duke@1: NoSuchFieldException nsfe = new NoSuchFieldException(); duke@1: nsfe.initCause( t ) ; duke@1: throw nsfe ; duke@1: } duke@1: } duke@1: duke@1: /* duke@1: * Set the class this version descriptor matches. duke@1: * The base class name and serializable hash must match. duke@1: * Fill in the reflected Fields that will be used duke@1: * for reading. duke@1: */ duke@1: final void setClass(Class cl) throws InvalidClassException { duke@1: duke@1: if (cl == null) { duke@1: localClassDesc = null; duke@1: ofClass = null; duke@1: computeFieldInfo(); duke@1: return; duke@1: } duke@1: duke@1: localClassDesc = lookupInternal(cl); duke@1: if (localClassDesc == null) duke@1: // XXX I18N, logging needed duke@1: throw new InvalidClassException(cl.getName(), duke@1: "Local class not compatible"); duke@1: if (suid != localClassDesc.suid) { duke@1: duke@1: /* Check for exceptional cases that allow mismatched suid. */ duke@1: duke@1: /* Allow adding Serializable or Externalizable duke@1: * to a later release of the class. duke@1: */ duke@1: boolean addedSerialOrExtern = duke@1: isNonSerializable() || localClassDesc.isNonSerializable(); duke@1: duke@1: /* Disregard the serialVersionUID of an array duke@1: * when name and cl.Name differ. If resolveClass() returns duke@1: * an array with a different package name, duke@1: * the serialVersionUIDs will not match since the fully duke@1: * qualified array class is used in the duke@1: * computation of the array's serialVersionUID. There is duke@1: * no way to set a permanent serialVersionUID for an array type. duke@1: */ duke@1: duke@1: boolean arraySUID = (cl.isArray() && ! cl.getName().equals(name)); duke@1: duke@1: if (! arraySUID && ! addedSerialOrExtern ) { duke@1: // XXX I18N, logging needed duke@1: throw new InvalidClassException(cl.getName(), duke@1: "Local class not compatible:" + duke@1: " stream classdesc serialVersionUID=" + suid + duke@1: " local class serialVersionUID=" + localClassDesc.suid); duke@1: } duke@1: } duke@1: duke@1: /* compare the class names, stripping off package names. */ duke@1: if (! compareClassNames(name, cl.getName(), '.')) duke@1: // XXX I18N, logging needed duke@1: throw new InvalidClassException(cl.getName(), duke@1: "Incompatible local class name. " + duke@1: "Expected class name compatible with " + duke@1: name); duke@1: duke@1: /* duke@1: * Test that both implement either serializable or externalizable. duke@1: */ duke@1: duke@1: // The next check is more generic, since it covers the duke@1: // Proxy case, the JDK 1.3 serialization code has duke@1: // both checks duke@1: //if ((serializable && localClassDesc.externalizable) || duke@1: // (externalizable && localClassDesc.serializable)) duke@1: // throw new InvalidClassException(localCl.getName(), duke@1: // "Serializable is incompatible with Externalizable"); duke@1: duke@1: if ((serializable != localClassDesc.serializable) || duke@1: (externalizable != localClassDesc.externalizable) || duke@1: (!serializable && !externalizable)) duke@1: duke@1: // XXX I18N, logging needed duke@1: throw new InvalidClassException(cl.getName(), duke@1: "Serialization incompatible with Externalization"); duke@1: duke@1: /* Set up the reflected Fields in the class where the value of each duke@1: * field in this descriptor should be stored. duke@1: * Each field in this ObjectStreamClass (the source) is located (by duke@1: * name) in the ObjectStreamClass of the class(the destination). duke@1: * In the usual (non-versioned case) the field is in both duke@1: * descriptors and the types match, so the reflected Field is copied. duke@1: * If the type does not match, a InvalidClass exception is thrown. duke@1: * If the field is not present in the class, the reflected Field duke@1: * remains null so the field will be read but discarded. duke@1: * If extra fields are present in the class they are ignored. Their duke@1: * values will be set to the default value by the object allocator. duke@1: * Both the src and dest field list are sorted by type and name. duke@1: */ duke@1: duke@1: ObjectStreamField[] destfield = duke@1: (ObjectStreamField[])localClassDesc.fields; duke@1: ObjectStreamField[] srcfield = duke@1: (ObjectStreamField[])fields; duke@1: duke@1: int j = 0; duke@1: nextsrc: duke@1: for (int i = 0; i < srcfield.length; i++ ) { duke@1: /* Find this field in the dest*/ duke@1: for (int k = j; k < destfield.length; k++) { duke@1: if (srcfield[i].getName().equals(destfield[k].getName())) { duke@1: /* found match */ duke@1: if (srcfield[i].isPrimitive() && duke@1: !srcfield[i].typeEquals(destfield[k])) { duke@1: // XXX I18N, logging needed duke@1: throw new InvalidClassException(cl.getName(), duke@1: "The type of field " + duke@1: srcfield[i].getName() + duke@1: " of class " + name + duke@1: " is incompatible."); duke@1: } duke@1: duke@1: /* Skip over any fields in the dest that are not in the src */ duke@1: j = k; duke@1: duke@1: srcfield[i].setField(destfield[j].getField()); duke@1: // go on to the next source field duke@1: continue nextsrc; duke@1: } duke@1: } duke@1: } duke@1: duke@1: /* Set up field data for use while reading from the input stream. */ duke@1: computeFieldInfo(); duke@1: duke@1: /* Remember the class this represents */ duke@1: ofClass = cl; duke@1: duke@1: /* get the cache of these methods from the local class duke@1: * implementation. duke@1: */ duke@1: readObjectMethod = localClassDesc.readObjectMethod; duke@1: readResolveObjectMethod = localClassDesc.readResolveObjectMethod; duke@1: } duke@1: duke@1: /* Compare the base class names of streamName and localName. duke@1: * duke@1: * @return Return true iff the base class name compare. duke@1: * @parameter streamName Fully qualified class name. duke@1: * @parameter localName Fully qualified class name. duke@1: * @parameter pkgSeparator class names use either '.' or '/'. duke@1: * duke@1: * Only compare base class name to allow package renaming. duke@1: */ duke@1: static boolean compareClassNames(String streamName, duke@1: String localName, duke@1: char pkgSeparator) { duke@1: /* compare the class names, stripping off package names. */ duke@1: int streamNameIndex = streamName.lastIndexOf(pkgSeparator); duke@1: if (streamNameIndex < 0) duke@1: streamNameIndex = 0; duke@1: duke@1: int localNameIndex = localName.lastIndexOf(pkgSeparator); duke@1: if (localNameIndex < 0) duke@1: localNameIndex = 0; duke@1: duke@1: return streamName.regionMatches(false, streamNameIndex, duke@1: localName, localNameIndex, duke@1: streamName.length() - streamNameIndex); duke@1: } duke@1: duke@1: /* duke@1: * Compare the types of two class descriptors. duke@1: * They match if they have the same class name and suid duke@1: */ duke@1: final boolean typeEquals(ObjectStreamClass other) { duke@1: return (suid == other.suid) && duke@1: compareClassNames(name, other.name, '.'); duke@1: } duke@1: duke@1: /* duke@1: * Return the superclass descriptor of this descriptor. duke@1: */ duke@1: final void setSuperclass(ObjectStreamClass s) { duke@1: superclass = s; duke@1: } duke@1: duke@1: /* duke@1: * Return the superclass descriptor of this descriptor. duke@1: */ duke@1: final ObjectStreamClass getSuperclass() { duke@1: return superclass; duke@1: } duke@1: duke@1: /** duke@1: * Return whether the class has a readObject method duke@1: */ duke@1: final boolean hasReadObject() { duke@1: return readObjectMethod != null; duke@1: } duke@1: duke@1: /* duke@1: * Return whether the class has a writeObject method duke@1: */ duke@1: final boolean hasWriteObject() { duke@1: return writeObjectMethod != null ; duke@1: } duke@1: duke@1: /** duke@1: * Returns when or not this class should be custom duke@1: * marshaled (use chunking). This should happen if duke@1: * it is Externalizable OR if it or duke@1: * any of its superclasses has a writeObject method, duke@1: */ duke@1: final boolean isCustomMarshaled() { duke@1: return (hasWriteObject() || isExternalizable()) duke@1: || (superclass != null && superclass.isCustomMarshaled()); duke@1: } duke@1: duke@1: /* duke@1: * Return true if all instances of 'this' Externalizable class duke@1: * are written in block-data mode from the stream that 'this' was read duke@1: * from.

duke@1: * duke@1: * In JDK 1.1, all Externalizable instances are not written duke@1: * in block-data mode. duke@1: * In JDK 1.2, all Externalizable instances, by default, are written duke@1: * in block-data mode and the Externalizable instance is terminated with duke@1: * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable duke@1: * instances. duke@1: * duke@1: * IMPLEMENTATION NOTE: duke@1: * This should have been a mode maintained per stream; however, duke@1: * for compatibility reasons, it was only possible to record duke@1: * this change per class. All Externalizable classes within duke@1: * a given stream should either have this mode enabled or duke@1: * disabled. This is enforced by not allowing the PROTOCOL_VERSION duke@1: * of a stream to he changed after any objects have been written. duke@1: * duke@1: * @see ObjectOutputStream#useProtocolVersion duke@1: * @see ObjectStreamConstants#PROTOCOL_VERSION_1 duke@1: * @see ObjectStreamConstants#PROTOCOL_VERSION_2 duke@1: * duke@1: * @since JDK 1.2 duke@1: */ duke@1: boolean hasExternalizableBlockDataMode() { duke@1: return hasExternalizableBlockData; duke@1: } duke@1: duke@1: /** duke@1: * Creates a new instance of the represented class. If the class is duke@1: * externalizable, invokes its public no-arg constructor; otherwise, if the duke@1: * class is serializable, invokes the no-arg constructor of the first duke@1: * non-serializable superclass. Throws UnsupportedOperationException if duke@1: * this class descriptor is not associated with a class, if the associated duke@1: * class is non-serializable or if the appropriate no-arg constructor is duke@1: * inaccessible/unavailable. duke@1: */ duke@1: Object newInstance() duke@1: throws InstantiationException, InvocationTargetException, duke@1: UnsupportedOperationException duke@1: { duke@1: if (cons != null) { duke@1: try { duke@1: return cons.newInstance(new Object[0]); duke@1: } catch (IllegalAccessException ex) { duke@1: // should not occur, as access checks have been suppressed duke@1: InternalError ie = new InternalError(); duke@1: ie.initCause( ex ) ; duke@1: throw ie ; duke@1: } duke@1: } else { duke@1: throw new UnsupportedOperationException(); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Returns public no-arg constructor of given class, or null if none found. duke@1: * Access checks are disabled on the returned constructor (if any), since duke@1: * the defining class may still be non-public. duke@1: */ duke@1: private static Constructor getExternalizableConstructor(Class cl) { duke@1: try { duke@1: Constructor cons = cl.getDeclaredConstructor(new Class[0]); duke@1: cons.setAccessible(true); duke@1: return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ? duke@1: cons : null; duke@1: } catch (NoSuchMethodException ex) { duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Returns subclass-accessible no-arg constructor of first non-serializable duke@1: * superclass, or null if none found. Access checks are disabled on the duke@1: * returned constructor (if any). duke@1: */ duke@1: private static Constructor getSerializableConstructor(Class cl) { duke@1: Class initCl = cl; duke@1: while (Serializable.class.isAssignableFrom(initCl)) { duke@1: if ((initCl = initCl.getSuperclass()) == null) { duke@1: return null; duke@1: } duke@1: } duke@1: try { duke@1: Constructor cons = initCl.getDeclaredConstructor(new Class[0]); duke@1: int mods = cons.getModifiers(); duke@1: if ((mods & Modifier.PRIVATE) != 0 || duke@1: ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 && duke@1: !packageEquals(cl, initCl))) duke@1: { duke@1: return null; duke@1: } duke@1: cons = bridge.newConstructorForSerialization(cl, cons); duke@1: cons.setAccessible(true); duke@1: return cons; duke@1: } catch (NoSuchMethodException ex) { duke@1: return null; duke@1: } duke@1: } duke@1: duke@1: /* duke@1: * Return the ObjectStreamClass of the local class this one is based on. duke@1: */ duke@1: final ObjectStreamClass localClassDescriptor() { duke@1: return localClassDesc; duke@1: } duke@1: duke@1: /* duke@1: * Get the Serializability of the class. duke@1: */ duke@1: boolean isSerializable() { duke@1: return serializable; duke@1: } duke@1: duke@1: /* duke@1: * Get the externalizability of the class. duke@1: */ duke@1: boolean isExternalizable() { duke@1: return externalizable; duke@1: } duke@1: duke@1: boolean isNonSerializable() { duke@1: return ! (externalizable || serializable); duke@1: } duke@1: duke@1: /* duke@1: * Calculate the size of the array needed to store primitive data and the duke@1: * number of object references to read when reading from the input duke@1: * stream. duke@1: */ duke@1: private void computeFieldInfo() { duke@1: primBytes = 0; duke@1: objFields = 0; duke@1: duke@1: for (int i = 0; i < fields.length; i++ ) { duke@1: switch (fields[i].getTypeCode()) { duke@1: case 'B': duke@1: case 'Z': duke@1: primBytes += 1; duke@1: break; duke@1: case 'C': duke@1: case 'S': duke@1: primBytes += 2; duke@1: break; duke@1: duke@1: case 'I': duke@1: case 'F': duke@1: primBytes += 4; duke@1: break; duke@1: case 'J': duke@1: case 'D' : duke@1: primBytes += 8; duke@1: break; duke@1: duke@1: case 'L': duke@1: case '[': duke@1: objFields += 1; duke@1: break; duke@1: } duke@1: } duke@1: } duke@1: duke@1: private static void msg( String str ) duke@1: { duke@1: System.out.println( str ) ; duke@1: } duke@1: duke@1: /* JDK 1.5 has introduced some new modifier bits (such as SYNTHETIC) duke@1: * that can affect the SVUID computation (see bug 4897937). These bits duke@1: * must be ignored, as otherwise interoperability with ORBs in earlier duke@1: * JDK versions can be compromised. I am adding these masks for this duke@1: * purpose as discussed in the CCC for this bug (see http://ccc.sfbay/4897937). duke@1: */ duke@1: duke@1: public static final int CLASS_MASK = Modifier.PUBLIC | Modifier.FINAL | duke@1: Modifier.INTERFACE | Modifier.ABSTRACT ; duke@1: public static final int FIELD_MASK = Modifier.PUBLIC | Modifier.PRIVATE | duke@1: Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | duke@1: Modifier.TRANSIENT | Modifier.VOLATILE ; duke@1: public static final int METHOD_MASK = Modifier.PUBLIC | Modifier.PRIVATE | duke@1: Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | duke@1: Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT | duke@1: Modifier.STRICT ; duke@1: duke@1: /* duke@1: * Compute a hash for the specified class. Incrementally add duke@1: * items to the hash accumulating in the digest stream. duke@1: * Fold the hash into a long. Use the SHA secure hash function. duke@1: */ duke@1: private static long _computeSerialVersionUID(Class cl) { duke@1: if (DEBUG_SVUID) duke@1: msg( "Computing SerialVersionUID for " + cl ) ; duke@1: ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); duke@1: duke@1: long h = 0; duke@1: try { duke@1: MessageDigest md = MessageDigest.getInstance("SHA"); duke@1: DigestOutputStream mdo = new DigestOutputStream(devnull, md); duke@1: DataOutputStream data = new DataOutputStream(mdo); duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + cl.getName() + "\" )" ) ; duke@1: data.writeUTF(cl.getName()); duke@1: duke@1: int classaccess = cl.getModifiers(); duke@1: classaccess &= (Modifier.PUBLIC | Modifier.FINAL | duke@1: Modifier.INTERFACE | Modifier.ABSTRACT); duke@1: duke@1: /* Workaround for javac bug that only set ABSTRACT for duke@1: * interfaces if the interface had some methods. duke@1: * The ABSTRACT bit reflects that the number of methods > 0. duke@1: * This is required so correct hashes can be computed duke@1: * for existing class files. duke@1: * Previously this hack was previously present in the VM. duke@1: */ duke@1: Method[] method = cl.getDeclaredMethods(); duke@1: if ((classaccess & Modifier.INTERFACE) != 0) { duke@1: classaccess &= (~Modifier.ABSTRACT); duke@1: if (method.length > 0) { duke@1: classaccess |= Modifier.ABSTRACT; duke@1: } duke@1: } duke@1: duke@1: // Mask out any post-1.4 attributes duke@1: classaccess &= CLASS_MASK ; duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteInt( " + classaccess + " ) " ) ; duke@1: data.writeInt(classaccess); duke@1: duke@1: /* duke@1: * Get the list of interfaces supported, duke@1: * Accumulate their names their names in Lexical order duke@1: * and add them to the hash duke@1: */ duke@1: if (!cl.isArray()) { duke@1: /* In 1.2fcs, getInterfaces() was modified to return duke@1: * {java.lang.Cloneable, java.io.Serializable} when duke@1: * called on array classes. These values would upset duke@1: * the computation of the hash, so we explicitly omit duke@1: * them from its computation. duke@1: */ duke@1: duke@1: Class interfaces[] = cl.getInterfaces(); duke@1: Arrays.sort(interfaces, compareClassByName); duke@1: duke@1: for (int i = 0; i < interfaces.length; i++) { duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + interfaces[i].getName() + "\" ) " ) ; duke@1: data.writeUTF(interfaces[i].getName()); duke@1: } duke@1: } duke@1: duke@1: /* Sort the field names to get a deterministic order */ duke@1: Field[] field = cl.getDeclaredFields(); duke@1: Arrays.sort(field, compareMemberByName); duke@1: duke@1: for (int i = 0; i < field.length; i++) { duke@1: Field f = field[i]; duke@1: duke@1: /* Include in the hash all fields except those that are duke@1: * private transient and private static. duke@1: */ duke@1: int m = f.getModifiers(); duke@1: if (Modifier.isPrivate(m) && duke@1: (Modifier.isTransient(m) || Modifier.isStatic(m))) duke@1: continue; duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + f.getName() + "\" ) " ) ; duke@1: data.writeUTF(f.getName()); duke@1: duke@1: // Mask out any post-1.4 bits duke@1: m &= FIELD_MASK ; duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteInt( " + m + " ) " ) ; duke@1: data.writeInt(m); duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + getSignature(f.getType()) + "\" ) " ) ; duke@1: data.writeUTF(getSignature(f.getType())); duke@1: } duke@1: duke@1: if (hasStaticInitializer(cl)) { duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"\" ) " ) ; duke@1: data.writeUTF(""); duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteInt( " + Modifier.STATIC + " )" ) ; duke@1: data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"()V\" )" ) ; duke@1: data.writeUTF("()V"); duke@1: } duke@1: duke@1: /* duke@1: * Get the list of constructors including name and signature duke@1: * Sort lexically, add all except the private constructors duke@1: * to the hash with their access flags duke@1: */ duke@1: duke@1: MethodSignature[] constructors = duke@1: MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors()); duke@1: for (int i = 0; i < constructors.length; i++) { duke@1: MethodSignature c = constructors[i]; duke@1: String mname = ""; duke@1: String desc = c.signature; duke@1: desc = desc.replace('/', '.'); duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + mname + "\" )" ) ; duke@1: data.writeUTF(mname); duke@1: duke@1: // mask out post-1.4 modifiers duke@1: int modifier = c.member.getModifiers() & METHOD_MASK ; duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteInt( " + modifier + " ) " ) ; duke@1: data.writeInt( modifier ) ; duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + desc+ "\" )" ) ; duke@1: data.writeUTF(desc); duke@1: } duke@1: duke@1: /* Include in the hash all methods except those that are duke@1: * private transient and private static. duke@1: */ duke@1: MethodSignature[] methods = duke@1: MethodSignature.removePrivateAndSort(method); duke@1: for (int i = 0; i < methods.length; i++ ) { duke@1: MethodSignature m = methods[i]; duke@1: String desc = m.signature; duke@1: desc = desc.replace('/', '.'); duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + m.member.getName()+ "\" )" ) ; duke@1: data.writeUTF(m.member.getName()); duke@1: duke@1: // mask out post-1.4 modifiers duke@1: int modifier = m.member.getModifiers() & METHOD_MASK ; duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteInt( " + modifier + " ) " ) ; duke@1: data.writeInt( modifier ) ; duke@1: duke@1: if (DEBUG_SVUID) duke@1: msg( "\twriteUTF( \"" + desc + "\" )" ) ; duke@1: data.writeUTF(desc); duke@1: } duke@1: duke@1: /* Compute the hash value for this class. duke@1: * Use only the first 64 bits of the hash. duke@1: */ duke@1: data.flush(); duke@1: byte hasharray[] = md.digest(); duke@1: for (int i = 0; i < Math.min(8, hasharray.length); i++) { duke@1: h += (long)(hasharray[i] & 255) << (i * 8); duke@1: } duke@1: } catch (IOException ignore) { duke@1: /* can't happen, but be deterministic anyway. */ duke@1: h = -1; duke@1: } catch (NoSuchAlgorithmException complain) { duke@1: SecurityException se = new SecurityException() ; duke@1: se.initCause( complain ) ; duke@1: throw se ; duke@1: } duke@1: duke@1: return h; duke@1: } duke@1: duke@1: private static long computeStructuralUID(com.sun.corba.se.impl.io.ObjectStreamClass osc, Class cl) { duke@1: ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); duke@1: duke@1: long h = 0; duke@1: try { duke@1: duke@1: if ((!java.io.Serializable.class.isAssignableFrom(cl)) || duke@1: (cl.isInterface())){ duke@1: return 0; duke@1: } duke@1: duke@1: if (java.io.Externalizable.class.isAssignableFrom(cl)) { duke@1: return 1; duke@1: } duke@1: duke@1: MessageDigest md = MessageDigest.getInstance("SHA"); duke@1: DigestOutputStream mdo = new DigestOutputStream(devnull, md); duke@1: DataOutputStream data = new DataOutputStream(mdo); duke@1: duke@1: // Get SUID of parent duke@1: Class parent = cl.getSuperclass(); duke@1: if ((parent != null)) duke@1: // SerialBug 1; acc. to spec the one for duke@1: // java.lang.object duke@1: // should be computed and put duke@1: // && (parent != java.lang.Object.class)) duke@1: { duke@1: //data.writeLong(computeSerialVersionUID(null,parent)); duke@1: data.writeLong(computeStructuralUID(lookup(parent), parent)); duke@1: } duke@1: duke@1: if (osc.hasWriteObject()) duke@1: data.writeInt(2); duke@1: else duke@1: data.writeInt(1); duke@1: duke@1: // CORBA formal 00-11-03 10.6.2: For each field of the duke@1: // class that is mapped to IDL, sorted lexicographically duke@1: // by Java field name, in increasing order... duke@1: ObjectStreamField[] field = osc.getFields(); duke@1: if (field.length > 1) { duke@1: Arrays.sort(field, compareObjStrFieldsByName); duke@1: } duke@1: duke@1: // ...Java field name in UTF encoding, field duke@1: // descriptor, as defined by the JVM spec... duke@1: for (int i = 0; i < field.length; i++) { duke@1: data.writeUTF(field[i].getName()); duke@1: data.writeUTF(field[i].getSignature()); duke@1: } duke@1: duke@1: /* Compute the hash value for this class. duke@1: * Use only the first 64 bits of the hash. duke@1: */ duke@1: data.flush(); duke@1: byte hasharray[] = md.digest(); duke@1: // int minimum = Math.min(8, hasharray.length); duke@1: // SerialBug 3: SHA computation is wrong; for loop reversed duke@1: //for (int i = minimum; i > 0; i--) duke@1: for (int i = 0; i < Math.min(8, hasharray.length); i++) { duke@1: h += (long)(hasharray[i] & 255) << (i * 8); duke@1: } duke@1: } catch (IOException ignore) { duke@1: /* can't happen, but be deterministic anyway. */ duke@1: h = -1; duke@1: } catch (NoSuchAlgorithmException complain) { duke@1: SecurityException se = new SecurityException(); duke@1: se.initCause( complain ) ; duke@1: throw se ; duke@1: } duke@1: return h; duke@1: } duke@1: duke@1: /** duke@1: * Compute the JVM signature for the class. duke@1: */ duke@1: static String getSignature(Class clazz) { duke@1: String type = null; duke@1: if (clazz.isArray()) { duke@1: Class cl = clazz; duke@1: int dimensions = 0; duke@1: while (cl.isArray()) { duke@1: dimensions++; duke@1: cl = cl.getComponentType(); duke@1: } duke@1: StringBuffer sb = new StringBuffer(); duke@1: for (int i = 0; i < dimensions; i++) { duke@1: sb.append("["); duke@1: } duke@1: sb.append(getSignature(cl)); duke@1: type = sb.toString(); duke@1: } else if (clazz.isPrimitive()) { duke@1: if (clazz == Integer.TYPE) { duke@1: type = "I"; duke@1: } else if (clazz == Byte.TYPE) { duke@1: type = "B"; duke@1: } else if (clazz == Long.TYPE) { duke@1: type = "J"; duke@1: } else if (clazz == Float.TYPE) { duke@1: type = "F"; duke@1: } else if (clazz == Double.TYPE) { duke@1: type = "D"; duke@1: } else if (clazz == Short.TYPE) { duke@1: type = "S"; duke@1: } else if (clazz == Character.TYPE) { duke@1: type = "C"; duke@1: } else if (clazz == Boolean.TYPE) { duke@1: type = "Z"; duke@1: } else if (clazz == Void.TYPE) { duke@1: type = "V"; duke@1: } duke@1: } else { duke@1: type = "L" + clazz.getName().replace('.', '/') + ";"; duke@1: } duke@1: return type; duke@1: } duke@1: duke@1: /* duke@1: * Compute the JVM method descriptor for the method. duke@1: */ duke@1: static String getSignature(Method meth) { duke@1: StringBuffer sb = new StringBuffer(); duke@1: duke@1: sb.append("("); duke@1: duke@1: Class[] params = meth.getParameterTypes(); // avoid clone duke@1: for (int j = 0; j < params.length; j++) { duke@1: sb.append(getSignature(params[j])); duke@1: } duke@1: sb.append(")"); duke@1: sb.append(getSignature(meth.getReturnType())); duke@1: return sb.toString(); duke@1: } duke@1: duke@1: /* duke@1: * Compute the JVM constructor descriptor for the constructor. duke@1: */ duke@1: static String getSignature(Constructor cons) { duke@1: StringBuffer sb = new StringBuffer(); duke@1: duke@1: sb.append("("); duke@1: duke@1: Class[] params = cons.getParameterTypes(); // avoid clone duke@1: for (int j = 0; j < params.length; j++) { duke@1: sb.append(getSignature(params[j])); duke@1: } duke@1: sb.append(")V"); duke@1: return sb.toString(); duke@1: } duke@1: duke@1: /* duke@1: * Cache of Class -> ClassDescriptor Mappings. duke@1: */ duke@1: static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61]; duke@1: duke@1: /* duke@1: * findDescriptorFor a Class. This looks in the cache for a duke@1: * mapping from Class -> ObjectStreamClass mappings. The hashCode duke@1: * of the Class is used for the lookup since the Class is the key. duke@1: * The entries are extended from java.lang.ref.SoftReference so the duke@1: * gc will be able to free them if needed. duke@1: */ duke@1: private static ObjectStreamClass findDescriptorFor(Class cl) { duke@1: duke@1: int hash = cl.hashCode(); duke@1: int index = (hash & 0x7FFFFFFF) % descriptorFor.length; duke@1: ObjectStreamClassEntry e; duke@1: ObjectStreamClassEntry prev; duke@1: duke@1: /* Free any initial entries whose refs have been cleared */ duke@1: while ((e = descriptorFor[index]) != null && e.get() == null) { duke@1: descriptorFor[index] = e.next; duke@1: } duke@1: duke@1: /* Traverse the chain looking for a descriptor with ofClass == cl. duke@1: * unlink entries that are unresolved. duke@1: */ duke@1: prev = e; duke@1: while (e != null ) { duke@1: ObjectStreamClass desc = (ObjectStreamClass)(e.get()); duke@1: if (desc == null) { duke@1: // This entry has been cleared, unlink it duke@1: prev.next = e.next; duke@1: } else { duke@1: if (desc.ofClass == cl) duke@1: return desc; duke@1: prev = e; duke@1: } duke@1: e = e.next; duke@1: } duke@1: return null; duke@1: } duke@1: duke@1: /* duke@1: * insertDescriptorFor a Class -> ObjectStreamClass mapping. duke@1: */ duke@1: private static void insertDescriptorFor(ObjectStreamClass desc) { duke@1: // Make sure not already present duke@1: if (findDescriptorFor(desc.ofClass) != null) { duke@1: return; duke@1: } duke@1: duke@1: int hash = desc.ofClass.hashCode(); duke@1: int index = (hash & 0x7FFFFFFF) % descriptorFor.length; duke@1: ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc); duke@1: e.next = descriptorFor[index]; duke@1: descriptorFor[index] = e; duke@1: } duke@1: duke@1: private static Field[] getDeclaredFields(final Class clz) { duke@1: return (Field[]) AccessController.doPrivileged(new PrivilegedAction() { duke@1: public Object run() { duke@1: return clz.getDeclaredFields(); duke@1: } duke@1: }); duke@1: } duke@1: duke@1: duke@1: /* duke@1: * The name of this descriptor duke@1: */ duke@1: private String name; duke@1: duke@1: /* duke@1: * The descriptor of the supertype. duke@1: */ duke@1: private ObjectStreamClass superclass; duke@1: duke@1: /* duke@1: * Flags for Serializable and Externalizable. duke@1: */ duke@1: private boolean serializable; duke@1: private boolean externalizable; duke@1: duke@1: /* duke@1: * Array of persistent fields of this class, sorted by duke@1: * type and name. duke@1: */ duke@1: private ObjectStreamField[] fields; duke@1: duke@1: /* duke@1: * Class that is a descriptor for in this virtual machine. duke@1: */ duke@1: private Class ofClass; duke@1: duke@1: /* duke@1: * True if descriptor for a proxy class. duke@1: */ duke@1: boolean forProxyClass; duke@1: duke@1: duke@1: /* duke@1: * SerialVersionUID for this class. duke@1: */ duke@1: private long suid = kDefaultUID; duke@1: private String suidStr = null; duke@1: duke@1: /* duke@1: * Actual (computed) SerialVersionUID for this class. duke@1: */ duke@1: private long actualSuid = kDefaultUID; duke@1: private String actualSuidStr = null; duke@1: duke@1: /* duke@1: * The total number of bytes of primitive fields. duke@1: * The total number of object fields. duke@1: */ duke@1: int primBytes; duke@1: int objFields; duke@1: duke@1: /** duke@1: * Flag indicating whether or not this instance has duke@1: * successfully completed initialization. This is to duke@1: * try to fix bug 4373844. Working to move to duke@1: * reusing java.io.ObjectStreamClass for JDK 1.5. duke@1: */ duke@1: private boolean initialized = false; duke@1: duke@1: /* Internal lock object. */ duke@1: private Object lock = new Object(); duke@1: duke@1: /* In JDK 1.1, external data was not written in block mode. duke@1: * As of JDK 1.2, external data is written in block data mode. This duke@1: * flag enables JDK 1.2 to be able to read JDK 1.1 written external data. duke@1: * duke@1: * @since JDK 1.2 duke@1: */ duke@1: private boolean hasExternalizableBlockData; duke@1: Method writeObjectMethod; duke@1: Method readObjectMethod; duke@1: private Method writeReplaceObjectMethod; duke@1: private Method readResolveObjectMethod; duke@1: private Constructor cons ; duke@1: duke@1: /** duke@1: * Beginning in Java to IDL ptc/02-01-12, RMI-IIOP has a duke@1: * stream format version 2 which puts a fake valuetype around duke@1: * a Serializable's optional custom data. This valuetype has duke@1: * a special repository ID made from the Serializable's duke@1: * information which we are pre-computing and duke@1: * storing here. duke@1: */ duke@1: private String rmiiiopOptionalDataRepId = null; duke@1: duke@1: /* duke@1: * ObjectStreamClass that this one was built from. duke@1: */ duke@1: private ObjectStreamClass localClassDesc; duke@1: duke@1: /* Find out if the class has a static class initializer */ duke@1: private static Method hasStaticInitializerMethod = null; duke@1: /** duke@1: * Returns true if the given class defines a static initializer method, duke@1: * false otherwise. duke@1: */ duke@1: private static boolean hasStaticInitializer(Class cl) { duke@1: if (hasStaticInitializerMethod == null) { duke@1: Class classWithThisMethod = null; duke@1: duke@1: try { duke@1: try { duke@1: // When using rip-int with Merlin or when this is a Merlin duke@1: // workspace, the method we want is in sun.misc.ClassReflector duke@1: // and absent from java.io.ObjectStreamClass. duke@1: // duke@1: // When compiling rip-int with JDK 1.3.x, we have to get it duke@1: // from java.io.ObjectStreamClass. duke@1: classWithThisMethod = Class.forName("sun.misc.ClassReflector"); duke@1: } catch (ClassNotFoundException cnfe) { duke@1: // Do nothing. This is either not a Merlin workspace, duke@1: // or rip-int is being compiled with something other than duke@1: // Merlin, probably JDK 1.3. Fall back on java.io.ObjectStreaClass. duke@1: } duke@1: if (classWithThisMethod == null) duke@1: classWithThisMethod = java.io.ObjectStreamClass.class; duke@1: duke@1: hasStaticInitializerMethod = duke@1: classWithThisMethod.getDeclaredMethod("hasStaticInitializer", duke@1: new Class[] { Class.class }); duke@1: } catch (NoSuchMethodException ex) { duke@1: } duke@1: duke@1: if (hasStaticInitializerMethod == null) { duke@1: // XXX I18N, logging needed duke@1: throw new InternalError("Can't find hasStaticInitializer method on " duke@1: + classWithThisMethod.getName()); duke@1: } duke@1: hasStaticInitializerMethod.setAccessible(true); duke@1: } duke@1: duke@1: try { duke@1: Boolean retval = (Boolean) duke@1: hasStaticInitializerMethod.invoke(null, new Object[] { cl }); duke@1: return retval.booleanValue(); duke@1: } catch (Exception ex) { duke@1: // XXX I18N, logging needed duke@1: InternalError ie = new InternalError( "Error invoking hasStaticInitializer" ) ; duke@1: ie.initCause( ex ) ; duke@1: throw ie ; duke@1: } duke@1: } duke@1: duke@1: duke@1: /* The Class Object for java.io.Serializable */ duke@1: private static Class classSerializable = null; duke@1: private static Class classExternalizable = null; duke@1: duke@1: /* duke@1: * Resolve java.io.Serializable at load time. duke@1: */ duke@1: static { duke@1: try { duke@1: classSerializable = Class.forName("java.io.Serializable"); duke@1: classExternalizable = Class.forName("java.io.Externalizable"); duke@1: } catch (Throwable e) { duke@1: System.err.println("Could not load java.io.Serializable or java.io.Externalizable."); duke@1: } duke@1: } duke@1: duke@1: /** use serialVersionUID from JDK 1.1. for interoperability */ duke@1: private static final long serialVersionUID = -6120832682080437368L; duke@1: duke@1: /** duke@1: * Set serialPersistentFields of a Serializable class to this value to duke@1: * denote that the class has no Serializable fields. duke@1: */ duke@1: public static final ObjectStreamField[] NO_FIELDS = duke@1: new ObjectStreamField[0]; duke@1: duke@1: /* duke@1: * Entries held in the Cache of known ObjectStreamClass objects. duke@1: * Entries are chained together with the same hash value (modulo array size). duke@1: */ duke@1: private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference duke@1: { duke@1: ObjectStreamClassEntry(ObjectStreamClass c) { duke@1: //super(c); duke@1: this.c = c; duke@1: } duke@1: ObjectStreamClassEntry next; duke@1: duke@1: public Object get() duke@1: { duke@1: return c; duke@1: } duke@1: private ObjectStreamClass c; duke@1: } duke@1: duke@1: /* duke@1: * Comparator object for Classes and Interfaces duke@1: */ duke@1: private static Comparator compareClassByName = duke@1: new CompareClassByName(); duke@1: duke@1: private static class CompareClassByName implements Comparator { duke@1: public int compare(Object o1, Object o2) { duke@1: Class c1 = (Class)o1; duke@1: Class c2 = (Class)o2; duke@1: return (c1.getName()).compareTo(c2.getName()); duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Comparator for ObjectStreamFields by name duke@1: */ duke@1: private final static Comparator compareObjStrFieldsByName duke@1: = new CompareObjStrFieldsByName(); duke@1: duke@1: private static class CompareObjStrFieldsByName implements Comparator { duke@1: public int compare(Object o1, Object o2) { duke@1: ObjectStreamField osf1 = (ObjectStreamField)o1; duke@1: ObjectStreamField osf2 = (ObjectStreamField)o2; duke@1: duke@1: return osf1.getName().compareTo(osf2.getName()); duke@1: } duke@1: } duke@1: duke@1: /* duke@1: * Comparator object for Members, Fields, and Methods duke@1: */ duke@1: private static Comparator compareMemberByName = duke@1: new CompareMemberByName(); duke@1: duke@1: private static class CompareMemberByName implements Comparator { duke@1: public int compare(Object o1, Object o2) { duke@1: String s1 = ((Member)o1).getName(); duke@1: String s2 = ((Member)o2).getName(); duke@1: duke@1: if (o1 instanceof Method) { duke@1: s1 += getSignature((Method)o1); duke@1: s2 += getSignature((Method)o2); duke@1: } else if (o1 instanceof Constructor) { duke@1: s1 += getSignature((Constructor)o1); duke@1: s2 += getSignature((Constructor)o2); duke@1: } duke@1: return s1.compareTo(s2); duke@1: } duke@1: } duke@1: duke@1: /* It is expensive to recompute a method or constructor signature duke@1: many times, so compute it only once using this data structure. */ duke@1: private static class MethodSignature implements Comparator { duke@1: Member member; duke@1: String signature; // cached parameter signature duke@1: duke@1: /* Given an array of Method or Constructor members, duke@1: return a sorted array of the non-private members.*/ duke@1: /* A better implementation would be to implement the returned data duke@1: structure as an insertion sorted link list.*/ duke@1: static MethodSignature[] removePrivateAndSort(Member[] m) { duke@1: int numNonPrivate = 0; duke@1: for (int i = 0; i < m.length; i++) { duke@1: if (! Modifier.isPrivate(m[i].getModifiers())) { duke@1: numNonPrivate++; duke@1: } duke@1: } duke@1: MethodSignature[] cm = new MethodSignature[numNonPrivate]; duke@1: int cmi = 0; duke@1: for (int i = 0; i < m.length; i++) { duke@1: if (! Modifier.isPrivate(m[i].getModifiers())) { duke@1: cm[cmi] = new MethodSignature(m[i]); duke@1: cmi++; duke@1: } duke@1: } duke@1: if (cmi > 0) duke@1: Arrays.sort(cm, cm[0]); duke@1: return cm; duke@1: } duke@1: duke@1: /* Assumes that o1 and o2 are either both methods duke@1: or both constructors.*/ duke@1: public int compare(Object o1, Object o2) { duke@1: /* Arrays.sort calls compare when o1 and o2 are equal.*/ duke@1: if (o1 == o2) duke@1: return 0; duke@1: duke@1: MethodSignature c1 = (MethodSignature)o1; duke@1: MethodSignature c2 = (MethodSignature)o2; duke@1: duke@1: int result; duke@1: if (isConstructor()) { duke@1: result = c1.signature.compareTo(c2.signature); duke@1: } else { // is a Method. duke@1: result = c1.member.getName().compareTo(c2.member.getName()); duke@1: if (result == 0) duke@1: result = c1.signature.compareTo(c2.signature); duke@1: } duke@1: return result; duke@1: } duke@1: duke@1: final private boolean isConstructor() { duke@1: return member instanceof Constructor; duke@1: } duke@1: private MethodSignature(Member m) { duke@1: member = m; duke@1: if (isConstructor()) { duke@1: signature = ObjectStreamClass.getSignature((Constructor)m); duke@1: } else { duke@1: signature = ObjectStreamClass.getSignature((Method)m); duke@1: } duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Returns non-static, non-abstract method with given signature provided it duke@1: * is defined by or accessible (via inheritance) by the given class, or duke@1: * null if no match found. Access checks are disabled on the returned duke@1: * method (if any). duke@1: * duke@1: * Copied from the Merlin java.io.ObjectStreamClass. duke@1: */ duke@1: private static Method getInheritableMethod(Class cl, String name, duke@1: Class[] argTypes, duke@1: Class returnType) duke@1: { duke@1: Method meth = null; duke@1: Class defCl = cl; duke@1: while (defCl != null) { duke@1: try { duke@1: meth = defCl.getDeclaredMethod(name, argTypes); duke@1: break; duke@1: } catch (NoSuchMethodException ex) { duke@1: defCl = defCl.getSuperclass(); duke@1: } duke@1: } duke@1: duke@1: if ((meth == null) || (meth.getReturnType() != returnType)) { duke@1: return null; duke@1: } duke@1: meth.setAccessible(true); duke@1: int mods = meth.getModifiers(); duke@1: if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) { duke@1: return null; duke@1: } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) { duke@1: return meth; duke@1: } else if ((mods & Modifier.PRIVATE) != 0) { duke@1: return (cl == defCl) ? meth : null; duke@1: } else { duke@1: return packageEquals(cl, defCl) ? meth : null; duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * Returns true if classes are defined in the same package, false duke@1: * otherwise. duke@1: * duke@1: * Copied from the Merlin java.io.ObjectStreamClass. duke@1: */ duke@1: private static boolean packageEquals(Class cl1, Class cl2) { duke@1: Package pkg1 = cl1.getPackage(), pkg2 = cl2.getPackage(); duke@1: return ((pkg1 == pkg2) || ((pkg1 != null) && (pkg1.equals(pkg2)))); duke@1: } duke@1: }