mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1162 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1162 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* ObjectStreamClass.java -- Class used to write class information
 | |
|    about serialized objects.
 | |
|    Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005  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;
 | |
| 
 | |
| /**
 | |
|  * @author Tom Tromey (tromey@redhat.com)
 | |
|  * @author Jeroen Frijters (jeroen@frijters.net)
 | |
|  * @author Guilhem Lavaux (guilhem@kaffe.org)
 | |
|  * @author Michael Koch (konqueror@gmx.de)
 | |
|  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 | |
|  */
 | |
| 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 = 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 the class represented by this and the class
 | |
|    * represented by this itself in order from most super to this.
 | |
|    * ObjectStreamClass[0] is the highest superclass of this that is
 | |
|    * serializable.
 | |
|    *
 | |
|    * The result of consecutive calls this hierarchy() will be the same
 | |
|    * array instance.
 | |
|    *
 | |
|    * @return an array of ObjectStreamClass representing the
 | |
|    * super-class hierarchy of serializable classes.
 | |
|    */
 | |
|   ObjectStreamClass[] hierarchy()
 | |
|   {
 | |
|     ObjectStreamClass[] result = hierarchy;
 | |
|     if (result == null)
 | |
|         {
 | |
|         int d = 0;
 | |
| 
 | |
|         for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper())
 | |
|           d++;
 | |
| 
 | |
|         result = new ObjectStreamClass[d];
 | |
| 
 | |
|         for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper())
 | |
|           {
 | |
|             result[--d] = osc;
 | |
|           }
 | |
| 
 | |
|         hierarchy = result;
 | |
|       }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Cache for hierarchy() result.
 | |
|    */
 | |
|   private ObjectStreamClass[] hierarchy = null;
 | |
| 
 | |
|   // 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
 | |
