ClassCastException: cannot assign SerializedLambda to field with ClassLoader
David Holmes
david.holmes at oracle.com
Tue Mar 26 03:40:24 UTC 2019
On 26/03/2019 1:06 pm, seth lytle wrote:
> i still get the same stack trace after breaking the example out into two
> files. does that sufficiently address the nesting issue ?
??
public static class MyCode ...
is still a nested class.
David
> file: SerializedLambdaTest
> package test;
>
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> import java.io.IOException;
> import java.io.ObjectInputStream;
> import java.io.ObjectOutputStream;
> import java.io.Serializable;
>
> // from: https://bugs.openjdk.java.net/browse/JDK-8008770
> public class SerializedLambdaTest implements Runnable {
> public void run() { new MyCode().run(); }
> 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);
> }
> }
> }
>
>
> file: MyClassLoader
> package test;
>
> import java.io.IOException;
> import java.io.InputStream;
>
> 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 (final 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);
> }
> }
> public static void main(String[] args) throws Exception {
> ClassLoader myCl = new MyClassLoader(
> MyClassLoader.class.getClassLoader()
> );
> Class<?> myCodeClass = Class.forName(
> "test.SerializedLambdaTest",
> true,
> myCl
> );
> Runnable myCode = (Runnable) myCodeClass.newInstance();
> myCode.run();
> }
> }
>
>
>
>
> On Mon, Mar 25, 2019 at 10:34 PM David Holmes <david.holmes at oracle.com
> <mailto:david.holmes at oracle.com>> wrote:
>
> Hi Seth,
>
> On 26/03/2019 12:16 pm, seth lytle wrote:
> > 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
>
> I think the basic problem is that this test loads a nested type into a
> different classloader to its enclosing type. I can easily imagine this
> messing up the code that gets generated to implement the lambda
> expression - but I'd need to examine that code in detail to see exactly
> why (others may know this more readily than I do). It's unclear to me
> whether this would be considered a bug or a "don't do that" situation.
> As of JDK 11, nested types define "nests" at the VM level (see JEP-181)
> and it is a requirement that all nest members be defined in the same
> package - which implies the same classloader.
>
> David
>
> >
> >
> >
> >
> >
> > On Mon, Mar 25, 2019 at 9:34 PM David Holmes
> <david.holmes at oracle.com <mailto:david.holmes at oracle.com>
> > <mailto:david.holmes at oracle.com
> <mailto: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 <http://java.io>
> >
> <http://java.io>.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2190)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectStreamClass$FieldReflector.checkObjectFieldValueTypes(ObjectStreamClass.java:2153)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectStreamClass.checkObjFieldValueTypes(ObjectStreamClass.java:1407)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.defaultCheckFieldValues(ObjectInputStream.java:2371)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readSerialData(ObjectInputStream.java:2278)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readArray(ObjectInputStream.java:1993)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readObject0(ObjectInputStream.java:1588)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2355)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readSerialData(ObjectInputStream.java:2249)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://java.io>.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
> > > at
> > > java.base/java.io <http://java.io>
> >
> <http://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