diff --git a/README.md b/README.md
index 534811748..06b49b8dd 100644
--- a/README.md
+++ b/README.md
@@ -617,12 +617,11 @@ Kryo `isFinal` is used to determine if a class is final. This method can be over
Kryo can serialize Java 8+ closures that implement java.io.Serializable, with some caveats. Closures serialized on one JVM may fail to be deserialized on a different JVM.
-Kryo `isClosure` is used to determine if a class is a closure. If so, then ClosureSerializer.Closure is used to find the class registration instead of the closure's class. To serialize closures, the following classes must be registered: ClosureSerializer.Closure, SerializedLambda, Object[], and Class. Additionally, the closure's capturing class must be registered.
+Kryo `isClosure` is used to determine if a class is a closure. If so, then ClosureSerializer.Closure is used to find the class registration instead of the closure's class. To serialize closures, the following classes must be registered: ClosureSerializer.Closure, Object[], and Class. Additionally, the closure's capturing class must be registered.
```java
kryo.register(Object[].class);
kryo.register(Class.class);
-kryo.register(SerializedLambda.class);
kryo.register(ClosureSerializer.Closure.class, new ClosureSerializer());
kryo.register(CapturingClass.class);
diff --git a/pom.xml b/pom.xml
index 13ddb18f3..9518c3538 100644
--- a/pom.xml
+++ b/pom.xml
@@ -436,15 +436,9 @@
--enable-preview
- --add-opens java.base/java.lang=ALL-UNNAMED
- --add-opens java.base/java.lang.invoke=ALL-UNNAMED
- --add-opens=java.base/java.net=ALL-UNNAMED
- --add-opens=java.base/java.nio=ALL-UNNAMED
- --add-opens=java.base/java.time=ALL-UNNAMED
- --add-opens=java.base/java.util=ALL-UNNAMED
+ --add-opens java.base/sun.nio.ch=ALL-UNNAMED
+ --add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED
- --add-opens=java.base/sun.nio.ch=ALL-UNNAMED
- --add-opens=java.base/sun.util.calendar=ALL-UNNAMED
diff --git a/src/com/esotericsoftware/kryo/serializers/ClosureSerializer.java b/src/com/esotericsoftware/kryo/serializers/ClosureSerializer.java
index 8f8c19fc5..977e51d0f 100644
--- a/src/com/esotericsoftware/kryo/serializers/ClosureSerializer.java
+++ b/src/com/esotericsoftware/kryo/serializers/ClosureSerializer.java
@@ -27,6 +27,7 @@
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.minlog.Log;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
@@ -36,7 +37,6 @@
*
* kryo.register(Object[].class);
* kryo.register(Class.class);
- * kryo.register(java.lang.invoke.SerializedLambda.class);
* kryo.register(ClosureSerializer.Closure.class, new ClosureSerializer());
*
* Also, the closure's capturing class must be registered.
@@ -56,7 +56,9 @@ public ClosureSerializer () {
readResolve = SerializedLambda.class.getDeclaredMethod("readResolve");
readResolve.setAccessible(true);
} catch (Exception ex) {
- throw new KryoException("Unable to obtain SerializedLambda#readResolve via reflection.", ex);
+ readResolve = null;
+ Log.warn("Unable to obtain SerializedLambda#readResolve via reflection. " +
+ "Falling back on resolving lambdas via capturing class.", ex);
}
}
}
@@ -87,11 +89,12 @@ public Object read (Kryo kryo, Input input, Class type) {
Object[] capturedArgs = new Object[count];
for (int i = 0; i < count; i++)
capturedArgs[i] = kryo.readClassAndObject(input);
- SerializedLambda serializedLambda = new SerializedLambda(kryo.readClass(input).getType(), input.readString(),
+ Class> capturingClass = kryo.readClass(input).getType();
+ SerializedLambda serializedLambda = new SerializedLambda(capturingClass, input.readString(),
input.readString(), input.readString(), input.readVarInt(true), input.readString(), input.readString(),
input.readString(), input.readString(), capturedArgs);
try {
- return readResolve.invoke(serializedLambda);
+ return readResolve(capturingClass, serializedLambda);
} catch (Exception ex) {
throw new KryoException("Error reading closure.", ex);
}
@@ -99,12 +102,25 @@ public Object read (Kryo kryo, Input input, Class type) {
public Object copy (Kryo kryo, Object original) {
try {
- return readResolve.invoke(toSerializedLambda(original));
+ SerializedLambda lambda = toSerializedLambda(original);
+ Class> capturingClass = Class.forName(lambda.getCapturingClass().replace('/', '.'));
+ return readResolve(capturingClass, lambda);
} catch (Exception ex) {
throw new KryoException("Error copying closure.", ex);
}
}
+ private Object readResolve (Class> capturingClass, SerializedLambda lambda) throws Exception {
+ if (readResolve != null) {
+ return readResolve.invoke(lambda);
+ }
+
+ // See SerializedLambda#readResolve
+ Method m = capturingClass.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
+ m.setAccessible(true);
+ return m.invoke(null, lambda);
+ }
+
private SerializedLambda toSerializedLambda (Object object) {
Object replacement;
try {
@@ -118,9 +134,7 @@ private SerializedLambda toSerializedLambda (Object object) {
try {
return (SerializedLambda)replacement;
} catch (Exception ex) {
- throw new KryoException(
- "writeReplace must return a SerializedLambda: " + (replacement == null ? null : className(replacement.getClass())),
- ex);
+ throw new KryoException("writeReplace must return a SerializedLambda: " + className(replacement.getClass()), ex);
}
}
}
diff --git a/src/com/esotericsoftware/kryo/util/Util.java b/src/com/esotericsoftware/kryo/util/Util.java
index 52a345b33..f724b2ea9 100644
--- a/src/com/esotericsoftware/kryo/util/Util.java
+++ b/src/com/esotericsoftware/kryo/util/Util.java
@@ -68,6 +68,10 @@ public class Util {
primitiveWrappers.put(short.class, Short.class);
}
+ public static boolean isUnsafeAvailable () {
+ return unsafe;
+ }
+
public static boolean isClassAvailable (String className) {
try {
Class.forName(className);
diff --git a/test-jdk11/com/esotericsoftware/kryo/serializers/ImmutableCollectionsSerializersTest.java b/test-jdk11/com/esotericsoftware/kryo/serializers/ImmutableCollectionsSerializersTest.java
index 6c338793b..c19f89479 100644
--- a/test-jdk11/com/esotericsoftware/kryo/serializers/ImmutableCollectionsSerializersTest.java
+++ b/test-jdk11/com/esotericsoftware/kryo/serializers/ImmutableCollectionsSerializersTest.java
@@ -82,7 +82,7 @@ void testImmutableSet () {
roundTrip(6, Set.of(1, 2, 3));
}
- static class TestClass {
+ public static class TestClass {
List list;
Map map;
Set set;
diff --git a/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java b/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java
index 99d129a98..55f8ea84d 100644
--- a/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java
+++ b/test-jdk14/com/esotericsoftware/kryo/TestDataJava14.java
@@ -19,14 +19,24 @@
package com.esotericsoftware.kryo;
-import com.esotericsoftware.kryo.SerializationCompatTestData;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
/** 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, Integer[] n) { };
+ public record Rec (byte b, short s, int i, long l, float f, double d, boolean bool, char c, String str, Integer[] n) {
+ // Overriden because of https://stackoverflow.com/questions/61261226/java-14-records-and-arrays
+ public boolean equals(Object o) {
+ return EqualsBuilder.reflectionEquals(this, o);
+ }
+
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+ };
private Rec rec;
diff --git a/test/com/esotericsoftware/kryo/KryoTestCase.java b/test/com/esotericsoftware/kryo/KryoTestCase.java
index 1cf7dcd2d..3dd96682a 100644
--- a/test/com/esotericsoftware/kryo/KryoTestCase.java
+++ b/test/com/esotericsoftware/kryo/KryoTestCase.java
@@ -30,6 +30,7 @@
import com.esotericsoftware.kryo.unsafe.UnsafeByteBufferOutput;
import com.esotericsoftware.kryo.unsafe.UnsafeInput;
import com.esotericsoftware.kryo.unsafe.UnsafeOutput;
+import com.esotericsoftware.kryo.util.Util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -139,50 +140,54 @@ public Input createInput (byte[] buffer) {
}
});
- roundTripWithBufferFactory(length, object1, new BufferFactory() {
- public Output createOutput (OutputStream os) {
- return new UnsafeOutput(os);
- }
-
- public Output createOutput (OutputStream os, int size) {
- return new UnsafeOutput(os, size);
- }
+ if (Util.isUnsafeAvailable()) {
+ roundTripWithBufferFactory(length, object1, new BufferFactory() {
+ public Output createOutput(OutputStream os) {
+ return new UnsafeOutput(os);
+ }
- public Output createOutput (int size, int limit) {
- return new UnsafeOutput(size, limit);
- }
+ public Output createOutput(OutputStream os, int size) {
+ return new UnsafeOutput(os, size);
+ }
- public Input createInput (InputStream os, int size) {
- return new UnsafeInput(os, size);
- }
+ public Output createOutput(int size, int limit) {
+ return new UnsafeOutput(size, limit);
+ }
- public Input createInput (byte[] buffer) {
- return new UnsafeInput(buffer);
- }
- });
+ public Input createInput(InputStream os, int size) {
+ return new UnsafeInput(os, size);
+ }
- roundTripWithBufferFactory(length, object1, new BufferFactory() {
- public Output createOutput (OutputStream os) {
- return new UnsafeByteBufferOutput(os);
- }
-
- public Output createOutput (OutputStream os, int size) {
- return new UnsafeByteBufferOutput(os, size);
- }
-
- public Output createOutput (int size, int limit) {
- return new UnsafeByteBufferOutput(size, limit);
- }
-
- public Input createInput (InputStream os, int size) {
- return new UnsafeByteBufferInput(os, size);
- }
+ public Input createInput(byte[] buffer) {
+ return new UnsafeInput(buffer);
+ }
+ });
+ }
- public Input createInput (byte[] buffer) {
- ByteBuffer byteBuffer = allocateByteBuffer(buffer);
- return new UnsafeByteBufferInput(byteBuffer.asReadOnlyBuffer());
- }
- });
+ if (Util.isUnsafeAvailable()) {
+ roundTripWithBufferFactory(length, object1, new BufferFactory() {
+ public Output createOutput(OutputStream os) {
+ return new UnsafeByteBufferOutput(os);
+ }
+
+ public Output createOutput(OutputStream os, int size) {
+ return new UnsafeByteBufferOutput(os, size);
+ }
+
+ public Output createOutput(int size, int limit) {
+ return new UnsafeByteBufferOutput(size, limit);
+ }
+
+ public Input createInput(InputStream os, int size) {
+ return new UnsafeByteBufferInput(os, size);
+ }
+
+ public Input createInput(byte[] buffer) {
+ ByteBuffer byteBuffer = allocateByteBuffer(buffer);
+ return new UnsafeByteBufferInput(byteBuffer.asReadOnlyBuffer());
+ }
+ });
+ }
return object2;
}
diff --git a/test/com/esotericsoftware/kryo/ReferenceTest.java b/test/com/esotericsoftware/kryo/ReferenceTest.java
index 924d6c3cd..0eeb2844b 100644
--- a/test/com/esotericsoftware/kryo/ReferenceTest.java
+++ b/test/com/esotericsoftware/kryo/ReferenceTest.java
@@ -25,9 +25,9 @@
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.MapSerializer;
-import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.TreeMap;
import org.junit.jupiter.api.Test;
@@ -89,19 +89,13 @@ void testReadingNestedObjectsFirst () {
list.add("2");
list.add("1");
list.add("1");
- List subList = list.subList(0, 5);
+ ArrayListHolder subList = new ArrayListHolder(list);
kryo.setRegistrationRequired(false);
kryo.register(ArrayList.class);
- Class subListClass = (Class)subList.getClass();
- if (subListClass.getName().equals("java.util.ArrayList$SubList")) {
- // This is JDK > = 1.7
- kryo.register(subList.getClass(), new ArraySubListSerializer());
- } else {
- kryo.register(subList.getClass(), new SubListSerializer());
+ kryo.register(ArrayListHolder.class);
- }
- roundTrip(23, subList);
+ roundTrip(15, subList);
}
@Test
@@ -118,86 +112,25 @@ void testReadInvalidReference() {
fail("Exception was expected");
}
- public static class SubListSerializer extends Serializer {
- private Field listField, offsetField, sizeField;
-
- public SubListSerializer () {
- try {
- Class sublistClass = Class.forName("java.util.SubList");
- listField = sublistClass.getDeclaredField("l");
- offsetField = sublistClass.getDeclaredField("offset");
- sizeField = sublistClass.getDeclaredField("size");
- listField.setAccessible(true);
- offsetField.setAccessible(true);
- sizeField.setAccessible(true);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- public void write (Kryo kryo, Output output, List list) {
- try {
- kryo.writeClassAndObject(output, listField.get(list));
- int fromIndex = offsetField.getInt(list);
- int count = sizeField.getInt(list);
- output.writeInt(fromIndex);
- output.writeInt(count);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- public List read (Kryo kryo, Input input, Class extends List> type) {
- List list = (List)kryo.readClassAndObject(input);
- int fromIndex = input.readInt();
- int count = input.readInt();
- return list.subList(fromIndex, fromIndex + count);
- }
- }
+ public static class ArrayListHolder {
+ private List parent;
- public static class ArraySubListSerializer extends Serializer {
- private Field parentField, offsetField, sizeField;
-
- public ArraySubListSerializer () {
- try {
- Class sublistClass = Class.forName("java.util.ArrayList$SubList");
- parentField = getParentField(sublistClass);
- offsetField = sublistClass.getDeclaredField("offset");
- sizeField = sublistClass.getDeclaredField("size");
- parentField.setAccessible(true);
- offsetField.setAccessible(true);
- sizeField.setAccessible(true);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
+ public ArrayListHolder () {
}
- private Field getParentField(Class sublistClass) throws NoSuchFieldException {
- try {
- // java 9+
- return sublistClass.getDeclaredField("root");
- } catch(NoSuchFieldException e) {
- return sublistClass.getDeclaredField("parent");
- }
+ public ArrayListHolder (List parent) {
+ this.parent = parent;
}
- public void write (Kryo kryo, Output output, List list) {
- try {
- kryo.writeClassAndObject(output, parentField.get(list));
- int offset = offsetField.getInt(list);
- int size = sizeField.getInt(list);
- output.writeInt(offset);
- output.writeInt(size);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
+ public boolean equals (Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ArrayListHolder that = (ArrayListHolder)o;
+ return Objects.equals(parent, that.parent);
}
- public List read (Kryo kryo, Input input, Class extends List> type) {
- List list = (List)kryo.readClassAndObject(input);
- int offset = input.readInt();
- int size = input.readInt();
- return list.subList(offset, offset + size);
+ public int hashCode () {
+ return Objects.hash(parent);
}
}
}
diff --git a/test/com/esotericsoftware/kryo/ReflectionAssert.java b/test/com/esotericsoftware/kryo/ReflectionAssert.java
index c048b1ecb..b3ca742d5 100644
--- a/test/com/esotericsoftware/kryo/ReflectionAssert.java
+++ b/test/com/esotericsoftware/kryo/ReflectionAssert.java
@@ -128,6 +128,12 @@ private static void assertReflectionEquals (final Object one, final Object anoth
assertCollectionEquals((Collection)one, (Collection)another, requireMatchingCollectionClasses, alreadyChecked, path);
return;
}
+
+ if (one instanceof StringBuilder || one instanceof StringBuffer) {
+ assertEquals(((CharSequence)one).toString(), ((CharSequence)another).toString(),
+ "Values not equals for path '" + (StringUtils.isEmpty(path) ? "." : path) + "' - ");
+ return;
+ }
if (one instanceof Currency) {
// Check that the transient field defaultFractionDigits is initialized
@@ -143,6 +149,11 @@ private static void assertReflectionEquals (final Object one, final Object anoth
}
Class clazz = one.getClass();
+ if (hasCustomEquals(clazz)) {
+ assertEquals(one, another, "Values not equals for path '" + (StringUtils.isEmpty(path) ? "." : path) + "' - ");
+ return;
+ }
+
while (clazz != null) {
assertEqualDeclaredFields(clazz, one, another, requireMatchingCollectionClasses, alreadyChecked, path);
clazz = clazz.getSuperclass();
@@ -164,6 +175,17 @@ private static boolean oneIsAssignable (final Object one, final Object another,
return false;
}
+ private static boolean hasCustomEquals(Class> c) {
+ while (!Object.class.equals(c)) {
+ try {
+ c.getDeclaredMethod("equals", Object.class);
+ return true;
+ } catch (Exception ignored) {}
+ c = c.getSuperclass();
+ }
+ return false;
+ }
+
/*
* TODO (MG): this assumes same iteration order, which must not be given for sets. There could be a specialized implementation
* for sets.
@@ -194,8 +216,8 @@ private static void assertMapEquals (final Map, ?> m1, final Map, ?> m2, fin
private static void assertEqualDeclaredFields (final Class extends Object> clazz, final Object one, final Object another,
final boolean requireMatchingClasses, final Map alreadyChecked, final String path) {
for (final Field field : clazz.getDeclaredFields()) {
- field.setAccessible(true);
- if (!Modifier.isTransient(field.getModifiers())) {
+ if (!Modifier.isTransient(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()) && !field.isSynthetic()) {
+ field.setAccessible(true);
try {
assertReflectionEquals(field.get(one), field.get(another), requireMatchingClasses, alreadyChecked,
path + "." + field.getName());
diff --git a/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java b/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java
index 251393740..496faa5af 100644
--- a/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java
+++ b/test/com/esotericsoftware/kryo/SerializationBenchmarkTest.java
@@ -109,6 +109,7 @@ void testByteBufferOutputFixed () throws Exception {
}
@Test
+ @Unsafe
void testUnsafeOutput () throws Exception {
UnsafeOutput output = new UnsafeOutput(OUTPUT_BUFFER_SIZE);
UnsafeInput input = new UnsafeInput(output.getBuffer());
@@ -117,6 +118,7 @@ void testUnsafeOutput () throws Exception {
}
@Test
+ @Unsafe
void testUnsafeOutputFixed () throws Exception {
UnsafeOutput output = new UnsafeOutput(OUTPUT_BUFFER_SIZE);
UnsafeInput input = new UnsafeInput(output.getBuffer());
@@ -127,6 +129,7 @@ void testUnsafeOutputFixed () throws Exception {
}
@Test
+ @Unsafe
void testUnsafeByteBufferOutput () throws Exception {
UnsafeByteBufferOutput output = new UnsafeByteBufferOutput(OUTPUT_BUFFER_SIZE);
UnsafeByteBufferInput input = new UnsafeByteBufferInput(output.getByteBuffer());
@@ -135,6 +138,7 @@ void testUnsafeByteBufferOutput () throws Exception {
}
@Test
+ @Unsafe
void testUnsafeByteBufferOutputFixed () throws Exception {
UnsafeByteBufferOutput output = new UnsafeByteBufferOutput(OUTPUT_BUFFER_SIZE);
UnsafeByteBufferInput input = new UnsafeByteBufferInput(output.getByteBuffer());
@@ -198,7 +202,7 @@ public void setUp () throws Exception {
Log.WARN();
}
- private static class SampleObject implements Externalizable, KryoSerializable {
+ public static class SampleObject implements Externalizable, KryoSerializable {
private int intValue;
public float floatValue;
protected Short shortValue;
diff --git a/test/com/esotericsoftware/kryo/SerializationCompatTestData.java b/test/com/esotericsoftware/kryo/SerializationCompatTestData.java
index 7fa58f3a6..1730191d3 100644
--- a/test/com/esotericsoftware/kryo/SerializationCompatTestData.java
+++ b/test/com/esotericsoftware/kryo/SerializationCompatTestData.java
@@ -277,14 +277,6 @@ public TestData () {
_public = new PublicClass(new PrivateClass("foo"));
}
- public int hashCode () {
- return HashCodeBuilder.reflectionHashCode(this);
- }
-
- public boolean equals (Object obj) {
- return EqualsBuilder.reflectionEquals(this, obj);
- }
-
}
static class Generic {
diff --git a/test/com/esotericsoftware/kryo/Unsafe.java b/test/com/esotericsoftware/kryo/Unsafe.java
new file mode 100644
index 000000000..0ebb77e34
--- /dev/null
+++ b/test/com/esotericsoftware/kryo/Unsafe.java
@@ -0,0 +1,34 @@
+/* Copyright (c) 2008-2022, Nathan Sweet
+ * 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.condition.EnabledIf;
+
+/** Conditional annotation that runs a test only if Unsafe is available */
+@Target(value = {ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@EnabledIf("com.esotericsoftware.kryo.util.Util#isUnsafeAvailable")
+public @interface Unsafe {
+}
diff --git a/test/com/esotericsoftware/kryo/io/UnsafeByteBufferInputOutputTest.java b/test/com/esotericsoftware/kryo/io/UnsafeByteBufferInputOutputTest.java
index 890ca1590..232f4a29d 100644
--- a/test/com/esotericsoftware/kryo/io/UnsafeByteBufferInputOutputTest.java
+++ b/test/com/esotericsoftware/kryo/io/UnsafeByteBufferInputOutputTest.java
@@ -22,6 +22,7 @@
import static com.esotericsoftware.kryo.KryoAssert.*;
import static org.junit.jupiter.api.Assertions.*;
+import com.esotericsoftware.kryo.Unsafe;
import com.esotericsoftware.kryo.unsafe.UnsafeByteBufferInput;
import com.esotericsoftware.kryo.unsafe.UnsafeByteBufferOutput;
import com.esotericsoftware.kryo.unsafe.UnsafeUtil;
@@ -33,6 +34,7 @@
import org.junit.jupiter.api.Test;
/** @author Roman Levenstein */
+@Unsafe
@SuppressWarnings("restriction")
class UnsafeByteBufferInputOutputTest {
diff --git a/test/com/esotericsoftware/kryo/io/UnsafeInputOutputTest.java b/test/com/esotericsoftware/kryo/io/UnsafeInputOutputTest.java
index a8fe1eea3..90841efae 100644
--- a/test/com/esotericsoftware/kryo/io/UnsafeInputOutputTest.java
+++ b/test/com/esotericsoftware/kryo/io/UnsafeInputOutputTest.java
@@ -22,6 +22,7 @@
import static com.esotericsoftware.kryo.KryoAssert.*;
import static org.junit.jupiter.api.Assertions.*;
+import com.esotericsoftware.kryo.Unsafe;
import com.esotericsoftware.kryo.unsafe.UnsafeInput;
import com.esotericsoftware.kryo.unsafe.UnsafeOutput;
@@ -32,6 +33,7 @@
import org.junit.jupiter.api.Test;
/** @author Nathan Sweet */
+@Unsafe
class UnsafeInputOutputTest {
@Test
void testOutputStream () {
diff --git a/test/com/esotericsoftware/kryo/serializers/ClosureSerializerTest.java b/test/com/esotericsoftware/kryo/serializers/ClosureSerializerTest.java
index 4980a7951..30b6ff826 100644
--- a/test/com/esotericsoftware/kryo/serializers/ClosureSerializerTest.java
+++ b/test/com/esotericsoftware/kryo/serializers/ClosureSerializerTest.java
@@ -25,7 +25,6 @@
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
-import java.lang.invoke.SerializedLambda;
import java.util.concurrent.Callable;
import org.junit.jupiter.api.BeforeEach;
@@ -40,7 +39,6 @@ public void setUp () throws Exception {
kryo.register(Object[].class);
kryo.register(Class.class);
kryo.register(getClass()); // The closure's capturing class must be registered.
- kryo.register(SerializedLambda.class);
kryo.register(ClosureSerializer.Closure.class, new ClosureSerializer());
}
@@ -60,6 +58,15 @@ void testSerializableClosure () {
doAssertEquals(closure1, closure2);
}
+ @Test
+ void testCopyClosure() {
+ Callable closure1 = (Callable & java.io.Serializable)( () -> 72363);
+
+ final Callable closure2 = kryo.copy(closure1);
+
+ doAssertEquals(closure1, closure2);
+ }
+
protected void doAssertEquals (Object object1, Object object2) {
try {
assertEquals(((Callable)object1).call(), ((Callable)object2).call());
diff --git a/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java b/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java
index a0f6a0b29..9cafdc64c 100644
--- a/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java
+++ b/test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java
@@ -29,7 +29,6 @@
import com.esotericsoftware.kryo.io.Output;
import java.io.Serializable;
-import java.lang.invoke.SerializedLambda;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -499,7 +498,6 @@ void testClassWithLambdaField () {
CompatibleFieldSerializer.CompatibleFieldSerializerConfig config = new CompatibleFieldSerializer.CompatibleFieldSerializerConfig();
kryo.setDefaultSerializer(new CompatibleFieldSerializerFactory(config));
kryo.register(ClassWithLambdaField.class);
- kryo.register(SerializedLambda.class);
kryo.register(ClosureSerializer.Closure.class, new ClosureSerializer());
roundTrip(236, new ClassWithLambdaField());
diff --git a/test/com/esotericsoftware/kryo/serializers/FieldSerializerTest.java b/test/com/esotericsoftware/kryo/serializers/FieldSerializerTest.java
index 1a749e5d9..67f9f54b3 100644
--- a/test/com/esotericsoftware/kryo/serializers/FieldSerializerTest.java
+++ b/test/com/esotericsoftware/kryo/serializers/FieldSerializerTest.java
@@ -38,6 +38,7 @@
import com.esotericsoftware.kryo.serializers.FieldSerializer.NotNull;
import com.esotericsoftware.kryo.serializers.FieldSerializer.Optional;
import com.esotericsoftware.kryo.serializers.MapSerializer.BindMap;
+import com.esotericsoftware.kryo.util.Util;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
@@ -402,7 +403,7 @@ void testDefaultInstantiatorStrategy () {
kryo.register(HasPrivateConstructor.class);
roundTrip(4, test);
- assertEquals(20, HasPrivateConstructor.invocations, "Wrong number of constructor invocations");
+ assertEquals(Util.isUnsafeAvailable() ? 20 : 10, HasPrivateConstructor.invocations, "Wrong number of constructor invocations");
}
/** This test uses StdInstantiatorStrategy and should bypass invocation of no-arg constructor, even if it is provided. **/
diff --git a/test/com/esotericsoftware/kryo/serializers/GenericsTest.java b/test/com/esotericsoftware/kryo/serializers/GenericsTest.java
index 104f2a0fe..dc9ee364a 100644
--- a/test/com/esotericsoftware/kryo/serializers/GenericsTest.java
+++ b/test/com/esotericsoftware/kryo/serializers/GenericsTest.java
@@ -186,11 +186,11 @@ void testClassHierarchyWithMissingTypeVariables () {
roundTrip(168, o);
}
- private interface Holder {
+ interface Holder {
V getValue ();
}
- private abstract static class AbstractValueHolder implements Holder {
+ abstract static class AbstractValueHolder implements Holder {
private final V value;
AbstractValueHolder (V value) {
@@ -210,13 +210,13 @@ public boolean equals (Object o) {
}
}
- private abstract static class AbstractValueListHolder extends AbstractValueHolder> {
+ abstract static class AbstractValueListHolder extends AbstractValueHolder> {
AbstractValueListHolder (List value) {
super(value);
}
}
- private static class LongHolder extends AbstractValueHolder {
+ static class LongHolder extends AbstractValueHolder {
/** Kryo Constructor */
LongHolder () {
super(null);
@@ -227,7 +227,7 @@ private static class LongHolder extends AbstractValueHolder {
}
}
- private static class LongListHolder extends AbstractValueListHolder {
+ static class LongListHolder extends AbstractValueListHolder {
/** Kryo Constructor */
LongListHolder () {
super(null);
@@ -272,7 +272,7 @@ public boolean equals (Object o) {
}
// A simple serializable class.
- private static class SerializableObjectFoo implements Serializable {
+ public static class SerializableObjectFoo implements Serializable {
String name;
SerializableObjectFoo (String name) {
@@ -295,18 +295,18 @@ public boolean equals (Object obj) {
}
}
- private static class BaseGeneric {
+ static class BaseGeneric {
// The type of this field cannot be derived from the context.
// Therefore, Kryo should consider it to be Object.
private final List listPayload;
/** Kryo Constructor */
- protected BaseGeneric () {
+ BaseGeneric () {
super();
this.listPayload = null;
}
- protected BaseGeneric (final List listPayload) {
+ BaseGeneric (final List listPayload) {
super();
// Defensive copy, listPayload is mutable
this.listPayload = new ArrayList(listPayload);
@@ -330,7 +330,7 @@ public boolean equals (Object obj) {
}
// This is a non-generic class with a generic superclass.
- private static class ConcreteClass2 extends BaseGeneric {
+ static class ConcreteClass2 extends BaseGeneric {
/** Kryo Constructor */
ConcreteClass2 () {
super();
@@ -341,7 +341,7 @@ public ConcreteClass2 (final List listPayload) {
}
}
- private static class ConcreteClass1 extends ConcreteClass2 {
+ static class ConcreteClass1 extends ConcreteClass2 {
/** Kryo Constructor */
ConcreteClass1 () {
super();
@@ -352,7 +352,7 @@ public ConcreteClass1 (final List listPayload) {
}
}
- private static class ConcreteClass extends ConcreteClass1 {
+ static class ConcreteClass extends ConcreteClass1 {
/** Kryo Constructor */
ConcreteClass () {
super();
@@ -363,7 +363,7 @@ public ConcreteClass (final List listPayload) {
}
}
- public static class SuperGenerics {
+ static class SuperGenerics {
public static class RootSuper {
public ValueSuper rootSuperField;
@@ -396,7 +396,7 @@ public boolean equals (Object o) {
}
}
- public static class ClassWithMap {
+ static class ClassWithMap {
public final Map> values = new HashMap();
public boolean equals (Object obj) {
@@ -419,7 +419,7 @@ public String toString () {
}
}
- public static class A {
+ static class A {
public static class B extends A {
}
diff --git a/test/com/esotericsoftware/kryo/serializers/OptionalSerializersTest.java b/test/com/esotericsoftware/kryo/serializers/OptionalSerializersTest.java
index 7725a092c..1c15eaff5 100644
--- a/test/com/esotericsoftware/kryo/serializers/OptionalSerializersTest.java
+++ b/test/com/esotericsoftware/kryo/serializers/OptionalSerializersTest.java
@@ -75,7 +75,7 @@ void testOptionalDouble () {
roundTrip(10, OptionalDouble.of(Double.MAX_VALUE));
}
- static class TestClass {
+ public static class TestClass {
Optional maybe;
public TestClass () {