Merged gcj-eclipse branch to trunk.

From-SVN: r120621
This commit is contained in:
Tom Tromey 2007-01-09 19:58:05 +00:00
parent c648dedbde
commit 97b8365caf
17478 changed files with 606493 additions and 100744 deletions

View 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;
}
/**
* 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()
{
Method[] methods = forClass().getDeclaredMethods();
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);
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());
// 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)
{
long result = 0;
Long cache = (Long) uidCache.get(cl);
if (cache != null)
result = cache.longValue();
else
{
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));
}
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)
return suid.getLong(null);
}
catch (NoSuchFieldException ignore)
{
// 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
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
{
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;
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");
}
catch (IOException ioe)
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())
{
throw new RuntimeException(ioe);
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;
}
/**
@ -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;