# HG changeset patch # User mcimadamore # Date 1362492297 0 # Node ID 088a91896245c2d0b038e42436cced3a4c32a183 # Parent 332d7f5fdf62d878ee7b828bc49fa76a3f6c4606 8004962: Code generation crash with lambda and local classes Summary: Translation info should be propagated from LambdaToMethod to Lower Reviewed-by: jjg, rfield diff -r 332d7f5fdf62 -r 088a91896245 langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Fri Mar 01 10:47:39 2013 -0800 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Tue Mar 05 14:04:57 2013 +0000 @@ -44,6 +44,7 @@ import com.sun.tools.javac.code.Type.MethodType; import com.sun.tools.javac.code.Types; import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*; +import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector; import com.sun.tools.javac.jvm.*; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.List; @@ -70,6 +71,7 @@ */ public class LambdaToMethod extends TreeTranslator { + private Lower lower; private Names names; private Symtab syms; private Resolve rs; @@ -147,6 +149,7 @@ } private LambdaToMethod(Context context) { + lower = Lower.instance(context); names = Names.instance(context); syms = Symtab.instance(context); rs = Resolve.instance(context); @@ -1037,6 +1040,8 @@ private Map serializableLambdaCounts = new HashMap(); + private Map localClassDefs; + /** * maps for fake clinit symbols to be used as owners of lambda occurring in * a static var init context @@ -1046,6 +1051,7 @@ private void analyzeClass(JCClassDecl tree) { frameStack = List.nil(); + localClassDefs = new HashMap(); scan(tree); } @@ -1072,13 +1078,22 @@ try { serializableLambdaCounts = new HashMap(); prevClinits = new HashMap(); + if (tree.sym.owner.kind == MTH) { + localClassDefs.put(tree.sym, tree); + } if (directlyEnclosingLambda() != null) { tree.sym.owner = owner(); if (tree.sym.hasOuterInstance()) { //if a class is defined within a lambda, the lambda must capture //its enclosing instance (if any) - ((LambdaTranslationContext) context()) - .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS); + TranslationContext localContext = context(); + while (localContext != null) { + if (localContext.tree.getTag() == LAMBDA) { + ((LambdaTranslationContext)localContext) + .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS); + } + localContext = localContext.prev; + } } } frameStack = frameStack.prepend(new Frame(tree)); @@ -1164,11 +1179,50 @@ @Override public void visitNewClass(JCNewClass tree) { if (lambdaNewClassFilter(context(), tree)) { - ((LambdaTranslationContext) context()) - .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS); + TranslationContext localContext = context(); + while (localContext != null) { + if (localContext.tree.getTag() == LAMBDA) { + ((LambdaTranslationContext)localContext) + .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS); + } + localContext = localContext.prev; + } + } + if (context() != null && tree.type.tsym.owner.kind == MTH) { + LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context(); + captureLocalClassDefs(tree.type.tsym, lambdaContext); } super.visitNewClass(tree); } + //where + void captureLocalClassDefs(Symbol csym, final LambdaTranslationContext lambdaContext) { + JCClassDecl localCDef = localClassDefs.get(csym); + if (localCDef != null && localCDef.pos < lambdaContext.tree.pos) { + BasicFreeVarCollector fvc = lower.new BasicFreeVarCollector() { + @Override + void addFreeVars(ClassSymbol c) { + captureLocalClassDefs(c, lambdaContext); + } + @Override + void visitSymbol(Symbol sym) { + if (sym.kind == VAR && + sym.owner.kind == MTH && + ((VarSymbol)sym).getConstValue() == null) { + TranslationContext localContext = context(); + while (localContext != null) { + if (localContext.tree.getTag() == LAMBDA) { + JCTree block = capturedDecl(localContext.depth, sym); + if (block == null) break; + ((LambdaTranslationContext)localContext).addSymbol(sym, CAPTURED_VAR); + } + localContext = localContext.prev; + } + } + } + }; + fvc.scan(localCDef); + } + } @Override public void visitReference(JCMemberReference tree) { @@ -1550,7 +1604,7 @@ * Translate a symbol of a given kind into something suitable for the * synthetic lambda body */ - Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) { + Symbol translate(String name, final Symbol sym, LambdaSymbolKind skind) { switch (skind) { case CAPTURED_THIS: return sym; // self represented @@ -1558,6 +1612,14 @@ // Just erase the type var return new VarSymbol(sym.flags(), names.fromString(name), types.erasure(sym.type), sym.owner); + case CAPTURED_VAR: + return new VarSymbol(SYNTHETIC | FINAL, names.fromString(name), types.erasure(sym.type), translatedSym) { + @Override + public Symbol baseSymbol() { + //keep mapping with original captured symbol + return sym; + } + }; default: return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym); } diff -r 332d7f5fdf62 -r 088a91896245 langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Fri Mar 01 10:47:39 2013 -0800 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Tue Mar 05 14:04:57 2013 +0000 @@ -163,6 +163,12 @@ */ JCTree outermostMemberDef; + /** A map from local variable symbols to their translation (as per LambdaToMethod). + * This is required when a capturing local class is created from a lambda (in which + * case the captured symbols should be replaced with the translated lambda symbols). + */ + Map lambdaTranslationMap = null; + /** A navigator class for assembling a mapping from local class symbols * to class definition trees. * There is only one case; all other cases simply traverse down the tree. @@ -206,10 +212,51 @@ Map> freevarCache; /** A navigator class for collecting the free variables accessed - * from a local class. - * There is only one case; all other cases simply traverse down the tree. + * from a local class. There is only one case; all other cases simply + * traverse down the tree. This class doesn't deal with the specific + * of Lower - it's an abstract visitor that is meant to be reused in + * order to share the local variable capture logic. */ - class FreeVarCollector extends TreeScanner { + abstract class BasicFreeVarCollector extends TreeScanner { + + /** Add all free variables of class c to fvs list + * unless they are already there. + */ + abstract void addFreeVars(ClassSymbol c); + + /** If tree refers to a variable in owner of local class, add it to + * free variables list. + */ + public void visitIdent(JCIdent tree) { + visitSymbol(tree.sym); + } + // where + abstract void visitSymbol(Symbol _sym); + + /** If tree refers to a class instance creation expression + * add all free variables of the freshly created class. + */ + public void visitNewClass(JCNewClass tree) { + ClassSymbol c = (ClassSymbol)tree.constructor.owner; + addFreeVars(c); + super.visitNewClass(tree); + } + + /** If tree refers to a superclass constructor call, + * add all free variables of the superclass. + */ + public void visitApply(JCMethodInvocation tree) { + if (TreeInfo.name(tree.meth) == names._super) { + addFreeVars((ClassSymbol) TreeInfo.symbol(tree.meth).owner); + } + super.visitApply(tree); + } + } + + /** + * Lower-specific subclass of {@code BasicFreeVarCollector}. + */ + class FreeVarCollector extends BasicFreeVarCollector { /** The owner of the local class. */ @@ -238,10 +285,8 @@ fvs = fvs.prepend(v); } - /** Add all free variables of class c to fvs list - * unless they are already there. - */ - private void addFreeVars(ClassSymbol c) { + @Override + void addFreeVars(ClassSymbol c) { List fvs = freevarCache.get(c); if (fvs != null) { for (List l = fvs; l.nonEmpty(); l = l.tail) { @@ -250,15 +295,8 @@ } } - /** If tree refers to a variable in owner of local class, add it to - * free variables list. - */ - public void visitIdent(JCIdent tree) { - result = tree; - visitSymbol(tree.sym); - } - // where - private void visitSymbol(Symbol _sym) { + @Override + void visitSymbol(Symbol _sym) { Symbol sym = _sym; if (sym.kind == VAR || sym.kind == MTH) { while (sym != null && sym.owner != owner) @@ -281,7 +319,6 @@ */ public void visitNewClass(JCNewClass tree) { ClassSymbol c = (ClassSymbol)tree.constructor.owner; - addFreeVars(c); if (tree.encl == null && c.hasOuterInstance() && outerThisStack.head != null) @@ -306,7 +343,6 @@ */ public void visitApply(JCMethodInvocation tree) { if (TreeInfo.name(tree.meth) == names._super) { - addFreeVars((ClassSymbol) TreeInfo.symbol(tree.meth).owner); Symbol constructor = TreeInfo.symbol(tree.meth); ClassSymbol c = (ClassSymbol)constructor.owner; if (c.hasOuterInstance() && @@ -1171,6 +1207,14 @@ accessBase(tree.pos(), sym), sym).setType(tree.type); } } + } else if (sym.owner.kind == MTH && lambdaTranslationMap != null) { + //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); + if (translatedSym != null) { + tree = make.at(tree.pos).Ident(translatedSym); + } } } return tree; @@ -2725,10 +2769,30 @@ outerThisStack = prevOuterThisStack; } else { - super.visitMethodDef(tree); + Map prevLambdaTranslationMap = + lambdaTranslationMap; + try { + lambdaTranslationMap = (tree.sym.flags() & SYNTHETIC) != 0 && + tree.sym.name.startsWith(names.lambda) ? + makeTranslationMap(tree) : null; + super.visitMethodDef(tree); + } finally { + lambdaTranslationMap = prevLambdaTranslationMap; + } } result = tree; } + //where + private Map makeTranslationMap(JCMethodDecl tree) { + Map translationMap = new HashMap(); + for (JCVariableDecl vd : tree.params) { + Symbol p = vd.sym; + if (p != p.baseSymbol()) { + translationMap.put(p.baseSymbol(), p); + } + } + return translationMap; + } public void visitAnnotatedType(JCAnnotatedType tree) { // No need to retain type annotations any longer. diff -r 332d7f5fdf62 -r 088a91896245 langtools/test/tools/javac/lambda/LambdaCapture07.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/lambda/LambdaCapture07.java Tue Mar 05 14:04:57 2013 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8004962 + * @summary Code generation crash with lambda and local classes + */ +public class LambdaCapture07 { + + static int assertionCount = 0; + + static void assertTrue(boolean cond) { + assertionCount++; + if (!cond) + throw new AssertionError(); + } + + interface SAM { + void m(); + } + + void test(int i) { + class Local { Local() { assertTrue(i == 42); } } + class LocalSub extends Local { } + SAM s_sup = ()->new Local(); + s_sup.m(); + SAM s_sub = ()->new LocalSub(); + s_sub.m(); + SAM s_sup_nested = ()->{ SAM s = ()->new Local(); s.m(); }; + s_sup_nested.m(); + SAM s_sub_nested = ()->{ SAM s = ()->new LocalSub(); s.m(); }; + s_sub_nested.m(); + } + + public static void main(String[] args) { + new LambdaCapture07().test(42); + assertTrue(assertionCount == 4); + } +}