From 2286646981811c818e7da61a806426d38481218b Mon Sep 17 00:00:00 2001
Date: Wed, 4 Jan 2023 20:45:23 +0800
Subject: Fast Serializer

---
 src/hotspot/share/prims/unsafe.cpp            |   5 +
 src/hotspot/share/runtime/globals.hpp         |   4 +
 .../classes/java/io/ObjectInputStream.java    | 222 +++++++++++++++---
 .../classes/java/io/ObjectOutputStream.java   |  83 ++++++-
 .../classes/java/io/ObjectStreamClass.java    |  81 +++++++
 .../classes/jdk/internal/misc/Unsafe.java     |   2 +-
 6 files changed, 355 insertions(+), 42 deletions(-)

diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp
index ee6c218f5..97f36a2db 100644
--- a/src/hotspot/share/prims/unsafe.cpp
+++ b/src/hotspot/share/prims/unsafe.cpp
@@ -870,6 +870,10 @@ UNSAFE_ENTRY(jint, Unsafe_GetLoadAverage0(JNIEnv *env, jobject unsafe, jdoubleAr
   return ret;
 } UNSAFE_END
 
+UNSAFE_ENTRY(jboolean, Unsafe_GetUseFastSerializer(JNIEnv *env, jobject unsafe)) {
+  return UseFastSerializer;
+}
+UNSAFE_END
 
 /// JVM_RegisterUnsafeMethods
 
@@ -951,6 +955,7 @@ static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = {
     {CC "loadFence",          CC "()V",                  FN_PTR(Unsafe_LoadFence)},
     {CC "storeFence",         CC "()V",                  FN_PTR(Unsafe_StoreFence)},
     {CC "fullFence",          CC "()V",                  FN_PTR(Unsafe_FullFence)},
+    {CC "getUseFastSerializer",   CC "()Z",              FN_PTR(Unsafe_GetUseFastSerializer)},
 };
 
 #undef CC
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 8c61e6a05..6852f1ac3 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2071,6 +2071,10 @@ const intx ObjectAlignmentInBytes = 8;
   JFR_ONLY(product(ccstr, StartFlightRecording, NULL,                       \
           "Start flight recording with options"))                           \
                                                                             \
+  product(bool, UseFastSerializer, false, EXPERIMENTAL,                     \
+          "Cache-based serialization.It is extremely fast, but it"          \
+          "can only be effective in certain scenarios.")                    \
+                                                                            \
   product(bool, UseFastUnorderedTimeStamps, false, EXPERIMENTAL,            \
           "Use platform unstable time where supported for timestamps only") \
                                                                             \
diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java
index b3e063b3d..1cf3a38c8 100644
--- a/src/java.base/share/classes/java/io/ObjectInputStream.java
+++ b/src/java.base/share/classes/java/io/ObjectInputStream.java
@@ -41,6 +41,7 @@ import java.security.PrivilegedExceptionAction;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.event.DeserializationEvent;
@@ -312,6 +313,23 @@ public class ObjectInputStream
             filterLogger = (filterLog.isLoggable(Logger.Level.DEBUG)
                     || filterLog.isLoggable(Logger.Level.TRACE)) ? filterLog : null;
         }
+
+        /*
+         * Logger for FastSerializer.
+         * Setup the FastSerializer logger if it is set to DEBUG.
+         * (Assuming it will not change).
+         */
+        static final System.Logger fastSerLogger;
+
+        static {
+            if (printFastSerializer) {
+                Logger fastSerLog = System.getLogger("fastSerializer");
+                fastSerLogger = (fastSerLog.isLoggable(Logger.Level.DEBUG))
+                        ? fastSerLog : null;
+            } else {
+                fastSerLogger = null;
+            }
+        }
     }
 
     /** filter stream for handling block data conversion */
@@ -337,6 +355,9 @@ public class ObjectInputStream
     /** if true, invoke resolveObject() */
     private boolean enableResolve;
 
+    /** Used to get the commandline option: useFastSerializer */
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
     /**
      * Context during upcalls to class-defined readObject methods; holds
      * object currently being deserialized and descriptor for current class.
@@ -350,6 +371,40 @@ public class ObjectInputStream
      */
     private ObjectInputFilter serialFilter;
 
