From 033d659a118d7206dfa8d9c605bcec601c46f116 Mon Sep 17 00:00:00 2001 From: Martin Grotzke Date: Fri, 11 Aug 2017 21:19:18 +0200 Subject: [PATCH] Fix #529: Support serializing the Enum class object When an `Enum.class` instance is serialized (by `ClassSerializer`), this causes the creation of the registered default serializer (`EnumSerializer`) with this call chain: ``` ClassSerializer.write -> Kryo.writeClass -> DefaultClassResolver.writeClass -> Kryo.getDefaultSerializer -> ReflectionSerializerFactory.makeSerializer(kryo, EnumSerializer, Enum.class) ``` This is done so that `Kryo.writeClass` can write the class name or the id of the serializer registration. Until now, the `EnumSerializer` failed instantiation for a type with `null` enum constants. This however is the case for `Enum.class` itself. The creation of `EnumSerializer` for `Enum.class` is now allowed, under the assumption that this happens in the context of the `Enum.class` serialization, and that `write`/`read` on the created `EnumSerializer` instance will never be called. This assumption should be safe, since there should never be an instance of the abstract `Enum` class. --- src/com/esotericsoftware/kryo/Kryo.java | 2 +- .../kryo/serializers/DefaultSerializers.java | 7 ++++++- test/com/esotericsoftware/kryo/DefaultSerializersTest.java | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/com/esotericsoftware/kryo/Kryo.java b/src/com/esotericsoftware/kryo/Kryo.java index 6e8993183..8b4507df5 100644 --- a/src/com/esotericsoftware/kryo/Kryo.java +++ b/src/com/esotericsoftware/kryo/Kryo.java @@ -490,7 +490,7 @@ public Registration getRegistration (Class type) { if (Proxy.isProxyClass(type)) { // If a Proxy class, treat it like an InvocationHandler because the concrete class for a proxy is generated. registration = getRegistration(InvocationHandler.class); - } else if (!type.isEnum() && Enum.class.isAssignableFrom(type)) { + } else if (!type.isEnum() && Enum.class.isAssignableFrom(type) && !Enum.class.equals(type)) { // This handles an enum value that is an inner class. Eg: enum A {b{}}; registration = getRegistration(type.getEnclosingClass()); } else if (EnumSet.class.isAssignableFrom(type)) { diff --git a/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java b/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java index dd5fc09b2..a01fadf48 100644 --- a/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java +++ b/src/com/esotericsoftware/kryo/serializers/DefaultSerializers.java @@ -394,7 +394,12 @@ static public class EnumSerializer extends Serializer { public EnumSerializer (Class type) { enumConstants = type.getEnumConstants(); - if (enumConstants == null) throw new IllegalArgumentException("The type must be an enum: " + type); + // We allow the serialization of the (abstract!) Enum.class (instead of an actual "user" enum), + // which also creates an EnumSerializer instance during Kryo.writeClass with the following trace: + // ClassSerializer.write -> Kryo.writeClass -> DefaultClassResolver.writeClass + // -> Kryo.getDefaultSerializer -> ReflectionSerializerFactory.makeSerializer(kryo, EnumSerializer, Enum.class) + // This EnumSerializer instance is expected to be never called for write/read. + if (enumConstants == null && !Enum.class.equals(type)) throw new IllegalArgumentException("The type must be an enum: " + type); } public void write (Kryo kryo, Output output, Enum object) { diff --git a/test/com/esotericsoftware/kryo/DefaultSerializersTest.java b/test/com/esotericsoftware/kryo/DefaultSerializersTest.java index 6ec894c67..fc70798b7 100644 --- a/test/com/esotericsoftware/kryo/DefaultSerializersTest.java +++ b/test/com/esotericsoftware/kryo/DefaultSerializersTest.java @@ -314,6 +314,7 @@ public void testClassSerializer () { kryo.writeObject(out, void.class); kryo.writeObject(out, ArrayList.class); kryo.writeObject(out, TestEnum.class); + kryo.writeObject(out, Enum.class); final Input in = new Input(out.getBuffer()); @@ -336,6 +337,7 @@ public void testClassSerializer () { assertEquals(void.class, kryo.readObject(in, Class.class)); assertEquals(ArrayList.class, kryo.readObject(in, Class.class)); assertEquals(TestEnum.class, kryo.readObject(in, Class.class)); + assertEquals(Enum.class, kryo.readObject(in, Class.class)); } public void testLocaleSerializer () {