Thu, 01 May 2014 15:43:28 -0700
8029852: Bad code generated (VerifyError) when lambda instantiates enclosing local class and has captured variables
8029725: Lambda reference to containing local class causes javac infinite recursion
Reviewed-by: vromero, jlahoda, dlsmith
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu May 01 11:35:02 2014 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Thu May 01 15:43:28 2014 -0700 1.3 @@ -36,6 +36,7 @@ 1.4 import com.sun.tools.javac.code.Symbol.ClassSymbol; 1.5 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; 1.6 import com.sun.tools.javac.code.Symbol.MethodSymbol; 1.7 +import com.sun.tools.javac.code.Symbol.TypeSymbol; 1.8 import com.sun.tools.javac.code.Symbol.VarSymbol; 1.9 import com.sun.tools.javac.code.Symtab; 1.10 import com.sun.tools.javac.code.Type; 1.11 @@ -50,8 +51,10 @@ 1.12 1.13 import java.util.EnumMap; 1.14 import java.util.HashMap; 1.15 +import java.util.HashSet; 1.16 import java.util.LinkedHashMap; 1.17 import java.util.Map; 1.18 +import java.util.Set; 1.19 1.20 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; 1.21 import static com.sun.tools.javac.code.Flags.*; 1.22 @@ -1282,7 +1285,10 @@ 1.23 1.24 @Override 1.25 public void visitNewClass(JCNewClass tree) { 1.26 - if (lambdaNewClassFilter(context(), tree)) { 1.27 + TypeSymbol def = tree.type.tsym; 1.28 + boolean inReferencedClass = currentlyInClass(def); 1.29 + boolean isLocal = def.isLocal(); 1.30 + if ((inReferencedClass && isLocal || lambdaNewClassFilter(context(), tree))) { 1.31 TranslationContext<?> localContext = context(); 1.32 while (localContext != null) { 1.33 if (localContext.tree.getTag() == LAMBDA) { 1.34 @@ -1292,16 +1298,16 @@ 1.35 localContext = localContext.prev; 1.36 } 1.37 } 1.38 - if (context() != null && tree.type.tsym.owner.kind == MTH) { 1.39 + if (context() != null && !inReferencedClass && isLocal) { 1.40 LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context(); 1.41 - captureLocalClassDefs(tree.type.tsym, lambdaContext); 1.42 + captureLocalClassDefs(def, lambdaContext); 1.43 } 1.44 super.visitNewClass(tree); 1.45 } 1.46 //where 1.47 void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) { 1.48 JCClassDecl localCDef = localClassDefs.get(csym); 1.49 - if (localCDef != null && localCDef.pos < lambdaContext.tree.pos) { 1.50 + if (localCDef != null && lambdaContext.freeVarProcessedLocalClasses.add(csym)) { 1.51 BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() { 1.52 @Override 1.53 void addFreeVars(ClassSymbol c) { 1.54 @@ -1327,6 +1333,18 @@ 1.55 fvc.scan(localCDef); 1.56 } 1.57 } 1.58 + //where 1.59 + boolean currentlyInClass(Symbol csym) { 1.60 + for (Frame frame : frameStack) { 1.61 + if (frame.tree.hasTag(JCTree.Tag.CLASSDEF)) { 1.62 + JCClassDecl cdef = (JCClassDecl) frame.tree; 1.63 + if (cdef.sym == csym) { 1.64 + return true; 1.65 + } 1.66 + } 1.67 + } 1.68 + return false; 1.69 + } 1.70 1.71 /** 1.72 * Method references to local class constructors, may, if the local 1.73 @@ -1752,6 +1770,11 @@ 1.74 1.75 List<JCVariableDecl> syntheticParams; 1.76 1.77 + /** 1.78 + * to prevent recursion, track local classes processed 1.79 + */ 1.80 + final Set<Symbol> freeVarProcessedLocalClasses; 1.81 + 1.82 LambdaTranslationContext(JCLambda tree) { 1.83 super(tree); 1.84 Frame frame = frameStack.head; 1.85 @@ -1781,6 +1804,8 @@ 1.86 translatedSymbols.put(CAPTURED_VAR, new LinkedHashMap<Symbol, Symbol>()); 1.87 translatedSymbols.put(CAPTURED_THIS, new LinkedHashMap<Symbol, Symbol>()); 1.88 translatedSymbols.put(TYPE_VAR, new LinkedHashMap<Symbol, Symbol>()); 1.89 + 1.90 + freeVarProcessedLocalClasses = new HashSet<>(); 1.91 } 1.92 1.93 /**
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/lambda/LambdaLocalTest.java Thu May 01 15:43:28 2014 -0700 2.3 @@ -0,0 +1,55 @@ 2.4 +/* 2.5 + * Copyright (c) 2014, 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. 2.11 + * 2.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.15 + * version 2 for more details (a copy is included in the LICENSE file that 2.16 + * accompanied this code). 2.17 + * 2.18 + * You should have received a copy of the GNU General Public License version 2.19 + * 2 along with this work; if not, write to the Free Software Foundation, 2.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.21 + * 2.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.23 + * or visit www.oracle.com if you need additional information or have any 2.24 + * questions. 2.25 + */ 2.26 + 2.27 +/* 2.28 + * @test 2.29 + * @bug 8029725 2.30 + * @summary Lambda reference to containing local class causes javac infinite recursion 2.31 + * @author Robert Field 2.32 + * @run main LambdaLocalTest 2.33 + */ 2.34 + 2.35 +public class LambdaLocalTest { 2.36 + interface F {void f();} 2.37 + 2.38 + static F f; 2.39 + static StringBuffer sb = new StringBuffer(); 2.40 + 2.41 + static void assertEquals(Object val, Object expected) { 2.42 + if (!val.equals(expected)) { 2.43 + throw new AssertionError("expected '" + expected + "' got '" + val + "'"); 2.44 + } 2.45 + } 2.46 + 2.47 + public static void main(String[] args) { 2.48 + class Local { 2.49 + public Local() { 2.50 + f = () -> new Local(); 2.51 + sb.append("+"); 2.52 + } 2.53 + } 2.54 + new Local(); 2.55 + f.f(); 2.56 + assertEquals(sb.toString(), "++"); 2.57 + } 2.58 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/test/tools/javac/lambda/LambdaOuterLocalTest.java Thu May 01 15:43:28 2014 -0700 3.3 @@ -0,0 +1,61 @@ 3.4 +/* 3.5 + * Copyright (c) 2014, 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. 3.11 + * 3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 3.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 3.15 + * version 2 for more details (a copy is included in the LICENSE file that 3.16 + * accompanied this code). 3.17 + * 3.18 + * You should have received a copy of the GNU General Public License version 3.19 + * 2 along with this work; if not, write to the Free Software Foundation, 3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 3.21 + * 3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 3.23 + * or visit www.oracle.com if you need additional information or have any 3.24 + * questions. 3.25 + */ 3.26 + 3.27 +/* 3.28 + * @test 3.29 + * @bug 8029725 3.30 + * @summary Lambda reference to containing local class causes javac infinite recursion 3.31 + * @author Robert Field 3.32 + * @run main LambdaOuterLocalTest 3.33 + */ 3.34 + 3.35 +public class LambdaOuterLocalTest { 3.36 + interface F {void f();} 3.37 + 3.38 + static F f; 3.39 + static StringBuffer sb = new StringBuffer(); 3.40 + 3.41 + static void assertEquals(Object val, Object expected) { 3.42 + if (!val.equals(expected)) { 3.43 + throw new AssertionError("expected '" + expected + "' got '" + val + "'"); 3.44 + } 3.45 + } 3.46 + 3.47 + public static void main(String[] args) { 3.48 + class Local1 { 3.49 + public Local1() { 3.50 + class Local2 { 3.51 + public Local2() { 3.52 + f = () -> new Local1(); 3.53 + sb.append("2"); 3.54 + } 3.55 + } 3.56 + sb.append("1"); 3.57 + new Local2(); 3.58 + } 3.59 + } 3.60 + new Local1(); 3.61 + f.f(); 3.62 + assertEquals(sb.toString(), "1212"); 3.63 + } 3.64 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/lambda/SingleLocalTest.java Thu May 01 15:43:28 2014 -0700 4.3 @@ -0,0 +1,50 @@ 4.4 +/* 4.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +/* 4.28 + * @test 4.29 + * @bug 8029852 4.30 + * @summary Bad code generated (VerifyError) when lambda instantiates 4.31 + * enclosing local class and has captured variables 4.32 + */ 4.33 +public class SingleLocalTest { 4.34 + interface F {void f();} 4.35 + 4.36 + static F f; 4.37 + 4.38 + public static void main(String[] args) { 4.39 + StringBuffer sb = new StringBuffer(); 4.40 + class Local1 { 4.41 + public Local1() { 4.42 + f = () -> new Local1(); 4.43 + sb.append("1"); 4.44 + } 4.45 + } 4.46 + new Local1(); 4.47 + f.f(); 4.48 + String s = sb.toString(); 4.49 + if (!s.equals("11")) { 4.50 + throw new AssertionError("Expected '11' got '" + s + "'"); 4.51 + } 4.52 + } 4.53 +}