| /* ObjectStreamClass.java -- Class used to write class information | |
| about serialized objects. | |
| Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.io; | |
| import gnu.java.io.NullOutputStream; | |
| import gnu.java.lang.reflect.TypeSignature; | |
| import gnu.java.security.action.SetAccessibleAction; | |
| import gnu.java.security.provider.Gnu; | |
| import java.lang.reflect.Constructor; | |
| import java.lang.reflect.Field; | |
| import java.lang.reflect.Member; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.Modifier; | |
| import java.lang.reflect.Proxy; | |
| import java.security.AccessController; | |
| import java.security.DigestOutputStream; | |
| import java.security.MessageDigest; | |
| import java.security.NoSuchAlgorithmException; | |
| import java.security.PrivilegedAction; | |
| import java.security.Security; | |
| import java.util.Arrays; | |
| import java.util.Comparator; | |
| import java.util.Hashtable; | |
| import java.util.Vector; | |
| public class ObjectStreamClass implements Serializable | |
| { | |
| static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0]; | |
| /** | |
| * Returns the <code>ObjectStreamClass</code> for <code>cl</code>. | |
| * If <code>cl</code> is null, or is not <code>Serializable</code>, | |
| * null is returned. <code>ObjectStreamClass</code>'s are memorized; | |
| * later calls to this method with the same class will return the | |
| * same <code>ObjectStreamClass</code> object and no recalculation | |
| * will be done. | |
| * | |
| * Warning: If this class contains an invalid serialPersistentField arrays | |
| * lookup will not throw anything. However {@link #getFields()} will return | |
| * an empty array and {@link java.io.ObjectOutputStream#writeObject} will throw an | |
| * {@link java.io.InvalidClassException}. | |
| * | |
| * @see java.io.Serializable | |
| */ | |
| public static ObjectStreamClass lookup(Class cl) | |
| { | |
| if (cl == null) | |
| return null; | |
| if (! (Serializable.class).isAssignableFrom(cl)) | |
| return null; | |
| return lookupForClassObject(cl); | |
| } | |
| /** | |
| * This lookup for internal use by ObjectOutputStream. Suppose | |
| * we have a java.lang.Class object C for class A, though A is not | |
| * serializable, but it's okay to serialize C. | |
| */ | |
| static ObjectStreamClass lookupForClassObject(Class cl) | |
| { | |
| if (cl == null) | |
| return null; | |
| ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl); | |
| if (osc != null) | |
| return osc; | |
| else | |
| { | |
| osc = new ObjectStreamClass(cl); | |
| classLookupTable.put(cl, osc); | |
| return osc; | |
| } | |
| } | |
| /** | |
| * Returns the name of the class that this | |
| * <code>ObjectStreamClass</code> represents. | |
| * | |
| * @return the name of the class. | |
| */ | |
| public String getName() | |
| { | |
| return name; | |
| } | |
| /** | |
| * Returns the class that this <code>ObjectStreamClass</code> | |
| * represents. Null could be returned if this | |
| * <code>ObjectStreamClass</code> was read from an | |
| * <code>ObjectInputStream</code> and the class it represents cannot | |
| * be found or loaded. | |
| * | |
| * @see java.io.ObjectInputStream | |
| */ | |
| public Class forClass() | |
| { | |
| return clazz; | |
| } | |
| /** | |
| * Returns the serial version stream-unique identifier for the class | |
| * represented by this <code>ObjectStreamClass</code>. This SUID is | |
| * either defined by the class as <code>static final long | |
| * serialVersionUID</code> or is calculated as specified in | |
| * Javasoft's "Object Serialization Specification" XXX: add reference | |
| * | |
| * @return the serial version UID. | |
| */ | |
| public long getSerialVersionUID() | |
| { | |
| return uid; | |
| } | |
| /** | |
| * Returns the serializable (non-static and non-transient) Fields | |
| * of the class represented by this ObjectStreamClass. The Fields | |
| * are sorted by name. | |
| * If fields were obtained using serialPersistentFields and this array | |
| * is faulty then the returned array of this method will be empty. | |
| * | |
| * @return the fields. | |
| */ | |
| public ObjectStreamField[] getFields() | |
| { | |
| ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; | |
| System.arraycopy(fields, 0, copy, 0, fields.length); | |
| return copy; | |
| } | |
| // XXX doc | |
| // Can't do binary search since fields is sorted by name and | |
| // primitiveness. | |
| public ObjectStreamField getField (String name) | |
| { | |
| for (int i = 0; i < fields.length; i++) | |
| if (fields[i].getName().equals(name)) | |
| return fields[i]; | |
| return null; | |
| } | |
| /** | |
| * Returns a textual representation of this | |
| * <code>ObjectStreamClass</code> object including the name of the | |
| * class it represents as well as that class's serial version | |
| * stream-unique identifier. | |
| * | |
| * @see #getSerialVersionUID() | |
| * @see #getName() | |
| */ | |
| public String toString() | |
| { | |
| return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; | |
| } | |
| // Returns true iff the class that this ObjectStreamClass represents | |
| // has the following method: | |
| // | |
| // private void writeObject (ObjectOutputStream) | |
| // | |
| // This method is used by the class to override default | |
| // serialization behavior. | |
| boolean hasWriteMethod() | |
| { | |
| return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; | |
| } | |
| // Returns true iff the class that this ObjectStreamClass represents | |
| // implements Serializable but does *not* implement Externalizable. | |
| boolean isSerializable() | |
| { | |
| return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; | |
| } | |
| // Returns true iff the class that this ObjectStreamClass represents | |
| // implements Externalizable. | |
| boolean isExternalizable() | |
| { | |
| return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; | |
| } | |
| // Returns true iff the class that this ObjectStreamClass represents | |
| // implements Externalizable. | |
| boolean isEnum() | |
| { | |
| return (flags & ObjectStreamConstants.SC_ENUM) != 0; | |
| } | |
| // Returns the <code>ObjectStreamClass</code> that represents the | |
| // class that is the superclass of the class this | |
| // <code>ObjectStreamClass</code> represents. If the superclass is | |
| // not Serializable, null is returned. | |
| ObjectStreamClass getSuper() | |
| { | |
| return superClass; | |
| } | |
| // returns an array of ObjectStreamClasses that represent the super | |
| // classes of CLAZZ and CLAZZ itself in order from most super to | |
| // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ | |
| // that is serializable. | |
| static ObjectStreamClass[] getObjectStreamClasses(Class clazz) | |
| { | |
| ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); | |
| if (osc == null) | |
| return new ObjectStreamClass[0]; | |
| else | |
| { | |
| Vector oscs = new Vector(); | |
| while (osc != null) | |
| { | |
| oscs.addElement (osc); | |
| osc = osc.getSuper(); | |
| } | |
| int count = oscs.size(); | |
| ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; | |
| for (int i = count - 1; i >= 0; i--) | |
| sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i); | |
| return sorted_oscs; | |
| } | |
| } | |
| // Returns an integer that consists of bit-flags that indicate | |
| // properties of the class represented by this ObjectStreamClass. | |
| // The bit-flags that could be present are those defined in | |
| // ObjectStreamConstants that begin with `SC_' | |
| int getFlags() | |
| { | |
| return flags; | |
| } | |
| ObjectStreamClass(String name, long uid, byte flags, | |
| ObjectStreamField[] fields) | |
| { | |
| this.name = name; | |
| this.uid = uid; | |
| this.flags = flags; | |
| this.fields = fields; | |
| } | |
| /** | |
| * This method builds the internal description corresponding to a Java Class. | |
| * As the constructor only assign a name to the current ObjectStreamClass instance, | |
| * that method sets the serial UID, chose the fields which will be serialized, | |
| * and compute the position of the fields in the serialized stream. | |
| * | |
| * @param cl The Java class which is used as a reference for building the descriptor. | |
| * @param superClass The descriptor of the super class for this class descriptor. | |
| * @throws InvalidClassException if an incompatibility between computed UID and | |
| * already set UID is found. | |
| */ | |
| void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException | |
| { | |
| this.clazz = cl; | |
| cacheMethods(); | |
| long class_uid = getClassUID(cl); | |
| if (uid == 0) | |
| uid = class_uid; | |
| else | |
| { | |
| // Check that the actual UID of the resolved class matches the UID from | |
| // the stream. | |
| if (uid != class_uid) | |
| { | |
| String msg = cl + | |
| ": Local class not compatible: stream serialVersionUID=" | |
| + uid + ", local serialVersionUID=" + class_uid; | |
| throw new InvalidClassException (msg); | |
| } | |
| } | |
| isProxyClass = clazz != null && Proxy.isProxyClass(clazz); | |
| this.superClass = superClass; | |
| calculateOffsets(); | |
| try | |
| { | |
| ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz); | |
| if (exportedFields == null) | |
| return; | |
| ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length]; | |
| int i, j, k; | |
| /* We now check the import fields against the exported fields. | |
| * There should not be contradiction (e.g. int x and String x) | |
| * but extra virtual fields can be added to the class. | |
| */ | |
| Arrays.sort(exportedFields); | |
| i = 0; j = 0; k = 0; | |
| while (i < fields.length && j < exportedFields.length) | |
| { | |
| int comp = fields[i].compareTo(exportedFields[j]); | |
| if (comp < 0) | |
| { | |
| newFieldList[k] = fields[i]; | |
| fields[i].setPersistent(false); | |
| fields[i].setToSet(false); | |
| i++; | |
| } | |
| else if (comp > 0) | |
| { | |
| /* field not found in imported fields. We add it | |
| * in the list of supported fields. | |
| */ | |
| newFieldList[k] = exportedFields[j]; | |
| newFieldList[k].setPersistent(true); | |
| newFieldList[k].setToSet(false); | |
| try | |
| { | |
| newFieldList[k].lookupField(clazz); | |
| newFieldList[k].checkFieldType(); | |
| } | |
| catch (NoSuchFieldException _) | |
| { | |
| } | |
| j++; | |
| } | |
| else | |
| { | |
| try | |
| { | |
| exportedFields[j].lookupField(clazz); | |
| exportedFields[j].checkFieldType(); | |
| } | |
| catch (NoSuchFieldException _) | |
| { | |
| } | |
| if (!fields[i].getType().equals(exportedFields[j].getType())) | |
| throw new InvalidClassException | |
| ("serialPersistentFields must be compatible with" + | |
| " imported fields (about " + fields[i].getName() + ")"); | |
| newFieldList[k] = fields[i]; | |
| fields[i].setPersistent(true); | |
| i++; | |
| j++; | |
| } | |
| k++; | |
| } | |
| if (i < fields.length) | |
| for (;i<fields.length;i++,k++) | |
| { | |
| fields[i].setPersistent(false); | |
| fields[i].setToSet(false); | |
| newFieldList[k] = fields[i]; | |
| } | |
| else | |
| if (j < exportedFields.length) | |
| for (;j<exportedFields.length;j++,k++) | |
| { | |
| exportedFields[j].setPersistent(true); | |
| exportedFields[j].setToSet(false); | |
| newFieldList[k] = exportedFields[j]; | |
| } | |
| fields = new ObjectStreamField[k]; | |
| System.arraycopy(newFieldList, 0, fields, 0, k); | |
| } | |
| catch (NoSuchFieldException ignore) | |
| { | |
| return; | |
| } | |
| catch (IllegalAccessException ignore) | |
| { | |
| return; | |
| } | |
| } | |
| void setSuperclass (ObjectStreamClass osc) | |
| { | |
| superClass = osc; | |
| } | |
| void calculateOffsets() | |
| { | |
| int i; | |
| ObjectStreamField field; | |
| primFieldSize = 0; | |
| int fcount = fields.length; | |
| for (i = 0; i < fcount; ++ i) | |
| { | |
| field = fields[i]; | |
| if (! field.isPrimitive()) | |
| break; | |
| field.setOffset(primFieldSize); | |
| switch (field.getTypeCode()) | |
| { | |
| case 'B': | |
| case 'Z': | |
| ++ primFieldSize; | |
| break; | |
| case 'C': | |
| case 'S': | |
| primFieldSize += 2; | |
| break; | |
| case 'I': | |
| case 'F': | |
| primFieldSize += 4; | |
| break; | |
| case 'D': | |
| case 'J': | |
| primFieldSize += 8; | |
| break; | |
| } | |
| } | |
| for (objectFieldCount = 0; i < fcount; ++ i) | |
| fields[i].setOffset(objectFieldCount++); | |
| } | |
| private Method findMethod(Method[] methods, String name, Class[] params, | |
| Class returnType, boolean mustBePrivate) | |
| { | |
| outer: | |
| for (int i = 0; i < methods.length; i++) | |
| { | |
| final Method m = methods[i]; | |
| int mods = m.getModifiers(); | |
| if (Modifier.isStatic(mods) | |
| || (mustBePrivate && !Modifier.isPrivate(mods))) | |
| { | |
| continue; | |
| } | |
| if (m.getName().equals(name) | |
| && m.getReturnType() == returnType) | |
| { | |
| Class[] mp = m.getParameterTypes(); | |
| if (mp.length == params.length) | |
| { | |
| for (int j = 0; j < mp.length; j++) | |
| { | |
| if (mp[j] != params[j]) | |
| { | |
| continue outer; | |
| } | |
| } | |
| AccessController.doPrivileged(new SetAccessibleAction(m)); | |
| return m; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| private static boolean inSamePackage(Class c1, Class c2) | |
| { | |
| String name1 = c1.getName(); | |
| String name2 = c2.getName(); | |
| int id1 = name1.lastIndexOf('.'); | |
| int id2 = name2.lastIndexOf('.'); | |
| // Handle the default package | |
| if (id1 == -1 || id2 == -1) | |
| return id1 == id2; | |
| String package1 = name1.substring(0, id1); | |
| String package2 = name2.substring(0, id2); | |
| return package1.equals(package2); | |
| } | |
| final static Class[] noArgs = new Class[0]; | |
| private static Method findAccessibleMethod(String name, Class from) | |
| { | |
| for (Class c = from; c != null; c = c.getSuperclass()) | |
| { | |
| try | |
| { | |
| Method res = c.getDeclaredMethod(name, noArgs); | |
| int mods = res.getModifiers(); | |
| if (c == from | |
| || Modifier.isProtected(mods) | |
| || Modifier.isPublic(mods) | |
| || (! Modifier.isPrivate(mods) && inSamePackage(c, from))) | |
| { | |
| AccessController.doPrivileged(new SetAccessibleAction(res)); | |
| return res; | |
| } | |
| } | |
| catch (NoSuchMethodException e) | |
| { | |
| } | |
| } | |
| return null; | |
| } | |
| private void cacheMethods() | |
| { | |
| Method[] methods = forClass().getDeclaredMethods(); | |
| readObjectMethod = findMethod(methods, "readObject", | |
| new Class[] { ObjectInputStream.class }, | |
| Void.TYPE, true); | |
| writeObjectMethod = findMethod(methods, "writeObject", | |
| new Class[] { ObjectOutputStream.class }, | |
| Void.TYPE, true); | |
| // readResolve and writeReplace can be in parent classes, as long as they | |
| // are accessible from this class. | |
| readResolveMethod = findAccessibleMethod("readResolve", forClass()); | |
| writeReplaceMethod = findAccessibleMethod("writeReplace", forClass()); | |
| } | |
| private ObjectStreamClass(Class cl) | |
| { | |
| uid = 0; | |
| flags = 0; | |
| isProxyClass = Proxy.isProxyClass(cl); | |
| clazz = cl; | |
| cacheMethods(); | |
| name = cl.getName(); | |
| setFlags(cl); | |
| setFields(cl); | |
| // to those class nonserializable, its uid field is 0 | |
| if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass) | |
| uid = getClassUID(cl); | |
| superClass = lookup(cl.getSuperclass()); | |
| } | |
| // Sets bits in flags according to features of CL. | |
| private void setFlags(Class cl) | |
| { | |
| if ((java.io.Externalizable.class).isAssignableFrom(cl)) | |
| flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; | |
| else if ((java.io.Serializable.class).isAssignableFrom(cl)) | |
| // only set this bit if CL is NOT Externalizable | |
| flags |= ObjectStreamConstants.SC_SERIALIZABLE; | |
| if (writeObjectMethod != null) | |
| flags |= ObjectStreamConstants.SC_WRITE_METHOD; | |
| if (cl.isEnum() || cl == Enum.class) | |
| flags |= ObjectStreamConstants.SC_ENUM; | |
| } | |
| // Sets fields to be a sorted array of the serializable fields of | |
| // clazz. | |
| private void setFields(Class cl) | |
| { | |
| SetAccessibleAction setAccessible = new SetAccessibleAction(); | |
| if (!isSerializable() || isExternalizable() || isEnum()) | |
| { | |
| fields = NO_FIELDS; | |
| return; | |
| } | |
| try | |
| { | |
| final Field f = | |
| cl.getDeclaredField("serialPersistentFields"); | |
| setAccessible.setMember(f); | |
| AccessController.doPrivileged(setAccessible); | |
| int modifiers = f.getModifiers(); | |
| if (Modifier.isStatic(modifiers) | |
| && Modifier.isFinal(modifiers) | |
| && Modifier.isPrivate(modifiers)) | |
| { | |
| fields = getSerialPersistentFields(cl); | |
| if (fields != null) | |
| { | |
| ObjectStreamField[] fieldsName = new ObjectStreamField[fields.length]; | |
| System.arraycopy(fields, 0, fieldsName, 0, fields.length); | |
| Arrays.sort (fieldsName, new Comparator() { | |
| public int compare(Object o1, Object o2) | |
| { | |
| ObjectStreamField f1 = (ObjectStreamField)o1; | |
| ObjectStreamField f2 = (ObjectStreamField)o2; | |
| return f1.getName().compareTo(f2.getName()); | |
| } | |
| }); | |
| for (int i=1; i < fields.length; i++) | |
| { | |
| if (fieldsName[i-1].getName().equals(fieldsName[i].getName())) | |
| { | |
| fields = INVALID_FIELDS; | |
| return; | |
| } | |
| } | |
| Arrays.sort (fields); | |
| // Retrieve field reference. | |
| for (int i=0; i < fields.length; i++) | |
| { | |
| try | |
| { | |
| fields[i].lookupField(cl); | |
| } | |
| catch (NoSuchFieldException _) | |
| { | |
| fields[i].setToSet(false); | |
| } | |
| } | |
| calculateOffsets(); | |
| return; | |
| } | |
| } | |
| } | |
| catch (NoSuchFieldException ignore) | |
| { | |
| } | |
| catch (IllegalAccessException ignore) | |
| { | |
| } | |
| int num_good_fields = 0; | |
| Field[] all_fields = cl.getDeclaredFields(); | |
| int modifiers; | |
| // set non-serializable fields to null in all_fields | |
| for (int i = 0; i < all_fields.length; i++) | |
| { | |
| modifiers = all_fields[i].getModifiers(); | |
| if (Modifier.isTransient(modifiers) | |
| || Modifier.isStatic(modifiers)) | |
| all_fields[i] = null; | |
| else | |
| num_good_fields++; | |
| } | |
| // make a copy of serializable (non-null) fields | |
| fields = new ObjectStreamField[ num_good_fields ]; | |
| for (int from = 0, to = 0; from < all_fields.length; from++) | |
| if (all_fields[from] != null) | |
| { | |
| final Field f = all_fields[from]; | |
| setAccessible.setMember(f); | |
| AccessController.doPrivileged(setAccessible); | |
| fields[to] = new ObjectStreamField(all_fields[from]); | |
| to++; | |
| } | |
| Arrays.sort(fields); | |
| // Make sure we don't have any duplicate field names | |
| // (Sun JDK 1.4.1. throws an Internal Error as well) | |
| for (int i = 1; i < fields.length; i++) | |
| { | |
| if(fields[i - 1].getName().equals(fields[i].getName())) | |
| throw new InternalError("Duplicate field " + | |
| fields[i].getName() + " in class " + cl.getName()); | |
| } | |
| calculateOffsets(); | |
| } | |
| // Returns the serial version UID defined by class, or if that | |
| // isn't present, calculates value of serial version UID. | |
| private long getClassUID(Class cl) | |
| { | |
| try | |
| { | |
| // Use getDeclaredField rather than getField, since serialVersionUID | |
| // may not be public AND we only want the serialVersionUID of this | |
| // class, not a superclass or interface. | |
| final Field suid = cl.getDeclaredField("serialVersionUID"); | |
| SetAccessibleAction setAccessible = new SetAccessibleAction(suid); | |
| AccessController.doPrivileged(setAccessible); | |
| int modifiers = suid.getModifiers(); | |
| if (Modifier.isStatic(modifiers) | |
| && Modifier.isFinal(modifiers) | |
| && suid.getType() == Long.TYPE) | |
| return suid.getLong(null); | |
| } | |
| catch (NoSuchFieldException ignore) | |
| { | |
| } | |
| catch (IllegalAccessException ignore) | |
| { | |
| } | |
| // cl didn't define serialVersionUID, so we have to compute it | |
| try | |
| { | |
| MessageDigest md; | |
| try | |
| { | |
| md = MessageDigest.getInstance("SHA"); | |
| } | |
| catch (NoSuchAlgorithmException e) | |
| { | |
| // If a provider already provides SHA, use it; otherwise, use this. | |
| Gnu gnuProvider = new Gnu(); | |
| Security.addProvider(gnuProvider); | |
| md = MessageDigest.getInstance("SHA"); | |
| } | |
| DigestOutputStream digest_out = | |
| new DigestOutputStream(nullOutputStream, md); | |
| DataOutputStream data_out = new DataOutputStream(digest_out); | |
| data_out.writeUTF(cl.getName()); | |
| int modifiers = cl.getModifiers(); | |
| // just look at interesting bits | |
| modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL | |
| | Modifier.INTERFACE | Modifier.PUBLIC); | |
| data_out.writeInt(modifiers); | |
| // Pretend that an array has no interfaces, because when array | |
| // serialization was defined (JDK 1.1), arrays didn't have it. | |
| if (! cl.isArray()) | |
| { | |
| Class[] interfaces = cl.getInterfaces(); | |
| Arrays.sort(interfaces, interfaceComparator); | |
| for (int i = 0; i < interfaces.length; i++) | |
| data_out.writeUTF(interfaces[i].getName()); | |
| } | |
| Field field; | |
| Field[] fields = cl.getDeclaredFields(); | |
| Arrays.sort(fields, memberComparator); | |
| for (int i = 0; i < fields.length; i++) | |
| { | |
| field = fields[i]; | |
| modifiers = field.getModifiers(); | |
| if (Modifier.isPrivate(modifiers) | |
| && (Modifier.isStatic(modifiers) | |
| || Modifier.isTransient(modifiers))) | |
| continue; | |
| data_out.writeUTF(field.getName()); | |
| data_out.writeInt(modifiers); | |
| data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); | |
| } | |
| // write class initializer method if present | |
| if (VMObjectStreamClass.hasClassInitializer(cl)) | |
| { | |
| data_out.writeUTF("<clinit>"); | |
| data_out.writeInt(Modifier.STATIC); | |
| data_out.writeUTF("()V"); | |
| } | |
| Constructor constructor; | |
| Constructor[] constructors = cl.getDeclaredConstructors(); | |
| Arrays.sort (constructors, memberComparator); | |
| for (int i = 0; i < constructors.length; i++) | |
| { | |
| constructor = constructors[i]; | |
| modifiers = constructor.getModifiers(); | |
| if (Modifier.isPrivate(modifiers)) | |
| continue; | |
| data_out.writeUTF("<init>"); | |
| data_out.writeInt(modifiers); | |
| // the replacement of '/' with '.' was needed to make computed | |
| // SUID's agree with those computed by JDK | |
| data_out.writeUTF | |
| (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); | |
| } | |
| Method method; | |
| Method[] methods = cl.getDeclaredMethods(); | |
| Arrays.sort(methods, memberComparator); | |
| for (int i = 0; i < methods.length; i++) | |
| { | |
| method = methods[i]; | |
| modifiers = method.getModifiers(); | |
| if (Modifier.isPrivate(modifiers)) | |
| continue; | |
| data_out.writeUTF(method.getName()); | |
| data_out.writeInt(modifiers); | |
| // the replacement of '/' with '.' was needed to make computed | |
| // SUID's agree with those computed by JDK | |
| data_out.writeUTF | |
| (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); | |
| } | |
| data_out.close(); | |
| byte[] sha = md.digest(); | |
| long result = 0; | |
| int len = sha.length < 8 ? sha.length : 8; | |
| for (int i = 0; i < len; i++) | |
| result += (long) (sha[i] & 0xFF) << (8 * i); | |
| return result; | |
| } | |
| catch (NoSuchAlgorithmException e) | |
| { | |
| throw new RuntimeException | |
| ("The SHA algorithm was not found to use in computing the Serial Version UID for class " | |
| + cl.getName(), e); | |
| } | |
| catch (IOException ioe) | |
| { | |
| throw new RuntimeException(ioe); | |
| } | |
| } | |
| /** | |
| * Returns the value of CLAZZ's private static final field named | |
| * `serialPersistentFields'. It performs some sanity checks before | |
| * returning the real array. Besides, the returned array is a clean | |
| * copy of the original. So it can be modified. | |
| * | |
| * @param clazz Class to retrieve 'serialPersistentFields' from. | |
| * @return The content of 'serialPersistentFields'. | |
| */ | |
| private ObjectStreamField[] getSerialPersistentFields(Class clazz) | |
| throws NoSuchFieldException, IllegalAccessException | |
| { | |
| ObjectStreamField[] fieldsArray = null; | |
| ObjectStreamField[] o; | |
| // Use getDeclaredField rather than getField for the same reason | |
| // as above in getDefinedSUID. | |
| Field f = clazz.getDeclaredField("serialPersistentFields"); | |
| f.setAccessible(true); | |
| int modifiers = f.getModifiers(); | |
| if (!(Modifier.isStatic(modifiers) && | |
| Modifier.isFinal(modifiers) && | |
| Modifier.isPrivate(modifiers))) | |
| return null; | |
| o = (ObjectStreamField[]) f.get(null); | |
| if (o == null) | |
| return null; | |
| fieldsArray = new ObjectStreamField[ o.length ]; | |
| System.arraycopy(o, 0, fieldsArray, 0, o.length); | |
| return fieldsArray; | |
| } | |
| /** | |
| * Returns a new instance of the Class this ObjectStreamClass corresponds | |
| * to. | |
| * Note that this should only be used for Externalizable classes. | |
| * | |
| * @return A new instance. | |
| */ | |
| Externalizable newInstance() throws InvalidClassException | |
| { | |
| synchronized(this) | |
| { | |
| if (constructor == null) | |
| { | |
| try | |
| { | |
| final Constructor c = clazz.getConstructor(new Class[0]); | |
| AccessController.doPrivileged(new PrivilegedAction() | |
| { | |
| public Object run() | |
| { | |
| c.setAccessible(true); | |
| return null; | |
| } | |
| }); | |
| constructor = c; | |
| } | |
| catch(NoSuchMethodException x) | |
| { | |
| throw new InvalidClassException(clazz.getName(), | |
| "No public zero-argument constructor"); | |
| } | |
| } | |
| } | |
| try | |
| { | |
| return (Externalizable)constructor.newInstance(null); | |
| } | |
| catch(Exception x) | |
| { | |
| throw (InvalidClassException) | |
| new InvalidClassException(clazz.getName(), | |
| "Unable to instantiate").initCause(x); | |
| } | |
| } | |
| public static final ObjectStreamField[] NO_FIELDS = {}; | |
| private static Hashtable classLookupTable = new Hashtable(); | |
| private static final NullOutputStream nullOutputStream = new NullOutputStream(); | |
| private static final Comparator interfaceComparator = new InterfaceComparator(); | |
| private static final Comparator memberComparator = new MemberComparator(); | |
| private static final | |
| Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; | |
| private ObjectStreamClass superClass; | |
| private Class clazz; | |
| private String name; | |
| private long uid; | |
| private byte flags; | |
| // this field is package protected so that ObjectInputStream and | |
| // ObjectOutputStream can access it directly | |
| ObjectStreamField[] fields; | |
| // these are accessed by ObjectIn/OutputStream | |
| int primFieldSize = -1; // -1 if not yet calculated | |
| int objectFieldCount; | |
| Method readObjectMethod; | |
| Method readResolveMethod; | |
| Method writeReplaceMethod; | |
| Method writeObjectMethod; | |
| boolean realClassIsSerializable; | |
| boolean realClassIsExternalizable; | |
| ObjectStreamField[] fieldMapping; | |
| Constructor firstNonSerializableParentConstructor; | |
| private Constructor constructor; // default constructor for Externalizable | |
| boolean isProxyClass = false; | |
| // This is probably not necessary because this class is special cased already | |
| // but it will avoid showing up as a discrepancy when comparing SUIDs. | |
| private static final long serialVersionUID = -6120832682080437368L; | |
| // interfaces are compared only by name | |
| private static final class InterfaceComparator implements Comparator | |
| { | |
| public int compare(Object o1, Object o2) | |
| { | |
| return ((Class) o1).getName().compareTo(((Class) o2).getName()); | |
| } | |
| } | |
| // Members (Methods and Constructors) are compared first by name, | |
| // conflicts are resolved by comparing type signatures | |
| private static final class MemberComparator implements Comparator | |
| { | |
| public int compare(Object o1, Object o2) | |
| { | |
| Member m1 = (Member) o1; | |
| Member m2 = (Member) o2; | |
| int comp = m1.getName().compareTo(m2.getName()); | |
| if (comp == 0) | |
| return TypeSignature.getEncodingOfMember(m1). | |
| compareTo(TypeSignature.getEncodingOfMember(m2)); | |
| else | |
| return comp; | |
| } | |
| } | |
| } |