i still get the same stack trace after breaking the example out into two files. does that sufficiently address the nesting issue ?
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.hol...@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.hol...@oracle.com > > <mailto:david.hol...@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@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 > > <http://java.io > >.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2190) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectStreamClass$FieldReflector.checkObjectFieldValueTypes(ObjectStreamClass.java:2153) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectStreamClass.checkObjFieldValueTypes(ObjectStreamClass.java:1407) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.defaultCheckFieldValues(ObjectInputStream.java:2371) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readSerialData(ObjectInputStream.java:2278) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readObject0(ObjectInputStream.java:1594) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readArray(ObjectInputStream.java:1993) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readObject0(ObjectInputStream.java:1588) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2355) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readSerialData(ObjectInputStream.java:2249) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2087) > > > at > > > java.base/java.io > > <http://java.io > >.ObjectInputStream.readObject0(ObjectInputStream.java:1594) > > > at > > > java.base/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); > > > } > > > } > > > } > > > } > > > > > >