       1 /*
       2  * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
       3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       4  *
       5  * This code is free software; you can redistribute it and/or modify it
       6  * under the terms of the GNU General Public License version 2 only, as
       7  * published by the Free Software Foundation.  Oracle designates this
       8  * particular file as subject to the "Classpath" exception as provided
       9  * by Oracle in the LICENSE file that accompanied this code.
      10  *
      11  * This code is distributed in the hope that it will be useful, but WITHOUT
      12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14  * version 2 for more details (a copy is included in the LICENSE file that
      15  * accompanied this code).
      16  *
      17  * You should have received a copy of the GNU General Public License version
      18  * 2 along with this work; if not, write to the Free Software Foundation,
      19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
      20  *
      21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
      22  * or visit www.oracle.com if you need additional information or have any
      23  * questions.
      24  */
      25
      26 package java.io;
      27
      28 import java.lang.invoke.MethodHandle;
      29 import java.lang.invoke.MethodHandles;
      30 import java.lang.invoke.MethodType;
      31 import java.lang.ref.Reference;
      32 import java.lang.ref.ReferenceQueue;
      33 import java.lang.ref.SoftReference;
      34 import java.lang.ref.WeakReference;
      35 import java.lang.reflect.Constructor;
      36 import java.lang.reflect.Field;
      37 import java.lang.reflect.InvocationTargetException;
      38 import java.lang.reflect.RecordComponent;
      39 import java.lang.reflect.UndeclaredThrowableException;
      40 import java.lang.reflect.Member;
      41 import java.lang.reflect.Method;
      42 import java.lang.reflect.Modifier;
      43 import java.lang.reflect.Proxy;
      44 import java.security.AccessControlContext;
      45 import java.security.AccessController;
      46 import java.security.MessageDigest;
      47 import java.security.NoSuchAlgorithmException;
      48 import java.security.PermissionCollection;
      49 import java.security.Permissions;
      50 import java.security.PrivilegedAction;
      51 import java.security.PrivilegedActionException;
      52 import java.security.PrivilegedExceptionAction;
      53 import java.security.ProtectionDomain;
      54 import java.util.ArrayList;
      55 import java.util.Arrays;
      56 import java.util.Collections;
      57 import java.util.Comparator;
      58 import java.util.HashSet;
      59 import java.util.Map;
      60 import java.util.Set;
      61 import java.util.concurrent.ConcurrentHashMap;
      62 import java.util.concurrent.ConcurrentMap;
      63 import jdk.internal.misc.Unsafe;
      64 import jdk.internal.reflect.CallerSensitive;
      65 import jdk.internal.reflect.Reflection;
      66 import jdk.internal.reflect.ReflectionFactory;
      67 import jdk.internal.access.SharedSecrets;
      68 import jdk.internal.access.JavaSecurityAccess;
      69 import sun.reflect.misc.ReflectUtil;
      70 import static java.io.ObjectStreamField.*;
      71
      72 /**
      73  * Serialization's descriptor for classes.  It contains the name and
      74  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
      75  * loaded in this Java VM can be found/created using the lookup method.
      76  *
      77  * <p>The algorithm to compute the SerialVersionUID is described in
      78  * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
      79  *     Object Serialization Specification, Section 4.6, Stream Unique Identifiers</a>.
      80  *
      81  * @author      Mike Warres
      82  * @author      Roger Riggs
      83  * @see ObjectStreamField
      84  * @see <a href="{@docRoot}/../specs/serialization/class.html">
      85  *     Object Serialization Specification, Section 4, Class Descriptors</a>
      86  * @since   1.1
      87  */
      88 public class ObjectStreamClass implements Serializable {
      89
      90     /** serialPersistentFields value indicating no serializable fields */
      91     public static final ObjectStreamField[] NO_FIELDS =
      92         new ObjectStreamField[0];
      93
      94     @java.io.Serial
      95     private static final long serialVersionUID = -6120832682080437368L;

      96     @java.io.Serial
      97     private static final ObjectStreamField[] serialPersistentFields =
      98         NO_FIELDS;
      99
     100     /** reflection factory for obtaining serialization constructors */
     101     private static final ReflectionFactory reflFactory =
     102         AccessController.doPrivileged(
     103             new ReflectionFactory.GetReflectionFactoryAction());
     104
     105     private static class Caches {
     106         /** cache mapping local classes -> descriptors */
     107         static final ConcurrentMap<WeakClassKey,Reference<?>> localDescs =
     108             new ConcurrentHashMap<>();
     109
     110         /** cache mapping field group/local desc pairs -> field reflectors */
     111         static final ConcurrentMap<FieldReflectorKey,Reference<?>> reflectors =
     112             new ConcurrentHashMap<>();
     113
     114         /** queue for WeakReferences to local classes */
     115         private static final ReferenceQueue<Class<?>> localDescsQueue =
     116             new ReferenceQueue<>();
     117         /** queue for WeakReferences to field reflectors keys */
     118         private static final ReferenceQueue<Class<?>> reflectorsQueue =
     119             new ReferenceQueue<>();
     120     }
     121
     122     /** class associated with this descriptor (if any) */
     123     private Class<?> cl;
     124     /** name of class represented by this descriptor */
     125     private String name;
     126     /** serialVersionUID of represented class (null if not computed yet) */
     127     private volatile Long suid;
     128
     129     /** true if represents dynamic proxy class */
     130     private boolean isProxy;
     131     /** true if represents enum type */
     132     private boolean isEnum;
     133     /** true if represents record type */
     134     private boolean isRecord;
     135     /** true if represented class implements Serializable */
     136     private boolean serializable;
     137     /** true if represented class implements Externalizable */
     138     private boolean externalizable;
     139     /** true if desc has data written by class-defined writeObject method */
     140     private boolean hasWriteObjectData;
     141     /**
     142      * true if desc has externalizable data written in block data format; this
     143      * must be true by default to accommodate ObjectInputStream subclasses which
     144      * override readClassDescriptor() to return class descriptors obtained from
     145      * ObjectStreamClass.lookup() (see 4461737)
     146      */
     147     private boolean hasBlockExternalData = true;
     148
     149     /**
     150      * Contains information about InvalidClassException instances to be thrown
     151      * when attempting operations on an invalid class. Note that instances of
     152      * this class are immutable and are potentially shared among
     153      * ObjectStreamClass instances.
     154      */
     155     private static class ExceptionInfo {
     156         private final String className;
     157         private final String message;
     158
     159         ExceptionInfo(String cn, String msg) {
     160             className = cn;
     161             message = msg;
     162         }
     163
     164         /**
     165          * Returns (does not throw) an InvalidClassException instance created
     166          * from the information in this object, suitable for being thrown by
     167          * the caller.
     168          */
     169         InvalidClassException newInvalidClassException() {
     170             return new InvalidClassException(className, message);
     171         }
     172     }
     173
     174     /** exception (if any) thrown while attempting to resolve class */
     175     private ClassNotFoundException resolveEx;
     176     /** exception (if any) to throw if non-enum deserialization attempted */
     177     private ExceptionInfo deserializeEx;
     178     /** exception (if any) to throw if non-enum serialization attempted */
     179     private ExceptionInfo serializeEx;
     180     /** exception (if any) to throw if default serialization attempted */
     181     private ExceptionInfo defaultSerializeEx;
     182
     183     /** serializable fields */
     184     private ObjectStreamField[] fields;
     185     /** aggregate marshalled size of primitive fields */
     186     private int primDataSize;
     187     /** number of non-primitive fields */
     188     private int numObjFields;
     189     /** reflector for setting/getting serializable field values */
     190     private FieldReflector fieldRefl;
     191     /** data layout of serialized objects described by this class desc */
     192     private volatile ClassDataSlot[] dataLayout;
     193
     194     /** serialization-appropriate constructor, or null if none */
     195     private Constructor<?> cons;
     196     /** record canonical constructor (shared among OSCs for same class), or null */
     197     private MethodHandle canonicalCtr;
     198     /** cache of record deserialization constructors per unique set of stream fields
     199      * (shared among OSCs for same class), or null */
     200     private DeserializationConstructorsCache deserializationCtrs;
     201     /** session-cache of record deserialization constructor
     202      * (in de-serialized OSC only), or null */
     203     private MethodHandle deserializationCtr;
     204     /** protection domains that need to be checked when calling the constructor */
     205     private ProtectionDomain[] domains;
     206
     207     /** class-defined writeObject method, or null if none */
     208     private Method writeObjectMethod;
     209     /** class-defined readObject method, or null if none */
     210     private Method readObjectMethod;
     211     /** class-defined readObjectNoData method, or null if none */
     212     private Method readObjectNoDataMethod;
     213     /** class-defined writeReplace method, or null if none */
     214     private Method writeReplaceMethod;
     215     /** class-defined readResolve method, or null if none */
     216     private Method readResolveMethod;
     217
     218     /** local class descriptor for represented class (may point to self) */
     219     private ObjectStreamClass localDesc;
     220     /** superclass descriptor appearing in stream */
     221     private ObjectStreamClass superDesc;
     222
     223     /** true if, and only if, the object has been correctly initialized */
     224     private boolean initialized;
     225
     226     /**
     227      * Initializes native code.
     228      */
     229     private static native void initNative();
     230     static {
     231         initNative();
     232     }
     233
     234     /**
     235      * Find the descriptor for a class that can be serialized.  Creates an
     236      * ObjectStreamClass instance if one does not exist yet for class. Null is
     237      * returned if the specified class does not implement java.io.Serializable
     238      * or java.io.Externalizable.
     239      *
     240      * @param   cl class for which to get the descriptor
     241      * @return  the class descriptor for the specified class
     242      */
     243     public static ObjectStreamClass lookup(Class<?> cl) {
     244         return lookup(cl, false);
     245     }
     246
     247     /**
     248      * Returns the descriptor for any class, regardless of whether it
     249      * implements {@link Serializable}.
     250      *
     251      * @param        cl class for which to get the descriptor
     252      * @return       the class descriptor for the specified class
     253      * @since 1.6
     254      */
     255     public static ObjectStreamClass lookupAny(Class<?> cl) {
     256         return lookup(cl, true);
     257     }
     258
     259     /**
     260      * Returns the name of the class described by this descriptor.
     261      * This method returns the name of the class in the format that
     262      * is used by the {@link Class#getName} method.
     263      *
     264      * @return a string representing the name of the class
     265      */
     266     public String getName() {
     267         return name;
     268     }
     269
     270     /**
     271      * Return the serialVersionUID for this class.  The serialVersionUID
     272      * defines a set of classes all with the same name that have evolved from a
     273      * common root class and agree to be serialized and deserialized using a
     274      * common format.  NonSerializable classes have a serialVersionUID of 0L.
     275      *
     276      * @return  the SUID of the class described by this descriptor
     277      */
     278     public long getSerialVersionUID() {
     279         // REMIND: synchronize instead of relying on volatile?
     280         if (suid == null) {
     281             if (isRecord)
     282                 return 0L;
     283
     284             suid = AccessController.doPrivileged(
     285                 new PrivilegedAction<Long>() {
     286                     public Long run() {
     287                         return computeDefaultSUID(cl);
     288                     }
     289                 }
     290             );
     291         }
     292         return suid.longValue();
     293     }
     294
     295     /**
     296      * Return the class in the local VM that this version is mapped to.  Null
     297      * is returned if there is no corresponding local class.
     298      *
     299      * @return  the {@code Class} instance that this descriptor represents
     300      */
     301     @CallerSensitive
     302     public Class<?> forClass() {
     303         if (cl == null) {
     304             return null;
     305         }
     306         requireInitialized();
     307         if (System.getSecurityManager() != null) {
     308             Class<?> caller = Reflection.getCallerClass();
     309             if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), cl.getClassLoader())) {
     310                 ReflectUtil.checkPackageAccess(cl);
     311             }
     312         }
     313         return cl;
     314     }
     315
     316     /**
     317      * Return an array of the fields of this serializable class.
     318      *
     319      * @return  an array containing an element for each persistent field of
     320      *          this class. Returns an array of length zero if there are no
     321      *          fields.
     322      * @since 1.2
     323      */
     324     public ObjectStreamField[] getFields() {
     325         return getFields(true);
     326     }
     327
     328     /**
     329      * Get the field of this class by name.
     330      *
     331      * @param   name the name of the data field to look for
     332      * @return  The ObjectStreamField object of the named field or null if
     333      *          there is no such named field.
     334      */
     335     public ObjectStreamField getField(String name) {
     336         return getField(name, null);
     337     }
     338
     339     /**
     340      * Return a string describing this ObjectStreamClass.
     341      */
     342     public String toString() {
     343         return name + ": static final long serialVersionUID = " +
     344             getSerialVersionUID() + "L;";
     345     }
     346
     347     /**
     348      * Looks up and returns class descriptor for given class, or null if class
     349      * is non-serializable and "all" is set to false.
     350      *
     351      * @param   cl class to look up
     352      * @param   all if true, return descriptors for all classes; if false, only
     353      *          return descriptors for serializable classes
     354      */
     355     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
     356         if (!(all || Serializable.class.isAssignableFrom(cl))) {
     357             return null;
     358         }
     359         processQueue(Caches.localDescsQueue, Caches.localDescs);
     360         WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
     361         Reference<?> ref = Caches.localDescs.get(key);
     362         Object entry = null;
     363         if (ref != null) {
     364             entry = ref.get();
     365         }
     366         EntryFuture future = null;
     367         if (entry == null) {
     368             EntryFuture newEntry = new EntryFuture();
     369             Reference<?> newRef = new SoftReference<>(newEntry);
     370             do {
     371                 if (ref != null) {
     372                     Caches.localDescs.remove(key, ref);
     373                 }
     374                 ref = Caches.localDescs.putIfAbsent(key, newRef);
     375                 if (ref != null) {
     376                     entry = ref.get();
     377                 }
     378             } while (ref != null && entry == null);
     379             if (entry == null) {
     380                 future = newEntry;
     381             }
     382         }
     383
     384         if (entry instanceof ObjectStreamClass) {  // check common case first
     385             return (ObjectStreamClass) entry;
     386         }
     387         if (entry instanceof EntryFuture) {
     388             future = (EntryFuture) entry;
     389             if (future.getOwner() == Thread.currentThread()) {
     390                 /*
     391                  * Handle nested call situation described by 4803747: waiting
     392                  * for future value to be set by a lookup() call further up the
     393                  * stack will result in deadlock, so calculate and set the
     394                  * future value here instead.
     395                  */
     396                 entry = null;
     397             } else {
     398                 entry = future.get();
     399             }
     400         }
     401         if (entry == null) {
     402             try {
     403                 entry = new ObjectStreamClass(cl);
     404             } catch (Throwable th) {
     405                 entry = th;
     406             }
     407             if (future.set(entry)) {
     408                 Caches.localDescs.put(key, new SoftReference<>(entry));
     409             } else {
     410                 // nested lookup call already set future
     411                 entry = future.get();
     412             }
     413         }
     414
     415         if (entry instanceof ObjectStreamClass) {
     416             return (ObjectStreamClass) entry;
     417         } else if (entry instanceof RuntimeException) {
     418             throw (RuntimeException) entry;
     419         } else if (entry instanceof Error) {
     420             throw (Error) entry;
     421         } else {
     422             throw new InternalError("unexpected entry: " + entry);
     423         }
     424     }
     425
     426     /**
     427      * Placeholder used in class descriptor and field reflector lookup tables
     428      * for an entry in the process of being initialized.  (Internal) callers
     429      * which receive an EntryFuture belonging to another thread as the result
     430      * of a lookup should call the get() method of the EntryFuture; this will
     431      * return the actual entry once it is ready for use and has been set().  To
     432      * conserve objects, EntryFutures synchronize on themselves.
     433      */
     434     private static class EntryFuture {
     435
     436         private static final Object unset = new Object();
     437         private final Thread owner = Thread.currentThread();
     438         private Object entry = unset;
     439
     440         /**
     441          * Attempts to set the value contained by this EntryFuture.  If the
     442          * EntryFuture's value has not been set already, then the value is
     443          * saved, any callers blocked in the get() method are notified, and
     444          * true is returned.  If the value has already been set, then no saving
     445          * or notification occurs, and false is returned.
     446          */
     447         synchronized boolean set(Object entry) {
     448             if (this.entry != unset) {
     449                 return false;
     450             }
     451             this.entry = entry;
     452             notifyAll();
     453             return true;
     454         }
     455
     456         /**
     457          * Returns the value contained by this EntryFuture, blocking if
     458          * necessary until a value is set.
     459          */
     460         synchronized Object get() {
     461             boolean interrupted = false;
     462             while (entry == unset) {
     463                 try {
     464                     wait();
     465                 } catch (InterruptedException ex) {
     466                     interrupted = true;
     467                 }
     468             }
     469             if (interrupted) {
     470                 AccessController.doPrivileged(
     471                     new PrivilegedAction<>() {
     472                         public Void run() {
     473                             Thread.currentThread().interrupt();
     474                             return null;
     475                         }
     476                     }
     477                 );
     478             }
     479             return entry;
     480         }
     481
     482         /**
     483          * Returns the thread that created this EntryFuture.
     484          */
     485         Thread getOwner() {
     486             return owner;
     487         }
     488     }
     489
     490     @SuppressWarnings("preview")
     491     private static boolean isRecord(Class<?> cls) {
     492         return cls.isRecord();
     493     }
     494
     495     /**
     496      * Creates local class descriptor representing given class.
     497      */
     498     private ObjectStreamClass(final Class<?> cl) {
     499         this.cl = cl;
     500         name = cl.getName();
     501         isProxy = Proxy.isProxyClass(cl);
     502         isEnum = Enum.class.isAssignableFrom(cl);
     503         isRecord = isRecord(cl);
     504         serializable = Serializable.class.isAssignableFrom(cl);
     505         externalizable = Externalizable.class.isAssignableFrom(cl);
     506
     507         Class<?> superCl = cl.getSuperclass();
     508         superDesc = (superCl != null) ? lookup(superCl, false) : null;
     509         localDesc = this;
     510
     511         if (serializable) {
     512             AccessController.doPrivileged(new PrivilegedAction<>() {
     513                 public Void run() {
     514                     if (isEnum) {
     515                         suid = Long.valueOf(0);
     516                         fields = NO_FIELDS;
     517                         return null;
     518                     }
     519                     if (cl.isArray()) {
     520                         fields = NO_FIELDS;
     521                         return null;
     522                     }
     523
     524                     suid = getDeclaredSUID(cl);
     525                     try {
     526                         fields = getSerialFields(cl);
     527                         computeFieldOffsets();
     528                     } catch (InvalidClassException e) {
     529                         serializeEx = deserializeEx =
     530                             new ExceptionInfo(e.classname, e.getMessage());
     531                         fields = NO_FIELDS;
     532                     }
     533
     534                     if (isRecord) {
     535                         canonicalCtr = canonicalRecordCtr(cl);
     536                         deserializationCtrs = new DeserializationConstructorsCache();
     537                     } else if (externalizable) {
     538                         cons = getExternalizableConstructor(cl);
     539                     } else {
     540                         cons = getSerializableConstructor(cl);
     541                         writeObjectMethod = getPrivateMethod(cl, "writeObject",
     542                             new Class<?>[] { ObjectOutputStream.class },
     543                             Void.TYPE);
     544                         readObjectMethod = getPrivateMethod(cl, "readObject",
     545                             new Class<?>[] { ObjectInputStream.class },
     546                             Void.TYPE);
     547                         readObjectNoDataMethod = getPrivateMethod(
     548                             cl, "readObjectNoData", null, Void.TYPE);
     549                         hasWriteObjectData = (writeObjectMethod != null);
     550                     }
     551                     domains = getProtectionDomains(cons, cl);
     552                     writeReplaceMethod = getInheritableMethod(
     553                         cl, "writeReplace", null, Object.class);
     554                     readResolveMethod = getInheritableMethod(
     555                         cl, "readResolve", null, Object.class);
     556                     return null;
     557                 }
     558             });
     559         } else {
     560             suid = Long.valueOf(0);
     561             fields = NO_FIELDS;
     562         }
     563
     564         try {
     565             fieldRefl = getReflector(fields, this);
     566         } catch (InvalidClassException ex) {
     567             // field mismatches impossible when matching local fields vs. self
     568             throw new InternalError(ex);
     569         }
     570
     571         if (deserializeEx == null) {
     572             if (isEnum) {
     573                 deserializeEx = new ExceptionInfo(name, "enum type");
     574             } else if (cons == null && !isRecord) {
     575                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
     576             }
     577         }
     578         if (isRecord && canonicalCtr == null) {
     579             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
     580         } else {
     581             for (int i = 0; i < fields.length; i++) {
     582                 if (fields[i].getField() == null) {
     583                     defaultSerializeEx = new ExceptionInfo(
     584                         name, "unmatched serializable field(s) declared");
     585                 }
     586             }
     587         }
     588         initialized = true;
     589     }
     590
     591     /**
     592      * Creates blank class descriptor which should be initialized via a
     593      * subsequent call to initProxy(), initNonProxy() or readNonProxy().
     594      */
     595     ObjectStreamClass() {
     596     }
     597
     598     /**
     599      * Creates a PermissionDomain that grants no permission.
     600      */
     601     private ProtectionDomain noPermissionsDomain() {
     602         PermissionCollection perms = new Permissions();
     603         perms.setReadOnly();
     604         return new ProtectionDomain(null, perms);
     605     }
     606
     607     /**
     608      * Aggregate the ProtectionDomains of all the classes that separate
     609      * a concrete class {@code cl} from its ancestor's class declaring
     610      * a constructor {@code cons}.
     611      *
     612      * If {@code cl} is defined by the boot loader, or the constructor
     613      * {@code cons} is declared by {@code cl}, or if there is no security
     614      * manager, then this method does nothing and {@code null} is returned.
     615      *
     616      * @param cons A constructor declared by {@code cl} or one of its
     617      *             ancestors.
     618      * @param cl A concrete class, which is either the class declaring
     619      *           the constructor {@code cons}, or a serializable subclass
     620      *           of that class.
     621      * @return An array of ProtectionDomain representing the set of
     622      *         ProtectionDomain that separate the concrete class {@code cl}
     623      *         from its ancestor's declaring {@code cons}, or {@code null}.
     624      */
     625     private ProtectionDomain[] getProtectionDomains(Constructor<?> cons,
     626                                                     Class<?> cl) {
     627         ProtectionDomain[] domains = null;
     628         if (cons != null && cl.getClassLoader() != null
     629                 && System.getSecurityManager() != null) {
     630             Class<?> cls = cl;
     631             Class<?> fnscl = cons.getDeclaringClass();
     632             Set<ProtectionDomain> pds = null;
     633             while (cls != fnscl) {
     634                 ProtectionDomain pd = cls.getProtectionDomain();
     635                 if (pd != null) {
     636                     if (pds == null) pds = new HashSet<>();
     637                     pds.add(pd);
     638                 }
     639                 cls = cls.getSuperclass();
     640                 if (cls == null) {
     641                     // that's not supposed to happen
     642                     // make a ProtectionDomain with no permission.
     643                     // should we throw instead?
     644                     if (pds == null) pds = new HashSet<>();
     645                     else pds.clear();
     646                     pds.add(noPermissionsDomain());
     647                     break;
     648                 }
     649             }
     650             if (pds != null) {
     651                 domains = pds.toArray(new ProtectionDomain[0]);
     652             }
     653         }
     654         return domains;
     655     }
     656
     657     /**
     658      * Initializes class descriptor representing a proxy class.
     659      */
     660     void initProxy(Class<?> cl,
     661                    ClassNotFoundException resolveEx,
     662                    ObjectStreamClass superDesc)
     663         throws InvalidClassException
     664     {
     665         ObjectStreamClass osc = null;
     666         if (cl != null) {
     667             osc = lookup(cl, true);
     668             if (!osc.isProxy) {
     669                 throw new InvalidClassException(
     670                     "cannot bind proxy descriptor to a non-proxy class");
     671             }
     672         }
     673         this.cl = cl;
     674         this.resolveEx = resolveEx;
     675         this.superDesc = superDesc;
     676         isProxy = true;
     677         serializable = true;
     678         suid = Long.valueOf(0);
     679         fields = NO_FIELDS;
     680         if (osc != null) {
     681             localDesc = osc;
     682             name = localDesc.name;
     683             externalizable = localDesc.externalizable;
     684             writeReplaceMethod = localDesc.writeReplaceMethod;
     685             readResolveMethod = localDesc.readResolveMethod;
     686             deserializeEx = localDesc.deserializeEx;
     687             domains = localDesc.domains;
     688             cons = localDesc.cons;
     689         }
     690         fieldRefl = getReflector(fields, localDesc);
     691         initialized = true;
     692     }
     693
     694     /**
     695      * Initializes class descriptor representing a non-proxy class.
     696      */
     697     void initNonProxy(ObjectStreamClass model,
     698                       Class<?> cl,
     699                       ClassNotFoundException resolveEx,
     700                       ObjectStreamClass superDesc)
     701         throws InvalidClassException
     702     {
     703         long suid = Long.valueOf(model.getSerialVersionUID());
     704         ObjectStreamClass osc = null;
     705         if (cl != null) {
     706             osc = lookup(cl, true);
     707             if (osc.isProxy) {
     708                 throw new InvalidClassException(
     709                         "cannot bind non-proxy descriptor to a proxy class");
     710             }
     711             if (model.isEnum != osc.isEnum) {
     712                 throw new InvalidClassException(model.isEnum ?
     713                         "cannot bind enum descriptor to a non-enum class" :
     714                         "cannot bind non-enum descriptor to an enum class");
     715             }
     716
     717             if (model.serializable == osc.serializable &&
     718                     !cl.isArray() && !isRecord(cl) &&
     719                     suid != osc.getSerialVersionUID()) {
     720                 throw new InvalidClassException(osc.name,
     721                         "local class incompatible: " +
     722                                 "stream classdesc serialVersionUID = " + suid +
     723                                 ", local class serialVersionUID = " +
     724                                 osc.getSerialVersionUID());
     725             }
     726
     727             if (!classNamesEqual(model.name, osc.name)) {
     728                 throw new InvalidClassException(osc.name,
     729                         "local class name incompatible with stream class " +
     730                                 "name \"" + model.name + "\"");
     731             }
     732
     733             if (!model.isEnum) {
     734                 if ((model.serializable == osc.serializable) &&
     735                         (model.externalizable != osc.externalizable)) {
     736                     throw new InvalidClassException(osc.name,
     737                             "Serializable incompatible with Externalizable");
     738                 }
     739
     740                 if ((model.serializable != osc.serializable) ||
     741                         (model.externalizable != osc.externalizable) ||
     742                         !(model.serializable || model.externalizable)) {
     743                     deserializeEx = new ExceptionInfo(
     744                             osc.name, "class invalid for deserialization");
     745                 }
     746             }
     747         }
     748
     749         this.cl = cl;
     750         this.resolveEx = resolveEx;
     751         this.superDesc = superDesc;
     752         name = model.name;
     753         this.suid = suid;
     754         isProxy = false;
     755         isEnum = model.isEnum;
     756         serializable = model.serializable;
     757         externalizable = model.externalizable;
     758         hasBlockExternalData = model.hasBlockExternalData;
     759         hasWriteObjectData = model.hasWriteObjectData;
     760         fields = model.fields;
     761         primDataSize = model.primDataSize;
     762         numObjFields = model.numObjFields;
     763
     764         if (osc != null) {
     765             localDesc = osc;
     766             isRecord = localDesc.isRecord;
     767             // canonical record constructor is shared
     768             canonicalCtr = localDesc.canonicalCtr;
     769             // cache of deserialization constructors is shared
     770             deserializationCtrs = localDesc.deserializationCtrs;
     771             writeObjectMethod = localDesc.writeObjectMethod;
     772             readObjectMethod = localDesc.readObjectMethod;
     773             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
     774             writeReplaceMethod = localDesc.writeReplaceMethod;
     775             readResolveMethod = localDesc.readResolveMethod;
     776             if (deserializeEx == null) {
     777                 deserializeEx = localDesc.deserializeEx;
     778             }
     779             domains = localDesc.domains;
     780             assert isRecord(cl) ? localDesc.cons == null : true;
     781             cons = localDesc.cons;
     782         }
     783
     784         fieldRefl = getReflector(fields, localDesc);
     785         // reassign to matched fields so as to reflect local unshared settings
     786         fields = fieldRefl.getFields();
     787
     788         initialized = true;
     789     }
     790
     791     /**
     792      * Reads non-proxy class descriptor information from given input stream.
     793      * The resulting class descriptor is not fully functional; it can only be
     794      * used as input to the ObjectInputStream.resolveClass() and
     795      * ObjectStreamClass.initNonProxy() methods.
     796      */
     797     void readNonProxy(ObjectInputStream in)
     798         throws IOException, ClassNotFoundException
     799     {
     800         name = in.readUTF();
     801         suid = Long.valueOf(in.readLong());
     802         isProxy = false;
     803
     804         byte flags = in.readByte();
     805         hasWriteObjectData =
     806             ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
     807         hasBlockExternalData =
     808             ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
     809         externalizable =
     810             ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
     811         boolean sflag =
     812             ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
     813         if (externalizable && sflag) {
     814             throw new InvalidClassException(
     815                 name, "serializable and externalizable flags conflict");
     816         }
     817         serializable = externalizable || sflag;
     818         isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
     819         if (isEnum && suid.longValue() != 0L) {
     820             throw new InvalidClassException(name,
     821                 "enum descriptor has non-zero serialVersionUID: " + suid);
     822         }
     823
     824         int numFields = in.readShort();
     825         if (isEnum && numFields != 0) {
     826             throw new InvalidClassException(name,
     827                 "enum descriptor has non-zero field count: " + numFields);
     828         }
     829         fields = (numFields > 0) ?
     830             new ObjectStreamField[numFields] : NO_FIELDS;
     831         for (int i = 0; i < numFields; i++) {
     832             char tcode = (char) in.readByte();
     833             String fname = in.readUTF();
     834             String signature = ((tcode == 'L') || (tcode == '[')) ?
     835                 in.readTypeString() : new String(new char[] { tcode });
     836             try {
     837                 fields[i] = new ObjectStreamField(fname, signature, false);
     838             } catch (RuntimeException e) {
     839                 throw (IOException) new InvalidClassException(name,
     840                     "invalid descriptor for field " + fname).initCause(e);
     841             }
     842         }
     843         computeFieldOffsets();
     844     }
     845
     846     /**
     847      * Writes non-proxy class descriptor information to given output stream.
     848      */
     849     void writeNonProxy(ObjectOutputStream out) throws IOException {
     850         out.writeUTF(name);
     851         out.writeLong(getSerialVersionUID());
     852
     853         byte flags = 0;
     854         if (externalizable) {
     855             flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
     856             int protocol = out.getProtocolVersion();
     857             if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
     858                 flags |= ObjectStreamConstants.SC_BLOCK_DATA;
     859             }
     860         } else if (serializable) {
     861             flags |= ObjectStreamConstants.SC_SERIALIZABLE;
     862         }
     863         if (hasWriteObjectData) {
     864             flags |= ObjectStreamConstants.SC_WRITE_METHOD;
     865         }
     866         if (isEnum) {
     867             flags |= ObjectStreamConstants.SC_ENUM;
     868         }
     869         out.writeByte(flags);
     870
     871         out.writeShort(fields.length);
     872         for (int i = 0; i < fields.length; i++) {
     873             ObjectStreamField f = fields[i];
     874             out.writeByte(f.getTypeCode());
     875             out.writeUTF(f.getName());
     876             if (!f.isPrimitive()) {
     877                 out.writeTypeString(f.getTypeString());
     878             }
     879         }
     880     }
     881
     882     /**
     883      * Returns ClassNotFoundException (if any) thrown while attempting to
     884      * resolve local class corresponding to this class descriptor.
     885      */
     886     ClassNotFoundException getResolveException() {
     887         return resolveEx;
     888     }
     889
     890     /**
     891      * Throws InternalError if not initialized.
     892      */
     893     private final void requireInitialized() {
     894         if (!initialized)
     895             throw new InternalError("Unexpected call when not initialized");
     896     }
     897
     898     /**
     899      * Throws InvalidClassException if not initialized.
     900      * To be called in cases where an uninitialized class descriptor indicates
     901      * a problem in the serialization stream.
     902      */
     903     final void checkInitialized() throws InvalidClassException {
     904         if (!initialized) {
     905             throw new InvalidClassException("Class descriptor should be initialized");
     906         }
     907     }
     908
     909     /**
     910      * Throws an InvalidClassException if object instances referencing this
     911      * class descriptor should not be allowed to deserialize.  This method does
     912      * not apply to deserialization of enum constants.
     913      */
     914     void checkDeserialize() throws InvalidClassException {
     915         requireInitialized();
     916         if (deserializeEx != null) {
     917             throw deserializeEx.newInvalidClassException();
     918         }
     919     }
     920
     921     /**
     922      * Throws an InvalidClassException if objects whose class is represented by
     923      * this descriptor should not be allowed to serialize.  This method does
     924      * not apply to serialization of enum constants.
     925      */
     926     void checkSerialize() throws InvalidClassException {
     927         requireInitialized();
     928         if (serializeEx != null) {
     929             throw serializeEx.newInvalidClassException();
     930         }
     931     }
     932
     933     /**
     934      * Throws an InvalidClassException if objects whose class is represented by
     935      * this descriptor should not be permitted to use default serialization
     936      * (e.g., if the class declares serializable fields that do not correspond
     937      * to actual fields, and hence must use the GetField API).  This method
     938      * does not apply to deserialization of enum constants.
     939      */
     940     void checkDefaultSerialize() throws InvalidClassException {
     941         requireInitialized();
     942         if (defaultSerializeEx != null) {
     943             throw defaultSerializeEx.newInvalidClassException();
     944         }
     945     }
     946
     947     /**
     948      * Returns superclass descriptor.  Note that on the receiving side, the
     949      * superclass descriptor may be bound to a class that is not a superclass
     950      * of the subclass descriptor's bound class.
     951      */
     952     ObjectStreamClass getSuperDesc() {
     953         requireInitialized();
     954         return superDesc;
     955     }
     956
     957     /**
     958      * Returns the "local" class descriptor for the class associated with this
     959      * class descriptor (i.e., the result of
     960      * ObjectStreamClass.lookup(this.forClass())) or null if there is no class
     961      * associated with this descriptor.
     962      */
     963     ObjectStreamClass getLocalDesc() {
     964         requireInitialized();
     965         return localDesc;
     966     }
     967
     968     /**
     969      * Returns arrays of ObjectStreamFields representing the serializable
     970      * fields of the represented class.  If copy is true, a clone of this class
     971      * descriptor's field array is returned, otherwise the array itself is
     972      * returned.
     973      */
     974     ObjectStreamField[] getFields(boolean copy) {
     975         return copy ? fields.clone() : fields;
     976     }
     977
     978     /**
     979      * Looks up a serializable field of the represented class by name and type.
     980      * A specified type of null matches all types, Object.class matches all
     981      * non-primitive types, and any other non-null type matches assignable
     982      * types only.  Returns matching field, or null if no match found.
     983      */
     984     ObjectStreamField getField(String name, Class<?> type) {
     985         for (int i = 0; i < fields.length; i++) {
     986             ObjectStreamField f = fields[i];
     987             if (f.getName().equals(name)) {
     988                 if (type == null ||
     989                     (type == Object.class && !f.isPrimitive()))
     990                 {
     991                     return f;
     992                 }
     993                 Class<?> ftype = f.getType();
     994                 if (ftype != null && type.isAssignableFrom(ftype)) {
     995                     return f;
     996                 }
     997             }
     998         }
     999         return null;
    1000     }
    1001
    1002     /**
    1003      * Returns true if class descriptor represents a dynamic proxy class, false
    1004      * otherwise.
    1005      */
    1006     boolean isProxy() {
    1007         requireInitialized();
    1008         return isProxy;
    1009     }
    1010
    1011     /**
    1012      * Returns true if class descriptor represents an enum type, false
    1013      * otherwise.
    1014      */
    1015     boolean isEnum() {
    1016         requireInitialized();
    1017         return isEnum;
    1018     }
    1019
    1020     /**
    1021      * Returns true if class descriptor represents a record type, false
    1022      * otherwise.
    1023      */
    1024     boolean isRecord() {
    1025         requireInitialized();
    1026         return isRecord;
    1027     }
    1028
    1029     /**
    1030      * Returns true if represented class implements Externalizable, false
    1031      * otherwise.
    1032      */
    1033     boolean isExternalizable() {
    1034         requireInitialized();
    1035         return externalizable;
    1036     }
    1037
    1038     /**
    1039      * Returns true if represented class implements Serializable, false
    1040      * otherwise.
    1041      */
    1042     boolean isSerializable() {
    1043         requireInitialized();
    1044         return serializable;
    1045     }
    1046
    1047     /**
    1048      * Returns true if class descriptor represents externalizable class that
    1049      * has written its data in 1.2 (block data) format, false otherwise.
    1050      */
    1051     boolean hasBlockExternalData() {
    1052         requireInitialized();
    1053         return hasBlockExternalData;
    1054     }
    1055
    1056     /**
    1057      * Returns true if class descriptor represents serializable (but not
    1058      * externalizable) class which has written its data via a custom
    1059      * writeObject() method, false otherwise.
    1060      */
    1061     boolean hasWriteObjectData() {
    1062         requireInitialized();
    1063         return hasWriteObjectData;
    1064     }
    1065
    1066     /**
    1067      * Returns true if represented class is serializable/externalizable and can
    1068      * be instantiated by the serialization runtime--i.e., if it is
    1069      * externalizable and defines a public no-arg constructor, or if it is
    1070      * non-externalizable and its first non-serializable superclass defines an
    1071      * accessible no-arg constructor.  Otherwise, returns false.
    1072      */
    1073     boolean isInstantiable() {
    1074         requireInitialized();
    1075         return (cons != null);
    1076     }
    1077
    1078     /**
    1079      * Returns true if represented class is serializable (but not
    1080      * externalizable) and defines a conformant writeObject method.  Otherwise,
    1081      * returns false.
    1082      */
    1083     boolean hasWriteObjectMethod() {
    1084         requireInitialized();
    1085         return (writeObjectMethod != null);
    1086     }
    1087
    1088     /**
    1089      * Returns true if represented class is serializable (but not
    1090      * externalizable) and defines a conformant readObject method.  Otherwise,
    1091      * returns false.
    1092      */
    1093     boolean hasReadObjectMethod() {
    1094         requireInitialized();
    1095         return (readObjectMethod != null);
    1096     }
    1097
    1098     /**
    1099      * Returns true if represented class is serializable (but not
    1100      * externalizable) and defines a conformant readObjectNoData method.
    1101      * Otherwise, returns false.
    1102      */
    1103     boolean hasReadObjectNoDataMethod() {
    1104         requireInitialized();
    1105         return (readObjectNoDataMethod != null);
    1106     }
    1107
    1108     /**
    1109      * Returns true if represented class is serializable or externalizable and
    1110      * defines a conformant writeReplace method.  Otherwise, returns false.
    1111      */
    1112     boolean hasWriteReplaceMethod() {
    1113         requireInitialized();
    1114         return (writeReplaceMethod != null);
    1115     }
    1116
    1117     /**
    1118      * Returns true if represented class is serializable or externalizable and
    1119      * defines a conformant readResolve method.  Otherwise, returns false.
    1120      */
    1121     boolean hasReadResolveMethod() {
    1122         requireInitialized();
    1123         return (readResolveMethod != null);
    1124     }
    1125
    1126     /**
    1127      * Creates a new instance of the represented class.  If the class is
    1128      * externalizable, invokes its public no-arg constructor; otherwise, if the
    1129      * class is serializable, invokes the no-arg constructor of the first
    1130      * non-serializable superclass.  Throws UnsupportedOperationException if
    1131      * this class descriptor is not associated with a class, if the associated
    1132      * class is non-serializable or if the appropriate no-arg constructor is
    1133      * inaccessible/unavailable.
    1134      */
    1135     Object newInstance()
    1136         throws InstantiationException, InvocationTargetException,
    1137                UnsupportedOperationException
    1138     {
    1139         requireInitialized();
    1140         if (cons != null) {
    1141             try {
    1142                 if (domains == null || domains.length == 0) {
    1143                     return cons.newInstance();
    1144                 } else {
    1145                     JavaSecurityAccess jsa = SharedSecrets.getJavaSecurityAccess();
    1146                     PrivilegedAction<?> pea = () -> {
    1147                         try {
    1148                             return cons.newInstance();
    1149                         } catch (InstantiationException
    1150                                  | InvocationTargetException
    1151                                  | IllegalAccessException x) {
    1152                             throw new UndeclaredThrowableException(x);
    1153                         }
    1154                     }; // Can't use PrivilegedExceptionAction with jsa
    1155                     try {
    1156                         return jsa.doIntersectionPrivilege(pea,
    1157                                    AccessController.getContext(),
    1158                                    new AccessControlContext(domains));
    1159                     } catch (UndeclaredThrowableException x) {
    1160                         Throwable cause = x.getCause();
    1161                         if (cause instanceof InstantiationException)
    1162                             throw (InstantiationException) cause;
    1163                         if (cause instanceof InvocationTargetException)
    1164                             throw (InvocationTargetException) cause;
    1165                         if (cause instanceof IllegalAccessException)
    1166                             throw (IllegalAccessException) cause;
    1167                         // not supposed to happen
    1168                         throw x;
    1169                     }
    1170                 }
    1171             } catch (IllegalAccessException ex) {
    1172                 // should not occur, as access checks have been suppressed
    1173                 throw new InternalError(ex);
    1174             } catch (InstantiationError err) {
    1175                 var ex = new InstantiationException();
    1176                 ex.initCause(err);
    1177                 throw ex;
    1178             }
    1179         } else {
    1180             throw new UnsupportedOperationException();
    1181         }
    1182     }
    1183
    1184     /**
    1185      * Invokes the writeObject method of the represented serializable class.
    1186      * Throws UnsupportedOperationException if this class descriptor is not
    1187      * associated with a class, or if the class is externalizable,
    1188      * non-serializable or does not define writeObject.
    1189      */
    1190     void invokeWriteObject(Object obj, ObjectOutputStream out)
    1191         throws IOException, UnsupportedOperationException
    1192     {
    1193         requireInitialized();
    1194         if (writeObjectMethod != null) {
    1195             try {
    1196                 writeObjectMethod.invoke(obj, new Object[]{ out });
    1197             } catch (InvocationTargetException ex) {
    1198                 Throwable th = ex.getTargetException();
    1199                 if (th instanceof IOException) {
    1200                     throw (IOException) th;
    1201                 } else {
    1202                     throwMiscException(th);
    1203                 }
    1204             } catch (IllegalAccessException ex) {
    1205                 // should not occur, as access checks have been suppressed
    1206                 throw new InternalError(ex);
    1207             }
    1208         } else {
    1209             throw new UnsupportedOperationException();
    1210         }
    1211     }
    1212
    1213     /**
    1214      * Invokes the readObject method of the represented serializable class.
    1215      * Throws UnsupportedOperationException if this class descriptor is not
    1216      * associated with a class, or if the class is externalizable,
    1217      * non-serializable or does not define readObject.
    1218      */
    1219     void invokeReadObject(Object obj, ObjectInputStream in)
    1220         throws ClassNotFoundException, IOException,
    1221                UnsupportedOperationException
    1222     {
    1223         requireInitialized();
    1224         if (readObjectMethod != null) {
    1225             try {
    1226                 readObjectMethod.invoke(obj, new Object[]{ in });
    1227             } catch (InvocationTargetException ex) {
    1228                 Throwable th = ex.getTargetException();
    1229                 if (th instanceof ClassNotFoundException) {
    1230                     throw (ClassNotFoundException) th;
    1231                 } else if (th instanceof IOException) {
    1232                     throw (IOException) th;
    1233                 } else {
    1234                     throwMiscException(th);
    1235                 }
    1236             } catch (IllegalAccessException ex) {
    1237                 // should not occur, as access checks have been suppressed
    1238                 throw new InternalError(ex);
    1239             }
    1240         } else {
    1241             throw new UnsupportedOperationException();
    1242         }
    1243     }
    1244
    1245     /**
    1246      * Invokes the readObjectNoData method of the represented serializable
    1247      * class.  Throws UnsupportedOperationException if this class descriptor is
    1248      * not associated with a class, or if the class is externalizable,
    1249      * non-serializable or does not define readObjectNoData.
    1250      */
    1251     void invokeReadObjectNoData(Object obj)
    1252         throws IOException, UnsupportedOperationException
    1253     {
    1254         requireInitialized();
    1255         if (readObjectNoDataMethod != null) {
    1256             try {
    1257                 readObjectNoDataMethod.invoke(obj, (Object[]) null);
    1258             } catch (InvocationTargetException ex) {
    1259                 Throwable th = ex.getTargetException();
    1260                 if (th instanceof ObjectStreamException) {
    1261                     throw (ObjectStreamException) th;
    1262                 } else {
    1263                     throwMiscException(th);
    1264                 }
    1265             } catch (IllegalAccessException ex) {
    1266                 // should not occur, as access checks have been suppressed
    1267                 throw new InternalError(ex);
    1268             }
    1269         } else {
    1270             throw new UnsupportedOperationException();
    1271         }
    1272     }
    1273
    1274     /**
    1275      * Invokes the writeReplace method of the represented serializable class and
    1276      * returns the result.  Throws UnsupportedOperationException if this class
    1277      * descriptor is not associated with a class, or if the class is
    1278      * non-serializable or does not define writeReplace.
    1279      */
    1280     Object invokeWriteReplace(Object obj)
    1281         throws IOException, UnsupportedOperationException
    1282     {
    1283         requireInitialized();
    1284         if (writeReplaceMethod != null) {
    1285             try {
    1286                 return writeReplaceMethod.invoke(obj, (Object[]) null);
    1287             } catch (InvocationTargetException ex) {
    1288                 Throwable th = ex.getTargetException();
    1289                 if (th instanceof ObjectStreamException) {
    1290                     throw (ObjectStreamException) th;
    1291                 } else {
    1292                     throwMiscException(th);
    1293                     throw new InternalError(th);  // never reached
    1294                 }
    1295             } catch (IllegalAccessException ex) {
    1296                 // should not occur, as access checks have been suppressed
    1297                 throw new InternalError(ex);
    1298             }
    1299         } else {
    1300             throw new UnsupportedOperationException();
    1301         }
    1302     }
    1303
    1304     /**
    1305      * Invokes the readResolve method of the represented serializable class and
    1306      * returns the result.  Throws UnsupportedOperationException if this class
    1307      * descriptor is not associated with a class, or if the class is
    1308      * non-serializable or does not define readResolve.
    1309      */
    1310     Object invokeReadResolve(Object obj)
    1311         throws IOException, UnsupportedOperationException
    1312     {
    1313         requireInitialized();
    1314         if (readResolveMethod != null) {
    1315             try {
    1316                 return readResolveMethod.invoke(obj, (Object[]) null);
    1317             } catch (InvocationTargetException ex) {
    1318                 Throwable th = ex.getTargetException();
    1319                 if (th instanceof ObjectStreamException) {
    1320                     throw (ObjectStreamException) th;
    1321                 } else {
    1322                     throwMiscException(th);
    1323                     throw new InternalError(th);  // never reached
    1324                 }
    1325             } catch (IllegalAccessException ex) {
    1326                 // should not occur, as access checks have been suppressed
    1327                 throw new InternalError(ex);
    1328             }
    1329         } else {
    1330             throw new UnsupportedOperationException();
    1331         }
    1332     }
    1333
    1334     /**
    1335      * Class representing the portion of an object's serialized form allotted
    1336      * to data described by a given class descriptor.  If "hasData" is false,
    1337      * the object's serialized form does not contain data associated with the
    1338      * class descriptor.
    1339      */
    1340     static class ClassDataSlot {
    1341
    1342         /** class descriptor "occupying" this slot */
    1343         final ObjectStreamClass desc;
    1344         /** true if serialized form includes data for this slot's descriptor */
    1345         final boolean hasData;
    1346
    1347         ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
    1348             this.desc = desc;
    1349             this.hasData = hasData;
    1350         }
    1351     }
    1352
    1353     /**
    1354      * Returns array of ClassDataSlot instances representing the data layout
    1355      * (including superclass data) for serialized objects described by this
    1356      * class descriptor.  ClassDataSlots are ordered by inheritance with those
    1357      * containing "higher" superclasses appearing first.  The final
    1358      * ClassDataSlot contains a reference to this descriptor.
    1359      */
    1360     ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
    1361         // REMIND: synchronize instead of relying on volatile?
    1362         if (dataLayout == null) {
    1363             dataLayout = getClassDataLayout0();
    1364         }
    1365         return dataLayout;
    1366     }
    1367
    1368     private ClassDataSlot[] getClassDataLayout0()
    1369         throws InvalidClassException
    1370     {
    1371         ArrayList<ClassDataSlot> slots = new ArrayList<>();
    1372         Class<?> start = cl, end = cl;
    1373
    1374         // locate closest non-serializable superclass
    1375         while (end != null && Serializable.class.isAssignableFrom(end)) {
    1376             end = end.getSuperclass();
    1377         }
    1378
    1379         HashSet<String> oscNames = new HashSet<>(3);
    1380
    1381         for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
    1382             if (oscNames.contains(d.name)) {
    1383                 throw new InvalidClassException("Circular reference.");
    1384             } else {
    1385                 oscNames.add(d.name);
    1386             }
    1387
    1388             // search up inheritance hierarchy for class with matching name
    1389             String searchName = (d.cl != null) ? d.cl.getName() : d.name;
    1390             Class<?> match = null;
    1391             for (Class<?> c = start; c != end; c = c.getSuperclass()) {
    1392                 if (searchName.equals(c.getName())) {
    1393                     match = c;
    1394                     break;
    1395                 }
    1396             }
    1397
    1398             // add "no data" slot for each unmatched class below match
    1399             if (match != null) {
    1400                 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
    1401                     slots.add(new ClassDataSlot(
    1402                         ObjectStreamClass.lookup(c, true), false));
    1403                 }
    1404                 start = match.getSuperclass();
    1405             }
    1406
    1407             // record descriptor/class pairing
    1408             slots.add(new ClassDataSlot(d.getVariantFor(match), true));
    1409         }
    1410
    1411         // add "no data" slot for any leftover unmatched classes
    1412         for (Class<?> c = start; c != end; c = c.getSuperclass()) {
    1413             slots.add(new ClassDataSlot(
    1414                 ObjectStreamClass.lookup(c, true), false));
    1415         }
    1416
    1417         // order slots from superclass -> subclass
    1418         Collections.reverse(slots);
    1419         return slots.toArray(new ClassDataSlot[slots.size()]);
    1420     }
    1421
    1422     /**
    1423      * Returns aggregate size (in bytes) of marshalled primitive field values
    1424      * for represented class.
    1425      */
    1426     int getPrimDataSize() {
    1427         return primDataSize;
    1428     }
    1429
    1430     /**
    1431      * Returns number of non-primitive serializable fields of represented
    1432      * class.
    1433      */
    1434     int getNumObjFields() {
    1435         return numObjFields;
    1436     }
    1437
    1438     /**
    1439      * Fetches the serializable primitive field values of object obj and
    1440      * marshals them into byte array buf starting at offset 0.  It is the
    1441      * responsibility of the caller to ensure that obj is of the proper type if
    1442      * non-null.
    1443      */
    1444     void getPrimFieldValues(Object obj, byte[] buf) {
    1445         fieldRefl.getPrimFieldValues(obj, buf);
    1446     }
    1447
    1448     /**
    1449      * Sets the serializable primitive fields of object obj using values
    1450      * unmarshalled from byte array buf starting at offset 0.  It is the
    1451      * responsibility of the caller to ensure that obj is of the proper type if
    1452      * non-null.
    1453      */
    1454     void setPrimFieldValues(Object obj, byte[] buf) {
    1455         fieldRefl.setPrimFieldValues(obj, buf);
    1456     }
    1457
    1458     /**
    1459      * Fetches the serializable object field values of object obj and stores
    1460      * them in array vals starting at offset 0.  It is the responsibility of
    1461      * the caller to ensure that obj is of the proper type if non-null.
    1462      */
    1463     void getObjFieldValues(Object obj, Object[] vals) {
    1464         fieldRefl.getObjFieldValues(obj, vals);
    1465     }
    1466
    1467     /**
    1468      * Checks that the given values, from array vals starting at offset 0,
    1469      * are assignable to the given serializable object fields.
    1470      * @throws ClassCastException if any value is not assignable
    1471      */
    1472     void checkObjFieldValueTypes(Object obj, Object[] vals) {
    1473         fieldRefl.checkObjectFieldValueTypes(obj, vals);
    1474     }
    1475
    1476     /**
    1477      * Sets the serializable object fields of object obj using values from
    1478      * array vals starting at offset 0.  It is the responsibility of the caller
    1479      * to ensure that obj is of the proper type if non-null.
    1480      */
    1481     void setObjFieldValues(Object obj, Object[] vals) {
    1482         fieldRefl.setObjFieldValues(obj, vals);
    1483     }
    1484
    1485     /**
    1486      * Calculates and sets serializable field offsets, as well as primitive
    1487      * data size and object field count totals.  Throws InvalidClassException
    1488      * if fields are illegally ordered.
    1489      */
    1490     private void computeFieldOffsets() throws InvalidClassException {
    1491         primDataSize = 0;
    1492         numObjFields = 0;
    1493         int firstObjIndex = -1;
    1494
    1495         for (int i = 0; i < fields.length; i++) {
    1496             ObjectStreamField f = fields[i];
    1497             switch (f.getTypeCode()) {
    1498                 case 'Z':
    1499                 case 'B':
    1500                     f.setOffset(primDataSize++);
    1501                     break;
    1502
    1503                 case 'C':
    1504                 case 'S':
    1505                     f.setOffset(primDataSize);
    1506                     primDataSize += 2;
    1507                     break;
    1508
    1509                 case 'I':
    1510                 case 'F':
    1511                     f.setOffset(primDataSize);
    1512                     primDataSize += 4;
    1513                     break;
    1514
    1515                 case 'J':
    1516                 case 'D':
    1517                     f.setOffset(primDataSize);
    1518                     primDataSize += 8;
    1519                     break;
    1520
    1521                 case '[':
    1522                 case 'L':
    1523                     f.setOffset(numObjFields++);
    1524                     if (firstObjIndex == -1) {
    1525                         firstObjIndex = i;
    1526                     }
    1527                     break;
    1528
    1529                 default:
    1530                     throw new InternalError();
    1531             }
    1532         }
    1533         if (firstObjIndex != -1 &&
    1534             firstObjIndex + numObjFields != fields.length)
    1535         {
    1536             throw new InvalidClassException(name, "illegal field order");
    1537         }
    1538     }
    1539
    1540     /**
    1541      * If given class is the same as the class associated with this class
    1542      * descriptor, returns reference to this class descriptor.  Otherwise,
    1543      * returns variant of this class descriptor bound to given class.
    1544      */
    1545     private ObjectStreamClass getVariantFor(Class<?> cl)
    1546         throws InvalidClassException
    1547     {
    1548         if (this.cl == cl) {
    1549             return this;
    1550         }
    1551         ObjectStreamClass desc = new ObjectStreamClass();
    1552         if (isProxy) {
    1553             desc.initProxy(cl, null, superDesc);
    1554         } else {
    1555             desc.initNonProxy(this, cl, null, superDesc);
    1556         }
    1557         return desc;
    1558     }
    1559
    1560     /**
    1561      * Returns public no-arg constructor of given class, or null if none found.
    1562      * Access checks are disabled on the returned constructor (if any), since
    1563      * the defining class may still be non-public.
    1564      */
    1565     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
    1566         try {
    1567             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
    1568             cons.setAccessible(true);
    1569             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
    1570                 cons : null;
    1571         } catch (NoSuchMethodException ex) {
    1572             return null;
    1573         }
    1574     }
    1575
    1576     /**
    1577      * Returns subclass-accessible no-arg constructor of first non-serializable
    1578      * superclass, or null if none found.  Access checks are disabled on the
    1579      * returned constructor (if any).
    1580      */
    1581     private static Constructor<?> getSerializableConstructor(Class<?> cl) {
    1582         return reflFactory.newConstructorForSerialization(cl);
    1583     }
    1584
    1585     /**
    1586      * Returns the canonical constructor for the given record class, or null if
    1587      * the not found ( which should never happen for correctly generated record
    1588      * classes ).
    1589      */
    1590     @SuppressWarnings("preview")
    1591     private static MethodHandle canonicalRecordCtr(Class<?> cls) {
    1592         assert isRecord(cls) : "Expected record, got: " + cls;
    1593         PrivilegedAction<MethodHandle> pa = () -> {
    1594             Class<?>[] paramTypes = Arrays.stream(cls.getRecordComponents())
    1595                                           .map(RecordComponent::getType)
    1596                                           .toArray(Class<?>[]::new);
    1597             try {
    1598                 Constructor<?> ctr = cls.getDeclaredConstructor(paramTypes);
    1599                 ctr.setAccessible(true);
    1600                 return MethodHandles.lookup().unreflectConstructor(ctr);
    1601             } catch (IllegalAccessException | NoSuchMethodException e) {
    1602                 return null;
    1603             }
    1604         };
    1605         return AccessController.doPrivileged(pa);
    1606     }
    1607
    1608     /**
    1609      * Returns the canonical constructor, if the local class equivalent of this
    1610      * stream class descriptor is a record class, otherwise null.
    1611      */
    1612     MethodHandle getRecordConstructor() {
    1613         return canonicalCtr;
    1614     }
    1615
    1616     /**
    1617      * Returns non-static, non-abstract method with given signature provided it
    1618      * is defined by or accessible (via inheritance) by the given class, or
    1619      * null if no match found.  Access checks are disabled on the returned
    1620      * method (if any).
    1621      */
    1622     private static Method getInheritableMethod(Class<?> cl, String name,
    1623                                                Class<?>[] argTypes,
    1624                                                Class<?> returnType)
    1625     {
    1626         Method meth = null;
    1627         Class<?> defCl = cl;
    1628         while (defCl != null) {
    1629             try {
    1630                 meth = defCl.getDeclaredMethod(name, argTypes);
    1631                 break;
    1632             } catch (NoSuchMethodException ex) {
    1633                 defCl = defCl.getSuperclass();
    1634             }
    1635         }
    1636
    1637         if ((meth == null) || (meth.getReturnType() != returnType)) {
    1638             return null;
    1639         }
    1640         meth.setAccessible(true);
    1641         int mods = meth.getModifiers();
    1642         if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) {
    1643             return null;
    1644         } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
    1645             return meth;
    1646         } else if ((mods & Modifier.PRIVATE) != 0) {
    1647             return (cl == defCl) ? meth : null;
    1648         } else {
    1649             return packageEquals(cl, defCl) ? meth : null;
    1650         }
    1651     }
    1652
    1653     /**
    1654      * Returns non-static private method with given signature defined by given
    1655      * class, or null if none found.  Access checks are disabled on the
    1656      * returned method (if any).
    1657      */
    1658     private static Method getPrivateMethod(Class<?> cl, String name,
    1659                                            Class<?>[] argTypes,
    1660                                            Class<?> returnType)
    1661     {
    1662         try {
    1663             Method meth = cl.getDeclaredMethod(name, argTypes);
    1664             meth.setAccessible(true);
    1665             int mods = meth.getModifiers();
    1666             return ((meth.getReturnType() == returnType) &&
    1667                     ((mods & Modifier.STATIC) == 0) &&
    1668                     ((mods & Modifier.PRIVATE) != 0)) ? meth : null;
    1669         } catch (NoSuchMethodException ex) {
    1670             return null;
    1671         }
    1672     }
    1673
    1674     /**
    1675      * Returns true if classes are defined in the same runtime package, false
    1676      * otherwise.
    1677      */
    1678     private static boolean packageEquals(Class<?> cl1, Class<?> cl2) {
    1679         return (cl1.getClassLoader() == cl2.getClassLoader() &&
    1680                 cl1.getPackageName().equals(cl2.getPackageName()));
    1681     }
    1682
    1683     /**
    1684      * Compares class names for equality, ignoring package names.  Returns true
    1685      * if class names equal, false otherwise.
    1686      */
    1687     private static boolean classNamesEqual(String name1, String name2) {
    1688         int idx1 = name1.lastIndexOf('.') + 1;
    1689         int idx2 = name2.lastIndexOf('.') + 1;
    1690         int len1 = name1.length() - idx1;
    1691         int len2 = name2.length() - idx2;
    1692         return len1 == len2 &&
    1693                 name1.regionMatches(idx1, name2, idx2, len1);
    1694     }
    1695
    1696     /**
    1697      * Returns JVM type signature for given list of parameters and return type.
    1698      */
    1699     private static String getMethodSignature(Class<?>[] paramTypes,
    1700                                              Class<?> retType)
    1701     {
    1702         StringBuilder sb = new StringBuilder();
    1703         sb.append('(');
    1704         for (int i = 0; i < paramTypes.length; i++) {
    1705             appendClassSignature(sb, paramTypes[i]);
    1706         }
    1707         sb.append(')');
    1708         appendClassSignature(sb, retType);
    1709         return sb.toString();
    1710     }
    1711
    1712     /**
    1713      * Convenience method for throwing an exception that is either a
    1714      * RuntimeException, Error, or of some unexpected type (in which case it is
    1715      * wrapped inside an IOException).
    1716      */
    1717     private static void throwMiscException(Throwable th) throws IOException {
    1718         if (th instanceof RuntimeException) {
    1719             throw (RuntimeException) th;
    1720         } else if (th instanceof Error) {
    1721             throw (Error) th;
    1722         } else {
    1723             IOException ex = new IOException("unexpected exception type");
    1724             ex.initCause(th);
    1725             throw ex;
    1726         }
    1727     }
    1728
    1729     /**
    1730      * Returns ObjectStreamField array describing the serializable fields of
    1731      * the given class.  Serializable fields backed by an actual field of the
    1732      * class are represented by ObjectStreamFields with corresponding non-null
    1733      * Field objects.  Throws InvalidClassException if the (explicitly
    1734      * declared) serializable fields are invalid.
    1735      */
    1736     private static ObjectStreamField[] getSerialFields(Class<?> cl)
    1737         throws InvalidClassException
    1738     {
    1739         if (!Serializable.class.isAssignableFrom(cl))
    1740             return NO_FIELDS;
    1741
    1742         ObjectStreamField[] fields;
    1743         if (isRecord(cl)) {
    1744             fields = getDefaultSerialFields(cl);
    1745             Arrays.sort(fields);
    1746         } else if (!Externalizable.class.isAssignableFrom(cl) &&
    1747             !Proxy.isProxyClass(cl) &&
    1748                    !cl.isInterface()) {
    1749             if ((fields = getDeclaredSerialFields(cl)) == null) {
    1750                 fields = getDefaultSerialFields(cl);
    1751             }
    1752             Arrays.sort(fields);
    1753         } else {
    1754             fields = NO_FIELDS;
    1755         }
    1756         return fields;
    1757     }
    1758
    1759     /**
    1760      * Returns serializable fields of given class as defined explicitly by a
    1761      * "serialPersistentFields" field, or null if no appropriate
    1762      * "serialPersistentFields" field is defined.  Serializable fields backed
    1763      * by an actual field of the class are represented by ObjectStreamFields
    1764      * with corresponding non-null Field objects.  For compatibility with past
    1765      * releases, a "serialPersistentFields" field with a null value is
    1766      * considered equivalent to not declaring "serialPersistentFields".  Throws
    1767      * InvalidClassException if the declared serializable fields are
    1768      * invalid--e.g., if multiple fields share the same name.
    1769      */
    1770     private static ObjectStreamField[] getDeclaredSerialFields(Class<?> cl)
    1771         throws InvalidClassException
    1772     {
    1773         ObjectStreamField[] serialPersistentFields = null;
    1774         try {
    1775             Field f = cl.getDeclaredField("serialPersistentFields");
    1776             int mask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
    1777             if ((f.getModifiers() & mask) == mask) {
    1778                 f.setAccessible(true);
    1779                 serialPersistentFields = (ObjectStreamField[]) f.get(null);
    1780             }
    1781         } catch (Exception ex) {
    1782         }
    1783         if (serialPersistentFields == null) {
    1784             return null;
    1785         } else if (serialPersistentFields.length == 0) {
    1786             return NO_FIELDS;
    1787         }
    1788
    1789         ObjectStreamField[] boundFields =
    1790             new ObjectStreamField[serialPersistentFields.length];
    1791         Set<String> fieldNames = new HashSet<>(serialPersistentFields.length);
    1792
    1793         for (int i = 0; i < serialPersistentFields.length; i++) {
    1794             ObjectStreamField spf = serialPersistentFields[i];
    1795
    1796             String fname = spf.getName();
    1797             if (fieldNames.contains(fname)) {
    1798                 throw new InvalidClassException(
    1799                     "multiple serializable fields named " + fname);
    1800             }
    1801             fieldNames.add(fname);
    1802
    1803             try {
    1804                 Field f = cl.getDeclaredField(fname);
    1805                 if ((f.getType() == spf.getType()) &&
    1806                     ((f.getModifiers() & Modifier.STATIC) == 0))
    1807                 {
    1808                     boundFields[i] =
    1809                         new ObjectStreamField(f, spf.isUnshared(), true);
    1810                 }
    1811             } catch (NoSuchFieldException ex) {
    1812             }
    1813             if (boundFields[i] == null) {
    1814                 boundFields[i] = new ObjectStreamField(
    1815                     fname, spf.getType(), spf.isUnshared());
    1816             }
    1817         }
    1818         return boundFields;
    1819     }
    1820
    1821     /**
    1822      * Returns array of ObjectStreamFields corresponding to all non-static
    1823      * non-transient fields declared by given class.  Each ObjectStreamField
    1824      * contains a Field object for the field it represents.  If no default
    1825      * serializable fields exist, NO_FIELDS is returned.
    1826      */
    1827     private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
    1828         Field[] clFields = cl.getDeclaredFields();
    1829         ArrayList<ObjectStreamField> list = new ArrayList<>();
    1830         int mask = Modifier.STATIC | Modifier.TRANSIENT;
    1831
    1832         for (int i = 0; i < clFields.length; i++) {
    1833             if ((clFields[i].getModifiers() & mask) == 0) {
    1834                 list.add(new ObjectStreamField(clFields[i], false, true));
    1835             }
    1836         }
    1837         int size = list.size();
    1838         return (size == 0) ? NO_FIELDS :
    1839             list.toArray(new ObjectStreamField[size]);
    1840     }
    1841
    1842     /**
    1843      * Returns explicit serial version UID value declared by given class, or
    1844      * null if none.
    1845      */
    1846     private static Long getDeclaredSUID(Class<?> cl) {
    1847         try {
    1848             Field f = cl.getDeclaredField("serialVersionUID");
    1849             int mask = Modifier.STATIC | Modifier.FINAL;
    1850             if ((f.getModifiers() & mask) == mask) {
    1851                 f.setAccessible(true);
    1852                 return Long.valueOf(f.getLong(null));
    1853             }
    1854         } catch (Exception ex) {
    1855         }
    1856         return null;
    1857     }
    1858
    1859     /**
    1860      * Computes the default serial version UID value for the given class.
    1861      */
    1862     private static long computeDefaultSUID(Class<?> cl) {
    1863         if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
    1864         {
    1865             return 0L;
    1866         }
    1867
    1868         try {
    1869             ByteArrayOutputStream bout = new ByteArrayOutputStream();
    1870             DataOutputStream dout = new DataOutputStream(bout);
    1871
    1872             dout.writeUTF(cl.getName());
    1873
    1874             int classMods = cl.getModifiers() &
    1875                 (Modifier.PUBLIC | Modifier.FINAL |
    1876                  Modifier.INTERFACE | Modifier.ABSTRACT);
    1877
    1878             /*
    1879              * compensate for javac bug in which ABSTRACT bit was set for an
    1880              * interface only if the interface declared methods
    1881              */
    1882             Method[] methods = cl.getDeclaredMethods();
    1883             if ((classMods & Modifier.INTERFACE) != 0) {
    1884                 classMods = (methods.length > 0) ?
    1885                     (classMods | Modifier.ABSTRACT) :
    1886                     (classMods & ~Modifier.ABSTRACT);
    1887             }
    1888             dout.writeInt(classMods);
    1889
    1890             if (!cl.isArray()) {
    1891                 /*
    1892                  * compensate for change in 1.2FCS in which
    1893                  * Class.getInterfaces() was modified to return Cloneable and
    1894                  * Serializable for array classes.
    1895                  */
    1896                 Class<?>[] interfaces = cl.getInterfaces();
    1897                 String[] ifaceNames = new String[interfaces.length];
    1898                 for (int i = 0; i < interfaces.length; i++) {
    1899                     ifaceNames[i] = interfaces[i].getName();
    1900                 }
    1901                 Arrays.sort(ifaceNames);
    1902                 for (int i = 0; i < ifaceNames.length; i++) {
    1903                     dout.writeUTF(ifaceNames[i]);
    1904                 }
    1905             }
    1906
    1907             Field[] fields = cl.getDeclaredFields();
    1908             MemberSignature[] fieldSigs = new MemberSignature[fields.length];
    1909             for (int i = 0; i < fields.length; i++) {
    1910                 fieldSigs[i] = new MemberSignature(fields[i]);
    1911             }
    1912             Arrays.sort(fieldSigs, new Comparator<>() {
    1913                 public int compare(MemberSignature ms1, MemberSignature ms2) {
    1914                     return ms1.name.compareTo(ms2.name);
    1915                 }
    1916             });
    1917             for (int i = 0; i < fieldSigs.length; i++) {
    1918                 MemberSignature sig = fieldSigs[i];
    1919                 int mods = sig.member.getModifiers() &
    1920                     (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
    1921                      Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
    1922                      Modifier.TRANSIENT);
    1923                 if (((mods & Modifier.PRIVATE) == 0) ||
    1924                     ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
    1925                 {
    1926                     dout.writeUTF(sig.name);
    1927                     dout.writeInt(mods);
    1928                     dout.writeUTF(sig.signature);
    1929                 }
    1930             }
    1931
    1932             if (hasStaticInitializer(cl)) {
    1933                 dout.writeUTF("<clinit>");
    1934                 dout.writeInt(Modifier.STATIC);
    1935                 dout.writeUTF("()V");
    1936             }
    1937
    1938             Constructor<?>[] cons = cl.getDeclaredConstructors();
    1939             MemberSignature[] consSigs = new MemberSignature[cons.length];
    1940             for (int i = 0; i < cons.length; i++) {
    1941                 consSigs[i] = new MemberSignature(cons[i]);
    1942             }
    1943             Arrays.sort(consSigs, new Comparator<>() {
    1944                 public int compare(MemberSignature ms1, MemberSignature ms2) {
    1945                     return ms1.signature.compareTo(ms2.signature);
    1946                 }
    1947             });
    1948             for (int i = 0; i < consSigs.length; i++) {
    1949                 MemberSignature sig = consSigs[i];
    1950                 int mods = sig.member.getModifiers() &
    1951                     (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
    1952                      Modifier.STATIC | Modifier.FINAL |
    1953                      Modifier.SYNCHRONIZED | Modifier.NATIVE |
    1954                      Modifier.ABSTRACT | Modifier.STRICT);
    1955                 if ((mods & Modifier.PRIVATE) == 0) {
    1956                     dout.writeUTF("<init>");
    1957                     dout.writeInt(mods);
    1958                     dout.writeUTF(sig.signature.replace('/', '.'));
    1959                 }
    1960             }
    1961
    1962             MemberSignature[] methSigs = new MemberSignature[methods.length];
    1963             for (int i = 0; i < methods.length; i++) {
    1964                 methSigs[i] = new MemberSignature(methods[i]);
    1965             }
    1966             Arrays.sort(methSigs, new Comparator<>() {
    1967                 public int compare(MemberSignature ms1, MemberSignature ms2) {
    1968                     int comp = ms1.name.compareTo(ms2.name);
    1969                     if (comp == 0) {
    1970                         comp = ms1.signature.compareTo(ms2.signature);
    1971                     }
    1972                     return comp;
    1973                 }
    1974             });
    1975             for (int i = 0; i < methSigs.length; i++) {
    1976                 MemberSignature sig = methSigs[i];
    1977                 int mods = sig.member.getModifiers() &
    1978                     (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
    1979                      Modifier.STATIC | Modifier.FINAL |
    1980                      Modifier.SYNCHRONIZED | Modifier.NATIVE |
    1981                      Modifier.ABSTRACT | Modifier.STRICT);
    1982                 if ((mods & Modifier.PRIVATE) == 0) {
    1983                     dout.writeUTF(sig.name);
    1984                     dout.writeInt(mods);
    1985                     dout.writeUTF(sig.signature.replace('/', '.'));
    1986                 }
    1987             }
    1988
    1989             dout.flush();
    1990
    1991             MessageDigest md = MessageDigest.getInstance("SHA");
    1992             byte[] hashBytes = md.digest(bout.toByteArray());
    1993             long hash = 0;
    1994             for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
    1995                 hash = (hash << 8) | (hashBytes[i] & 0xFF);
    1996             }
    1997             return hash;
    1998         } catch (IOException ex) {
    1999             throw new InternalError(ex);
    2000         } catch (NoSuchAlgorithmException ex) {
    2001             throw new SecurityException(ex.getMessage());
    2002         }
    2003     }
    2004
    2005     /**
    2006      * Returns true if the given class defines a static initializer method,
    2007      * false otherwise.
    2008      */
    2009     private static native boolean hasStaticInitializer(Class<?> cl);
    2010
    2011     /**
    2012      * Class for computing and caching field/constructor/method signatures
    2013      * during serialVersionUID calculation.
    2014      */
    2015     private static class MemberSignature {
    2016
    2017         public final Member member;
    2018         public final String name;
    2019         public final String signature;
    2020
    2021         public MemberSignature(Field field) {
    2022             member = field;
    2023             name = field.getName();
    2024             signature = getClassSignature(field.getType());
    2025         }
    2026
    2027         public MemberSignature(Constructor<?> cons) {
    2028             member = cons;
    2029             name = cons.getName();
    2030             signature = getMethodSignature(
    2031                 cons.getParameterTypes(), Void.TYPE);
    2032         }
    2033
    2034         public MemberSignature(Method meth) {
    2035             member = meth;
    2036             name = meth.getName();
    2037             signature = getMethodSignature(
    2038                 meth.getParameterTypes(), meth.getReturnType());
    2039         }
    2040     }
    2041
    2042     /**
    2043      * Class for setting and retrieving serializable field values in batch.
    2044      */
    2045     // REMIND: dynamically generate these?
    2046     private static class FieldReflector {
    2047
    2048         /** handle for performing unsafe operations */
    2049         private static final Unsafe unsafe = Unsafe.getUnsafe();
    2050
    2051         /** fields to operate on */
    2052         private final ObjectStreamField[] fields;
    2053         /** number of primitive fields */
    2054         private final int numPrimFields;
    2055         /** unsafe field keys for reading fields - may contain dupes */
    2056         private final long[] readKeys;
    2057         /** unsafe fields keys for writing fields - no dupes */
    2058         private final long[] writeKeys;
    2059         /** field data offsets */
    2060         private final int[] offsets;
    2061         /** field type codes */
    2062         private final char[] typeCodes;
    2063         /** field types */
    2064         private final Class<?>[] types;
    2065
    2066         /**
    2067          * Constructs FieldReflector capable of setting/getting values from the
    2068          * subset of fields whose ObjectStreamFields contain non-null
    2069          * reflective Field objects.  ObjectStreamFields with null Fields are
    2070          * treated as filler, for which get operations return default values
    2071          * and set operations discard given values.
    2072          */
    2073         FieldReflector(ObjectStreamField[] fields) {
    2074             this.fields = fields;
    2075             int nfields = fields.length;
    2076             readKeys = new long[nfields];
    2077             writeKeys = new long[nfields];
    2078             offsets = new int[nfields];
    2079             typeCodes = new char[nfields];
    2080             ArrayList<Class<?>> typeList = new ArrayList<>();
    2081             Set<Long> usedKeys = new HashSet<>();
    2082
    2083
    2084             for (int i = 0; i < nfields; i++) {
    2085                 ObjectStreamField f = fields[i];
    2086                 Field rf = f.getField();
    2087                 long key = (rf != null) ?
    2088                     unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
    2089                 readKeys[i] = key;
    2090                 writeKeys[i] = usedKeys.add(key) ?
    2091                     key : Unsafe.INVALID_FIELD_OFFSET;
    2092                 offsets[i] = f.getOffset();
    2093                 typeCodes[i] = f.getTypeCode();
    2094                 if (!f.isPrimitive()) {
    2095                     typeList.add((rf != null) ? rf.getType() : null);
    2096                 }
    2097             }
    2098
    2099             types = typeList.toArray(new Class<?>[typeList.size()]);
    2100             numPrimFields = nfields - types.length;
    2101         }
    2102
    2103         /**
    2104          * Returns list of ObjectStreamFields representing fields operated on
    2105          * by this reflector.  The shared/unshared values and Field objects
    2106          * contained by ObjectStreamFields in the list reflect their bindings
    2107          * to locally defined serializable fields.
    2108          */
    2109         ObjectStreamField[] getFields() {
    2110             return fields;
    2111         }
    2112
    2113         /**
    2114          * Fetches the serializable primitive field values of object obj and
    2115          * marshals them into byte array buf starting at offset 0.  The caller
    2116          * is responsible for ensuring that obj is of the proper type.
    2117          */
    2118         void getPrimFieldValues(Object obj, byte[] buf) {
    2119             if (obj == null) {
    2120                 throw new NullPointerException();
    2121             }
    2122             /* assuming checkDefaultSerialize() has been called on the class
    2123              * descriptor this FieldReflector was obtained from, no field keys
    2124              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
    2125              */
    2126             for (int i = 0; i < numPrimFields; i++) {
    2127                 long key = readKeys[i];
    2128                 int off = offsets[i];
    2129                 switch (typeCodes[i]) {
    2130                     case 'Z':
    2131                         Bits.putBoolean(buf, off, unsafe.getBoolean(obj, key));
    2132                         break;
    2133
    2134                     case 'B':
    2135                         buf[off] = unsafe.getByte(obj, key);
    2136                         break;
    2137
    2138                     case 'C':
    2139                         Bits.putChar(buf, off, unsafe.getChar(obj, key));
    2140                         break;
    2141
    2142                     case 'S':
    2143                         Bits.putShort(buf, off, unsafe.getShort(obj, key));
    2144                         break;
    2145
    2146                     case 'I':
    2147                         Bits.putInt(buf, off, unsafe.getInt(obj, key));
    2148                         break;
    2149
    2150                     case 'F':
    2151                         Bits.putFloat(buf, off, unsafe.getFloat(obj, key));
    2152                         break;
    2153
    2154                     case 'J':
    2155                         Bits.putLong(buf, off, unsafe.getLong(obj, key));
    2156                         break;
    2157
    2158                     case 'D':
    2159                         Bits.putDouble(buf, off, unsafe.getDouble(obj, key));
    2160                         break;
    2161
    2162                     default:
    2163                         throw new InternalError();
    2164                 }
    2165             }
    2166         }
    2167
    2168         /**
    2169          * Sets the serializable primitive fields of object obj using values
    2170          * unmarshalled from byte array buf starting at offset 0.  The caller
    2171          * is responsible for ensuring that obj is of the proper type.
    2172          */
    2173         void setPrimFieldValues(Object obj, byte[] buf) {
    2174             if (obj == null) {
    2175                 throw new NullPointerException();
    2176             }
    2177             for (int i = 0; i < numPrimFields; i++) {
    2178                 long key = writeKeys[i];
    2179                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
    2180                     continue;           // discard value
    2181                 }
    2182                 int off = offsets[i];
    2183                 switch (typeCodes[i]) {
    2184                     case 'Z':
    2185                         unsafe.putBoolean(obj, key, Bits.getBoolean(buf, off));
    2186                         break;
    2187
    2188                     case 'B':
    2189                         unsafe.putByte(obj, key, buf[off]);
    2190                         break;
    2191
    2192                     case 'C':
    2193                         unsafe.putChar(obj, key, Bits.getChar(buf, off));
    2194                         break;
    2195
    2196                     case 'S':
    2197                         unsafe.putShort(obj, key, Bits.getShort(buf, off));
    2198                         break;
    2199
    2200                     case 'I':
    2201                         unsafe.putInt(obj, key, Bits.getInt(buf, off));
    2202                         break;
    2203
    2204                     case 'F':
    2205                         unsafe.putFloat(obj, key, Bits.getFloat(buf, off));
    2206                         break;
    2207
    2208                     case 'J':
    2209                         unsafe.putLong(obj, key, Bits.getLong(buf, off));
    2210                         break;
    2211
    2212                     case 'D':
    2213                         unsafe.putDouble(obj, key, Bits.getDouble(buf, off));
    2214                         break;
    2215
    2216                     default:
    2217                         throw new InternalError();
    2218                 }
    2219             }
    2220         }
    2221
    2222         /**
    2223          * Fetches the serializable object field values of object obj and
    2224          * stores them in array vals starting at offset 0.  The caller is
    2225          * responsible for ensuring that obj is of the proper type.
    2226          */
    2227         void getObjFieldValues(Object obj, Object[] vals) {
    2228             if (obj == null) {
    2229                 throw new NullPointerException();
    2230             }
    2231             /* assuming checkDefaultSerialize() has been called on the class
    2232              * descriptor this FieldReflector was obtained from, no field keys
    2233              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
    2234              */
    2235             for (int i = numPrimFields; i < fields.length; i++) {
    2236                 switch (typeCodes[i]) {
    2237                     case 'L':
    2238                     case '[':
    2239                         vals[offsets[i]] = unsafe.getReference(obj, readKeys[i]);
    2240                         break;
    2241
    2242                     default:
    2243                         throw new InternalError();
    2244                 }
    2245             }
    2246         }
    2247
    2248         /**
    2249          * Checks that the given values, from array vals starting at offset 0,
    2250          * are assignable to the given serializable object fields.
    2251          * @throws ClassCastException if any value is not assignable
    2252          */
    2253         void checkObjectFieldValueTypes(Object obj, Object[] vals) {
    2254             setObjFieldValues(obj, vals, true);
    2255         }
    2256
    2257         /**
    2258          * Sets the serializable object fields of object obj using values from
    2259          * array vals starting at offset 0.  The caller is responsible for
    2260          * ensuring that obj is of the proper type; however, attempts to set a
    2261          * field with a value of the wrong type will trigger an appropriate
    2262          * ClassCastException.
    2263          */
    2264         void setObjFieldValues(Object obj, Object[] vals) {
    2265             setObjFieldValues(obj, vals, false);
    2266         }
    2267
    2268         private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
    2269             if (obj == null) {
    2270                 throw new NullPointerException();
    2271             }
    2272             for (int i = numPrimFields; i < fields.length; i++) {
    2273                 long key = writeKeys[i];
    2274                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
    2275                     continue;           // discard value
    2276                 }
    2277                 switch (typeCodes[i]) {
    2278                     case 'L':
    2279                     case '[':
    2280                         Object val = vals[offsets[i]];
    2281                         if (val != null &&
    2282                             !types[i - numPrimFields].isInstance(val))
    2283                         {
    2284                             Field f = fields[i].getField();
    2285                             throw new ClassCastException(
    2286                                 "cannot assign instance of " +
    2287                                 val.getClass().getName() + " to field " +
    2288                                 f.getDeclaringClass().getName() + "." +
    2289                                 f.getName() + " of type " +
    2290                                 f.getType().getName() + " in instance of " +
    2291                                 obj.getClass().getName());
    2292                         }
    2293                         if (!dryRun)
    2294                             unsafe.putReference(obj, key, val);
    2295                         break;
    2296
    2297                     default:
    2298                         throw new InternalError();
    2299                 }
    2300             }
    2301         }
    2302     }
    2303
    2304     /**
    2305      * Matches given set of serializable fields with serializable fields
    2306      * described by the given local class descriptor, and returns a
    2307      * FieldReflector instance capable of setting/getting values from the
    2308      * subset of fields that match (non-matching fields are treated as filler,
    2309      * for which get operations return default values and set operations
    2310      * discard given values).  Throws InvalidClassException if unresolvable
    2311      * type conflicts exist between the two sets of fields.
    2312      */
    2313     private static FieldReflector getReflector(ObjectStreamField[] fields,
    2314                                                ObjectStreamClass localDesc)
    2315         throws InvalidClassException
    2316     {
    2317         // class irrelevant if no fields
    2318         Class<?> cl = (localDesc != null && fields.length > 0) ?
    2319             localDesc.cl : null;
    2320         processQueue(Caches.reflectorsQueue, Caches.reflectors);
    2321         FieldReflectorKey key = new FieldReflectorKey(cl, fields,
    2322                                                       Caches.reflectorsQueue);
    2323         Reference<?> ref = Caches.reflectors.get(key);
    2324         Object entry = null;
    2325         if (ref != null) {
    2326             entry = ref.get();
    2327         }
    2328         EntryFuture future = null;
    2329         if (entry == null) {
    2330             EntryFuture newEntry = new EntryFuture();
    2331             Reference<?> newRef = new SoftReference<>(newEntry);
    2332             do {
    2333                 if (ref != null) {
    2334                     Caches.reflectors.remove(key, ref);
    2335                 }
    2336                 ref = Caches.reflectors.putIfAbsent(key, newRef);
    2337                 if (ref != null) {
    2338                     entry = ref.get();
    2339                 }
    2340             } while (ref != null && entry == null);
    2341             if (entry == null) {
    2342                 future = newEntry;
    2343             }
    2344         }
    2345
    2346         if (entry instanceof FieldReflector) {  // check common case first
    2347             return (FieldReflector) entry;
    2348         } else if (entry instanceof EntryFuture) {
    2349             entry = ((EntryFuture) entry).get();
    2350         } else if (entry == null) {
    2351             try {
    2352                 entry = new FieldReflector(matchFields(fields, localDesc));
    2353             } catch (Throwable th) {
    2354                 entry = th;
    2355             }
    2356             future.set(entry);
    2357             Caches.reflectors.put(key, new SoftReference<>(entry));
    2358         }
    2359
    2360         if (entry instanceof FieldReflector) {
    2361             return (FieldReflector) entry;
    2362         } else if (entry instanceof InvalidClassException) {
    2363             throw (InvalidClassException) entry;
    2364         } else if (entry instanceof RuntimeException) {
    2365             throw (RuntimeException) entry;
    2366         } else if (entry instanceof Error) {
    2367             throw (Error) entry;
    2368         } else {
    2369             throw new InternalError("unexpected entry: " + entry);
    2370         }
    2371     }
    2372
    2373     /**
    2374      * FieldReflector cache lookup key.  Keys are considered equal if they
    2375      * refer to the same class and equivalent field formats.
    2376      */
    2377     private static class FieldReflectorKey extends WeakReference<Class<?>> {
    2378
    2379         private final String[] sigs;
    2380         private final int hash;
    2381         private final boolean nullClass;
    2382
    2383         FieldReflectorKey(Class<?> cl, ObjectStreamField[] fields,
    2384                           ReferenceQueue<Class<?>> queue)
    2385         {
    2386             super(cl, queue);
    2387             nullClass = (cl == null);
    2388             sigs = new String[2 * fields.length];
    2389             for (int i = 0, j = 0; i < fields.length; i++) {
    2390                 ObjectStreamField f = fields[i];
    2391                 sigs[j++] = f.getName();
    2392                 sigs[j++] = f.getSignature();
    2393             }
    2394             hash = System.identityHashCode(cl) + Arrays.hashCode(sigs);
    2395         }
    2396
    2397         public int hashCode() {
    2398             return hash;
    2399         }
    2400
    2401         public boolean equals(Object obj) {
    2402             if (obj == this) {
    2403                 return true;
    2404             }
    2405
    2406             if (obj instanceof FieldReflectorKey) {
    2407                 FieldReflectorKey other = (FieldReflectorKey) obj;
    2408                 Class<?> referent;
    2409                 return (nullClass ? other.nullClass
    2410                                   : ((referent = get()) != null) &&
    2411                                     (referent == other.get())) &&
    2412                         Arrays.equals(sigs, other.sigs);
    2413             } else {
    2414                 return false;
    2415             }
    2416         }
    2417     }
    2418
    2419     /**
    2420      * Matches given set of serializable fields with serializable fields
    2421      * obtained from the given local class descriptor (which contain bindings
    2422      * to reflective Field objects).  Returns list of ObjectStreamFields in
    2423      * which each ObjectStreamField whose signature matches that of a local
    2424      * field contains a Field object for that field; unmatched
    2425      * ObjectStreamFields contain null Field objects.  Shared/unshared settings
    2426      * of the returned ObjectStreamFields also reflect those of matched local
    2427      * ObjectStreamFields.  Throws InvalidClassException if unresolvable type
    2428      * conflicts exist between the two sets of fields.
    2429      */
    2430     private static ObjectStreamField[] matchFields(ObjectStreamField[] fields,
    2431                                                    ObjectStreamClass localDesc)
    2432         throws InvalidClassException
    2433     {
    2434         ObjectStreamField[] localFields = (localDesc != null) ?
    2435             localDesc.fields : NO_FIELDS;
    2436
    2437         /*
    2438          * Even if fields == localFields, we cannot simply return localFields
    2439          * here.  In previous implementations of serialization,
    2440          * ObjectStreamField.getType() returned Object.class if the
    2441          * ObjectStreamField represented a non-primitive field and belonged to
    2442          * a non-local class descriptor.  To preserve this (questionable)
    2443          * behavior, the ObjectStreamField instances returned by matchFields
    2444          * cannot report non-primitive types other than Object.class; hence
    2445          * localFields cannot be returned directly.
    2446          */
    2447
    2448         ObjectStreamField[] matches = new ObjectStreamField[fields.length];
    2449         for (int i = 0; i < fields.length; i++) {
    2450             ObjectStreamField f = fields[i], m = null;
    2451             for (int j = 0; j < localFields.length; j++) {
    2452                 ObjectStreamField lf = localFields[j];
    2453                 if (f.getName().equals(lf.getName())) {
    2454                     if ((f.isPrimitive() || lf.isPrimitive()) &&
    2455                         f.getTypeCode() != lf.getTypeCode())
    2456                     {
    2457                         throw new InvalidClassException(localDesc.name,
    2458                             "incompatible types for field " + f.getName());
    2459                     }
    2460                     if (lf.getField() != null) {
    2461                         m = new ObjectStreamField(
    2462                             lf.getField(), lf.isUnshared(), false);
    2463                     } else {
    2464                         m = new ObjectStreamField(
    2465                             lf.getName(), lf.getSignature(), lf.isUnshared());
    2466                     }
    2467                 }
    2468             }
    2469             if (m == null) {
    2470                 m = new ObjectStreamField(
    2471                     f.getName(), f.getSignature(), false);
    2472             }
    2473             m.setOffset(f.getOffset());
    2474             matches[i] = m;
    2475         }
    2476         return matches;
    2477     }
    2478
    2479     /**
    2480      * Removes from the specified map any keys that have been enqueued
    2481      * on the specified reference queue.
    2482      */
    2483     static void processQueue(ReferenceQueue<Class<?>> queue,
    2484                              ConcurrentMap<? extends
    2485                              WeakReference<Class<?>>, ?> map)
    2486     {
    2487         Reference<? extends Class<?>> ref;
    2488         while((ref = queue.poll()) != null) {
    2489             map.remove(ref);
    2490         }
    2491     }
    2492
    2493     /**
    2494      *  Weak key for Class objects.
    2495      *
    2496      **/
    2497     static class WeakClassKey extends WeakReference<Class<?>> {
    2498         /**
    2499          * saved value of the referent's identity hash code, to maintain
    2500          * a consistent hash code after the referent has been cleared
    2501          */
    2502         private final int hash;
    2503
    2504         /**
    2505          * Create a new WeakClassKey to the given object, registered
    2506          * with a queue.
    2507          */
    2508         WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
    2509             super(cl, refQueue);
    2510             hash = System.identityHashCode(cl);
    2511         }
    2512
    2513         /**
    2514          * Returns the identity hash code of the original referent.
    2515          */
    2516         public int hashCode() {
    2517             return hash;
    2518         }
    2519
    2520         /**
    2521          * Returns true if the given object is this identical
    2522          * WeakClassKey instance, or, if this object's referent has not
    2523          * been cleared, if the given object is another WeakClassKey
    2524          * instance with the identical non-null referent as this one.
    2525          */
    2526         public boolean equals(Object obj) {
    2527             if (obj == this) {
    2528                 return true;
    2529             }
    2530
    2531             if (obj instanceof WeakClassKey) {
    2532                 Object referent = get();
    2533                 return (referent != null) &&
    2534                        (referent == ((WeakClassKey) obj).get());
    2535             } else {
    2536                 return false;
    2537             }
    2538         }
    2539     }
    2540
    2541     // a LRA cache of record deserialization constructors

    2542     @SuppressWarnings("serial")
    2543     private static final class DeserializationConstructorsCache
    2544         extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle>  {
    2545
    2546         // keep max. 10 cached entries - when the 11th element is inserted the oldest
    2547         // is removed and 10 remains - 11 is the biggest map size where internal
    2548         // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
    2549         private static final int MAX_SIZE = 10;
    2550         private Key.Impl first, last; // first and last in FIFO queue
    2551
    2552         DeserializationConstructorsCache() {
    2553             // start small - if there is more than one shape of ObjectStreamClass
    2554             // deserialized, there will typically be two (current version and previous version)
    2555             super(2);
    2556         }
    2557
    2558         MethodHandle get(ObjectStreamField[] fields) {
    2559             return get(new Key.Lookup(fields));
    2560         }
    2561
    2562         synchronized MethodHandle putIfAbsentAndGet(ObjectStreamField[] fields, MethodHandle mh) {
    2563             Key.Impl key = new Key.Impl(fields);
    2564             var oldMh = putIfAbsent(key, mh);
    2565             if (oldMh != null) return oldMh;
    2566             // else we did insert new entry -> link the new key as last
    2567             if (last == null) {
    2568                 last = first = key;
    2569             } else {
    2570                 last = (last.next = key);
    2571             }
    2572             // may need to remove first
    2573             if (size() > MAX_SIZE) {
    2574                 assert first != null;
    2575                 remove(first);
    2576                 first = first.next;
    2577                 if (first == null) {
    2578                     last = null;
    2579                 }
    2580             }
    2581             return mh;
    2582         }
    2583
    2584         // a key composed of ObjectStreamField[] names and types
    2585         static abstract class Key {
    2586             abstract int length();
    2587             abstract String fieldName(int i);
    2588             abstract Class<?> fieldType(int i);
    2589
    2590             @Override
    2591             public final int hashCode() {
    2592                 int n = length();
    2593                 int h = 0;
    2594                 for (int i = 0; i < n; i++) h = h * 31 + fieldType(i).hashCode();
    2595                 for (int i = 0; i < n; i++) h = h * 31 + fieldName(i).hashCode();
    2596                 return h;
    2597             }
    2598
    2599             @Override
    2600             public final boolean equals(Object obj) {
    2601                 if (!(obj instanceof Key)) return false;
    2602                 Key other = (Key) obj;
    2603                 int n = length();
    2604                 if (n != other.length()) return false;
    2605                 for (int i = 0; i < n; i++) if (fieldType(i) != other.fieldType(i)) return false;
    2606                 for (int i = 0; i < n; i++) if (!fieldName(i).equals(other.fieldName(i))) return false;
    2607                 return true;
    2608             }
    2609
    2610             // lookup key - just wraps ObjectStreamField[]
    2611             static final class Lookup extends Key {
    2612                 final ObjectStreamField[] fields;
    2613
    2614                 Lookup(ObjectStreamField[] fields) { this.fields = fields; }
    2615
    2616                 @Override
    2617                 int length() { return fields.length; }
    2618
    2619                 @Override
    2620                 String fieldName(int i) { return fields[i].getName(); }
    2621
    2622                 @Override
    2623                 Class<?> fieldType(int i) { return fields[i].getType(); }
    2624             }
    2625
    2626             // real key - copies field names and types and forms FIFO queue in cache
    2627             static final class Impl extends Key {
    2628                 Impl next;
    2629                 final String[] fieldNames;
    2630                 final Class<?>[] fieldTypes;
    2631
    2632                 Impl(ObjectStreamField[] fields) {
    2633                     this.fieldNames = new String[fields.length];
    2634                     this.fieldTypes = new Class<?>[fields.length];
    2635                     for (int i = 0; i < fields.length; i++) {
    2636                         fieldNames[i] = fields[i].getName();
    2637                         fieldTypes[i] = fields[i].getType();
    2638                     }
    2639                 }
    2640
    2641                 @Override
    2642                 int length() { return fieldNames.length; }
    2643
    2644                 @Override
    2645                 String fieldName(int i) { return fieldNames[i]; }
    2646
    2647                 @Override
    2648                 Class<?> fieldType(int i) { return fieldTypes[i]; }
    2649             }
    2650         }
    2651     }
    2652
    2653     /** Record specific support for retrieving and binding stream field values. */
    2654     static final class RecordSupport {
    2655         /**
    2656          * Returns canonical record constructor adapted to take two arguments:
    2657          * {@code (byte[] primValues, Object[] objValues)}
    2658          * and return
    2659          * {@code Object}
    2660          */
    2661         @SuppressWarnings("preview")
    2662         static MethodHandle deserializationCtr(ObjectStreamClass desc) {
    2663             // check the cached value 1st
    2664             MethodHandle mh = desc.deserializationCtr;
    2665             if (mh != null) return mh;
    2666             mh = desc.deserializationCtrs.get(desc.getFields(false));
    2667             if (mh != null) return desc.deserializationCtr = mh;
    2668
    2669             // retrieve record components
    2670             RecordComponent[] recordComponents;
    2671             try {
    2672                 Class<?> cls = desc.forClass();
    2673                 PrivilegedExceptionAction<RecordComponent[]> pa = cls::getRecordComponents;
    2674                 recordComponents = AccessController.doPrivileged(pa);
    2675             } catch (PrivilegedActionException e) {
    2676                 throw new InternalError(e.getCause());
    2677             }
    2678
    2679             // retrieve the canonical constructor
    2680             // (T1, T2, ..., Tn):TR
    2681             mh = desc.getRecordConstructor();
    2682
    2683             // change return type to Object
    2684             // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
    2685             mh = mh.asType(mh.type().changeReturnType(Object.class));
    2686
    2687             // drop last 2 arguments representing primValues and objValues arrays
    2688             // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
    2689             mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
    2690
    2691             for (int i = recordComponents.length-1; i >= 0; i--) {
    2692                 String name = recordComponents[i].getName();
    2693                 Class<?> type = recordComponents[i].getType();
    2694                 // obtain stream field extractor that extracts argument at
    2695                 // position i (Ti+1) from primValues and objValues arrays
    2696                 // (byte[], Object[]):Ti+1
    2697                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
    2698                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
    2699                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
    2700                 mh = MethodHandles.foldArguments(mh, i, combiner);
    2701             }
    2702             // what we are left with is a MethodHandle taking just the primValues
    2703             // and objValues arrays and returning the constructed record instance
    2704             // (byte[], Object[]):Object
    2705
    2706             // store it into cache and return the 1st value stored
    2707             return desc.deserializationCtr =
    2708                 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
    2709         }
    2710
    2711         /** Returns the number of primitive fields for the given descriptor. */
    2712         private static int numberPrimValues(ObjectStreamClass desc) {
    2713             ObjectStreamField[] fields = desc.getFields();
    2714             int primValueCount = 0;
    2715             for (int i = 0; i < fields.length; i++) {
    2716                 if (fields[i].isPrimitive())
    2717                     primValueCount++;
    2718                 else
    2719                     break;  // can be no more
    2720             }
    2721             return primValueCount;
    2722         }
    2723
    2724         /**
    2725          * Returns extractor MethodHandle taking the primValues and objValues arrays
    2726          * and extracting the argument of canonical constructor with given name and type
    2727          * or producing  default value for the given type if the field is absent.
    2728          */
    2729         private static MethodHandle streamFieldExtractor(String pName,
    2730                                                          Class<?> pType,
    2731                                                          ObjectStreamClass desc) {
    2732             ObjectStreamField[] fields = desc.getFields(false);
    2733
    2734             for (int i = 0; i < fields.length; i++) {
    2735                 ObjectStreamField f = fields[i];
    2736                 String fName = f.getName();
    2737                 if (!fName.equals(pName))
    2738                     continue;
    2739
    2740                 Class<?> fType = f.getField().getType();
    2741                 if (!pType.isAssignableFrom(fType))
    2742                     throw new InternalError(fName + " unassignable, pType:" + pType + ", fType:" + fType);
    2743
    2744                 if (f.isPrimitive()) {
    2745                     // (byte[], int):fType
    2746                     MethodHandle mh = PRIM_VALUE_EXTRACTORS.get(fType);
    2747                     if (mh == null) {
    2748                         throw new InternalError("Unexpected type: " + fType);
    2749                     }
    2750                     // bind offset
    2751                     // (byte[], int):fType -> (byte[]):fType
    2752                     mh = MethodHandles.insertArguments(mh, 1, f.getOffset());
    2753                     // drop objValues argument
    2754                     // (byte[]):fType -> (byte[], Object[]):fType
    2755                     mh = MethodHandles.dropArguments(mh, 1, Object[].class);
    2756                     // adapt return type to pType
    2757                     // (byte[], Object[]):fType -> (byte[], Object[]):pType
    2758                     if (pType != fType) {
    2759                         mh = mh.asType(mh.type().changeReturnType(pType));
    2760                     }
    2761                     return mh;
    2762                 } else { // reference
    2763                     // (Object[], int):Object
    2764                     MethodHandle mh = MethodHandles.arrayElementGetter(Object[].class);
    2765                     // bind index
    2766                     // (Object[], int):Object -> (Object[]):Object
    2767                     mh = MethodHandles.insertArguments(mh, 1, i - numberPrimValues(desc));
    2768                     // drop primValues argument
    2769                     // (Object[]):Object -> (byte[], Object[]):Object
    2770                     mh = MethodHandles.dropArguments(mh, 0, byte[].class);
    2771                     // adapt return type to pType
    2772                     // (byte[], Object[]):Object -> (byte[], Object[]):pType
    2773                     if (pType != Object.class) {
    2774                         mh = mh.asType(mh.type().changeReturnType(pType));
    2775                     }
    2776                     return mh;
    2777                 }
    2778             }
    2779
    2780             // return default value extractor if no field matches pName
    2781             return MethodHandles.empty(MethodType.methodType(pType, byte[].class, Object[].class));
    2782         }
    2783
    2784         private static final Map<Class<?>, MethodHandle> PRIM_VALUE_EXTRACTORS;
    2785         static {
    2786             var lkp = MethodHandles.lookup();
    2787             try {
    2788                 PRIM_VALUE_EXTRACTORS = Map.of(
    2789                     byte.class, MethodHandles.arrayElementGetter(byte[].class),
    2790                     short.class, lkp.findStatic(Bits.class, "getShort", MethodType.methodType(short.class, byte[].class, int.class)),
    2791                     int.class, lkp.findStatic(Bits.class, "getInt", MethodType.methodType(int.class, byte[].class, int.class)),
    2792                     long.class, lkp.findStatic(Bits.class, "getLong", MethodType.methodType(long.class, byte[].class, int.class)),
    2793                     float.class, lkp.findStatic(Bits.class, "getFloat", MethodType.methodType(float.class, byte[].class, int.class)),
    2794                     double.class, lkp.findStatic(Bits.class, "getDouble", MethodType.methodType(double.class, byte[].class, int.class)),
    2795                     char.class, lkp.findStatic(Bits.class, "getChar", MethodType.methodType(char.class, byte[].class, int.class)),
    2796                     boolean.class, lkp.findStatic(Bits.class, "getBoolean", MethodType.methodType(boolean.class, byte[].class, int.class))
    2797                 );
    2798             } catch (NoSuchMethodException | IllegalAccessException e) {
    2799                 throw new InternalError("Can't lookup Bits.getXXX", e);
    2800             }
    2801         }
    2802     }
    2803 }
    --- EOF ---