changeset 58691:0b704fd76583 records

records: matching of the serialVersionUID is waived for record classes
author chegar
date Fri, 15 Nov 2019 17:01:56 +0000
parents 9f59f84e8461
children f351827fecd8
files src/java.base/share/classes/java/io/ObjectStreamClass.java test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java
diffstat 2 files changed, 68 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/io/ObjectStreamClass.java	Fri Nov 15 12:54:34 2019 +0000
+++ b/src/java.base/share/classes/java/io/ObjectStreamClass.java	Fri Nov 15 17:01:56 2019 +0000
@@ -706,7 +706,7 @@
             }
 
             if (model.serializable == osc.serializable &&
-                    !cl.isArray() && isRecord(cl) &&
+                    !cl.isArray() && !isRecord(cl) &&
                     suid != osc.getSerialVersionUID()) {
                 throw new InvalidClassException(osc.name,
                         "local class incompatible: " +
--- a/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java	Fri Nov 15 12:54:34 2019 +0000
+++ b/test/jdk/java/io/Serializable/records/SerialVersionUIDTest.java	Fri Nov 15 17:01:56 2019 +0000
@@ -32,17 +32,20 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.LongStream;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
-import static java.io.ObjectStreamConstants.STREAM_MAGIC;
-import static java.io.ObjectStreamConstants.STREAM_VERSION;
-import static java.io.ObjectStreamConstants.TC_CLASSDESC;
-import static java.io.ObjectStreamConstants.TC_OBJECT;
+import static java.io.ObjectStreamConstants.*;
 import static java.lang.System.out;
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 public class SerialVersionUIDTest {
 
@@ -73,6 +76,9 @@
         };
     }
 
+    /**
+     * Tests that a declared SUID for a record class is inserted into the stream.
+     */
     @Test(dataProvider = "recordObjects")
     public void testSerialize(Object objectToSerialize, long expectedUID)
         throws Exception
@@ -95,8 +101,57 @@
         assertEquals(dis.readLong(), expectedUID);
     }
 
+    @DataProvider(name = "recordClasses")
+    public Object[][] recordClasses() {
+        List<Object[]> list = new ArrayList<>();
+        List<Class<?>> recordClasses = List.of(R1.class, R2.class, R3.class, R4.class, R5.class);
+        LongStream.of(0L, 1L, 100L, 10_000L, 1_000_000L).forEach(suid ->
+                recordClasses.stream()
+                             .map(cl -> new Object[] {cl, suid})
+                             .forEach(list::add));
+        return list.stream().toArray(Object[][]::new);
+    }
+
+    /**
+     * Tests that matching of the serialVersionUID values ( stream value
+     * and runtime class value ) is waived for record classes.
+     */
+    @Test(dataProvider = "recordClasses")
+    public void testSerializeFromClass(Class<? extends Record> cl, long suid)
+        throws Exception
+    {
+        out.println("\n---");
+        byte[] bytes = byteStreamFor(cl.getName(), suid);
+        Object obj = deserialize(bytes);
+        assertEquals(obj.getClass(), cl);
+        assertTrue(obj.getClass().isRecord());
+    }
+
     // --- infra
 
+    /**
+     * Returns a stream of bytes for the given class and uid. The
+     * stream will have no stream field values.
+     */
+    static byte[] byteStreamFor(String className, long uid)
+        throws Exception
+    {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream dos = new DataOutputStream(baos);
+        dos.writeShort(STREAM_MAGIC);
+        dos.writeShort(STREAM_VERSION);
+        dos.writeByte(TC_OBJECT);
+        dos.writeByte(TC_CLASSDESC);
+        dos.writeUTF(className);
+        dos.writeLong(uid);
+        dos.writeByte(SC_SERIALIZABLE);
+        dos.writeShort(0);                // number of fields
+        dos.writeByte(TC_ENDBLOCKDATA);   // no annotations
+        dos.writeByte(TC_NULL);           // no superclasses
+        dos.close();
+        return baos.toByteArray();
+    }
+
     static <T> byte[] serialize(T obj) throws IOException {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(baos);
@@ -104,4 +159,12 @@
         oos.close();
         return baos.toByteArray();
     }
+
+    static <T> T deserialize(byte[] streamBytes)
+        throws IOException, ClassNotFoundException
+    {
+        ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes);
+        ObjectInputStream ois  = new ObjectInputStream(bais);
+        return (T) ois.readObject();
+    }
 }