+    /**
+     * value of "useFastSerializer" property
+     */
+    private static final boolean defaultFastSerializer = UNSAFE.getUseFastSerializer();
+
+    /**
+     *  true or false for open FastSerilizer
+     *  May be changed in readStreamHeader
+     */
+    private boolean useFastSerializer = defaultFastSerializer;
+
+    /**
+     * Value of "fastSerializerEscapeMode" property. It can be turned on
+     * when useFastSerializer is true.
+     */
+    @SuppressWarnings("removal")
+    private static final boolean fastSerializerEscapeMode = java.security.AccessController.doPrivileged(
+            new sun.security.action.GetBooleanAction(
+                    "fastSerializerEscapeMode")).booleanValue();
+
+    /**
+     * Magic number that is written to the stream header when using fastserilizer.
+     */
+    private static final short STREAM_MAGIC_FAST = (short)0xdeca;
+
+    /**
+     *  value of  "printFastSerializer" property,
+     *  as true or false for printing FastSerializer logs.
+     */
+    @SuppressWarnings("removal")
+    private static final boolean printFastSerializer = java.security.AccessController.doPrivileged(
+            new sun.security.action.GetBooleanAction(
+                    "printFastSerializer")).booleanValue();
+
     /**
      * True if the stream-specific filter has been set; initially false.
      */
@@ -437,6 +492,9 @@ public class ObjectInputStream
      * transitively so that a complete equivalent graph of objects is
      * reconstructed by readObject.
      *
+     * The difference between fastSerialzation and default serialization is the
+     * descriptor serialization. The data serialization is same with each other.
+     *
      * <p>The root object is completely restored when all of its fields and the
      * objects it references are completely restored.  At this point the object
      * validation callbacks are executed in order based on their registered
@@ -726,11 +784,20 @@ public class ObjectInputStream
         vlist.register(obj, prio);
     }
 
