RFR: 8258897: wrong translation of capturing local classes inside nested lambdas

classic Classic list List threaded Threaded
15 messages Options
Reply | Threaded
Open this post in threaded view
|

RFR: 8258897: wrong translation of capturing local classes inside nested lambdas

Jia Yanwei
**Issue:**
`javac` crashes with a NPE when compiling such code:
- a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
- instance the local class in a nested lambda

Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:

An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
    at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
    at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
    at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
    at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
    at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
    at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
    at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
printing javac parameters to: /tmp/javac.20201126_141249.args

The minimal reproducing code is:

 java
class Main {
    static Runnable runnable = () -> {
        boolean b0 = false;
        String s0 = "hello";

        class Local {
            int i = s0.length();
        }

        Runnable dummy = () -> new Local();
    };
}

If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:

 java
public class CaptureInt {
    static Runnable runnable = () -> {
        boolean b0 = false;
        int i0 = 5;

        class Local {
            int i = i0 + 2;
        }

        Runnable dummy = () -> new Local();
    };

    public static void main(String args[]) {
    }
}

**Reason & Solution:**

During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
Welcome any advice and better solution.

-------------

Commit messages:
 - Merge branch 'master' into javac-npe-fix
 - update variables order in test case & remove redundancy test cases
 - fix code style for test cases
 - merge test cases & introduce more cases with anonymous cases enabled
 - merge test cases & introduce more cases with anonymous cases enabled
 - remove redundancy test cases & rename the rest test cases
 - fix another NPE in jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)

Changes: https://git.openjdk.java.net/jdk/pull/1479/files
 Webrev: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=00
  Issue: https://bugs.openjdk.java.net/browse/JDK-8258897
  Stats: 154 lines in 3 files changed: 152 ins; 0 del; 2 mod
  Patch: https://git.openjdk.java.net/jdk/pull/1479.diff
  Fetch: git fetch https://git.openjdk.java.net/jdk pull/1479/head:pull/1479

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas

Jia Yanwei
On Fri, 27 Nov 2020 12:32:21 GMT, Jia Yanwei <[hidden email]> wrote:

> **Issue:**
> `javac` crashes with a NPE when compiling such code:
> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
> - instance the local class in a nested lambda
>
> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>
> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
> printing javac parameters to: /tmp/javac.20201126_141249.args
>
> The minimal reproducing code is:
>
>  java
> class Main {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         String s0 = "hello";
>
>         class Local {
>             int i = s0.length();
>         }
>
>         Runnable dummy = () -> new Local();
>     };
> }
>
> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>
>  java
> public class CaptureInt {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         int i0 = 5;
>
>         class Local {
>             int i = i0 + 2;
>         }
>
>         Runnable dummy = () -> new Local();
>     };
>
>     public static void main(String args[]) {
>     }
> }
>
> **Reason & Solution:**
>
> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
> Welcome any advice and better solution.

I had signed OCA, but named as `jyw`, the email and full name are identical.

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas

Jia Yanwei
On Fri, 27 Nov 2020 12:37:24 GMT, Jia Yanwei <[hidden email]> wrote:

>> **Issue:**
>> `javac` crashes with a NPE when compiling such code:
>> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
>> - instance the local class in a nested lambda
>>
>> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>>
>> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
>> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
>> printing javac parameters to: /tmp/javac.20201126_141249.args
>>
>> The minimal reproducing code is:
>>
>>  java
>> class Main {
>>     static Runnable runnable = () -> {
>>         boolean b0 = false;
>>         String s0 = "hello";
>>
>>         class Local {
>>             int i = s0.length();
>>         }
>>
>>         Runnable dummy = () -> new Local();
>>     };
>> }
>>
>> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>>
>>  java
>> public class CaptureInt {
>>     static Runnable runnable = () -> {
>>         boolean b0 = false;
>>         int i0 = 5;
>>
>>         class Local {
>>             int i = i0 + 2;
>>         }
>>
>>         Runnable dummy = () -> new Local();
>>     };
>>
>>     public static void main(String args[]) {
>>     }
>> }
>>
>> **Reason & Solution:**
>>
>> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
>> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
>> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
>> Welcome any advice and better solution.
>
> I had signed OCA, but named as `jyw`, the email and full name are identical.

