From 0dea98622445fef091775ef334f8d41b3e60d536 Mon Sep 17 00:00:00 2001 From: Thomas Heigl Date: Wed, 19 Apr 2023 10:22:35 +0200 Subject: [PATCH] #940 Fix type resolution for generic arrays --- .../kryo/serializers/ReflectField.java | 5 +- src/com/esotericsoftware/kryo/util/Util.java | 16 ++ .../kryo/serializers/GenericsTest.java | 144 ++++++++++++++++++ .../esotericsoftware/kryo/util/UtilTest.java | 8 + 4 files changed, 172 insertions(+), 1 deletion(-) diff --git a/src/com/esotericsoftware/kryo/serializers/ReflectField.java b/src/com/esotericsoftware/kryo/serializers/ReflectField.java index 4dddf91a0..a121a5196 100644 --- a/src/com/esotericsoftware/kryo/serializers/ReflectField.java +++ b/src/com/esotericsoftware/kryo/serializers/ReflectField.java @@ -27,6 +27,7 @@ import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.serializers.FieldSerializer.CachedField; import com.esotericsoftware.kryo.util.Generics.GenericType; +import com.esotericsoftware.kryo.util.Util; import java.lang.reflect.Field; @@ -151,7 +152,9 @@ public void read (Input input, Object object) { Class resolveFieldClass () { if (valueClass == null) { Class fieldClass = genericType.resolve(fieldSerializer.kryo.getGenerics()); - if (fieldClass != null && fieldSerializer.kryo.isFinal(fieldClass)) return fieldClass; + if (fieldClass != null && fieldSerializer.kryo.isFinal(fieldClass)) { + return field.getType().isArray() ? Util.getArrayType(fieldClass) : fieldClass; + } } return valueClass; } diff --git a/src/com/esotericsoftware/kryo/util/Util.java b/src/com/esotericsoftware/kryo/util/Util.java index bdda6bb7c..1157d22f0 100644 --- a/src/com/esotericsoftware/kryo/util/Util.java +++ b/src/com/esotericsoftware/kryo/util/Util.java @@ -27,6 +27,7 @@ import com.esotericsoftware.kryo.serializers.FieldSerializer; import com.esotericsoftware.kryo.util.Generics.GenericType; +import java.lang.reflect.Array; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; @@ -110,6 +111,21 @@ public static Class getPrimitiveClass (Class type) { return type; } + /** Returns the array type for a given class */ + public static Class getArrayType(Class type) { + if (type == String.class) return String[].class; + if (type == Integer.class) return Integer[].class; + if (type == Float.class) return Float[].class; + if (type == Boolean.class) return Boolean[].class; + if (type == Byte.class) return Byte[].class; + if (type == Long.class) return Long[].class; + if (type == Character.class) return Character[].class; + if (type == Double.class) return Double[].class; + if (type == Short.class) return Short[].class; + // See Class#arrayType() available from JDK 12 + return Array.newInstance(type, 0).getClass(); + } + public static boolean isWrapperClass (Class type) { return type == Integer.class || type == Float.class || type == Boolean.class || type == Byte.class || type == Long.class || type == Character.class || type == Double.class || type == Short.class; diff --git a/test/com/esotericsoftware/kryo/serializers/GenericsTest.java b/test/com/esotericsoftware/kryo/serializers/GenericsTest.java index dd0f2efe2..32d6d490d 100644 --- a/test/com/esotericsoftware/kryo/serializers/GenericsTest.java +++ b/test/com/esotericsoftware/kryo/serializers/GenericsTest.java @@ -145,6 +145,38 @@ void testFieldWithGenericArrayType() { roundTrip(70, o); } + // Test for https://github.com/EsotericSoftware/kryo/issues/940 + @Test + void testFieldWithStringArrayType () { + StringArray array = new StringArray(new String[] {"1"}); + + kryo.setRegistrationRequired(false); + + roundTrip(67, array); + } + + // Test for https://github.com/EsotericSoftware/kryo/issues/940 + @Test + void testFieldWithNumberArrayType () { + NumberArray array = new NumberArray<>(new Integer[] {1}); + NumberArrayHolder container = new NumberArrayHolder(array); + + kryo.setRegistrationRequired(false); + + roundTrip(137, container); + } + + // Test for https://github.com/EsotericSoftware/kryo/issues/940 + @Test + void testFieldWithObjectArrayType () { + ObjectArray array = new ObjectArray<>(new TestObject[] {new TestObject(1)}); + ObjectArrayHolder container = new ObjectArrayHolder(array); + + kryo.setRegistrationRequired(false); + + roundTrip(265, container); + } + // Test for https://github.com/EsotericSoftware/kryo/issues/655 @Test void testClassWithMultipleGenericTypes() { @@ -565,4 +597,116 @@ interface C { } } + public static class StringArray { + + private String[] values; + + public StringArray() { + } + + public StringArray(String[] array) { + this.values = array; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StringArray that = (StringArray) o; + return Arrays.equals(values, that.values); + } + } + + public static class NumberArray { + + private V[] values; + + public NumberArray() { + } + + public NumberArray(V[] array) { + this.values = array; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NumberArray that = (NumberArray) o; + return Arrays.equals(values, that.values); + } + } + + public static class NumberArrayHolder { + + private NumberArray field; + + public NumberArrayHolder() { + } + + public NumberArrayHolder(NumberArray array) { + this.field = array; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NumberArrayHolder that = (NumberArrayHolder) o; + return Objects.equals(field, that.field); + } + } + + public static class ObjectArray { + + private V[] values; + + public ObjectArray() { + } + + public ObjectArray(V[] array) { + this.values = array; + } + + public boolean equals (Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ObjectArray that = (ObjectArray)o; + return Arrays.equals(values, that.values); + } + } + + public static class ObjectArrayHolder { + + private ObjectArray field; + + public ObjectArrayHolder() { + } + + public ObjectArrayHolder(ObjectArray array) { + this.field = array; + } + + public boolean equals (Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ObjectArrayHolder that = (ObjectArrayHolder)o; + return Objects.equals(field, that.field); + } + } + + public static class TestObject { + private int i; + + public TestObject() { + } + + public TestObject(int i) { + this.i = i; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TestObject that = (TestObject) o; + return i == that.i; + } + } } diff --git a/test/com/esotericsoftware/kryo/util/UtilTest.java b/test/com/esotericsoftware/kryo/util/UtilTest.java index 545bf83df..b92f5b67f 100644 --- a/test/com/esotericsoftware/kryo/util/UtilTest.java +++ b/test/com/esotericsoftware/kryo/util/UtilTest.java @@ -44,4 +44,12 @@ void testIsAssignableTo() { assertFalse(Util.isAssignableTo(String.class, long.class)); } + @Test + void testGetArrayType() { + assertEquals(int[].class, Util.getArrayType(int.class)); + assertEquals(Integer[].class, Util.getArrayType(Integer.class)); + assertEquals(String[].class, Util.getArrayType(String.class)); + assertEquals(String[].class, Util.getArrayType(String.class)); + assertEquals(Object[].class, Util.getArrayType(Object.class)); + } }