Skip to content

Commit 6e61f77

Browse files
committed
Add check if primitive and wrapper types are assignable and remove condition for valueClass
1 parent 458e335 commit 6e61f77

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

src/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializer.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.esotericsoftware.kryo.io.Output;
3131
import com.esotericsoftware.kryo.io.OutputChunked;
3232
import com.esotericsoftware.kryo.util.ObjectMap;
33+
import com.esotericsoftware.kryo.util.Util;
3334

3435
/** Serializes objects using direct field assignment, providing both forward and backward compatibility. This means fields can be
3536
* added or removed without invalidating previously serialized bytes. Renaming or changing the type of a field is not supported.
@@ -90,13 +91,7 @@ public void write (Kryo kryo, Output output, T object) {
9091
try {
9192
if (object != null) {
9293
Object value = cachedField.field.get(object);
93-
if (value != null) {
94-
final Class<?> fieldType = cachedField.field.getType();
95-
if (fieldType.isPrimitive())
96-
valueClass = fieldType;
97-
else
98-
valueClass = value.getClass();
99-
}
94+
if (value != null) valueClass = value.getClass();
10095
}
10196
} catch (IllegalAccessException ex) {
10297
}
@@ -168,7 +163,7 @@ public T read (Kryo kryo, Input input, Class<? extends T> type) {
168163
}
169164

170165
// Ensure the type in the data is compatible with the field type.
171-
if (cachedField.valueClass != null && !cachedField.valueClass.isAssignableFrom(valueClass)) {
166+
if (cachedField.valueClass != null && !Util.isAssignableTo(valueClass, cachedField.valueClass)) {
172167
String message = "Read type is incompatible with the field type: " + className(valueClass) + " -> "
173168
+ className(cachedField.valueClass) + " (" + getType().getName() + "#" + cachedField + ")";
174169
if (!chunked) throw new KryoException(message);

src/com/esotericsoftware/kryo/util/Util.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import com.esotericsoftware.kryo.util.Generics.GenericType;
2828

2929
import java.lang.reflect.Type;
30+
import java.util.HashMap;
31+
import java.util.Map;
3032

3133
/** A few utility methods, mostly for private use.
3234
* @author Nathan Sweet */
@@ -53,6 +55,18 @@ public class Util {
5355
// Maximum reasonable array length. See: https://stackoverflow.com/questions/3038392/do-java-arrays-have-a-maximum-size
5456
public static final int maxArraySize = Integer.MAX_VALUE - 8;
5557

58+
private static final Map<Class<?>, Class<?>> primitiveWrappers = new HashMap<>();
59+
static {
60+
primitiveWrappers.put(boolean.class, Boolean.class);
61+
primitiveWrappers.put(byte.class, Byte.class);
62+
primitiveWrappers.put(char.class, Character.class);
63+
primitiveWrappers.put(double.class, Double.class);
64+
primitiveWrappers.put(float.class, Float.class);
65+
primitiveWrappers.put(int.class, Integer.class);
66+
primitiveWrappers.put(long.class, Long.class);
67+
primitiveWrappers.put(short.class, Short.class);
68+
}
69+
5670
public static boolean isClassAvailable (String className) {
5771
try {
5872
Class.forName(className);
@@ -207,6 +221,18 @@ public static Class getElementClass (Class arrayClass) {
207221
return elementClass;
208222
}
209223

224+
public static boolean isAssignableTo (Class<?> from, Class<?> to) {
225+
if (to.isAssignableFrom(from)) return true;
226+
if (from.isPrimitive()) return isPrimitiveWrapperOf(to, from);
227+
if (to.isPrimitive()) return isPrimitiveWrapperOf(from, to);
228+
return false;
229+
}
230+
231+
private static boolean isPrimitiveWrapperOf (Class<?> targetClass, Class<?> primitive) {
232+
if (!primitive.isPrimitive()) throw new IllegalArgumentException("First argument has to be primitive type");
233+
return primitiveWrappers.get(primitive) == targetClass;
234+
}
235+
210236
public static boolean isAscii (String value) {
211237
for (int i = 0, n = value.length(); i < n; i++)
212238
if (value.charAt(i) > 127) return false;

test/com/esotericsoftware/kryo/serializers/CompatibleFieldSerializerTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,28 @@ private void testChangeFieldType(int length, boolean chunked) {
243243
assertNull(o.value);
244244
}
245245

246+
@Test
247+
public void testChangePrimitiveAndWrapperFieldTypes () {
248+
testChangePrimitiveAndWrapperFieldTypes(26, true);
249+
testChangePrimitiveAndWrapperFieldTypes(22, false);
250+
}
251+
252+
private void testChangePrimitiveAndWrapperFieldTypes (int length, boolean chunked) {
253+
CompatibleFieldSerializer<ClassWithPrimitiveAndWrapper> serializer = new CompatibleFieldSerializer<>(kryo, ClassWithPrimitiveAndWrapper.class);
254+
serializer.getCompatibleFieldSerializerConfig().setChunkedEncoding(chunked);
255+
kryo.setReferences(false);
256+
kryo.register(ClassWithPrimitiveAndWrapper.class, serializer);
257+
258+
roundTrip(length, new ClassWithPrimitiveAndWrapper(1, 1L));
259+
260+
serializer.getField("primitive").setValueClass(Long.class);
261+
serializer.getField("wrapper").setValueClass(long.class);
262+
263+
ClassWithPrimitiveAndWrapper o = (ClassWithPrimitiveAndWrapper)kryo.readClassAndObject(input);
264+
assertEquals(1, o.primitive);
265+
assertEquals(1L, o.wrapper, 0);
266+
}
267+
246268
@Test
247269
public void testRemovedFieldFromClassWithManyFields () {
248270
testRemovedFieldFromClassWithManyFields(198, false, false, true);
@@ -566,6 +588,32 @@ public boolean equals (Object obj) {
566588
}
567589
}
568590

591+
public static class ClassWithPrimitiveAndWrapper {
592+
long primitive;
593+
Long wrapper;
594+
595+
public ClassWithPrimitiveAndWrapper () {
596+
}
597+
598+
public ClassWithPrimitiveAndWrapper (long primitive, Long wrapper) {
599+
this.primitive = primitive;
600+
this.wrapper = wrapper;
601+
}
602+
603+
@Override
604+
public boolean equals (Object o) {
605+
if (this == o) return true;
606+
if (o == null || getClass() != o.getClass()) return false;
607+
final ClassWithPrimitiveAndWrapper that = (ClassWithPrimitiveAndWrapper)o;
608+
return primitive == that.primitive && Objects.equals(wrapper, that.wrapper);
609+
}
610+
611+
@Override
612+
public int hashCode () {
613+
return Objects.hash(primitive, wrapper);
614+
}
615+
}
616+
569617
public static class ClassWithSuperTypeFields {
570618
private Object value;
571619
private Iterable<?> list;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.esotericsoftware.kryo.util;
2+
3+
import junit.framework.TestCase;
4+
5+
public class UtilTest extends TestCase {
6+
7+
public void testIsAssignableTo() {
8+
assertTrue(Util.isAssignableTo(Long.class, long.class));
9+
assertTrue(Util.isAssignableTo(long.class, Long.class));
10+
assertTrue(Util.isAssignableTo(Long.class, Long.class));
11+
assertTrue(Util.isAssignableTo(long.class, long.class));
12+
13+
assertFalse(Util.isAssignableTo(String.class, Long.class));
14+
assertFalse(Util.isAssignableTo(String.class, long.class));
15+
}
16+
17+
}

0 commit comments

Comments
 (0)