Bernard's reply:

The problem seems to be that 'lambdaTranslationMap' links the
untranslated local variable to its capture. So, what do you think of
the following solution to keep the translated local variable's mapping
with the original symbol (both examples and langtools:tier1 are OK on
jdk14u)?

 diff
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
@@ -2057,7 +2057,13 @@
                         };
                         break;
                     case LOCAL_VAR:
-                        ret = new VarSymbol(sym.flags() & FINAL,
sym.name, sym.type, translatedSym);
+                        ret = new VarSymbol(sym.flags() & FINAL,
sym.name, sym.type, translatedSym) {
+                            @Override
+                            public Symbol baseSymbol() {
+                                //keep mapping with original symbol
+                                return sym;
+                            }
+                        };
                         ((VarSymbol) ret).pos = ((VarSymbol) sym).pos;
                         break;
                     case PARAM:
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
@@ -1221,7 +1221,7 @@
                 //sym is a local variable - check the lambda translation map to
                 //see if sym has been translated to something else in
the current
                 //scope (by LambdaToMethod)
-                Symbol translatedSym = lambdaTranslationMap.get(sym);
+                Symbol translatedSym =
lambdaTranslationMap.get(sym.baseSymbol());
                 if (translatedSym != null) {
                     tree = make.at(tree.pos).Ident(translatedSym);
                 }

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v2]

Jia Yanwei
In reply to this post by Jia Yanwei
> **Issue:**
> `javac` crashes with a NPE when compiling such code:
> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
> - instance the local class in a nested lambda
>
> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>
> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
> printing javac parameters to: /tmp/javac.20201126_141249.args
>
> The minimal reproducing code is:
>
>  java
> class Main {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         String s0 = "hello";
>
>         class Local {
>             int i = s0.length();
>         }
>
>         Runnable dummy = () -> new Local();
>     };
> }
>
> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>
>  java
> public class CaptureInt {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         int i0 = 5;
>
>         class Local {
>             int i = i0 + 2;
>         }
>
>         Runnable dummy = () -> new Local();
>     };
>
>     public static void main(String args[]) {
>     }
> }
>
> **Reason & Experimental solution:**
>
> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
> I'm not sure whether the code style as well as the solution itself are appropriate. Welcome any advice and better solution.

Jia Yanwei has updated the pull request with a new target base due to a merge or a rebase. The pull request now contains eight commits:

 - Merge branch 'master' into javac-npe-fix
 - Merge branch 'master' into javac-npe-fix
 - update variables order in test case & remove redundancy test cases
 - fix code style for test cases
 - merge test cases & introduce more cases with anonymous cases enabled
 - merge test cases & introduce more cases with anonymous cases enabled
 - remove redundancy test cases & rename the rest test cases
 - fix another NPE in jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)

-------------

Changes: https://git.openjdk.java.net/jdk/pull/1479/files
 Webrev: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=01
  Stats: 154 lines in 3 files changed: 152 ins; 0 del; 2 mod
  Patch: https://git.openjdk.java.net/jdk/pull/1479.diff
  Fetch: git fetch https://git.openjdk.java.net/jdk pull/1479/head:pull/1479

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v3]

Jia Yanwei
In reply to this post by Jia Yanwei
> **Issue:**
> `javac` crashes with a NPE when compiling such code:
> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
> - instance the local class in a nested lambda
>
> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>
> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
> printing javac parameters to: /tmp/javac.20201126_141249.args
>
> The minimal reproducing code is:
>
>  java
> class Main {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         String s0 = "hello";
>
>         class Local {
>             int i = s0.length();
>         }
>
>         Runnable dummy = () -> new Local();
>     };
> }
>
> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>
>  java
> public class CaptureInt {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         int i0 = 5;
>
>         class Local {
>             int i = i0 + 2;
>         }
>
>         Runnable dummy = () -> new Local();
>     };
>
>     public static void main(String args[]) {
>     }
> }
>
> **Reason & Experimental solution:**
>
> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
> I'm not sure whether the code style as well as the solution itself are appropriate. Welcome any advice and better solution.

