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