diff -r 3fcc279c921b -r d382dacfd73f rt/emul/compact/src/main/java/java/io/ObjectStreamClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/compact/src/main/java/java/io/ObjectStreamClass.java Tue Feb 26 16:54:16 2013 +0100 @@ -0,0 +1,1396 @@ +/* + * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.io; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + +/** + * Serialization's descriptor for classes. It contains the name and + * serialVersionUID of the class. The ObjectStreamClass for a specific class + * loaded in this Java VM can be found/created using the lookup method. + * + *
The algorithm to compute the SerialVersionUID is described in
+ * Object
+ * Serialization Specification, Section 4.6, Stream Unique Identifiers.
+ *
+ * @author Mike Warres
+ * @author Roger Riggs
+ * @see ObjectStreamField
+ * @see Object Serialization Specification, Section 4, Class Descriptors
+ * @since JDK1.1
+ */
+public class ObjectStreamClass implements Serializable {
+
+ /** serialPersistentFields value indicating no serializable fields */
+ public static final ObjectStreamField[] NO_FIELDS =
+ new ObjectStreamField[0];
+
+ private static final long serialVersionUID = -6120832682080437368L;
+ private static final ObjectStreamField[] serialPersistentFields =
+ NO_FIELDS;
+
+
+ /** class associated with this descriptor (if any) */
+ private Class> cl;
+ /** name of class represented by this descriptor */
+ private String name;
+ /** serialVersionUID of represented class (null if not computed yet) */
+ private volatile Long suid;
+
+ /** true if represents dynamic proxy class */
+ private boolean isProxy;
+ /** true if represents enum type */
+ private boolean isEnum;
+ /** true if represented class implements Serializable */
+ private boolean serializable;
+ /** true if represented class implements Externalizable */
+ private boolean externalizable;
+ /** true if desc has data written by class-defined writeObject method */
+ private boolean hasWriteObjectData;
+ /**
+ * true if desc has externalizable data written in block data format; this
+ * must be true by default to accommodate ObjectInputStream subclasses which
+ * override readClassDescriptor() to return class descriptors obtained from
+ * ObjectStreamClass.lookup() (see 4461737)
+ */
+ private boolean hasBlockExternalData = true;
+
+ /** exception (if any) thrown while attempting to resolve class */
+ private ClassNotFoundException resolveEx;
+ /** exception (if any) to throw if non-enum deserialization attempted */
+ private InvalidClassException deserializeEx;
+ /** exception (if any) to throw if non-enum serialization attempted */
+ private InvalidClassException serializeEx;
+ /** exception (if any) to throw if default serialization attempted */
+ private InvalidClassException defaultSerializeEx;
+
+ /** serializable fields */
+ private ObjectStreamField[] fields;
+ /** aggregate marshalled size of primitive fields */
+ private int primDataSize;
+ /** number of non-primitive fields */
+ private int numObjFields;
+ /** reflector for setting/getting serializable field values */
+// private FieldReflector fieldRefl;
+ /** data layout of serialized objects described by this class desc */
+ private volatile ClassDataSlot[] dataLayout;
+
+ /** serialization-appropriate constructor, or null if none */
+ private Constructor cons;
+ /** class-defined writeObject method, or null if none */
+ private Method writeObjectMethod;
+ /** class-defined readObject method, or null if none */
+ private Method readObjectMethod;
+ /** class-defined readObjectNoData method, or null if none */
+ private Method readObjectNoDataMethod;
+ /** class-defined writeReplace method, or null if none */
+ private Method writeReplaceMethod;
+ /** class-defined readResolve method, or null if none */
+ private Method readResolveMethod;
+
+ /** local class descriptor for represented class (may point to self) */
+ private ObjectStreamClass localDesc;
+ /** superclass descriptor appearing in stream */
+ private ObjectStreamClass superDesc;
+
+ /**
+ * Initializes native code.
+ */
+ private static native void initNative();
+ static {
+ initNative();
+ }
+
+ /**
+ * Find the descriptor for a class that can be serialized. Creates an
+ * ObjectStreamClass instance if one does not exist yet for class. Null is
+ * returned if the specified class does not implement java.io.Serializable
+ * or java.io.Externalizable.
+ *
+ * @param cl class for which to get the descriptor
+ * @return the class descriptor for the specified class
+ */
+ public static ObjectStreamClass lookup(Class> cl) {
+ return lookup(cl, false);
+ }
+
+ /**
+ * Returns the descriptor for any class, regardless of whether it
+ * implements {@link Serializable}.
+ *
+ * @param cl class for which to get the descriptor
+ * @return the class descriptor for the specified class
+ * @since 1.6
+ */
+ public static ObjectStreamClass lookupAny(Class> cl) {
+ return lookup(cl, true);
+ }
+
+ /**
+ * Returns the name of the class described by this descriptor.
+ * This method returns the name of the class in the format that
+ * is used by the {@link Class#getName} method.
+ *
+ * @return a string representing the name of the class
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Return the serialVersionUID for this class. The serialVersionUID
+ * defines a set of classes all with the same name that have evolved from a
+ * common root class and agree to be serialized and deserialized using a
+ * common format. NonSerializable classes have a serialVersionUID of 0L.
+ *
+ * @return the SUID of the class described by this descriptor
+ */
+ public long getSerialVersionUID() {
+ // REMIND: synchronize instead of relying on volatile?
+ if (suid == null) {
+ return computeDefaultSUID(cl);
+ }
+ return suid.longValue();
+ }
+
+ /**
+ * Return the class in the local VM that this version is mapped to. Null
+ * is returned if there is no corresponding local class.
+ *
+ * @return the Class
instance that this descriptor represents
+ */
+ public Class> forClass() {
+ return cl;
+ }
+
+ /**
+ * Return an array of the fields of this serializable class.
+ *
+ * @return an array containing an element for each persistent field of
+ * this class. Returns an array of length zero if there are no
+ * fields.
+ * @since 1.2
+ */
+ public ObjectStreamField[] getFields() {
+ return getFields(true);
+ }
+
+ /**
+ * Get the field of this class by name.
+ *
+ * @param name the name of the data field to look for
+ * @return The ObjectStreamField object of the named field or null if
+ * there is no such named field.
+ */
+ public ObjectStreamField getField(String name) {
+ return getField(name, null);
+ }
+
+ /**
+ * Return a string describing this ObjectStreamClass.
+ */
+ public String toString() {
+ return name + ": static final long serialVersionUID = " +
+ getSerialVersionUID() + "L;";
+ }
+
+ /**
+ * Looks up and returns class descriptor for given class, or null if class
+ * is non-serializable and "all" is set to false.
+ *
+ * @param cl class to look up
+ * @param all if true, return descriptors for all classes; if false, only
+ * return descriptors for serializable classes
+ */
+ static ObjectStreamClass lookup(Class> cl, boolean all) {
+ if (!(all || Serializable.class.isAssignableFrom(cl))) {
+ return null;
+ }
+ Object entry = null;
+ EntryFuture future = null;
+ if (entry == null) {
+ EntryFuture newEntry = new EntryFuture();
+ Reference> newRef = new SoftReference<>(newEntry);
+ if (entry == null) {
+ future = newEntry;
+ }
+ }
+
+ if (entry instanceof ObjectStreamClass) { // check common case first
+ return (ObjectStreamClass) entry;
+ }
+ if (entry instanceof EntryFuture) {
+ future = (EntryFuture) entry;
+ if (true) {
+ /*
+ * Handle nested call situation described by 4803747: waiting
+ * for future value to be set by a lookup() call further up the
+ * stack will result in deadlock, so calculate and set the
+ * future value here instead.
+ */
+ entry = null;
+ } else {
+ entry = future.get();
+ }
+ }
+ if (entry == null) {
+ try {
+ entry = new ObjectStreamClass(cl);
+ } catch (Throwable th) {
+ entry = th;
+ }
+ // nested lookup call already set future
+ entry = future.get();
+ }
+
+ if (entry instanceof ObjectStreamClass) {
+ return (ObjectStreamClass) entry;
+ } else if (entry instanceof RuntimeException) {
+ throw (RuntimeException) entry;
+ } else if (entry instanceof Error) {
+ throw (Error) entry;
+ } else {
+ throw new InternalError("unexpected entry: " + entry);
+ }
+ }
+
+ /**
+ * Placeholder used in class descriptor and field reflector lookup tables
+ * for an entry in the process of being initialized. (Internal) callers
+ * which receive an EntryFuture belonging to another thread as the result
+ * of a lookup should call the get() method of the EntryFuture; this will
+ * return the actual entry once it is ready for use and has been set(). To
+ * conserve objects, EntryFutures synchronize on themselves.
+ */
+ private static class EntryFuture {
+
+ private static final Object unset = new Object();
+ private Object entry = unset;
+
+ /**
+ * Attempts to set the value contained by this EntryFuture. If the
+ * EntryFuture's value has not been set already, then the value is
+ * saved, any callers blocked in the get() method are notified, and
+ * true is returned. If the value has already been set, then no saving
+ * or notification occurs, and false is returned.
+ */
+ synchronized boolean set(Object entry) {
+ if (this.entry != unset) {
+ return false;
+ }
+ this.entry = entry;
+ notifyAll();
+ return true;
+ }
+
+ /**
+ * Returns the value contained by this EntryFuture, blocking if
+ * necessary until a value is set.
+ */
+ synchronized Object get() {
+ boolean interrupted = false;
+ while (entry == unset) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ interrupted = true;
+ }
+ }
+ return entry;
+ }
+ }
+
+ /**
+ * Creates local class descriptor representing given class.
+ */
+ private ObjectStreamClass(final Class> cl) {
+ this.cl = cl;
+ name = cl.getName();
+ isProxy = Proxy.isProxyClass(cl);
+ isEnum = Enum.class.isAssignableFrom(cl);
+ serializable = Serializable.class.isAssignableFrom(cl);
+ externalizable = Externalizable.class.isAssignableFrom(cl);
+
+ Class> superCl = cl.getSuperclass();
+ superDesc = (superCl != null) ? lookup(superCl, false) : null;
+ localDesc = this;
+
+ suid = Long.valueOf(0);
+ fields = NO_FIELDS;
+
+
+ if (deserializeEx == null) {
+ if (isEnum) {
+ deserializeEx = new InvalidClassException(name, "enum type");
+ } else if (cons == null) {
+ deserializeEx = new InvalidClassException(
+ name, "no valid constructor");
+ }
+ }
+ for (int i = 0; i < fields.length; i++) {
+ if (fields[i].getField() == null) {
+ defaultSerializeEx = new InvalidClassException(
+ name, "unmatched serializable field(s) declared");
+ }
+ }
+ }
+
+ /**
+ * Creates blank class descriptor which should be initialized via a
+ * subsequent call to initProxy(), initNonProxy() or readNonProxy().
+ */
+ ObjectStreamClass() {
+ }
+
+ /**
+ * Initializes class descriptor representing a proxy class.
+ */
+ void initProxy(Class> cl,
+ ClassNotFoundException resolveEx,
+ ObjectStreamClass superDesc)
+ throws InvalidClassException
+ {
+ this.cl = cl;
+ this.resolveEx = resolveEx;
+ this.superDesc = superDesc;
+ isProxy = true;
+ serializable = true;
+ suid = Long.valueOf(0);
+ fields = NO_FIELDS;
+
+ if (cl != null) {
+ localDesc = lookup(cl, true);
+ if (!localDesc.isProxy) {
+ throw new InvalidClassException(
+ "cannot bind proxy descriptor to a non-proxy class");
+ }
+ name = localDesc.name;
+ externalizable = localDesc.externalizable;
+ cons = localDesc.cons;
+ writeReplaceMethod = localDesc.writeReplaceMethod;
+ readResolveMethod = localDesc.readResolveMethod;
+ deserializeEx = localDesc.deserializeEx;
+ }
+ }
+
+ /**
+ * Initializes class descriptor representing a non-proxy class.
+ */
+ void initNonProxy(ObjectStreamClass model,
+ Class> cl,
+ ClassNotFoundException resolveEx,
+ ObjectStreamClass superDesc)
+ throws InvalidClassException
+ {
+ this.cl = cl;
+ this.resolveEx = resolveEx;
+ this.superDesc = superDesc;
+ name = model.name;
+ suid = Long.valueOf(model.getSerialVersionUID());
+ isProxy = false;
+ isEnum = model.isEnum;
+ serializable = model.serializable;
+ externalizable = model.externalizable;
+ hasBlockExternalData = model.hasBlockExternalData;
+ hasWriteObjectData = model.hasWriteObjectData;
+ fields = model.fields;
+ primDataSize = model.primDataSize;
+ numObjFields = model.numObjFields;
+
+ if (cl != null) {
+ localDesc = lookup(cl, true);
+ if (localDesc.isProxy) {
+ throw new InvalidClassException(
+ "cannot bind non-proxy descriptor to a proxy class");
+ }
+ if (isEnum != localDesc.isEnum) {
+ throw new InvalidClassException(isEnum ?
+ "cannot bind enum descriptor to a non-enum class" :
+ "cannot bind non-enum descriptor to an enum class");
+ }
+
+ if (serializable == localDesc.serializable &&
+ !cl.isArray() &&
+ suid.longValue() != localDesc.getSerialVersionUID())
+ {
+ throw new InvalidClassException(localDesc.name,
+ "local class incompatible: " +
+ "stream classdesc serialVersionUID = " + suid +
+ ", local class serialVersionUID = " +
+ localDesc.getSerialVersionUID());
+ }
+
+ if (!classNamesEqual(name, localDesc.name)) {
+ throw new InvalidClassException(localDesc.name,
+ "local class name incompatible with stream class " +
+ "name \"" + name + "\"");
+ }
+
+ if (!isEnum) {
+ if ((serializable == localDesc.serializable) &&
+ (externalizable != localDesc.externalizable))
+ {
+ throw new InvalidClassException(localDesc.name,
+ "Serializable incompatible with Externalizable");
+ }
+
+ if ((serializable != localDesc.serializable) ||
+ (externalizable != localDesc.externalizable) ||
+ !(serializable || externalizable))
+ {
+ deserializeEx = new InvalidClassException(localDesc.name,
+ "class invalid for deserialization");
+ }
+ }
+
+ cons = localDesc.cons;
+ writeObjectMethod = localDesc.writeObjectMethod;
+ readObjectMethod = localDesc.readObjectMethod;
+ readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
+ writeReplaceMethod = localDesc.writeReplaceMethod;
+ readResolveMethod = localDesc.readResolveMethod;
+ if (deserializeEx == null) {
+ deserializeEx = localDesc.deserializeEx;
+ }
+ }
+ // reassign to matched fields so as to reflect local unshared settings
+ fields = null;
+ }
+
+ /**
+ * Reads non-proxy class descriptor information from given input stream.
+ * The resulting class descriptor is not fully functional; it can only be
+ * used as input to the ObjectInputStream.resolveClass() and
+ * ObjectStreamClass.initNonProxy() methods.
+ */
+ void readNonProxy(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ name = in.readUTF();
+ suid = Long.valueOf(in.readLong());
+ isProxy = false;
+
+ byte flags = in.readByte();
+ hasWriteObjectData =
+ ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
+ hasBlockExternalData =
+ ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
+ externalizable =
+ ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
+ boolean sflag =
+ ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
+ if (externalizable && sflag) {
+ throw new InvalidClassException(
+ name, "serializable and externalizable flags conflict");
+ }
+ serializable = externalizable || sflag;
+ isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
+ if (isEnum && suid.longValue() != 0L) {
+ throw new InvalidClassException(name,
+ "enum descriptor has non-zero serialVersionUID: " + suid);
+ }
+
+ int numFields = in.readShort();
+ if (isEnum && numFields != 0) {
+ throw new InvalidClassException(name,
+ "enum descriptor has non-zero field count: " + numFields);
+ }
+ fields = (numFields > 0) ?
+ new ObjectStreamField[numFields] : NO_FIELDS;
+ for (int i = 0; i < numFields; i++) {
+ char tcode = (char) in.readByte();
+ String fname = in.readUTF();
+ String signature = ((tcode == 'L') || (tcode == '[')) ?
+ in.readTypeString() : new String(new char[] { tcode });
+ try {
+ fields[i] = new ObjectStreamField(fname, signature, false);
+ } catch (RuntimeException e) {
+ throw (IOException) new InvalidClassException(name,
+ "invalid descriptor for field " + fname).initCause(e);
+ }
+ }
+ computeFieldOffsets();
+ }
+
+ /**
+ * Writes non-proxy class descriptor information to given output stream.
+ */
+ void writeNonProxy(ObjectOutputStream out) throws IOException {
+ out.writeUTF(name);
+ out.writeLong(getSerialVersionUID());
+
+ byte flags = 0;
+ if (externalizable) {
+ flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
+ int protocol = out.getProtocolVersion();
+ if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
+ flags |= ObjectStreamConstants.SC_BLOCK_DATA;
+ }
+ } else if (serializable) {
+ flags |= ObjectStreamConstants.SC_SERIALIZABLE;
+ }
+ if (hasWriteObjectData) {
+ flags |= ObjectStreamConstants.SC_WRITE_METHOD;
+ }
+ if (isEnum) {
+ flags |= ObjectStreamConstants.SC_ENUM;
+ }
+ out.writeByte(flags);
+
+ out.writeShort(fields.length);
+ for (int i = 0; i < fields.length; i++) {
+ ObjectStreamField f = fields[i];
+ out.writeByte(f.getTypeCode());
+ out.writeUTF(f.getName());
+ if (!f.isPrimitive()) {
+ out.writeTypeString(f.getTypeString());
+ }
+ }
+ }
+
+ /**
+ * Returns ClassNotFoundException (if any) thrown while attempting to
+ * resolve local class corresponding to this class descriptor.
+ */
+ ClassNotFoundException getResolveException() {
+ return resolveEx;
+ }
+
+ /**
+ * Throws an InvalidClassException if object instances referencing this
+ * class descriptor should not be allowed to deserialize. This method does
+ * not apply to deserialization of enum constants.
+ */
+ void checkDeserialize() throws InvalidClassException {
+ if (deserializeEx != null) {
+ InvalidClassException ice =
+ new InvalidClassException(deserializeEx.classname,
+ deserializeEx.getMessage());
+ ice.initCause(deserializeEx);
+ throw ice;
+ }
+ }
+
+ /**
+ * Throws an InvalidClassException if objects whose class is represented by
+ * this descriptor should not be allowed to serialize. This method does
+ * not apply to serialization of enum constants.
+ */
+ void checkSerialize() throws InvalidClassException {
+ if (serializeEx != null) {
+ InvalidClassException ice =
+ new InvalidClassException(serializeEx.classname,
+ serializeEx.getMessage());
+ ice.initCause(serializeEx);
+ throw ice;
+ }
+ }
+
+ /**
+ * Throws an InvalidClassException if objects whose class is represented by
+ * this descriptor should not be permitted to use default serialization
+ * (e.g., if the class declares serializable fields that do not correspond
+ * to actual fields, and hence must use the GetField API). This method
+ * does not apply to deserialization of enum constants.
+ */
+ void checkDefaultSerialize() throws InvalidClassException {
+ if (defaultSerializeEx != null) {
+ InvalidClassException ice =
+ new InvalidClassException(defaultSerializeEx.classname,
+ defaultSerializeEx.getMessage());
+ ice.initCause(defaultSerializeEx);
+ throw ice;
+ }
+ }
+
+ /**
+ * Returns superclass descriptor. Note that on the receiving side, the
+ * superclass descriptor may be bound to a class that is not a superclass
+ * of the subclass descriptor's bound class.
+ */
+ ObjectStreamClass getSuperDesc() {
+ return superDesc;
+ }
+
+ /**
+ * Returns the "local" class descriptor for the class associated with this
+ * class descriptor (i.e., the result of
+ * ObjectStreamClass.lookup(this.forClass())) or null if there is no class
+ * associated with this descriptor.
+ */
+ ObjectStreamClass getLocalDesc() {
+ return localDesc;
+ }
+
+ /**
+ * Returns arrays of ObjectStreamFields representing the serializable
+ * fields of the represented class. If copy is true, a clone of this class
+ * descriptor's field array is returned, otherwise the array itself is
+ * returned.
+ */
+ ObjectStreamField[] getFields(boolean copy) {
+ return copy ? fields.clone() : fields;
+ }
+
+ /**
+ * Looks up a serializable field of the represented class by name and type.
+ * A specified type of null matches all types, Object.class matches all
+ * non-primitive types, and any other non-null type matches assignable
+ * types only. Returns matching field, or null if no match found.
+ */
+ ObjectStreamField getField(String name, Class> type) {
+ for (int i = 0; i < fields.length; i++) {
+ ObjectStreamField f = fields[i];
+ if (f.getName().equals(name)) {
+ if (type == null ||
+ (type == Object.class && !f.isPrimitive()))
+ {
+ return f;
+ }
+ Class> ftype = f.getType();
+ if (ftype != null && type.isAssignableFrom(ftype)) {
+ return f;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if class descriptor represents a dynamic proxy class, false
+ * otherwise.
+ */
+ boolean isProxy() {
+ return isProxy;
+ }
+
+ /**
+ * Returns true if class descriptor represents an enum type, false
+ * otherwise.
+ */
+ boolean isEnum() {
+ return isEnum;
+ }
+
+ /**
+ * Returns true if represented class implements Externalizable, false
+ * otherwise.
+ */
+ boolean isExternalizable() {
+ return externalizable;
+ }
+
+ /**
+ * Returns true if represented class implements Serializable, false
+ * otherwise.
+ */
+ boolean isSerializable() {
+ return serializable;
+ }
+
+ /**
+ * Returns true if class descriptor represents externalizable class that
+ * has written its data in 1.2 (block data) format, false otherwise.
+ */
+ boolean hasBlockExternalData() {
+ return hasBlockExternalData;
+ }
+
+ /**
+ * Returns true if class descriptor represents serializable (but not
+ * externalizable) class which has written its data via a custom
+ * writeObject() method, false otherwise.
+ */
+ boolean hasWriteObjectData() {
+ return hasWriteObjectData;
+ }
+
+ /**
+ * Returns true if represented class is serializable/externalizable and can
+ * be instantiated by the serialization runtime--i.e., if it is
+ * externalizable and defines a public no-arg constructor, or if it is
+ * non-externalizable and its first non-serializable superclass defines an
+ * accessible no-arg constructor. Otherwise, returns false.
+ */
+ boolean isInstantiable() {
+ return (cons != null);
+ }
+
+ /**
+ * Returns true if represented class is serializable (but not
+ * externalizable) and defines a conformant writeObject method. Otherwise,
+ * returns false.
+ */
+ boolean hasWriteObjectMethod() {
+ return (writeObjectMethod != null);
+ }
+
+ /**
+ * Returns true if represented class is serializable (but not
+ * externalizable) and defines a conformant readObject method. Otherwise,
+ * returns false.
+ */
+ boolean hasReadObjectMethod() {
+ return (readObjectMethod != null);
+ }
+
+ /**
+ * Returns true if represented class is serializable (but not
+ * externalizable) and defines a conformant readObjectNoData method.
+ * Otherwise, returns false.
+ */
+ boolean hasReadObjectNoDataMethod() {
+ return (readObjectNoDataMethod != null);
+ }
+
+ /**
+ * Returns true if represented class is serializable or externalizable and
+ * defines a conformant writeReplace method. Otherwise, returns false.
+ */
+ boolean hasWriteReplaceMethod() {
+ return (writeReplaceMethod != null);
+ }
+
+ /**
+ * Returns true if represented class is serializable or externalizable and
+ * defines a conformant readResolve method. Otherwise, returns false.
+ */
+ boolean hasReadResolveMethod() {
+ return (readResolveMethod != null);
+ }
+
+ /**
+ * Creates a new instance of the represented class. If the class is
+ * externalizable, invokes its public no-arg constructor; otherwise, if the
+ * class is serializable, invokes the no-arg constructor of the first
+ * non-serializable superclass. Throws UnsupportedOperationException if
+ * this class descriptor is not associated with a class, if the associated
+ * class is non-serializable or if the appropriate no-arg constructor is
+ * inaccessible/unavailable.
+ */
+ Object newInstance()
+ throws InstantiationException, InvocationTargetException,
+ UnsupportedOperationException
+ {
+ if (cons != null) {
+ try {
+ return cons.newInstance();
+ } catch (IllegalAccessException ex) {
+ // should not occur, as access checks have been suppressed
+ throw new InternalError();
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Invokes the writeObject method of the represented serializable class.
+ * Throws UnsupportedOperationException if this class descriptor is not
+ * associated with a class, or if the class is externalizable,
+ * non-serializable or does not define writeObject.
+ */
+ void invokeWriteObject(Object obj, ObjectOutputStream out)
+ throws IOException, UnsupportedOperationException
+ {
+ if (writeObjectMethod != null) {
+ try {
+ writeObjectMethod.invoke(obj, new Object[]{ out });
+ } catch (InvocationTargetException ex) {
+ Throwable th = ex.getTargetException();
+ if (th instanceof IOException) {
+ throw (IOException) th;
+ } else {
+ throwMiscException(th);
+ }
+ } catch (IllegalAccessException ex) {
+ // should not occur, as access checks have been suppressed
+ throw new InternalError();
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Invokes the readObject method of the represented serializable class.
+ * Throws UnsupportedOperationException if this class descriptor is not
+ * associated with a class, or if the class is externalizable,
+ * non-serializable or does not define readObject.
+ */
+ void invokeReadObject(Object obj, ObjectInputStream in)
+ throws ClassNotFoundException, IOException,
+ UnsupportedOperationException
+ {
+ if (readObjectMethod != null) {
+ try {
+ readObjectMethod.invoke(obj, new Object[]{ in });
+ } catch (InvocationTargetException ex) {
+ Throwable th = ex.getTargetException();
+ if (th instanceof ClassNotFoundException) {
+ throw (ClassNotFoundException) th;
+ } else if (th instanceof IOException) {
+ throw (IOException) th;
+ } else {
+ throwMiscException(th);
+ }
+ } catch (IllegalAccessException ex) {
+ // should not occur, as access checks have been suppressed
+ throw new InternalError();
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Invokes the readObjectNoData method of the represented serializable
+ * class. Throws UnsupportedOperationException if this class descriptor is
+ * not associated with a class, or if the class is externalizable,
+ * non-serializable or does not define readObjectNoData.
+ */
+ void invokeReadObjectNoData(Object obj)
+ throws IOException, UnsupportedOperationException
+ {
+ if (readObjectNoDataMethod != null) {
+ try {
+ readObjectNoDataMethod.invoke(obj, (Object[]) null);
+ } catch (InvocationTargetException ex) {
+ Throwable th = ex.getTargetException();
+ if (th instanceof ObjectStreamException) {
+ throw (ObjectStreamException) th;
+ } else {
+ throwMiscException(th);
+ }
+ } catch (IllegalAccessException ex) {
+ // should not occur, as access checks have been suppressed
+ throw new InternalError();
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Invokes the writeReplace method of the represented serializable class and
+ * returns the result. Throws UnsupportedOperationException if this class
+ * descriptor is not associated with a class, or if the class is
+ * non-serializable or does not define writeReplace.
+ */
+ Object invokeWriteReplace(Object obj)
+ throws IOException, UnsupportedOperationException
+ {
+ if (writeReplaceMethod != null) {
+ try {
+ return writeReplaceMethod.invoke(obj, (Object[]) null);
+ } catch (InvocationTargetException ex) {
+ Throwable th = ex.getTargetException();
+ if (th instanceof ObjectStreamException) {
+ throw (ObjectStreamException) th;
+ } else {
+ throwMiscException(th);
+ throw new InternalError(); // never reached
+ }
+ } catch (IllegalAccessException ex) {
+ // should not occur, as access checks have been suppressed
+ throw new InternalError();
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Invokes the readResolve method of the represented serializable class and
+ * returns the result. Throws UnsupportedOperationException if this class
+ * descriptor is not associated with a class, or if the class is
+ * non-serializable or does not define readResolve.
+ */
+ Object invokeReadResolve(Object obj)
+ throws IOException, UnsupportedOperationException
+ {
+ if (readResolveMethod != null) {
+ try {
+ return readResolveMethod.invoke(obj, (Object[]) null);
+ } catch (InvocationTargetException ex) {
+ Throwable th = ex.getTargetException();
+ if (th instanceof ObjectStreamException) {
+ throw (ObjectStreamException) th;
+ } else {
+ throwMiscException(th);
+ throw new InternalError(); // never reached
+ }
+ } catch (IllegalAccessException ex) {
+ // should not occur, as access checks have been suppressed
+ throw new InternalError();
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Class representing the portion of an object's serialized form allotted
+ * to data described by a given class descriptor. If "hasData" is false,
+ * the object's serialized form does not contain data associated with the
+ * class descriptor.
+ */
+ static class ClassDataSlot {
+
+ /** class descriptor "occupying" this slot */
+ final ObjectStreamClass desc;
+ /** true if serialized form includes data for this slot's descriptor */
+ final boolean hasData;
+
+ ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
+ this.desc = desc;
+ this.hasData = hasData;
+ }
+ }
+
+ /**
+ * Returns array of ClassDataSlot instances representing the data layout
+ * (including superclass data) for serialized objects described by this
+ * class descriptor. ClassDataSlots are ordered by inheritance with those
+ * containing "higher" superclasses appearing first. The final
+ * ClassDataSlot contains a reference to this descriptor.
+ */
+ ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
+ // REMIND: synchronize instead of relying on volatile?
+ if (dataLayout == null) {
+ dataLayout = getClassDataLayout0();
+ }
+ return dataLayout;
+ }
+
+ private ClassDataSlot[] getClassDataLayout0()
+ throws InvalidClassException
+ {
+ ArrayList