Index: modules/luni/src/main/java/java/io/ObjectInputStream.java
===================================================================
--- modules/luni/src/main/java/java/io/ObjectInputStream.java (revision 660154)
+++ modules/luni/src/main/java/java/io/ObjectInputStream.java (working copy)
@@ -702,8 +702,7 @@
ObjectStreamClass streamClass = ObjectStreamClass
.lookup(proxyClass);
streamClass.setLoadFields(new ObjectStreamField[0]);
- registerObjectRead(streamClass, Integer.valueOf(nextHandle()),
- false);
+ registerObjectRead(streamClass, nextHandle(), false);
checkedSetSuperClassDesc(streamClass, readClassDesc());
return streamClass;
case TC_REFERENCE:
@@ -1324,7 +1323,7 @@
int index = findStreamSuperclass(superclass, streamClassList,
lastIndex);
if (index == -1) {
- readObjectNoData(object, superclass);
+ readObjectNoData(object, superclass, ObjectStreamClass.lookupStreamClass(superclass));
} else {
for (int j = lastIndex; j <= index; j++) {
readObjectForClass(object, streamClassList.get(j));
@@ -1358,12 +1357,11 @@
return -1;
}
- private void readObjectNoData(Object object, Class> cl)
+ private void readObjectNoData(Object object, Class> cl, ObjectStreamClass classDesc)
throws ObjectStreamException {
- if (!ObjectStreamClass.isSerializable(cl)) {
+ if (!classDesc.isSerializable()) {
return;
}
- ObjectStreamClass classDesc = ObjectStreamClass.lookupStreamClass(cl);
if (classDesc.hasMethodReadObjectNoData()){
final Method readMethod = classDesc.getMethodReadObjectNoData();
try {
@@ -1501,7 +1499,7 @@
throw new InvalidClassException(Msg.getString("K00d1")); //$NON-NLS-1$
}
- Integer newHandle = Integer.valueOf(nextHandle());
+ Integer newHandle = nextHandle();
// Array size
int size = input.readInt();
@@ -1562,6 +1560,10 @@
// Array of Objects
Object[] objectArray = (Object[]) result;
for (int i = 0; i < size; i++) {
+ // TODO: This place is the opportunity for enhancement
+ // We can implement writing elements through fast-path,
+ // without setting up the context (see readObject()) for
+ // each element with public API
objectArray[i] = readObject();
}
}
@@ -1590,10 +1592,9 @@
ObjectStreamClass classDesc = readClassDesc();
if (classDesc != null) {
- Integer newHandle = Integer.valueOf(nextHandle());
Class> localClass = classDesc.forClass();
if (localClass != null) {
- registerObjectRead(localClass, newHandle, unshared);
+ registerObjectRead(localClass, nextHandle(), unshared);
}
return localClass;
}
@@ -1659,7 +1660,7 @@
ClassNotFoundException, IOException {
// read classdesc for Enum first
ObjectStreamClass classDesc = readEnumDesc();
- Integer newHandle = Integer.valueOf(nextHandle());
+ int newHandle = nextHandle();
// read name after class desc
String name;
byte tc = nextTC();
@@ -1705,7 +1706,7 @@
// subclasses during readClassDescriptor()
primitiveData = input;
Integer oldHandle = descriptorHandle;
- descriptorHandle = Integer.valueOf(nextHandle());
+ descriptorHandle = nextHandle();
ObjectStreamClass newClassDesc = readClassDescriptor();
registerObjectRead(newClassDesc, descriptorHandle, unshared);
descriptorHandle = oldHandle;
@@ -1795,8 +1796,7 @@
* descriptors. If called outside of readObject, the descriptorHandle
* might be null.
*/
- descriptorHandle = (null == descriptorHandle ? Integer
- .valueOf(nextHandle()) : descriptorHandle);
+ descriptorHandle = (null == descriptorHandle ? nextHandle() : descriptorHandle);
registerObjectRead(newClassDesc, descriptorHandle, false);
readFieldDescriptors(newClassDesc);
@@ -1817,6 +1817,9 @@
*/
protected Class> resolveProxyClass(String[] interfaceNames)
throws IOException, ClassNotFoundException {
+
+ // TODO: This method is opportunity for performance enhancement
+ // We can cache the classloader and recently used interfaces.
ClassLoader loader = VM.getNonBootstrapClassLoader();
Class>[] interfaces = new Class>[interfaceNames.length];
for (int i = 0; i < interfaceNames.length; i++) {
@@ -1837,8 +1840,8 @@
* @throws IOException
* If an IO exception happened when reading the handle
*/
- private Integer readNewHandle() throws IOException {
- return Integer.valueOf(input.readInt());
+ private int readNewHandle() throws IOException {
+ return input.readInt();
}
private Class> resolveConstructorClass(Class> objectClass, boolean wasSerializable, boolean wasExternalizable)
@@ -1936,7 +1939,7 @@
throw new InvalidClassException(Msg.getString("K00d1")); //$NON-NLS-1$
}
- Integer newHandle = Integer.valueOf(nextHandle());
+ Integer newHandle = nextHandle();
// Note that these values come from the Stream, and in fact it could be
// that the classes have been changed so that the info below now
@@ -2009,9 +2012,8 @@
if (objectClass != null) {
- ObjectStreamClass desc = ObjectStreamClass.lookupStreamClass(objectClass);
- if (desc.hasMethodReadResolve()){
- Method methodReadResolve = desc.getMethodReadResolve();
+ if (classDesc.hasMethodReadResolve()){
+ Method methodReadResolve = classDesc.getMethodReadResolve();
try {
result = methodReadResolve.invoke(result, (Object[]) null);
} catch (IllegalAccessException iae) {
@@ -2059,8 +2061,7 @@
if (enableResolve) {
result = resolveObject(result);
}
- int newHandle = nextHandle();
- registerObjectRead(result, Integer.valueOf(newHandle), unshared);
+ registerObjectRead(result, nextHandle(), unshared);
return result;
}
@@ -2082,8 +2083,7 @@
if (enableResolve) {
result = resolveObject(result);
}
- int newHandle = nextHandle();
- registerObjectRead(result, Integer.valueOf(newHandle), unshared);
+ registerObjectRead(result, nextHandle(), unshared);
return result;
}
@@ -2427,8 +2427,6 @@
// Use the first non-null ClassLoader on the stack. If null, use
// the system class loader
cls = Class.forName(className, true, callerClassLoader);
- // save the value
- osClass.setClass(cls);
}
}
return cls;
Index: modules/luni/src/main/java/java/io/ObjectOutputStream.java
===================================================================
--- modules/luni/src/main/java/java/io/ObjectOutputStream.java (revision 660154)
+++ modules/luni/src/main/java/java/io/ObjectOutputStream.java (working copy)
@@ -115,6 +115,11 @@
private ObjectAccessor accessor = AccessorFactory.getObjectAccessor();
+ /*
+ * Descriptor for java.lang.reflect.Proxy
+ */
+ private final ObjectStreamClass proxyClassDesc = ObjectStreamClass.lookup(Proxy.class);
+
/**
* Inner class to provide access to serializable fields
*/
@@ -468,8 +473,8 @@
*
* @see #nextHandle
*/
- private Integer registerObjectWritten(Object obj) {
- Integer handle = Integer.valueOf(nextHandle());
+ private int registerObjectWritten(Object obj) {
+ int handle = nextHandle();
objectsWritten.put(obj, handle);
return handle;
}
@@ -740,9 +745,10 @@
}
// If we got here, it is a new (non-null) classDesc that will have
// to be registered as well
- handle = registerObjectWritten(classDesc);
+ handle = nextHandle();
+ objectsWritten.put(classDesc, handle);
- if (Proxy.isProxyClass(classToWrite)) {
+ if (classDesc.isProxy()) {
output.writeByte(TC_PROXYCLASSDESC);
Class>[] interfaces = classToWrite.getInterfaces();
output.writeInt(interfaces.length);
@@ -751,7 +757,7 @@
}
annotateProxyClass(classToWrite);
output.writeByte(TC_ENDBLOCKDATA);
- writeClassDescForClass(Proxy.class);
+ writeClassDesc(proxyClassDesc, false);
if (unshared) {
// remove reference to unshared object
removeUnsharedReference(classDesc, previousHandle);
@@ -783,25 +789,6 @@
}
/**
- * Writes a class descriptor (an ObjectStreamClass) that
- * corresponds to the java.lang.Class objClass to the stream.
- *
- * @param objClass
- * The class for which a class descriptor (an
- * ObjectStreamClass) will be dumped.
- * @return the handle assigned to the class descriptor
- *
- * @throws IOException
- * If an IO exception happened when writing the class
- * descriptor.
- *
- */
- private Integer writeClassDescForClass(Class> objClass)
- throws IOException {
- return writeClassDesc(ObjectStreamClass.lookup(objClass), false);
- }
-
- /**
* Writes a handle representing a cyclic reference (object previously
* dumped).
*
@@ -1176,20 +1163,16 @@
* @throws IOException
* If an IO exception happened when writing the array.
*/
- private Integer writeNewArray(Object array, Class> arrayClass,
+ private Integer writeNewArray(Object array, Class> arrayClass, ObjectStreamClass arrayClDesc,
Class> componentType, boolean unshared) throws IOException {
output.writeByte(TC_ARRAY);
- writeClassDescForClass(arrayClass);
+ writeClassDesc(arrayClDesc, false);
- Integer previousHandle = null;
- if (unshared) {
- previousHandle = objectsWritten.get(array);
+ int handle = nextHandle();
+
+ if (!unshared) {
+ objectsWritten.put(array, handle);
}
- Integer handle = registerObjectWritten(array);
- if (unshared) {
- // remove reference to unshared object
- removeUnsharedReference(array, previousHandle);
- }
// Now we have code duplication just because Java is typed. We have to
// write N elements and assign to array positions, but we must typecast
@@ -1253,6 +1236,10 @@
Object[] objectArray = (Object[]) array;
output.writeInt(objectArray.length);
for (int i = 0; i < objectArray.length; i++) {
+ // TODO: This place is the opportunity for enhancement
+ // We can implement writing elements through fast-path,
+ // without setting up the context (see writeObject()) for
+ // each element with public API
writeObject(objectArray[i]);
}
}
@@ -1282,25 +1269,21 @@
// We cannot call lookup because it returns null if the parameter
// represents instances that cannot be serialized, and that is not what
// we want.
-
+ ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(object);
+
// The handle for the classDesc is NOT the handle for the class object
// being dumped. We must allocate a new handle and return it.
- if (object.isEnum()) {
- writeEnumDesc(object, unshared);
+ if (clDesc.isEnum()) {
+ writeEnumDesc(object, clDesc, unshared);
} else {
- writeClassDesc(ObjectStreamClass.lookupStreamClass(object),
- unshared);
+ writeClassDesc(clDesc, unshared);
}
+
+ int handle = nextHandle();
- Integer previousHandle = null;
- if (unshared) {
- previousHandle = objectsWritten.get(object);
+ if (!unshared) {
+ objectsWritten.put(object, handle);
}
- Integer handle = registerObjectWritten(object);
- if (unshared) {
- // remove reference to unshared object
- removeUnsharedReference(object, previousHandle);
- }
return handle;
}
@@ -1324,9 +1307,8 @@
output.writeUTF(classDesc.getName());
output.writeLong(classDesc.getSerialVersionUID());
byte flags = classDesc.getFlags();
- boolean externalizable = false;
- externalizable = ObjectStreamClass.isExternalizable(classDesc
- .forClass());
+
+ boolean externalizable = classDesc.isExternalizable();
if (externalizable) {
if (protocolVersion == PROTOCOL_VERSION_1) {
@@ -1411,7 +1393,7 @@
* @throws IOException
* If an IO exception happened when writing the object.
*/
- private Integer writeNewObject(Object object, Class> theClass,
+ private Integer writeNewObject(Object object, Class> theClass, ObjectStreamClass clDesc,
boolean unshared) throws IOException {
// Not String, not null, not array, not cyclic reference
@@ -1419,8 +1401,8 @@
currentPutField = null; // null it, to make sure one will be computed if
// needed
- boolean externalizable = ObjectStreamClass.isExternalizable(theClass);
- boolean serializable = ObjectStreamClass.isSerializable(theClass);
+ boolean externalizable = clDesc.isExternalizable();
+ boolean serializable = clDesc.isSerializable();
if (!externalizable && !serializable) {
// Object is neither externalizable nor serializable. Error
throw new NotSerializableException(theClass.getName());
@@ -1428,19 +1410,20 @@
// Either serializable or externalizable, now we can save info
output.writeByte(TC_OBJECT);
- writeClassDescForClass(theClass);
+ writeClassDesc(clDesc, false);
Integer previousHandle = null;
if (unshared) {
previousHandle = objectsWritten.get(object);
}
- Integer handle = registerObjectWritten(object);
+ int handle = nextHandle();
+ objectsWritten.put(object, handle);
// This is how we know what to do in defaultWriteObject. And it is also
// used by defaultWriteObject to check if it was called from an invalid
// place.
// It allows writeExternal to call defaultWriteObject and have it work.
currentObject = object;
- currentClass = ObjectStreamClass.lookup(theClass);
+ currentClass = clDesc;
try {
if (externalizable) {
boolean noBlockData = protocolVersion == PROTOCOL_VERSION_1;
@@ -1480,6 +1463,24 @@
}
/**
+ * Updates the references table with new handle
+ *
+ * @param obj
+ * Non-null object being updated
+ * @param unshared
+ * whether the handle is unshared
+ */
+ private int updateReference(Object object, boolean unshared) {
+ int handle = nextHandle();
+
+ if (!unshared) {
+ objectsWritten.put(object, handle);
+ }
+
+ return handle;
+ }
+
+ /**
* Write String object into the receiver. It is assumed the
* String has not been dumped yet. Return an Integer that
* represents the handle for this object (String) which is dumped here.
@@ -1503,16 +1504,13 @@
output.writeLong(count);
}
output.writeUTFBytes(object, count);
+
+ int handle = nextHandle();
- Integer previousHandle = null;
- if (unshared) {
- previousHandle = objectsWritten.get(object);
+ if (!unshared) {
+ objectsWritten.put(object, handle);
}
- Integer handle = registerObjectWritten(object);
- if (unshared) {
- // remove reference to unshared object
- removeUnsharedReference(object, previousHandle);
- }
+
return handle;
}
@@ -1634,6 +1632,8 @@
// Non-null object, first time seen...
Class> objClass = object.getClass();
+ ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass);
+
nestedLevels++;
try {
@@ -1648,9 +1648,8 @@
}
}
- if (ObjectStreamClass.isSerializable(object.getClass())
+ if (clDesc.isSerializable()
&& computeClassBasedReplacement) {
- ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass);
if(clDesc.hasMethodWriteReplace()){
Method methodWriteReplace = clDesc.getMethodWriteReplace();
Object replObj = null;
@@ -1724,7 +1723,7 @@
// Is it an Array ?
if (objClass.isArray()) {
- return writeNewArray(object, objClass, objClass
+ return writeNewArray(object, objClass, clDesc, objClass
.getComponentType(), unshared);
}
@@ -1733,17 +1732,17 @@
}
// Not a String or Class or Array. Default procedure.
- return writeNewObject(object, objClass, unshared);
+ return writeNewObject(object, objClass, clDesc, unshared);
} finally {
nestedLevels--;
}
}
// write for Enum Class Desc only, which is different from other classes
- private ObjectStreamClass writeEnumDesc(Class> theClass, boolean unshared)
+ private ObjectStreamClass writeEnumDesc(Class> theClass, ObjectStreamClass classDesc, boolean unshared)
throws IOException {
// write classDesc, classDesc for enum is different
- ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass);
+
// set flag for enum, the flag is (SC_SERIALIZABLE | SC_ENUM)
classDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
Integer previousHandle = null;
@@ -1758,7 +1757,7 @@
Class> classToWrite = classDesc.forClass();
// If we got here, it is a new (non-null) classDesc that will have
// to be registered as well
- registerObjectWritten(classDesc);
+ objectsWritten.put(classDesc, nextHandle());
output.writeByte(TC_CLASSDESC);
if (protocolVersion == PROTOCOL_VERSION_1) {
@@ -1775,11 +1774,11 @@
drain(); // flush primitive types in the annotation
output.writeByte(TC_ENDBLOCKDATA);
// write super class
- ObjectStreamClass superClass = classDesc.getSuperclass();
- if (null != superClass) {
+ ObjectStreamClass superClassDesc = classDesc.getSuperclass();
+ if (null != superClassDesc) {
// super class is also enum
- superClass.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
- writeEnumDesc(superClass.forClass(), unshared);
+ superClassDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
+ writeEnumDesc(superClassDesc.forClass(), superClassDesc, unshared);
} else {
output.writeByte(TC_NULL);
}
@@ -1803,13 +1802,15 @@
// write enum only
theClass = theClass.getSuperclass();
}
- ObjectStreamClass classDesc = writeEnumDesc(theClass, unshared);
+ ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass);
+ writeEnumDesc(theClass, classDesc, unshared);
Integer previousHandle = null;
if (unshared) {
previousHandle = objectsWritten.get(object);
}
- Integer handle = registerObjectWritten(object);
+ int handle = nextHandle();
+ objectsWritten.put(object, handle);
ObjectStreamField[] fields = classDesc.getSuperclass().fields();
Class> declaringClass = classDesc.getSuperclass().forClass();
Index: modules/luni/src/main/java/java/io/ObjectStreamClass.java
===================================================================
--- modules/luni/src/main/java/java/io/ObjectStreamClass.java (revision 660154)
+++ modules/luni/src/main/java/java/io/ObjectStreamClass.java (working copy)
@@ -148,6 +148,27 @@
private transient Method methodReadObjectNoData;
+ /**
+ * Indicates whether the class properties resolved
+ *
+ * @see #resolveProperties()
+ */
+ private transient boolean arePropertiesResolved = false;
+
+ /**
+ * Cached class properties
+ *
+ * @see #resolveProperties()
+ * @see #isSerializable()
+ * @see #isExternalizable()
+ * @see #isProxy()
+ * @see #isEnum()
+ */
+ private transient boolean isSerializable;
+ private transient boolean isExternalizable;
+ private transient boolean isProxy;
+ private transient boolean isEnum;
+
// ClassDesc //
// Name of the class this descriptor represents
@@ -203,26 +224,24 @@
}
/**
- * Compute class descriptor for a given class cl. If
- * computeSUID is true, this method will compute the SUID for
- * this class.
+ * Compute class descriptor for a given class cl.
*
* @param cl
* a java.langClass for which to compute the corresponding
- * descriptor
- * @param computeSUID
- * a boolean indicating if SUID should be computed or not.
+ * descriptor
* @return the computer class descriptor
*/
- private static ObjectStreamClass createClassDesc(Class> cl,
- boolean computeSUID) {
+ private static ObjectStreamClass createClassDesc(Class> cl) {
ObjectStreamClass result = new ObjectStreamClass();
- boolean isProxy = Proxy.isProxyClass(cl);
- boolean isEnum = Enum.class.isAssignableFrom(cl);
boolean isArray = cl.isArray();
+ boolean serializable = isSerializable(cl);
+ boolean externalizable = isExternalizable(cl);
+ result.isSerializable = serializable;
+ result.isExternalizable = externalizable;
+
// Now we fill in the values
result.setName(cl.getName());
result.setClass(cl);
@@ -232,9 +251,10 @@
}
Field[] declaredFields = null;
- if (computeSUID) {
- // Lazy computation, to save speed & space
- if (isEnum || isProxy) {
+
+ // Compute the SUID
+ if(serializable || externalizable) {
+ if (result.isEnum() || result.isProxy()) {
result.setSerialVersionUID(0L);
} else {
declaredFields = cl.getDeclaredFields();
@@ -243,7 +263,6 @@
}
}
- boolean serializable = isSerializable(cl);
// Serializables need field descriptors
if (serializable && !isArray) {
if (declaredFields == null) {
@@ -274,7 +293,6 @@
}
byte flags = 0;
- boolean externalizable = isExternalizable(cl);
if (externalizable) {
flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
flags |= ObjectStreamConstants.SC_BLOCK_DATA; // use protocol version 2 by default
@@ -688,7 +706,7 @@
ObjectStreamField[] fields() {
if (fields == null) {
Class> forCl = forClass();
- if (forCl != null && isSerializable(forCl) && !forCl.isArray()) {
+ if (forCl != null && isSerializable() && !forCl.isArray()) {
buildFieldDescriptors(forCl.getDeclaredFields());
} else {
// Externalizables or arrays do not need FieldDesc info
@@ -869,6 +887,61 @@
}
/**
+ * Resolves the class properties, if they weren't already
+ */
+ private void resolveProperties() {
+ if (arePropertiesResolved) return;
+
+ Class> cl = forClass();
+ isProxy = Proxy.isProxyClass(cl);
+ isEnum = Enum.class.isAssignableFrom(cl);
+ isSerializable = isSerializable(cl);
+ isExternalizable = isExternalizable(cl);
+
+ arePropertiesResolved = true;
+ }
+
+ /**
+ * Answers whether the class for this descriptor is serializable
+ *
+ * @return true if class implements Serializable
+ */
+ boolean isSerializable() {
+ resolveProperties();
+ return isSerializable;
+ }
+
+ /**
+ * Answers whether the class for this descriptor is serializable
+ *
+ * @return true if class implements Serializable
+ */
+ boolean isExternalizable() {
+ resolveProperties();
+ return isExternalizable;
+ }
+
+ /**
+ * Answers whether the class for this descriptor is proxied class
+ *
+ * @return true if class is proxied
+ */
+ boolean isProxy() {
+ resolveProperties();
+ return isProxy;
+ }
+
+ /**
+ * Answers whether the class for this descriptor is subclass of Enum
+ *
+ * @return true if class is subclass of Enum
+ */
+ boolean isEnum() {
+ resolveProperties();
+ return isEnum;
+ }
+
+ /**
* Return a little endian long stored in a given position of the buffer
*
* @param buffer
@@ -899,15 +972,13 @@
* the class cl is Serializable or Externalizable
*/
public static ObjectStreamClass lookup(Class> cl) {
- boolean serializable = isSerializable(cl);
- boolean externalizable = isExternalizable(cl);
-
- // Has to be either Serializable or Externalizable
- if (!serializable && !externalizable) {
- return null;
+ ObjectStreamClass osc = lookupStreamClass(cl);
+
+ if (osc.isSerializable() || osc.isExternalizable()) {
+ return osc;
}
-
- return lookupStreamClass(cl, true);
+
+ return null;
}
/**
@@ -916,34 +987,19 @@
* class cannot be serialized
*
* @param cl
- * a java.langClass for which to obtain the corresponding
- * descriptor
- * @return the corresponding descriptor
- */
- static ObjectStreamClass lookupStreamClass(Class> cl) {
- return lookupStreamClass(cl, isSerializable(cl) || isExternalizable(cl));
- }
-
- /**
- * Return the descriptor (ObjectStreamClass) corresponding to the class
- * cl. Returns an ObjectStreamClass even if instances of the
- * class cannot be serialized
- *
- * @param cl
* a java.langClass for which to obtain the
* corresponding descriptor
* @param computeSUID
* a boolean indicating if SUID should be computed or not.
* @return the corresponding descriptor
*/
- private static ObjectStreamClass lookupStreamClass(Class> cl,
- boolean computeSUID) {
+ static ObjectStreamClass lookupStreamClass(Class> cl) {
WeakHashMap,ObjectStreamClass> tlc = OSCThreadLocalCache.oscWeakHashMap.get();
ObjectStreamClass cachedValue = tlc.get(cl);
if (cachedValue == null) {
- cachedValue = createClassDesc(cl, computeSUID);
+ cachedValue = createClassDesc(cl);
tlc.put(cl, cachedValue);
}
return cachedValue;