[8] Review request for 8004970, 8004971, and 8006817: implement serialization in lambda metafactory and metafactory fix
Robert Field
robert.field at oracle.com
Fri Feb 22 19:05:30 PST 2013
Thanks much Peter, we are investigating this issue and your suggestion.
-Robert
On 02/22/13 04:27, Peter Levart wrote:
> Hi Robert,
>
> What I was trying to say in my previous post is illustrated in the
> following minimal test-case:
>
>
> package test;
>
> import sun.misc.IOUtils;
>
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> import java.io.IOException;
> import java.io.InputStream;
> import java.io.ObjectInputStream;
> import java.io.ObjectOutputStream;
> import java.io.Serializable;
>
> public class SerializedLambdaTest {
>
> public interface SerializableRunnable extends Runnable,
> Serializable {}
>
> public static class MyCode implements SerializableRunnable {
>
> private byte[] serialize(Object o) {
> ByteArrayOutputStream baos;
> try (
> ObjectOutputStream oos =
> new ObjectOutputStream(baos = new
> ByteArrayOutputStream())
> ) {
> oos.writeObject(o);
> }
> catch (IOException e) {
> throw new RuntimeException(e);
> }
> return baos.toByteArray();
> }
>
> private <T> T deserialize(byte[] bytes) {
> try (
> ObjectInputStream ois =
> new ObjectInputStream(new ByteArrayInputStream(bytes))
> ) {
> return (T) ois.readObject();
> }
> catch (IOException | ClassNotFoundException e) {
> throw new RuntimeException(e);
> }
> }
>
> @Override
> public void run() {
> System.out.println(" this: " + this);
>
> SerializableRunnable deSerializedThis =
> deserialize(serialize(this));
> System.out.println(" deSerializedThis: " +
> deSerializedThis);
>
> SerializableRunnable runnable = () ->
> {System.out.println("HELLO");};
> System.out.println(" runnable: " + runnable);
>
> SerializableRunnable deSerializedRunnable =
> deserialize(serialize(runnable));
> System.out.println("deSerializedRunnable: " +
> deSerializedRunnable);
> }
> }
>
> public static void main(String[] args) throws Exception {
> ClassLoader myCl = new MyClassLoader(
> SerializedLambdaTest.class.getClassLoader()
> );
> Class<?> myCodeClass = Class.forName(
> SerializedLambdaTest.class.getName() + "$MyCode",
> true,
> myCl
> );
> Runnable myCode = (Runnable) myCodeClass.newInstance();
> myCode.run();
> }
>
> static class MyClassLoader extends ClassLoader {
> MyClassLoader(ClassLoader parent) {
> super(parent);
> }
>
> @Override
> protected Class<?> loadClass(String name, boolean resolve)
> throws ClassNotFoundException {
> if (name.startsWith("test.")) {
> synchronized (getClassLoadingLock(name)) {
> Class<?> c = findLoadedClass(name);
> if (c == null) {
> c = findClass(name);
> }
> if (resolve) {
> resolveClass(c);
> }
> return c;
> }
> } else {
> return super.loadClass(name, resolve);
> }
> }
>
> @Override
> protected Class<?> findClass(String name) throws
> ClassNotFoundException {
> String path = name.replace('.', '/').concat(".class");
> try (InputStream is = getResourceAsStream(path)) {
> if (is != null) {
> byte[] bytes = IOUtils.readFully(is, -1, true);
> return defineClass(name, bytes, 0, bytes.length);
> } else {
> throw new ClassNotFoundException(name);
> }
> }
> catch (IOException e) {
> throw new ClassNotFoundException(name, e);
> }
> }
> }
> }
>
>
> ... which produces the following output:
>
>
> this: test.SerializedLambdaTest$MyCode at 5e481248
> deSerializedThis: test.SerializedLambdaTest$MyCode at d716361
> runnable: test.SerializedLambdaTest$MyCode$$Lambda$1 at eed1f14
> Exception in thread "main" java.lang.ClassCastException:
> test.SerializedLambdaTest$MyCode$$Lambda$2 cannot be cast to
> test.SerializedLambdaTest$SerializableRunnable
>
>
> If capturing class' Class object is not available at SAM proxy
> serialization time (but only it's name), it can be resolved at that
> time rather than at de-serialization time, when the caller
> class-loader is not obvious. Like for example in this patch:
>
>
> Index: jdk/src/java/lang/invoke/SerializedLambda.java
> ===================================================================
> --- jdk/src/java/lang/invoke/SerializedLambda.java (date 1361524994000)
> +++ jdk/src/java/lang/invoke/SerializedLambda.java (revision )
> @@ -24,6 +24,8 @@
> */
> package java.lang.invoke;
>
> +import sun.reflect.Reflection;
> +
> import java.io.Serializable;
> import java.lang.reflect.Method;
> import java.security.AccessController;
> @@ -39,7 +41,7 @@
> * @see LambdaMetafactory
> */
> public final class SerializedLambda implements Serializable {
> - private final String capturingClass;
> + private final Class<?> capturingClass;
> private final String functionalInterfaceClass;
> private final String functionalInterfaceMethodName;
> private final String functionalInterfaceMethodSignature;
> @@ -73,7 +75,7 @@
> Object[] capturedArgs) throws
> ReflectiveOperationException {
> MethodHandleInfo samMhi = new
> MethodHandleInfo(Objects.requireNonNull(functionalInterface));
> MethodHandleInfo implMhi = new
> MethodHandleInfo(Objects.requireNonNull(implementation));
> - this.capturingClass =
> Objects.requireNonNull(capturingClass).getName();
> + this.capturingClass = Objects.requireNonNull(capturingClass);
> this.capturedArgs = Objects.requireNonNull(capturedArgs).clone();
> this.functionalInterfaceClass =
> samMhi.getDeclaringClass().getName();
> this.functionalInterfaceMethodName = samMhi.getName();
> @@ -118,7 +120,17 @@
> String implMethodSignature,
> String instantiatedMethodType,
> Object[] capturedArgs) {
> - this.capturingClass = capturingClass;
> + try {
> + this.capturingClass = Class.forName(
> + capturingClass.replace('/', '.'),
> + false,
> + Reflection.getCallerClass(2).getClassLoader()
> + );
> + }
> + catch (ClassNotFoundException e) {
> + throw (Error) new NoClassDefFoundError(e.getMessage())
> + .initCause(e);
> + }
> this.functionalInterfaceMethodKind =
> functionalInterfaceMethodKind;
> this.functionalInterfaceClass = functionalInterfaceClass;
> this.functionalInterfaceMethodName =
> functionalInterfaceMethodName;
> @@ -133,7 +145,7 @@
>
> /** Get the name of the class that captured this lambda */
> public String getCapturingClass() {
> - return capturingClass;
> + return capturingClass.getName().replace('.', '/');
> }
>
> /** Get the name of the functional interface class to which this
> lambda has been converted */
> @@ -200,9 +212,7 @@
> Method deserialize = AccessController.doPrivileged(new
> PrivilegedExceptionAction<Method>() {
> @Override
> public Method run() throws Exception {
> - Class<?> clazz =
> Class.forName(capturingClass.replace('/', '.'), true,
> - Thread.currentThread().getContextClassLoader());
> - Method m =
> clazz.getDeclaredMethod("$deserializeLambda$", SerializedLambda.class);
> + Method m =
> capturingClass.getDeclaredMethod("$deserializeLambda$",
> SerializedLambda.class);
> m.setAccessible(true);
> return m;
> }
>
>
>
> Regards, Peter
>
>
> On 02/12/2013 08:10 PM, Peter Levart wrote:
>> Hi Robert,
>>
>> Just a minor note on ClassLoaders.
>>
>> In SerializedLambda.readResolve(), the capturingClass is resolved
>> from it's name using the current thread's context ClassLoader. I
>> don't know if this is the right thing to do. ObjectInputStream has
>> it's own (pluggable) mechanism for resolving classes, which by
>> default uses the so called "latest user defined loader" (see
>> ObjectInputStream.resolveClass()). It would be better if
>> SerializedLambda kept a reference to j.l.Class object representing
>> capturingClass and leave to the serialization infrastructure do the
>> resolving. But there is a SerializedLambda constructor that only
>> takes a String, hm...
>>
>> I guess other classes that are resolved inside the capturingClass'
>> $deserializeLambda$ method are using the caprutingClass' class
>> loader, which is ok.
>>
>> Regards, Peter
>>
>> On 02/12/2013 04:23 PM, Robert Field wrote:
>>> Please review the fixes for CRs:
>>>
>>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8004970
>>>
>>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8004971
>>>
>>> http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8006817
>>>
>>>
>>>
>>> Webrev:
>>>
>>> http://cr.openjdk.java.net/~rfield/8004970
>>>
>>> Thank,
>>> Robert
>>>
>>>
>>
>
More information about the lambda-dev
mailing list