changeset 1262:b2e6e833eeaf

Fix: lambdas capturing 'super' are not translated properly
author mcimadamore
date Fri, 23 Mar 2012 18:16:43 +0000
parents b949ff379c06
children f822145ddd15
files src/share/classes/com/sun/tools/javac/comp/LambdaToInnerClass.java src/share/classes/com/sun/tools/javac/comp/LambdaTranslator.java test/tools/javac/lambda/LambdaExpr16.java
diffstat 3 files changed, 204 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaToInnerClass.java	Fri Mar 23 15:00:01 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToInnerClass.java	Fri Mar 23 18:16:43 2012 +0000
@@ -40,6 +40,7 @@
 import com.sun.tools.javac.code.Type.*;
 
 import java.util.Map;
+import java.util.HashMap;
 
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.comp.LambdaTranslator.LambdaSymbolKind.*;
@@ -158,12 +159,54 @@
         JCNewClass newClass = make.NewClass(null,
                 List.<JCExpression>nil(),
                 make.QualIdent(tree.targetType.tsym),
-                syntheticInits.toList(),
+                translate(syntheticInits.toList(), localContext.prev),
                 samClassDecl);
         newClass.constructor = samConstrDecl.sym;
         newClass.setType(localContext.lambdaClassSym.type);
         
-        result = translate(newClass, localContext.prev);
+        result = newClass;
+        
+        //add super bridges
+        addSuperBridges(localContext);
+    }
+
+    @Override
+    public void visitApply(JCMethodInvocation tree) {
+        super.visitApply(tree);
+        LambdaToInnerTranslationContext localContext = (LambdaToInnerTranslationContext)context;
+        if (localContext != null && localContext.superCallSyms.containsKey(tree)) {
+            MethodSymbol superCallSym = localContext.superCallSyms.get(tree);
+            JCIdent meth = make.Ident(superCallSym);
+            result = make.Apply(List.<JCExpression>nil(), meth, tree.args).setType(tree.type);
+        }
+    }
+    
+    /**
+     * Add super bridges required to handle captured super expressions
+     */
+    void addSuperBridges(LambdaToInnerTranslationContext context) {
+        for (Map.Entry<JCMethodInvocation, MethodSymbol> entries : context.superCallSyms.entrySet()) {
+            //generated a bridge method of the kind:
+            //   R $super$(A1 a1, A2 a2 ... An an) throws T1, T2 ... Tn {
+            //      [return] super.m(a1, a2 ... an);
+            //   }
+            JCMethodInvocation supCallTree = entries.getKey();
+            MethodSymbol supCallSym = entries.getValue();
+            
+            JCMethodDecl supCallBridge = make.MethodDef(supCallSym, null);
+            ListBuffer<JCVariableDecl> params = ListBuffer.lb();
+            int count = 0;
+            for (Type t : supCallSym.type.getParameterTypes()) {
+                VarSymbol p = makeSyntheticVar(PARAMETER, "sup$"+count++, t, supCallSym);
+                params.append(make.VarDef(p, null));
+            }
+            supCallBridge.params = params.toList();
+            JCStatement body = supCallSym.type.getReturnType().tag == TypeTags.VOID ?
+                    make.Exec(supCallTree) : make.Return(supCallTree);
+            
+            supCallBridge.body = make.Block(0, List.of(body));
+            trans_instance.append(supCallBridge);
+        }
     }
     
     @Override
@@ -272,12 +315,12 @@
         JCNewClass newClass = make.NewClass(null,
                 List.<JCExpression>nil(),
                 make.QualIdent(tree.targetType.tsym),
-                syntheticInits.toList(),
+                translate(syntheticInits.toList(), localContext.prev),
                 samClassDecl);
         newClass.constructor = samConstrDecl.sym;
         newClass.setType(localContext.referenceClassSym.type);
         
-        result = translate(newClass, localContext.prev);
+        result = newClass;
         
         //if we had a static reference with non-static qualifier, add a let
         //expression to force the evaluation of the qualifier expr
@@ -418,10 +461,17 @@
 
             /** the synthetic symbol for the class hoisting the translated lambda */
             ClassSymbol lambdaClassSym;
+            
+            /** the synthetic symbol for the class hoisting the translated lambda */
+            Map<JCMethodInvocation, MethodSymbol> superCallSyms =
+                    new HashMap<JCMethodInvocation, MethodSymbol>();
 
             LambdaToInnerTranslationContext(JCLambda tree) {
                 super(tree);
-                this.lambdaClassSym = makeFunctionalClassSym(owner.flags() & STATIC, tree.targetType, owner);
+                SuperCallAnalyzer sca = new SuperCallAnalyzer();
+                sca.scan(tree.body);
+                long flags = sca.captureSuper ? 0 : owner.flags() & STATIC;
+                this.lambdaClassSym = makeFunctionalClassSym(flags, tree.targetType, owner);
                 this.translatedSym = makeFunctionalDescriptorSym(tree.targetType, lambdaClassSym);
             }
 
