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;