From 370aad59b30497c76914c32b0801142213994891 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Wed, 26 Aug 2020 11:03:55 +0100 Subject: [PATCH 01/20] add test and serializer skeleton and update pom --- pom.xml | 83 +++++++++++++++++++ .../kryo/serializers/RecordSerializer.java | 38 +++++++++ test/jdk14/RecordSerializerTest.java | 39 +++++++++ 3 files changed, 160 insertions(+) create mode 100644 src/com/esotericsoftware/kryo/serializers/RecordSerializer.java create mode 100644 test/jdk14/RecordSerializerTest.java diff --git a/pom.xml b/pom.xml index 4f450cf94..0321daafb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -173,6 +194,27 @@ + + + jdk8+ + + [1.8,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + true + + + jdk14/** + + + + + + until-java11 @@ -196,6 +238,47 @@ + + + jdk14+ + + [14,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + true + + ${java.vm.specification.version} + + --enable-preview + + + jdk14/** + + + com/** + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + + + jdk14/** + + --enable-preview + + + + + + + diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java new file mode 100644 index 000000000..e89572b14 --- /dev/null +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -0,0 +1,38 @@ +/* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +package com.esotericsoftware.kryo.serializers; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +public class RecordSerializer extends Serializer { + @Override + public void write(Kryo kryo, Output output, T object) { + // + } + + @Override + public T read(Kryo kryo, Input input, Class type) { + return null; + } +} \ No newline at end of file diff --git a/test/jdk14/RecordSerializerTest.java b/test/jdk14/RecordSerializerTest.java new file mode 100644 index 000000000..991bef058 --- /dev/null +++ b/test/jdk14/RecordSerializerTest.java @@ -0,0 +1,39 @@ +/* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +package jdk14; + +import com.esotericsoftware.kryo.KryoTestCase; + +import org.junit.Test; + +/** @author Nathan Sweet */ +public class RecordSerializerTest extends KryoTestCase { + { + supportsCopy = false; + } + + @Test + public void testRecordSerializer() { + // todo + } + + public record PointRecord(int x, int y) { } +} From 57822d23c0cffc280b1498009ee8d08bf3011cd1 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Tue, 1 Sep 2020 10:02:28 +0100 Subject: [PATCH 02/20] add record serializer and test --- src/com/esotericsoftware/kryo/Kryo.java | 9 + .../kryo/serializers/RecordSerializer.java | 186 +++++++++++- test/jdk14/RecordSerializerTest.java | 286 +++++++++++++++++- 3 files changed, 472 insertions(+), 9 deletions(-) diff --git a/src/com/esotericsoftware/kryo/Kryo.java b/src/com/esotericsoftware/kryo/Kryo.java index e6450730e..bc41f2706 100644 --- a/src/com/esotericsoftware/kryo/Kryo.java +++ b/src/com/esotericsoftware/kryo/Kryo.java @@ -1,4 +1,5 @@ /* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -80,6 +81,7 @@ import com.esotericsoftware.kryo.serializers.ImmutableCollectionsSerializers; import com.esotericsoftware.kryo.serializers.MapSerializer; import com.esotericsoftware.kryo.serializers.OptionalSerializers; +import com.esotericsoftware.kryo.serializers.RecordSerializer; import com.esotericsoftware.kryo.serializers.TimeSerializers; import com.esotericsoftware.kryo.util.DefaultClassResolver; import com.esotericsoftware.kryo.util.DefaultGenerics; @@ -237,6 +239,13 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { register(short.class, new ShortSerializer()); register(long.class, new LongSerializer()); register(double.class, new DoubleSerializer()); + + // Add RecordSerializer if JDK 14+ available + if (isClassAvailable("java.lang.Record")) { + try { + addDefaultSerializer(Class.forName("java.lang.Record"), RecordSerializer.class); + } catch (ClassNotFoundException ignored) {} // handled in if-clause + } } // --- Default serializers --- diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java index e89572b14..d497413f8 100644 --- a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -21,18 +21,196 @@ package com.esotericsoftware.kryo.serializers; import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -public class RecordSerializer extends Serializer { +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; +import java.util.Arrays; + +import static com.esotericsoftware.minlog.Log.TRACE; +import static com.esotericsoftware.minlog.Log.trace; +import static java.lang.invoke.MethodType.methodType; + +public class RecordSerializer extends ImmutableSerializer { + private static final MethodHandle MH_IS_RECORD; + private static final MethodHandle MH_GET_RECORD_COMPONENTS; + private static final MethodHandle MH_GET_NAME; + private static final MethodHandle MH_GET_TYPE; + private static final MethodHandles.Lookup LOOKUP; + + static { + MethodHandle MH_isRecord; + MethodHandle MH_getRecordComponents; + MethodHandle MH_getName; + MethodHandle MH_getType; + LOOKUP = MethodHandles.lookup(); + + try { + // reflective machinery required to access the record components + // without a static dependency on Java SE 14 APIs + Class c = Class.forName("java.lang.reflect.RecordComponent"); + MH_isRecord = LOOKUP.findVirtual(Class.class, "isRecord", methodType(boolean.class)); + MH_getRecordComponents = LOOKUP.findVirtual(Class.class, "getRecordComponents", + methodType(Array.newInstance(c, 0).getClass())) + .asType(methodType(Object[].class, Class.class)); + MH_getName = LOOKUP.findVirtual(c, "getName", methodType(String.class)) + .asType(methodType(String.class, Object.class)); + MH_getType = LOOKUP.findVirtual(c, "getType", methodType(Class.class)) + .asType(methodType(Class.class, Object.class)); + } catch (ClassNotFoundException | NoSuchMethodException e) { + // pre-Java-14 + MH_isRecord = null; + MH_getRecordComponents = null; + MH_getName = null; + MH_getType = null; + } catch (IllegalAccessException unexpected) { + throw new AssertionError(unexpected); + } + + MH_IS_RECORD = MH_isRecord; + MH_GET_RECORD_COMPONENTS = MH_getRecordComponents; + MH_GET_NAME = MH_getName; + MH_GET_TYPE = MH_getType; + } + + public RecordSerializer() { + } + @Override public void write(Kryo kryo, Output output, T object) { - // + final Class cls = object.getClass(); + if (!isRecord(cls)) { + throw new KryoException(object + " is not a record"); + } + for (RecordComponent rc : recordComponents(cls)) { + final Class type = rc.type(); + final String name = rc.name(); + try { + if (TRACE) trace("kryo", "Write property: " + name + " (" + type.getName() + ")"); + if (rc.type().isPrimitive()) { + kryo.writeObject(output, componentValue(object, rc)); + } else { + kryo.writeObjectOrNull(output, componentValue(object, rc), type); + } + } catch (KryoException ex) { + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } + } } @Override public T read(Kryo kryo, Input input, Class type) { - return null; + if (!isRecord(type)) { + throw new KryoException("Not a record (" + type + ")"); + } + final RecordComponent[] recordComponents = recordComponents(type); + final Object[] values = new Object[recordComponents.length]; + for (int i = 0; i < recordComponents.length; i++) { + final RecordComponent rc = recordComponents[i]; + final String name = rc.name(); + try { + if (TRACE) trace("kryo", "Read property: " + name + " (" + type.getName() + ")"); + values[i] = rc.type().isPrimitive() ? kryo.readObject(input, rc.type()) + : kryo.readObjectOrNull(input, rc.type()); + } catch (KryoException ex) { + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } + } + return invokeCanonicalConstructor(type, recordComponents, values); + } + + /** Returns true if, and only if, the given class is a record class. */ + private boolean isRecord(Class type) { + try { + return (boolean) MH_IS_RECORD.invokeExact(type); + } catch (Throwable t) { + throw new KryoException("Could not determine type (" + type + ")"); + } + } + + /** A record component, which has a name and a type. */ + final static class RecordComponent { + private final String name; + private final Class type; + RecordComponent(String name, Class type) { + this.name = name; + this.type = type; + } + String name() { return name; } + Class type() { return type; } + } + + /** + * Returns an ordered array of the record components for the given record + * class. The order is that of the components in the record attribute of the + * class file. + */ + private static RecordComponent[] recordComponents(Class type) { + try { + Object[] rawComponents = (Object[]) MH_GET_RECORD_COMPONENTS.invokeExact(type); + RecordComponent[] recordComponents = new RecordComponent[rawComponents.length]; + for (int i = 0; i < rawComponents.length; i++) { + final Object comp = rawComponents[i]; + recordComponents[i] = new RecordComponent( + (String) MH_GET_NAME.invokeExact(comp), + (Class) MH_GET_TYPE.invokeExact(comp)); + } + return recordComponents; + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace("Could not retrieve record components (" + type.getName() + ")"); + throw ex; + } + } + + /** Retrieves the value of the record component for the given record object. */ + private static Object componentValue(Object recordObject, + RecordComponent recordComponent) { + try { + MethodHandle MH_get = LOOKUP.findVirtual(recordObject.getClass(), + recordComponent.name(), + methodType(recordComponent.type())); + return (Object) MH_get.invoke(recordObject); + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace("Could not retrieve record components (" + + recordObject.getClass().getName() + ")"); + throw ex; + } + } + + /** + * Invokes the canonical constructor of a record class with the + * given argument values. + */ + private static T invokeCanonicalConstructor(Class recordType, + RecordComponent[] recordComponents, + Object[] args) { + try { + Class[] paramTypes = Arrays.stream(recordComponents) + .map(RecordComponent::type) + .toArray(Class[]::new); + MethodHandle MH_canonicalConstructor = + LOOKUP.findConstructor(recordType, methodType(void.class, paramTypes)) + .asType(methodType(Object.class, paramTypes)); + return (T)MH_canonicalConstructor.invokeWithArguments(args); + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace("Could not construct type (" + recordType.getName() + ")"); + throw ex; + } } } \ No newline at end of file diff --git a/test/jdk14/RecordSerializerTest.java b/test/jdk14/RecordSerializerTest.java index 991bef058..5b2f4f0ab 100644 --- a/test/jdk14/RecordSerializerTest.java +++ b/test/jdk14/RecordSerializerTest.java @@ -20,20 +20,296 @@ package jdk14; +import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoTestCase; - +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; import org.junit.Test; -/** @author Nathan Sweet */ +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.stream.IntStream; + +import static java.lang.System.out; +import static org.junit.Assert.assertEquals; + public class RecordSerializerTest extends KryoTestCase { { supportsCopy = false; } + /** Test where the single object is a record. */ + public record RecordRectangle (String height, int width, long x, double y) { } + + @Test + public void testBasicRecord() { + out.println("testBasicRecord\n"); + kryo.register(RecordRectangle.class); + + final var r1 = new RecordRectangle("one", 2, 3L, 4.0); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordRectangle.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(14, r1); + out.println("------\n"); + } + + /** Test where the single object is an empty record. */ + public record EmptyRecord () { } + + @Test + public void testEmptyRecord() { + out.println("testEmptyRecord\n"); + kryo.register(EmptyRecord.class); + + final var r1 = new EmptyRecord(); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, EmptyRecord.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(1, r1); + out.println("------\n"); + } + + /** Test deserialisation of an empty record where the input provides values. + * In this case no values are read from the input. + */ @Test - public void testRecordSerializer() { - // todo + public void testDeserializeEmptyRecordWithValues() { + out.println("testDeserializeEmptyRecordWithValues\n"); + kryo.register(EmptyRecord.class); + + final var input = new Input(new byte[]{1, 2, -128, 0}); // create bad input + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + final var r = kryo.readObject(input, EmptyRecord.class); + out.println("Deserialized record: \n" + r); + out.println("------\n"); } - public record PointRecord(int x, int y) { } + /** Test deserialisation of a record where the number of input values exceeds + * the number of record components. In this case values are read from the + * input sequentially until the number of record components is met, + * any additional input values are ignored. + */ + public record RecordPoint (int x, int y) { } + + @Test + public void testDeserializeWrongNumberOfValues() { + out.println("testDeserializeWrongNumberOfValues\n"); + kryo.register(RecordPoint.class); + + final var r1 = new RecordPoint(1,1); + final var input = new Input(new byte[]{2, 2, 2}); // create bad input + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + final var r2 = kryo.readObject(input, RecordPoint.class); + out.println("Deserialized record: \n" + r2); + doAssertEquals(r1, r2); + out.println("------\n"); + } + + /** Test where the record has an explicit constructor.*/ + public record RecordWithConstructor (String height, int width, long x, double y) { + public RecordWithConstructor(String height) { + this(height, 20, 30L, 40.0); + } + } + + @Test + public void testRecordWithConstructor() { + out.println("testRecordWithConstructor\n"); + kryo.register(RecordWithConstructor.class); + + final var r1 = new RecordWithConstructor("ten"); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithConstructor.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(14, r1); + out.println("------\n"); + } + + /** Test where the record component object is a record. */ + public record RecordOfRecord (RecordRectangle r) { } + + @Test + public void testRecordOfRecord() { + out.println("testRecordOfRecord\n"); + kryo.register(RecordOfRecord.class); + kryo.register(RecordRectangle.class); + + final var r1 = new RecordOfRecord(new RecordRectangle("one", 2, 3L, 4.0)); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordOfRecord.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(15, r1); + out.println("------\n"); + } + + /** Test where the single object is an array of records. */ + @Test + public void testArrayOfRecords() { + out.println("testArrayOfRecords\n"); + kryo.register(RecordPoint.class); + kryo.register(RecordPoint[].class); + + final var arr = new RecordPoint[100]; + IntStream.range(0, 100).forEach(i -> arr[i] = new RecordPoint(i, i+1)); + + roundTrip(375, arr); + } + + /** Test where the record component object is an array of records. */ + public record RecordWithArray (RecordRectangle[] recordArray) { } + + @Test + public void testRecordWithArray() { + out.println("testRecordWithArray\n"); + kryo.register(RecordWithArray.class); + kryo.register(RecordRectangle[].class); + kryo.register(RecordRectangle.class); + + final var r1 = new RecordWithArray(new RecordRectangle[] {new RecordRectangle("one", 2, 3L, 4.0)}); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithArray.class); + out.println("Deserialized record: \n" + r2); + + assertWithArrayEquals(r1, r2); + out.println("------\n"); + } + + private void assertWithArrayEquals(final RecordWithArray expected, + final RecordWithArray actual) { + assertEquals(expected.getClass(), actual.getClass()); + final RecordRectangle[] expectedArray = expected.recordArray(); + final RecordRectangle[] actualArray = actual.recordArray(); + assertEquals(Array.getLength(expectedArray), Array.getLength(actualArray)); + for (int i = 0; i < Array.getLength(expectedArray); i++) { + assertEquals(Array.get(expectedArray, i), Array.get(actualArray, i)); + } + } + + /** Test where record components are non-primitives with their default + * value (null). + */ + public record RecordWithNull (Object o, Number n, String s) { } + + @Test + public void testRecordWithNull() { + out.println("testRecordWithNull\n"); + kryo.register(RecordWithNull.class); + kryo.register(Object.class); + kryo.register(Number.class); + kryo.register(String.class); + + final var r1 = new RecordWithNull(null, null, null); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithNull.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(4, r1); + out.println("------\n"); + } + + /** Test where record components are primitives with their default values. */ + public record RecordWithDefaultValues(byte b, short s, int i, long l, float f, double d, char c, boolean bool) { } + + @Test + public void testRecordWithPrimitiveDefaultValues() { + out.println("testRecordWithPrimitiveDefaultValues\n"); + kryo.register(RecordWithDefaultValues.class); + + final var r1 = new RecordWithDefaultValues( + (byte)0, (short)0, 0, 0l, 0.0f, 0.0d, '\u0000', false); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithDefaultValues.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(21, r1); + out.println("------\n"); + } + + /** Test where the an exception is thrown in the record constructor. */ + public record PositivePoint (int x, int y) { + public PositivePoint { // compact syntax + if (x < 0) + throw new IllegalArgumentException("negative x:" + x); + if (y < 0) + throw new IllegalArgumentException("negative y:" + y); + } + } + + @Test + public void testDeserializeRecordWithIllegalValue1() { + out.println("testDeserializeRecordWithIllegalValue1\n"); + kryo.register(PositivePoint.class); + + final var input = new Input(new byte[]{1, 2}); // create bad input of -1, 1 + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + var e = expectThrows(IllegalArgumentException.class, + () -> kryo.readObject(input, PositivePoint.class)); + assertEquals("negative x:-1", e.getMessage()); + out.println("------\n"); + } + + @Test + public void testDeserializeRecordWithIllegalValue2() { + out.println("testDeserializeRecordWithIllegalValue2\n"); + kryo.register(PositivePoint.class); + + final var input = new Input(new byte[]{2, 1}); // create bad input of 1, -1 + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + var e = expectThrows(IllegalArgumentException.class, + () -> kryo.readObject(input, PositivePoint.class)); + assertEquals("negative y:-1", e.getMessage()); + out.println("------\n"); + } + + static T expectThrows(Class throwableClass, Runnable task) { + try { + task.run(); + throw new AssertionError("Exception not thrown"); + } catch (KryoException ce) { + Throwable cause = ce.getCause(); + if (!throwableClass.isInstance(cause)) { + throw new RuntimeException("expected: " + throwableClass + ", actual: " + cause); + } + return throwableClass.cast(cause); + } + } } From 054adcf56ac1936954f1ab54051accedc5dc70d3 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Wed, 2 Sep 2020 13:15:49 +0100 Subject: [PATCH 03/20] minimal commit to reproduce pom config --- pom-main.xml | 131 ++++++++++++++++++ pom-versioned.xml | 131 ++++++++++++++++++ src/com/esotericsoftware/kryo/Kryo.java | 9 ++ .../kryo/serializers/RecordSerializer.java | 38 +++++ test/jdk14/RecordSerializerTest.java | 38 +++++ 5 files changed, 347 insertions(+) create mode 100644 src/com/esotericsoftware/kryo/serializers/RecordSerializer.java create mode 100644 test/jdk14/RecordSerializerTest.java diff --git a/pom-main.xml b/pom-main.xml index 9b08f9138..7839aea32 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -124,6 +145,116 @@ + + + + jdk8ge + + [1.8,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + + + + + + + + + + jdk14ge + + [14,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + ${java.vm.specification.version} + + --enable-preview + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + --enable-preview + + + + + + + + + diff --git a/pom-versioned.xml b/pom-versioned.xml index 09c945c8d..2901df84a 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -82,4 +103,114 @@ + + + + jdk8ge + + [1.8,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + + + + + + + + + + jdk14ge + + [14,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + ${java.vm.specification.version} + + --enable-preview + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + --enable-preview + + + + + + + + + diff --git a/src/com/esotericsoftware/kryo/Kryo.java b/src/com/esotericsoftware/kryo/Kryo.java index e6450730e..bc41f2706 100644 --- a/src/com/esotericsoftware/kryo/Kryo.java +++ b/src/com/esotericsoftware/kryo/Kryo.java @@ -1,4 +1,5 @@ /* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -80,6 +81,7 @@ import com.esotericsoftware.kryo.serializers.ImmutableCollectionsSerializers; import com.esotericsoftware.kryo.serializers.MapSerializer; import com.esotericsoftware.kryo.serializers.OptionalSerializers; +import com.esotericsoftware.kryo.serializers.RecordSerializer; import com.esotericsoftware.kryo.serializers.TimeSerializers; import com.esotericsoftware.kryo.util.DefaultClassResolver; import com.esotericsoftware.kryo.util.DefaultGenerics; @@ -237,6 +239,13 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { register(short.class, new ShortSerializer()); register(long.class, new LongSerializer()); register(double.class, new DoubleSerializer()); + + // Add RecordSerializer if JDK 14+ available + if (isClassAvailable("java.lang.Record")) { + try { + addDefaultSerializer(Class.forName("java.lang.Record"), RecordSerializer.class); + } catch (ClassNotFoundException ignored) {} // handled in if-clause + } } // --- Default serializers --- diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java new file mode 100644 index 000000000..46317fff6 --- /dev/null +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -0,0 +1,38 @@ +/* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +package com.esotericsoftware.kryo.serializers; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +public class RecordSerializer extends ImmutableSerializer { + + @Override + public void write(Kryo kryo, Output output, T object) { + // do something + } + + @Override + public T read(Kryo kryo, Input input, Class type) { + return null; + } +} \ No newline at end of file diff --git a/test/jdk14/RecordSerializerTest.java b/test/jdk14/RecordSerializerTest.java new file mode 100644 index 000000000..ab4819b98 --- /dev/null +++ b/test/jdk14/RecordSerializerTest.java @@ -0,0 +1,38 @@ +/* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +package jdk14; + +import com.esotericsoftware.kryo.KryoTestCase; +import org.junit.Test; + +public class RecordSerializerTest extends KryoTestCase { + { + supportsCopy = false; + } + + /** Test where the single object is a record. */ + public record RecordRectangle (int height, int width, int x, int y) { } + + @Test + public void testBasicRecord() { + // test something + } +} \ No newline at end of file From e46c198ae5b1cffa6c4cb302deb441596d1ab3b3 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Wed, 2 Sep 2020 13:35:07 +0100 Subject: [PATCH 04/20] fix test compilation --- pom-main.xml | 131 ++++++++++++++++++++++++++++++++++++++++++++++ pom-versioned.xml | 131 ++++++++++++++++++++++++++++++++++++++++++++++ pom.xml | 83 ----------------------------- 3 files changed, 262 insertions(+), 83 deletions(-) diff --git a/pom-main.xml b/pom-main.xml index 9b08f9138..7839aea32 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -124,6 +145,116 @@ + + + + jdk8ge + + [1.8,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + + + + + + + + + + jdk14ge + + [14,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + ${java.vm.specification.version} + + --enable-preview + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + --enable-preview + + + + + + + + + diff --git a/pom-versioned.xml b/pom-versioned.xml index 09c945c8d..2901df84a 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -82,4 +103,114 @@ + + + + jdk8ge + + [1.8,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + + + + + + + + + + jdk14ge + + [14,) + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + true + + + default-testCompile + test-compile + + testCompile + + + ${java.vm.specification.version} + + --enable-preview + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + true + + + default-test + test + + test + + + + jdk14/** + + --enable-preview + + + + + + + + + diff --git a/pom.xml b/pom.xml index 0321daafb..4f450cf94 100644 --- a/pom.xml +++ b/pom.xml @@ -1,25 +1,4 @@ - 4.0.0 @@ -194,27 +173,6 @@ - - - jdk8+ - - [1.8,) - - - - - org.apache.maven.plugins - maven-compiler-plugin - true - - - jdk14/** - - - - - - until-java11 @@ -238,47 +196,6 @@ - - - jdk14+ - - [14,) - - - - - org.apache.maven.plugins - maven-compiler-plugin - true - - ${java.vm.specification.version} - - --enable-preview - - - jdk14/** - - - com/** - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - - - jdk14/** - - --enable-preview - - - - - - - From f167b1778d36a9f37214b06e582f3ad0bea199f3 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Thu, 3 Sep 2020 13:35:27 +0100 Subject: [PATCH 05/20] fix compile and test run configuration --- pom-main.xml | 56 ++----------------- pom-versioned.xml | 56 ++----------------- pom.xml | 41 +++++++++++++- .../kryo/SerializationCompatTest.java | 5 +- 4 files changed, 51 insertions(+), 107 deletions(-) diff --git a/pom-main.xml b/pom-main.xml index 7839aea32..60949af23 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -146,19 +146,17 @@ - + - jdk8ge + jdk11ge - [1.8,) + [11,14) org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -174,30 +172,10 @@ - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - - - - - + jdk14ge @@ -208,8 +186,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -222,30 +198,6 @@ --enable-preview - - jdk14/** - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - --enable-preview diff --git a/pom-versioned.xml b/pom-versioned.xml index 2901df84a..01bdaa56a 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -104,19 +104,17 @@ - + - jdk8ge + jdk11ge - [1.8,) + [11,14) org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -132,30 +130,10 @@ - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - - - - - + jdk14ge @@ -166,8 +144,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -180,30 +156,6 @@ --enable-preview - - jdk14/** - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - --enable-preview diff --git a/pom.xml b/pom.xml index 4f450cf94..86055f7da 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.apache.maven.plugins maven-compiler-plugin - true + 3.8.1 ${javac.target} ${javac.target} @@ -190,12 +190,51 @@ **/java11/*Test.java + jdk14/** + + + jdk11ge + + [11,14) + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + jdk14/** + + + + + + + + + jdk14ge + + [14,) + + + + + org.apache.maven.plugins + maven-surefire-plugin + + --enable-preview + + + + + diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 627605c95..ddb5d0a6f 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -1,4 +1,5 @@ /* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -38,7 +39,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.lang.reflect.Field; -import java.nio.ByteOrder; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; @@ -81,7 +81,8 @@ public class SerializationCompatTest extends KryoTestCase { int[] versions = new int[] {parseInt(strVersions[0]), parseInt(strVersions[1])}; JAVA_VERSION = versions[0] > 1 ? versions[0] : versions[1]; } - private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 11 ? 57 : 67; // Also change Kryo#defaultSerializers. + // Also change Kryo#defaultSerializers. + private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 11 ? 57 : JAVA_VERSION < 14 ? 67 : 68; private static final List TEST_DATAS = new ArrayList<>(); static { From 59d7532949e70bd65cc5d76c6d7ab33d42416ecc Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Thu, 3 Sep 2020 17:10:41 +0100 Subject: [PATCH 06/20] add record serializer and test and update pom --- pom-main.xml | 56 +--- pom-versioned.xml | 56 +--- pom.xml | 62 +++- .../kryo/serializers/RecordSerializer.java | 184 +++++++++++- .../kryo/SerializationCompatTest.java | 4 +- test/jdk14/RecordSerializerTest.java | 283 +++++++++++++++++- 6 files changed, 533 insertions(+), 112 deletions(-) diff --git a/pom-main.xml b/pom-main.xml index 7839aea32..60949af23 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -146,19 +146,17 @@ - + - jdk8ge + jdk11ge - [1.8,) + [11,14) org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -174,30 +172,10 @@ - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - - - - - + jdk14ge @@ -208,8 +186,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -222,30 +198,6 @@ --enable-preview - - jdk14/** - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - --enable-preview diff --git a/pom-versioned.xml b/pom-versioned.xml index 2901df84a..bc67124a4 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -104,19 +104,17 @@ - + - jdk8ge + jdk11ge - [1.8,) + [11,14) org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -132,30 +130,10 @@ - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - - - - - + jdk14ge @@ -166,8 +144,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - true default-testCompile @@ -180,30 +156,6 @@ --enable-preview - - jdk14/** - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - true - - - default-test - test - - test - - - - jdk14/** - - --enable-preview diff --git a/pom.xml b/pom.xml index 4f450cf94..2cb7eab42 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -75,7 +96,7 @@ org.apache.maven.plugins maven-compiler-plugin - true + 3.8.1 ${javac.target} ${javac.target} @@ -190,12 +211,51 @@ **/java11/*Test.java + jdk14/** + + + jdk11ge + + [11,14) + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + jdk14/** + + + + + + + + + jdk14ge + + [14,) + + + + + org.apache.maven.plugins + maven-surefire-plugin + + --enable-preview + + + + + diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java index 46317fff6..9d1b7d59c 100644 --- a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -21,18 +21,196 @@ package com.esotericsoftware.kryo.serializers; import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Array; +import java.util.Arrays; + +import static com.esotericsoftware.minlog.Log.TRACE; +import static com.esotericsoftware.minlog.Log.trace; +import static java.lang.invoke.MethodType.methodType; + public class RecordSerializer extends ImmutableSerializer { + private static final MethodHandle MH_IS_RECORD; + private static final MethodHandle MH_GET_RECORD_COMPONENTS; + private static final MethodHandle MH_GET_NAME; + private static final MethodHandle MH_GET_TYPE; + private static final MethodHandles.Lookup LOOKUP; + + static { + MethodHandle MH_isRecord; + MethodHandle MH_getRecordComponents; + MethodHandle MH_getName; + MethodHandle MH_getType; + LOOKUP = MethodHandles.lookup(); + + try { + // reflective machinery required to access the record components + // without a static dependency on Java SE 14 APIs + Class c = Class.forName("java.lang.reflect.RecordComponent"); + MH_isRecord = LOOKUP.findVirtual(Class.class, "isRecord", methodType(boolean.class)); + MH_getRecordComponents = LOOKUP.findVirtual(Class.class, "getRecordComponents", + methodType(Array.newInstance(c, 0).getClass())) + .asType(methodType(Object[].class, Class.class)); + MH_getName = LOOKUP.findVirtual(c, "getName", methodType(String.class)) + .asType(methodType(String.class, Object.class)); + MH_getType = LOOKUP.findVirtual(c, "getType", methodType(Class.class)) + .asType(methodType(Class.class, Object.class)); + } catch (ClassNotFoundException | NoSuchMethodException e) { + // pre-Java-14 + MH_isRecord = null; + MH_getRecordComponents = null; + MH_getName = null; + MH_getType = null; + } catch (IllegalAccessException unexpected) { + throw new AssertionError(unexpected); + } + + MH_IS_RECORD = MH_isRecord; + MH_GET_RECORD_COMPONENTS = MH_getRecordComponents; + MH_GET_NAME = MH_getName; + MH_GET_TYPE = MH_getType; + } + + public RecordSerializer() { + } @Override public void write(Kryo kryo, Output output, T object) { - // do something + final Class cls = object.getClass(); + if (!isRecord(cls)) { + throw new KryoException(object + " is not a record"); + } + for (RecordComponent rc : recordComponents(cls)) { + final Class type = rc.type(); + final String name = rc.name(); + try { + if (TRACE) trace("kryo", "Write property: " + name + " (" + type.getName() + ")"); + if (rc.type().isPrimitive()) { + kryo.writeObject(output, componentValue(object, rc)); + } else { + kryo.writeObjectOrNull(output, componentValue(object, rc), type); + } + } catch (KryoException ex) { + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } + } } @Override public T read(Kryo kryo, Input input, Class type) { - return null; + if (!isRecord(type)) { + throw new KryoException("Not a record (" + type + ")"); + } + final RecordComponent[] recordComponents = recordComponents(type); + final Object[] values = new Object[recordComponents.length]; + for (int i = 0; i < recordComponents.length; i++) { + final RecordComponent rc = recordComponents[i]; + final String name = rc.name(); + try { + if (TRACE) trace("kryo", "Read property: " + name + " (" + type.getName() + ")"); + values[i] = rc.type().isPrimitive() ? kryo.readObject(input, rc.type()) + : kryo.readObjectOrNull(input, rc.type()); + } catch (KryoException ex) { + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace(name + " (" + type.getName() + ")"); + throw ex; + } + } + return invokeCanonicalConstructor(type, recordComponents, values); + } + + /** Returns true if, and only if, the given class is a record class. */ + private boolean isRecord(Class type) { + try { + return (boolean) MH_IS_RECORD.invokeExact(type); + } catch (Throwable t) { + throw new KryoException("Could not determine type (" + type + ")"); + } + } + + /** A record component, which has a name and a type. */ + final static class RecordComponent { + private final String name; + private final Class type; + RecordComponent(String name, Class type) { + this.name = name; + this.type = type; + } + String name() { return name; } + Class type() { return type; } + } + + /** + * Returns an ordered array of the record components for the given record + * class. The order is that of the components in the record attribute of the + * class file. + */ + private static RecordComponent[] recordComponents(Class type) { + try { + Object[] rawComponents = (Object[]) MH_GET_RECORD_COMPONENTS.invokeExact(type); + RecordComponent[] recordComponents = new RecordComponent[rawComponents.length]; + for (int i = 0; i < rawComponents.length; i++) { + final Object comp = rawComponents[i]; + recordComponents[i] = new RecordComponent( + (String) MH_GET_NAME.invokeExact(comp), + (Class) MH_GET_TYPE.invokeExact(comp)); + } + return recordComponents; + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace("Could not retrieve record components (" + type.getName() + ")"); + throw ex; + } + } + + /** Retrieves the value of the record component for the given record object. */ + private static Object componentValue(Object recordObject, + RecordComponent recordComponent) { + try { + MethodHandle MH_get = LOOKUP.findVirtual(recordObject.getClass(), + recordComponent.name(), + methodType(recordComponent.type())); + return (Object) MH_get.invoke(recordObject); + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace("Could not retrieve record components (" + + recordObject.getClass().getName() + ")"); + throw ex; + } + } + + /** + * Invokes the canonical constructor of a record class with the + * given argument values. + */ + private static T invokeCanonicalConstructor(Class recordType, + RecordComponent[] recordComponents, + Object[] args) { + try { + Class[] paramTypes = Arrays.stream(recordComponents) + .map(RecordComponent::type) + .toArray(Class[]::new); + MethodHandle MH_canonicalConstructor = + LOOKUP.findConstructor(recordType, methodType(void.class, paramTypes)) + .asType(methodType(Object.class, paramTypes)); + return (T)MH_canonicalConstructor.invokeWithArguments(args); + } catch (Throwable t) { + KryoException ex = new KryoException(t); + ex.addTrace("Could not construct type (" + recordType.getName() + ")"); + throw ex; + } } -} \ No newline at end of file +} diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 627605c95..220dccc5b 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -1,4 +1,5 @@ /* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -81,7 +82,8 @@ public class SerializationCompatTest extends KryoTestCase { int[] versions = new int[] {parseInt(strVersions[0]), parseInt(strVersions[1])}; JAVA_VERSION = versions[0] > 1 ? versions[0] : versions[1]; } - private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 11 ? 57 : 67; // Also change Kryo#defaultSerializers. + private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 11 + ? 57 : JAVA_VERSION < 14 ? 67 : 68; // Also change Kryo#defaultSerializers. private static final List TEST_DATAS = new ArrayList<>(); static { diff --git a/test/jdk14/RecordSerializerTest.java b/test/jdk14/RecordSerializerTest.java index ab4819b98..5b2f4f0ab 100644 --- a/test/jdk14/RecordSerializerTest.java +++ b/test/jdk14/RecordSerializerTest.java @@ -20,19 +20,296 @@ package jdk14; +import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoTestCase; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; import org.junit.Test; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.stream.IntStream; + +import static java.lang.System.out; +import static org.junit.Assert.assertEquals; + public class RecordSerializerTest extends KryoTestCase { { supportsCopy = false; } /** Test where the single object is a record. */ - public record RecordRectangle (int height, int width, int x, int y) { } + public record RecordRectangle (String height, int width, long x, double y) { } @Test public void testBasicRecord() { - // test something + out.println("testBasicRecord\n"); + kryo.register(RecordRectangle.class); + + final var r1 = new RecordRectangle("one", 2, 3L, 4.0); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordRectangle.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(14, r1); + out.println("------\n"); + } + + /** Test where the single object is an empty record. */ + public record EmptyRecord () { } + + @Test + public void testEmptyRecord() { + out.println("testEmptyRecord\n"); + kryo.register(EmptyRecord.class); + + final var r1 = new EmptyRecord(); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, EmptyRecord.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(1, r1); + out.println("------\n"); + } + + /** Test deserialisation of an empty record where the input provides values. + * In this case no values are read from the input. + */ + @Test + public void testDeserializeEmptyRecordWithValues() { + out.println("testDeserializeEmptyRecordWithValues\n"); + kryo.register(EmptyRecord.class); + + final var input = new Input(new byte[]{1, 2, -128, 0}); // create bad input + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + final var r = kryo.readObject(input, EmptyRecord.class); + out.println("Deserialized record: \n" + r); + out.println("------\n"); + } + + /** Test deserialisation of a record where the number of input values exceeds + * the number of record components. In this case values are read from the + * input sequentially until the number of record components is met, + * any additional input values are ignored. + */ + public record RecordPoint (int x, int y) { } + + @Test + public void testDeserializeWrongNumberOfValues() { + out.println("testDeserializeWrongNumberOfValues\n"); + kryo.register(RecordPoint.class); + + final var r1 = new RecordPoint(1,1); + final var input = new Input(new byte[]{2, 2, 2}); // create bad input + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + final var r2 = kryo.readObject(input, RecordPoint.class); + out.println("Deserialized record: \n" + r2); + doAssertEquals(r1, r2); + out.println("------\n"); + } + + /** Test where the record has an explicit constructor.*/ + public record RecordWithConstructor (String height, int width, long x, double y) { + public RecordWithConstructor(String height) { + this(height, 20, 30L, 40.0); + } + } + + @Test + public void testRecordWithConstructor() { + out.println("testRecordWithConstructor\n"); + kryo.register(RecordWithConstructor.class); + + final var r1 = new RecordWithConstructor("ten"); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithConstructor.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(14, r1); + out.println("------\n"); + } + + /** Test where the record component object is a record. */ + public record RecordOfRecord (RecordRectangle r) { } + + @Test + public void testRecordOfRecord() { + out.println("testRecordOfRecord\n"); + kryo.register(RecordOfRecord.class); + kryo.register(RecordRectangle.class); + + final var r1 = new RecordOfRecord(new RecordRectangle("one", 2, 3L, 4.0)); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordOfRecord.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(15, r1); + out.println("------\n"); + } + + /** Test where the single object is an array of records. */ + @Test + public void testArrayOfRecords() { + out.println("testArrayOfRecords\n"); + kryo.register(RecordPoint.class); + kryo.register(RecordPoint[].class); + + final var arr = new RecordPoint[100]; + IntStream.range(0, 100).forEach(i -> arr[i] = new RecordPoint(i, i+1)); + + roundTrip(375, arr); + } + + /** Test where the record component object is an array of records. */ + public record RecordWithArray (RecordRectangle[] recordArray) { } + + @Test + public void testRecordWithArray() { + out.println("testRecordWithArray\n"); + kryo.register(RecordWithArray.class); + kryo.register(RecordRectangle[].class); + kryo.register(RecordRectangle.class); + + final var r1 = new RecordWithArray(new RecordRectangle[] {new RecordRectangle("one", 2, 3L, 4.0)}); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithArray.class); + out.println("Deserialized record: \n" + r2); + + assertWithArrayEquals(r1, r2); + out.println("------\n"); + } + + private void assertWithArrayEquals(final RecordWithArray expected, + final RecordWithArray actual) { + assertEquals(expected.getClass(), actual.getClass()); + final RecordRectangle[] expectedArray = expected.recordArray(); + final RecordRectangle[] actualArray = actual.recordArray(); + assertEquals(Array.getLength(expectedArray), Array.getLength(actualArray)); + for (int i = 0; i < Array.getLength(expectedArray); i++) { + assertEquals(Array.get(expectedArray, i), Array.get(actualArray, i)); + } + } + + /** Test where record components are non-primitives with their default + * value (null). + */ + public record RecordWithNull (Object o, Number n, String s) { } + + @Test + public void testRecordWithNull() { + out.println("testRecordWithNull\n"); + kryo.register(RecordWithNull.class); + kryo.register(Object.class); + kryo.register(Number.class); + kryo.register(String.class); + + final var r1 = new RecordWithNull(null, null, null); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithNull.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(4, r1); + out.println("------\n"); + } + + /** Test where record components are primitives with their default values. */ + public record RecordWithDefaultValues(byte b, short s, int i, long l, float f, double d, char c, boolean bool) { } + + @Test + public void testRecordWithPrimitiveDefaultValues() { + out.println("testRecordWithPrimitiveDefaultValues\n"); + kryo.register(RecordWithDefaultValues.class); + + final var r1 = new RecordWithDefaultValues( + (byte)0, (short)0, 0, 0l, 0.0f, 0.0d, '\u0000', false); + final var output = new Output(32); + kryo.writeObject(output, r1); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, RecordWithDefaultValues.class); + out.println("Deserialized record: \n" + r2); + + doAssertEquals(r1, r2); + roundTrip(21, r1); + out.println("------\n"); + } + + /** Test where the an exception is thrown in the record constructor. */ + public record PositivePoint (int x, int y) { + public PositivePoint { // compact syntax + if (x < 0) + throw new IllegalArgumentException("negative x:" + x); + if (y < 0) + throw new IllegalArgumentException("negative y:" + y); + } + } + + @Test + public void testDeserializeRecordWithIllegalValue1() { + out.println("testDeserializeRecordWithIllegalValue1\n"); + kryo.register(PositivePoint.class); + + final var input = new Input(new byte[]{1, 2}); // create bad input of -1, 1 + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + var e = expectThrows(IllegalArgumentException.class, + () -> kryo.readObject(input, PositivePoint.class)); + assertEquals("negative x:-1", e.getMessage()); + out.println("------\n"); + } + + @Test + public void testDeserializeRecordWithIllegalValue2() { + out.println("testDeserializeRecordWithIllegalValue2\n"); + kryo.register(PositivePoint.class); + + final var input = new Input(new byte[]{2, 1}); // create bad input of 1, -1 + out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); + var e = expectThrows(IllegalArgumentException.class, + () -> kryo.readObject(input, PositivePoint.class)); + assertEquals("negative y:-1", e.getMessage()); + out.println("------\n"); + } + + static T expectThrows(Class throwableClass, Runnable task) { + try { + task.run(); + throw new AssertionError("Exception not thrown"); + } catch (KryoException ce) { + Throwable cause = ce.getCause(); + if (!throwableClass.isInstance(cause)) { + throw new RuntimeException("expected: " + throwableClass + ", actual: " + cause); + } + return throwableClass.cast(cause); + } } -} \ No newline at end of file +} From f97e3b32af7da4e647aa3132f3123bf23fdbf2ad Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Thu, 3 Sep 2020 18:55:09 +0100 Subject: [PATCH 07/20] fix lower bound for compiler profile --- pom-main.xml | 2 +- pom-versioned.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom-main.xml b/pom-main.xml index 60949af23..0e16bbe1e 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -150,7 +150,7 @@ jdk11ge - [11,14) + [1.5,14) diff --git a/pom-versioned.xml b/pom-versioned.xml index bc67124a4..b4ff83a05 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -108,7 +108,7 @@ jdk11ge - [11,14) + [1.5,14) From 2a7c2bf43dda55dd7732420f6244f87239dbbfbd Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Mon, 7 Sep 2020 15:16:00 +0100 Subject: [PATCH 08/20] adjust config and small clean ups --- pom-main.xml | 85 +---------- pom-versioned.xml | 86 +---------- pom.xml | 135 +++++++++++++----- src/com/esotericsoftware/kryo/Kryo.java | 12 +- .../ImmutableCollectionsSerializersTest.java | 2 +- 5 files changed, 108 insertions(+), 212 deletions(-) rename test/{com/esotericsoftware/kryo/serializers/java11 => jdk11}/ImmutableCollectionsSerializersTest.java (98%) diff --git a/pom-main.xml b/pom-main.xml index 0e16bbe1e..a1fbfea73 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -1,26 +1,5 @@ - - 4.0.0 +4.0.0 com.esotericsoftware @@ -145,68 +124,6 @@ - - - - jdk11ge - - [1.5,14) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - - jdk14/** - - - - - - - - - - - jdk14ge - - [14,) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - ${java.vm.specification.version} - - --enable-preview - - - - - - - - - - diff --git a/pom-versioned.xml b/pom-versioned.xml index b4ff83a05..157236f9e 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -1,26 +1,5 @@ - - 4.0.0 +4.0.0 com.esotericsoftware @@ -102,67 +81,4 @@ - - - - - jdk11ge - - [1.5,14) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - - jdk14/** - - - - - - - - - - - jdk14ge - - [14,) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - ${java.vm.specification.version} - - --enable-preview - - - - - - - - - - diff --git a/pom.xml b/pom.xml index 2cb7eab42..bcbbb02b2 100644 --- a/pom.xml +++ b/pom.xml @@ -139,7 +139,7 @@ -Xdoclint:none - + 8 @@ -194,8 +194,10 @@ + + - until-java11 + until-jdk11 + jdk11ge [11,14) - - - org.apache.maven.plugins - maven-surefire-plugin - - - jdk14/** - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-testCompile + test-compile + + testCompile + + + + jdk14/** + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + jdk14/** + + + + + @@ -245,15 +287,36 @@ [14,) - - - org.apache.maven.plugins - maven-surefire-plugin - - --enable-preview - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-testCompile + test-compile + + testCompile + + + ${java.vm.specification.version} + + --enable-preview + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + --enable-preview + + + + diff --git a/src/com/esotericsoftware/kryo/Kryo.java b/src/com/esotericsoftware/kryo/Kryo.java index bc41f2706..6df4ff132 100644 --- a/src/com/esotericsoftware/kryo/Kryo.java +++ b/src/com/esotericsoftware/kryo/Kryo.java @@ -227,6 +227,12 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { OptionalSerializers.addDefaultSerializers(this); TimeSerializers.addDefaultSerializers(this); ImmutableCollectionsSerializers.addDefaultSerializers(this); + // Add RecordSerializer if JDK 14+ available + if (isClassAvailable("java.lang.Record")) { + try { + addDefaultSerializer(Class.forName("java.lang.Record"), RecordSerializer.class); + } catch (ClassNotFoundException ignored) {} // handled in if-clause + } lowPriorityDefaultSerializerCount = defaultSerializers.size(); // Primitives and string. Primitive wrappers automatically use the same registration as primitives. @@ -240,12 +246,6 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { register(long.class, new LongSerializer()); register(double.class, new DoubleSerializer()); - // Add RecordSerializer if JDK 14+ available - if (isClassAvailable("java.lang.Record")) { - try { - addDefaultSerializer(Class.forName("java.lang.Record"), RecordSerializer.class); - } catch (ClassNotFoundException ignored) {} // handled in if-clause - } } // --- Default serializers --- diff --git a/test/com/esotericsoftware/kryo/serializers/java11/ImmutableCollectionsSerializersTest.java b/test/jdk11/ImmutableCollectionsSerializersTest.java similarity index 98% rename from test/com/esotericsoftware/kryo/serializers/java11/ImmutableCollectionsSerializersTest.java rename to test/jdk11/ImmutableCollectionsSerializersTest.java index 61fc7fc55..0f759c3c8 100644 --- a/test/com/esotericsoftware/kryo/serializers/java11/ImmutableCollectionsSerializersTest.java +++ b/test/jdk11/ImmutableCollectionsSerializersTest.java @@ -17,7 +17,7 @@ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.esotericsoftware.kryo.serializers.java11; +package jdk11; import com.esotericsoftware.kryo.KryoTestCase; From 2d1497c8c832f165aa07d951cb18b037abbf6889 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Mon, 7 Sep 2020 15:38:48 +0100 Subject: [PATCH 09/20] fix indentation --- pom-main.xml | 2 +- pom-versioned.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom-main.xml b/pom-main.xml index a1fbfea73..9b08f9138 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -1,5 +1,5 @@ -4.0.0 + 4.0.0 com.esotericsoftware diff --git a/pom-versioned.xml b/pom-versioned.xml index 157236f9e..5137f12a3 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -1,5 +1,5 @@ -4.0.0 + 4.0.0 com.esotericsoftware From da5a8c2490c2157424a9eb4658f7b675fa34c2e8 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Mon, 7 Sep 2020 17:26:26 +0100 Subject: [PATCH 10/20] change to 2-arg constructor to work with JDK14 --- src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java b/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java index 0e74c0a22..7d5b830ec 100644 --- a/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java +++ b/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java @@ -1,4 +1,5 @@ /* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -96,7 +97,7 @@ public class UnsafeUtil { static { ByteBuffer buffer = ByteBuffer.allocateDirect(1); try { - directByteBufferConstructor = buffer.getClass().getDeclaredConstructor(long.class, int.class, Object.class); + directByteBufferConstructor = buffer.getClass().getDeclaredConstructor(long.class, int.class); directByteBufferConstructor.setAccessible(true); } catch (Exception ex) { if (DEBUG) debug("kryo", "No direct ByteBuffer constructor is available.", ex); @@ -124,7 +125,7 @@ public static ByteBuffer newDirectBuffer (long address, int size) { if (directByteBufferConstructor == null) throw new UnsupportedOperationException("No direct ByteBuffer constructor is available."); try { - return directByteBufferConstructor.newInstance(address, size, null); + return directByteBufferConstructor.newInstance(address, size); } catch (Exception ex) { throw new KryoException("Error creating a ByteBuffer at address: " + address, ex); } From 7641fca020dc27fb113ecd96b65427217a96ebd1 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Wed, 9 Sep 2020 14:43:38 +0100 Subject: [PATCH 11/20] add test data for JDK14 and add ordering by name to serializer --- .../kryo/serializers/RecordSerializer.java | 24 ++++++--- .../kryo/SerializationCompatTest.java | 9 ++++ test/jdk14/RecordSerializerTest.java | 46 ++++++++++++++++++ test/jdk14/TestDataJava14.java | 33 +++++++++++++ test/resources/TestDataJava14-bytebuffer.ser | Bin 0 -> 1906 bytes test/resources/TestDataJava14-standard.ser | Bin 0 -> 1906 bytes 6 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 test/jdk14/TestDataJava14.java create mode 100644 test/resources/TestDataJava14-bytebuffer.ser create mode 100644 test/resources/TestDataJava14-standard.ser diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java index 9d1b7d59c..18e3c61f1 100644 --- a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -29,6 +29,7 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.util.Arrays; +import java.util.Comparator; import static com.esotericsoftware.minlog.Log.TRACE; import static com.esotericsoftware.minlog.Log.trace; @@ -118,7 +119,8 @@ public T read(Kryo kryo, Input input, Class type) { final String name = rc.name(); try { if (TRACE) trace("kryo", "Read property: " + name + " (" + type.getName() + ")"); - values[i] = rc.type().isPrimitive() ? kryo.readObject(input, rc.type()) + // Populate values in the order required by the canonical constructor + values[rc.index()] = rc.type().isPrimitive() ? kryo.readObject(input, rc.type()) : kryo.readObjectOrNull(input, rc.type()); } catch (KryoException ex) { ex.addTrace(name + " (" + type.getName() + ")"); @@ -141,22 +143,28 @@ private boolean isRecord(Class type) { } } - /** A record component, which has a name and a type. */ + /** A record component, which has a name, a type and an index. + * The latter is the index of the record component in the class file's + * record attribute, and required to invoke the record's canonical constructor .*/ final static class RecordComponent { private final String name; private final Class type; - RecordComponent(String name, Class type) { + private final int index; + + RecordComponent(String name, Class type, int index) { this.name = name; this.type = type; + this.index = index; } + String name() { return name; } Class type() { return type; } + int index() { return index; } } /** * Returns an ordered array of the record components for the given record - * class. The order is that of the components in the record attribute of the - * class file. + * class. The order is that of the components' names in ascending order. */ private static RecordComponent[] recordComponents(Class type) { try { @@ -166,8 +174,9 @@ private static RecordComponent[] recordComponents(Class type) { final Object comp = rawComponents[i]; recordComponents[i] = new RecordComponent( (String) MH_GET_NAME.invokeExact(comp), - (Class) MH_GET_TYPE.invokeExact(comp)); + (Class) MH_GET_TYPE.invokeExact(comp), i); } + Arrays.sort(recordComponents, Comparator.comparing(RecordComponent::name)); return recordComponents; } catch (Throwable t) { KryoException ex = new KryoException(t); @@ -194,13 +203,14 @@ private static Object componentValue(Object recordObject, /** * Invokes the canonical constructor of a record class with the - * given argument values. + * given argument values in the order given in the class file. */ private static T invokeCanonicalConstructor(Class recordType, RecordComponent[] recordComponents, Object[] args) { try { Class[] paramTypes = Arrays.stream(recordComponents) + .sorted(Comparator.comparing(RecordComponent::index)) .map(RecordComponent::type) .toArray(Class[]::new); MethodHandle MH_canonicalConstructor = diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 220dccc5b..418b341ee 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -90,8 +90,17 @@ public class SerializationCompatTest extends KryoTestCase { TEST_DATAS.add(new TestDataDescription<>(new TestData(), 1940, 1958)); if (JAVA_VERSION >= 8) TEST_DATAS.add(new TestDataDescription<>(new TestDataJava8(), 2098, 2116)); if (JAVA_VERSION >= 11) TEST_DATAS.add(new TestDataDescription<>(new TestDataJava11(), 2210, 2238)); + if (JAVA_VERSION >= 14) TEST_DATAS.add(new TestDataDescription<>(createTestDataJava14(), 1928, 1946)); }; + private static TestData createTestDataJava14() { + try { + return (TestData) Class.forName("jdk14.TestDataJava14").getConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("TestDataJava14 could not be instantiated", e); + } + } + @Override @Before public void setUp () throws Exception { diff --git a/test/jdk14/RecordSerializerTest.java b/test/jdk14/RecordSerializerTest.java index 5b2f4f0ab..3040ec0c8 100644 --- a/test/jdk14/RecordSerializerTest.java +++ b/test/jdk14/RecordSerializerTest.java @@ -312,4 +312,50 @@ static T expectThrows(Class throwableClass, Runnable ta return throwableClass.cast(cause); } } + + /** Test where the record parameters are the same but in different order. + * This is supported as record components are sorted by name during + * de/serialization. + */ + public record R (long l, int i, String s) { } + public record R1 (int i, long l, String s) { } + public record R2 (String s, int i, long l) { } + + @Test + public void testRecordWithParametersReordered1() { + out.println("testRecordWithParametersReordered1\n"); + kryo.register(R.class); + kryo.register(R1.class); + + final var r = new R(1L, 1, "foo"); + final var output = new Output(32); + kryo.writeObject(output, r); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r1 = kryo.readObject(input, R1.class); + out.println("Deserialized record: \n" + r1); + + roundTrip(6, r1); + out.println("------\n"); + } + + @Test + public void testRecordWithParametersReordered2() { + out.println("testRecordWithParametersReordered2\n"); + kryo.register(R.class); + kryo.register(R2.class); + + final var r = new R(1L, 1, "foo"); + final var output = new Output(32); + kryo.writeObject(output, r); + out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); + + final var input = new Input(output.getBuffer(), 0, output.position()); + final var r2 = kryo.readObject(input, R2.class); + out.println("Deserialized record: \n" + r2); + + roundTrip(6, r2); + out.println("------\n"); + } } diff --git a/test/jdk14/TestDataJava14.java b/test/jdk14/TestDataJava14.java new file mode 100644 index 000000000..24fbde92b --- /dev/null +++ b/test/jdk14/TestDataJava14.java @@ -0,0 +1,33 @@ +/* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +package jdk14; + +import com.esotericsoftware.kryo.SerializationCompatTestData; + +public class TestDataJava14 extends SerializationCompatTestData.TestData { + public record Rec (byte b, short s, int i, long l, float f, double d, boolean bool, char c, String str, Number[] n) { }; + + private Rec rec; + + public TestDataJava14() { + rec = new Rec("b".getBytes()[0], (short)1, 2, 3L, 4.0f, 5.0d, true, 'c', "foo", new Number[]{1,2,3}); + } +} diff --git a/test/resources/TestDataJava14-bytebuffer.ser b/test/resources/TestDataJava14-bytebuffer.ser new file mode 100644 index 0000000000000000000000000000000000000000..8d52283ff7d1aeb858d7d1aa86b3d89814bb165c GIT binary patch literal 1906 zcmbtVOHUI~6rOK7rHebK=bU@q_d5VE0z-ik{B|dam`-97 zn$Sedo9s5zE7G*|lJj_#O3H#|L)yHp$;JaMWx1tNQWvUdv2 zt27F^Vk(!;yh9<^8xosVOY`T7uTaRJ>wSfmHT=Gpfq*`n*Gf(fy$NMLvE?smc3Db9 z48w&F*cuh!ER$bwG_2SB8N`;UY6X+n7U`yiWpP8A!tr{B?J*e+54QhD<06{}cKk=d z?;F$>V5cE$oqIz1<@dy>yu)0s-*U%=o+B2s%zMmtofh^OlX>n*3Z3;_=B_84p7Yc^ zzMz7dJP~9uafUKTqP0G&WR84(rip6XhylZYJU@Y5*RmTj^P2kBikRjfakOv9R`B3y zavz{oq5R_Lh`Muud1b$2@+fYv}C9zWTzsTp6)UBW*En5!hd+4cENQBoB=cZc-YAnm7#QaItZ&~R`ayrHebK=bU@q_d5VE0z-ik{B|dam`-97 zn$Sedo9s5zE7G*|lJj_#O3H#|L)yHp$;JaMWx1tNQWvUdv2 zt27F^Vk(!;yh9<^8xosVOY`T7uTaRJ>wSfmHT=Gpfq*`n*Gf(fy$NMLvE?smc3Db9 z48w&F*cuh!ER$bwG_2SB8N`;UY6X+n7U`yiWpP8A!tr{B?J*e+54QhD<06{}cKk=d z?;F$>V5cE$oqIz1<@dy>yu)0s-*U%=o+B2s%zMmtofh^OlX>n*3Z3;_=B_84p7Yc^ zzMz7dJP~9uafUKTqP0G&WR84(rip6XhylZYJU@Y5*RmTj^P2kBikRjfakOv9R`B3y zavz{oq5R_Lh`Muud1b$2@+fYv}C9zWTzsTp6)UBW*En5!hd+4cENQBoB=cZc-YAnm7#QaItZ&~R`ay Date: Wed, 9 Sep 2020 16:50:45 +0100 Subject: [PATCH 12/20] remove child pom config (again) --- pom-main.xml | 83 ----------------------------------------------- pom-versioned.xml | 21 ------------ 2 files changed, 104 deletions(-) diff --git a/pom-main.xml b/pom-main.xml index 60949af23..9b08f9138 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -1,25 +1,4 @@ - 4.0.0 @@ -145,68 +124,6 @@ - - - - jdk11ge - - [11,14) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - - jdk14/** - - - - - - - - - - - jdk14ge - - [14,) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - ${java.vm.specification.version} - - --enable-preview - - - - - - - - - - diff --git a/pom-versioned.xml b/pom-versioned.xml index fdf179481..5137f12a3 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -1,25 +1,4 @@ - 4.0.0 From 260ccca1bf0e5cfb2123d96c854593bb493ee724 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Wed, 9 Sep 2020 17:56:07 +0100 Subject: [PATCH 13/20] remove .ser files and cleanup RecordSerializer --- .../kryo/serializers/RecordSerializer.java | 20 +++++++++++------- test/resources/TestDataJava14-bytebuffer.ser | Bin 1906 -> 0 bytes test/resources/TestDataJava14-standard.ser | Bin 1906 -> 0 bytes 3 files changed, 12 insertions(+), 8 deletions(-) delete mode 100644 test/resources/TestDataJava14-bytebuffer.ser delete mode 100644 test/resources/TestDataJava14-standard.ser diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java index 18e3c61f1..af6d45d40 100644 --- a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -86,7 +86,7 @@ public void write(Kryo kryo, Output output, T object) { if (!isRecord(cls)) { throw new KryoException(object + " is not a record"); } - for (RecordComponent rc : recordComponents(cls)) { + for (RecordComponent rc : recordComponents(cls, Comparator.comparing(RecordComponent::name))) { final Class type = rc.type(); final String name = rc.name(); try { @@ -112,7 +112,8 @@ public T read(Kryo kryo, Input input, Class type) { if (!isRecord(type)) { throw new KryoException("Not a record (" + type + ")"); } - final RecordComponent[] recordComponents = recordComponents(type); + final RecordComponent[] recordComponents = + recordComponents(type, Comparator.comparing(RecordComponent::name)); final Object[] values = new Object[recordComponents.length]; for (int i = 0; i < recordComponents.length; i++) { final RecordComponent rc = recordComponents[i]; @@ -131,6 +132,7 @@ public T read(Kryo kryo, Input input, Class type) { throw ex; } } + Arrays.sort(recordComponents, Comparator.comparing(RecordComponent::index)); return invokeCanonicalConstructor(type, recordComponents, values); } @@ -145,7 +147,7 @@ private boolean isRecord(Class type) { /** A record component, which has a name, a type and an index. * The latter is the index of the record component in the class file's - * record attribute, and required to invoke the record's canonical constructor .*/ + * record attribute, required to invoke the record's canonical constructor .*/ final static class RecordComponent { private final String name; private final Class type; @@ -164,9 +166,12 @@ final static class RecordComponent { /** * Returns an ordered array of the record components for the given record - * class. The order is that of the components' names in ascending order. + * class. The order is imposed by the given comparator. If the given + * comparator is null, the order is that of the record components in the + * record attribute of the class file. */ - private static RecordComponent[] recordComponents(Class type) { + private static RecordComponent[] recordComponents(Class type, + Comparator comparator) { try { Object[] rawComponents = (Object[]) MH_GET_RECORD_COMPONENTS.invokeExact(type); RecordComponent[] recordComponents = new RecordComponent[rawComponents.length]; @@ -176,7 +181,7 @@ private static RecordComponent[] recordComponents(Class type) { (String) MH_GET_NAME.invokeExact(comp), (Class) MH_GET_TYPE.invokeExact(comp), i); } - Arrays.sort(recordComponents, Comparator.comparing(RecordComponent::name)); + if (comparator != null) Arrays.sort(recordComponents, comparator); return recordComponents; } catch (Throwable t) { KryoException ex = new KryoException(t); @@ -203,14 +208,13 @@ private static Object componentValue(Object recordObject, /** * Invokes the canonical constructor of a record class with the - * given argument values in the order given in the class file. + * given argument values. */ private static T invokeCanonicalConstructor(Class recordType, RecordComponent[] recordComponents, Object[] args) { try { Class[] paramTypes = Arrays.stream(recordComponents) - .sorted(Comparator.comparing(RecordComponent::index)) .map(RecordComponent::type) .toArray(Class[]::new); MethodHandle MH_canonicalConstructor = diff --git a/test/resources/TestDataJava14-bytebuffer.ser b/test/resources/TestDataJava14-bytebuffer.ser deleted file mode 100644 index 8d52283ff7d1aeb858d7d1aa86b3d89814bb165c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1906 zcmbtVOHUI~6rOK7rHebK=bU@q_d5VE0z-ik{B|dam`-97 zn$Sedo9s5zE7G*|lJj_#O3H#|L)yHp$;JaMWx1tNQWvUdv2 zt27F^Vk(!;yh9<^8xosVOY`T7uTaRJ>wSfmHT=Gpfq*`n*Gf(fy$NMLvE?smc3Db9 z48w&F*cuh!ER$bwG_2SB8N`;UY6X+n7U`yiWpP8A!tr{B?J*e+54QhD<06{}cKk=d z?;F$>V5cE$oqIz1<@dy>yu)0s-*U%=o+B2s%zMmtofh^OlX>n*3Z3;_=B_84p7Yc^ zzMz7dJP~9uafUKTqP0G&WR84(rip6XhylZYJU@Y5*RmTj^P2kBikRjfakOv9R`B3y zavz{oq5R_Lh`Muud1b$2@+fYv}C9zWTzsTp6)UBW*En5!hd+4cENQBoB=cZc-YAnm7#QaItZ&~R`ayrHebK=bU@q_d5VE0z-ik{B|dam`-97 zn$Sedo9s5zE7G*|lJj_#O3H#|L)yHp$;JaMWx1tNQWvUdv2 zt27F^Vk(!;yh9<^8xosVOY`T7uTaRJ>wSfmHT=Gpfq*`n*Gf(fy$NMLvE?smc3Db9 z48w&F*cuh!ER$bwG_2SB8N`;UY6X+n7U`yiWpP8A!tr{B?J*e+54QhD<06{}cKk=d z?;F$>V5cE$oqIz1<@dy>yu)0s-*U%=o+B2s%zMmtofh^OlX>n*3Z3;_=B_84p7Yc^ zzMz7dJP~9uafUKTqP0G&WR84(rip6XhylZYJU@Y5*RmTj^P2kBikRjfakOv9R`B3y zavz{oq5R_Lh`Muud1b$2@+fYv}C9zWTzsTp6)UBW*En5!hd+4cENQBoB=cZc-YAnm7#QaItZ&~R`ay Date: Wed, 9 Sep 2020 18:00:56 +0100 Subject: [PATCH 14/20] fix typo and empty line --- pom-versioned.xml | 1 + src/com/esotericsoftware/kryo/serializers/RecordSerializer.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pom-versioned.xml b/pom-versioned.xml index 5137f12a3..09c945c8d 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -81,4 +81,5 @@ + diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java index af6d45d40..67fbc9c5b 100644 --- a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -146,7 +146,7 @@ private boolean isRecord(Class type) { } /** A record component, which has a name, a type and an index. - * The latter is the index of the record component in the class file's + * The latter is the index of the record components in the class file's * record attribute, required to invoke the record's canonical constructor .*/ final static class RecordComponent { private final String name; From c66346dbfe674d0c3c40ca73910ad29f5c797d88 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Tue, 15 Sep 2020 18:06:16 +0100 Subject: [PATCH 15/20] add .ser files and add convenience method --- src/com/esotericsoftware/kryo/Kryo.java | 15 ++++++++++++--- test/resources/TestDataJava14-bytebuffer.ser | Bin 0 -> 1906 bytes test/resources/TestDataJava14-standard.ser | Bin 0 -> 1906 bytes 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 test/resources/TestDataJava14-bytebuffer.ser create mode 100644 test/resources/TestDataJava14-standard.ser diff --git a/src/com/esotericsoftware/kryo/Kryo.java b/src/com/esotericsoftware/kryo/Kryo.java index 6df4ff132..2eee05978 100644 --- a/src/com/esotericsoftware/kryo/Kryo.java +++ b/src/com/esotericsoftware/kryo/Kryo.java @@ -229,9 +229,7 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { ImmutableCollectionsSerializers.addDefaultSerializers(this); // Add RecordSerializer if JDK 14+ available if (isClassAvailable("java.lang.Record")) { - try { - addDefaultSerializer(Class.forName("java.lang.Record"), RecordSerializer.class); - } catch (ClassNotFoundException ignored) {} // handled in if-clause + addDefaultSerializer("java.lang.Record", RecordSerializer.class); } lowPriorityDefaultSerializerCount = defaultSerializers.size(); @@ -284,6 +282,17 @@ public void addDefaultSerializer (Class type, SerializerFactory serializerFactor insertDefaultSerializer(type, serializerFactory); } + /** Instances with the specified class name will use the specified serializer when {@link #register(Class)} or + * {@link #register(Class, int)} are called. + * @see #setDefaultSerializer(Class) */ + public void addDefaultSerializer(String className, Class serializer) { + try { + addDefaultSerializer(Class.forName(className), serializer); + } catch (ClassNotFoundException e) { + throw new KryoException("default serializer cannot be added: " + className); + } + } + /** Instances of the specified class will use the specified serializer when {@link #register(Class)} or * {@link #register(Class, int)} are called. Serializer instances are created as needed via * {@link ReflectionSerializerFactory#newSerializer(Kryo, Class, Class)}. By default, the following classes have a default diff --git a/test/resources/TestDataJava14-bytebuffer.ser b/test/resources/TestDataJava14-bytebuffer.ser new file mode 100644 index 0000000000000000000000000000000000000000..8d52283ff7d1aeb858d7d1aa86b3d89814bb165c GIT binary patch literal 1906 zcmbtVOHUI~6rOK7rHebK=bU@q_d5VE0z-ik{B|dam`-97 zn$Sedo9s5zE7G*|lJj_#O3H#|L)yHp$;JaMWx1tNQWvUdv2 zt27F^Vk(!;yh9<^8xosVOY`T7uTaRJ>wSfmHT=Gpfq*`n*Gf(fy$NMLvE?smc3Db9 z48w&F*cuh!ER$bwG_2SB8N`;UY6X+n7U`yiWpP8A!tr{B?J*e+54QhD<06{}cKk=d z?;F$>V5cE$oqIz1<@dy>yu)0s-*U%=o+B2s%zMmtofh^OlX>n*3Z3;_=B_84p7Yc^ zzMz7dJP~9uafUKTqP0G&WR84(rip6XhylZYJU@Y5*RmTj^P2kBikRjfakOv9R`B3y zavz{oq5R_Lh`Muud1b$2@+fYv}C9zWTzsTp6)UBW*En5!hd+4cENQBoB=cZc-YAnm7#QaItZ&~R`ayrHebK=bU@q_d5VE0z-ik{B|dam`-97 zn$Sedo9s5zE7G*|lJj_#O3H#|L)yHp$;JaMWx1tNQWvUdv2 zt27F^Vk(!;yh9<^8xosVOY`T7uTaRJ>wSfmHT=Gpfq*`n*Gf(fy$NMLvE?smc3Db9 z48w&F*cuh!ER$bwG_2SB8N`;UY6X+n7U`yiWpP8A!tr{B?J*e+54QhD<06{}cKk=d z?;F$>V5cE$oqIz1<@dy>yu)0s-*U%=o+B2s%zMmtofh^OlX>n*3Z3;_=B_84p7Yc^ zzMz7dJP~9uafUKTqP0G&WR84(rip6XhylZYJU@Y5*RmTj^P2kBikRjfakOv9R`B3y zavz{oq5R_Lh`Muud1b$2@+fYv}C9zWTzsTp6)UBW*En5!hd+4cENQBoB=cZc-YAnm7#QaItZ&~R`ay Date: Fri, 18 Sep 2020 12:44:56 +0100 Subject: [PATCH 16/20] move jdk14 tests and add with build-helper-maven-plugin --- pom-main.xml | 25 +++++ pom-versioned.xml | 27 +++++- pom.xml | 95 ++++++------------- .../kryo}/TestDataJava14.java | 2 +- .../serializers}/RecordSerializerTest.java | 2 +- .../kryo/SerializationCompatTest.java | 14 ++- 6 files changed, 90 insertions(+), 75 deletions(-) rename {test/jdk14 => test-jdk14/com/esotericsoftware/kryo}/TestDataJava14.java (98%) rename {test/jdk14 => test-jdk14/com/esotericsoftware/kryo/serializers}/RecordSerializerTest.java (99%) diff --git a/pom-main.xml b/pom-main.xml index 9b08f9138..b17159b5b 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -34,6 +55,10 @@ + + org.codehaus.mojo + build-helper-maven-plugin + org.apache.maven.plugins maven-jar-plugin diff --git a/pom-versioned.xml b/pom-versioned.xml index 09c945c8d..74fb1eda8 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -1,4 +1,25 @@ + 4.0.0 @@ -25,6 +46,10 @@ + + org.codehaus.mojo + build-helper-maven-plugin + org.apache.maven.plugins maven-jar-plugin @@ -39,7 +64,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.1 + 3.2.4 package diff --git a/pom.xml b/pom.xml index bcbbb02b2..a97c6f2bc 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,12 @@ + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + org.apache.maven.plugins maven-compiler-plugin @@ -195,7 +201,6 @@ - until-jdk11 @@ -207,24 +212,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - - jdk14/** - - - - - org.apache.maven.plugins maven-surefire-plugin @@ -232,7 +219,6 @@ jdk11/** - jdk14/** @@ -240,74 +226,49 @@ - + - jdk11ge + jdk14ge - [11,14) + [14, - org.apache.maven.plugins - maven-compiler-plugin + org.codehaus.mojo + build-helper-maven-plugin - default-testCompile - test-compile + add-test-source + generate-test-sources - testCompile + add-test-source - - jdk14/** - + + test-jdk14 + org.apache.maven.plugins - maven-surefire-plugin + maven-compiler-plugin - - jdk14/** - + true + + ${java.vm.specification.version} + ${java.vm.specification.version} + + -parameters + --enable-preview + --add-exports=java.base/jdk.internal.ref=ALL-UNNAMED + --add-exports=java.base/sun.nio.ch=ALL-UNNAMED + - - - - - - - jdk14ge - - [14,) - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-testCompile - test-compile - - testCompile - - - ${java.vm.specification.version} - - --enable-preview - - - - - org.apache.maven.plugins maven-surefire-plugin diff --git a/test/jdk14/TestDataJava14.java b/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java similarity index 98% rename from test/jdk14/TestDataJava14.java rename to test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java index 24fbde92b..ce8938fa7 100644 --- a/test/jdk14/TestDataJava14.java +++ b/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java @@ -18,7 +18,7 @@ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package jdk14; +package com.esotericsoftware.kryo; import com.esotericsoftware.kryo.SerializationCompatTestData; diff --git a/test/jdk14/RecordSerializerTest.java b/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java similarity index 99% rename from test/jdk14/RecordSerializerTest.java rename to test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java index 3040ec0c8..81edb81ed 100644 --- a/test/jdk14/RecordSerializerTest.java +++ b/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java @@ -18,7 +18,7 @@ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package jdk14; +package com.esotericsoftware.kryo.serializers; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoTestCase; diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 418b341ee..814cc2460 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -77,10 +77,14 @@ public class SerializationCompatTest extends KryoTestCase { private static final int JAVA_VERSION; static { - // java.version is e.g. 1.8.0 or 9.0.4 + // java.version is e.g. "1.8.0", "9.0.4", or "14" String[] strVersions = System.getProperty("java.version").split("\\."); - int[] versions = new int[] {parseInt(strVersions[0]), parseInt(strVersions[1])}; - JAVA_VERSION = versions[0] > 1 ? versions[0] : versions[1]; + if (strVersions.length == 1) { + JAVA_VERSION = parseInt(strVersions[0]); + } else { + int[] versions = new int[] {parseInt(strVersions[0]), parseInt(strVersions[1])}; + JAVA_VERSION = versions[0] > 1 ? versions[0] : versions[1]; + } } private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 11 ? 57 : JAVA_VERSION < 14 ? 67 : 68; // Also change Kryo#defaultSerializers. @@ -90,12 +94,12 @@ public class SerializationCompatTest extends KryoTestCase { TEST_DATAS.add(new TestDataDescription<>(new TestData(), 1940, 1958)); if (JAVA_VERSION >= 8) TEST_DATAS.add(new TestDataDescription<>(new TestDataJava8(), 2098, 2116)); if (JAVA_VERSION >= 11) TEST_DATAS.add(new TestDataDescription<>(new TestDataJava11(), 2210, 2238)); - if (JAVA_VERSION >= 14) TEST_DATAS.add(new TestDataDescription<>(createTestDataJava14(), 1928, 1946)); + if (JAVA_VERSION >= 14) TEST_DATAS.add(new TestDataDescription<>(createTestDataJava14(), 1948, 1966)); }; private static TestData createTestDataJava14() { try { - return (TestData) Class.forName("jdk14.TestDataJava14").getConstructor().newInstance(); + return (TestData) Class.forName("com.esotericsoftware.kryo.TestDataJava14").getConstructor().newInstance(); } catch (ReflectiveOperationException e) { throw new RuntimeException("TestDataJava14 could not be instantiated", e); } From 9d7ff95f8ceaeace36f127e7b93bf97c37e25ad9 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Wed, 23 Sep 2020 10:36:19 +0100 Subject: [PATCH 17/20] consolidate jdk11 and jdk14 tests, cleanup imports and debugging output --- pom.xml | 36 ++++++---- .../kryo/serializers/RecordSerializer.java | 8 +-- .../esotericsoftware/kryo/TestDataJava11.java | 51 +++++++++++++ .../ImmutableCollectionsSerializersTest.java | 2 +- .../serializers/RecordSerializerTest.java | 68 ++---------------- .../kryo/SerializationCompatTest.java | 11 ++- .../kryo/SerializationCompatTestData.java | 24 ------- test/resources/TestDataJava11-bytebuffer.ser | Bin 2140 -> 2140 bytes test/resources/TestDataJava11-standard.ser | Bin 2140 -> 2140 bytes 9 files changed, 88 insertions(+), 112 deletions(-) create mode 100644 test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java rename {test/jdk11 => test-jdk11/com/esotericsoftware/kryo/serializers}/ImmutableCollectionsSerializersTest.java (98%) diff --git a/pom.xml b/pom.xml index a97c6f2bc..cd5bedaa0 100644 --- a/pom.xml +++ b/pom.xml @@ -200,27 +200,36 @@ - + - until-jdk11 + jdk11ge - - [1.5,11) + [11, + + org.codehaus.mojo + build-helper-maven-plugin + + + add-test-source + generate-test-sources + + add-test-source + + + + test-jdk11 + + + + + org.apache.maven.plugins - maven-surefire-plugin - 2.22.0 - - - jdk11/** - - + maven-compiler-plugin @@ -247,6 +256,7 @@ + test-jdk11 test-jdk14 diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java index 67fbc9c5b..4769a178b 100644 --- a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -20,6 +20,10 @@ package com.esotericsoftware.kryo.serializers; +import static com.esotericsoftware.minlog.Log.TRACE; +import static com.esotericsoftware.minlog.Log.trace; +import static java.lang.invoke.MethodType.methodType; + import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.io.Input; @@ -31,10 +35,6 @@ import java.util.Arrays; import java.util.Comparator; -import static com.esotericsoftware.minlog.Log.TRACE; -import static com.esotericsoftware.minlog.Log.trace; -import static java.lang.invoke.MethodType.methodType; - public class RecordSerializer extends ImmutableSerializer { private static final MethodHandle MH_IS_RECORD; private static final MethodHandle MH_GET_RECORD_COMPONENTS; diff --git a/test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java b/test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java new file mode 100644 index 000000000..0bfca86ef --- /dev/null +++ b/test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java @@ -0,0 +1,51 @@ +/* Copyright (c) 2008-2020, Nathan Sweet + * Copyright (C) 2020, Oracle and/or its affiliates. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the distribution. + * - Neither the name of Esoteric Software nor the names of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +package com.esotericsoftware.kryo; + +import com.esotericsoftware.kryo.SerializationCompatTestData; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class TestDataJava11 extends SerializationCompatTestData.TestData { + private List emptyImmutableList; + private Map emptyImmutableMap; + private Set emptyImmutableSet; + private List singleImmutableList; + private Map singleImmutableMap; + private Set singleImmutableSet; + private List immutableList; + private Map immutableMap; + private Set immutableSet; + + public TestDataJava11 () { + emptyImmutableList = List.of(); + emptyImmutableMap = Map.of(); + emptyImmutableSet = Set.of(); + singleImmutableList = List.of(42); + singleImmutableMap = Map.of(42, 42); + singleImmutableSet = Set.of(42); + immutableList = List.of(1, 2, 3); + immutableMap = Map.of(1, 2, 3, 4); + immutableSet = Set.of(1, 2, 3); + } +} diff --git a/test/jdk11/ImmutableCollectionsSerializersTest.java b/test-jdk11/com/esotericsoftware/kryo/serializers/ImmutableCollectionsSerializersTest.java similarity index 98% rename from test/jdk11/ImmutableCollectionsSerializersTest.java rename to test-jdk11/com/esotericsoftware/kryo/serializers/ImmutableCollectionsSerializersTest.java index 0f759c3c8..e75f16d4b 100644 --- a/test/jdk11/ImmutableCollectionsSerializersTest.java +++ b/test-jdk11/com/esotericsoftware/kryo/serializers/ImmutableCollectionsSerializersTest.java @@ -17,7 +17,7 @@ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package jdk11; +package com.esotericsoftware.kryo.serializers; import com.esotericsoftware.kryo.KryoTestCase; diff --git a/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java b/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java index 81edb81ed..689033fd7 100644 --- a/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java +++ b/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java @@ -20,18 +20,17 @@ package com.esotericsoftware.kryo.serializers; +import static org.junit.Assert.assertEquals; + import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoTestCase; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; -import org.junit.Test; import java.lang.reflect.Array; -import java.util.Arrays; import java.util.stream.IntStream; -import static java.lang.System.out; -import static org.junit.Assert.assertEquals; +import org.junit.Test; public class RecordSerializerTest extends KryoTestCase { { @@ -43,21 +42,16 @@ public record RecordRectangle (String height, int width, long x, double y) { } @Test public void testBasicRecord() { - out.println("testBasicRecord\n"); kryo.register(RecordRectangle.class); final var r1 = new RecordRectangle("one", 2, 3L, 4.0); final var output = new Output(32); kryo.writeObject(output, r1); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, RecordRectangle.class); - out.println("Deserialized record: \n" + r2); doAssertEquals(r1, r2); roundTrip(14, r1); - out.println("------\n"); } /** Test where the single object is an empty record. */ @@ -65,21 +59,16 @@ public record EmptyRecord () { } @Test public void testEmptyRecord() { - out.println("testEmptyRecord\n"); kryo.register(EmptyRecord.class); final var r1 = new EmptyRecord(); final var output = new Output(32); kryo.writeObject(output, r1); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, EmptyRecord.class); - out.println("Deserialized record: \n" + r2); doAssertEquals(r1, r2); roundTrip(1, r1); - out.println("------\n"); } /** Test deserialisation of an empty record where the input provides values. @@ -87,14 +76,10 @@ public void testEmptyRecord() { */ @Test public void testDeserializeEmptyRecordWithValues() { - out.println("testDeserializeEmptyRecordWithValues\n"); kryo.register(EmptyRecord.class); final var input = new Input(new byte[]{1, 2, -128, 0}); // create bad input - out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); final var r = kryo.readObject(input, EmptyRecord.class); - out.println("Deserialized record: \n" + r); - out.println("------\n"); } /** Test deserialisation of a record where the number of input values exceeds @@ -106,16 +91,13 @@ public record RecordPoint (int x, int y) { } @Test public void testDeserializeWrongNumberOfValues() { - out.println("testDeserializeWrongNumberOfValues\n"); kryo.register(RecordPoint.class); final var r1 = new RecordPoint(1,1); final var input = new Input(new byte[]{2, 2, 2}); // create bad input - out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); final var r2 = kryo.readObject(input, RecordPoint.class); - out.println("Deserialized record: \n" + r2); + doAssertEquals(r1, r2); - out.println("------\n"); } /** Test where the record has an explicit constructor.*/ @@ -127,21 +109,16 @@ public RecordWithConstructor(String height) { @Test public void testRecordWithConstructor() { - out.println("testRecordWithConstructor\n"); kryo.register(RecordWithConstructor.class); final var r1 = new RecordWithConstructor("ten"); final var output = new Output(32); kryo.writeObject(output, r1); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, RecordWithConstructor.class); - out.println("Deserialized record: \n" + r2); doAssertEquals(r1, r2); roundTrip(14, r1); - out.println("------\n"); } /** Test where the record component object is a record. */ @@ -149,28 +126,22 @@ public record RecordOfRecord (RecordRectangle r) { } @Test public void testRecordOfRecord() { - out.println("testRecordOfRecord\n"); kryo.register(RecordOfRecord.class); kryo.register(RecordRectangle.class); final var r1 = new RecordOfRecord(new RecordRectangle("one", 2, 3L, 4.0)); final var output = new Output(32); kryo.writeObject(output, r1); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, RecordOfRecord.class); - out.println("Deserialized record: \n" + r2); doAssertEquals(r1, r2); roundTrip(15, r1); - out.println("------\n"); } /** Test where the single object is an array of records. */ @Test public void testArrayOfRecords() { - out.println("testArrayOfRecords\n"); kryo.register(RecordPoint.class); kryo.register(RecordPoint[].class); @@ -185,7 +156,6 @@ public record RecordWithArray (RecordRectangle[] recordArray) { } @Test public void testRecordWithArray() { - out.println("testRecordWithArray\n"); kryo.register(RecordWithArray.class); kryo.register(RecordRectangle[].class); kryo.register(RecordRectangle.class); @@ -193,14 +163,10 @@ public void testRecordWithArray() { final var r1 = new RecordWithArray(new RecordRectangle[] {new RecordRectangle("one", 2, 3L, 4.0)}); final var output = new Output(32); kryo.writeObject(output, r1); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, RecordWithArray.class); - out.println("Deserialized record: \n" + r2); assertWithArrayEquals(r1, r2); - out.println("------\n"); } private void assertWithArrayEquals(final RecordWithArray expected, @@ -221,7 +187,6 @@ public record RecordWithNull (Object o, Number n, String s) { } @Test public void testRecordWithNull() { - out.println("testRecordWithNull\n"); kryo.register(RecordWithNull.class); kryo.register(Object.class); kryo.register(Number.class); @@ -230,15 +195,11 @@ public void testRecordWithNull() { final var r1 = new RecordWithNull(null, null, null); final var output = new Output(32); kryo.writeObject(output, r1); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, RecordWithNull.class); - out.println("Deserialized record: \n" + r2); doAssertEquals(r1, r2); roundTrip(4, r1); - out.println("------\n"); } /** Test where record components are primitives with their default values. */ @@ -246,22 +207,17 @@ public record RecordWithDefaultValues(byte b, short s, int i, long l, float f, d @Test public void testRecordWithPrimitiveDefaultValues() { - out.println("testRecordWithPrimitiveDefaultValues\n"); kryo.register(RecordWithDefaultValues.class); final var r1 = new RecordWithDefaultValues( (byte)0, (short)0, 0, 0l, 0.0f, 0.0d, '\u0000', false); final var output = new Output(32); kryo.writeObject(output, r1); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, RecordWithDefaultValues.class); - out.println("Deserialized record: \n" + r2); doAssertEquals(r1, r2); roundTrip(21, r1); - out.println("------\n"); } /** Test where the an exception is thrown in the record constructor. */ @@ -276,28 +232,22 @@ public record PositivePoint (int x, int y) { @Test public void testDeserializeRecordWithIllegalValue1() { - out.println("testDeserializeRecordWithIllegalValue1\n"); kryo.register(PositivePoint.class); final var input = new Input(new byte[]{1, 2}); // create bad input of -1, 1 - out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); var e = expectThrows(IllegalArgumentException.class, () -> kryo.readObject(input, PositivePoint.class)); assertEquals("negative x:-1", e.getMessage()); - out.println("------\n"); } @Test public void testDeserializeRecordWithIllegalValue2() { - out.println("testDeserializeRecordWithIllegalValue2\n"); kryo.register(PositivePoint.class); final var input = new Input(new byte[]{2, 1}); // create bad input of 1, -1 - out.println("Serialized record: \n" + Arrays.toString(input.getBuffer())); var e = expectThrows(IllegalArgumentException.class, () -> kryo.readObject(input, PositivePoint.class)); assertEquals("negative y:-1", e.getMessage()); - out.println("------\n"); } static T expectThrows(Class throwableClass, Runnable task) { @@ -323,39 +273,29 @@ public record R2 (String s, int i, long l) { } @Test public void testRecordWithParametersReordered1() { - out.println("testRecordWithParametersReordered1\n"); kryo.register(R.class); kryo.register(R1.class); final var r = new R(1L, 1, "foo"); final var output = new Output(32); kryo.writeObject(output, r); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r1 = kryo.readObject(input, R1.class); - out.println("Deserialized record: \n" + r1); roundTrip(6, r1); - out.println("------\n"); } @Test public void testRecordWithParametersReordered2() { - out.println("testRecordWithParametersReordered2\n"); kryo.register(R.class); kryo.register(R2.class); final var r = new R(1L, 1, "foo"); final var output = new Output(32); kryo.writeObject(output, r); - out.println("Serialized record: \n" + Arrays.toString(output.getBuffer())); - final var input = new Input(output.getBuffer(), 0, output.position()); final var r2 = kryo.readObject(input, R2.class); - out.println("Deserialized record: \n" + r2); roundTrip(6, r2); - out.println("------\n"); } } diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 814cc2460..814e9ff80 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -25,7 +25,6 @@ import static org.junit.Assert.*; import com.esotericsoftware.kryo.SerializationCompatTestData.TestData; -import com.esotericsoftware.kryo.SerializationCompatTestData.TestDataJava11; import com.esotericsoftware.kryo.SerializationCompatTestData.TestDataJava8; import com.esotericsoftware.kryo.io.ByteBufferInput; import com.esotericsoftware.kryo.io.ByteBufferOutput; @@ -93,15 +92,15 @@ public class SerializationCompatTest extends KryoTestCase { static { TEST_DATAS.add(new TestDataDescription<>(new TestData(), 1940, 1958)); if (JAVA_VERSION >= 8) TEST_DATAS.add(new TestDataDescription<>(new TestDataJava8(), 2098, 2116)); - if (JAVA_VERSION >= 11) TEST_DATAS.add(new TestDataDescription<>(new TestDataJava11(), 2210, 2238)); - if (JAVA_VERSION >= 14) TEST_DATAS.add(new TestDataDescription<>(createTestDataJava14(), 1948, 1966)); + if (JAVA_VERSION >= 11) TEST_DATAS.add(new TestDataDescription<>(createTestData(11), 2182, 2210)); + if (JAVA_VERSION >= 14) TEST_DATAS.add(new TestDataDescription<>(createTestData(14), 1948, 1966)); }; - private static TestData createTestDataJava14() { + private static TestData createTestData(int version) { try { - return (TestData) Class.forName("com.esotericsoftware.kryo.TestDataJava14").getConstructor().newInstance(); + return (TestData) Class.forName("com.esotericsoftware.kryo.TestDataJava" + version).getConstructor().newInstance(); } catch (ReflectiveOperationException e) { - throw new RuntimeException("TestDataJava14 could not be instantiated", e); + throw new RuntimeException("TestDataJava" + version + " could not be instantiated", e); } } diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTestData.java b/test/com/esotericsoftware/kryo/SerializationCompatTestData.java index c9877147b..c0fe41434 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTestData.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTestData.java @@ -115,30 +115,6 @@ static class TestDataJava8 extends TestData { } } - static class TestDataJava11 extends TestData { - private List emptyImmutableList; - private Map emptyImmutableMap; - private Set emptyImmutableSet; - private List singleImmutableList; - private Map singleImmutableMap; - private Set singleImmutableSet; - private List immutableList; - private Map immutableMap; - private Set immutableSet; - - TestDataJava11 () { - emptyImmutableList = List.of(); - emptyImmutableMap = Map.of(); - emptyImmutableSet = Set.of(); - singleImmutableList = List.of(42); - singleImmutableMap = Map.of(42, 42); - singleImmutableSet = Set.of(42); - immutableList = List.of(1, 2, 3); - immutableMap = Map.of(1, 2, 3, 4); - immutableSet = Set.of(1, 2, 3); - } - } - public static class TestData implements Serializable { private boolean _boolean; private char _char; diff --git a/test/resources/TestDataJava11-bytebuffer.ser b/test/resources/TestDataJava11-bytebuffer.ser index d5b9a1ee110034db1f67c64d95803e105c8e501f..cd1a3487b0f942f310edf14007f6415894697e8d 100644 GIT binary patch delta 23 ecmca3a7SRnRd#MB7B&t>HAWUTCYH$$*lPezECsRv delta 23 ecmca3a7SRnRd#MR4ki{xHAWUD7PiR`*lPezHU+W( diff --git a/test/resources/TestDataJava11-standard.ser b/test/resources/TestDataJava11-standard.ser index d5b9a1ee110034db1f67c64d95803e105c8e501f..cd1a3487b0f942f310edf14007f6415894697e8d 100644 GIT binary patch delta 23 ecmca3a7SRnRd#MB7B&t>HAWUTCYH$$*lPezECsRv delta 23 ecmca3a7SRnRd#MR4ki{xHAWUD7PiR`*lPezHU+W( From e5e64224dfc07f331ad32c6890a7b920eb1bbd87 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Tue, 9 Mar 2021 10:12:30 +0000 Subject: [PATCH 18/20] add author tags --- CONTRIBUTING.md | 2 +- pom-main.xml | 21 ------------------- pom-versioned.xml | 21 ------------------- src/com/esotericsoftware/kryo/Kryo.java | 2 -- .../kryo/serializers/RecordSerializer.java | 5 ++++- .../kryo/unsafe/UnsafeUtil.java | 1 - .../esotericsoftware/kryo/TestDataJava11.java | 1 - .../esotericsoftware/kryo/TestDataJava14.java | 5 ++++- .../serializers/RecordSerializerTest.java | 9 +++++--- 9 files changed, 15 insertions(+), 52 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4aa48ef0..ccbdfee20 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,4 +4,4 @@ Project license(s): 3-Clause BSD License * You will only Submit Contributions to which You have the necessary rights. This means that if You are employed You have received the necessary permissions from Your employer to make the Contributions. -* Whatever content You Contribute will be provided under the Project License(s). +* Whatever content You Contribute will be under the copyright and license listed in LICENSE.md. diff --git a/pom-main.xml b/pom-main.xml index 51ab8086d..342c3474a 100644 --- a/pom-main.xml +++ b/pom-main.xml @@ -1,25 +1,4 @@ - 4.0.0 diff --git a/pom-versioned.xml b/pom-versioned.xml index a3f8d1eb9..a92f82206 100644 --- a/pom-versioned.xml +++ b/pom-versioned.xml @@ -1,25 +1,4 @@ - 4.0.0 diff --git a/src/com/esotericsoftware/kryo/Kryo.java b/src/com/esotericsoftware/kryo/Kryo.java index 74ae99a07..f1a5a0eca 100644 --- a/src/com/esotericsoftware/kryo/Kryo.java +++ b/src/com/esotericsoftware/kryo/Kryo.java @@ -1,5 +1,4 @@ /* Copyright (c) 2008-2020, Nathan Sweet - * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -247,7 +246,6 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver) { register(short.class, new ShortSerializer()); register(long.class, new LongSerializer()); register(double.class, new DoubleSerializer()); - } // --- Default serializers --- diff --git a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java index 4769a178b..486a37f0d 100644 --- a/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java +++ b/src/com/esotericsoftware/kryo/serializers/RecordSerializer.java @@ -1,5 +1,4 @@ /* Copyright (c) 2008-2020, Nathan Sweet - * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -35,6 +34,10 @@ import java.util.Arrays; import java.util.Comparator; +/** Serializer for record classes. + * @author Julia Boes + * @author Chris Hegarty + */ public class RecordSerializer extends ImmutableSerializer { private static final MethodHandle MH_IS_RECORD; private static final MethodHandle MH_GET_RECORD_COMPONENTS; diff --git a/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java b/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java index 7d5b830ec..d50f417e5 100644 --- a/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java +++ b/src/com/esotericsoftware/kryo/unsafe/UnsafeUtil.java @@ -1,5 +1,4 @@ /* Copyright (c) 2008-2020, Nathan Sweet - * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following diff --git a/test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java b/test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java index 0bfca86ef..cb87eb959 100644 --- a/test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java +++ b/test-jdk11/com/esotericsoftware/kryo/TestDataJava11.java @@ -1,5 +1,4 @@ /* Copyright (c) 2008-2020, Nathan Sweet - * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following diff --git a/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java b/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java index ce8938fa7..dba04c874 100644 --- a/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java +++ b/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java @@ -1,5 +1,4 @@ /* Copyright (c) 2008-2020, Nathan Sweet - * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -22,6 +21,10 @@ import com.esotericsoftware.kryo.SerializationCompatTestData; +/** Test data for {@link com.esotericsoftware.kryo.serializers.RecordSerializerTest}. + * @author Julia Boes + * @author Chris Hegarty + */ public class TestDataJava14 extends SerializationCompatTestData.TestData { public record Rec (byte b, short s, int i, long l, float f, double d, boolean bool, char c, String str, Number[] n) { }; diff --git a/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java b/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java index 689033fd7..3fac7878e 100644 --- a/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java +++ b/test-jdk14/com/esotericsoftware/kryo/serializers/RecordSerializerTest.java @@ -1,5 +1,4 @@ /* Copyright (c) 2008-2020, Nathan Sweet - * Copyright (C) 2020, Oracle and/or its affiliates. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -20,7 +19,7 @@ package com.esotericsoftware.kryo.serializers; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.esotericsoftware.kryo.KryoException; import com.esotericsoftware.kryo.KryoTestCase; @@ -30,8 +29,12 @@ import java.lang.reflect.Array; import java.util.stream.IntStream; -import org.junit.Test; +import org.junit.jupiter.api.Test; +/** Test for {@link RecordSerializer}. + * @author Julia Boes + * @author Chris Hegarty + */ public class RecordSerializerTest extends KryoTestCase { { supportsCopy = false; From 18df8387186e1e6473a2ebdbbb830642eea496c0 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Tue, 9 Mar 2021 12:39:32 +0000 Subject: [PATCH 19/20] update ser test/resources and serializer count --- .../kryo/SerializationCompatTest.java | 2 +- test/resources/TestDataJava11-bytebuffer.ser | Bin 2140 -> 2140 bytes test/resources/TestDataJava11-standard.ser | Bin 2140 -> 2140 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 30fba4ff9..5b5521ddb 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -84,7 +84,7 @@ class SerializationCompatTest extends KryoTestCase { } } private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 11 - ? 57 : JAVA_VERSION < 14 ? 67 : 68; // Also change Kryo#defaultSerializers. + ? 57 : JAVA_VERSION < 14 ? 68 : 69; // Also change Kryo#defaultSerializers. private static final List TEST_DATAS = new ArrayList<>(); static { diff --git a/test/resources/TestDataJava11-bytebuffer.ser b/test/resources/TestDataJava11-bytebuffer.ser index cd1a3487b0f942f310edf14007f6415894697e8d..3b83f5d33e61f32b61ec690c85dc288549cb67dc 100644 GIT binary patch delta 15 Wcmca3a7SRnU3O+B7PiR`*lPeV0tIpa delta 15 Wcmca3a7SRnU3O+RCYH$$*lPeV2nBKg diff --git a/test/resources/TestDataJava11-standard.ser b/test/resources/TestDataJava11-standard.ser index cd1a3487b0f942f310edf14007f6415894697e8d..3b83f5d33e61f32b61ec690c85dc288549cb67dc 100644 GIT binary patch delta 15 Wcmca3a7SRnU3O+B7PiR`*lPeV0tIpa delta 15 Wcmca3a7SRnU3O+RCYH$$*lPeV2nBKg From de170a76a829816e57fbfaec2a49c18e510bafc1 Mon Sep 17 00:00:00 2001 From: Julia Boes Date: Tue, 9 Mar 2021 13:08:17 +0000 Subject: [PATCH 20/20] fix serializier count --- .../kryo/SerializationCompatTest.java | 2 +- test/resources/TestDataJava11-bytebuffer.ser | Bin 2140 -> 2140 bytes test/resources/TestDataJava11-standard.ser | Bin 2140 -> 2140 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTest.java b/test/com/esotericsoftware/kryo/SerializationCompatTest.java index 5b5521ddb..24c52503d 100644 --- a/test/com/esotericsoftware/kryo/SerializationCompatTest.java +++ b/test/com/esotericsoftware/kryo/SerializationCompatTest.java @@ -84,7 +84,7 @@ class SerializationCompatTest extends KryoTestCase { } } private static final int EXPECTED_DEFAULT_SERIALIZER_COUNT = JAVA_VERSION < 11 - ? 57 : JAVA_VERSION < 14 ? 68 : 69; // Also change Kryo#defaultSerializers. + ? 58 : JAVA_VERSION < 14 ? 68 : 69; // Also change Kryo#defaultSerializers. private static final List TEST_DATAS = new ArrayList<>(); static { diff --git a/test/resources/TestDataJava11-bytebuffer.ser b/test/resources/TestDataJava11-bytebuffer.ser index 3b83f5d33e61f32b61ec690c85dc288549cb67dc..8fc2a16e2808485e088aa615f2e35ba301db68ca 100644 GIT binary patch delta 23 ecmca3a7SRnRd#MR4ki{xHAWT|Hm1oB*lPezJO#1< delta 23 dcmca3a7SRnRd#MB7B&t>HAWU7J^2B94FFCi1+oAD diff --git a/test/resources/TestDataJava11-standard.ser b/test/resources/TestDataJava11-standard.ser index 3b83f5d33e61f32b61ec690c85dc288549cb67dc..8fc2a16e2808485e088aa615f2e35ba301db68ca 100644 GIT binary patch delta 23 ecmca3a7SRnRd#MR4ki{xHAWT|Hm1oB*lPezJO#1< delta 23 dcmca3a7SRnRd#MB7B&t>HAWU7J^2B94FFCi1+oAD