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.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
> .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);
> >              }
> >          }
> >      }
> > }
> >
>

Reply via email to