Skip to content

Commit

Permalink
Add support for serialization of Java8 closures (see #215).
Browse files Browse the repository at this point in the history
  • Loading branch information
romix committed May 16, 2014
1 parent 2881130 commit 0b733dd
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/com/esotericsoftware/kryo/Kryo.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
import com.esotericsoftware.kryo.util.MapReferenceResolver;
import com.esotericsoftware.kryo.util.ObjectMap;
import com.esotericsoftware.kryo.util.Util;
import com.esotericsoftware.minlog.Log;
import com.esotericsoftware.reflectasm.ConstructorAccess;

import java.lang.reflect.Constructor;
Expand Down Expand Up @@ -205,6 +206,20 @@ public Kryo (ClassResolver classResolver, ReferenceResolver referenceResolver, S
register(long.class, new LongSerializer());
register(double.class, new DoubleSerializer());
register(void.class, new VoidSerializer());

// Lambdas support
// Enable only if JVM supports it
try {
String version = System.getProperty("java.version");
char minor = version.charAt(2);
if (minor >= '8') {
register(Class.forName("java.lang.invoke.SerializedLambda"));
register(Closure.class, (Serializer)Class.forName("com.esotericsoftware.kryo.serializers.ClosureSerializer")
.newInstance());
}
} catch (Exception e) {
Log.trace("Serialization of Java8 lambdas is not available on this system.");
}
}

// --- Default serializers ---
Expand Down Expand Up @@ -452,6 +467,8 @@ public Registration getRegistration (Class type) {
registration = getRegistration(type.getEnclosingClass());
} else if (EnumSet.class.isAssignableFrom(type)) {
registration = classResolver.getRegistration(EnumSet.class);
} else if (isClousre(type)) {
registration = classResolver.getRegistration(Closure.class);
}
if (registration == null) {
if (registrationRequired) {
Expand Down Expand Up @@ -1111,6 +1128,15 @@ public boolean isFinal (Class type) {
if (type.isArray()) return Modifier.isFinal(Util.getElementClass(type).getModifiers());
return Modifier.isFinal(type.getModifiers());
}

/** Returns true if the specified type is a closure.
* <p>
* This can be overridden to support alternative implementations of clousres. Current version supports
* Oracle's Java8 only */
public boolean isClousre (Class type) {
if (type == null) throw new IllegalArgumentException("type cannot be null.");
return type.getName().indexOf('/') >= 0;
}

static final class DefaultSerializerEntry {
final Class type;
Expand Down Expand Up @@ -1231,4 +1257,7 @@ public Object newInstance () {
return fallbackStrategy.newInstantiatorOf(type);
}
}

private static class Closure {
}
}
70 changes: 70 additions & 0 deletions src/com/esotericsoftware/kryo/serializers/ClosureSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.esotericsoftware.kryo.serializers;

import java.io.IOException;
import java.lang.reflect.Method;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

/** Serializer for Java8 closures.
* @author Roman Levenstein <[email protected]> */
public class ClosureSerializer extends Serializer {

private static Method readResolve;
private static Class serializedLambda;
static {
try {
serializedLambda = Class.forName("java.lang.invoke.SerializedLambda");
readResolve = serializedLambda.getDeclaredMethod("readResolve");
readResolve.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException("Could not obtain SerializedLambda or its methods via reflection", e);
}
}

public ClosureSerializer () {
}

public void write (Kryo kryo, Output output, Object object) {
try {
Class type = object.getClass();
Method writeReplace = type.getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
Object replacement = writeReplace.invoke(object);
if (serializedLambda.isInstance(replacement)) {
// Serialize the representation of this lambda
kryo.writeObject(output, replacement);
} else
throw new RuntimeException("Could not serialize lambda");
} catch (Exception e) {
throw new RuntimeException("Could not serialize lambda", e);
}
}

public Object read (Kryo kryo, Input input, Class type) {
try {
Object object = kryo.readObject(input, serializedLambda);
return readResolve.invoke(object);
} catch (Exception e) {
throw new RuntimeException("Could not serialize lambda", e);
}
}

public Object copy (Kryo kryo, Object original) {
try {
Class type = original.getClass();
Method writeReplace = type.getDeclaredMethod("writeReplace");
writeReplace.setAccessible(true);
Object replacement = writeReplace.invoke(original);
if (serializedLambda.isInstance(replacement)) {
return readResolve.invoke(replacement);
} else
throw new RuntimeException("Could not serialize lambda");
} catch (Exception e) {
throw new RuntimeException("Could not serialize lambda", e);
}
}
}

0 comments on commit 0b733dd

Please sign in to comment.