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.hol...@oracle.com <mailto: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>
     > <mailto: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>
>  <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);
     >      >              }
     >      >          }
     >      >      }
     >      > }
     >      >
     >

Reply via email to