+    /**
+     * Cache the class meta during serialization.
+     * Only used in FastSerilizer.
+     */
+    private static ConcurrentHashMap<String,Class<?>> nameToClass = new ConcurrentHashMap<>();
+
     /**
      * Load the local class equivalent of the specified stream class
      * description.  Subclasses may implement this method to allow classes to
      * be fetched from an alternate source.
      *
+     * When fastSerializer is turned on, fields of desc will be null except
+     * name. When resolveClass is override, this may cause null pointer exception.
+     *
      * <p>The corresponding method in {@code ObjectOutputStream} is
      * {@code annotateClass}.  This method will be invoked only once for
      * each unique class in the stream.  This method can be implemented by
@@ -769,16 +836,29 @@ public class ObjectInputStream
         throws IOException, ClassNotFoundException
     {
         String name = desc.getName();
-        try {
-            return Class.forName(name, false, latestUserDefinedLoader());
-        } catch (ClassNotFoundException ex) {
-            Class<?> cl = primClasses.get(name);
+        Class<?> cl = null;
+
+        if (useFastSerializer) {
+            cl = nameToClass.get(name);
             if (cl != null) {
                 return cl;
-            } else {
+            }
+        }
+
+        try {
+            cl = Class.forName(name, false, latestUserDefinedLoader());
+        } catch (ClassNotFoundException ex) {
+            cl = primClasses.get(name);
+            if (cl == null) {
                 throw ex;
             }
         }
+
+        if (useFastSerializer) {
+            nameToClass.put(name, cl);
+        }
+
+        return cl;
     }
 
     /**
@@ -953,9 +1033,33 @@ public class ObjectInputStream
     {
         short s0 = bin.readShort();
         short s1 = bin.readShort();
-        if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) {
-            throw new StreamCorruptedException(
-                String.format("invalid stream header: %04X%04X", s0, s1));
+        if (useFastSerializer) {
+            if (s0 != STREAM_MAGIC_FAST || s1 != STREAM_VERSION) {
+                if (s0 != STREAM_MAGIC) {
+                    throw new StreamCorruptedException(
+                            String.format("invalid stream header: %04X%04X, and FastSerializer is activated", s0, s1));
+                }
+
+                if (!fastSerializerEscapeMode) {
+                    throw new StreamCorruptedException(
+                            String.format("invalid stream header: %04X%04X.Fast serialization does not support " +
+                                          "original serialized files", s0, s1));
+                }
+
+                // Escape to default serialization
+                useFastSerializer = false;
+                if (Logging.fastSerLogger != null) {
+                    Logging.fastSerLogger.log(Logger.Level.DEBUG, "[Deserialize]: Escape and disable FastSerializer");
+                }
+            }
+        } else if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) {
+            if (s0 == STREAM_MAGIC_FAST && s1 == STREAM_VERSION) {
+                throw new StreamCorruptedException(
+                        String.format("invalid stream header: %04X%04X, and it is a FastSerializer stream", s0, s1));
+            } else {
+                throw new StreamCorruptedException(
+                        String.format("invalid stream header: %04X%04X", s0, s1));
+            }
         }
     }
 
@@ -969,6 +1073,11 @@ public class ObjectInputStream
      * this method reads class descriptors according to the format defined in
      * the Object Serialization specification.
      *
+     * In fastSerialize mode, the descriptor is obtained by lookup method. And
+     * the resolveClass method is called here to get the classmeta. Since the
+     * descriptor is obtained by lookup, the descriptor is same as localdesc.
+     * So we cann't distinguish the receiver desc and local desc.
+     *
      * @return  the class descriptor read
      * @throws  IOException If an I/O error has occurred.
      * @throws  ClassNotFoundException If the Class of a serialized object used
@@ -979,6 +1088,29 @@ public class ObjectInputStream
     protected ObjectStreamClass readClassDescriptor()
         throws IOException, ClassNotFoundException
     {
+        // fastSerializer
+        if (useFastSerializer) {
+            String name = readUTF();
+            Class<?> cl = null;
+            ObjectStreamClass desc = new ObjectStreamClass(name);
+            try {
+                // In order to match this method, we add an annotateClass method in
+                // writeClassDescriptor.
+                cl = resolveClass(desc);
+            } catch (ClassNotFoundException ex) {
+                // resolveClass is just used to obtain Class which required by lookup method
+                // and it will be called again later, so we don't throw ClassNotFoundException here.
+                return desc;
+            }
+            if (cl != null) {
+                // This desc is localDesc. It may be different from the descriptor
+                // obtained from the stream.
+                desc = ObjectStreamClass.lookup(cl, true);
+            }
+            return desc;
+        }
+
+        // Default deserialization. If the Class cannot be found, throw ClassNotFoundException.
         ObjectStreamClass desc = new ObjectStreamClass();
         desc.readNonProxy(this);
         return desc;
@@ -2045,41 +2177,63 @@ public class ObjectInputStream
 
         skipCustomData();
 
-        try {
-            totalObjectRefs++;
-            depth++;
-            desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
+        totalObjectRefs++;
+        depth++;
 
-            if (cl != null) {
-                // Check that serial filtering has been done on the local class descriptor's superclass,
-                // in case it does not appear in the stream.
-
-                // Find the next super descriptor that has a local class descriptor.
-                // Descriptors for which there is no local class are ignored.
-                ObjectStreamClass superLocal = null;
-                for (ObjectStreamClass sDesc = desc.getSuperDesc(); sDesc != null; sDesc = sDesc.getSuperDesc()) {
-                    if ((superLocal = sDesc.getLocalDesc()) != null) {
-                        break;
+        if (useFastSerializer) {
+            desc.initNonProxyFast(readDesc, resolveEx);
+            ObjectStreamClass superDesc = desc.getSuperDesc();
+            long originDepth = depth - 1;
+            // Since desc is obtained from the lookup method, we will lose the depth and
+            // totalObjectRefs of superDesc. So we add a loop here to compute the depth
+            // and objectRef of superDesc.
+            while (superDesc != null && superDesc.forClass() != null) {
+                filterCheck(superDesc.forClass(), -1);
+                superDesc = superDesc.getSuperDesc();
+                totalObjectRefs++;
+                depth++;
+            }
+            depth = originDepth;
+        } else {
+            try {
+                desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
+
+                if (cl != null) {
+                    // Check that serial filtering has been done on the local class descriptor's superclass,
+                    // in case it does not appear in the stream.
+                    // Find the next super descriptor that has a local class descriptor.
+                    // Descriptors for which there is no local class are ignored.
+                    ObjectStreamClass superLocal = null;
+                    for (ObjectStreamClass sDesc = desc.getSuperDesc(); sDesc != null; sDesc = sDesc.getSuperDesc()) {
+                        if ((superLocal = sDesc.getLocalDesc()) != null) {
+                            break;
+                        }
                     }
-                }
 
-                // Scan local descriptor superclasses for a match with the local descriptor of the super found above.
-                // For each super descriptor before the match, invoke the serial filter on the class.
-                // The filter is invoked for each class that has not already been filtered
-                // but would be filtered if the instance had been serialized by this Java runtime.
-                for (ObjectStreamClass lDesc = desc.getLocalDesc().getSuperDesc();
-                     lDesc != null && lDesc != superLocal;
-                     lDesc = lDesc.getSuperDesc()) {
-                    filterCheck(lDesc.forClass(), -1);
+                    // Scan local descriptor superclasses for a match with the local descriptor of the super found above.
+                    // For each super descriptor before the match, invoke the serial filter on the class.
+                    // The filter is invoked for each class that has not already been filtered
+                    // but would be filtered if the instance had been serialized by this Java runtime.
+                    for (ObjectStreamClass lDesc = desc.getLocalDesc().getSuperDesc();
+                         lDesc != null && lDesc != superLocal;
+                         lDesc = lDesc.getSuperDesc()) {
+                        filterCheck(lDesc.forClass(), -1);
+                    }
                 }
+            } finally {
+                depth--;
             }
-        } finally {
-            depth--;
         }
 
         handles.finish(descHandle);
         passHandle = descHandle;
 
+        if (Logging.fastSerLogger != null) {
+            Logging.fastSerLogger.log(Logger.Level.DEBUG,
+                "[Deserialize] useFastSerializer:{0}, Class name:{1}, SerialVersionUID:{2}, flags:{3}",
+                useFastSerializer, desc.getName(), desc.getSerialVersionUID(), desc.getFlags(this));
+        }
+
         return desc;
     }
 
@@ -2946,8 +3100,6 @@ public class ObjectInputStream
         }
     }
 
-    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
-
     /**
      * Performs a "freeze" action, required to adhere to final field semantics.
      *
diff --git a/src/java.base/share/classes/java/io/ObjectOutputStream.java b/src/java.base/share/classes/java/io/ObjectOutputStream.java
index be52df020..f855d6ee4 100644
--- a/src/java.base/share/classes/java/io/ObjectOutputStream.java
+++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java
@@ -31,6 +31,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.StringJoiner;
+import jdk.internal.misc.Unsafe;
 import sun.reflect.misc.ReflectUtil;
 
 /**
@@ -173,6 +174,25 @@ public class ObjectOutputStream
             };
     }
 
+    private static class Logging {
+        /*
+         * Logger for FastSerializer.
+         * Setup the FastSerializer logger if it is set to DEBUG.
+         * (Assuming it will not change).
+         */
+        static final System.Logger fastSerLogger;
+
+        static {
+            if (printFastSerializer) {
+                System.Logger fastSerLog = System.getLogger("fastSerializer");
+                fastSerLogger = (fastSerLog.isLoggable(System.Logger.Level.DEBUG))
+                        ? fastSerLog : null;
+            } else {
+                fastSerLogger = null;
+            }
+        }
+    }
+
     /** filter stream for handling block data conversion */
     private final BlockDataOutputStream bout;
     /** obj -> wire handle map */
