[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
FYI: Serialization
From: |
Guilhem Lavaux |
Subject: |
FYI: Serialization |
Date: |
Sun, 28 Dec 2003 18:16:18 +0100 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.5) Gecko/20031007 |
Hi,
Here is a patch which merges kaffe serialization code into classpath.
The next one will reindent the code for clarity.
Guilhem.
Index: ChangeLog
===================================================================
RCS file: /cvsroot/classpath/classpath/ChangeLog,v
retrieving revision 1.1731
diff -u -r1.1731 ChangeLog
--- ChangeLog 28 Dec 2003 13:45:15 -0000 1.1731
+++ ChangeLog 28 Dec 2003 17:13:48 -0000
@@ -1,5 +1,27 @@
2003-12-28 Guilhem Lavaux <address@hidden>
+ * java/io/ObjectOutputStream.java
+ (writeClassDescriptor): Externalizable class must also write their
+ hierarchy.
+ (putFields): Added some text message for an exception.
+
+ * java/io/ObjectInputStream.java
+ (readClassDescriptor): Documented. Better field checking.
+ (defaultReadObject): Readjusted messages in exceptions.
+ (currentLoader): Documented.
+ (inputGetObjectStreamClasses): Changed indentation.
+ (readFields): Documented. Changed logic to improve error reporting and
+ behaviour compared to the JDK.
+ (readFields): Fixed behaviour.
+ (getField): Documented. Fixed behaviour.
+ (callReadMethod): Invalidate read fields.
+
+ * java/io/ObjectStreamClass.java
+ (setFields): Fix to avoid a NullPointerException.
+ (getSerialPersistentFields): Documented. Improved modifier checking.
+
+2003-12-28 Guilhem Lavaux <address@hidden>
+
* java/io/LineNumberReader.java
(countLines): Removed.
(fill): New private method.
Index: java/io/ObjectInputStream.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/io/ObjectInputStream.java,v
retrieving revision 1.33
diff -u -r1.33 ObjectInputStream.java
--- java/io/ObjectInputStream.java 2 Dec 2003 18:35:51 -0000 1.33
+++ java/io/ObjectInputStream.java 28 Dec 2003 17:13:49 -0000
@@ -47,6 +47,7 @@
import java.util.Hashtable;
import java.util.Vector;
+
import gnu.java.io.ObjectIdentityWrapper;
import gnu.java.lang.reflect.TypeSignature;
import java.lang.reflect.Field;
@@ -417,6 +418,22 @@
return ret_val;
}
+ /**
+ * This method reads a class descriptor from the real input stream
+ * and use these data to create a new instance of ObjectStreamClass.
+ * Fields are sorted and ordered for the real read which occurs for
+ * each instance of the described class. Be aware that if you call that
+ * method you must ensure that the stream is synchronized, in the other
+ * case it may be completely desynchronized.
+ *
+ * @return A new instance of ObjectStreamClass containing the freshly
+ * created descriptor.
+ * @throws ClassNotFoundException if the required class to build the
+ * descriptor has not been found in the system.
+ * @throws IOException An input/output error occured.
+ * @throws InvalidClassException If there was a compatibility problem
+ * between the class present in the system and the serialized class.
+ */
protected ObjectStreamClass readClassDescriptor ()
throws ClassNotFoundException, IOException
{
@@ -443,22 +460,44 @@
dumpElementln (field_name);
String class_name;
+ // If the type code is an array or an object we must
+ // decode a String here. In the other case we convert
+ // the type code and pass it to ObjectStreamField.
+ // Type codes are decoded by gnu.java.lang.reflect.TypeSignature.
if (type_code == 'L' || type_code == '[')
class_name = (String)readObject ();
else
class_name = String.valueOf (type_code);
- // There're many cases you can't get java.lang.Class from
- // typename if your context class loader can't load it,
- // then use typename to construct the field
fields[i] =
- new ObjectStreamField (field_name, class_name);
+ new ObjectStreamField(field_name, class_name, currentLoader());
}
- Class clazz = resolveClass (osc);
+ /* Now that fields have been read we may resolve the class
+ * (and read annotation if needed). */
+ Class clazz = resolveClass(osc);
+
+ for (int i = 0; i < field_count; i++)
+ {
+ Field f;
+
+ try
+ {
+ f = clazz.getDeclaredField(fields[i].getName());
+ if (f != null && !f.getType().equals(fields[i].getType()))
+ throw new InvalidClassException
+ ("invalid field type for " + fields[i].getName() + " in class "
+ + name + " (requested was \"" + fields[i].getType()
+ + " and found \"" + f.getType() + "\")");
+ }
+ catch (NoSuchFieldException _)
+ {
+ }
+ }
+
boolean oldmode = setBlockDataMode (true);
- osc.setClass (clazz, lookupClass (clazz.getSuperclass()));
- classLookupTable.put (clazz, osc);
+ osc.setClass (clazz, lookupClass(clazz.getSuperclass()));
+ classLookupTable.put(clazz, osc);
setBlockDataMode (oldmode);
return osc;
@@ -487,10 +526,13 @@
throws ClassNotFoundException, IOException, NotActiveException
{
if (this.currentObject == null || this.currentObjectStreamClass == null)
- throw new NotActiveException ("defaultReadObject called by non-active
class and/or object");
+ throw new NotActiveException("defaultReadObject called by non-active"
+ + " class and/or object");
if (fieldsAlreadyRead)
- throw new NotActiveException ("defaultReadObject called but fields
already read from stream (by defaultReadObject or readFields)");
+ throw new NotActiveException("defaultReadObject called but fields "
+ + "already read from stream (by "
+ + "defaultReadObject or readFields)");
boolean oldmode = setBlockDataMode(false);
readFields (this.currentObject, this.currentObjectStreamClass);
@@ -523,10 +565,12 @@
throws InvalidObjectException, NotActiveException
{
if (this.currentObject == null || this.currentObjectStreamClass == null)
- throw new NotActiveException ("registerValidation called by non-active
class and/or object");
+ throw new NotActiveException ("registerValidation called by non-active "
+ +"class and/or object");
if (validator == null)
- throw new InvalidObjectException ("attempt to add a null
ObjectInputValidation object");
+ throw new InvalidObjectException ("attempt to add a null "
+ +"ObjectInputValidation object");
this.validators.addElement (new ValidatorAndPriority (validator,
priority));
@@ -552,16 +596,24 @@
protected Class resolveClass (ObjectStreamClass osc)
throws ClassNotFoundException, IOException
{
- return Class.forName (osc.getName(), true, currentLoader());
+ return Class.forName(osc.getName(), true, currentLoader());
}
-
- private ClassLoader currentLoader ()
+
+ /**
+ * This method invokes the method currentClassLoader for the
+ * current security manager (or build an empty one if it is not
+ * present).
+ *
+ * @return The most recent non-system ClassLoader on the execution stack.
+ * @see java.lang.SecurityManager#currentClassLoader()
+ */
+ private ClassLoader currentLoader()
{
SecurityManager sm = System.getSecurityManager ();
if (sm == null)
sm = new SecurityManager () {};
-
- return currentClassLoader (sm);
+
+ return currentClassLoader(sm);
}
/**
@@ -571,14 +623,14 @@
* the behaviour detected in the JDK by Kaffe's team.
*
* @param clazz Class to lookup in the hash table or for which
- * we must build a descriptor.
+ * we must build a descriptor.
* @return A valid instance of ObjectStreamClass corresponding
* to the specified class.
*/
private ObjectStreamClass lookupClass (Class clazz)
{
ObjectStreamClass oclazz;
-
+
oclazz = (ObjectStreamClass) classLookupTable.get(clazz);
if (oclazz == null)
return ObjectStreamClass.lookup (clazz);
@@ -590,8 +642,8 @@
* Reconstruct class hierarchy the same way
* address@hidden
java.io.ObjectStreamClass.getObjectStreamClasses(java.lang.Class)} does
* but using lookupClass instead of ObjectStreamClass.lookup. This
- * dup is necessary localize the lookup table. Hopefully some future
rewritings will
- * be able to prevent this.
+ * dup is necessary localize the lookup table. Hopefully some future
+ * rewritings will be able to prevent this.
*
* @param clazz This is the class for which we want the hierarchy.
*
@@ -607,23 +659,23 @@
if (osc == null)
return new ObjectStreamClass[0];
else
- {
- Vector oscs = new Vector ();
-
- while (osc != null)
{
- oscs.addElement (osc);
- osc = osc.getSuper ();
- }
+ Vector oscs = new Vector();
- int count = oscs.size ();
- ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ];
+ while (osc != null)
+ {
+ oscs.addElement(osc);
+ osc = osc.getSuper();
+ }
- for (int i = count - 1; i >= 0; i--)
- sorted_oscs[ count - i - 1 ] = (ObjectStreamClass)oscs.elementAt (i);
+ int count = oscs.size();
+ ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count];
- return sorted_oscs;
- }
+ for (int i = count - 1; i >= 0; i--)
+ sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i);
+
+ return sorted_oscs;
+ }
}
/**
@@ -983,14 +1035,31 @@
throws IOException, IllegalArgumentException;
}
+ /**
+ * This method should be called by a method called 'readObject' in the
+ * deserializing class (if present). It cannot (and should not)be called
+ * outside of it. Its goal is to read all fields in the real input stream
+ * and keep them accessible through the address@hidden #GetField} class.
Calling
+ * this method will not alterate the deserializing object.
+ *
+ * @return A valid freshly created 'GetField' instance to get access to
+ * the deserialized stream.
+ * @throws IOException An input/output exception occured.
+ * @throws ClassNotFoundException
+ * @throws NotActiveException
+ */
public GetField readFields ()
throws IOException, ClassNotFoundException, NotActiveException
{
if (this.currentObject == null || this.currentObjectStreamClass == null)
throw new NotActiveException ("readFields called by non-active class
and/or object");
+ if (prereadFields != null)
+ return prereadFields;
+
if (fieldsAlreadyRead)
- throw new NotActiveException ("readFields called but fields already read
from stream (by defaultReadObject or readFields)");
+ throw new NotActiveException ("readFields called but fields already read
from"
+ + " stream (by defaultReadObject or
readFields)");
final ObjectStreamClass clazz = this.currentObjectStreamClass;
final byte[] prim_field_data = new byte[clazz.primFieldSize];
@@ -1005,7 +1074,7 @@
objs[i] = readObject ();
setBlockDataMode (oldmode);
- return new GetField ()
+ prereadFields = new GetField()
{
public ObjectStreamClass getObjectStreamClass ()
{
@@ -1015,7 +1084,31 @@
public boolean defaulted (String name)
throws IOException, IllegalArgumentException
{
- return clazz.getField (name) == null;
+ ObjectStreamField f = clazz.getField(name);
+
+ /* First if we have a serialized field use the descriptor */
+ if (f != null)
+ {
+ /* It is in serialPersistentFields but setClass tells us
+ * it should not be set. This value is defaulted.
+ */
+ if (f.isPersistent() && !f.isToSet())
+ return true;
+
+ return false;
+ }
+
+ /* This is not a serialized field. There should be
+ * a default value only if the field really exists.
+ */
+ try
+ {
+ return (clazz.forClass().getDeclaredField (name) != null);
+ }
+ catch (NoSuchFieldException e)
+ {
+ throw new IllegalArgumentException (e.getMessage());
+ }
}
public boolean get (String name, boolean defvalue)
@@ -1157,24 +1250,76 @@
throws IllegalArgumentException
{
ObjectStreamField field = clazz.getField (name);
+ boolean illegal = false;
- if (field == null)
- return null;
-
- Class field_type = field.getType ();
-
- if (type == field_type ||
- (type == null && ! field_type.isPrimitive ()))
- return field;
-
- throw new IllegalArgumentException ("Field requested is of type "
- + field_type.getName ()
- + ", but requested type was "
- + (type == null ?
- "Object" : type.getName ()));
+ try
+ {
+ try
+ {
+ Class field_type = field.getType();
+
+ if (type == field_type ||
+ (type == null && !field_type.isPrimitive()))
+ {
+ /* See defaulted */
+ return field;
+ }
+
+ illegal = true;
+ throw new IllegalArgumentException
+ ("Field requested is of type "
+ + field_type.getName()
+ + ", but requested type was "
+ + (type == null ? "Object" : type.getName()));
+ }
+ catch (NullPointerException _)
+ {
+ /* Here we catch NullPointerException, because it may
+ only come from the call 'field.getType()'. If field
+ is null, we have to return null and classpath ethic
+ say we must try to avoid 'if (xxx == null)'.
+ */
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw e;
+ }
+
+ return null;
+ }
+ finally
+ {
+ /* If this is an unassigned field we should return
+ * the default value.
+ */
+ if (!illegal && field != null && !field.isToSet() &&
field.isPersistent())
+ return null;
+
+ /* We do not want to modify transient fields. They should
+ * be left to 0.
+ */
+ try
+ {
+ Field f = clazz.forClass().getDeclaredField (name);
+ if (Modifier.isTransient(f.getModifiers()))
+ throw new IllegalArgumentException
+ ("no such field (non transient) " + name);
+ if (field == null && f.getType() != type)
+ throw new IllegalArgumentException
+ ("Invalid requested type for field " + name);
+ }
+ catch (NoSuchFieldException e)
+ {
+ if (field == null)
+ throw new IllegalArgumentException(e.getMessage());
+ }
+
+ }
}
};
+ fieldsAlreadyRead = true;
+ return prereadFields;
}
/**
@@ -1369,7 +1514,7 @@
{
ObjectStreamField[] stream_fields = stream_osc.fields;
ObjectStreamField[] real_fields =
- lookupClass (stream_osc.forClass ()).fields;
+ lookupClass(stream_osc.forClass()).fields;
boolean default_initialize, set_value;
String field_name = null;
@@ -1424,6 +1569,15 @@
}
}
+ if (stream_field.getOffset() < 0)
+ {
+ default_initialize = true;
+ set_value = false;
+ }
+
+ if (!stream_field.isToSet())
+ set_value = false;
+
try
{
if (type == Boolean.TYPE)
@@ -1571,10 +1725,23 @@
*/
private static native ClassLoader currentClassLoader (SecurityManager sm);
- private static Field getField (Class klass, String name)
+ /**
+ * This method tries to access a precise field called in the
+ * specified class. Before accessing the field, it tries to
+ * gain control on this field. If the field is either declared as
+ * not persistent or transient then it returns null
+ * immediately.
+ *
+ * @param klass Class to get the field from.
+ * @param name Name of the field to access.
+ * @return Field instance representing the requested field.
+ * @throws NoSuchFieldException if the field does not exist.
+ */
+ private Field getField(Class klass, String name)
throws java.lang.NoSuchFieldException
{
final Field f = klass.getDeclaredField(name);
+ ObjectStreamField sf = lookupClass(klass).getField(name);
AccessController.doPrivileged(new PrivilegedAction()
{
@@ -1584,6 +1751,14 @@
return null;
}
});
+
+ /* We do not want to modify transient fields. They should
+ * be left to 0.
+ * N.B.: Not valid if the field is in serialPersistentFields.
+ */
+ if (Modifier.isTransient(f.getModifiers()) && !sf.isPersistent())
+ return null;
+
return f;
}
@@ -1633,6 +1808,9 @@
throw new IOException ("Failure invoking readObject() on " +
klass + ": " + x.getClass().getName());
}
+
+ // Invalidate fields which has been read through readFields.
+ prereadFields = null;
}
private native Object allocateObject (Class clazz)
@@ -1916,6 +2094,7 @@
private boolean fieldsAlreadyRead;
private Vector validators;
private Hashtable classLookupTable;
+ private GetField prereadFields;
private static boolean dump;
Index: java/io/ObjectOutputStream.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/io/ObjectOutputStream.java,v
retrieving revision 1.40
diff -u -r1.40 ObjectOutputStream.java
--- java/io/ObjectOutputStream.java 2 Dec 2003 18:35:52 -0000 1.40
+++ java/io/ObjectOutputStream.java 28 Dec 2003 17:13:50 -0000
@@ -410,8 +410,8 @@
annotateClass (osc.forClass ());
setBlockDataMode (oldmode);
realOutput.writeByte (TC_ENDBLOCKDATA);
-
- if (osc.isSerializable ())
+
+ if (osc.isSerializable () || osc.isExternalizable())
writeObject (osc.getSuper ());
else
writeObject (null);
@@ -989,8 +989,9 @@
ObjectStreamField field = getField (name);
if (value != null &&
- ! field.getType ().isAssignableFrom (value.getClass ()))
- throw new IllegalArgumentException ();
+ ! field.getType ().isAssignableFrom (value.getClass ()))
+ throw new IllegalArgumentException ("Class " + value.getClass() +
+ " cannot be cast to " +
field.getType());
objs[field.getOffset ()] = value;
}
Index: java/io/ObjectStreamClass.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/io/ObjectStreamClass.java,v
retrieving revision 1.25
diff -u -r1.25 ObjectStreamClass.java
--- java/io/ObjectStreamClass.java 2 Dec 2003 18:35:52 -0000 1.25
+++ java/io/ObjectStreamClass.java 28 Dec 2003 17:13:50 -0000
@@ -518,9 +518,12 @@
&& Modifier.isPrivate (modifiers))
{
fields = getSerialPersistentFields (cl);
- Arrays.sort (fields);
- calculateOffsets ();
- return;
+ if (fields != null)
+ {
+ Arrays.sort (fields);
+ calculateOffsets ();
+ return;
+ }
}
}
catch (NoSuchFieldException ignore)
@@ -701,18 +704,41 @@
}
}
- // Returns the value of CLAZZ's private static final field named
- // `serialPersistentFields'.
+ /**
+ * 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[] o = null;
+ 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);
- return (ObjectStreamField[])f.get (null);
+
+ 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;
}
public static final ObjectStreamField[] NO_FIELDS = {};
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- FYI: Serialization,
Guilhem Lavaux <=