Jia Yanwei has updated the pull request incrementally with three additional commits since the last revision:

 - fix 8258897: wrong translation of capturing local classes inside nested lambdas
 - rename test case directory name
 - revert the experimental fix in 9dec0d2

-------------

Changes:
  - all: https://git.openjdk.java.net/jdk/pull/1479/files
  - new: https://git.openjdk.java.net/jdk/pull/1479/files/74330aa2..86cb0ad8

Webrevs:
 - full: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=02
 - incr: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=01-02

  Stats: 32 lines in 4 files changed: 6 ins; 22 del; 4 mod
  Patch: https://git.openjdk.java.net/jdk/pull/1479.diff
  Fetch: git fetch https://git.openjdk.java.net/jdk pull/1479/head:pull/1479

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v3]

Jan Lahoda-3
On Tue, 29 Dec 2020 04:53:10 GMT, Jia Yanwei <[hidden email]> wrote:

>> **Issue:**
>> `javac` crashes with a NPE when compiling such code:
>> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
>> - instance the local class in a nested lambda
>>
>> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>>
>> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
>> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
>> printing javac parameters to: /tmp/javac.20201126_141249.args
>>
>> The minimal reproducing code is:
>>
>>  java
>> class Main {
>>     static Runnable runnable = () -> {
>>         boolean b0 = false;
>>         String s0 = "hello";
>>
>>         class Local {
>>             int i = s0.length();
>>         }
>>
>>         Runnable dummy = () -> new Local();
>>     };
>> }
>>
>> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>>
>>  java
>> public class CaptureInt {
>>     static Runnable runnable = () -> {
>>         boolean b0 = false;
>>         int i0 = 5;
>>
>>         class Local {
>>             int i = i0 + 2;
>>         }
>>
>>         Runnable dummy = () -> new Local();
>>     };
>>
>>     public static void main(String args[]) {
>>     }
>> }
>>
>> **Reason & Experimental solution:**
>>
>> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
>> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
>> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
>> I'm not sure whether the code style as well as the solution itself are appropriate. Welcome any advice and better solution.
>
> Jia Yanwei has updated the pull request incrementally with three additional commits since the last revision:
>
>  - fix 8258897: wrong translation of capturing local classes inside nested lambdas
>  - rename test case directory name
>  - revert the experimental fix in 9dec0d2

Seems sensible to me. Some recommendations on improving the text comment format are inline.

test/langtools/tools/javac/lambda/8258897/CaptureVariables.java line 3:

