1 /*
2  * Copyright (c) 1996, 2013, 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.Member;
39 import java.lang.reflect.Method;
40 import java.lang.reflect.Modifier;
41 import java.lang.reflect.Proxy;
42 import java.lang.reflect.RecordComponent;
43 import java.security.AccessController;
44 import java.security.MessageDigest;
45 import java.security.NoSuchAlgorithmException;
46 import java.security.PrivilegedAction;
47 import java.security.PrivilegedActionException;
48 import java.security.PrivilegedExceptionAction;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Collections;
52 import java.util.Comparator;
53 import java.util.HashSet;
54 import java.util.Map;
55 import java.util.Set;
56 import java.util.concurrent.ConcurrentHashMap;
57 import java.util.concurrent.ConcurrentMap;
58 import sun.misc.Unsafe;
59 import sun.reflect.CallerSensitive;
60 import sun.reflect.Reflection;
61 import sun.reflect.misc.ReflectUtil;
62 import dalvik.system.VMRuntime;
63 
64 /**
65  * Serialization's descriptor for classes.  It contains the name and
66  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
67  * loaded in this Java VM can be found/created using the lookup method.
68  *
69  * <p>The algorithm to compute the SerialVersionUID is described in
70  * <a href="../../../platform/serialization/spec/class.html#4100">Object
71  * Serialization Specification, Section 4.6, Stream Unique Identifiers</a>.
72  *
73  * @author      Mike Warres
74  * @author      Roger Riggs
75  * @see ObjectStreamField
76  * @see <a href="../../../platform/serialization/spec/class.html">Object Serialization Specification, Section 4, Class Descriptors</a>
77  * @since   JDK1.1
78  */
79 public class ObjectStreamClass implements Serializable {
80 
81     /** serialPersistentFields value indicating no serializable fields */
82     public static final ObjectStreamField[] NO_FIELDS =
83         new ObjectStreamField[0];
84 
85     private static final long serialVersionUID = -6120832682080437368L;
86     private static final ObjectStreamField[] serialPersistentFields =
87         NO_FIELDS;
88 
89     // BEGIN Android-removed: ReflectionFactory not used on Android.
90     /*
91     /** reflection factory for obtaining serialization constructors *
92     private static final ReflectionFactory reflFactory =
93         AccessController.doPrivileged(
94             new ReflectionFactory.GetReflectionFactoryAction());
95     */
96     // END Android-removed: ReflectionFactory not used on Android.
97 
98     private static class Caches {
99         /** cache mapping local classes -> descriptors */
100         static final ConcurrentMap<WeakClassKey,Reference<?>> localDescs =
101             new ConcurrentHashMap<>();
102 
103         /** cache mapping field group/local desc pairs -> field reflectors */
104         static final ConcurrentMap<FieldReflectorKey,Reference<?>> reflectors =
105             new ConcurrentHashMap<>();
106 
107         /** queue for WeakReferences to local classes */
108         private static final ReferenceQueue<Class<?>> localDescsQueue =
109             new ReferenceQueue<>();
110         /** queue for WeakReferences to field reflectors keys */
111         private static final ReferenceQueue<Class<?>> reflectorsQueue =
112             new ReferenceQueue<>();
113     }
114 
115     /** class associated with this descriptor (if any) */
116     private Class<?> cl;
117     /** name of class represented by this descriptor */
118     private String name;
119     /** serialVersionUID of represented class (null if not computed yet) */
120     private volatile Long suid;
121 
122     /** true if represents dynamic proxy class */
123     private boolean isProxy;
124     /** true if represents enum type */
125     private boolean isEnum;
126     /** true if represents record type */
127     private boolean isRecord;
128     /** true if represented class implements Serializable */
129     private boolean serializable;
130     /** true if represented class implements Externalizable */
131     private boolean externalizable;
132     /** true if desc has data written by class-defined writeObject method */
133     private boolean hasWriteObjectData;
134     /**
135      * true if desc has externalizable data written in block data format; this
136      * must be true by default to accommodate ObjectInputStream subclasses which
137      * override readClassDescriptor() to return class descriptors obtained from
138      * ObjectStreamClass.lookup() (see 4461737)
139      */
140     private boolean hasBlockExternalData = true;
141 
142     /**
143      * Contains information about InvalidClassException instances to be thrown
144      * when attempting operations on an invalid class. Note that instances of
145      * this class are immutable and are potentially shared among
146      * ObjectStreamClass instances.
147      */
148     private static class ExceptionInfo {
149         private final String className;
150         private final String message;
151 
ExceptionInfo(String cn, String msg)152         ExceptionInfo(String cn, String msg) {
153             className = cn;
154             message = msg;
155         }
156 
157         /**
158          * Returns (does not throw) an InvalidClassException instance created
159          * from the information in this object, suitable for being thrown by
160          * the caller.
161          */
newInvalidClassException()162         InvalidClassException newInvalidClassException() {
163             return new InvalidClassException(className, message);
164         }
165     }
166 
167     /** exception (if any) thrown while attempting to resolve class */
168     private ClassNotFoundException resolveEx;
169     /** exception (if any) to throw if non-enum deserialization attempted */
170     private ExceptionInfo deserializeEx;
171     /** exception (if any) to throw if non-enum serialization attempted */
172     private ExceptionInfo serializeEx;
173     /** exception (if any) to throw if default serialization attempted */
174     private ExceptionInfo defaultSerializeEx;
175 
176     /** serializable fields */
177     private ObjectStreamField[] fields;
178     /** aggregate marshalled size of primitive fields */
179     private int primDataSize;
180     /** number of non-primitive fields */
181     private int numObjFields;
182     /** reflector for setting/getting serializable field values */
183     private FieldReflector fieldRefl;
184     /** data layout of serialized objects described by this class desc */
185     private volatile ClassDataSlot[] dataLayout;
186 
187     /** serialization-appropriate constructor, or null if none */
188     private Constructor<?> cons;
189     /** record canonical constructor (shared among OSCs for same class), or null */
190     private MethodHandle canonicalCtr;
191     /** cache of record deserialization constructors per unique set of stream fields
192      * (shared among OSCs for same class), or null */
193     private DeserializationConstructorsCache deserializationCtrs;
194     /** session-cache of record deserialization constructor
195      * (in de-serialized OSC only), or null */
196     private MethodHandle deserializationCtr;
197     /** class-defined writeObject method, or null if none */
198     private Method writeObjectMethod;
199     /** class-defined readObject method, or null if none */
200     private Method readObjectMethod;
201     /** class-defined readObjectNoData method, or null if none */
202     private Method readObjectNoDataMethod;
203     /** class-defined writeReplace method, or null if none */
204     private Method writeReplaceMethod;
205     /** class-defined readResolve method, or null if none */
206     private Method readResolveMethod;
207 
208     /** local class descriptor for represented class (may point to self) */
209     private ObjectStreamClass localDesc;
210     /** superclass descriptor appearing in stream */
211     private ObjectStreamClass superDesc;
212 
213     /** true if, and only if, the object has been correctly initialized */
214     private boolean initialized;
215 
216     // BEGIN Android-removed: Initialization not required on Android.
217     /*
218     /**
219      * Initializes native code.
220      *
221     private static native void initNative();
222     static {
223         initNative();
224     }
225     */
226     // END Android-removed: Initialization not required on Android.
227 
228     /**
229      * Find the descriptor for a class that can be serialized.  Creates an
230      * ObjectStreamClass instance if one does not exist yet for class. Null is
231      * returned if the specified class does not implement java.io.Serializable
232      * or java.io.Externalizable.
233      *
234      * @param   cl class for which to get the descriptor
235      * @return  the class descriptor for the specified class
236      */
lookup(Class<?> cl)237     public static ObjectStreamClass lookup(Class<?> cl) {
238         return lookup(cl, false);
239     }
240 
241     /**
242      * Returns the descriptor for any class, regardless of whether it
243      * implements {@link Serializable}.
244      *
245      * @param        cl class for which to get the descriptor
246      * @return       the class descriptor for the specified class
247      * @since 1.6
248      */
lookupAny(Class<?> cl)249     public static ObjectStreamClass lookupAny(Class<?> cl) {
250         return lookup(cl, true);
251     }
252 
253     /**
254      * Returns the name of the class described by this descriptor.
255      * This method returns the name of the class in the format that
256      * is used by the {@link Class#getName} method.
257      *
258      * @return a string representing the name of the class
259      */
getName()260     public String getName() {
261         return name;
262     }
263 
264     /**
265      * Return the serialVersionUID for this class.  The serialVersionUID
266      * defines a set of classes all with the same name that have evolved from a
267      * common root class and agree to be serialized and deserialized using a
268      * common format.  NonSerializable classes have a serialVersionUID of 0L.
269      *
270      * @return  the SUID of the class described by this descriptor
271      */
getSerialVersionUID()272     public long getSerialVersionUID() {
273         // REMIND: synchronize instead of relying on volatile?
274         if (suid == null) {
275             if (isRecord)
276                 return 0L;
277 
278             suid = AccessController.doPrivileged(
279                 new PrivilegedAction<Long>() {
280                     public Long run() {
281                         return computeDefaultSUID(cl);
282                     }
283                 }
284             );
285         }
286         return suid.longValue();
287     }
288 
289     /**
290      * Return the class in the local VM that this version is mapped to.  Null
291      * is returned if there is no corresponding local class.
292      *
293      * @return  the <code>Class</code> instance that this descriptor represents
294      */
295     @CallerSensitive
forClass()296     public Class<?> forClass() {
297         if (cl == null) {
298             return null;
299         }
300         requireInitialized();
301         if (System.getSecurityManager() != null) {
302             Class<?> caller = Reflection.getCallerClass();
303             if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), cl.getClassLoader())) {
304                 ReflectUtil.checkPackageAccess(cl);
305             }
306         }
307         return cl;
308     }
309 
310     /**
311      * Return an array of the fields of this serializable class.
312      *
313      * @return  an array containing an element for each persistent field of
314      *          this class. Returns an array of length zero if there are no
315      *          fields.
316      * @since 1.2
317      */
getFields()318     public ObjectStreamField[] getFields() {
319         return getFields(true);
320     }
321 
322     /**
323      * Get the field of this class by name.
324      *
325      * @param   name the name of the data field to look for
326      * @return  The ObjectStreamField object of the named field or null if
327      *          there is no such named field.
328      */
getField(String name)329     public ObjectStreamField getField(String name) {
330         return getField(name, null);
331     }
332 
333     /**
334      * Return a string describing this ObjectStreamClass.
335      */
toString()336     public String toString() {
337         return name + ": static final long serialVersionUID = " +
338             getSerialVersionUID() + "L;";
339     }
340 
341     /**
342      * Looks up and returns class descriptor for given class, or null if class
343      * is non-serializable and "all" is set to false.
344      *
345      * @param   cl class to look up
346      * @param   all if true, return descriptors for all classes; if false, only
347      *          return descriptors for serializable classes
348      */
lookup(Class<?> cl, boolean all)349     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
350         if (!(all || Serializable.class.isAssignableFrom(cl))) {
351             return null;
352         }
353         processQueue(Caches.localDescsQueue, Caches.localDescs);
354         WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
355         Reference<?> ref = Caches.localDescs.get(key);
356         Object entry = null;
357         if (ref != null) {
358             entry = ref.get();
359         }
360         EntryFuture future = null;
361         if (entry == null) {
362             EntryFuture newEntry = new EntryFuture();
363             Reference<?> newRef = new SoftReference<>(newEntry);
364             do {
365                 if (ref != null) {
366                     Caches.localDescs.remove(key, ref);
367                 }
368                 ref = Caches.localDescs.putIfAbsent(key, newRef);
369                 if (ref != null) {
370                     entry = ref.get();
371                 }
372             } while (ref != null && entry == null);
373             if (entry == null) {
374                 future = newEntry;
375             }
376         }
377 
378         if (entry instanceof ObjectStreamClass) {  // check common case first
379             return (ObjectStreamClass) entry;
380         }
381         if (entry instanceof EntryFuture) {
382             future = (EntryFuture) entry;
383             if (future.getOwner() == Thread.currentThread()) {
384                 /*
385                  * Handle nested call situation described by 4803747: waiting
386                  * for future value to be set by a lookup() call further up the
387                  * stack will result in deadlock, so calculate and set the
388                  * future value here instead.
389                  */
390                 entry = null;
391             } else {
392                 entry = future.get();
393             }
394         }
395         if (entry == null) {
396             try {
397                 entry = new ObjectStreamClass(cl);
398             } catch (Throwable th) {
399                 entry = th;
400             }
401             if (future.set(entry)) {
402                 Caches.localDescs.put(key, new SoftReference<Object>(entry));
403             } else {
404                 // nested lookup call already set future
405                 entry = future.get();
406             }
407         }
408 
409         if (entry instanceof ObjectStreamClass) {
410             return (ObjectStreamClass) entry;
411         } else if (entry instanceof RuntimeException) {
412             throw (RuntimeException) entry;
413         } else if (entry instanceof Error) {
414             throw (Error) entry;
415         } else {
416             throw new InternalError("unexpected entry: " + entry);
417         }
418     }
419 
420     /**
421      * Placeholder used in class descriptor and field reflector lookup tables
422      * for an entry in the process of being initialized.  (Internal) callers
423      * which receive an EntryFuture belonging to another thread as the result
424      * of a lookup should call the get() method of the EntryFuture; this will
425      * return the actual entry once it is ready for use and has been set().  To
426      * conserve objects, EntryFutures synchronize on themselves.
427      */
428     private static class EntryFuture {
429 
430         private static final Object unset = new Object();
431         private final Thread owner = Thread.currentThread();
432         private Object entry = unset;
433 
434         /**
435          * Attempts to set the value contained by this EntryFuture.  If the
436          * EntryFuture's value has not been set already, then the value is
437          * saved, any callers blocked in the get() method are notified, and
438          * true is returned.  If the value has already been set, then no saving
439          * or notification occurs, and false is returned.
440          */
set(Object entry)441         synchronized boolean set(Object entry) {
442             if (this.entry != unset) {
443                 return false;
444             }
445             this.entry = entry;
446             notifyAll();
447             return true;
448         }
449 
450         /**
451          * Returns the value contained by this EntryFuture, blocking if
452          * necessary until a value is set.
453          */
get()454         synchronized Object get() {
455             boolean interrupted = false;
456             while (entry == unset) {
457                 try {
458                     wait();
459                 } catch (InterruptedException ex) {
460                     interrupted = true;
461                 }
462             }
463             if (interrupted) {
464                 AccessController.doPrivileged(
465                     new PrivilegedAction<Void>() {
466                         public Void run() {
467                             Thread.currentThread().interrupt();
468                             return null;
469                         }
470                     }
471                 );
472             }
473             return entry;
474         }
475 
476         /**
477          * Returns the thread that created this EntryFuture.
478          */
getOwner()479         Thread getOwner() {
480             return owner;
481         }
482     }
483 
484     /**
485      * Creates local class descriptor representing given class.
486      */
ObjectStreamClass(final Class<?> cl)487     private ObjectStreamClass(final Class<?> cl) {
488         this.cl = cl;
489         name = cl.getName();
490         isProxy = Proxy.isProxyClass(cl);
491         isEnum = Enum.class.isAssignableFrom(cl);
492         isRecord = cl.isRecord();
493         serializable = Serializable.class.isAssignableFrom(cl);
494         externalizable = Externalizable.class.isAssignableFrom(cl);
495 
496         Class<?> superCl = cl.getSuperclass();
497         superDesc = (superCl != null) ? lookup(superCl, false) : null;
498         localDesc = this;
499 
500         if (serializable) {
501             AccessController.doPrivileged(new PrivilegedAction<Void>() {
502                 public Void run() {
503                     if (isEnum) {
504                         suid = Long.valueOf(0);
505                         fields = NO_FIELDS;
506                         return null;
507                     }
508                     if (cl.isArray()) {
509                         fields = NO_FIELDS;
510                         return null;
511                     }
512 
513                     suid = getDeclaredSUID(cl);
514                     try {
515                         fields = getSerialFields(cl);
516                         computeFieldOffsets();
517                     } catch (InvalidClassException e) {
518                         serializeEx = deserializeEx =
519                             new ExceptionInfo(e.classname, e.getMessage());
520                         fields = NO_FIELDS;
521                     }
522 
523                     if (isRecord) {
524                         canonicalCtr = canonicalRecordCtr(cl);
525                         deserializationCtrs = new DeserializationConstructorsCache();
526                     } else if (externalizable) {
527                         cons = getExternalizableConstructor(cl);
528                     } else {
529                         cons = getSerializableConstructor(cl);
530                         writeObjectMethod = getPrivateMethod(cl, "writeObject",
531                             new Class<?>[] { ObjectOutputStream.class },
532                             Void.TYPE);
533                         readObjectMethod = getPrivateMethod(cl, "readObject",
534                             new Class<?>[] { ObjectInputStream.class },
535                             Void.TYPE);
536                         readObjectNoDataMethod = getPrivateMethod(
537                             cl, "readObjectNoData", null, Void.TYPE);
538                         hasWriteObjectData = (writeObjectMethod != null);
539                     }
540                     writeReplaceMethod = getInheritableMethod(
541                         cl, "writeReplace", null, Object.class);
542                     readResolveMethod = getInheritableMethod(
543                         cl, "readResolve", null, Object.class);
544                     return null;
545                 }
546             });
547         } else {
548             suid = Long.valueOf(0);
549             fields = NO_FIELDS;
550         }
551 
552         try {
553             fieldRefl = getReflector(fields, this);
554         } catch (InvalidClassException ex) {
555             // field mismatches impossible when matching local fields vs. self
556             throw new InternalError(ex);
557         }
558 
559         if (deserializeEx == null) {
560             if (isEnum) {
561                 deserializeEx = new ExceptionInfo(name, "enum type");
562             } else if (cons == null && !isRecord) {
563                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
564             }
565         }
566         if (isRecord && canonicalCtr == null) {
567             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
568         } else {
569             for (int i = 0; i < fields.length; i++) {
570                 if (fields[i].getField() == null) {
571                     defaultSerializeEx = new ExceptionInfo(
572                             name, "unmatched serializable field(s) declared");
573                 }
574             }
575         }
576         initialized = true;
577     }
578 
579     /**
580      * Creates blank class descriptor which should be initialized via a
581      * subsequent call to initProxy(), initNonProxy() or readNonProxy().
582      */
ObjectStreamClass()583     ObjectStreamClass() {
584     }
585 
586     /**
587      * Initializes class descriptor representing a proxy class.
588      */
initProxy(Class<?> cl, ClassNotFoundException resolveEx, ObjectStreamClass superDesc)589     void initProxy(Class<?> cl,
590                    ClassNotFoundException resolveEx,
591                    ObjectStreamClass superDesc)
592         throws InvalidClassException
593     {
594         ObjectStreamClass osc = null;
595         if (cl != null) {
596             osc = lookup(cl, true);
597             if (!osc.isProxy) {
598                 throw new InvalidClassException(
599                     "cannot bind proxy descriptor to a non-proxy class");
600             }
601         }
602         this.cl = cl;
603         this.resolveEx = resolveEx;
604         this.superDesc = superDesc;
605         isProxy = true;
606         serializable = true;
607         suid = Long.valueOf(0);
608         fields = NO_FIELDS;
609         if (osc != null) {
610             localDesc = osc;
611             name = localDesc.name;
612             externalizable = localDesc.externalizable;
613             writeReplaceMethod = localDesc.writeReplaceMethod;
614             readResolveMethod = localDesc.readResolveMethod;
615             deserializeEx = localDesc.deserializeEx;
616             cons = localDesc.cons;
617         }
618         fieldRefl = getReflector(fields, localDesc);
619         initialized = true;
620     }
621 
622     /**
623      * Initializes class descriptor representing a non-proxy class.
624      */
initNonProxy(ObjectStreamClass model, Class<?> cl, ClassNotFoundException resolveEx, ObjectStreamClass superDesc)625     void initNonProxy(ObjectStreamClass model,
626                       Class<?> cl,
627                       ClassNotFoundException resolveEx,
628                       ObjectStreamClass superDesc)
629         throws InvalidClassException
630     {
631         long suid = Long.valueOf(model.getSerialVersionUID());
632         ObjectStreamClass osc = null;
633         if (cl != null) {
634             osc = lookup(cl, true);
635             if (osc.isProxy) {
636                 throw new InvalidClassException(
637                         "cannot bind non-proxy descriptor to a proxy class");
638             }
639             if (model.isEnum != osc.isEnum) {
640                 throw new InvalidClassException(model.isEnum ?
641                         "cannot bind enum descriptor to a non-enum class" :
642                         "cannot bind non-enum descriptor to an enum class");
643             }
644 
645             if (model.serializable == osc.serializable &&
646                     !cl.isArray() && !cl.isRecord() &&
647                     suid != osc.getSerialVersionUID()) {
648                 throw new InvalidClassException(osc.name,
649                         "local class incompatible: " +
650                                 "stream classdesc serialVersionUID = " + suid +
651                                 ", local class serialVersionUID = " +
652                                 osc.getSerialVersionUID());
653             }
654 
655             if (!classNamesEqual(model.name, osc.name)) {
656                 throw new InvalidClassException(osc.name,
657                         "local class name incompatible with stream class " +
658                                 "name \"" + model.name + "\"");
659             }
660 
661             if (!model.isEnum) {
662                 if ((model.serializable == osc.serializable) &&
663                         (model.externalizable != osc.externalizable)) {
664                     throw new InvalidClassException(osc.name,
665                             "Serializable incompatible with Externalizable");
666                 }
667 
668                 if ((model.serializable != osc.serializable) ||
669                         (model.externalizable != osc.externalizable) ||
670                         !(model.serializable || model.externalizable)) {
671                     deserializeEx = new ExceptionInfo(
672                             osc.name, "class invalid for deserialization");
673                 }
674             }
675         }
676 
677         this.cl = cl;
678         this.resolveEx = resolveEx;
679         this.superDesc = superDesc;
680         name = model.name;
681         this.suid = suid;
682         isProxy = false;
683         isEnum = model.isEnum;
684         serializable = model.serializable;
685         externalizable = model.externalizable;
686         hasBlockExternalData = model.hasBlockExternalData;
687         hasWriteObjectData = model.hasWriteObjectData;
688         fields = model.fields;
689         primDataSize = model.primDataSize;
690         numObjFields = model.numObjFields;
691 
692         if (osc != null) {
693             localDesc = osc;
694             isRecord = localDesc.isRecord;
695             // canonical record constructor is shared
696             canonicalCtr = localDesc.canonicalCtr;
697             // cache of deserialization constructors is shared
698             deserializationCtrs = localDesc.deserializationCtrs;
699             writeObjectMethod = localDesc.writeObjectMethod;
700             readObjectMethod = localDesc.readObjectMethod;
701             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
702             writeReplaceMethod = localDesc.writeReplaceMethod;
703             readResolveMethod = localDesc.readResolveMethod;
704             if (deserializeEx == null) {
705                 deserializeEx = localDesc.deserializeEx;
706             }
707             assert cl.isRecord() ? localDesc.cons == null : true;
708             cons = localDesc.cons;
709         }
710 
711         fieldRefl = getReflector(fields, localDesc);
712         // reassign to matched fields so as to reflect local unshared settings
713         fields = fieldRefl.getFields();
714         initialized = true;
715     }
716 
717     /**
718      * Reads non-proxy class descriptor information from given input stream.
719      * The resulting class descriptor is not fully functional; it can only be
720      * used as input to the ObjectInputStream.resolveClass() and
721      * ObjectStreamClass.initNonProxy() methods.
722      */
readNonProxy(ObjectInputStream in)723     void readNonProxy(ObjectInputStream in)
724         throws IOException, ClassNotFoundException
725     {
726         name = in.readUTF();
727         suid = Long.valueOf(in.readLong());
728         isProxy = false;
729 
730         byte flags = in.readByte();
731         hasWriteObjectData =
732             ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
733         hasBlockExternalData =
734             ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
735         externalizable =
736             ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
737         boolean sflag =
738             ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
739         if (externalizable && sflag) {
740             throw new InvalidClassException(
741                 name, "serializable and externalizable flags conflict");
742         }
743         serializable = externalizable || sflag;
744         isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
745         if (isEnum && suid.longValue() != 0L) {
746             throw new InvalidClassException(name,
747                 "enum descriptor has non-zero serialVersionUID: " + suid);
748         }
749 
750         int numFields = in.readShort();
751         if (isEnum && numFields != 0) {
752             throw new InvalidClassException(name,
753                 "enum descriptor has non-zero field count: " + numFields);
754         }
755         fields = (numFields > 0) ?
756             new ObjectStreamField[numFields] : NO_FIELDS;
757         for (int i = 0; i < numFields; i++) {
758             char tcode = (char) in.readByte();
759             String fname = in.readUTF();
760             String signature = ((tcode == 'L') || (tcode == '[')) ?
761                 in.readTypeString() : new String(new char[] { tcode });
762             try {
763                 fields[i] = new ObjectStreamField(fname, signature, false);
764             } catch (RuntimeException e) {
765                 throw (IOException) new InvalidClassException(name,
766                     "invalid descriptor for field " + fname).initCause(e);
767             }
768         }
769         computeFieldOffsets();
770     }
771 
772     /**
773      * Writes non-proxy class descriptor information to given output stream.
774      */
writeNonProxy(ObjectOutputStream out)775     void writeNonProxy(ObjectOutputStream out) throws IOException {
776         out.writeUTF(name);
777         out.writeLong(getSerialVersionUID());
778 
779         byte flags = 0;
780         if (externalizable) {
781             flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
782             int protocol = out.getProtocolVersion();
783             if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
784                 flags |= ObjectStreamConstants.SC_BLOCK_DATA;
785             }
786         } else if (serializable) {
787             flags |= ObjectStreamConstants.SC_SERIALIZABLE;
788         }
789         if (hasWriteObjectData) {
790             flags |= ObjectStreamConstants.SC_WRITE_METHOD;
791         }
792         if (isEnum) {
793             flags |= ObjectStreamConstants.SC_ENUM;
794         }
795         out.writeByte(flags);
796 
797         out.writeShort(fields.length);
798         for (int i = 0; i < fields.length; i++) {
799             ObjectStreamField f = fields[i];
800             out.writeByte(f.getTypeCode());
801             out.writeUTF(f.getName());
802             if (!f.isPrimitive()) {
803                 out.writeTypeString(f.getTypeString());
804             }
805         }
806     }
807 
808     /**
809      * Returns ClassNotFoundException (if any) thrown while attempting to
810      * resolve local class corresponding to this class descriptor.
811      */
getResolveException()812     ClassNotFoundException getResolveException() {
813         return resolveEx;
814     }
815 
816     /**
817      * Throws InternalError if not initialized.
818      */
requireInitialized()819     private final void requireInitialized() {
820         if (!initialized)
821             throw new InternalError("Unexpected call when not initialized");
822     }
823 
824     /**
825      * Throws an InvalidClassException if object instances referencing this
826      * class descriptor should not be allowed to deserialize.  This method does
827      * not apply to deserialization of enum constants.
828      */
checkDeserialize()829     void checkDeserialize() throws InvalidClassException {
830         requireInitialized();
831         if (deserializeEx != null) {
832             throw deserializeEx.newInvalidClassException();
833         }
834     }
835 
836     /**
837      * Throws an InvalidClassException if objects whose class is represented by
838      * this descriptor should not be allowed to serialize.  This method does
839      * not apply to serialization of enum constants.
840      */
checkSerialize()841     void checkSerialize() throws InvalidClassException {
842         requireInitialized();
843         if (serializeEx != null) {
844             throw serializeEx.newInvalidClassException();
845         }
846     }
847 
848     /**
849      * Throws an InvalidClassException if objects whose class is represented by
850      * this descriptor should not be permitted to use default serialization
851      * (e.g., if the class declares serializable fields that do not correspond
852      * to actual fields, and hence must use the GetField API).  This method
853      * does not apply to deserialization of enum constants.
854      */
checkDefaultSerialize()855     void checkDefaultSerialize() throws InvalidClassException {
856         requireInitialized();
857         if (defaultSerializeEx != null) {
858             throw defaultSerializeEx.newInvalidClassException();
859         }
860     }
861 
862     /**
863      * Returns superclass descriptor.  Note that on the receiving side, the
864      * superclass descriptor may be bound to a class that is not a superclass
865      * of the subclass descriptor's bound class.
866      */
getSuperDesc()867     ObjectStreamClass getSuperDesc() {
868         requireInitialized();
869         return superDesc;
870     }
871 
872     /**
873      * Returns the "local" class descriptor for the class associated with this
874      * class descriptor (i.e., the result of
875      * ObjectStreamClass.lookup(this.forClass())) or null if there is no class
876      * associated with this descriptor.
877      */
getLocalDesc()878     ObjectStreamClass getLocalDesc() {
879         requireInitialized();
880         return localDesc;
881     }
882 
883     /**
884      * Returns arrays of ObjectStreamFields representing the serializable
885      * fields of the represented class.  If copy is true, a clone of this class
886      * descriptor's field array is returned, otherwise the array itself is
887      * returned.
888      */
getFields(boolean copy)889     ObjectStreamField[] getFields(boolean copy) {
890         return copy ? fields.clone() : fields;
891     }
892 
893     /**
894      * Looks up a serializable field of the represented class by name and type.
895      * A specified type of null matches all types, Object.class matches all
896      * non-primitive types, and any other non-null type matches assignable
897      * types only.  Returns matching field, or null if no match found.
898      */
getField(String name, Class<?> type)899     ObjectStreamField getField(String name, Class<?> type) {
900         for (int i = 0; i < fields.length; i++) {
901             ObjectStreamField f = fields[i];
902             if (f.getName().equals(name)) {
903                 if (type == null ||
904                     (type == Object.class && !f.isPrimitive()))
905                 {
906                     return f;
907                 }
908                 Class<?> ftype = f.getType();
909                 if (ftype != null && type.isAssignableFrom(ftype)) {
910                     return f;
911                 }
912             }
913         }
914         return null;
915     }
916 
917     /**
918      * Returns true if class descriptor represents a dynamic proxy class, false
919      * otherwise.
920      */
isProxy()921     boolean isProxy() {
922         requireInitialized();
923         return isProxy;
924     }
925 
926     /**
927      * Returns true if class descriptor represents an enum type, false
928      * otherwise.
929      */
isEnum()930     boolean isEnum() {
931         requireInitialized();
932         return isEnum;
933     }
934 
935     /**
936      * Returns true if class descriptor represents a record type, false
937      * otherwise.
938      */
isRecord()939     boolean isRecord() {
940         requireInitialized();
941         return isRecord;
942     }
943 
944     /**
945      * Returns true if represented class implements Externalizable, false
946      * otherwise.
947      */
isExternalizable()948     boolean isExternalizable() {
949         requireInitialized();
950         return externalizable;
951     }
952 
953     /**
954      * Returns true if represented class implements Serializable, false
955      * otherwise.
956      */
isSerializable()957     boolean isSerializable() {
958         requireInitialized();
959         return serializable;
960     }
961 
962     /**
963      * Returns true if class descriptor represents externalizable class that
964      * has written its data in 1.2 (block data) format, false otherwise.
965      */
hasBlockExternalData()966     boolean hasBlockExternalData() {
967         requireInitialized();
968         return hasBlockExternalData;
969     }
970 
971     /**
972      * Returns true if class descriptor represents serializable (but not
973      * externalizable) class which has written its data via a custom
974      * writeObject() method, false otherwise.
975      */
hasWriteObjectData()976     boolean hasWriteObjectData() {
977         requireInitialized();
978         return hasWriteObjectData;
979     }
980 
981     /**
982      * Returns true if represented class is serializable/externalizable and can
983      * be instantiated by the serialization runtime--i.e., if it is
984      * externalizable and defines a public no-arg constructor, or if it is
985      * non-externalizable and its first non-serializable superclass defines an
986      * accessible no-arg constructor.  Otherwise, returns false.
987      */
isInstantiable()988     boolean isInstantiable() {
989         requireInitialized();
990         return (cons != null);
991     }
992 
993     /**
994      * Returns true if represented class is serializable (but not
995      * externalizable) and defines a conformant writeObject method.  Otherwise,
996      * returns false.
997      */
hasWriteObjectMethod()998     boolean hasWriteObjectMethod() {
999         requireInitialized();
1000         return (writeObjectMethod != null);
1001     }
1002 
1003     /**
1004      * Returns true if represented class is serializable (but not
1005      * externalizable) and defines a conformant readObject method.  Otherwise,
1006      * returns false.
1007      */
hasReadObjectMethod()1008     boolean hasReadObjectMethod() {
1009         requireInitialized();
1010         return (readObjectMethod != null);
1011     }
1012 
1013     /**
1014      * Returns true if represented class is serializable (but not
1015      * externalizable) and defines a conformant readObjectNoData method.
1016      * Otherwise, returns false.
1017      */
hasReadObjectNoDataMethod()1018     boolean hasReadObjectNoDataMethod() {
1019         requireInitialized();
1020         return (readObjectNoDataMethod != null);
1021     }
1022 
1023     /**
1024      * Returns true if represented class is serializable or externalizable and
1025      * defines a conformant writeReplace method.  Otherwise, returns false.
1026      */
hasWriteReplaceMethod()1027     boolean hasWriteReplaceMethod() {
1028         requireInitialized();
1029         return (writeReplaceMethod != null);
1030     }
1031 
1032     /**
1033      * Returns true if represented class is serializable or externalizable and
1034      * defines a conformant readResolve method.  Otherwise, returns false.
1035      */
hasReadResolveMethod()1036     boolean hasReadResolveMethod() {
1037         requireInitialized();
1038         return (readResolveMethod != null);
1039     }
1040 
1041     /**
1042      * Creates a new instance of the represented class.  If the class is
1043      * externalizable, invokes its public no-arg constructor; otherwise, if the
1044      * class is serializable, invokes the no-arg constructor of the first
1045      * non-serializable superclass.  Throws UnsupportedOperationException if
1046      * this class descriptor is not associated with a class, if the associated
1047      * class is non-serializable or if the appropriate no-arg constructor is
1048      * inaccessible/unavailable.
1049      */
newInstance()1050     Object newInstance()
1051         throws InstantiationException, InvocationTargetException,
1052                UnsupportedOperationException
1053     {
1054         requireInitialized();
1055         if (cons != null) {
1056             try {
1057                 return cons.newInstance();
1058             } catch (IllegalAccessException ex) {
1059                 // should not occur, as access checks have been suppressed
1060                 throw new InternalError(ex);
1061             }
1062         } else {
1063             throw new UnsupportedOperationException();
1064         }
1065     }
1066 
1067     /**
1068      * Invokes the writeObject method of the represented serializable class.
1069      * Throws UnsupportedOperationException if this class descriptor is not
1070      * associated with a class, or if the class is externalizable,
1071      * non-serializable or does not define writeObject.
1072      */
invokeWriteObject(Object obj, ObjectOutputStream out)1073     void invokeWriteObject(Object obj, ObjectOutputStream out)
1074         throws IOException, UnsupportedOperationException
1075     {
1076         requireInitialized();
1077         if (writeObjectMethod != null) {
1078             try {
1079                 writeObjectMethod.invoke(obj, new Object[]{ out });
1080             } catch (InvocationTargetException ex) {
1081                 Throwable th = ex.getTargetException();
1082                 if (th instanceof IOException) {
1083                     throw (IOException) th;
1084                 } else {
1085                     throwMiscException(th);
1086                 }
1087             } catch (IllegalAccessException ex) {
1088                 // should not occur, as access checks have been suppressed
1089                 throw new InternalError(ex);
1090             }
1091         } else {
1092             throw new UnsupportedOperationException();
1093         }
1094     }
1095 
1096     /**
1097      * Invokes the readObject method of the represented serializable class.
1098      * Throws UnsupportedOperationException if this class descriptor is not
1099      * associated with a class, or if the class is externalizable,
1100      * non-serializable or does not define readObject.
1101      */
invokeReadObject(Object obj, ObjectInputStream in)1102     void invokeReadObject(Object obj, ObjectInputStream in)
1103         throws ClassNotFoundException, IOException,
1104                UnsupportedOperationException
1105     {
1106         requireInitialized();
1107         if (readObjectMethod != null) {
1108             try {
1109                 readObjectMethod.invoke(obj, new Object[]{ in });
1110             } catch (InvocationTargetException ex) {
1111                 Throwable th = ex.getTargetException();
1112                 if (th instanceof ClassNotFoundException) {
1113                     throw (ClassNotFoundException) th;
1114                 } else if (th instanceof IOException) {
1115                     throw (IOException) th;
1116                 } else {
1117                     throwMiscException(th);
1118                 }
1119             } catch (IllegalAccessException ex) {
1120                 // should not occur, as access checks have been suppressed
1121                 throw new InternalError(ex);
1122             }
1123         } else {
1124             throw new UnsupportedOperationException();
1125         }
1126     }
1127 
1128     /**
1129      * Invokes the readObjectNoData method of the represented serializable
1130      * class.  Throws UnsupportedOperationException if this class descriptor is
1131      * not associated with a class, or if the class is externalizable,
1132      * non-serializable or does not define readObjectNoData.
1133      */
invokeReadObjectNoData(Object obj)1134     void invokeReadObjectNoData(Object obj)
1135         throws IOException, UnsupportedOperationException
1136     {
1137         requireInitialized();
1138         if (readObjectNoDataMethod != null) {
1139             try {
1140                 readObjectNoDataMethod.invoke(obj, (Object[]) null);
1141             } catch (InvocationTargetException ex) {
1142                 Throwable th = ex.getTargetException();
1143                 if (th instanceof ObjectStreamException) {
1144                     throw (ObjectStreamException) th;
1145                 } else {
1146                     throwMiscException(th);
1147                 }
1148             } catch (IllegalAccessException ex) {
1149                 // should not occur, as access checks have been suppressed
1150                 throw new InternalError(ex);
1151             }
1152         } else {
1153             throw new UnsupportedOperationException();
1154         }
1155     }
1156 
1157     /**
1158      * Invokes the writeReplace method of the represented serializable class and
1159      * returns the result.  Throws UnsupportedOperationException if this class
1160      * descriptor is not associated with a class, or if the class is
1161      * non-serializable or does not define writeReplace.
1162      */
invokeWriteReplace(Object obj)1163     Object invokeWriteReplace(Object obj)
1164         throws IOException, UnsupportedOperationException
1165     {
1166         requireInitialized();
1167         if (writeReplaceMethod != null) {
1168             try {
1169                 return writeReplaceMethod.invoke(obj, (Object[]) null);
1170             } catch (InvocationTargetException ex) {
1171                 Throwable th = ex.getTargetException();
1172                 if (th instanceof ObjectStreamException) {
1173                     throw (ObjectStreamException) th;
1174                 } else {
1175                     throwMiscException(th);
1176                     throw new InternalError(th);  // never reached
1177                 }
1178             } catch (IllegalAccessException ex) {
1179                 // should not occur, as access checks have been suppressed
1180                 throw new InternalError(ex);
1181             }
1182         } else {
1183             throw new UnsupportedOperationException();
1184         }
1185     }
1186 
1187     /**
1188      * Invokes the readResolve method of the represented serializable class and
1189      * returns the result.  Throws UnsupportedOperationException if this class
1190      * descriptor is not associated with a class, or if the class is
1191      * non-serializable or does not define readResolve.
1192      */
invokeReadResolve(Object obj)1193     Object invokeReadResolve(Object obj)
1194         throws IOException, UnsupportedOperationException
1195     {
1196         requireInitialized();
1197         if (readResolveMethod != null) {
1198             try {
1199                 return readResolveMethod.invoke(obj, (Object[]) null);
1200             } catch (InvocationTargetException ex) {
1201                 Throwable th = ex.getTargetException();
1202                 if (th instanceof ObjectStreamException) {
1203                     throw (ObjectStreamException) th;
1204                 } else {
1205                     throwMiscException(th);
1206                     throw new InternalError(th);  // never reached
1207                 }
1208             } catch (IllegalAccessException ex) {
1209                 // should not occur, as access checks have been suppressed
1210                 throw new InternalError(ex);
1211             }
1212         } else {
1213             throw new UnsupportedOperationException();
1214         }
1215     }
1216 
1217     /**
1218      * Class representing the portion of an object's serialized form allotted
1219      * to data described by a given class descriptor.  If "hasData" is false,
1220      * the object's serialized form does not contain data associated with the
1221      * class descriptor.
1222      */
1223     static class ClassDataSlot {
1224 
1225         /** class descriptor "occupying" this slot */
1226         final ObjectStreamClass desc;
1227         /** true if serialized form includes data for this slot's descriptor */
1228         final boolean hasData;
1229 
ClassDataSlot(ObjectStreamClass desc, boolean hasData)1230         ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1231             this.desc = desc;
1232             this.hasData = hasData;
1233         }
1234     }
1235 
1236     /**
1237      * Returns array of ClassDataSlot instances representing the data layout
1238      * (including superclass data) for serialized objects described by this
1239      * class descriptor.  ClassDataSlots are ordered by inheritance with those
1240      * containing "higher" superclasses appearing first.  The final
1241      * ClassDataSlot contains a reference to this descriptor.
1242      */
getClassDataLayout()1243     ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
1244         // REMIND: synchronize instead of relying on volatile?
1245         if (dataLayout == null) {
1246             dataLayout = getClassDataLayout0();
1247         }
1248         return dataLayout;
1249     }
1250 
getClassDataLayout0()1251     private ClassDataSlot[] getClassDataLayout0()
1252         throws InvalidClassException
1253     {
1254         ArrayList<ClassDataSlot> slots = new ArrayList<>();
1255         Class<?> start = cl, end = cl;
1256 
1257         // locate closest non-serializable superclass
1258         while (end != null && Serializable.class.isAssignableFrom(end)) {
1259             end = end.getSuperclass();
1260         }
1261 
1262         HashSet<String> oscNames = new HashSet<>(3);
1263 
1264         for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1265             if (oscNames.contains(d.name)) {
1266                 throw new InvalidClassException("Circular reference.");
1267             } else {
1268                 oscNames.add(d.name);
1269             }
1270 
1271             // search up inheritance hierarchy for class with matching name
1272             String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1273             Class<?> match = null;
1274             for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1275                 if (searchName.equals(c.getName())) {
1276                     match = c;
1277                     break;
1278                 }
1279             }
1280 
1281             // add "no data" slot for each unmatched class below match
1282             if (match != null) {
1283                 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1284                     slots.add(new ClassDataSlot(
1285                         ObjectStreamClass.lookup(c, true), false));
1286                 }
1287                 start = match.getSuperclass();
1288             }
1289 
1290             // record descriptor/class pairing
1291             slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1292         }
1293 
1294         // add "no data" slot for any leftover unmatched classes
1295         for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1296             slots.add(new ClassDataSlot(
1297                 ObjectStreamClass.lookup(c, true), false));
1298         }
1299 
1300         // order slots from superclass -> subclass
1301         Collections.reverse(slots);
1302         return slots.toArray(new ClassDataSlot[slots.size()]);
1303     }
1304 
1305     /**
1306      * Returns aggregate size (in bytes) of marshalled primitive field values
1307      * for represented class.
1308      */
getPrimDataSize()1309     int getPrimDataSize() {
1310         return primDataSize;
1311     }
1312 
1313     /**
1314      * Returns number of non-primitive serializable fields of represented
1315      * class.
1316      */
getNumObjFields()1317     int getNumObjFields() {
1318         return numObjFields;
1319     }
1320 
1321     /**
1322      * Fetches the serializable primitive field values of object obj and
1323      * marshals them into byte array buf starting at offset 0.  It is the
1324      * responsibility of the caller to ensure that obj is of the proper type if
1325      * non-null.
1326      */
getPrimFieldValues(Object obj, byte[] buf)1327     void getPrimFieldValues(Object obj, byte[] buf) {
1328         fieldRefl.getPrimFieldValues(obj, buf);
1329     }
1330 
1331     /**
1332      * Sets the serializable primitive fields of object obj using values
1333      * unmarshalled from byte array buf starting at offset 0.  It is the
1334      * responsibility of the caller to ensure that obj is of the proper type if
1335      * non-null.
1336      */
setPrimFieldValues(Object obj, byte[] buf)1337     void setPrimFieldValues(Object obj, byte[] buf) {
1338         fieldRefl.setPrimFieldValues(obj, buf);
1339     }
1340 
1341     /**
1342      * Fetches the serializable object field values of object obj and stores
1343      * them in array vals starting at offset 0.  It is the responsibility of
1344      * the caller to ensure that obj is of the proper type if non-null.
1345      */
getObjFieldValues(Object obj, Object[] vals)1346     void getObjFieldValues(Object obj, Object[] vals) {
1347         fieldRefl.getObjFieldValues(obj, vals);
1348     }
1349 
1350     /**
1351      * Sets the serializable object fields of object obj using values from
1352      * array vals starting at offset 0.  It is the responsibility of the caller
1353      * to ensure that obj is of the proper type if non-null.
1354      */
setObjFieldValues(Object obj, Object[] vals)1355     void setObjFieldValues(Object obj, Object[] vals) {
1356         fieldRefl.setObjFieldValues(obj, vals);
1357     }
1358 
1359     /**
1360      * Calculates and sets serializable field offsets, as well as primitive
1361      * data size and object field count totals.  Throws InvalidClassException
1362      * if fields are illegally ordered.
1363      */
computeFieldOffsets()1364     private void computeFieldOffsets() throws InvalidClassException {
1365         primDataSize = 0;
1366         numObjFields = 0;
1367         int firstObjIndex = -1;
1368 
1369         for (int i = 0; i < fields.length; i++) {
1370             ObjectStreamField f = fields[i];
1371             switch (f.getTypeCode()) {
1372                 case 'Z':
1373                 case 'B':
1374                     f.setOffset(primDataSize++);
1375                     break;
1376 
1377                 case 'C':
1378                 case 'S':
1379                     f.setOffset(primDataSize);
1380                     primDataSize += 2;
1381                     break;
1382 
1383                 case 'I':
1384                 case 'F':
1385                     f.setOffset(primDataSize);
1386                     primDataSize += 4;
1387                     break;
1388 
1389                 case 'J':
1390                 case 'D':
1391                     f.setOffset(primDataSize);
1392                     primDataSize += 8;
1393                     break;
1394 
1395                 case '[':
1396                 case 'L':
1397                     f.setOffset(numObjFields++);
1398                     if (firstObjIndex == -1) {
1399                         firstObjIndex = i;
1400                     }
1401                     break;
1402 
1403                 default:
1404                     throw new InternalError();
1405             }
1406         }
1407         if (firstObjIndex != -1 &&
1408             firstObjIndex + numObjFields != fields.length)
1409         {
1410             throw new InvalidClassException(name, "illegal field order");
1411         }
1412     }
1413 
1414     /**
1415      * If given class is the same as the class associated with this class
1416      * descriptor, returns reference to this class descriptor.  Otherwise,
1417      * returns variant of this class descriptor bound to given class.
1418      */
getVariantFor(Class<?> cl)1419     private ObjectStreamClass getVariantFor(Class<?> cl)
1420         throws InvalidClassException
1421     {
1422         if (this.cl == cl) {
1423             return this;
1424         }
1425         ObjectStreamClass desc = new ObjectStreamClass();
1426         if (isProxy) {
1427             desc.initProxy(cl, null, superDesc);
1428         } else {
1429             desc.initNonProxy(this, cl, null, superDesc);
1430         }
1431         return desc;
1432     }
1433 
1434     /**
1435      * Returns public no-arg constructor of given class, or null if none found.
1436      * Access checks are disabled on the returned constructor (if any), since
1437      * the defining class may still be non-public.
1438      */
getExternalizableConstructor(Class<?> cl)1439     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1440         try {
1441             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1442             cons.setAccessible(true);
1443             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1444                 cons : null;
1445         } catch (NoSuchMethodException ex) {
1446             return null;
1447         }
1448     }
1449 
1450     /**
1451      * Returns subclass-accessible no-arg constructor of first non-serializable
1452      * superclass, or null if none found.  Access checks are disabled on the
1453      * returned constructor (if any).
1454      */
getSerializableConstructor(Class<?> cl)1455     private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1456         Class<?> initCl = cl;
1457         while (Serializable.class.isAssignableFrom(initCl)) {
1458             if ((initCl = initCl.getSuperclass()) == null) {
1459                 return null;
1460             }
1461         }
1462         try {
1463             Constructor<?> cons = initCl.getDeclaredConstructor((Class<?>[]) null);
1464             int mods = cons.getModifiers();
1465             if ((mods & Modifier.PRIVATE) != 0 ||
1466                 ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
1467                  !packageEquals(cl, initCl)))
1468             {
1469                 return null;
1470             }
1471             // BEGIN Android-changed: Serialization constructor obtained differently.
1472             // cons = reflFactory.newConstructorForSerialization(cl, cons);
1473             if (cons.getDeclaringClass() != cl) {
1474                 cons = cons.serializationCopy(cons.getDeclaringClass(), cl);
1475             }
1476             // END Android-changed: Serialization constructor obtained differently.
1477             cons.setAccessible(true);
1478             return cons;
1479         } catch (NoSuchMethodException ex) {
1480             return null;
1481         }
1482     }
1483 
1484     /**
1485      * Returns the canonical constructor for the given record class, or null if
1486      * the not found ( which should never happen for correctly generated record
1487      * classes ).
1488      */
1489     @SuppressWarnings("removal")
canonicalRecordCtr(Class<?> cls)1490     private static MethodHandle canonicalRecordCtr(Class<?> cls) {
1491         assert cls.isRecord() : "Expected record, got: " + cls;
1492         PrivilegedAction<MethodHandle> pa = () -> {
1493             Class<?>[] paramTypes = Arrays.stream(cls.getRecordComponents())
1494                     .map(RecordComponent::getType)
1495                     .toArray(Class<?>[]::new);
1496             try {
1497                 Constructor<?> ctr = cls.getDeclaredConstructor(paramTypes);
1498                 ctr.setAccessible(true);
1499                 return MethodHandles.lookup().unreflectConstructor(ctr);
1500             } catch (IllegalAccessException | NoSuchMethodException e) {
1501                 return null;
1502             }
1503         };
1504         return AccessController.doPrivileged(pa);
1505     }
1506 
1507     /**
1508      * Returns the canonical constructor, if the local class equivalent of this
1509      * stream class descriptor is a record class, otherwise null.
1510      */
getRecordConstructor()1511     MethodHandle getRecordConstructor() {
1512         return canonicalCtr;
1513     }
1514 
1515     /**
1516      * Returns non-static, non-abstract method with given signature provided it
1517      * is defined by or accessible (via inheritance) by the given class, or
1518      * null if no match found.  Access checks are disabled on the returned
1519      * method (if any).
1520      */
getInheritableMethod(Class<?> cl, String name, Class<?>[] argTypes, Class<?> returnType)1521     private static Method getInheritableMethod(Class<?> cl, String name,
1522                                                Class<?>[] argTypes,
1523                                                Class<?> returnType)
1524     {
1525         Method meth = null;
1526         Class<?> defCl = cl;
1527         while (defCl != null) {
1528             try {
1529                 meth = defCl.getDeclaredMethod(name, argTypes);
1530                 break;
1531             } catch (NoSuchMethodException ex) {
1532                 defCl = defCl.getSuperclass();
1533             }
1534         }
1535 
1536         if ((meth == null) || (meth.getReturnType() != returnType)) {
1537             return null;
1538         }
1539         meth.setAccessible(true);
1540         int mods = meth.getModifiers();
1541         if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) {
1542             return null;
1543         } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) {
1544             return meth;
1545         } else if ((mods & Modifier.PRIVATE) != 0) {
1546             return (cl == defCl) ? meth : null;
1547         } else {
1548             return packageEquals(cl, defCl) ? meth : null;
1549         }
1550     }
1551 
1552     /**
1553      * Returns non-static private method with given signature defined by given
1554      * class, or null if none found.  Access checks are disabled on the
1555      * returned method (if any).
1556      */
getPrivateMethod(Class<?> cl, String name, Class<?>[] argTypes, Class<?> returnType)1557     private static Method getPrivateMethod(Class<?> cl, String name,
1558                                            Class<?>[] argTypes,
1559                                            Class<?> returnType)
1560     {
1561         try {
1562             Method meth = cl.getDeclaredMethod(name, argTypes);
1563             meth.setAccessible(true);
1564             int mods = meth.getModifiers();
1565             return ((meth.getReturnType() == returnType) &&
1566                     ((mods & Modifier.STATIC) == 0) &&
1567                     ((mods & Modifier.PRIVATE) != 0)) ? meth : null;
1568         } catch (NoSuchMethodException ex) {
1569             return null;
1570         }
1571     }
1572 
1573     /**
1574      * Returns true if classes are defined in the same runtime package, false
1575      * otherwise.
1576      */
packageEquals(Class<?> cl1, Class<?> cl2)1577     private static boolean packageEquals(Class<?> cl1, Class<?> cl2) {
1578         return (cl1.getClassLoader() == cl2.getClassLoader() &&
1579                 getPackageName(cl1).equals(getPackageName(cl2)));
1580     }
1581 
1582     /**
1583      * Returns package name of given class.
1584      */
getPackageName(Class<?> cl)1585     private static String getPackageName(Class<?> cl) {
1586         String s = cl.getName();
1587         int i = s.lastIndexOf('[');
1588         if (i >= 0) {
1589             s = s.substring(i + 2);
1590         }
1591         i = s.lastIndexOf('.');
1592         return (i >= 0) ? s.substring(0, i) : "";
1593     }
1594 
1595     /**
1596      * Compares class names for equality, ignoring package names.  Returns true
1597      * if class names equal, false otherwise.
1598      */
classNamesEqual(String name1, String name2)1599     private static boolean classNamesEqual(String name1, String name2) {
1600         name1 = name1.substring(name1.lastIndexOf('.') + 1);
1601         name2 = name2.substring(name2.lastIndexOf('.') + 1);
1602         return name1.equals(name2);
1603     }
1604 
1605     /**
1606      * Returns JVM type signature for given class.
1607      */
getClassSignature(Class<?> cl)1608     private static String getClassSignature(Class<?> cl) {
1609         StringBuilder sbuf = new StringBuilder();
1610         while (cl.isArray()) {
1611             sbuf.append('[');
1612             cl = cl.getComponentType();
1613         }
1614         if (cl.isPrimitive()) {
1615             if (cl == Integer.TYPE) {
1616                 sbuf.append('I');
1617             } else if (cl == Byte.TYPE) {
1618                 sbuf.append('B');
1619             } else if (cl == Long.TYPE) {
1620                 sbuf.append('J');
1621             } else if (cl == Float.TYPE) {
1622                 sbuf.append('F');
1623             } else if (cl == Double.TYPE) {
1624                 sbuf.append('D');
1625             } else if (cl == Short.TYPE) {
1626                 sbuf.append('S');
1627             } else if (cl == Character.TYPE) {
1628                 sbuf.append('C');
1629             } else if (cl == Boolean.TYPE) {
1630                 sbuf.append('Z');
1631             } else if (cl == Void.TYPE) {
1632                 sbuf.append('V');
1633             } else {
1634                 throw new InternalError();
1635             }
1636         } else {
1637             sbuf.append('L' + cl.getName().replace('.', '/') + ';');
1638         }
1639         return sbuf.toString();
1640     }
1641 
1642     /**
1643      * Returns JVM type signature for given list of parameters and return type.
1644      */
getMethodSignature(Class<?>[] paramTypes, Class<?> retType)1645     private static String getMethodSignature(Class<?>[] paramTypes,
1646                                              Class<?> retType)
1647     {
1648         StringBuilder sbuf = new StringBuilder();
1649         sbuf.append('(');
1650         for (int i = 0; i < paramTypes.length; i++) {
1651             sbuf.append(getClassSignature(paramTypes[i]));
1652         }
1653         sbuf.append(')');
1654         sbuf.append(getClassSignature(retType));
1655         return sbuf.toString();
1656     }
1657 
1658     /**
1659      * Convenience method for throwing an exception that is either a
1660      * RuntimeException, Error, or of some unexpected type (in which case it is
1661      * wrapped inside an IOException).
1662      */
throwMiscException(Throwable th)1663     private static void throwMiscException(Throwable th) throws IOException {
1664         if (th instanceof RuntimeException) {
1665             throw (RuntimeException) th;
1666         } else if (th instanceof Error) {
1667             throw (Error) th;
1668         } else {
1669             IOException ex = new IOException("unexpected exception type");
1670             ex.initCause(th);
1671             throw ex;
1672         }
1673     }
1674 
1675     /**
1676      * Returns ObjectStreamField array describing the serializable fields of
1677      * the given class.  Serializable fields backed by an actual field of the
1678      * class are represented by ObjectStreamFields with corresponding non-null
1679      * Field objects.  Throws InvalidClassException if the (explicitly
1680      * declared) serializable fields are invalid.
1681      */
getSerialFields(Class<?> cl)1682     private static ObjectStreamField[] getSerialFields(Class<?> cl)
1683         throws InvalidClassException
1684     {
1685         ObjectStreamField[] fields;
1686         if (cl.isRecord()) {
1687             fields = getDefaultSerialFields(cl);
1688             Arrays.sort(fields);
1689         } else if (Serializable.class.isAssignableFrom(cl) &&
1690             !Externalizable.class.isAssignableFrom(cl) &&
1691             !Proxy.isProxyClass(cl) &&
1692             !cl.isInterface())
1693         {
1694             if ((fields = getDeclaredSerialFields(cl)) == null) {
1695                 fields = getDefaultSerialFields(cl);
1696             }
1697             Arrays.sort(fields);
1698         } else {
1699             fields = NO_FIELDS;
1700         }
1701         return fields;
1702     }
1703 
1704     /**
1705      * Returns serializable fields of given class as defined explicitly by a
1706      * "serialPersistentFields" field, or null if no appropriate
1707      * "serialPersistentFields" field is defined.  Serializable fields backed
1708      * by an actual field of the class are represented by ObjectStreamFields
1709      * with corresponding non-null Field objects.  For compatibility with past
1710      * releases, a "serialPersistentFields" field with a null value is
1711      * considered equivalent to not declaring "serialPersistentFields".  Throws
1712      * InvalidClassException if the declared serializable fields are
1713      * invalid--e.g., if multiple fields share the same name.
1714      */
getDeclaredSerialFields(Class<?> cl)1715     private static ObjectStreamField[] getDeclaredSerialFields(Class<?> cl)
1716         throws InvalidClassException
1717     {
1718         ObjectStreamField[] serialPersistentFields = null;
1719         try {
1720             Field f = cl.getDeclaredField("serialPersistentFields");
1721             int mask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL;
1722             if ((f.getModifiers() & mask) == mask) {
1723                 f.setAccessible(true);
1724                 serialPersistentFields = (ObjectStreamField[]) f.get(null);
1725             }
1726         } catch (Exception ex) {
1727         }
1728         if (serialPersistentFields == null) {
1729             return null;
1730         } else if (serialPersistentFields.length == 0) {
1731             return NO_FIELDS;
1732         }
1733 
1734         ObjectStreamField[] boundFields =
1735             new ObjectStreamField[serialPersistentFields.length];
1736         Set<String> fieldNames = new HashSet<>(serialPersistentFields.length);
1737 
1738         for (int i = 0; i < serialPersistentFields.length; i++) {
1739             ObjectStreamField spf = serialPersistentFields[i];
1740 
1741             String fname = spf.getName();
1742             if (fieldNames.contains(fname)) {
1743                 throw new InvalidClassException(
1744                     "multiple serializable fields named " + fname);
1745             }
1746             fieldNames.add(fname);
1747 
1748             try {
1749                 Field f = cl.getDeclaredField(fname);
1750                 if ((f.getType() == spf.getType()) &&
1751                     ((f.getModifiers() & Modifier.STATIC) == 0))
1752                 {
1753                     boundFields[i] =
1754                         new ObjectStreamField(f, spf.isUnshared(), true);
1755                 }
1756             } catch (NoSuchFieldException ex) {
1757             }
1758             if (boundFields[i] == null) {
1759                 boundFields[i] = new ObjectStreamField(
1760                     fname, spf.getType(), spf.isUnshared());
1761             }
1762         }
1763         return boundFields;
1764     }
1765 
1766     /**
1767      * Returns array of ObjectStreamFields corresponding to all non-static
1768      * non-transient fields declared by given class.  Each ObjectStreamField
1769      * contains a Field object for the field it represents.  If no default
1770      * serializable fields exist, NO_FIELDS is returned.
1771      */
getDefaultSerialFields(Class<?> cl)1772     private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1773         Field[] clFields = cl.getDeclaredFields();
1774         ArrayList<ObjectStreamField> list = new ArrayList<>();
1775         int mask = Modifier.STATIC | Modifier.TRANSIENT;
1776 
1777         for (int i = 0; i < clFields.length; i++) {
1778             if ((clFields[i].getModifiers() & mask) == 0) {
1779                 list.add(new ObjectStreamField(clFields[i], false, true));
1780             }
1781         }
1782         int size = list.size();
1783         return (size == 0) ? NO_FIELDS :
1784             list.toArray(new ObjectStreamField[size]);
1785     }
1786 
1787     /**
1788      * Returns explicit serial version UID value declared by given class, or
1789      * null if none.
1790      */
getDeclaredSUID(Class<?> cl)1791     private static Long getDeclaredSUID(Class<?> cl) {
1792         try {
1793             Field f = cl.getDeclaredField("serialVersionUID");
1794             int mask = Modifier.STATIC | Modifier.FINAL;
1795             if ((f.getModifiers() & mask) == mask) {
1796                 f.setAccessible(true);
1797                 return Long.valueOf(f.getLong(null));
1798             }
1799         } catch (Exception ex) {
1800         }
1801         return null;
1802     }
1803 
1804     /**
1805      * Computes the default serial version UID value for the given class.
1806      */
computeDefaultSUID(Class<?> cl)1807     private static long computeDefaultSUID(Class<?> cl) {
1808         if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
1809         {
1810             return 0L;
1811         }
1812 
1813         try {
1814             ByteArrayOutputStream bout = new ByteArrayOutputStream();
1815             DataOutputStream dout = new DataOutputStream(bout);
1816 
1817             dout.writeUTF(cl.getName());
1818 
1819             int classMods = cl.getModifiers() &
1820                 (Modifier.PUBLIC | Modifier.FINAL |
1821                  Modifier.INTERFACE | Modifier.ABSTRACT);
1822 
1823             /*
1824              * compensate for javac bug in which ABSTRACT bit was set for an
1825              * interface only if the interface declared methods
1826              */
1827             Method[] methods = cl.getDeclaredMethods();
1828             if ((classMods & Modifier.INTERFACE) != 0) {
1829                 classMods = (methods.length > 0) ?
1830                     (classMods | Modifier.ABSTRACT) :
1831                     (classMods & ~Modifier.ABSTRACT);
1832             }
1833             dout.writeInt(classMods);
1834 
1835             if (!cl.isArray()) {
1836                 /*
1837                  * compensate for change in 1.2FCS in which
1838                  * Class.getInterfaces() was modified to return Cloneable and
1839                  * Serializable for array classes.
1840                  */
1841                 Class<?>[] interfaces = cl.getInterfaces();
1842                 String[] ifaceNames = new String[interfaces.length];
1843                 for (int i = 0; i < interfaces.length; i++) {
1844                     ifaceNames[i] = interfaces[i].getName();
1845                 }
1846                 Arrays.sort(ifaceNames);
1847                 for (int i = 0; i < ifaceNames.length; i++) {
1848                     dout.writeUTF(ifaceNames[i]);
1849                 }
1850             }
1851 
1852             Field[] fields = cl.getDeclaredFields();
1853             MemberSignature[] fieldSigs = new MemberSignature[fields.length];
1854             for (int i = 0; i < fields.length; i++) {
1855                 fieldSigs[i] = new MemberSignature(fields[i]);
1856             }
1857             Arrays.sort(fieldSigs, new Comparator<MemberSignature>() {
1858                 public int compare(MemberSignature ms1, MemberSignature ms2) {
1859                     return ms1.name.compareTo(ms2.name);
1860                 }
1861             });
1862             for (int i = 0; i < fieldSigs.length; i++) {
1863                 MemberSignature sig = fieldSigs[i];
1864                 int mods = sig.member.getModifiers() &
1865                     (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
1866                      Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
1867                      Modifier.TRANSIENT);
1868                 if (((mods & Modifier.PRIVATE) == 0) ||
1869                     ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
1870                 {
1871                     dout.writeUTF(sig.name);
1872                     dout.writeInt(mods);
1873                     dout.writeUTF(sig.signature);
1874                 }
1875             }
1876 
1877             // BEGIN Android-changed: Fix/log clinit serialization workaround. b/29064453
1878             // Prior to SDK 24 hasStaticInitializer() would return true if the superclass had a
1879             // static initializer, that was contrary to the specification. In SDK 24 the default
1880             // behavior was corrected but the old behavior was preserved for apps that targeted 23
1881             // or below in order to maintain backwards compatibility.
1882             //
1883             // if (hasStaticInitializer(cl)) {
1884             boolean inheritStaticInitializer =
1885                 (VMRuntime.getRuntime().getTargetSdkVersion()
1886                 <= MAX_SDK_TARGET_FOR_CLINIT_UIDGEN_WORKAROUND);
1887             boolean warnIncompatibleSUIDChange = false;
1888             if (hasStaticInitializer(cl, inheritStaticInitializer)) {
1889                 // If a static initializer was found but the current class does not have one then
1890                 // the class's default SUID will change if the app targets SDK > 24 so send a
1891                 // warning.
1892                 if (inheritStaticInitializer && !hasStaticInitializer(cl, false)) {
1893                     // Defer until hash has been calculated so the warning message can give precise
1894                     // instructions to the developer on how to fix the problems.
1895                     warnIncompatibleSUIDChange = true;
1896                 }
1897             // END Android-changed: Fix/log clinit serialization workaround. b/29064453
1898                 dout.writeUTF("<clinit>");
1899                 dout.writeInt(Modifier.STATIC);
1900                 dout.writeUTF("()V");
1901             }
1902 
1903             Constructor<?>[] cons = cl.getDeclaredConstructors();
1904             MemberSignature[] consSigs = new MemberSignature[cons.length];
1905             for (int i = 0; i < cons.length; i++) {
1906                 consSigs[i] = new MemberSignature(cons[i]);
1907             }
1908             Arrays.sort(consSigs, new Comparator<MemberSignature>() {
1909                 public int compare(MemberSignature ms1, MemberSignature ms2) {
1910                     return ms1.signature.compareTo(ms2.signature);
1911                 }
1912             });
1913             for (int i = 0; i < consSigs.length; i++) {
1914                 MemberSignature sig = consSigs[i];
1915                 int mods = sig.member.getModifiers() &
1916                     (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
1917                      Modifier.STATIC | Modifier.FINAL |
1918                      Modifier.SYNCHRONIZED | Modifier.NATIVE |
1919                      Modifier.ABSTRACT | Modifier.STRICT);
1920                 if ((mods & Modifier.PRIVATE) == 0) {
1921                     dout.writeUTF("<init>");
1922                     dout.writeInt(mods);
1923                     dout.writeUTF(sig.signature.replace('/', '.'));
1924                 }
1925             }
1926 
1927             MemberSignature[] methSigs = new MemberSignature[methods.length];
1928             for (int i = 0; i < methods.length; i++) {
1929                 methSigs[i] = new MemberSignature(methods[i]);
1930             }
1931             Arrays.sort(methSigs, new Comparator<MemberSignature>() {
1932                 public int compare(MemberSignature ms1, MemberSignature ms2) {
1933                     int comp = ms1.name.compareTo(ms2.name);
1934                     if (comp == 0) {
1935                         comp = ms1.signature.compareTo(ms2.signature);
1936                     }
1937                     return comp;
1938                 }
1939             });
1940             for (int i = 0; i < methSigs.length; i++) {
1941                 MemberSignature sig = methSigs[i];
1942                 int mods = sig.member.getModifiers() &
1943                     (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
1944                      Modifier.STATIC | Modifier.FINAL |
1945                      Modifier.SYNCHRONIZED | Modifier.NATIVE |
1946                      Modifier.ABSTRACT | Modifier.STRICT);
1947                 if ((mods & Modifier.PRIVATE) == 0) {
1948                     dout.writeUTF(sig.name);
1949                     dout.writeInt(mods);
1950                     dout.writeUTF(sig.signature.replace('/', '.'));
1951                 }
1952             }
1953 
1954             dout.flush();
1955 
1956             MessageDigest md = MessageDigest.getInstance("SHA");
1957             byte[] hashBytes = md.digest(bout.toByteArray());
1958             long hash = 0;
1959             for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
1960                 hash = (hash << 8) | (hashBytes[i] & 0xFF);
1961             }
1962             // BEGIN Android-added: Fix/log clinit serialization workaround. b/29064453
1963             // ObjectStreamClass instances are cached per Class and caches its default
1964             // serialVersionUID so it will only log one message per class per app process
1965             // irrespective of the number of times the class is serialized.
1966             if (warnIncompatibleSUIDChange) {
1967                 suidCompatibilityListener.warnDefaultSUIDTargetVersionDependent(cl, hash);
1968             }
1969             // END Android-added: Fix/log clinit serialization workaround. b/29064453
1970             return hash;
1971         } catch (IOException ex) {
1972             throw new InternalError(ex);
1973         } catch (NoSuchAlgorithmException ex) {
1974             throw new SecurityException(ex.getMessage());
1975         }
1976     }
1977 
1978     // BEGIN Android-changed: Fix/log clinit serialization workaround. b/29064453
1979     /**
1980      * Created for testing as there is no nice way to detect when a message is logged.
1981      *
1982      * @hide
1983      */
1984     public interface DefaultSUIDCompatibilityListener {
1985         /**
1986          * Called when a class being serialized/deserialized relies on the default SUID computation
1987          * (because it has no explicit {@code serialVersionUID} field) where that computation is
1988          * dependent on the app's targetSdkVersion.
1989          *
1990          * @param clazz the clazz for which the default SUID is being computed.
1991          * @param hash the computed value.
1992          */
warnDefaultSUIDTargetVersionDependent(Class<?> clazz, long hash)1993         void warnDefaultSUIDTargetVersionDependent(Class<?> clazz, long hash);
1994     }
1995 
1996     /**
1997      * Public and mutable for testing purposes.
1998      *
1999      * @hide
2000      */
2001     public static DefaultSUIDCompatibilityListener suidCompatibilityListener =
2002         (clazz, hash) -> {
2003             System.logW("Class " + clazz.getCanonicalName() + " relies on its default SUID which"
2004                 + " is dependent on the app's targetSdkVersion. To avoid problems during upgrade"
2005                 + " add the following to class " + clazz.getCanonicalName() + "\n"
2006                 + "    private static final long serialVersionUID = " + hash + "L;");
2007         };
2008 
2009     /** Max SDK target version for which we use buggy hasStaticInitializer implementation. */
2010     static final int MAX_SDK_TARGET_FOR_CLINIT_UIDGEN_WORKAROUND = 23;
2011 
2012     /**
2013      * Returns true if the given class defines a static initializer method,
2014      * false otherwise.
2015      *
2016      * @param inheritStaticInitializer if false then this method will return true iff the given
2017      * class has its own static initializer, if true (used for backwards compatibility for apps
2018      * that target SDK version <= {@link #MAX_SDK_TARGET_FOR_CLINIT_UIDGEN_WORKAROUND}) it will
2019      * return true if the given class or any of its ancestor classes have a static initializer.
2020      */
hasStaticInitializer( Class<?> cl, boolean inheritStaticInitializer)2021     private native static boolean hasStaticInitializer(
2022         Class<?> cl, boolean inheritStaticInitializer);
2023     // END Android-changed: Fix/log clinit serialization workaround. b/29064453
2024 
2025     /**
2026      * Class for computing and caching field/constructor/method signatures
2027      * during serialVersionUID calculation.
2028      */
2029     private static class MemberSignature {
2030 
2031         public final Member member;
2032         public final String name;
2033         public final String signature;
2034 
MemberSignature(Field field)2035         public MemberSignature(Field field) {
2036             member = field;
2037             name = field.getName();
2038             signature = getClassSignature(field.getType());
2039         }
2040 
MemberSignature(Constructor<?> cons)2041         public MemberSignature(Constructor<?> cons) {
2042             member = cons;
2043             name = cons.getName();
2044             signature = getMethodSignature(
2045                 cons.getParameterTypes(), Void.TYPE);
2046         }
2047 
MemberSignature(Method meth)2048         public MemberSignature(Method meth) {
2049             member = meth;
2050             name = meth.getName();
2051             signature = getMethodSignature(
2052                 meth.getParameterTypes(), meth.getReturnType());
2053         }
2054     }
2055 
2056     /**
2057      * Class for setting and retrieving serializable field values in batch.
2058      */
2059     // REMIND: dynamically generate these?
2060     private static class FieldReflector {
2061 
2062         /** handle for performing unsafe operations */
2063         private static final Unsafe unsafe = Unsafe.getUnsafe();
2064 
2065         /** fields to operate on */
2066         private final ObjectStreamField[] fields;
2067         /** number of primitive fields */
2068         private final int numPrimFields;
2069         /** unsafe field keys for reading fields - may contain dupes */
2070         private final long[] readKeys;
2071         /** unsafe fields keys for writing fields - no dupes */
2072         private final long[] writeKeys;
2073         /** field data offsets */
2074         private final int[] offsets;
2075         /** field type codes */
2076         private final char[] typeCodes;
2077         /** field types */
2078         private final Class<?>[] types;
2079 
2080         /**
2081          * Constructs FieldReflector capable of setting/getting values from the
2082          * subset of fields whose ObjectStreamFields contain non-null
2083          * reflective Field objects.  ObjectStreamFields with null Fields are
2084          * treated as filler, for which get operations return default values
2085          * and set operations discard given values.
2086          */
FieldReflector(ObjectStreamField[] fields)2087         FieldReflector(ObjectStreamField[] fields) {
2088             this.fields = fields;
2089             int nfields = fields.length;
2090             readKeys = new long[nfields];
2091             writeKeys = new long[nfields];
2092             offsets = new int[nfields];
2093             typeCodes = new char[nfields];
2094             ArrayList<Class<?>> typeList = new ArrayList<>();
2095             Set<Long> usedKeys = new HashSet<>();
2096 
2097 
2098             for (int i = 0; i < nfields; i++) {
2099                 ObjectStreamField f = fields[i];
2100                 Field rf = f.getField();
2101                 long key = (rf != null) ?
2102                     unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
2103                 readKeys[i] = key;
2104                 writeKeys[i] = usedKeys.add(key) ?
2105                     key : Unsafe.INVALID_FIELD_OFFSET;
2106                 offsets[i] = f.getOffset();
2107                 typeCodes[i] = f.getTypeCode();
2108                 if (!f.isPrimitive()) {
2109                     typeList.add((rf != null) ? rf.getType() : null);
2110                 }
2111             }
2112 
2113             types = typeList.toArray(new Class<?>[typeList.size()]);
2114             numPrimFields = nfields - types.length;
2115         }
2116 
2117         /**
2118          * Returns list of ObjectStreamFields representing fields operated on
2119          * by this reflector.  The shared/unshared values and Field objects
2120          * contained by ObjectStreamFields in the list reflect their bindings
2121          * to locally defined serializable fields.
2122          */
getFields()2123         ObjectStreamField[] getFields() {
2124             return fields;
2125         }
2126 
2127         /**
2128          * Fetches the serializable primitive field values of object obj and
2129          * marshals them into byte array buf starting at offset 0.  The caller
2130          * is responsible for ensuring that obj is of the proper type.
2131          */
getPrimFieldValues(Object obj, byte[] buf)2132         void getPrimFieldValues(Object obj, byte[] buf) {
2133             if (obj == null) {
2134                 throw new NullPointerException();
2135             }
2136             /* assuming checkDefaultSerialize() has been called on the class
2137              * descriptor this FieldReflector was obtained from, no field keys
2138              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2139              */
2140             for (int i = 0; i < numPrimFields; i++) {
2141                 long key = readKeys[i];
2142                 int off = offsets[i];
2143                 switch (typeCodes[i]) {
2144                     case 'Z':
2145                         Bits.putBoolean(buf, off, unsafe.getBoolean(obj, key));
2146                         break;
2147 
2148                     case 'B':
2149                         buf[off] = unsafe.getByte(obj, key);
2150                         break;
2151 
2152                     case 'C':
2153                         Bits.putChar(buf, off, unsafe.getChar(obj, key));
2154                         break;
2155 
2156                     case 'S':
2157                         Bits.putShort(buf, off, unsafe.getShort(obj, key));
2158                         break;
2159 
2160                     case 'I':
2161                         Bits.putInt(buf, off, unsafe.getInt(obj, key));
2162                         break;
2163 
2164                     case 'F':
2165                         Bits.putFloat(buf, off, unsafe.getFloat(obj, key));
2166                         break;
2167 
2168                     case 'J':
2169                         Bits.putLong(buf, off, unsafe.getLong(obj, key));
2170                         break;
2171 
2172                     case 'D':
2173                         Bits.putDouble(buf, off, unsafe.getDouble(obj, key));
2174                         break;
2175 
2176                     default:
2177                         throw new InternalError();
2178                 }
2179             }
2180         }
2181 
2182         /**
2183          * Sets the serializable primitive fields of object obj using values
2184          * unmarshalled from byte array buf starting at offset 0.  The caller
2185          * is responsible for ensuring that obj is of the proper type.
2186          */
setPrimFieldValues(Object obj, byte[] buf)2187         void setPrimFieldValues(Object obj, byte[] buf) {
2188             if (obj == null) {
2189                 throw new NullPointerException();
2190             }
2191             for (int i = 0; i < numPrimFields; i++) {
2192                 long key = writeKeys[i];
2193                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2194                     continue;           // discard value
2195                 }
2196                 int off = offsets[i];
2197                 switch (typeCodes[i]) {
2198                     case 'Z':
2199                         unsafe.putBoolean(obj, key, Bits.getBoolean(buf, off));
2200                         break;
2201 
2202                     case 'B':
2203                         unsafe.putByte(obj, key, buf[off]);
2204                         break;
2205 
2206                     case 'C':
2207                         unsafe.putChar(obj, key, Bits.getChar(buf, off));
2208                         break;
2209 
2210                     case 'S':
2211                         unsafe.putShort(obj, key, Bits.getShort(buf, off));
2212                         break;
2213 
2214                     case 'I':
2215                         unsafe.putInt(obj, key, Bits.getInt(buf, off));
2216                         break;
2217 
2218                     case 'F':
2219                         unsafe.putFloat(obj, key, Bits.getFloat(buf, off));
2220                         break;
2221 
2222                     case 'J':
2223                         unsafe.putLong(obj, key, Bits.getLong(buf, off));
2224                         break;
2225 
2226                     case 'D':
2227                         unsafe.putDouble(obj, key, Bits.getDouble(buf, off));
2228                         break;
2229 
2230                     default:
2231                         throw new InternalError();
2232                 }
2233             }
2234         }
2235 
2236         /**
2237          * Fetches the serializable object field values of object obj and
2238          * stores them in array vals starting at offset 0.  The caller is
2239          * responsible for ensuring that obj is of the proper type.
2240          */
getObjFieldValues(Object obj, Object[] vals)2241         void getObjFieldValues(Object obj, Object[] vals) {
2242             if (obj == null) {
2243                 throw new NullPointerException();
2244             }
2245             /* assuming checkDefaultSerialize() has been called on the class
2246              * descriptor this FieldReflector was obtained from, no field keys
2247              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2248              */
2249             for (int i = numPrimFields; i < fields.length; i++) {
2250                 switch (typeCodes[i]) {
2251                     case 'L':
2252                     case '[':
2253                         vals[offsets[i]] = unsafe.getObject(obj, readKeys[i]);
2254                         break;
2255 
2256                     default:
2257                         throw new InternalError();
2258                 }
2259             }
2260         }
2261 
2262         /**
2263          * Sets the serializable object fields of object obj using values from
2264          * array vals starting at offset 0.  The caller is responsible for
2265          * ensuring that obj is of the proper type; however, attempts to set a
2266          * field with a value of the wrong type will trigger an appropriate
2267          * ClassCastException.
2268          */
setObjFieldValues(Object obj, Object[] vals)2269         void setObjFieldValues(Object obj, Object[] vals) {
2270             if (obj == null) {
2271                 throw new NullPointerException();
2272             }
2273             for (int i = numPrimFields; i < fields.length; i++) {
2274                 long key = writeKeys[i];
2275                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2276                     continue;           // discard value
2277                 }
2278                 switch (typeCodes[i]) {
2279                     case 'L':
2280                     case '[':
2281                         Object val = vals[offsets[i]];
2282                         if (val != null &&
2283                             !types[i - numPrimFields].isInstance(val))
2284                         {
2285                             Field f = fields[i].getField();
2286                             throw new ClassCastException(
2287                                 "cannot assign instance of " +
2288                                 val.getClass().getName() + " to field " +
2289                                 f.getDeclaringClass().getName() + "." +
2290                                 f.getName() + " of type " +
2291                                 f.getType().getName() + " in instance of " +
2292                                 obj.getClass().getName());
2293                         }
2294                         unsafe.putObject(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      */
getReflector(ObjectStreamField[] fields, ObjectStreamClass localDesc)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<Object>(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 
FieldReflectorKey(Class<?> cl, ObjectStreamField[] fields, ReferenceQueue<Class<?>> queue)2383         FieldReflectorKey(Class<?> cl, ObjectStreamField[] fields,
2384                           ReferenceQueue<Class<?>> queue)
2385         {
2386             super(cl, queue);
2387             nullClass = (cl == null);
2388             StringBuilder sbuf = new StringBuilder();
2389             for (int i = 0; i < fields.length; i++) {
2390                 ObjectStreamField f = fields[i];
2391                 sbuf.append(f.getName()).append(f.getSignature());
2392             }
2393             sigs = sbuf.toString();
2394             hash = System.identityHashCode(cl) + sigs.hashCode();
2395         }
2396 
hashCode()2397         public int hashCode() {
2398             return hash;
2399         }
2400 
equals(Object obj)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                     sigs.equals(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      */
matchFields(ObjectStreamField[] fields, ObjectStreamClass localDesc)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                 // Android-changed: We can have fields with a same name and a different type.
2454                 if (f.getName().equals(lf.getName()) &&
2455                     f.getSignature().equals(lf.getSignature())) {
2456                     if (lf.getField() != null) {
2457                         m = new ObjectStreamField(
2458                             lf.getField(), lf.isUnshared(), false);
2459                     } else {
2460                         m = new ObjectStreamField(
2461                             lf.getName(), lf.getSignature(), lf.isUnshared());
2462                     }
2463                 }
2464             }
2465             if (m == null) {
2466                 m = new ObjectStreamField(
2467                     f.getName(), f.getSignature(), false);
2468             }
2469             m.setOffset(f.getOffset());
2470             matches[i] = m;
2471         }
2472         return matches;
2473     }
2474     // BEGIN Android-added: Keep some private API for app compat. b/28283540.
2475     // NOTE: The following couple of methods are left here because frameworks such as objenesis
2476     // use them.
2477     //
2478     // **** THESE METHODS WILL BE REMOVED IN A FUTURE ANDROID RELEASE ****.
2479     //
getConstructorId(Class<?> clazz)2480     private static long getConstructorId(Class<?> clazz) {
2481         final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
2482         if (targetSdkVersion > 0 && targetSdkVersion <= 24) {
2483             System.logE("WARNING: ObjectStreamClass.getConstructorId(Class<?>) is private API and" +
2484                         "will be removed in a future Android release.");
2485             // NOTE: This method is a stub that returns a fixed value. It's meant to be used
2486             // with newInstance(Class<?>, long) and our current implementation of that method ignores
2487             // the "constructorId" argument. We return :
2488             //
2489             // oh one one eight nine nine nine
2490             // eight eight one nine nine
2491             // nine one one nine seven two five
2492             // three
2493             //
2494             // in all cases.
2495             return 1189998819991197253L;
2496         }
2497 
2498         throw new UnsupportedOperationException("ObjectStreamClass.getConstructorId(Class<?>) is " +
2499                                                 "not supported on SDK " + targetSdkVersion);
2500     }
newInstance(Class<?> clazz, long constructorId)2501     private static Object newInstance(Class<?> clazz, long constructorId) {
2502         final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
2503         if (targetSdkVersion > 0 && targetSdkVersion <= 24) {
2504             System.logE("WARNING: ObjectStreamClass.newInstance(Class<?>, long) is private API and" +
2505                         "will be removed in a future Android release.");
2506             return sun.misc.Unsafe.getUnsafe().allocateInstance(clazz);
2507         }
2508 
2509         throw new UnsupportedOperationException("ObjectStreamClass.newInstance(Class<?>, long) " +
2510                                                 "is not supported on SDK " + targetSdkVersion);
2511     }
2512     // END Android-added: Keep some private API for app compat. b/28283540.
2513 
2514     /**
2515      * Removes from the specified map any keys that have been enqueued
2516      * on the specified reference queue.
2517      */
processQueue(ReferenceQueue<Class<?>> queue, ConcurrentMap<? extends WeakReference<Class<?>>, ?> map)2518     static void processQueue(ReferenceQueue<Class<?>> queue,
2519                              ConcurrentMap<? extends
2520                              WeakReference<Class<?>>, ?> map)
2521     {
2522         Reference<? extends Class<?>> ref;
2523         while((ref = queue.poll()) != null) {
2524             map.remove(ref);
2525         }
2526     }
2527 
2528     /**
2529      *  Weak key for Class objects.
2530      *
2531      **/
2532     static class WeakClassKey extends WeakReference<Class<?>> {
2533         /**
2534          * saved value of the referent's identity hash code, to maintain
2535          * a consistent hash code after the referent has been cleared
2536          */
2537         private final int hash;
2538 
2539         /**
2540          * Create a new WeakClassKey to the given object, registered
2541          * with a queue.
2542          */
WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue)2543         WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
2544             super(cl, refQueue);
2545             hash = System.identityHashCode(cl);
2546         }
2547 
2548         /**
2549          * Returns the identity hash code of the original referent.
2550          */
hashCode()2551         public int hashCode() {
2552             return hash;
2553         }
2554 
2555         /**
2556          * Returns true if the given object is this identical
2557          * WeakClassKey instance, or, if this object's referent has not
2558          * been cleared, if the given object is another WeakClassKey
2559          * instance with the identical non-null referent as this one.
2560          */
equals(Object obj)2561         public boolean equals(Object obj) {
2562             if (obj == this) {
2563                 return true;
2564             }
2565 
2566             if (obj instanceof WeakClassKey) {
2567                 Object referent = get();
2568                 return (referent != null) &&
2569                        (referent == ((WeakClassKey) obj).get());
2570             } else {
2571                 return false;
2572             }
2573         }
2574     }
2575 
2576     /**
2577      * A LRA cache of record deserialization constructors.
2578      */
2579     @SuppressWarnings("serial")
2580     private static final class DeserializationConstructorsCache
2581             extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle>  {
2582 
2583         // keep max. 10 cached entries - when the 11th element is inserted the oldest
2584         // is removed and 10 remains - 11 is the biggest map size where internal
2585         // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2586         private static final int MAX_SIZE = 10;
2587         private Key.Impl first, last; // first and last in FIFO queue
2588 
DeserializationConstructorsCache()2589         DeserializationConstructorsCache() {
2590             // start small - if there is more than one shape of ObjectStreamClass
2591             // deserialized, there will typically be two (current version and previous version)
2592             super(2);
2593         }
2594 
get(ObjectStreamField[] fields)2595         MethodHandle get(ObjectStreamField[] fields) {
2596             return get(new Key.Lookup(fields));
2597         }
2598 
putIfAbsentAndGet(ObjectStreamField[] fields, MethodHandle mh)2599         synchronized MethodHandle putIfAbsentAndGet(ObjectStreamField[] fields, MethodHandle mh) {
2600             Key.Impl key = new Key.Impl(fields);
2601             var oldMh = putIfAbsent(key, mh);
2602             if (oldMh != null) return oldMh;
2603             // else we did insert new entry -> link the new key as last
2604             if (last == null) {
2605                 last = first = key;
2606             } else {
2607                 last = (last.next = key);
2608             }
2609             // may need to remove first
2610             if (size() > MAX_SIZE) {
2611                 assert first != null;
2612                 remove(first);
2613                 first = first.next;
2614                 if (first == null) {
2615                     last = null;
2616                 }
2617             }
2618             return mh;
2619         }
2620 
2621         // a key composed of ObjectStreamField[] names and types
2622         static abstract class Key {
length()2623             abstract int length();
fieldName(int i)2624             abstract String fieldName(int i);
fieldType(int i)2625             abstract Class<?> fieldType(int i);
2626 
2627             @Override
hashCode()2628             public final int hashCode() {
2629                 int n = length();
2630                 int h = 0;
2631                 for (int i = 0; i < n; i++) h = h * 31 + fieldType(i).hashCode();
2632                 for (int i = 0; i < n; i++) h = h * 31 + fieldName(i).hashCode();
2633                 return h;
2634             }
2635 
2636             @Override
equals(Object obj)2637             public final boolean equals(Object obj) {
2638                 if (!(obj instanceof Key other)) return false;
2639                 int n = length();
2640                 if (n != other.length()) return false;
2641                 for (int i = 0; i < n; i++) if (fieldType(i) != other.fieldType(i)) return false;
2642                 for (int i = 0; i < n; i++) if (!fieldName(i).equals(other.fieldName(i))) return false;
2643                 return true;
2644             }
2645 
2646             // lookup key - just wraps ObjectStreamField[]
2647             static final class Lookup extends Key {
2648                 final ObjectStreamField[] fields;
2649 
Lookup(ObjectStreamField[] fields)2650                 Lookup(ObjectStreamField[] fields) { this.fields = fields; }
2651 
2652                 @Override
length()2653                 int length() { return fields.length; }
2654 
2655                 @Override
fieldName(int i)2656                 String fieldName(int i) { return fields[i].getName(); }
2657 
2658                 @Override
fieldType(int i)2659                 Class<?> fieldType(int i) { return fields[i].getType(); }
2660             }
2661 
2662             // real key - copies field names and types and forms FIFO queue in cache
2663             static final class Impl extends Key {
2664                 Impl next;
2665                 final String[] fieldNames;
2666                 final Class<?>[] fieldTypes;
2667 
Impl(ObjectStreamField[] fields)2668                 Impl(ObjectStreamField[] fields) {
2669                     this.fieldNames = new String[fields.length];
2670                     this.fieldTypes = new Class<?>[fields.length];
2671                     for (int i = 0; i < fields.length; i++) {
2672                         fieldNames[i] = fields[i].getName();
2673                         fieldTypes[i] = fields[i].getType();
2674                     }
2675                 }
2676 
2677                 @Override
length()2678                 int length() { return fieldNames.length; }
2679 
2680                 @Override
fieldName(int i)2681                 String fieldName(int i) { return fieldNames[i]; }
2682 
2683                 @Override
fieldType(int i)2684                 Class<?> fieldType(int i) { return fieldTypes[i]; }
2685             }
2686         }
2687     }
2688 
2689     /** Record specific support for retrieving and binding stream field values. */
2690     static final class RecordSupport {
2691         /**
2692          * Returns canonical record constructor adapted to take two arguments:
2693          * {@code (byte[] primValues, Object[] objValues)}
2694          * and return
2695          * {@code Object}
2696          */
2697         @SuppressWarnings("removal")
deserializationCtr(ObjectStreamClass desc)2698         static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2699             // check the cached value 1st
2700             MethodHandle mh = desc.deserializationCtr;
2701             if (mh != null) return mh;
2702             mh = desc.deserializationCtrs.get(desc.getFields(false));
2703             if (mh != null) return desc.deserializationCtr = mh;
2704 
2705             // retrieve record components
2706             RecordComponent[] recordComponents;
2707             try {
2708                 Class<?> cls = desc.forClass();
2709                 PrivilegedExceptionAction<RecordComponent[]> pa = cls::getRecordComponents;
2710                 recordComponents = AccessController.doPrivileged(pa);
2711             } catch (PrivilegedActionException e) {
2712                 throw new InternalError(e.getCause());
2713             }
2714 
2715             // retrieve the canonical constructor
2716             // (T1, T2, ..., Tn):TR
2717             mh = desc.getRecordConstructor();
2718 
2719             // change return type to Object
2720             // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
2721             mh = mh.asType(mh.type().changeReturnType(Object.class));
2722 
2723             // drop last 2 arguments representing primValues and objValues arrays
2724             // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
2725             mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
2726 
2727             for (int i = recordComponents.length-1; i >= 0; i--) {
2728                 String name = recordComponents[i].getName();
2729                 Class<?> type = recordComponents[i].getType();
2730                 // obtain stream field extractor that extracts argument at
2731                 // position i (Ti+1) from primValues and objValues arrays
2732                 // (byte[], Object[]):Ti+1
2733                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2734                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2735                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2736                 mh = MethodHandles.foldArguments(mh, i, combiner);
2737             }
2738             // what we are left with is a MethodHandle taking just the primValues
2739             // and objValues arrays and returning the constructed record instance
2740             // (byte[], Object[]):Object
2741 
2742             // store it into cache and return the 1st value stored
2743             return desc.deserializationCtr =
2744                     desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2745         }
2746 
2747         /** Returns the number of primitive fields for the given descriptor. */
numberPrimValues(ObjectStreamClass desc)2748         private static int numberPrimValues(ObjectStreamClass desc) {
2749             ObjectStreamField[] fields = desc.getFields();
2750             int primValueCount = 0;
2751             for (int i = 0; i < fields.length; i++) {
2752                 if (fields[i].isPrimitive())
2753                     primValueCount++;
2754                 else
2755                     break;  // can be no more
2756             }
2757             return primValueCount;
2758         }
2759 
2760         /**
2761          * Returns extractor MethodHandle taking the primValues and objValues arrays
2762          * and extracting the argument of canonical constructor with given name and type
2763          * or producing  default value for the given type if the field is absent.
2764          */
streamFieldExtractor(String pName, Class<?> pType, ObjectStreamClass desc)2765         private static MethodHandle streamFieldExtractor(String pName,
2766                 Class<?> pType,
2767                 ObjectStreamClass desc) {
2768             ObjectStreamField[] fields = desc.getFields(false);
2769 
2770             for (int i = 0; i < fields.length; i++) {
2771                 ObjectStreamField f = fields[i];
2772                 String fName = f.getName();
2773                 if (!fName.equals(pName))
2774                     continue;
2775 
2776                 Class<?> fType = f.getField().getType();
2777                 if (!pType.isAssignableFrom(fType))
2778                     throw new InternalError(fName + " unassignable, pType:" + pType + ", fType:" + fType);
2779 
2780                 if (f.isPrimitive()) {
2781                     // (byte[], int):fType
2782                     MethodHandle mh = PRIM_VALUE_EXTRACTORS.get(fType);
2783                     if (mh == null) {
2784                         throw new InternalError("Unexpected type: " + fType);
2785                     }
2786                     // bind offset
2787                     // (byte[], int):fType -> (byte[]):fType
2788                     mh = MethodHandles.insertArguments(mh, 1, f.getOffset());
2789                     // drop objValues argument
2790                     // (byte[]):fType -> (byte[], Object[]):fType
2791                     mh = MethodHandles.dropArguments(mh, 1, Object[].class);
2792                     // adapt return type to pType
2793                     // (byte[], Object[]):fType -> (byte[], Object[]):pType
2794                     if (pType != fType) {
2795                         mh = mh.asType(mh.type().changeReturnType(pType));
2796                     }
2797                     return mh;
2798                 } else { // reference
2799                     // (Object[], int):Object
2800                     MethodHandle mh = MethodHandles.arrayElementGetter(Object[].class);
2801                     // bind index
2802                     // (Object[], int):Object -> (Object[]):Object
2803                     mh = MethodHandles.insertArguments(mh, 1, i - numberPrimValues(desc));
2804                     // drop primValues argument
2805                     // (Object[]):Object -> (byte[], Object[]):Object
2806                     mh = MethodHandles.dropArguments(mh, 0, byte[].class);
2807                     // adapt return type to pType
2808                     // (byte[], Object[]):Object -> (byte[], Object[]):pType
2809                     if (pType != Object.class) {
2810                         mh = mh.asType(mh.type().changeReturnType(pType));
2811                     }
2812                     return mh;
2813                 }
2814             }
2815 
2816             // return default value extractor if no field matches pName
2817             return MethodHandles.empty(MethodType.methodType(pType, byte[].class, Object[].class));
2818         }
2819 
2820         private static final Map<Class<?>, MethodHandle> PRIM_VALUE_EXTRACTORS;
2821         static {
2822             var lkp = MethodHandles.lookup();
2823             try {
2824                 PRIM_VALUE_EXTRACTORS = Map.of(
2825                         byte.class, MethodHandles.arrayElementGetter(byte[].class),
2826                         short.class, lkp.findStatic(Bits.class, "getShort", MethodType.methodType(short.class, byte[].class, int.class)),
2827                         int.class, lkp.findStatic(Bits.class, "getInt", MethodType.methodType(int.class, byte[].class, int.class)),
2828                         long.class, lkp.findStatic(Bits.class, "getLong", MethodType.methodType(long.class, byte[].class, int.class)),
2829                         float.class, lkp.findStatic(Bits.class, "getFloat", MethodType.methodType(float.class, byte[].class, int.class)),
2830                         double.class, lkp.findStatic(Bits.class, "getDouble", MethodType.methodType(double.class, byte[].class, int.class)),
2831                         char.class, lkp.findStatic(Bits.class, "getChar", MethodType.methodType(char.class, byte[].class, int.class)),
2832                         boolean.class, lkp.findStatic(Bits.class, "getBoolean", MethodType.methodType(boolean.class, byte[].class, int.class))
2833                 );
2834             } catch (NoSuchMethodException | IllegalAccessException e) {
2835                 throw new InternalError("Can't lookup Bits.getXXX", e);
2836             }
2837         }
2838     }
2839 }
2840