@@ -191,7 +211,6 @@ public class ObjectOutputStream
     private final boolean enableOverride;
     /** if true, invoke replaceObject() */
     private boolean enableReplace;
-
     // values below valid only during upcalls to writeObject()/writeExternal()
     /**
      * Context during upcalls to class-defined writeObject methods; holds
@@ -215,6 +234,28 @@ public class ObjectOutputStream
             new sun.security.action.GetBooleanAction(
                 "sun.io.serialization.extendedDebugInfo")).booleanValue();
 
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+    /**
+     * Value of "UseFastSerializer" property, The fastSerializer is turned
+     * on when it is true.
+     */
+    private static final boolean useFastSerializer = UNSAFE.getUseFastSerializer();
+
+    /**
+     *  value of  "printFastSerializer" property,
+     *  as true or false for printing FastSerializer logs.
+     */
+    @SuppressWarnings("removal")
+    private static final boolean printFastSerializer = java.security.AccessController.doPrivileged(
+            new sun.security.action.GetBooleanAction(
+                    "printFastSerializer")).booleanValue();
+
+    /**
+     * Magic number that is written to the stream header when using fastserilizer.
+     */
+    private static final short STREAM_MAGIC_FAST = (short)0xdeca;
+
     /**
      * Creates an ObjectOutputStream that writes to the specified OutputStream.
      * This constructor writes the serialization stream header to the
@@ -329,6 +370,9 @@ public class ObjectOutputStream
      * object are written transitively so that a complete equivalent graph of
      * objects can be reconstructed by an ObjectInputStream.
      *
+     * The difference between fastSerialzation and default serialization is the
+     * descriptor serialization. The data serialization is same with each other.
+     *
      * <p>Exceptions are thrown for problems with the OutputStream and for
      * classes that should not be serialized.  All exceptions are fatal to the
      * OutputStream, which is left in an indeterminate state, and it is up to
@@ -638,7 +682,11 @@ public class ObjectOutputStream
      *          stream
      */
     protected void writeStreamHeader() throws IOException {
-        bout.writeShort(STREAM_MAGIC);
+        if (useFastSerializer) {
+            bout.writeShort(STREAM_MAGIC_FAST);
+        } else {
+            bout.writeShort(STREAM_MAGIC);
+        }
         bout.writeShort(STREAM_VERSION);
     }
 