@@ -442,7 +492,61 @@
 
             @Override
             protected boolean staticContext() {
-                return (owner.flags() & STATIC) != 0;
+                return (owner.flags() & STATIC) != 0 &&
+                        lambdaClassSym.isStatic();
+            }
+            
+            /**
+             * Inspect the body of a lambda expression looking for captured
+             * 'super' expressions. Such expression can be of two kinds:
+             * (i) super calls (of the kind 'super.foo()'), (ii) super-references
+             * (of the kind 'super::m').
+             */
+            class SuperCallAnalyzer extends TreeScanner {
+                
+                boolean captureSuper = true;
+                boolean nested = false;
+                
+                @Override
+                public void visitApply(JCMethodInvocation tree) {
+                    JCExpression meth = tree.meth;
+                    if (meth.getTag() == JCTree.SELECT) {
+                        JCFieldAccess select = (JCFieldAccess)meth;
+                        if (select.selected.getTag() == JCTree.IDENT &&
+                                TreeInfo.name(select.selected) == names._super) {
+                            captureSuper = true;
+                            if (!nested) {
+                                MethodSymbol superCallSym =
+                                        makeSyntheticMethod(0, "$super$" + tree.pos, tree.meth.type, instance());
+                                superCallSyms.put(tree, superCallSym);
+                            }
+                        }
+                    }
+                }
+
+                @Override
+                public void visitClassDef(JCClassDecl tree) {
+                    //do nothing
+                }
+
+                @Override
+                public void visitLambda(JCLambda tree) {
+                    boolean prevNested = nested;
+                    try {
+                        nested = true;
+                        super.visitLambda(tree);
+                    } finally {
+                        nested = prevNested;
+                    }
+                }
+
+                @Override
+                public void visitReference(JCMemberReference tree) {
+                    if (tree.getQualifierExpression().getTag() == JCTree.IDENT &&
+                            TreeInfo.name(tree.getQualifierExpression()) == names._super) {
+                        captureSuper = true;
+                    }
+                }
             }
         }
 
@@ -462,7 +566,7 @@
                 this.referenceClassSym = makeFunctionalClassSym(owner.flags() & STATIC, tree.targetType, owner);
                 this.translatedSym = makeFunctionalDescriptorSym(tree.targetType, referenceClassSym);
                 if (tree.hasKind(ReferenceKind.SUPER)) {
-                    this.bridgeSym = makeSyntheticMethod(0, "$bridge$" + tree.pos, translatedSym.type, owner.enclClass());
+                    this.bridgeSym = makeSyntheticMethod(0, "$bridge$" + tree.pos, translatedSym.type, instance());
                 }
             }   
         }
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaTranslator.java	Fri Mar 23 15:00:01 2012 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/LambdaTranslator.java	Fri Mar 23 18:16:43 2012 +0000
@@ -295,7 +295,7 @@
      * Create new synthetic method with given flags, name, type, owner
      */
     MethodSymbol makeSyntheticMethod(long flags, String name, Type type, Symbol owner) {
-        return new MethodSymbol(flags, names.fromString(name), type, owner);
+        return new MethodSymbol(flags | SYNTHETIC, names.fromString(name), type, owner);
     }
 
     /**
@@ -489,6 +489,24 @@
         }
 
         /**
+         * Return a valid instance owner given the current declaration stack
+         * (goes to innermost enclosing class declaration)
+         */
+        protected Symbol instance() {
+            List<Frame> frameStack2 = frameStack;
+            while (frameStack2.nonEmpty()) {
+                switch (frameStack2.head.tree.getTag()) {
+                    case JCTree.CLASSDEF:
+                        return ((JCClassDecl)frameStack2.head.tree).sym;
+                    default:
+                        frameStack2 = frameStack2.tail;
+                }
+            }
+            Assert.error();
+            return null;
+        }
+
+        /**
          * Return the declaration corresponding to a symbol in the enclosing
          * scope; the depth parameter is used to filter out symbols defined
          * in nested scopes (which do not need to undergo capture).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/LambdaExpr16.java	Fri Mar 23 18:16:43 2012 +0000
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012, 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
+ * @summary check that super inside lambda is handled correctly
+ * @run main LambdaExpr16
+ */
+public class LambdaExpr16 {
+    
+    static int assertionCount = 0;
+
+    static void assertTrue(boolean cond) {
+        assertionCount++;
+        if (!cond)
+            throw new AssertionError();
+    }
+
+    interface A { void m(); }
+
+    static class Sup {
+       void m() {
+          assertTrue(true);
+       }
+    }
+
+    static class Sub extends Sup {
+        void testLambda1() {
+            A a = ()->{ super.m(); };
+            a.m();
+        }
+        void testLambda2() {
+            A a = () -> { A a1 = () -> { super.m(); }; a1.m(); };
+            a.m();
+        }
+        void testRef1() {
+            A a = () -> { A a1 = super::m; a1.m(); };
+            a.m();
+        }
+        void testRef2() {
+            A a = () -> { A a1 = () -> { A a2 = super::m; a2.m(); }; a1.m(); };
+            a.m();
+        }
+    }
+
+   public static void main(String[] args) {
+      Sub s = new Sub();
+      s.testLambda1();
+      s.testLambda2();
+      s.testRef1();
+      s.testRef2();
+      assertTrue(assertionCount == 4);
+   }
+}