ClassCastException: cannot assign SerializedLambda to field with ClassLoader

seth lytle seth.lytle at gmail.com
Tue Mar 26 01:22:23 UTC 2019


if a lambda is a field and captures `this`, and it's deserialized into a
new class loader, it throws a ClassCastException. 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 at 8efb846
    deSerializedThis: test.SerializedLambdaTest$MyCode at 2b71fc7e
            runnable:
test.SerializedLambdaTest$MyCode$$Lambda$1/0x0000000801188440 at 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);
            }
        }
    }
}


More information about the core-libs-dev mailing list