Wed, 01 May 2013 08:46:04 -0700
8011591: BootstrapMethodError when capturing constructor ref to local classes
Reviewed-by: mcimadamore
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Tue Apr 30 17:53:30 2013 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java Wed May 01 08:46:04 2013 -0700 1.3 @@ -40,10 +40,9 @@ 1.4 import com.sun.tools.javac.code.Symbol.VarSymbol; 1.5 import com.sun.tools.javac.code.Symtab; 1.6 import com.sun.tools.javac.code.Type; 1.7 -import com.sun.tools.javac.code.Type.ClassType; 1.8 import com.sun.tools.javac.code.Type.MethodType; 1.9 import com.sun.tools.javac.code.Types; 1.10 -import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*; 1.11 +import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*; 1.12 import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector; 1.13 import com.sun.tools.javac.jvm.*; 1.14 import com.sun.tools.javac.util.*; 1.15 @@ -81,7 +80,7 @@ 1.16 private Env<AttrContext> attrEnv; 1.17 1.18 /** the analyzer scanner */ 1.19 - private LambdaAnalyzer analyzer; 1.20 + private LambdaAnalyzerPreprocessor analyzer; 1.21 1.22 /** map from lambda trees to translation contexts */ 1.23 private Map<JCTree, TranslationContext<?>> contextMap; 1.24 @@ -156,7 +155,7 @@ 1.25 make = TreeMaker.instance(context); 1.26 types = Types.instance(context); 1.27 transTypes = TransTypes.instance(context); 1.28 - analyzer = new LambdaAnalyzer(); 1.29 + analyzer = new LambdaAnalyzerPreprocessor(); 1.30 } 1.31 // </editor-fold> 1.32 1.33 @@ -206,7 +205,7 @@ 1.34 public void visitClassDef(JCClassDecl tree) { 1.35 if (tree.sym.owner.kind == PCK) { 1.36 //analyze class 1.37 - analyzer.analyzeClass(tree); 1.38 + tree = analyzer.analyzeAndPreprocessClass(tree); 1.39 } 1.40 KlassInfo prevKlassInfo = kInfo; 1.41 try { 1.42 @@ -531,16 +530,25 @@ 1.43 /** Make an attributed class instance creation expression. 1.44 * @param ctype The class type. 1.45 * @param args The constructor arguments. 1.46 + * @param cons The constructor symbol 1.47 */ 1.48 - JCNewClass makeNewClass(Type ctype, List<JCExpression> args) { 1.49 + JCNewClass makeNewClass(Type ctype, List<JCExpression> args, Symbol cons) { 1.50 JCNewClass tree = make.NewClass(null, 1.51 null, make.QualIdent(ctype.tsym), args, null); 1.52 - tree.constructor = rs.resolveConstructor( 1.53 - null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil()); 1.54 + tree.constructor = cons; 1.55 tree.type = ctype; 1.56 return tree; 1.57 } 1.58 1.59 + /** Make an attributed class instance creation expression. 1.60 + * @param ctype The class type. 1.61 + * @param args The constructor arguments. 1.62 + */ 1.63 + JCNewClass makeNewClass(Type ctype, List<JCExpression> args) { 1.64 + return makeNewClass(ctype, args, 1.65 + rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil())); 1.66 + } 1.67 + 1.68 private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym, 1.69 DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) { 1.70 String functionalInterfaceClass = classSig(targetType); 1.71 @@ -1019,8 +1027,9 @@ 1.72 * This visitor collects information about translation of a lambda expression. 1.73 * More specifically, it keeps track of the enclosing contexts and captured locals 1.74 * accessed by the lambda being translated (as well as other useful info). 1.75 + * It also translates away problems for LambdaToMethod. 1.76 */ 1.77 - class LambdaAnalyzer extends TreeScanner { 1.78 + class LambdaAnalyzerPreprocessor extends TreeTranslator { 1.79 1.80 /** the frame stack - used to reconstruct translation info about enclosing scopes */ 1.81 private List<Frame> frameStack; 1.82 @@ -1047,10 +1056,10 @@ 1.83 private Map<ClassSymbol, Symbol> clinits = 1.84 new HashMap<ClassSymbol, Symbol>(); 1.85 1.86 - private void analyzeClass(JCClassDecl tree) { 1.87 + private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) { 1.88 frameStack = List.nil(); 1.89 localClassDefs = new HashMap<Symbol, JCClassDecl>(); 1.90 - scan(tree); 1.91 + return translate(tree); 1.92 } 1.93 1.94 @Override 1.95 @@ -1154,7 +1163,7 @@ 1.96 frameStack.head.addLocal(param.sym); 1.97 } 1.98 contextMap.put(tree, context); 1.99 - scan(tree.body); 1.100 + super.visitLambda(tree); 1.101 context.complete(); 1.102 } 1.103 finally { 1.104 @@ -1220,12 +1229,47 @@ 1.105 }; 1.106 fvc.scan(localCDef); 1.107 } 1.108 - } 1.109 + } 1.110 1.111 + /** 1.112 + * Method references to local class constructors, may, if the local 1.113 + * class references local variables, have implicit constructor 1.114 + * parameters added in Lower; As a result, the invokedynamic bootstrap 1.115 + * information added in the LambdaToMethod pass will have the wrong 1.116 + * signature. Hooks between Lower and LambdaToMethod have been added to 1.117 + * handle normal "new" in this case. This visitor converts potentially 1.118 + * effected method references into a lambda containing a normal "new" of 1.119 + * the class. 1.120 + * 1.121 + * @param tree 1.122 + */ 1.123 @Override 1.124 public void visitReference(JCMemberReference tree) { 1.125 - scan(tree.getQualifierExpression()); 1.126 - contextMap.put(tree, makeReferenceContext(tree)); 1.127 + if (tree.getMode() == ReferenceMode.NEW 1.128 + && tree.kind != ReferenceKind.ARRAY_CTOR 1.129 + && tree.sym.owner.isLocal()) { 1.130 + MethodSymbol consSym = (MethodSymbol) tree.sym; 1.131 + List<Type> ptypes = ((MethodType) consSym.type).getParameterTypes(); 1.132 + Type classType = consSym.owner.type; 1.133 + 1.134 + // Make new-class call 1.135 + List<JCVariableDecl> params = make.Params(ptypes, owner()); 1.136 + JCNewClass nc = makeNewClass(classType, make.Idents(params)); 1.137 + nc.pos = tree.pos; 1.138 + 1.139 + // Make lambda holding the new-class call 1.140 + JCLambda slam = make.Lambda(params, nc); 1.141 + slam.descriptorType = tree.descriptorType; 1.142 + slam.targets = tree.targets; 1.143 + slam.type = tree.type; 1.144 + slam.pos = tree.pos; 1.145 + 1.146 + // Now it is a lambda, process as such 1.147 + visitLambda(slam); 1.148 + } else { 1.149 + super.visitReference(tree); 1.150 + contextMap.put(tree, makeReferenceContext(tree)); 1.151 + } 1.152 } 1.153 1.154 @Override 1.155 @@ -1240,10 +1284,8 @@ 1.156 } 1.157 localContext = localContext.prev; 1.158 } 1.159 - scan(tree.selected); 1.160 - } else { 1.161 - super.visitSelect(tree); 1.162 } 1.163 + super.visitSelect(tree); 1.164 } 1.165 1.166 @Override
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java Wed May 01 08:46:04 2013 -0700 2.3 @@ -0,0 +1,82 @@ 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 8011591 2.32 + * @summary BootstrapMethodError when capturing constructor ref to local classes 2.33 + * @run testng MethodReferenceTestNewInnerImplicitArgs 2.34 + */ 2.35 + 2.36 +import org.testng.annotations.Test; 2.37 + 2.38 +import static org.testng.Assert.assertEquals; 2.39 + 2.40 +/** 2.41 + * Test the case that a constructor has implicit parameters added to 2.42 + * access local variables and that this constructor is used in a 2.43 + * method reference. 2.44 + * @author Robert Field 2.45 + */ 2.46 + 2.47 +@Test 2.48 +public class MethodReferenceTestNewInnerImplicitArgs { 2.49 + 2.50 + 2.51 + static class S { 2.52 + String b; 2.53 + S(String s, String s2) { b = s + s2; } 2.54 + } 2.55 + 2.56 + interface I { 2.57 + S m(); 2.58 + } 2.59 + 2.60 + interface I2 { 2.61 + S m(int i, int j); 2.62 + } 2.63 + 2.64 + public static void testConstructorReferenceImplicitParameters() { 2.65 + String title = "Hey"; 2.66 + String a2 = "!!!"; 2.67 + class MS extends S { 2.68 + MS() { 2.69 + super(title, a2); 2.70 + } 2.71 + } 2.72 + 2.73 + I result = MS::new; 2.74 + assertEquals(result.m().b, "Hey!!!"); 2.75 + 2.76 + class MS2 extends S { 2.77 + MS2(int x, int y) { 2.78 + super(title+x, a2+y); 2.79 + } 2.80 + } 2.81 + 2.82 + I2 result2 = MS2::new; 2.83 + assertEquals(result2.m(8, 4).b, "Hey8!!!4"); 2.84 + } 2.85 +}