Wed, 23 Oct 2013 07:50:04 +0200
8026861: Wrong LineNumberTable for variable declarations in lambdas
Summary: Setting or correcting positions for many trees produced by LambdaToMethod.
Reviewed-by: vromero, rfield
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Wed Oct 23 23:02:17 2013 +0200 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Wed Oct 23 07:50:04 2013 +0200 1.3 @@ -156,12 +156,15 @@ 1.4 */ 1.5 private final VarSymbol deserParamSym; 1.6 1.7 - private KlassInfo(Symbol kSym) { 1.8 + private final JCClassDecl clazz; 1.9 + 1.10 + private KlassInfo(JCClassDecl clazz) { 1.11 + this.clazz = clazz; 1.12 appendedMethodList = new ListBuffer<>(); 1.13 deserializeCases = new HashMap<String, ListBuffer<JCStatement>>(); 1.14 MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType, 1.15 List.<Type>nil(), syms.methodClass); 1.16 - deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, kSym); 1.17 + deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym); 1.18 deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), 1.19 syms.serializedLambdaType, deserMethodSym); 1.20 } 1.21 @@ -221,10 +224,16 @@ 1.22 } 1.23 KlassInfo prevKlassInfo = kInfo; 1.24 try { 1.25 - kInfo = new KlassInfo(tree.sym); 1.26 + kInfo = new KlassInfo(tree); 1.27 super.visitClassDef(tree); 1.28 if (!kInfo.deserializeCases.isEmpty()) { 1.29 - kInfo.addMethod(makeDeserializeMethod(tree.sym)); 1.30 + int prevPos = make.pos; 1.31 + try { 1.32 + make.at(tree); 1.33 + kInfo.addMethod(makeDeserializeMethod(tree.sym)); 1.34 + } finally { 1.35 + make.at(prevPos); 1.36 + } 1.37 } 1.38 //add all translated instance methods here 1.39 List<JCTree> newMethods = kInfo.appendedMethodList.toList(); 1.40 @@ -400,14 +409,21 @@ 1.41 if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) { 1.42 super.visitIdent(tree); 1.43 } else { 1.44 - LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; 1.45 - JCTree ltree = lambdaContext.translate(tree); 1.46 - if (ltree != null) { 1.47 - result = ltree; 1.48 - } else { 1.49 - //access to untranslated symbols (i.e. compile-time constants, 1.50 - //members defined inside the lambda body, etc.) ) 1.51 - super.visitIdent(tree); 1.52 + int prevPos = make.pos; 1.53 + try { 1.54 + make.at(tree); 1.55 + 1.56 + LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; 1.57 + JCTree ltree = lambdaContext.translate(tree); 1.58 + if (ltree != null) { 1.59 + result = ltree; 1.60 + } else { 1.61 + //access to untranslated symbols (i.e. compile-time constants, 1.62 + //members defined inside the lambda body, etc.) ) 1.63 + super.visitIdent(tree); 1.64 + } 1.65 + } finally { 1.66 + make.at(prevPos); 1.67 } 1.68 } 1.69 } 1.70 @@ -417,11 +433,21 @@ 1.71 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; 1.72 if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { 1.73 JCExpression init = translate(tree.init); 1.74 - result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init); 1.75 + int prevPos = make.pos; 1.76 + try { 1.77 + result = make.at(tree).VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init); 1.78 + } finally { 1.79 + make.at(prevPos); 1.80 + } 1.81 } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) { 1.82 JCExpression init = translate(tree.init); 1.83 VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym); 1.84 - result = make.VarDef(xsym, init); 1.85 + int prevPos = make.pos; 1.86 + try { 1.87 + result = make.at(tree).VarDef(xsym, init); 1.88 + } finally { 1.89 + make.at(prevPos); 1.90 + } 1.91 // Replace the entered symbol for this variable 1.92 Scope sc = tree.sym.owner.members(); 1.93 if (sc != null) { 1.94 @@ -448,23 +474,28 @@ 1.95 boolean isLambda_void = expr.type.hasTag(VOID); 1.96 boolean isTarget_void = restype.hasTag(VOID); 1.97 boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); 1.98 - if (isTarget_void) { 1.99 - //target is void: 1.100 - // BODY; 1.101 - JCStatement stat = make.Exec(expr); 1.102 - return make.Block(0, List.<JCStatement>of(stat)); 1.103 - } else if (isLambda_void && isTarget_Void) { 1.104 - //void to Void conversion: 1.105 - // BODY; return null; 1.106 - ListBuffer<JCStatement> stats = new ListBuffer<>(); 1.107 - stats.append(make.Exec(expr)); 1.108 - stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 1.109 - return make.Block(0, stats.toList()); 1.110 - } else { 1.111 - //non-void to non-void conversion: 1.112 - // return (TYPE)BODY; 1.113 - JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype); 1.114 - return make.at(retExpr).Block(0, List.<JCStatement>of(make.Return(retExpr))); 1.115 + int prevPos = make.pos; 1.116 + try { 1.117 + if (isTarget_void) { 1.118 + //target is void: 1.119 + // BODY; 1.120 + JCStatement stat = make.at(expr).Exec(expr); 1.121 + return make.Block(0, List.<JCStatement>of(stat)); 1.122 + } else if (isLambda_void && isTarget_Void) { 1.123 + //void to Void conversion: 1.124 + // BODY; return null; 1.125 + ListBuffer<JCStatement> stats = new ListBuffer<>(); 1.126 + stats.append(make.at(expr).Exec(expr)); 1.127 + stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); 1.128 + return make.Block(0, stats.toList()); 1.129 + } else { 1.130 + //non-void to non-void conversion: 1.131 + // return (TYPE)BODY; 1.132 + JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype); 1.133 + return make.at(retExpr).Block(0, List.<JCStatement>of(make.Return(retExpr))); 1.134 + } 1.135 + } finally { 1.136 + make.at(prevPos); 1.137 } 1.138 } 1.139 1.140 @@ -966,8 +997,14 @@ 1.141 } 1.142 } 1.143 if (context.isSerializable()) { 1.144 - addDeserializationCase(refKind, refSym, tree.type, samSym, 1.145 - tree, staticArgs, indyType); 1.146 + int prevPos = make.pos; 1.147 + try { 1.148 + make.at(kInfo.clazz); 1.149 + addDeserializationCase(refKind, refSym, tree.type, samSym, 1.150 + tree, staticArgs, indyType); 1.151 + } finally { 1.152 + make.at(prevPos); 1.153 + } 1.154 } 1.155 } 1.156
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/T8019486/WrongLNTForLambdaTest.java Wed Oct 23 07:50:04 2013 +0200 2.3 @@ -0,0 +1,160 @@ 2.4 +/* 2.5 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. Oracle designates this 2.11 + * particular file as subject to the "Classpath" exception as provided 2.12 + * by Oracle in the LICENSE file that accompanied this code. 2.13 + * 2.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.17 + * version 2 for more details (a copy is included in the LICENSE file that 2.18 + * accompanied this code). 2.19 + * 2.20 + * You should have received a copy of the GNU General Public License version 2.21 + * 2 along with this work; if not, write to the Free Software Foundation, 2.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.23 + * 2.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.25 + * or visit www.oracle.com if you need additional information or have any 2.26 + * questions. 2.27 + */ 2.28 + 2.29 +/* 2.30 + * @test 2.31 + * @bug 8019486 8026861 2.32 + * @summary javac, generates erroneous LVT for a test case with lambda code 2.33 + * @library /tools/javac/lib 2.34 + * @build ToolBox 2.35 + * @run main WrongLNTForLambdaTest 2.36 + */ 2.37 + 2.38 +import java.io.File; 2.39 +import java.nio.file.Paths; 2.40 + 2.41 +import com.sun.tools.classfile.ClassFile; 2.42 +import com.sun.tools.classfile.Code_attribute; 2.43 +import com.sun.tools.classfile.LineNumberTable_attribute; 2.44 +import com.sun.tools.classfile.Method; 2.45 +import com.sun.tools.javac.util.Assert; 2.46 + 2.47 +public class WrongLNTForLambdaTest { 2.48 + 2.49 + static final String testSource = 2.50 + /* 01 */ "import java.util.List;\n" + 2.51 + /* 02 */ "import java.util.Arrays;\n" + 2.52 + /* 03 */ "import java.util.stream.Collectors;\n" + 2.53 + /* 04 */ "\n" + 2.54 + /* 05 */ "public class Foo {\n" + 2.55 + /* 06 */ " void bar(int value) {\n" + 2.56 + /* 07 */ " final List<Integer> numbers = Arrays.asList(1, 2, 3);\n" + 2.57 + /* 08 */ " final List<Integer> numbersPlusOne = \n" + 2.58 + /* 09 */ " numbers.stream().map(number -> number / 1).collect(Collectors.toList());\n" + 2.59 + /* 10 */ " }\n" + 2.60 + /* 11 */ " void variablesInLambdas(int value) {\n" + 2.61 + /* 12 */ " Runnable r1 = () -> {\n" + 2.62 + /* 13 */ " int i = value;\n" + 2.63 + /* 14 */ " class FooBar<T extends CharSequence> {\n" + 2.64 + /* 15 */ " public void run() {\n" + 2.65 + /* 16 */ " T t = null;\n" + 2.66 + /* 17 */ " }\n" + 2.67 + /* 18 */ " }\n" + 2.68 + /* 19 */ " };\n" + 2.69 + /* 20 */ " Runnable r2 = () -> System.err.println(1);\n" + 2.70 + /* 21 */ " Runnable r3 = (Runnable & java.io.Serializable) this::foo;\n" + 2.71 + /* 22 */ " Runnable r4 = super :: notify;\n" + 2.72 + /* 23 */ " }\n" + 2.73 + /* 24 */ " private void foo() {}\n" + 2.74 + /* 25 */ "}"; 2.75 + 2.76 + static final int[][] simpleLambdaExpectedLNT = { 2.77 + // {line-number, start-pc}, 2.78 + {9, 0}, //number -> number / 1 2.79 + }; 2.80 + 2.81 + static final int[][] lambdaWithVarsExpectedLNT = { 2.82 + // {line-number, start-pc}, 2.83 + {13, 0}, //number -> number / 1 2.84 + {19, 2}, //number -> number / 1 2.85 + }; 2.86 + 2.87 + static final int[][] insideLambdaWithVarsExpectedLNT = { 2.88 + // {line-number, start-pc}, 2.89 + {16, 0}, //number -> number / 1 2.90 + {17, 2}, //number -> number / 1 2.91 + }; 2.92 + 2.93 + static final int[][] lambdaVoid2VoidExpectedLNT = { 2.94 + // {line-number, start-pc}, 2.95 + {20, 0}, //number -> number / 1 2.96 + }; 2.97 + 2.98 + static final int[][] deserializeExpectedLNT = { 2.99 + // {line-number, start-pc}, 2.100 + {05, 0}, //number -> number / 1 2.101 + }; 2.102 + 2.103 + static final int[][] lambdaBridgeExpectedLNT = { 2.104 + // {line-number, start-pc}, 2.105 + {22, 0}, //number -> number / 1 2.106 + }; 2.107 + 2.108 + public static void main(String[] args) throws Exception { 2.109 + new WrongLNTForLambdaTest().run(); 2.110 + } 2.111 + 2.112 + void run() throws Exception { 2.113 + compileTestClass(); 2.114 + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), 2.115 + "Foo.class").toUri()), "lambda$bar$0", simpleLambdaExpectedLNT); 2.116 + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), 2.117 + "Foo.class").toUri()), "lambda$variablesInLambdas$1", lambdaWithVarsExpectedLNT); 2.118 + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), 2.119 + "Foo$1FooBar.class").toUri()), "run", insideLambdaWithVarsExpectedLNT); 2.120 + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), 2.121 + "Foo.class").toUri()), "lambda$variablesInLambdas$2", lambdaVoid2VoidExpectedLNT); 2.122 + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), 2.123 + "Foo.class").toUri()), "$deserializeLambda$", deserializeExpectedLNT); 2.124 + checkClassFile(new File(Paths.get(System.getProperty("user.dir"), 2.125 + "Foo.class").toUri()), "lambda$MR$variablesInLambdas$notify$8bc4f5bd$1", lambdaBridgeExpectedLNT); 2.126 + } 2.127 + 2.128 + void compileTestClass() throws Exception { 2.129 + ToolBox.JavaToolArgs javacSuccessArgs = 2.130 + new ToolBox.JavaToolArgs().setSources(testSource); 2.131 + ToolBox.javac(javacSuccessArgs); 2.132 + } 2.133 + 2.134 + void checkClassFile(final File cfile, String methodToFind, int[][] expectedLNT) throws Exception { 2.135 + ClassFile classFile = ClassFile.read(cfile); 2.136 + boolean methodFound = false; 2.137 + for (Method method : classFile.methods) { 2.138 + if (method.getName(classFile.constant_pool).equals(methodToFind)) { 2.139 + methodFound = true; 2.140 + Code_attribute code = (Code_attribute) method.attributes.get("Code"); 2.141 + LineNumberTable_attribute lnt = 2.142 + (LineNumberTable_attribute) code.attributes.get("LineNumberTable"); 2.143 + Assert.check(lnt.line_number_table_length == expectedLNT.length, 2.144 + "The LineNumberTable found has a length different to the expected one"); 2.145 + int i = 0; 2.146 + for (LineNumberTable_attribute.Entry entry: lnt.line_number_table) { 2.147 + Assert.check(entry.line_number == expectedLNT[i][0] && 2.148 + entry.start_pc == expectedLNT[i][1], 2.149 + "LNT entry at pos " + i + " differ from expected." + 2.150 + "Found " + entry.line_number + ":" + entry.start_pc + 2.151 + ". Expected " + expectedLNT[i][0] + ":" + expectedLNT[i][1]); 2.152 + i++; 2.153 + } 2.154 + } 2.155 + } 2.156 + Assert.check(methodFound, "The seek method was not found"); 2.157 + } 2.158 + 2.159 + void error(String msg) { 2.160 + throw new AssertionError(msg); 2.161 + } 2.162 + 2.163 +}
3.1 --- a/test/tools/javac/T8019486/WrongLVTForLambdaTest.java Wed Oct 23 23:02:17 2013 +0200 3.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 3.3 @@ -1,109 +0,0 @@ 3.4 -/* 3.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3.6 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.7 - * 3.8 - * This code is free software; you can redistribute it and/or modify it 3.9 - * under the terms of the GNU General Public License version 2 only, as 3.10 - * published by the Free Software Foundation. Oracle designates this 3.11 - * particular file as subject to the "Classpath" exception as provided 3.12 - * by Oracle in the LICENSE file that accompanied this code. 3.13 - * 3.14 - * This code is distributed in the hope that it will be useful, but WITHOUT 3.15 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.16 - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.17 - * version 2 for more details (a copy is included in the LICENSE file that 3.18 - * accompanied this code). 3.19 - * 3.20 - * You should have received a copy of the GNU General Public License version 3.21 - * 2 along with this work; if not, write to the Free Software Foundation, 3.22 - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.23 - * 3.24 - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.25 - * or visit www.oracle.com if you need additional information or have any 3.26 - * questions. 3.27 - */ 3.28 - 3.29 -/* 3.30 - * @test 3.31 - * @bug 8019486 3.32 - * @summary javac, generates erroneous LVT for a test case with lambda code 3.33 - * @library /tools/javac/lib 3.34 - * @build ToolBox 3.35 - * @run main WrongLVTForLambdaTest 3.36 - */ 3.37 - 3.38 -import java.io.File; 3.39 -import java.nio.file.Paths; 3.40 - 3.41 -import com.sun.tools.classfile.ClassFile; 3.42 -import com.sun.tools.classfile.Code_attribute; 3.43 -import com.sun.tools.classfile.LineNumberTable_attribute; 3.44 -import com.sun.tools.classfile.Method; 3.45 -import com.sun.tools.javac.util.Assert; 3.46 - 3.47 -public class WrongLVTForLambdaTest { 3.48 - 3.49 - static final String testSource = 3.50 - /* 01 */ "import java.util.List;\n" + 3.51 - /* 02 */ "import java.util.Arrays;\n" + 3.52 - /* 03 */ "import java.util.stream.Collectors;\n" + 3.53 - /* 04 */ "\n" + 3.54 - /* 05 */ "public class Foo {\n" + 3.55 - /* 06 */ " void bar(int value) {\n" + 3.56 - /* 07 */ " final List<Integer> numbers = Arrays.asList(1, 2, 3);\n" + 3.57 - /* 08 */ " final List<Integer> numbersPlusOne = \n" + 3.58 - /* 09 */ " numbers.stream().map(number -> number / 1).collect(Collectors.toList());\n" + 3.59 - /* 10 */ " }\n" + 3.60 - /* 11 */ "}"; 3.61 - 3.62 - static final int[][] expectedLNT = { 3.63 - // {line-number, start-pc}, 3.64 - {9, 0}, //number -> number / 1 3.65 - }; 3.66 - 3.67 - public static void main(String[] args) throws Exception { 3.68 - new WrongLVTForLambdaTest().run(); 3.69 - } 3.70 - 3.71 - void run() throws Exception { 3.72 - compileTestClass(); 3.73 - checkClassFile(new File(Paths.get(System.getProperty("user.dir"), 3.74 - "Foo.class").toUri())); 3.75 - } 3.76 - 3.77 - void compileTestClass() throws Exception { 3.78 - ToolBox.JavaToolArgs javacSuccessArgs = 3.79 - new ToolBox.JavaToolArgs().setSources(testSource); 3.80 - ToolBox.javac(javacSuccessArgs); 3.81 - } 3.82 - 3.83 - void checkClassFile(final File cfile) throws Exception { 3.84 - ClassFile classFile = ClassFile.read(cfile); 3.85 - int methodsFound = 0; 3.86 - for (Method method : classFile.methods) { 3.87 - if (method.getName(classFile.constant_pool).startsWith("lambda$")) { 3.88 - ++methodsFound; 3.89 - Code_attribute code = (Code_attribute) method.attributes.get("Code"); 3.90 - LineNumberTable_attribute lnt = 3.91 - (LineNumberTable_attribute) code.attributes.get("LineNumberTable"); 3.92 - Assert.check(lnt.line_number_table_length == expectedLNT.length, 3.93 - "The LineNumberTable found has a length different to the expected one"); 3.94 - int i = 0; 3.95 - for (LineNumberTable_attribute.Entry entry: lnt.line_number_table) { 3.96 - Assert.check(entry.line_number == expectedLNT[i][0] && 3.97 - entry.start_pc == expectedLNT[i][1], 3.98 - "LNT entry at pos " + i + " differ from expected." + 3.99 - "Found " + entry.line_number + ":" + entry.start_pc + 3.100 - ". Expected " + expectedLNT[i][0] + ":" + expectedLNT[i][1]); 3.101 - i++; 3.102 - } 3.103 - } 3.104 - } 3.105 - Assert.check(methodsFound == 1, "Expected to find one lambda method, found " + methodsFound); 3.106 - } 3.107 - 3.108 - void error(String msg) { 3.109 - throw new AssertionError(msg); 3.110 - } 3.111 - 3.112 -}