@@ -653,6 +701,9 @@ public class ObjectOutputStream
      * By default, this method writes class descriptors according to the format
      * defined in the Object Serialization specification.
      *
+     * In fastSerializer mode, we will only write the classname to the stream.
+     * The annotateClass is used to match the resolveClass in readClassDescriptor.
+     *
      * <p>Note that this method will only be called if the ObjectOutputStream
      * is not using the old serialization stream format (set by calling
      * ObjectOutputStream's {@code useProtocolVersion} method).  If this
@@ -670,7 +721,14 @@ public class ObjectOutputStream
     protected void writeClassDescriptor(ObjectStreamClass desc)
         throws IOException
     {
-        desc.writeNonProxy(this);
+        if (useFastSerializer) {
+            writeUTF(desc.getName());
+            // The annotateClass is used to match the resolveClass called in
+            // readClassDescriptor.
+            annotateClass(desc.forClass());
+        } else {
+            desc.writeNonProxy(this);
+        }
     }
 
     /**
@@ -1278,9 +1336,21 @@ public class ObjectOutputStream
         bout.writeByte(TC_CLASSDESC);
         handles.assign(unshared ? null : desc);
 
+        if (Logging.fastSerLogger != null) {
+            Logging.fastSerLogger.log(System.Logger.Level.DEBUG,
+            "[Serialize]   useFastSerializer:{0}, Class name:{1}, SerialVersionUID:{2}, flags:{3}, protocol:{4}",
+            useFastSerializer, desc.getName(), desc.getSerialVersionUID(), desc.getFlags(this), protocol);
+        }
+
         if (protocol == PROTOCOL_VERSION_1) {
             // do not invoke class descriptor write hook with old protocol
-            desc.writeNonProxy(this);
+            if (useFastSerializer) {
+                // only write name and annotate class when using FastSerializer
+                writeUTF(desc.getName());
+                annotateClass(desc.forClass());
+            } else {
+                desc.writeNonProxy(this);
+            }
         } else {
             writeClassDescriptor(desc);
         }
@@ -1293,8 +1363,9 @@ public class ObjectOutputStream
         annotateClass(cl);
         bout.setBlockDataMode(false);
         bout.writeByte(TC_ENDBLOCKDATA);
-
-        writeClassDesc(desc.getSuperDesc(), false);
+        if (!useFastSerializer) {
+            writeClassDesc(desc.getSuperDesc(), false);
+        }
     }
 
     /**
diff --git a/src/java.base/share/classes/java/io/ObjectStreamClass.java b/src/java.base/share/classes/java/io/ObjectStreamClass.java
index afb3d8447..08488d19c 100644
--- a/src/java.base/share/classes/java/io/ObjectStreamClass.java
+++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java
@@ -295,6 +295,40 @@ public class ObjectStreamClass implements Serializable {
         return suid.longValue();
     }
 
+    /**
+     * Return the flags for this class described by this descriptor. The flags
+     * means a set of bit masks for ObjectStreamClass, which indicate the status
+     * of SC_WRITE_METHOD, SC_SERIALIZABLE, SC_EXTERNALIZABLE, SC_BLOCK_DATA and
+     * SC_ENUM.
+     *
+     * @param serialStream ObjectOutputStream or ObjectInputStream
+     *
+     * @return the flags for this class described by this descriptor
+     */
+    byte getFlags(Object serialStream) {
+        byte flags = 0;
+        if (externalizable) {
+            flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
+            if (serialStream instanceof ObjectOutputStream) {
+                int protocol = ((ObjectOutputStream)serialStream).getProtocolVersion();
+                if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
+                    flags |= ObjectStreamConstants.SC_BLOCK_DATA;
+                }
+            } else if (serialStream instanceof ObjectInputStream) {
+                flags |= ObjectStreamConstants.SC_BLOCK_DATA;
+            }
+        } else if (serializable) {
+            flags |= ObjectStreamConstants.SC_SERIALIZABLE;
+        }
+        if (hasWriteObjectData) {
+            flags |= ObjectStreamConstants.SC_WRITE_METHOD;
+        }
+        if (isEnum) {
+            flags |= ObjectStreamConstants.SC_ENUM;
+        }
+        return flags;
+    }
+
     /**
      * Return the class in the local VM that this version is mapped to.  Null
      * is returned if there is no corresponding local class.
@@ -467,6 +501,15 @@ public class ObjectStreamClass implements Serializable {
     ObjectStreamClass() {
     }
 
+    /**
+     * Create a blank class descriptor with name. It is only used
+     * in fastSerialize path.
+     * @param name  class name
+     */
+    ObjectStreamClass(String name) {
+        this.name  = name;
+    }
+
     /**
      * Creates a PermissionDomain that grants no permission.
      */
