ClassCastException: cannot assign SerializedLambda to field with ClassLoader

David Holmes david.holmes at oracle.com
Tue Mar 26 01:34:23 UTC 2019


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