# HG changeset patch # User rfield # Date 1367423164 25200 # Node ID fec581c36de4e24d4bbeebedb63e89b45bfe8ac5 # Parent 1beaa4b6bb260499848d7deafe006c53d6b3559b 8011591: BootstrapMethodError when capturing constructor ref to local classes Reviewed-by: mcimadamore diff -r 1beaa4b6bb26 -r fec581c36de4 langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Tue Apr 30 17:53:30 2013 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Wed May 01 08:46:04 2013 -0700 @@ -40,10 +40,9 @@ import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Symtab; import com.sun.tools.javac.code.Type; -import com.sun.tools.javac.code.Type.ClassType; 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.LambdaToMethod.LambdaAnalyzerPreprocessor.*; import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector; import com.sun.tools.javac.jvm.*; import com.sun.tools.javac.util.*; @@ -81,7 +80,7 @@ private Env attrEnv; /** the analyzer scanner */ - private LambdaAnalyzer analyzer; + private LambdaAnalyzerPreprocessor analyzer; /** map from lambda trees to translation contexts */ private Map> contextMap; @@ -156,7 +155,7 @@ make = TreeMaker.instance(context); types = Types.instance(context); transTypes = TransTypes.instance(context); - analyzer = new LambdaAnalyzer(); + analyzer = new LambdaAnalyzerPreprocessor(); } // @@ -206,7 +205,7 @@ public void visitClassDef(JCClassDecl tree) { if (tree.sym.owner.kind == PCK) { //analyze class - analyzer.analyzeClass(tree); + tree = analyzer.analyzeAndPreprocessClass(tree); } KlassInfo prevKlassInfo = kInfo; try { @@ -531,16 +530,25 @@ /** Make an attributed class instance creation expression. * @param ctype The class type. * @param args The constructor arguments. + * @param cons The constructor symbol */ - JCNewClass makeNewClass(Type ctype, List args) { + JCNewClass makeNewClass(Type ctype, List args, Symbol cons) { JCNewClass tree = make.NewClass(null, null, make.QualIdent(ctype.tsym), args, null); - tree.constructor = rs.resolveConstructor( - null, attrEnv, ctype, TreeInfo.types(args), List.nil()); + tree.constructor = cons; tree.type = ctype; return tree; } + /** Make an attributed class instance creation expression. + * @param ctype The class type. + * @param args The constructor arguments. + */ + JCNewClass makeNewClass(Type ctype, List args) { + return makeNewClass(ctype, args, + rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil())); + } + private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym, DiagnosticPosition pos, List staticArgs, MethodType indyType) { String functionalInterfaceClass = classSig(targetType); @@ -1019,8 +1027,9 @@ * This visitor collects information about translation of a lambda expression. * More specifically, it keeps track of the enclosing contexts and captured locals * accessed by the lambda being translated (as well as other useful info). + * It also translates away problems for LambdaToMethod. */ - class LambdaAnalyzer extends TreeScanner { + class LambdaAnalyzerPreprocessor extends TreeTranslator { /** the frame stack - used to reconstruct translation info about enclosing scopes */ private List frameStack; @@ -1047,10 +1056,10 @@ private Map clinits = new HashMap(); - private void analyzeClass(JCClassDecl tree) { + private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { frameStack = List.nil(); localClassDefs = new HashMap(); - scan(tree); + return translate(tree); } @Override @@ -1154,7 +1163,7 @@ frameStack.head.addLocal(param.sym); } contextMap.put(tree, context); - scan(tree.body); + super.visitLambda(tree); context.complete(); } finally { @@ -1220,12 +1229,47 @@ }; fvc.scan(localCDef); } - } + } + /** + * Method references to local class constructors, may, if the local + * class references local variables, have implicit constructor + * parameters added in Lower; As a result, the invokedynamic bootstrap + * information added in the LambdaToMethod pass will have the wrong + * signature. Hooks between Lower and LambdaToMethod have been added to + * handle normal "new" in this case. This visitor converts potentially + * effected method references into a lambda containing a normal "new" of + * the class. + * + * @param tree + */ @Override public void visitReference(JCMemberReference tree) { - scan(tree.getQualifierExpression()); - contextMap.put(tree, makeReferenceContext(tree)); + if (tree.getMode() == ReferenceMode.NEW + && tree.kind != ReferenceKind.ARRAY_CTOR + && tree.sym.owner.isLocal()) { + MethodSymbol consSym = (MethodSymbol) tree.sym; + List ptypes = ((MethodType) consSym.type).getParameterTypes(); + Type classType = consSym.owner.type; + + // Make new-class call + List params = make.Params(ptypes, owner()); + JCNewClass nc = makeNewClass(classType, make.Idents(params)); + nc.pos = tree.pos; + + // Make lambda holding the new-class call + JCLambda slam = make.Lambda(params, nc); + slam.descriptorType = tree.descriptorType; + slam.targets = tree.targets; + slam.type = tree.type; + slam.pos = tree.pos; + + // Now it is a lambda, process as such + visitLambda(slam); + } else { + super.visitReference(tree); + contextMap.put(tree, makeReferenceContext(tree)); + } } @Override @@ -1240,10 +1284,8 @@ } localContext = localContext.prev; } - scan(tree.selected); - } else { - super.visitSelect(tree); } + super.visitSelect(tree); } @Override diff -r 1beaa4b6bb26 -r fec581c36de4 langtools/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java Wed May 01 08:46:04 2013 -0700 @@ -0,0 +1,82 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 8011591 + * @summary BootstrapMethodError when capturing constructor ref to local classes + * @run testng MethodReferenceTestNewInnerImplicitArgs + */ + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * Test the case that a constructor has implicit parameters added to + * access local variables and that this constructor is used in a + * method reference. + * @author Robert Field + */ + +@Test +public class MethodReferenceTestNewInnerImplicitArgs { + + + static class S { + String b; + S(String s, String s2) { b = s + s2; } + } + + interface I { + S m(); + } + + interface I2 { + S m(int i, int j); + } + + public static void testConstructorReferenceImplicitParameters() { + String title = "Hey"; + String a2 = "!!!"; + class MS extends S { + MS() { + super(title, a2); + } + } + + I result = MS::new; + assertEquals(result.m().b, "Hey!!!"); + + class MS2 extends S { + MS2(int x, int y) { + super(title+x, a2+y); + } + } + + I2 result2 = MS2::new; + assertEquals(result2.m(8, 4).b, "Hey8!!!4"); + } +}