From 4a8cb00ab048fc3c83e6b6d7ee9faf9250867cab Mon Sep 17 00:00:00 2001 From: rfield Date: Wed, 1 May 2013 08:46:04 -0700 Subject: [PATCH] 8011591: BootstrapMethodError when capturing constructor ref to local classes Reviewed-by: mcimadamore --- .../sun/tools/javac/comp/LambdaToMethod.java | 78 ++++++++++++++---- ...thodReferenceTestNewInnerImplicitArgs.java | 82 +++++++++++++++++++ 2 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java diff --git a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 0413254f..bcf4e6ba 100644 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -40,10 +40,9 @@ import com.sun.tools.javac.code.Symbol.MethodSymbol; 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 @@ public class LambdaToMethod extends TreeTranslator { 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 @@ public class LambdaToMethod extends TreeTranslator { make = TreeMaker.instance(context); types = Types.instance(context); transTypes = TransTypes.instance(context); - analyzer = new LambdaAnalyzer(); + analyzer = new LambdaAnalyzerPreprocessor(); } // @@ -206,7 +205,7 @@ public class LambdaToMethod extends TreeTranslator { 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 @@ public class LambdaToMethod extends TreeTranslator { /** 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 @@ public class LambdaToMethod extends TreeTranslator { * 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 @@ public class LambdaToMethod extends TreeTranslator { 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 @@ public class LambdaToMethod extends TreeTranslator { frameStack.head.addLocal(param.sym); } contextMap.put(tree, context); - scan(tree.body); + super.visitLambda(tree); context.complete(); } finally { @@ -1220,12 +1229,47 @@ public class LambdaToMethod extends TreeTranslator { }; 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 @@ public class LambdaToMethod extends TreeTranslator { } localContext = localContext.prev; } - scan(tree.selected); - } else { - super.visitSelect(tree); } + super.visitSelect(tree); } @Override diff --git a/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java b/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java new file mode 100644 index 00000000..4f910572 --- /dev/null +++ b/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java @@ -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"); + } +} -- GitLab