changeset 1716:8e27e84de2e9

8011591: BootstrapMethodError when capturing constructor ref to local classes Reviewed-by: mcimadamore
author rfield
date Wed, 01 May 2013 08:46:04 -0700
parents 260013a710ef
children ec434cfd2752 abd153854f16
files src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java test/tools/javac/lambda/methodReferenceExecution/MethodReferenceTestNewInnerImplicitArgs.java
diffstat 2 files changed, 142 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Tue Apr 30 17:53:30 2013 -0700
+++ b/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<AttrContext> attrEnv;
 
     /** the analyzer scanner */
-    private LambdaAnalyzer analyzer;
+    private LambdaAnalyzerPreprocessor analyzer;
 
     /** map from lambda trees to translation contexts */
     private Map<JCTree, TranslationContext<?>> contextMap;
@@ -156,7 +155,7 @@
         make = TreeMaker.instance(context);
         types = Types.instance(context);
         transTypes = TransTypes.instance(context);
-        analyzer = new LambdaAnalyzer();
+        analyzer = new LambdaAnalyzerPreprocessor();
     }
     // </editor-fold>
 
@@ -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<JCExpression> args) {
+    JCNewClass makeNewClass(Type ctype, List<JCExpression> 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.<Type>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<JCExpression> args) {
+        return makeNewClass(ctype, args,
+                rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil()));
+     }
+
     private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
             DiagnosticPosition pos, List<Object> 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<Frame> frameStack;
@@ -1047,10 +1056,10 @@
         private Map<ClassSymbol, Symbol> clinits =
                 new HashMap<ClassSymbol, Symbol>();
 
-        private void analyzeClass(JCClassDecl tree) {
+        private JCClassDecl analyzeAndPreprocessClass(JCClassDecl tree) {
             frameStack = List.nil();
             localClassDefs = new HashMap<Symbol, JCClassDecl>();
-            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<Type> ptypes = ((MethodType) consSym.type).getParameterTypes();
+                Type classType = consSym.owner.type;
+
+                // Make new-class call
+                List<JCVariableDecl> 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/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");
+    }
+}