Index: luni/src/main/java/java/io/ObjectOutputStream.java =================================================================== --- luni/src/main/java/java/io/ObjectOutputStream.java (revision 533306) +++ luni/src/main/java/java/io/ObjectOutputStream.java (working copy) @@ -105,10 +105,6 @@ */ private boolean subclassOverridingImplementation; - /* - * cache for writeReplace methods - */ - private IdentityHashMap, Object> writeReplaceCache; /** * Inner class to provide access to serializable fields @@ -207,7 +203,6 @@ this.enableReplace = false; this.protocolVersion = PROTOCOL_VERSION_2; this.subclassOverridingImplementation = false; - this.writeReplaceCache = new IdentityHashMap, Object>(); resetState(); this.nestedException = new StreamCorruptedException(); @@ -1304,12 +1299,8 @@ boolean executed = false; Class targetClass = classDesc.forClass(); try { - final Method method = ObjectStreamClass - .getPrivateWriteObjectMethod(targetClass); - if (method != null) { - // We have to be able to fetch its value, even if it is - // private - AccessController.doPrivileged(new PriviAction(method)); + if (classDesc.hasMethodWriteObject()){ + final Method method = classDesc.getMethodWriteObject(); try { method.invoke(object, new Object[] { this }); executed = true; @@ -1326,6 +1317,7 @@ } } + if (executed) { drain(); output.writeByte(TC_ENDBLOCKDATA); @@ -1853,57 +1845,40 @@ if (ObjectStreamClass.isSerializable(object.getClass()) && computeClassBasedReplacement) { - Object writeReplaceMethod = writeReplaceCache.get(objClass); - if (writeReplaceMethod != this) { - if (writeReplaceMethod == null) { - final Method writeReplace = ObjectStreamClass - .methodWriteReplace(objClass); - if (writeReplace == null) { - writeReplaceCache.put(objClass, this); - // writeReplaceMethod must be null here - assert writeReplaceMethod == null; + ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass); + if(clDesc.hasMethodWriteReplace()){ + Method methodWriteReplace = clDesc.getMethodWriteReplace(); + Object replObj = null; + try { + replObj = methodWriteReplace.invoke(object, (Object[]) null); + } catch (IllegalAccessException iae) { + replObj = object; + } catch (InvocationTargetException ite) { + // WARNING - Not sure this is the right thing to do + // if we can't run the method + Throwable target = ite.getTargetException(); + if (target instanceof ObjectStreamException) { + throw (ObjectStreamException) target; + } else if (target instanceof Error) { + throw (Error) target; } else { - // Has replacement method - AccessController - .doPrivileged(new PriviAction( - writeReplace)); - writeReplaceCache.put(objClass, writeReplace); - writeReplaceMethod = writeReplace; + throw (RuntimeException) target; } } - if (writeReplaceMethod != null) { - Object classBasedReplacement; - try { - classBasedReplacement = ((Method) writeReplaceMethod) - .invoke(object, (Object[]) null); - } catch (IllegalAccessException iae) { - classBasedReplacement = object; - } catch (InvocationTargetException ite) { - // WARNING - Not sure this is the right thing to do - // if we can't run the method - Throwable target = ite.getTargetException(); - if (target instanceof ObjectStreamException) { - throw (ObjectStreamException) target; - } else if (target instanceof Error) { - throw (Error) target; - } else { - throw (RuntimeException) target; - } + if (replObj != object) { + // All over, class-based replacement off this time. + Integer replacementHandle = writeObjectInternal( + replObj, false, false, + computeStreamReplacement); + // Make the original object also map to the same + // handle. + if (replacementHandle != null) { + registerObjectWritten(object, replacementHandle); } - if (classBasedReplacement != object) { - // All over, class-based replacement off this time. - Integer replacementHandle = writeObjectInternal( - classBasedReplacement, false, false, - computeStreamReplacement); - // Make the original object also map to the same - // handle. - if (replacementHandle != null) { - registerObjectWritten(object, replacementHandle); - } - return replacementHandle; - } + return replacementHandle; } } + } // We get here either if class-based replacement was not needed or Index: luni/src/main/java/java/io/ObjectInputStream.java =================================================================== --- luni/src/main/java/java/io/ObjectInputStream.java (revision 533306) +++ luni/src/main/java/java/io/ObjectInputStream.java (working copy) @@ -105,9 +105,6 @@ // Handle for the current class descriptor private Integer descriptorHandle; - // cache for readResolve methods - private IdentityHashMap, Object> readResolveCache; - private static final Hashtable> PRIMITIVE_CLASSES = new Hashtable>(); static { @@ -325,7 +322,6 @@ primitiveTypes = new DataInputStream(this); enableResolve = false; this.subclassOverridingImplementation = false; - this.readResolveCache = new IdentityHashMap, Object>(); resetState(); nestedLevels = 0; // So read...() methods can be used by @@ -1173,13 +1169,12 @@ // Object type (array included). String fieldName = fieldDesc.getName(); boolean setBack = false; - ObjectStreamField field = classDesc.getField(fieldName); - if (mustResolve && field == null) { + if (mustResolve && fieldDesc == null) { setBack = true; mustResolve = false; } Object toSet; - if (field != null && field.isUnshared()) { + if (fieldDesc != null && fieldDesc.isUnshared()) { toSet = readUnshared(); } else { toSet = readObject(); @@ -1187,9 +1182,9 @@ if (setBack) { mustResolve = true; } - if (field != null) { + if (fieldDesc != null) { if (toSet != null) { - Class fieldType = field.getType(); + Class fieldType = fieldDesc.getType(); Class valueType = toSet.getClass(); if (!fieldType.isAssignableFrom(valueType)) { throw new ClassCastException(Msg.getString( @@ -1199,7 +1194,7 @@ + fieldName })); } try { - objSetField(obj, declaringClass, fieldName, field + objSetField(obj, declaringClass, fieldName, fieldDesc .getTypeString(), toSet); } catch (NoSuchFieldError e) { // Ignored @@ -1360,11 +1355,9 @@ if (!ObjectStreamClass.isSerializable(cl)) { return; } - - final Method readMethod = ObjectStreamClass - .getPrivateReadObjectNoDataMethod(cl); - if (readMethod != null) { - AccessController.doPrivileged(new PriviAction(readMethod)); + ObjectStreamClass classDesc = ObjectStreamClass.lookupStreamClass(cl); + if (classDesc.hasMethodReadObjectNoData()){ + final Method readMethod = classDesc.getMethodReadObjectNoData(); try { readMethod.invoke(object, new Object[0]); } catch (InvocationTargetException e) { @@ -1379,6 +1372,7 @@ throw new RuntimeException(e.toString()); } } + } private void readObjectForClass(Object object, ObjectStreamClass classDesc) @@ -1390,12 +1384,12 @@ boolean hadWriteMethod = (classDesc.getFlags() & SC_WRITE_METHOD) > 0; Class targetClass = classDesc.forClass(); + final Method readMethod; if (targetClass == null || !mustResolve) { readMethod = null; } else { - readMethod = ObjectStreamClass - .getPrivateReadObjectMethod(targetClass); + readMethod = classDesc.getMethodReadObject(); } try { if (readMethod != null) { @@ -1716,10 +1710,8 @@ // We need to map classDesc to class. try { newClassDesc.setClass(resolveClass(newClassDesc)); - // Check SUIDs - verifySUID(newClassDesc); - // Check base name of the class - verifyBaseName(newClassDesc); + // Check SUIDs & base name of the class + verifyAndInit(newClassDesc); } catch (ClassNotFoundException e) { if (mustResolve) { throw e; @@ -1997,39 +1989,24 @@ } if (objectClass != null) { - Object readResolveMethod = readResolveCache.get(objectClass); - if (readResolveMethod != this) { - if (readResolveMethod == null) { - final Method readResolve = ObjectStreamClass - .methodReadResolve(objectClass); - if (readResolve == null) { - readResolveCache.put(objectClass, this); - // readResolveMethod must be null here - assert readResolveMethod == null; + + ObjectStreamClass desc = ObjectStreamClass.lookupStreamClass(objectClass); + if (desc.hasMethodReadResolve()){ + Method methodReadResolve = desc.getMethodReadResolve(); + try { + result = methodReadResolve.invoke(result, (Object[]) null); + } catch (IllegalAccessException iae) { + } catch (InvocationTargetException ite) { + Throwable target = ite.getTargetException(); + if (target instanceof ObjectStreamException) { + throw (ObjectStreamException) target; + } else if (target instanceof Error) { + throw (Error) target; } else { - // Has replacement method - AccessController.doPrivileged(new PriviAction( - readResolve)); - readResolveCache.put(objectClass, readResolve); - readResolveMethod = readResolve; + throw (RuntimeException) target; } } - if (readResolveMethod != null) { - try { - result = ((Method) readResolveMethod).invoke(result, - (Object[]) null); - } catch (IllegalAccessException iae) { - } catch (InvocationTargetException ite) { - Throwable target = ite.getTargetException(); - if (target instanceof ObjectStreamException) { - throw (ObjectStreamException) target; - } else if (target instanceof Error) { - throw (Error) target; - } else { - throw (RuntimeException) target; - } - } - } + } } // We get here either if class-based replacement was not needed or if it @@ -2726,8 +2703,10 @@ } /** - * Verify if the SUID for descriptor loadedStreamClassmatches - * the SUID of the corresponding loaded class. + * Verify if the SUID & the base name for descriptor + * loadedStreamClassmatches + * the SUID & the base name of the corresponding loaded class and + * init private fields. * * @param loadedStreamClass * An ObjectStreamClass that was loaded from the stream. @@ -2735,41 +2714,20 @@ * @throws InvalidClassException * If the SUID of the stream class does not match the VM class */ - private void verifySUID(ObjectStreamClass loadedStreamClass) + private void verifyAndInit(ObjectStreamClass loadedStreamClass) throws InvalidClassException { + Class localClass = loadedStreamClass.forClass(); - // Instances of java.lang.Class are always Serializable, even if their - // instances aren't (e.g. java.lang.Object.class). We cannot call lookup - // because it returns null if the parameter represents instances that - // cannot be serialized, and that is not what we want. If we are loading - // an instance of java.lang.Class, we better have the corresponding - // ObjectStreamClass. ObjectStreamClass localStreamClass = ObjectStreamClass .lookupStreamClass(localClass); + if (loadedStreamClass.getSerialVersionUID() != localStreamClass .getSerialVersionUID()) { throw new InvalidClassException(loadedStreamClass.getName(), Msg .getString("K00da", loadedStreamClass, //$NON-NLS-1$ localStreamClass)); } - } - /** - * Verify if the base name for descriptor loadedStreamClass - * matches the base name of the corresponding loaded class. - * - * @param loadedStreamClass - * An ObjectStreamClass that was loaded from the stream. - * - * @throws InvalidClassException - * If the base name of the stream class does not match the VM - * class - */ - private void verifyBaseName(ObjectStreamClass loadedStreamClass) - throws InvalidClassException { - Class localClass = loadedStreamClass.forClass(); - ObjectStreamClass localStreamClass = ObjectStreamClass - .lookupStreamClass(localClass); String loadedClassBaseName = getBaseName(loadedStreamClass.getName()); String localClassBaseName = getBaseName(localStreamClass.getName()); @@ -2778,6 +2736,8 @@ .getString("KA015", loadedClassBaseName, //$NON-NLS-1$ localClassBaseName)); } + + loadedStreamClass.initPrivateFields(localStreamClass); } private static String getBaseName(String fullName) { Index: luni/src/main/java/java/io/ObjectStreamClass.java =================================================================== --- luni/src/main/java/java/io/ObjectStreamClass.java (revision 533306) +++ luni/src/main/java/java/io/ObjectStreamClass.java (working copy) @@ -138,6 +138,12 @@ // of ObjectStreamClass private static final WeakHashMap, ObjectStreamClass> classesAndDescriptors = new WeakHashMap, ObjectStreamClass>(); + private transient Method methodWriteReplace; + private transient Method methodReadResolve; + private transient Method methodWriteObject; + private transient Method methodReadObject; + private transient Method methodReadObjectNoData; + // ClassDesc // // Name of the class this descriptor represents @@ -182,8 +188,7 @@ } /** - * Add an extra entry mapping a given class cl to its class - * descriptor, which will be computed (an ObjectStreamClass). If + * Compute class descriptor for a given class cl. If * computeSUID is true, this method will compute the SUID for * this class. * @@ -194,7 +199,7 @@ * a boolean indicating if SUID should be computed or not. * @return the computer class descriptor */ - private static ObjectStreamClass addToCache(Class cl, boolean computeSUID) { + private static ObjectStreamClass createClassDesc(Class cl, boolean computeSUID) { ObjectStreamClass result = new ObjectStreamClass(); @@ -234,11 +239,20 @@ } else if (serializable) { flags |= ObjectStreamConstants.SC_SERIALIZABLE; } - if (getPrivateWriteObjectMethod(cl) != null) { + result.methodWriteReplace = + findMethod(cl, "writeReplace"); + result.methodReadResolve = + findMethod(cl, "readResolve"); + result.methodWriteObject = + findPrivateMethod(cl, "writeObject", WRITE_PARAM_TYPES); + result.methodReadObject = + findPrivateMethod(cl, "readObject", READ_PARAM_TYPES); + result.methodReadObjectNoData = + findPrivateMethod(cl, "readObjectNoData", EMPTY_CONSTRUCTOR_PARAM_TYPES); + if (result.hasMethodWriteObject()) { flags |= ObjectStreamConstants.SC_WRITE_METHOD; } result.setFlags(flags); - classesAndDescriptors.put(cl, result); return result; } @@ -633,14 +647,12 @@ */ ObjectStreamField[] fields() { if (fields == null) { - synchronized(this){ - Class forCl = forClass(); - if (forCl != null && isSerializable(forCl) && !forCl.isArray()) { - buildFieldDescriptors(forCl.getDeclaredFields()); - } else { - // Externalizables or arrays do not need FieldDesc info - setFields(new ObjectStreamField[0]); - } + Class forCl = forClass(); + if (forCl != null && isSerializable(forCl) && !forCl.isArray()) { + buildFieldDescriptors(forCl.getDeclaredFields()); + } else { + // Externalizables or arrays do not need FieldDesc info + setFields(new ObjectStreamField[0]); } } return fields; @@ -771,75 +783,6 @@ private static native boolean hasClinit(Class cl); /** - * Return true if the given class cl implements private - * method readObject(). - * - * @param cl - * a java.lang.Class which to test - * @return true if the class implements readObject - * false if the class does not implement readObject - */ - static Method getPrivateReadObjectMethod(Class cl) { - try { - Method method = cl - .getDeclaredMethod("readObject", READ_PARAM_TYPES); //$NON-NLS-1$ - if (Modifier.isPrivate(method.getModifiers()) - && method.getReturnType() == VOID_CLASS) { - return method; - } - } catch (NoSuchMethodException nsm) { - // Ignored - } - return null; - } - - /** - * Return true if the given class cl implements private - * method readObject(). - * - * @param cl - * a java.lang.Class which to test - * @return true if the class implements readObject - * false if the class does not implement readObject - */ - static Method getPrivateReadObjectNoDataMethod(Class cl) { - try { - Method method = cl.getDeclaredMethod("readObjectNoData", //$NON-NLS-1$ - EMPTY_CONSTRUCTOR_PARAM_TYPES); - if (Modifier.isPrivate(method.getModifiers()) - && method.getReturnType() == VOID_CLASS) { - return method; - } - } catch (NoSuchMethodException nsm) { - // Ignored - } - return null; - } - - /** - * Return true if the given class cl implements private - * method writeObject(). - * - * @param cl - * a java.lang.Class which to test - * @return true if the class implements writeObject - * false if the class does not implement writeObject - */ - static Method getPrivateWriteObjectMethod(Class cl) { - try { - Method method = cl.getDeclaredMethod("writeObject", //$NON-NLS-1$ - WRITE_PARAM_TYPES); - if (Modifier.isPrivate(method.getModifiers()) - && method.getReturnType() == VOID_CLASS) { - return method; - } - } catch (NoSuchMethodException nsm) { - // Ignored - } - return null; - } - - /** * Return true if instances of class cl are Externalizable, * false otherwise. * @@ -953,37 +896,43 @@ * a boolean indicating if SUID should be computed or not. * @return the corresponding descriptor */ - private static synchronized ObjectStreamClass lookupStreamClass( + private static ObjectStreamClass lookupStreamClass( Class cl, boolean computeSUID) { // Synchronized because of the lookup table 'classesAndDescriptors' - ObjectStreamClass cachedValue = classesAndDescriptors.get(cl); - if (cachedValue != null) { - return cachedValue; + + ObjectStreamClass cachedValue; + synchronized(classesAndDescriptors){ + cachedValue = classesAndDescriptors.get(cl); + if (cachedValue == null) { + cachedValue = createClassDesc(cl, computeSUID);; + classesAndDescriptors.put(cl, cachedValue); + } } - return addToCache(cl, computeSUID); + return cachedValue; + } /** - * Return the java.lang.reflect.Method readResolve if class - * cl implements it. Return null otherwise. + * Return the java.lang.reflect.Method if class + * cl implements methodName . Return null otherwise. * * @param cl * a java.lang.Class which to test * @return java.lang.reflect.Method if the class implements - * readResolve null if the class does not implement - * readResolve + * writeReplace null if the class does not implement + * writeReplace */ - static Method methodReadResolve(Class cl) { + static Method findMethod(Class cl, String methodName) { Class search = cl; + Method method = null; while (search != null) { try { - Method method = search.getDeclaredMethod( - "readResolve", (Class[]) null); //$NON-NLS-1$ + method = search.getDeclaredMethod(methodName, (Class[]) null); //$NON-NLS-1$ if (search == cl || (method.getModifiers() & Modifier.PRIVATE) == 0) { + method.setAccessible(true); return method; } - return null; } catch (NoSuchMethodException nsm) { } search = search.getSuperclass(); @@ -992,8 +941,8 @@ } /** - * Return the java.lang.reflect.Method writeReplace if class - * cl implements it. Return null otherwise. + * Return the java.lang.reflect.Method if class + * cl implements private methodName . Return null otherwise. * * @param cl * a java.lang.Class which to test @@ -1001,25 +950,68 @@ * writeReplace null if the class does not implement * writeReplace */ - static Method methodWriteReplace(Class cl) { - Class search = cl; - while (search != null) { - try { - Method method = search.getDeclaredMethod( - "writeReplace", (Class[]) null); //$NON-NLS-1$ - if (search == cl - || (method.getModifiers() & Modifier.PRIVATE) == 0) { - return method; - } - return null; - } catch (NoSuchMethodException nsm) { - // Ignored + static Method findPrivateMethod(Class cl, String methodName, Class[] param) { + try { + Method method = cl + .getDeclaredMethod(methodName, param); //$NON-NLS-1$ + if (Modifier.isPrivate(method.getModifiers()) + && method.getReturnType() == VOID_CLASS) { + method.setAccessible(true); + return method; } - search = search.getSuperclass(); + } catch (NoSuchMethodException nsm) { + // Ignored } return null; } + boolean hasMethodWriteReplace(){ + return (methodWriteReplace != null); + } + + Method getMethodWriteReplace(){ + return methodWriteReplace; + } + + boolean hasMethodReadResolve(){ + return (methodReadResolve != null); + } + + Method getMethodReadResolve(){ + return methodReadResolve; + } + + boolean hasMethodWriteObject(){ + return (methodWriteObject != null); + } + + Method getMethodWriteObject(){ + return methodWriteObject; + } + + boolean hasMethodReadObject(){ + return (methodReadObject != null); + } + + Method getMethodReadObject(){ + return methodReadObject; + } + + boolean hasMethodReadObjectNoData(){ + return (methodReadObjectNoData != null); + } + + Method getMethodReadObjectNoData(){ + return methodReadObjectNoData; + } + + void initPrivateFields(ObjectStreamClass desc){ + methodWriteReplace = desc.methodWriteReplace; + methodReadResolve = desc.methodReadResolve; + methodWriteObject = desc.methodWriteObject; + methodReadObject = desc.methodReadObject; + methodReadObjectNoData = desc.methodReadObjectNoData; + } /** * Set the class (java.lang.Class) that the receiver represents *