> 1: import java.util.function.Supplier;
> 2:
> 3: /**

It would be nice to tweak the test to so that:
-the comment with `@test` is first, and imports follow
-`@test` is augmented with `/nodynamiccopyright/`
-`@bug` is added
-`@summary` is added

(applies to both tests)

-------------

Marked as reviewed by jlahoda (Reviewer).

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v3]

Guoxiong Li-2
On Mon, 25 Jan 2021 12:15:36 GMT, Jan Lahoda <[hidden email]> wrote:

>> Jia Yanwei has updated the pull request incrementally with three additional commits since the last revision:
>>
>>  - fix 8258897: wrong translation of capturing local classes inside nested lambdas
>>  - rename test case directory name
>>  - revert the experimental fix in 9dec0d2
>
> test/langtools/tools/javac/lambda/8258897/CaptureVariables.java line 3:
>
>> 1: import java.util.function.Supplier;
>> 2:
>> 3: /**
>
> It would be nice to tweak the test to so that:
> -the comment with `@test` is first, and imports follow
> -`@test` is augmented with `/nodynamiccopyright/`
> -`@bug` is added
> -`@summary` is added
>
> (applies to both tests)

And the action tag `@run main CaptureVariables` should be added.

@hltj Some documents are helpful to you:
[Regression Test Harness for the JDK: jtreg](http://openjdk.java.net/jtreg/index.html)
[The JDK Test Framework: Tag Language Specification](http://openjdk.java.net/jtreg/tag-spec.html)

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v3]

Jia Yanwei
In reply to this post by Jan Lahoda-3
On Mon, 25 Jan 2021 12:15:36 GMT, Jan Lahoda <[hidden email]> wrote:

>> Jia Yanwei has updated the pull request incrementally with three additional commits since the last revision:
>>
>>  - fix 8258897: wrong translation of capturing local classes inside nested lambdas
>>  - rename test case directory name
>>  - revert the experimental fix in 9dec0d2
>
> test/langtools/tools/javac/lambda/8258897/CaptureVariables.java line 3:
>
>> 1: import java.util.function.Supplier;
>> 2:
>> 3: /**
>
> It would be nice to tweak the test to so that:
> -the comment with `@test` is first, and imports follow
> -`@test` is augmented with `/nodynamiccopyright/`
> -`@bug` is added
> -`@summary` is added
>
> (applies to both tests)

Thanks to @lahodaj and @lgxbslgx ! I will add the tags.

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v4]

Jia Yanwei
In reply to this post by Jia Yanwei
> **Issue:**
> `javac` crashes with a NPE when compiling such code:
> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
> - instance the local class in a nested lambda
>
> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>
> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
> printing javac parameters to: /tmp/javac.20201126_141249.args
>
> The minimal reproducing code is:
>
>  java
> class Main {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         String s0 = "hello";
>
>         class Local {
>             int i = s0.length();
>         }
>
>         Runnable dummy = () -> new Local();
>     };
> }
>
> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>
>  java
> public class CaptureInt {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         int i0 = 5;
>
>         class Local {
>             int i = i0 + 2;
>         }
>
>         Runnable dummy = () -> new Local();
>     };
>
>     public static void main(String args[]) {
>     }
> }
>
> **Reason & Experimental solution:**
>
> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
> I'm not sure whether the code style as well as the solution itself are appropriate. Welcome any advice and better solution.

Jia Yanwei has updated the pull request incrementally with one additional commit since the last revision:

  tweak the test: add informational tags

-------------

Changes:
  - all: https://git.openjdk.java.net/jdk/pull/1479/files
  - new: https://git.openjdk.java.net/jdk/pull/1479/files/86cb0ad8..810b909d

Webrevs:
 - full: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=03
 - incr: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=02-03

  Stats: 8 lines in 2 files changed: 6 ins; 0 del; 2 mod
  Patch: https://git.openjdk.java.net/jdk/pull/1479.diff
  Fetch: git fetch https://git.openjdk.java.net/jdk pull/1479/head:pull/1479

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v5]

Jia Yanwei
In reply to this post by Jia Yanwei
> **Issue:**
> `javac` crashes with a NPE when compiling such code:
> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
> - instance the local class in a nested lambda
>
> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>
> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
> printing javac parameters to: /tmp/javac.20201126_141249.args
>
> The minimal reproducing code is:
>
>  java
> class Main {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         String s0 = "hello";
>
>         class Local {
>             int i = s0.length();
>         }
>
>         Runnable dummy = () -> new Local();
>     };
> }
>
> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>
>  java
> public class CaptureInt {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         int i0 = 5;
>
>         class Local {
>             int i = i0 + 2;
>         }
>
>         Runnable dummy = () -> new Local();
>     };
>
>     public static void main(String args[]) {
>     }
> }
>
> **Reason & Experimental solution:**
>
> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
> I'm not sure whether the code style as well as the solution itself are appropriate. Welcome any advice and better solution.

Jia Yanwei has updated the pull request incrementally with one additional commit since the last revision:

  tweak the test comments

-------------

Changes:
  - all: https://git.openjdk.java.net/jdk/pull/1479/files
  - new: https://git.openjdk.java.net/jdk/pull/1479/files/810b909d..f142d14e

Webrevs:
 - full: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=04
 - incr: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=03-04

  Stats: 12 lines in 2 files changed: 6 ins; 4 del; 2 mod
  Patch: https://git.openjdk.java.net/jdk/pull/1479.diff
  Fetch: git fetch https://git.openjdk.java.net/jdk pull/1479/head:pull/1479

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v3]

Guoxiong Li-2
In reply to this post by Jan Lahoda-3
On Mon, 25 Jan 2021 12:15:36 GMT, Jan Lahoda <[hidden email]> wrote:

>> Jia Yanwei has updated the pull request incrementally with three additional commits since the last revision:
>>
>>  - fix 8258897: wrong translation of capturing local classes inside nested lambdas
>>  - rename test case directory name
>>  - revert the experimental fix in 9dec0d2
>
> test/langtools/tools/javac/lambda/8258897/CaptureVariables.java line 3:
>
>> 1: import java.util.function.Supplier;
>> 2:
>> 3: /**
>
> It would be nice to tweak the test to so that:
> -the comment with `@test` is first, and imports follow
> -`@test` is augmented with `/nodynamiccopyright/`
> -`@bug` is added
> -`@summary` is added
>
> (applies to both tests)

@lahodaj I think the tests in this case can use the copyright header instead of `/nodynamiccopyright/`. What's your opinion?

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v3]

Jan Lahoda-3
On Wed, 27 Jan 2021 06:36:21 GMT, Guoxiong Li <[hidden email]> wrote:

>> test/langtools/tools/javac/lambda/8258897/CaptureVariables.java line 3:
>>
>>> 1: import java.util.function.Supplier;
>>> 2:
>>> 3: /**
>>
>> It would be nice to tweak the test to so that:
>> -the comment with `@test` is first, and imports follow
>> -`@test` is augmented with `/nodynamiccopyright/`
>> -`@bug` is added
>> -`@summary` is added
>>
>> (applies to both tests)
>
> @lahodaj I think the tests in this case can use the copyright header instead of `/nodynamiccopyright/`. What's your opinion?

@lgxbslgx - you are right, I am sorry. `/nodymiccopyright/` is used when line numbers are significant, and I somehow thought they are here, but you are right they are not. So there should be a normal header here.

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v6]

Jia Yanwei
In reply to this post by Jia Yanwei
> **Issue:**
> `javac` crashes with a NPE when compiling such code:
> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
> - instance the local class in a nested lambda
>
> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>
> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
> printing javac parameters to: /tmp/javac.20201126_141249.args
>
> The minimal reproducing code is:
>
>  java
> class Main {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         String s0 = "hello";
>
>         class Local {
>             int i = s0.length();
>         }
>
>         Runnable dummy = () -> new Local();
>     };
> }
>
> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>
>  java
> public class CaptureInt {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         int i0 = 5;
>
>         class Local {
>             int i = i0 + 2;
>         }
>
>         Runnable dummy = () -> new Local();
>     };
>
>     public static void main(String args[]) {
>     }
> }
>
> **Reason & Experimental solution:**
>
> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
> I'm not sure whether the code style as well as the solution itself are appropriate. Welcome any advice and better solution.

Jia Yanwei has updated the pull request incrementally with one additional commit since the last revision:

  add copyright comments

-------------

Changes:
  - all: https://git.openjdk.java.net/jdk/pull/1479/files
  - new: https://git.openjdk.java.net/jdk/pull/1479/files/f142d14e..a4c6aec0

Webrevs:
 - full: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=05
 - incr: https://webrevs.openjdk.java.net/?repo=jdk&pr=1479&range=04-05

  Stats: 48 lines in 2 files changed: 46 ins; 0 del; 2 mod
  Patch: https://git.openjdk.java.net/jdk/pull/1479.diff
  Fetch: git fetch https://git.openjdk.java.net/jdk pull/1479/head:pull/1479

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Re: RFR: 8258897: wrong translation of capturing local classes inside nested lambdas [v3]

Jia Yanwei
In reply to this post by Jan Lahoda-3
On Wed, 27 Jan 2021 07:45:19 GMT, Jan Lahoda <[hidden email]> wrote:

>> @lahodaj I think the tests in this case can use the copyright header instead of `/nodynamiccopyright/`. What's your opinion?
>
> @lgxbslgx - you are right, I am sorry. `/nodymiccopyright/` is used when line numbers are significant, and I somehow thought they are here, but you are right they are not. So there should be a normal header here.

I added the copyright comments moments ago. Thanks.

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479
Reply | Threaded
Open this post in threaded view
|

Integrated: 8258897: wrong translation of capturing local classes inside nested lambdas

Jia Yanwei
In reply to this post by Jia Yanwei
On Fri, 27 Nov 2020 12:32:21 GMT, Jia Yanwei <[hidden email]> wrote:

> **Issue:**
> `javac` crashes with a NPE when compiling such code:
> - a local class captured a reference type local variable of a enclosed lambda (which contains multiple local variables with different types)
> - instance the local class in a nested lambda
>
> Affects all versions from Java 8 to 16-ea, here is a example output with OpenJDK 15.0.1:
>
> An exception has occurred in the compiler (15.0.1). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
> java.lang.NullPointerException: Cannot read field "sym" because "this.lvar[1]" is null
>     at jdk.compiler/com.sun.tools.javac.jvm.Code.emitop0(Code.java:571)
>     at jdk.compiler/com.sun.tools.javac.jvm.Items$LocalItem.load(Items.java:399)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:889)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitNewClass(Gen.java:1942)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCNewClass.accept(JCTree.java:1800)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:864)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1723)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1532)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:618)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStats(Gen.java:669)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1084)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1047)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genStat(Gen.java:632)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:954)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:917)
>     at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:893)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genDef(Gen.java:597)
>     at jdk.compiler/com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2395)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:756)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1644)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1612)
>     at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:317)
>     at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:176)
>     at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:59)
>     at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:45)
> printing javac parameters to: /tmp/javac.20201126_141249.args
>
> The minimal reproducing code is:
>
>  java
> class Main {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         String s0 = "hello";
>
>         class Local {
>             int i = s0.length();
>         }
>
>         Runnable dummy = () -> new Local();
>     };
> }
>
> If the captured variable is a primitive type, the code will compile but cause a runtime crash at startup. e.g.:
>
>  java
> public class CaptureInt {
>     static Runnable runnable = () -> {
>         boolean b0 = false;
>         int i0 = 5;
>
>         class Local {
>             int i = i0 + 2;
>         }
>
>         Runnable dummy = () -> new Local();
>     };
>
>     public static void main(String args[]) {
>     }
> }
>
> **Reason & Experimental solution:**
>
> During compilation, the captured variable was correctly synthetized as a synthetic argument in the synthetic methods for the nested lambda after `desugar()`.
> But the synthetic argument was not used when loading free variables, and it always use the original symbol for the original variable as if not in a nested lambda.
> My experimental solution is substituting the symbols in free variables that were captured as synthetic arguments in nested lambda.
> I'm not sure whether the code style as well as the solution itself are appropriate. Welcome any advice and better solution.

This pull request has now been integrated.

Changeset: de3f519d
Author:    hltj <[hidden email]>
Committer: Jan Lahoda <[hidden email]>
URL:       https://git.openjdk.java.net/jdk/commit/de3f519d
Stats:     192 lines in 4 files changed: 190 ins; 0 del; 2 mod

8258897: wrong translation of capturing local classes inside nested lambdas

Co-authored-by: Bernard Blaser <[hidden email]>
Reviewed-by: jlahoda

-------------

PR: https://git.openjdk.java.net/jdk/pull/1479