JDK-8300623 : Lambda deserialization regression involving Enum method reference
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 20
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2023-01-19
  • Updated: 2024-01-17
  • Resolved: 2023-01-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 20 JDK 21
20 b33Fixed 21Fixed
Related Reports
CSR :  
Relates :  
Relates :  
Description
In the following example, serializing a lambda and reading it back in succeeds on JDK 19 and fails on JDK 20.

I think the culprit is JDK-8059632.

```
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.Predicate;

@SuppressWarnings("unchecked")
public class T {

  enum E {
    ONE,
    TWO,
    THREE
  }

  public static void main(String[] args) throws Exception {
    E e = E.ONE;
    Predicate<E> p = (Serializable & Predicate<E>) e::equals;
    try (OutputStream os = Files.newOutputStream(Paths.get("o"));
        ObjectOutputStream oos = new ObjectOutputStream(os)) {
      oos.writeObject(p);
    }
    try (InputStream is = Files.newInputStream(Paths.get("o"));
        ObjectInputStream ois = new ObjectInputStream(is)) {
      p = (Predicate<E>) ois.readObject();
    }
    Arrays.stream(E.values()).filter(p).forEachOrdered(System.err::println);
  }
}
```

$ java -fullversion
openjdk full version "19.0.2+7-44"
$ javac T.java
$ java T
$ java T
ONE 

$ java -fullversion
openjdk full version "20-ea+31-2311"
$ javac T.java
$ java T
Exception in thread "main" java.io.InvalidObjectException: ReflectiveOperationException during deserialization
        at java.base/java.lang.invoke.SerializedLambda.readResolve(SerializedLambda.java:280)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
        at java.base/java.lang.reflect.Method.invoke(Method.java:578)
        at java.base/java.io.ObjectStreamClass.invokeReadResolve(ObjectStreamClass.java:1190)
        at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2289)
        at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1760)
        at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:538)
        at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:496)
        at T.main(T.java:29)
Caused by: java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:119)
        at java.base/java.lang.reflect.Method.invoke(Method.java:578)
        at java.base/java.lang.invoke.SerializedLambda.readResolve(SerializedLambda.java:278)
        ... 8 more
Caused by: java.lang.IllegalArgumentException: Invalid lambda deserialization
        at T.$deserializeLambda$(T.java:11)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
        ... 10 more
Comments
Dan mentioned that JDK-8172815, is related and it could be blocking this current issue
25-01-2023

For the records, the problem is that the compiler generates a check if the SerializedLambda correspond to a serializable lambda that was created in the current class or not. The problem is that the SerializedLambda instance is created by the jdk runtime, the LambdaMetaFactory which uses runtime information while the check is generated by the compiler which uses compile time informations, sometimes they disagree. I believe that the only way to solve that is that the generated bytecode should do a runtime call to the a new static method of the LambdaMetaFactory to create a second SerializedLambda from the compiler static information and compare the two SerializedLambda.
24-01-2023

Changeset: a3ed7e94 Author: Jan Lahoda <jlahoda@openjdk.org> Date: 2023-01-24 06:40:06 +0000 URL: https://git.openjdk.org/jdk20/commit/a3ed7e94a23c0c89138d831f4b36b26dce5b3d01
24-01-2023

Fix request approved
23-01-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk20/pull/113 Date: 2023-01-23 11:12:08 +0000
23-01-2023

Fix Request This is an unintended regression in (de-)serialization caused by the fix for JDK-8059632. Unfortunately, the fix for the regression will require some discussion. The proposal is therefore to rollback the fix for JDK-8059632. The rollback applies without manual changes. Testing: Mach5 tier1-3, all passing.
23-01-2023