ClassCastException: cannot assign SerializedLambda to field with ClassLoader
seth lytle
seth.lytle at gmail.com
Tue Mar 26 02:16:43 UTC 2019
i haven't changed the assignment from the original test case (which was
accepted as valid at the time). i haven't gone through it in depth, but
assume that it's ok and used it since people are already familiar with it.
it appears to me that that example uses reflection to allocate an instance
using a definitive class loader and then uses a runnable for the
serialization/deserialization cycle
the class cast exception happens not at the example level, but deep inside
`readObject` when it's doing an internal assignment
perhaps my choice of words "into a new classloader" was insufficient or
misleading. in both examples that i've come up with so far, the class
loader calls defineClass directly without first delegating to super (for a
subset of the classes). so "into a definitive classloader" might have been
a better choice
On Mon, Mar 25, 2019 at 9:34 PM David Holmes <david.holmes at oracle.com>
wrote:
> 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 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