OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / io / ObjectStreamClass.java
index abb26d8..52a1ad4 100644 (file)
@@ -1,6 +1,6 @@
 /* ObjectStreamClass.java -- Class used to write class information
    about serialized objects.
-   Copyright (C) 1998, 1999, 2000, 2001, 2003  Free Software Foundation, Inc.
+   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2005  Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -59,8 +59,14 @@ import java.security.Security;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.Hashtable;
-import java.util.Vector;
 
+/**
+ * @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];
@@ -80,7 +86,7 @@ public class ObjectStreamClass implements Serializable
    *
    * @see java.io.Serializable
    */
-  public static ObjectStreamClass lookup(Class cl)
+  public static ObjectStreamClass lookup(Class<?> cl)
   {
     if (cl == null)
       return null;
@@ -132,7 +138,7 @@ public class ObjectStreamClass implements Serializable
    *
    * @see java.io.ObjectInputStream
    */
-  public Class forClass()
+  public Class<?> forClass()
   {
     return clazz;
   }
@@ -235,37 +241,45 @@ public class ObjectStreamClass implements Serializable
     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)
+  /**
+   * 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 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;
+    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.
@@ -298,7 +312,7 @@ public class ObjectStreamClass implements Serializable
    * already set UID is found.
    */
   void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException
-  {
+  {hierarchy = null;
     this.clazz = cl;
 
     cacheMethods();
@@ -309,8 +323,8 @@ public class ObjectStreamClass implements Serializable
     else
       {
        // Check that the actual UID of the resolved class matches the UID from 
-       // the stream.    
-       if (uid != class_uid)
+       // the stream. Mismatches for array classes are ignored.
+       if (!cl.isArray() && uid != class_uid)
          {
            String msg = cl + 
              ": Local class not compatible: stream serialVersionUID="
@@ -425,6 +439,7 @@ public class ObjectStreamClass implements Serializable
   void setSuperclass (ObjectStreamClass osc)
   {
     superClass = osc;
+    hierarchy = null;
   }
 
   void calculateOffsets()
@@ -547,21 +562,62 @@ outer:
     return null;
   }
 
-  private void cacheMethods()
+  /**
+   * 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)
   {
-    Method[] methods = forClass().getDeclaredMethods();
+    ClassLoader l = cl.getClassLoader();
+    return 
+      (   l == null                             /* boot loader */       ) 
+      || (l == ClassLoader.getSystemClassLoader() /* application loader */);
+  } 
 
-    readObjectMethod = findMethod(methods, "readObject",
-                                 new Class[] { ObjectInputStream.class },
-                                 Void.TYPE, true);
-    writeObjectMethod = findMethod(methods, "writeObject",
-                                   new Class[] { ObjectOutputStream.class },
-                                   Void.TYPE, true);
+  static Hashtable methodCache = new Hashtable(); 
+  
+  static final Class[] readObjectSignature  = { ObjectInputStream.class };
+  static final Class[] writeObjectSignature = { ObjectOutputStream.class };
 
-    // 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 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)
@@ -713,152 +769,208 @@ outer:
     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)
   {
-    try
+    long result = 0;
+    Long cache = (Long) uidCache.get(cl);
+    if (cache != null)
+      result = cache.longValue(); 
+    else
       {
-       // 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);
+        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,new Long(result));
       }
-    catch (NoSuchFieldException ignore)
+    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();
       }
 
-    // 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;
+  }
 
-       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)
       {
-       throw new RuntimeException
-         ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
-          + cl.getName(), 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");
       }
-    catch (IOException ioe)
+    
+    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++)
       {
-       throw new RuntimeException(ioe);
+        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;
   }
 
   /**
@@ -948,7 +1060,8 @@ outer:
 
   public static final ObjectStreamField[] NO_FIELDS = {};
 
-  private static Hashtable classLookupTable = new Hashtable();
+  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();
@@ -956,7 +1069,7 @@ outer:
     Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
 
   private ObjectStreamClass superClass;
-  private Class clazz;
+  private Class<?> clazz;
   private String name;
   private long uid;
   private byte flags;