|   {hierarchy = null;
 | |
|     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. Mismatches for array classes are ignored.
 | |
|         if (!cl.isArray() && 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;
 | |
|     hierarchy = null;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Helper routine to check if a class was loaded by boot or
 | |
|    * application class loader.  Classes for which this is not the case
 | |
|    * should not be cached since caching prevent class file garbage
 | |
|    * collection.
 | |
|    *
 | |
|    * @param cl a class
 | |
|    *
 | |
|    * @return true if cl was loaded by boot or application class loader,
 | |
|    *         false if cl was loaded by a user class loader.
 | |
|    */
 | |
|   private static boolean loadedByBootOrApplicationClassLoader(Class cl)
 | |
|   {
 | |
|     ClassLoader l = cl.getClassLoader();
 | |
|     return
 | |
|       (   l == null                             /* boot loader */       )
 | |
|       || (l == ClassLoader.getSystemClassLoader() /* application loader */);
 | |
|   }
 | |
| 
 | |
|   static Hashtable methodCache = new Hashtable();
 | |
| 
 | |
|   static final Class[] readObjectSignature  = { ObjectInputStream.class };
 | |
|   static final Class[] writeObjectSignature = { ObjectOutputStream.class };
 | |
| 
 | |
|   private void cacheMethods()
 | |
|   {
 | |
|     Class cl = forClass();
 | |
|     Method[] cached = (Method[]) methodCache.get(cl);
 | |
|     if (cached == null)
 | |
|       {
 | |
|         cached = new Method[4];
 | |
|         Method[] methods = cl.getDeclaredMethods();
 | |
| 
 | |
|         cached[0] = findMethod(methods, "readObject",
 | |
|                                readObjectSignature,
 | |
|                                Void.TYPE, true);
 | |
|         cached[1] = findMethod(methods, "writeObject",
 | |
|                                writeObjectSignature,
 | |
|                                Void.TYPE, true);
 | |
| 
 | |
|         // readResolve and writeReplace can be in parent classes, as long as they
 | |
|         // are accessible from this class.
 | |
|         cached[2] = findAccessibleMethod("readResolve", cl);
 | |
|         cached[3] = findAccessibleMethod("writeReplace", cl);
 | |
| 
 | |
|         /* put in cache if classes not loaded by user class loader.
 | |
|          * For a user class loader, the cache may otherwise grow
 | |
|          * without limit.
 | |
|          */
 | |
|         if (loadedByBootOrApplicationClassLoader(cl))
 | |
|           methodCache.put(cl,cached);
 | |
|       }
 | |
|     readObjectMethod   = cached[0];
 | |
|     writeObjectMethod  = cached[1];
 | |
|     readResolveMethod  = cached[2];
 | |
|     writeReplaceMethod = cached[3];
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| 
 | |
| /* GCJ LOCAL */
 | |
|   // FIXME: This is a workaround for a fairly obscure bug that happens
 | |
|   // when reading a Proxy and then writing it back out again.  The
 | |
|   // result is that the ObjectStreamClass doesn't have its fields set,
 | |
|   // generating a NullPointerException.  Rather than this kludge we
 | |
|   // should probably fix the real bug, but it would require a fairly
 | |
|   // radical reorganization to do so.
 | |
|   final void ensureFieldsSet(Class cl)
 | |
|   {
 | |
|     if (! fieldsSet)
 | |
|       setFields(cl);
 | |
|   }
 | |
| /* END GCJ LOCAL */
 | |
| 
 | |
| 
 | |
|   // Sets fields to be a sorted array of the serializable fields of
 | |
|   // clazz.
 | |
|   private void setFields(Class cl)
 | |
|   {
 | |
| /* GCJ LOCAL */
 | |
|     fieldsSet = true;
 | |
| /* END GCJ LOCAL */
 | |
| 
 | |
|     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();
 | |
|   }
 | |
| 
 | |
|   static Hashtable uidCache = new Hashtable();
 | |
| 
 | |
|   // 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)
 | |
|   {
 | |
|     long result = 0;
 | |
|     Long cache = (Long) uidCache.get(cl);
 | |
|     if (cache != null)
 | |
|       result = cache.longValue();
 | |
|     else
 | |
|       {
 | |
|         // Note that we can't use Class.isEnum() here, because that returns
 | |
|         // false for java.lang.Enum and enum value sub classes.
 | |
|         if (Enum.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
 | |
|           {
 | |
|             // Spec says that enums and dynamic proxies have
 | |
|             // a serialVersionUID of 0L.
 | |
|             return 0L;
 | |
|           }
 | |
|         try
 | |
|           {
 | |
|             result = getClassUIDFromField(cl);
 | |
|           }
 | |
|         catch (NoSuchFieldException ignore)
 | |
|           {
 | |
|             try
 | |
|               {
 | |
|                 result = calculateClassUID(cl);
 | |
|               }
 | |
|             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);
 | |
|               }
 | |
|           }
 | |
| 
 | |
|         if (loadedByBootOrApplicationClassLoader(cl))
 | |
|           uidCache.put(cl,Long.valueOf(result));
 | |
|       }
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Search for a serialVersionUID field in the given class and read
 | |
|    * its value.
 | |
|    *
 | |
|    * @return the contents of the serialVersionUID field
 | |
|    *
 | |
|    * @throws NoSuchFieldException if such a field does not exist or is
 | |
|    * not static, not final, not of type Long or not accessible.
 | |
|    */
 | |
|   long getClassUIDFromField(Class cl)
 | |
|     throws NoSuchFieldException
 | |
|   {
 | |
|     long result;
 | |
| 
 | |
|     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)
 | |
|           result = suid.getLong(null);
 | |
|         else
 | |
|           throw new NoSuchFieldException();
 | |
|       }
 | |
|     catch (IllegalAccessException ignore)
 | |
|       {
 | |
|         throw new NoSuchFieldException();
 | |
|       }
 | |
| 
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Calculate class serial version UID for a class that does not
 | |
|    * define serialVersionUID:
 | |
|    *
 | |
|    * @param cl a class
 | |
|    *
 | |
|    * @return the calculated serial varsion UID.
 | |
|    *
 | |
|    * @throws NoSuchAlgorithmException if SHA algorithm not found
 | |
|    *
 | |
|    * @throws IOException if writing to the DigestOutputStream causes
 | |
|    * an IOException.
 | |
|    */
 | |
|   long calculateClassUID(Class cl)
 | |
|     throws NoSuchAlgorithmException, IOException
 | |
|   {
 | |
|     long result;
 | |
|     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();
 | |
|     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;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * 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();
 | |
|     }
 | |
|     catch(Exception x)
 | |
|     {
 | |
|         throw (InvalidClassException)
 | |
|             new InvalidClassException(clazz.getName(),
 | |
|                      "Unable to instantiate").initCause(x);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   public static final ObjectStreamField[] NO_FIELDS = {};
 | |
| 
 | |
|   private static Hashtable<Class,ObjectStreamClass> classLookupTable
 | |
|     = new Hashtable<Class,ObjectStreamClass>();
 | |
|   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;
 | |
| 
 | |
| /* GCJ LOCAL */
 | |
|   // True after setFields() has been called
 | |
|   private boolean fieldsSet = false;
 | |
| /* END GCJ LOCAL */
 | |
| 
 | |
|   // 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;
 | |
|     }
 | |
|   }
 | |
| }
 |