jaroslav@601: /* jaroslav@601: * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. jaroslav@601: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@601: * jaroslav@601: * This code is free software; you can redistribute it and/or modify it jaroslav@601: * under the terms of the GNU General Public License version 2 only, as jaroslav@601: * published by the Free Software Foundation. Oracle designates this jaroslav@601: * particular file as subject to the "Classpath" exception as provided jaroslav@601: * by Oracle in the LICENSE file that accompanied this code. jaroslav@601: * jaroslav@601: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@601: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@601: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@601: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@601: * accompanied this code). jaroslav@601: * jaroslav@601: * You should have received a copy of the GNU General Public License version jaroslav@601: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@601: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@601: * jaroslav@601: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@601: * or visit www.oracle.com if you need additional information or have any jaroslav@601: * questions. jaroslav@601: */ jaroslav@601: jaroslav@601: package java.io; jaroslav@601: jaroslav@601: import java.lang.ref.Reference; jaroslav@601: import java.lang.ref.ReferenceQueue; jaroslav@601: import java.lang.ref.SoftReference; jaroslav@601: import java.lang.ref.WeakReference; jaroslav@601: import java.lang.reflect.Constructor; jaroslav@601: import java.lang.reflect.Field; jaroslav@601: import java.lang.reflect.InvocationTargetException; jaroslav@601: import java.lang.reflect.Member; jaroslav@601: import java.lang.reflect.Method; jaroslav@601: import java.lang.reflect.Modifier; jaroslav@601: import java.lang.reflect.Proxy; jaroslav@601: import java.util.ArrayList; jaroslav@601: import java.util.Arrays; jaroslav@601: import java.util.Collections; jaroslav@601: import java.util.Comparator; jaroslav@601: import java.util.HashSet; jaroslav@601: import java.util.Set; jaroslav@601: jaroslav@601: /** jaroslav@601: * Serialization's descriptor for classes. It contains the name and jaroslav@601: * serialVersionUID of the class. The ObjectStreamClass for a specific class jaroslav@601: * loaded in this Java VM can be found/created using the lookup method. jaroslav@601: * jaroslav@601: *

The algorithm to compute the SerialVersionUID is described in jaroslav@601: * Object jaroslav@601: * Serialization Specification, Section 4.6, Stream Unique Identifiers. jaroslav@601: * jaroslav@601: * @author Mike Warres jaroslav@601: * @author Roger Riggs jaroslav@601: * @see ObjectStreamField jaroslav@601: * @see Object Serialization Specification, Section 4, Class Descriptors jaroslav@601: * @since JDK1.1 jaroslav@601: */ jaroslav@601: public class ObjectStreamClass implements Serializable { jaroslav@601: jaroslav@601: /** serialPersistentFields value indicating no serializable fields */ jaroslav@601: public static final ObjectStreamField[] NO_FIELDS = jaroslav@601: new ObjectStreamField[0]; jaroslav@601: jaroslav@601: private static final long serialVersionUID = -6120832682080437368L; jaroslav@601: private static final ObjectStreamField[] serialPersistentFields = jaroslav@601: NO_FIELDS; jaroslav@601: jaroslav@601: jaroslav@601: /** class associated with this descriptor (if any) */ jaroslav@601: private Class cl; jaroslav@601: /** name of class represented by this descriptor */ jaroslav@601: private String name; jaroslav@601: /** serialVersionUID of represented class (null if not computed yet) */ jaroslav@601: private volatile Long suid; jaroslav@601: jaroslav@601: /** true if represents dynamic proxy class */ jaroslav@601: private boolean isProxy; jaroslav@601: /** true if represents enum type */ jaroslav@601: private boolean isEnum; jaroslav@601: /** true if represented class implements Serializable */ jaroslav@601: private boolean serializable; jaroslav@601: /** true if represented class implements Externalizable */ jaroslav@601: private boolean externalizable; jaroslav@601: /** true if desc has data written by class-defined writeObject method */ jaroslav@601: private boolean hasWriteObjectData; jaroslav@601: /** jaroslav@601: * true if desc has externalizable data written in block data format; this jaroslav@601: * must be true by default to accommodate ObjectInputStream subclasses which jaroslav@601: * override readClassDescriptor() to return class descriptors obtained from jaroslav@601: * ObjectStreamClass.lookup() (see 4461737) jaroslav@601: */ jaroslav@601: private boolean hasBlockExternalData = true; jaroslav@601: jaroslav@601: /** exception (if any) thrown while attempting to resolve class */ jaroslav@601: private ClassNotFoundException resolveEx; jaroslav@601: /** exception (if any) to throw if non-enum deserialization attempted */ jaroslav@601: private InvalidClassException deserializeEx; jaroslav@601: /** exception (if any) to throw if non-enum serialization attempted */ jaroslav@601: private InvalidClassException serializeEx; jaroslav@601: /** exception (if any) to throw if default serialization attempted */ jaroslav@601: private InvalidClassException defaultSerializeEx; jaroslav@601: jaroslav@601: /** serializable fields */ jaroslav@601: private ObjectStreamField[] fields; jaroslav@601: /** aggregate marshalled size of primitive fields */ jaroslav@601: private int primDataSize; jaroslav@601: /** number of non-primitive fields */ jaroslav@601: private int numObjFields; jaroslav@601: /** reflector for setting/getting serializable field values */ jaroslav@604: // private FieldReflector fieldRefl; jaroslav@601: /** data layout of serialized objects described by this class desc */ jaroslav@601: private volatile ClassDataSlot[] dataLayout; jaroslav@601: jaroslav@601: /** serialization-appropriate constructor, or null if none */ jaroslav@601: private Constructor cons; jaroslav@601: /** class-defined writeObject method, or null if none */ jaroslav@601: private Method writeObjectMethod; jaroslav@601: /** class-defined readObject method, or null if none */ jaroslav@601: private Method readObjectMethod; jaroslav@601: /** class-defined readObjectNoData method, or null if none */ jaroslav@601: private Method readObjectNoDataMethod; jaroslav@601: /** class-defined writeReplace method, or null if none */ jaroslav@601: private Method writeReplaceMethod; jaroslav@601: /** class-defined readResolve method, or null if none */ jaroslav@601: private Method readResolveMethod; jaroslav@601: jaroslav@601: /** local class descriptor for represented class (may point to self) */ jaroslav@601: private ObjectStreamClass localDesc; jaroslav@601: /** superclass descriptor appearing in stream */ jaroslav@601: private ObjectStreamClass superDesc; jaroslav@601: jaroslav@601: /** jaroslav@601: * Initializes native code. jaroslav@601: */ jaroslav@601: private static native void initNative(); jaroslav@601: static { jaroslav@601: initNative(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Find the descriptor for a class that can be serialized. Creates an jaroslav@601: * ObjectStreamClass instance if one does not exist yet for class. Null is jaroslav@601: * returned if the specified class does not implement java.io.Serializable jaroslav@601: * or java.io.Externalizable. jaroslav@601: * jaroslav@601: * @param cl class for which to get the descriptor jaroslav@601: * @return the class descriptor for the specified class jaroslav@601: */ jaroslav@601: public static ObjectStreamClass lookup(Class cl) { jaroslav@601: return lookup(cl, false); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns the descriptor for any class, regardless of whether it jaroslav@601: * implements {@link Serializable}. jaroslav@601: * jaroslav@601: * @param cl class for which to get the descriptor jaroslav@601: * @return the class descriptor for the specified class jaroslav@601: * @since 1.6 jaroslav@601: */ jaroslav@601: public static ObjectStreamClass lookupAny(Class cl) { jaroslav@601: return lookup(cl, true); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns the name of the class described by this descriptor. jaroslav@601: * This method returns the name of the class in the format that jaroslav@601: * is used by the {@link Class#getName} method. jaroslav@601: * jaroslav@601: * @return a string representing the name of the class jaroslav@601: */ jaroslav@601: public String getName() { jaroslav@601: return name; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Return the serialVersionUID for this class. The serialVersionUID jaroslav@601: * defines a set of classes all with the same name that have evolved from a jaroslav@601: * common root class and agree to be serialized and deserialized using a jaroslav@601: * common format. NonSerializable classes have a serialVersionUID of 0L. jaroslav@601: * jaroslav@601: * @return the SUID of the class described by this descriptor jaroslav@601: */ jaroslav@601: public long getSerialVersionUID() { jaroslav@601: // REMIND: synchronize instead of relying on volatile? jaroslav@601: if (suid == null) { jaroslav@604: return computeDefaultSUID(cl); jaroslav@601: } jaroslav@601: return suid.longValue(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Return the class in the local VM that this version is mapped to. Null jaroslav@601: * is returned if there is no corresponding local class. jaroslav@601: * jaroslav@601: * @return the Class instance that this descriptor represents jaroslav@601: */ jaroslav@601: public Class forClass() { jaroslav@601: return cl; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Return an array of the fields of this serializable class. jaroslav@601: * jaroslav@601: * @return an array containing an element for each persistent field of jaroslav@601: * this class. Returns an array of length zero if there are no jaroslav@601: * fields. jaroslav@601: * @since 1.2 jaroslav@601: */ jaroslav@601: public ObjectStreamField[] getFields() { jaroslav@601: return getFields(true); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Get the field of this class by name. jaroslav@601: * jaroslav@601: * @param name the name of the data field to look for jaroslav@601: * @return The ObjectStreamField object of the named field or null if jaroslav@601: * there is no such named field. jaroslav@601: */ jaroslav@601: public ObjectStreamField getField(String name) { jaroslav@601: return getField(name, null); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Return a string describing this ObjectStreamClass. jaroslav@601: */ jaroslav@601: public String toString() { jaroslav@601: return name + ": static final long serialVersionUID = " + jaroslav@601: getSerialVersionUID() + "L;"; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Looks up and returns class descriptor for given class, or null if class jaroslav@601: * is non-serializable and "all" is set to false. jaroslav@601: * jaroslav@601: * @param cl class to look up jaroslav@601: * @param all if true, return descriptors for all classes; if false, only jaroslav@601: * return descriptors for serializable classes jaroslav@601: */ jaroslav@601: static ObjectStreamClass lookup(Class cl, boolean all) { jaroslav@601: if (!(all || Serializable.class.isAssignableFrom(cl))) { jaroslav@601: return null; jaroslav@601: } jaroslav@601: Object entry = null; jaroslav@601: EntryFuture future = null; jaroslav@601: if (entry == null) { jaroslav@601: EntryFuture newEntry = new EntryFuture(); jaroslav@601: Reference newRef = new SoftReference<>(newEntry); jaroslav@601: if (entry == null) { jaroslav@601: future = newEntry; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: if (entry instanceof ObjectStreamClass) { // check common case first jaroslav@601: return (ObjectStreamClass) entry; jaroslav@601: } jaroslav@601: if (entry instanceof EntryFuture) { jaroslav@601: future = (EntryFuture) entry; jaroslav@604: if (true) { jaroslav@601: /* jaroslav@601: * Handle nested call situation described by 4803747: waiting jaroslav@601: * for future value to be set by a lookup() call further up the jaroslav@601: * stack will result in deadlock, so calculate and set the jaroslav@601: * future value here instead. jaroslav@601: */ jaroslav@601: entry = null; jaroslav@601: } else { jaroslav@601: entry = future.get(); jaroslav@601: } jaroslav@601: } jaroslav@601: if (entry == null) { jaroslav@601: try { jaroslav@601: entry = new ObjectStreamClass(cl); jaroslav@601: } catch (Throwable th) { jaroslav@601: entry = th; jaroslav@601: } jaroslav@604: // nested lookup call already set future jaroslav@604: entry = future.get(); jaroslav@601: } jaroslav@601: jaroslav@601: if (entry instanceof ObjectStreamClass) { jaroslav@601: return (ObjectStreamClass) entry; jaroslav@601: } else if (entry instanceof RuntimeException) { jaroslav@601: throw (RuntimeException) entry; jaroslav@601: } else if (entry instanceof Error) { jaroslav@601: throw (Error) entry; jaroslav@601: } else { jaroslav@601: throw new InternalError("unexpected entry: " + entry); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Placeholder used in class descriptor and field reflector lookup tables jaroslav@601: * for an entry in the process of being initialized. (Internal) callers jaroslav@601: * which receive an EntryFuture belonging to another thread as the result jaroslav@601: * of a lookup should call the get() method of the EntryFuture; this will jaroslav@601: * return the actual entry once it is ready for use and has been set(). To jaroslav@601: * conserve objects, EntryFutures synchronize on themselves. jaroslav@601: */ jaroslav@601: private static class EntryFuture { jaroslav@601: jaroslav@601: private static final Object unset = new Object(); jaroslav@601: private Object entry = unset; jaroslav@601: jaroslav@601: /** jaroslav@601: * Attempts to set the value contained by this EntryFuture. If the jaroslav@601: * EntryFuture's value has not been set already, then the value is jaroslav@601: * saved, any callers blocked in the get() method are notified, and jaroslav@601: * true is returned. If the value has already been set, then no saving jaroslav@601: * or notification occurs, and false is returned. jaroslav@601: */ jaroslav@601: synchronized boolean set(Object entry) { jaroslav@601: if (this.entry != unset) { jaroslav@601: return false; jaroslav@601: } jaroslav@601: this.entry = entry; jaroslav@601: notifyAll(); jaroslav@601: return true; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns the value contained by this EntryFuture, blocking if jaroslav@601: * necessary until a value is set. jaroslav@601: */ jaroslav@601: synchronized Object get() { jaroslav@601: boolean interrupted = false; jaroslav@601: while (entry == unset) { jaroslav@601: try { jaroslav@601: wait(); jaroslav@601: } catch (InterruptedException ex) { jaroslav@601: interrupted = true; jaroslav@601: } jaroslav@601: } jaroslav@601: return entry; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates local class descriptor representing given class. jaroslav@601: */ jaroslav@601: private ObjectStreamClass(final Class cl) { jaroslav@601: this.cl = cl; jaroslav@601: name = cl.getName(); jaroslav@601: isProxy = Proxy.isProxyClass(cl); jaroslav@601: isEnum = Enum.class.isAssignableFrom(cl); jaroslav@601: serializable = Serializable.class.isAssignableFrom(cl); jaroslav@601: externalizable = Externalizable.class.isAssignableFrom(cl); jaroslav@601: jaroslav@601: Class superCl = cl.getSuperclass(); jaroslav@601: superDesc = (superCl != null) ? lookup(superCl, false) : null; jaroslav@601: localDesc = this; jaroslav@601: jaroslav@604: suid = Long.valueOf(0); jaroslav@604: fields = NO_FIELDS; jaroslav@601: jaroslav@601: jaroslav@601: if (deserializeEx == null) { jaroslav@601: if (isEnum) { jaroslav@601: deserializeEx = new InvalidClassException(name, "enum type"); jaroslav@601: } else if (cons == null) { jaroslav@601: deserializeEx = new InvalidClassException( jaroslav@601: name, "no valid constructor"); jaroslav@601: } jaroslav@601: } jaroslav@601: for (int i = 0; i < fields.length; i++) { jaroslav@601: if (fields[i].getField() == null) { jaroslav@601: defaultSerializeEx = new InvalidClassException( jaroslav@601: name, "unmatched serializable field(s) declared"); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates blank class descriptor which should be initialized via a jaroslav@601: * subsequent call to initProxy(), initNonProxy() or readNonProxy(). jaroslav@601: */ jaroslav@601: ObjectStreamClass() { jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Initializes class descriptor representing a proxy class. jaroslav@601: */ jaroslav@601: void initProxy(Class cl, jaroslav@601: ClassNotFoundException resolveEx, jaroslav@601: ObjectStreamClass superDesc) jaroslav@601: throws InvalidClassException jaroslav@601: { jaroslav@601: this.cl = cl; jaroslav@601: this.resolveEx = resolveEx; jaroslav@601: this.superDesc = superDesc; jaroslav@601: isProxy = true; jaroslav@601: serializable = true; jaroslav@601: suid = Long.valueOf(0); jaroslav@601: fields = NO_FIELDS; jaroslav@601: jaroslav@601: if (cl != null) { jaroslav@601: localDesc = lookup(cl, true); jaroslav@601: if (!localDesc.isProxy) { jaroslav@601: throw new InvalidClassException( jaroslav@601: "cannot bind proxy descriptor to a non-proxy class"); jaroslav@601: } jaroslav@601: name = localDesc.name; jaroslav@601: externalizable = localDesc.externalizable; jaroslav@601: cons = localDesc.cons; jaroslav@601: writeReplaceMethod = localDesc.writeReplaceMethod; jaroslav@601: readResolveMethod = localDesc.readResolveMethod; jaroslav@601: deserializeEx = localDesc.deserializeEx; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Initializes class descriptor representing a non-proxy class. jaroslav@601: */ jaroslav@601: void initNonProxy(ObjectStreamClass model, jaroslav@601: Class cl, jaroslav@601: ClassNotFoundException resolveEx, jaroslav@601: ObjectStreamClass superDesc) jaroslav@601: throws InvalidClassException jaroslav@601: { jaroslav@601: this.cl = cl; jaroslav@601: this.resolveEx = resolveEx; jaroslav@601: this.superDesc = superDesc; jaroslav@601: name = model.name; jaroslav@601: suid = Long.valueOf(model.getSerialVersionUID()); jaroslav@601: isProxy = false; jaroslav@601: isEnum = model.isEnum; jaroslav@601: serializable = model.serializable; jaroslav@601: externalizable = model.externalizable; jaroslav@601: hasBlockExternalData = model.hasBlockExternalData; jaroslav@601: hasWriteObjectData = model.hasWriteObjectData; jaroslav@601: fields = model.fields; jaroslav@601: primDataSize = model.primDataSize; jaroslav@601: numObjFields = model.numObjFields; jaroslav@601: jaroslav@601: if (cl != null) { jaroslav@601: localDesc = lookup(cl, true); jaroslav@601: if (localDesc.isProxy) { jaroslav@601: throw new InvalidClassException( jaroslav@601: "cannot bind non-proxy descriptor to a proxy class"); jaroslav@601: } jaroslav@601: if (isEnum != localDesc.isEnum) { jaroslav@601: throw new InvalidClassException(isEnum ? jaroslav@601: "cannot bind enum descriptor to a non-enum class" : jaroslav@601: "cannot bind non-enum descriptor to an enum class"); jaroslav@601: } jaroslav@601: jaroslav@601: if (serializable == localDesc.serializable && jaroslav@601: !cl.isArray() && jaroslav@601: suid.longValue() != localDesc.getSerialVersionUID()) jaroslav@601: { jaroslav@601: throw new InvalidClassException(localDesc.name, jaroslav@601: "local class incompatible: " + jaroslav@601: "stream classdesc serialVersionUID = " + suid + jaroslav@601: ", local class serialVersionUID = " + jaroslav@601: localDesc.getSerialVersionUID()); jaroslav@601: } jaroslav@601: jaroslav@601: if (!classNamesEqual(name, localDesc.name)) { jaroslav@601: throw new InvalidClassException(localDesc.name, jaroslav@601: "local class name incompatible with stream class " + jaroslav@601: "name \"" + name + "\""); jaroslav@601: } jaroslav@601: jaroslav@601: if (!isEnum) { jaroslav@601: if ((serializable == localDesc.serializable) && jaroslav@601: (externalizable != localDesc.externalizable)) jaroslav@601: { jaroslav@601: throw new InvalidClassException(localDesc.name, jaroslav@601: "Serializable incompatible with Externalizable"); jaroslav@601: } jaroslav@601: jaroslav@601: if ((serializable != localDesc.serializable) || jaroslav@601: (externalizable != localDesc.externalizable) || jaroslav@601: !(serializable || externalizable)) jaroslav@601: { jaroslav@601: deserializeEx = new InvalidClassException(localDesc.name, jaroslav@601: "class invalid for deserialization"); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: cons = localDesc.cons; jaroslav@601: writeObjectMethod = localDesc.writeObjectMethod; jaroslav@601: readObjectMethod = localDesc.readObjectMethod; jaroslav@601: readObjectNoDataMethod = localDesc.readObjectNoDataMethod; jaroslav@601: writeReplaceMethod = localDesc.writeReplaceMethod; jaroslav@601: readResolveMethod = localDesc.readResolveMethod; jaroslav@601: if (deserializeEx == null) { jaroslav@601: deserializeEx = localDesc.deserializeEx; jaroslav@601: } jaroslav@601: } jaroslav@601: // reassign to matched fields so as to reflect local unshared settings jaroslav@604: fields = null; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Reads non-proxy class descriptor information from given input stream. jaroslav@601: * The resulting class descriptor is not fully functional; it can only be jaroslav@601: * used as input to the ObjectInputStream.resolveClass() and jaroslav@601: * ObjectStreamClass.initNonProxy() methods. jaroslav@601: */ jaroslav@601: void readNonProxy(ObjectInputStream in) jaroslav@601: throws IOException, ClassNotFoundException jaroslav@601: { jaroslav@601: name = in.readUTF(); jaroslav@601: suid = Long.valueOf(in.readLong()); jaroslav@601: isProxy = false; jaroslav@601: jaroslav@601: byte flags = in.readByte(); jaroslav@601: hasWriteObjectData = jaroslav@601: ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0); jaroslav@601: hasBlockExternalData = jaroslav@601: ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0); jaroslav@601: externalizable = jaroslav@601: ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0); jaroslav@601: boolean sflag = jaroslav@601: ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0); jaroslav@601: if (externalizable && sflag) { jaroslav@601: throw new InvalidClassException( jaroslav@601: name, "serializable and externalizable flags conflict"); jaroslav@601: } jaroslav@601: serializable = externalizable || sflag; jaroslav@601: isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0); jaroslav@601: if (isEnum && suid.longValue() != 0L) { jaroslav@601: throw new InvalidClassException(name, jaroslav@601: "enum descriptor has non-zero serialVersionUID: " + suid); jaroslav@601: } jaroslav@601: jaroslav@601: int numFields = in.readShort(); jaroslav@601: if (isEnum && numFields != 0) { jaroslav@601: throw new InvalidClassException(name, jaroslav@601: "enum descriptor has non-zero field count: " + numFields); jaroslav@601: } jaroslav@601: fields = (numFields > 0) ? jaroslav@601: new ObjectStreamField[numFields] : NO_FIELDS; jaroslav@601: for (int i = 0; i < numFields; i++) { jaroslav@601: char tcode = (char) in.readByte(); jaroslav@601: String fname = in.readUTF(); jaroslav@601: String signature = ((tcode == 'L') || (tcode == '[')) ? jaroslav@601: in.readTypeString() : new String(new char[] { tcode }); jaroslav@601: try { jaroslav@601: fields[i] = new ObjectStreamField(fname, signature, false); jaroslav@601: } catch (RuntimeException e) { jaroslav@601: throw (IOException) new InvalidClassException(name, jaroslav@601: "invalid descriptor for field " + fname).initCause(e); jaroslav@601: } jaroslav@601: } jaroslav@601: computeFieldOffsets(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Writes non-proxy class descriptor information to given output stream. jaroslav@601: */ jaroslav@601: void writeNonProxy(ObjectOutputStream out) throws IOException { jaroslav@601: out.writeUTF(name); jaroslav@601: out.writeLong(getSerialVersionUID()); jaroslav@601: jaroslav@601: byte flags = 0; jaroslav@601: if (externalizable) { jaroslav@601: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; jaroslav@601: int protocol = out.getProtocolVersion(); jaroslav@601: if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) { jaroslav@601: flags |= ObjectStreamConstants.SC_BLOCK_DATA; jaroslav@601: } jaroslav@601: } else if (serializable) { jaroslav@601: flags |= ObjectStreamConstants.SC_SERIALIZABLE; jaroslav@601: } jaroslav@601: if (hasWriteObjectData) { jaroslav@601: flags |= ObjectStreamConstants.SC_WRITE_METHOD; jaroslav@601: } jaroslav@601: if (isEnum) { jaroslav@601: flags |= ObjectStreamConstants.SC_ENUM; jaroslav@601: } jaroslav@601: out.writeByte(flags); jaroslav@601: jaroslav@601: out.writeShort(fields.length); jaroslav@601: for (int i = 0; i < fields.length; i++) { jaroslav@601: ObjectStreamField f = fields[i]; jaroslav@601: out.writeByte(f.getTypeCode()); jaroslav@601: out.writeUTF(f.getName()); jaroslav@601: if (!f.isPrimitive()) { jaroslav@601: out.writeTypeString(f.getTypeString()); jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns ClassNotFoundException (if any) thrown while attempting to jaroslav@601: * resolve local class corresponding to this class descriptor. jaroslav@601: */ jaroslav@601: ClassNotFoundException getResolveException() { jaroslav@601: return resolveEx; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Throws an InvalidClassException if object instances referencing this jaroslav@601: * class descriptor should not be allowed to deserialize. This method does jaroslav@601: * not apply to deserialization of enum constants. jaroslav@601: */ jaroslav@601: void checkDeserialize() throws InvalidClassException { jaroslav@601: if (deserializeEx != null) { jaroslav@601: InvalidClassException ice = jaroslav@601: new InvalidClassException(deserializeEx.classname, jaroslav@601: deserializeEx.getMessage()); jaroslav@601: ice.initCause(deserializeEx); jaroslav@601: throw ice; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Throws an InvalidClassException if objects whose class is represented by jaroslav@601: * this descriptor should not be allowed to serialize. This method does jaroslav@601: * not apply to serialization of enum constants. jaroslav@601: */ jaroslav@601: void checkSerialize() throws InvalidClassException { jaroslav@601: if (serializeEx != null) { jaroslav@601: InvalidClassException ice = jaroslav@601: new InvalidClassException(serializeEx.classname, jaroslav@601: serializeEx.getMessage()); jaroslav@601: ice.initCause(serializeEx); jaroslav@601: throw ice; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Throws an InvalidClassException if objects whose class is represented by jaroslav@601: * this descriptor should not be permitted to use default serialization jaroslav@601: * (e.g., if the class declares serializable fields that do not correspond jaroslav@601: * to actual fields, and hence must use the GetField API). This method jaroslav@601: * does not apply to deserialization of enum constants. jaroslav@601: */ jaroslav@601: void checkDefaultSerialize() throws InvalidClassException { jaroslav@601: if (defaultSerializeEx != null) { jaroslav@601: InvalidClassException ice = jaroslav@601: new InvalidClassException(defaultSerializeEx.classname, jaroslav@601: defaultSerializeEx.getMessage()); jaroslav@601: ice.initCause(defaultSerializeEx); jaroslav@601: throw ice; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns superclass descriptor. Note that on the receiving side, the jaroslav@601: * superclass descriptor may be bound to a class that is not a superclass jaroslav@601: * of the subclass descriptor's bound class. jaroslav@601: */ jaroslav@601: ObjectStreamClass getSuperDesc() { jaroslav@601: return superDesc; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns the "local" class descriptor for the class associated with this jaroslav@601: * class descriptor (i.e., the result of jaroslav@601: * ObjectStreamClass.lookup(this.forClass())) or null if there is no class jaroslav@601: * associated with this descriptor. jaroslav@601: */ jaroslav@601: ObjectStreamClass getLocalDesc() { jaroslav@601: return localDesc; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns arrays of ObjectStreamFields representing the serializable jaroslav@601: * fields of the represented class. If copy is true, a clone of this class jaroslav@601: * descriptor's field array is returned, otherwise the array itself is jaroslav@601: * returned. jaroslav@601: */ jaroslav@601: ObjectStreamField[] getFields(boolean copy) { jaroslav@601: return copy ? fields.clone() : fields; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Looks up a serializable field of the represented class by name and type. jaroslav@601: * A specified type of null matches all types, Object.class matches all jaroslav@601: * non-primitive types, and any other non-null type matches assignable jaroslav@601: * types only. Returns matching field, or null if no match found. jaroslav@601: */ jaroslav@601: ObjectStreamField getField(String name, Class type) { jaroslav@601: for (int i = 0; i < fields.length; i++) { jaroslav@601: ObjectStreamField f = fields[i]; jaroslav@601: if (f.getName().equals(name)) { jaroslav@601: if (type == null || jaroslav@601: (type == Object.class && !f.isPrimitive())) jaroslav@601: { jaroslav@601: return f; jaroslav@601: } jaroslav@601: Class ftype = f.getType(); jaroslav@601: if (ftype != null && type.isAssignableFrom(ftype)) { jaroslav@601: return f; jaroslav@601: } jaroslav@601: } jaroslav@601: } jaroslav@601: return null; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if class descriptor represents a dynamic proxy class, false jaroslav@601: * otherwise. jaroslav@601: */ jaroslav@601: boolean isProxy() { jaroslav@601: return isProxy; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if class descriptor represents an enum type, false jaroslav@601: * otherwise. jaroslav@601: */ jaroslav@601: boolean isEnum() { jaroslav@601: return isEnum; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class implements Externalizable, false jaroslav@601: * otherwise. jaroslav@601: */ jaroslav@601: boolean isExternalizable() { jaroslav@601: return externalizable; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class implements Serializable, false jaroslav@601: * otherwise. jaroslav@601: */ jaroslav@601: boolean isSerializable() { jaroslav@601: return serializable; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if class descriptor represents externalizable class that jaroslav@601: * has written its data in 1.2 (block data) format, false otherwise. jaroslav@601: */ jaroslav@601: boolean hasBlockExternalData() { jaroslav@601: return hasBlockExternalData; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if class descriptor represents serializable (but not jaroslav@601: * externalizable) class which has written its data via a custom jaroslav@601: * writeObject() method, false otherwise. jaroslav@601: */ jaroslav@601: boolean hasWriteObjectData() { jaroslav@601: return hasWriteObjectData; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class is serializable/externalizable and can jaroslav@601: * be instantiated by the serialization runtime--i.e., if it is jaroslav@601: * externalizable and defines a public no-arg constructor, or if it is jaroslav@601: * non-externalizable and its first non-serializable superclass defines an jaroslav@601: * accessible no-arg constructor. Otherwise, returns false. jaroslav@601: */ jaroslav@601: boolean isInstantiable() { jaroslav@601: return (cons != null); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class is serializable (but not jaroslav@601: * externalizable) and defines a conformant writeObject method. Otherwise, jaroslav@601: * returns false. jaroslav@601: */ jaroslav@601: boolean hasWriteObjectMethod() { jaroslav@601: return (writeObjectMethod != null); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class is serializable (but not jaroslav@601: * externalizable) and defines a conformant readObject method. Otherwise, jaroslav@601: * returns false. jaroslav@601: */ jaroslav@601: boolean hasReadObjectMethod() { jaroslav@601: return (readObjectMethod != null); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class is serializable (but not jaroslav@601: * externalizable) and defines a conformant readObjectNoData method. jaroslav@601: * Otherwise, returns false. jaroslav@601: */ jaroslav@601: boolean hasReadObjectNoDataMethod() { jaroslav@601: return (readObjectNoDataMethod != null); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class is serializable or externalizable and jaroslav@601: * defines a conformant writeReplace method. Otherwise, returns false. jaroslav@601: */ jaroslav@601: boolean hasWriteReplaceMethod() { jaroslav@601: return (writeReplaceMethod != null); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if represented class is serializable or externalizable and jaroslav@601: * defines a conformant readResolve method. Otherwise, returns false. jaroslav@601: */ jaroslav@601: boolean hasReadResolveMethod() { jaroslav@601: return (readResolveMethod != null); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Creates a new instance of the represented class. If the class is jaroslav@601: * externalizable, invokes its public no-arg constructor; otherwise, if the jaroslav@601: * class is serializable, invokes the no-arg constructor of the first jaroslav@601: * non-serializable superclass. Throws UnsupportedOperationException if jaroslav@601: * this class descriptor is not associated with a class, if the associated jaroslav@601: * class is non-serializable or if the appropriate no-arg constructor is jaroslav@601: * inaccessible/unavailable. jaroslav@601: */ jaroslav@601: Object newInstance() jaroslav@601: throws InstantiationException, InvocationTargetException, jaroslav@601: UnsupportedOperationException jaroslav@601: { jaroslav@601: if (cons != null) { jaroslav@601: try { jaroslav@601: return cons.newInstance(); jaroslav@601: } catch (IllegalAccessException ex) { jaroslav@601: // should not occur, as access checks have been suppressed jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: throw new UnsupportedOperationException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Invokes the writeObject method of the represented serializable class. jaroslav@601: * Throws UnsupportedOperationException if this class descriptor is not jaroslav@601: * associated with a class, or if the class is externalizable, jaroslav@601: * non-serializable or does not define writeObject. jaroslav@601: */ jaroslav@601: void invokeWriteObject(Object obj, ObjectOutputStream out) jaroslav@601: throws IOException, UnsupportedOperationException jaroslav@601: { jaroslav@601: if (writeObjectMethod != null) { jaroslav@601: try { jaroslav@601: writeObjectMethod.invoke(obj, new Object[]{ out }); jaroslav@601: } catch (InvocationTargetException ex) { jaroslav@601: Throwable th = ex.getTargetException(); jaroslav@601: if (th instanceof IOException) { jaroslav@601: throw (IOException) th; jaroslav@601: } else { jaroslav@601: throwMiscException(th); jaroslav@601: } jaroslav@601: } catch (IllegalAccessException ex) { jaroslav@601: // should not occur, as access checks have been suppressed jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: throw new UnsupportedOperationException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Invokes the readObject method of the represented serializable class. jaroslav@601: * Throws UnsupportedOperationException if this class descriptor is not jaroslav@601: * associated with a class, or if the class is externalizable, jaroslav@601: * non-serializable or does not define readObject. jaroslav@601: */ jaroslav@601: void invokeReadObject(Object obj, ObjectInputStream in) jaroslav@601: throws ClassNotFoundException, IOException, jaroslav@601: UnsupportedOperationException jaroslav@601: { jaroslav@601: if (readObjectMethod != null) { jaroslav@601: try { jaroslav@601: readObjectMethod.invoke(obj, new Object[]{ in }); jaroslav@601: } catch (InvocationTargetException ex) { jaroslav@601: Throwable th = ex.getTargetException(); jaroslav@601: if (th instanceof ClassNotFoundException) { jaroslav@601: throw (ClassNotFoundException) th; jaroslav@601: } else if (th instanceof IOException) { jaroslav@601: throw (IOException) th; jaroslav@601: } else { jaroslav@601: throwMiscException(th); jaroslav@601: } jaroslav@601: } catch (IllegalAccessException ex) { jaroslav@601: // should not occur, as access checks have been suppressed jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: throw new UnsupportedOperationException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Invokes the readObjectNoData method of the represented serializable jaroslav@601: * class. Throws UnsupportedOperationException if this class descriptor is jaroslav@601: * not associated with a class, or if the class is externalizable, jaroslav@601: * non-serializable or does not define readObjectNoData. jaroslav@601: */ jaroslav@601: void invokeReadObjectNoData(Object obj) jaroslav@601: throws IOException, UnsupportedOperationException jaroslav@601: { jaroslav@601: if (readObjectNoDataMethod != null) { jaroslav@601: try { jaroslav@601: readObjectNoDataMethod.invoke(obj, (Object[]) null); jaroslav@601: } catch (InvocationTargetException ex) { jaroslav@601: Throwable th = ex.getTargetException(); jaroslav@601: if (th instanceof ObjectStreamException) { jaroslav@601: throw (ObjectStreamException) th; jaroslav@601: } else { jaroslav@601: throwMiscException(th); jaroslav@601: } jaroslav@601: } catch (IllegalAccessException ex) { jaroslav@601: // should not occur, as access checks have been suppressed jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: throw new UnsupportedOperationException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Invokes the writeReplace method of the represented serializable class and jaroslav@601: * returns the result. Throws UnsupportedOperationException if this class jaroslav@601: * descriptor is not associated with a class, or if the class is jaroslav@601: * non-serializable or does not define writeReplace. jaroslav@601: */ jaroslav@601: Object invokeWriteReplace(Object obj) jaroslav@601: throws IOException, UnsupportedOperationException jaroslav@601: { jaroslav@601: if (writeReplaceMethod != null) { jaroslav@601: try { jaroslav@601: return writeReplaceMethod.invoke(obj, (Object[]) null); jaroslav@601: } catch (InvocationTargetException ex) { jaroslav@601: Throwable th = ex.getTargetException(); jaroslav@601: if (th instanceof ObjectStreamException) { jaroslav@601: throw (ObjectStreamException) th; jaroslav@601: } else { jaroslav@601: throwMiscException(th); jaroslav@601: throw new InternalError(); // never reached jaroslav@601: } jaroslav@601: } catch (IllegalAccessException ex) { jaroslav@601: // should not occur, as access checks have been suppressed jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: throw new UnsupportedOperationException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Invokes the readResolve method of the represented serializable class and jaroslav@601: * returns the result. Throws UnsupportedOperationException if this class jaroslav@601: * descriptor is not associated with a class, or if the class is jaroslav@601: * non-serializable or does not define readResolve. jaroslav@601: */ jaroslav@601: Object invokeReadResolve(Object obj) jaroslav@601: throws IOException, UnsupportedOperationException jaroslav@601: { jaroslav@601: if (readResolveMethod != null) { jaroslav@601: try { jaroslav@601: return readResolveMethod.invoke(obj, (Object[]) null); jaroslav@601: } catch (InvocationTargetException ex) { jaroslav@601: Throwable th = ex.getTargetException(); jaroslav@601: if (th instanceof ObjectStreamException) { jaroslav@601: throw (ObjectStreamException) th; jaroslav@601: } else { jaroslav@601: throwMiscException(th); jaroslav@601: throw new InternalError(); // never reached jaroslav@601: } jaroslav@601: } catch (IllegalAccessException ex) { jaroslav@601: // should not occur, as access checks have been suppressed jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: throw new UnsupportedOperationException(); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Class representing the portion of an object's serialized form allotted jaroslav@601: * to data described by a given class descriptor. If "hasData" is false, jaroslav@601: * the object's serialized form does not contain data associated with the jaroslav@601: * class descriptor. jaroslav@601: */ jaroslav@601: static class ClassDataSlot { jaroslav@601: jaroslav@601: /** class descriptor "occupying" this slot */ jaroslav@601: final ObjectStreamClass desc; jaroslav@601: /** true if serialized form includes data for this slot's descriptor */ jaroslav@601: final boolean hasData; jaroslav@601: jaroslav@601: ClassDataSlot(ObjectStreamClass desc, boolean hasData) { jaroslav@601: this.desc = desc; jaroslav@601: this.hasData = hasData; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns array of ClassDataSlot instances representing the data layout jaroslav@601: * (including superclass data) for serialized objects described by this jaroslav@601: * class descriptor. ClassDataSlots are ordered by inheritance with those jaroslav@601: * containing "higher" superclasses appearing first. The final jaroslav@601: * ClassDataSlot contains a reference to this descriptor. jaroslav@601: */ jaroslav@601: ClassDataSlot[] getClassDataLayout() throws InvalidClassException { jaroslav@601: // REMIND: synchronize instead of relying on volatile? jaroslav@601: if (dataLayout == null) { jaroslav@601: dataLayout = getClassDataLayout0(); jaroslav@601: } jaroslav@601: return dataLayout; jaroslav@601: } jaroslav@601: jaroslav@601: private ClassDataSlot[] getClassDataLayout0() jaroslav@601: throws InvalidClassException jaroslav@601: { jaroslav@601: ArrayList slots = new ArrayList<>(); jaroslav@601: Class start = cl, end = cl; jaroslav@601: jaroslav@601: // locate closest non-serializable superclass jaroslav@601: while (end != null && Serializable.class.isAssignableFrom(end)) { jaroslav@601: end = end.getSuperclass(); jaroslav@601: } jaroslav@601: jaroslav@601: for (ObjectStreamClass d = this; d != null; d = d.superDesc) { jaroslav@601: jaroslav@601: // search up inheritance hierarchy for class with matching name jaroslav@601: String searchName = (d.cl != null) ? d.cl.getName() : d.name; jaroslav@601: Class match = null; jaroslav@601: for (Class c = start; c != end; c = c.getSuperclass()) { jaroslav@601: if (searchName.equals(c.getName())) { jaroslav@601: match = c; jaroslav@601: break; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: // add "no data" slot for each unmatched class below match jaroslav@601: if (match != null) { jaroslav@601: for (Class c = start; c != match; c = c.getSuperclass()) { jaroslav@601: slots.add(new ClassDataSlot( jaroslav@601: ObjectStreamClass.lookup(c, true), false)); jaroslav@601: } jaroslav@601: start = match.getSuperclass(); jaroslav@601: } jaroslav@601: jaroslav@601: // record descriptor/class pairing jaroslav@601: slots.add(new ClassDataSlot(d.getVariantFor(match), true)); jaroslav@601: } jaroslav@601: jaroslav@601: // add "no data" slot for any leftover unmatched classes jaroslav@601: for (Class c = start; c != end; c = c.getSuperclass()) { jaroslav@601: slots.add(new ClassDataSlot( jaroslav@601: ObjectStreamClass.lookup(c, true), false)); jaroslav@601: } jaroslav@601: jaroslav@601: // order slots from superclass -> subclass jaroslav@601: Collections.reverse(slots); jaroslav@601: return slots.toArray(new ClassDataSlot[slots.size()]); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns aggregate size (in bytes) of marshalled primitive field values jaroslav@601: * for represented class. jaroslav@601: */ jaroslav@601: int getPrimDataSize() { jaroslav@601: return primDataSize; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns number of non-primitive serializable fields of represented jaroslav@601: * class. jaroslav@601: */ jaroslav@601: int getNumObjFields() { jaroslav@601: return numObjFields; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Fetches the serializable primitive field values of object obj and jaroslav@601: * marshals them into byte array buf starting at offset 0. It is the jaroslav@601: * responsibility of the caller to ensure that obj is of the proper type if jaroslav@601: * non-null. jaroslav@601: */ jaroslav@601: void getPrimFieldValues(Object obj, byte[] buf) { jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Sets the serializable primitive fields of object obj using values jaroslav@601: * unmarshalled from byte array buf starting at offset 0. It is the jaroslav@601: * responsibility of the caller to ensure that obj is of the proper type if jaroslav@601: * non-null. jaroslav@601: */ jaroslav@601: void setPrimFieldValues(Object obj, byte[] buf) { jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Fetches the serializable object field values of object obj and stores jaroslav@601: * them in array vals starting at offset 0. It is the responsibility of jaroslav@601: * the caller to ensure that obj is of the proper type if non-null. jaroslav@601: */ jaroslav@601: void getObjFieldValues(Object obj, Object[] vals) { jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Sets the serializable object fields of object obj using values from jaroslav@601: * array vals starting at offset 0. It is the responsibility of the caller jaroslav@601: * to ensure that obj is of the proper type if non-null. jaroslav@601: */ jaroslav@601: void setObjFieldValues(Object obj, Object[] vals) { jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Calculates and sets serializable field offsets, as well as primitive jaroslav@601: * data size and object field count totals. Throws InvalidClassException jaroslav@601: * if fields are illegally ordered. jaroslav@601: */ jaroslav@601: private void computeFieldOffsets() throws InvalidClassException { jaroslav@601: primDataSize = 0; jaroslav@601: numObjFields = 0; jaroslav@601: int firstObjIndex = -1; jaroslav@601: jaroslav@601: for (int i = 0; i < fields.length; i++) { jaroslav@601: ObjectStreamField f = fields[i]; jaroslav@601: switch (f.getTypeCode()) { jaroslav@601: case 'Z': jaroslav@601: case 'B': jaroslav@601: f.setOffset(primDataSize++); jaroslav@601: break; jaroslav@601: jaroslav@601: case 'C': jaroslav@601: case 'S': jaroslav@601: f.setOffset(primDataSize); jaroslav@601: primDataSize += 2; jaroslav@601: break; jaroslav@601: jaroslav@601: case 'I': jaroslav@601: case 'F': jaroslav@601: f.setOffset(primDataSize); jaroslav@601: primDataSize += 4; jaroslav@601: break; jaroslav@601: jaroslav@601: case 'J': jaroslav@601: case 'D': jaroslav@601: f.setOffset(primDataSize); jaroslav@601: primDataSize += 8; jaroslav@601: break; jaroslav@601: jaroslav@601: case '[': jaroslav@601: case 'L': jaroslav@601: f.setOffset(numObjFields++); jaroslav@601: if (firstObjIndex == -1) { jaroslav@601: firstObjIndex = i; jaroslav@601: } jaroslav@601: break; jaroslav@601: jaroslav@601: default: jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } jaroslav@601: if (firstObjIndex != -1 && jaroslav@601: firstObjIndex + numObjFields != fields.length) jaroslav@601: { jaroslav@601: throw new InvalidClassException(name, "illegal field order"); jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * If given class is the same as the class associated with this class jaroslav@601: * descriptor, returns reference to this class descriptor. Otherwise, jaroslav@601: * returns variant of this class descriptor bound to given class. jaroslav@601: */ jaroslav@601: private ObjectStreamClass getVariantFor(Class cl) jaroslav@601: throws InvalidClassException jaroslav@601: { jaroslav@601: if (this.cl == cl) { jaroslav@601: return this; jaroslav@601: } jaroslav@601: ObjectStreamClass desc = new ObjectStreamClass(); jaroslav@601: if (isProxy) { jaroslav@601: desc.initProxy(cl, null, superDesc); jaroslav@601: } else { jaroslav@601: desc.initNonProxy(this, cl, null, superDesc); jaroslav@601: } jaroslav@601: return desc; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns public no-arg constructor of given class, or null if none found. jaroslav@601: * Access checks are disabled on the returned constructor (if any), since jaroslav@601: * the defining class may still be non-public. jaroslav@601: */ jaroslav@601: private static Constructor getExternalizableConstructor(Class cl) { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns subclass-accessible no-arg constructor of first non-serializable jaroslav@601: * superclass, or null if none found. Access checks are disabled on the jaroslav@601: * returned constructor (if any). jaroslav@601: */ jaroslav@601: private static Constructor getSerializableConstructor(Class cl) { jaroslav@601: Class initCl = cl; jaroslav@601: while (Serializable.class.isAssignableFrom(initCl)) { jaroslav@601: if ((initCl = initCl.getSuperclass()) == null) { jaroslav@601: return null; jaroslav@601: } jaroslav@601: } jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns non-static, non-abstract method with given signature provided it jaroslav@601: * is defined by or accessible (via inheritance) by the given class, or jaroslav@601: * null if no match found. Access checks are disabled on the returned jaroslav@601: * method (if any). jaroslav@601: */ jaroslav@601: private static Method getInheritableMethod(Class cl, String name, jaroslav@601: Class[] argTypes, jaroslav@601: Class returnType) jaroslav@601: { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns non-static private method with given signature defined by given jaroslav@601: * class, or null if none found. Access checks are disabled on the jaroslav@601: * returned method (if any). jaroslav@601: */ jaroslav@601: private static Method getPrivateMethod(Class cl, String name, jaroslav@601: Class[] argTypes, jaroslav@601: Class returnType) jaroslav@601: { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns true if classes are defined in the same runtime package, false jaroslav@601: * otherwise. jaroslav@601: */ jaroslav@601: private static boolean packageEquals(Class cl1, Class cl2) { jaroslav@601: return (cl1.getClassLoader() == cl2.getClassLoader() && jaroslav@601: getPackageName(cl1).equals(getPackageName(cl2))); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns package name of given class. jaroslav@601: */ jaroslav@601: private static String getPackageName(Class cl) { jaroslav@601: String s = cl.getName(); jaroslav@601: int i = s.lastIndexOf('['); jaroslav@601: if (i >= 0) { jaroslav@601: s = s.substring(i + 2); jaroslav@601: } jaroslav@601: i = s.lastIndexOf('.'); jaroslav@601: return (i >= 0) ? s.substring(0, i) : ""; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Compares class names for equality, ignoring package names. Returns true jaroslav@601: * if class names equal, false otherwise. jaroslav@601: */ jaroslav@601: private static boolean classNamesEqual(String name1, String name2) { jaroslav@601: name1 = name1.substring(name1.lastIndexOf('.') + 1); jaroslav@601: name2 = name2.substring(name2.lastIndexOf('.') + 1); jaroslav@601: return name1.equals(name2); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns JVM type signature for given class. jaroslav@601: */ jaroslav@601: private static String getClassSignature(Class cl) { jaroslav@601: StringBuilder sbuf = new StringBuilder(); jaroslav@601: while (cl.isArray()) { jaroslav@601: sbuf.append('['); jaroslav@601: cl = cl.getComponentType(); jaroslav@601: } jaroslav@601: if (cl.isPrimitive()) { jaroslav@601: if (cl == Integer.TYPE) { jaroslav@601: sbuf.append('I'); jaroslav@601: } else if (cl == Byte.TYPE) { jaroslav@601: sbuf.append('B'); jaroslav@601: } else if (cl == Long.TYPE) { jaroslav@601: sbuf.append('J'); jaroslav@601: } else if (cl == Float.TYPE) { jaroslav@601: sbuf.append('F'); jaroslav@601: } else if (cl == Double.TYPE) { jaroslav@601: sbuf.append('D'); jaroslav@601: } else if (cl == Short.TYPE) { jaroslav@601: sbuf.append('S'); jaroslav@601: } else if (cl == Character.TYPE) { jaroslav@601: sbuf.append('C'); jaroslav@601: } else if (cl == Boolean.TYPE) { jaroslav@601: sbuf.append('Z'); jaroslav@601: } else if (cl == Void.TYPE) { jaroslav@601: sbuf.append('V'); jaroslav@601: } else { jaroslav@601: throw new InternalError(); jaroslav@601: } jaroslav@601: } else { jaroslav@601: sbuf.append('L' + cl.getName().replace('.', '/') + ';'); jaroslav@601: } jaroslav@601: return sbuf.toString(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns JVM type signature for given list of parameters and return type. jaroslav@601: */ jaroslav@601: private static String getMethodSignature(Class[] paramTypes, jaroslav@601: Class retType) jaroslav@601: { jaroslav@601: StringBuilder sbuf = new StringBuilder(); jaroslav@601: sbuf.append('('); jaroslav@601: for (int i = 0; i < paramTypes.length; i++) { jaroslav@601: sbuf.append(getClassSignature(paramTypes[i])); jaroslav@601: } jaroslav@601: sbuf.append(')'); jaroslav@601: sbuf.append(getClassSignature(retType)); jaroslav@601: return sbuf.toString(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Convenience method for throwing an exception that is either a jaroslav@601: * RuntimeException, Error, or of some unexpected type (in which case it is jaroslav@601: * wrapped inside an IOException). jaroslav@601: */ jaroslav@601: private static void throwMiscException(Throwable th) throws IOException { jaroslav@601: if (th instanceof RuntimeException) { jaroslav@601: throw (RuntimeException) th; jaroslav@601: } else if (th instanceof Error) { jaroslav@601: throw (Error) th; jaroslav@601: } else { jaroslav@601: IOException ex = new IOException("unexpected exception type"); jaroslav@601: ex.initCause(th); jaroslav@601: throw ex; jaroslav@601: } jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns ObjectStreamField array describing the serializable fields of jaroslav@601: * the given class. Serializable fields backed by an actual field of the jaroslav@601: * class are represented by ObjectStreamFields with corresponding non-null jaroslav@601: * Field objects. Throws InvalidClassException if the (explicitly jaroslav@601: * declared) serializable fields are invalid. jaroslav@601: */ jaroslav@601: private static ObjectStreamField[] getSerialFields(Class cl) jaroslav@601: throws InvalidClassException jaroslav@601: { jaroslav@601: ObjectStreamField[] fields; jaroslav@601: if (Serializable.class.isAssignableFrom(cl) && jaroslav@601: !Externalizable.class.isAssignableFrom(cl) && jaroslav@601: !Proxy.isProxyClass(cl) && jaroslav@601: !cl.isInterface()) jaroslav@601: { jaroslav@601: if ((fields = getDeclaredSerialFields(cl)) == null) { jaroslav@601: fields = getDefaultSerialFields(cl); jaroslav@601: } jaroslav@601: Arrays.sort(fields); jaroslav@601: } else { jaroslav@601: fields = NO_FIELDS; jaroslav@601: } jaroslav@601: return fields; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns serializable fields of given class as defined explicitly by a jaroslav@601: * "serialPersistentFields" field, or null if no appropriate jaroslav@601: * "serialPersistentFields" field is defined. Serializable fields backed jaroslav@601: * by an actual field of the class are represented by ObjectStreamFields jaroslav@601: * with corresponding non-null Field objects. For compatibility with past jaroslav@601: * releases, a "serialPersistentFields" field with a null value is jaroslav@601: * considered equivalent to not declaring "serialPersistentFields". Throws jaroslav@601: * InvalidClassException if the declared serializable fields are jaroslav@601: * invalid--e.g., if multiple fields share the same name. jaroslav@601: */ jaroslav@601: private static ObjectStreamField[] getDeclaredSerialFields(Class cl) jaroslav@601: throws InvalidClassException jaroslav@601: { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns array of ObjectStreamFields corresponding to all non-static jaroslav@601: * non-transient fields declared by given class. Each ObjectStreamField jaroslav@601: * contains a Field object for the field it represents. If no default jaroslav@601: * serializable fields exist, NO_FIELDS is returned. jaroslav@601: */ jaroslav@601: private static ObjectStreamField[] getDefaultSerialFields(Class cl) { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Returns explicit serial version UID value declared by given class, or jaroslav@601: * null if none. jaroslav@601: */ jaroslav@601: private static Long getDeclaredSUID(Class cl) { jaroslav@601: return null; jaroslav@601: } jaroslav@601: jaroslav@601: /** jaroslav@601: * Computes the default serial version UID value for the given class. jaroslav@601: */ jaroslav@601: private static long computeDefaultSUID(Class cl) { jaroslav@604: throw new SecurityException(); jaroslav@601: } jaroslav@601: jaroslav@601: }