Hi Seth,

On 26/03/2019 11:22 am, seth lytle wrote:
if a lambda is a field and captures `this`, and it's deserialized into a
new class loader, it throws a ClassCastException.

Not sure I follow. If you load a class into a different classloader then you get a different type. It might appear the same to you but it is a distinct type, so you can't assign across different instances loaded by different classloaders.

Cheers,
David
-----

i came across this bug
independently while writing a test, but then found an old openjdk bug with
a similar issue and tweaked the test case (source below). my version
differs only in
1. moved the lambda to a field
2. reference `this` in it
3. uses readAllBytes instead of the internal method
4. formatting

running with java 11 or 12 (or java 8 using a substitute for readAllBytes)
results in:
                 this: test.SerializedLambdaTest$MyCode@8efb846
     deSerializedThis: test.SerializedLambdaTest$MyCode@2b71fc7e
             runnable:
test.SerializedLambdaTest$MyCode$$Lambda$1/0x0000000801188440@37bba400
Exception in thread "main" java.lang.ClassCastException: cannot assign
instance of java.lang.invoke.SerializedLambda to field
test.SerializedLambdaTest$MyCode.runnable2 of type
test.SerializedLambdaTest$SerializableRunnable in instance of
test.SerializedLambdaTest$MyCode
at
java.base/java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2190)
at
java.base/java.io.ObjectStreamClass$FieldReflector.checkObjectFieldValueTypes(ObjectStreamClass.java:2153)
at
java.base/java.io.ObjectStreamClass.checkObjFieldValueTypes(ObjectStreamClass.java:1407)
at
java.base/java.io.ObjectInputStream.defaultCheckFieldValues(ObjectInputStream.java:2371)
at
java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2278)
at
java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087)
at
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
at
java.base/java.io.ObjectInputStream.readArray(ObjectInputStream.java:1993)
at
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1588)
at
java.base/java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2355)
at
java.base/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2249)
at
java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087)
at
java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
at
java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
at
test.SerializedLambdaTest$MyCode.deserialize(SerializedLambdaTest.java:35)
at test.SerializedLambdaTest$MyCode.run(SerializedLambdaTest.java:51)
at test.SerializedLambdaTest.main(SerializedLambdaTest.java:66)



https://bugs.openjdk.java.net/browse/JDK-8008770


package test;

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;

// from: https://bugs.openjdk.java.net/browse/JDK-8008770
public class SerializedLambdaTest {
     public interface SerializableRunnable extends Runnable,Serializable {
     }
     public static class MyCode implements SerializableRunnable {
         SerializableRunnable runnable2 = ()
                 -> {
             System.out.println("HELLO"+this.getClass());
         };
         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 = runnable2;
             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 = is.readAllBytes();
                     return defineClass(name,bytes,0,bytes.length);
                 } else
                     throw new ClassNotFoundException(name);
             } catch (IOException e) {
                 throw new ClassNotFoundException(name,e);
             }
         }
     }
}

Reply via email to