@@ -661,6 +704,44 @@ public class ObjectStreamClass implements Serializable {
         initialized = true;
     }
 
+    /**
+     * Initializes class descriptor representing a non-proxy class.
+     * Used in fast serialization mode.
+     */
+    void initNonProxyFast(ObjectStreamClass model,
+                      ClassNotFoundException resolveEx)
+    {
+        this.cl = model.cl;
+        this.resolveEx = resolveEx;
+        this.superDesc = model.superDesc;
+        name = model.name;
+        this.suid = model.suid;
+        isProxy = false;
+        isEnum = model.isEnum;
+        serializable = model.serializable;
+        externalizable = model.externalizable;
+        hasBlockExternalData = model.hasBlockExternalData;
+        hasWriteObjectData = model.hasWriteObjectData;
+        fields = model.fields;
+        primDataSize = model.primDataSize;
+        numObjFields = model.numObjFields;
+
+        writeObjectMethod = model.writeObjectMethod;
+        readObjectMethod = model.readObjectMethod;
+        readObjectNoDataMethod = model.readObjectNoDataMethod;
+        writeReplaceMethod = model.writeReplaceMethod;
+        readResolveMethod = model.readResolveMethod;
+        if (deserializeEx == null) {
+            deserializeEx = model.deserializeEx;
+        }
+        domains = model.domains;
+        cons = model.cons;
+        fieldRefl = model.fieldRefl;
+        localDesc = model;
+
+        initialized = true;
+    }
+
     /**
      * Reads non-proxy class descriptor information from given input stream.
      * The resulting class descriptor is not fully functional; it can only be
diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
index 22aa09c9d..b6e7978a5 100644
--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
+++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
@@ -3811,7 +3811,7 @@ public final class Unsafe {
     private static long convEndian(boolean big, long n)   { return big == BIG_ENDIAN ? n : Long.reverseBytes(n)     ; }
 
 
-
+    public native boolean getUseFastSerializer();
     private native long allocateMemory0(long bytes);
     private native long reallocateMemory0(long address, long bytes);
     private native void freeMemory0(long address);
-- 
2.37.0

