changeset 1246:d916b00382ed

Code generation bug fixes: *) Supertype of synthetic lambda classes is not erased *) Problems when creating inner class (with proper enclosing instance) inside a lambda expression
author mcimadamore
date Tue, 10 Jan 2012 16:16:57 +0000
parents e5326aa3d2d0
children e758b1cfe881
files src/share/classes/com/sun/tools/javac/comp/LambdaToInnerClass.java src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java src/share/classes/com/sun/tools/javac/comp/LambdaTranslator.java test/tools/javac/lambda/LambdaExpr11.java test/tools/javac/lambda/LambdaExpr12.java test/tools/javac/lambda/TargetType35.java
diffstat 6 files changed, 409 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaToInnerClass.java	Fri Dec 16 14:32:49 2011 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToInnerClass.java	Tue Jan 10 16:16:57 2012 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -133,6 +133,10 @@
                 .prependList(List.convert(JCTree.class, capturedFields.toList()))
                 .prependList(bridges(samClassDecl));
         
+        //erase supertype as it might contain non-denotable types
+        ((ClassType)samClassDecl.type).interfaces_field =
+                List.of(types.erasure(((ClassType)samClassDecl.type).interfaces_field.head));
+        
         ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
 
         //append reference to enclosing contexts
@@ -161,7 +165,7 @@
         
         result = translate(newClass, localContext.prev);
     }
-
+    
     @Override
     public void visitReference(JCMemberReference tree) {
         ReferenceToInnerTranslationContext localContext = (ReferenceToInnerTranslationContext)context;
@@ -254,6 +258,10 @@
         samClassDecl.defs = List.<JCTree>of(samConstrDecl, samMethodDecl)
                 .prependList(List.convert(JCTree.class, capturedFields.toList()))
                 .prependList(bridges(samClassDecl));
+        
+        //erase supertype as it might contain non-denotable types
+        ((ClassType)samClassDecl.type).interfaces_field =
+                List.of(types.erasure(((ClassType)samClassDecl.type).interfaces_field.head));
 
         ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
 
@@ -385,6 +393,7 @@
 
         functionalClassSym.members_field = new Scope(functionalClassSym);
         
+        //supertype to be erased later (to allow transTypes to compute right set of bridge methods)
         ((ClassType)functionalClassSym.type).supertype_field = syms.objectType;
         ((ClassType)functionalClassSym.type).interfaces_field = List.of(targetType);
 
@@ -404,18 +413,15 @@
         TranslationContext makeReferenceContext(JCMemberReference tree) {
             return new ReferenceToInnerTranslationContext(tree);
         }
-        
+
         class LambdaToInnerTranslationContext extends LambdaTranslationContext {
 
-            /** the synthetic symbol for the method hoisting the translated lambda */
-            Symbol translatedSym;
-
             /** the synthetic symbol for the class hoisting the translated lambda */
             ClassSymbol lambdaClassSym;
 
             LambdaToInnerTranslationContext(JCLambda tree) {
                 super(tree);
-                this.lambdaClassSym = makeFunctionalClassSym(STATIC, tree.targetType, owner);
+                this.lambdaClassSym = makeFunctionalClassSym(owner.flags() & STATIC, tree.targetType, owner);
                 this.translatedSym = makeFunctionalDescriptorSym(tree.targetType, lambdaClassSym);
             }
 
@@ -433,6 +439,11 @@
                     default: throw new AssertionError();
                 }
             }
+
+            @Override
+            protected boolean staticContext() {
+                return (owner.flags() & STATIC) != 0;
+            }
         }
 
         class ReferenceToInnerTranslationContext extends ReferenceTranslationContext {
@@ -448,9 +459,7 @@
 
             ReferenceToInnerTranslationContext(JCMemberReference tree) {
                 super(tree);
-                long flags = tree.hasKind(ReferenceKind.SUPER) ||
-                        tree.hasKind(ReferenceKind.IMPLICIT_INNER) ? 0 : STATIC;
-                this.referenceClassSym = makeFunctionalClassSym(flags, tree.targetType, owner);
+                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());
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Fri Dec 16 14:32:49 2011 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Tue Jan 10 16:16:57 2012 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -25,6 +25,7 @@
 
 package com.sun.tools.javac.comp;
 
+import java.util.Map;
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
 import com.sun.tools.javac.code.*;
@@ -232,6 +233,35 @@
             result = make.LetExpr(recDef, result).setType(tree.type);
         }
     }
+    
+    @Override
+    public void visitNewClass(JCNewClass tree) {
+        super.visitNewClass(tree);
+        if (((LambdaToMethodAnalyzer)analyzer).new LambdaNewClassFilter(context).accepts(tree)) {
+            LambdaToMethodTranslationContext lambdaContext = (LambdaToMethodTranslationContext)context;
+            tree.encl = make.Ident(lambdaContext.getSymbolMap(CAPTURED_THIS).get(tree.type.getEnclosingType().tsym));
+            result = tree;
+        }
+    }
+
+    @Override
+    public void visitIdent(JCIdent tree) {
+        LambdaToMethodTranslationContext lambdaContext = (LambdaToMethodTranslationContext)context;
+        if (((LambdaToMethodAnalyzer)analyzer).new LambdaSuperFilter(context).accepts(tree)) {
+            for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(LambdaSymbolKind.CAPTURED_THIS).entrySet()) {
+                if (tree.sym.owner.isMemberOf((ClassSymbol)encl_entry.getKey(), types)) {
+                    JCExpression enclRef = make.Ident(encl_entry.getValue());
+                    JCFieldAccess sel = make.Select(enclRef, tree.name);
+                    sel.setType(tree.type);
+                    sel.sym = tree.sym;
+                    result = sel;
+                    return;
+                }
+            }
+        }
+        super.visitIdent(tree);
+    }
+
     // </editor-fold>
 
     // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
@@ -504,11 +534,92 @@
             return new ReferenceToMethodTranslationContext(tree);
         }
         
+        @Override
+        public void visitIdent(JCIdent tree) {
+            if (new LambdaSuperFilter(context()).accepts(tree)) {
+                TranslationContext<?> localContext = context();
+                while (localContext != null) {
+                    if (localContext.tree.getTag() == JCTree.LAMBDA) {
+                        JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym.owner);
+                        if (clazz == null) break;
+                        ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
+                    }
+                    localContext = localContext.prev;
+                }
+            } else {
+                super.visitIdent(tree);
+            }
+        }
+
+        @Override
+        public void visitNewClass(JCNewClass tree) {
+            if (new LambdaNewClassFilter(context()).accepts(tree)) {
+                ((LambdaTranslationContext)context()).addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
+            }
+            super.visitNewClass(tree);
+        }
+        
+        abstract class LambdaToMethodFilter<T extends JCTree> implements Filter<T> {
+            
+            TranslationContext<?> context;
+
+            LambdaToMethodFilter(TranslationContext<?> context) {
+                this.context = context;
+            }
+            
+            protected boolean hasEnclosingInstance(Type ctype) {
+                Type encl = ctype.getEnclosingType();
+                Type current = context.owner.enclClass().type;
+                while (current.tag != TypeTags.NONE) {
+                    if (current.tsym.isSubClass(encl.tsym, types)) {
+                        return true;
+                    }
+                    current = current.getEnclosingType();
+                }
+                return false;
+            }
+        }
+        
+        /**
+         *  This is used to filter out those new class expressions that needs
+         *  to be qualified with an enclosing tree
+         */
+        protected final class LambdaNewClassFilter extends LambdaToMethodFilter<JCNewClass> {
+            
+            LambdaNewClassFilter(TranslationContext<?> context) {
+                super(context);
+            }
+            
+            public boolean accepts(JCNewClass tree) {
+                return tree.encl == null &&
+                        tree.def == null &&
+                        tree.type.getEnclosingType().tag != TypeTags.NONE &&
+                        context != null &&
+                        hasEnclosingInstance(tree.type);
+            }
+        };
+        
+        /**
+         *  This is used to filter out those super expressions that needs
+         *  to be qualified with an enclosing tree
+         */
+        protected final class LambdaSuperFilter extends LambdaToMethodFilter<JCIdent> {
+
+            LambdaSuperFilter(TranslationContext<?> context) {
+                super(context);
+            }
+
+            public boolean accepts(JCIdent tree) {
+                return context != null &&
+                        tree.name == names._super &&
+                        tree.sym.owner.kind == Kinds.TYP &&
+                        tree.sym.owner.type.getEnclosingType().tag != TypeTags.NONE &&
+                        hasEnclosingInstance(tree.sym.owner.type);
+            }
+        };
+        
         class LambdaToMethodTranslationContext extends LambdaTranslationContext {
 
-            /** the synthetic symbol for the method hoisting the translated lambda */
-            Symbol translatedSym;
-
             /** the synthetic symbol for the method hoisting the bridged lambda */
             Symbol bridgeSym;
 
@@ -529,6 +640,11 @@
             protected Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) {
                 return makeSyntheticVar(0, name, types.erasure(sym.type), translatedSym);
             }
+
+            @Override
+            protected boolean staticContext() {
+                return true;
+            }
             
             protected Type generatedLambdaSig() {
                 return types.erasure(types.findDescriptor(tree.targetType));
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaTranslator.java	Fri Dec 16 14:32:49 2011 +0000
+++ b/src/share/classes/com/sun/tools/javac/comp/LambdaTranslator.java	Tue Jan 10 16:16:57 2012 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -429,7 +429,21 @@
             try {
                 if (frameStack.nonEmpty() && frameStack.head.tree.getTag() == JCTree.LAMBDA) {
                     tree.sym.owner = owner();
-                    ((ClassType)tree.sym.type).setEnclosingType(Type.noType);
+                    LambdaTranslationContext lambdaContext = (LambdaTranslationContext)contextMap.get(frameStack.head.tree);
+                    if (lambdaContext.staticContext()) {
+                        //if the translated lambda body occurs in a static context,
+                        //any class declaration within it must be made static
+                        tree.sym.flags_field |= STATIC;
+                        ((ClassType)tree.sym.type).setEnclosingType(Type.noType);
+                    } else {
+                        //if the translated lambda body is in an instance context
+                        //the enclosing type of any class declaration within it
+                        //must be updated to point to the new enclosing type (if any)
+                        Type encl = tree.sym.type.getEnclosingType() != Type.noType ?
+                                tree.sym.owner.enclClass().type :
+                                Type.noType;
+                        ((ClassType)tree.sym.type).setEnclosingType(encl);
+                    }
                 }
                 frameStack = frameStack.prepend(new Frame(tree));
                 super.visitClassDef(tree);
@@ -443,13 +457,24 @@
          * Return a valid owner given the current declaration stack
          * (required to skip synthetic lambda symbols)
          */
-        Symbol owner() {
-            for (Frame block : frameStack) {
-                switch (block.tree.getTag()) {
+        protected Symbol owner() {
+            List<Frame> frameStack2 = frameStack;
+            while (frameStack2.nonEmpty()) {
+                switch (frameStack2.head.tree.getTag()) {
+                    case JCTree.VARDEF:
+                        JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
+                        return makeSyntheticMethod(((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC, names.empty, null, cdecl.sym);
+                    case JCTree.BLOCK:
+                        JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
+                        return makeSyntheticMethod(((JCBlock)frameStack2.head.tree).flags & STATIC | BLOCK, names.empty, null, cdecl2.sym);
                     case JCTree.CLASSDEF:
-                        return ((JCClassDecl)block.tree).sym;
+                        return ((JCClassDecl)frameStack2.head.tree).sym;
                     case JCTree.METHODDEF:
-                        return ((JCMethodDecl)block.tree).sym;
+                        return ((JCMethodDecl)frameStack2.head.tree).sym;
+                    case JCTree.LAMBDA:
+                        return ((LambdaTranslationContext)contextMap.get(frameStack2.head.tree)).translatedSym;
+                    default:
+                        frameStack2 = frameStack2.tail;
                 }
             }
             Assert.error();
@@ -471,6 +496,11 @@
                             return currentDepth > depth ? null : block.tree;
                         }
                         break;
+                    case JCTree.VARDEF:
+                        if (((JCVariableDecl)block.tree).sym == sym) {
+                            return currentDepth > depth ? null : block.tree;
+                        }
+                        break;
                     case JCTree.BLOCK:
                     case JCTree.METHODDEF:
                     case JCTree.LAMBDA:
@@ -600,6 +630,7 @@
                 ((LambdaTranslationContext)context()).addSymbol(tree.sym, LOCAL_VAR);
             }
             Symbol prevSelf = self;
+            List<Frame> prevStack = frameStack;
             try {
                 if (tree.init != null &&
                         tree.init.getTag() == JCTree.LAMBDA) {
@@ -607,11 +638,14 @@
                 }
                 if (tree.sym.owner.kind == MTH) {
                     frameStack.head.addLocal(tree.sym);
+                } else {
+                    frameStack = frameStack.prepend(new Frame(tree));
                 }
                 super.visitVarDef(tree);
             }
             finally {
                 self = prevSelf;
+                frameStack = prevStack;
             }
         }
         
@@ -675,13 +709,25 @@
 
             /** map from class symbols to translated synthetic parameters (for captured member access) */
             private Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
+            
+            /** the synthetic symbol for the method hoisting the translated lambda */
+            Symbol translatedSym;
 
             protected LambdaTranslationContext(JCLambda tree) {
                 super(tree);
                 this.self = LambdaAnalyzer.this.self;
             }
 
+            /**
+             * Translate a symbol of a given kind into something suitable for the
+             * synthetic lambda body
+             */
             protected abstract Symbol translate(String name, Symbol sym, LambdaSymbolKind skind);
+            
+            /**
+             * Does lambda body have access to enclosing instance scope?
+             */
+            protected abstract boolean staticContext();
 
             /**
              * Does the lambda associated with this context refers to itself?
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/LambdaExpr11.java	Tue Jan 10 16:16:57 2012 +0000
@@ -0,0 +1,87 @@
+/*
+ * 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 creating an inner class from a lambda does add a captured this
+ * @run main LambdaExpr11
+ */
+public class LambdaExpr11 {
+    
+    static int assertionCount = 0;
+
+    static void assertTrue(boolean cond) {
+        assertionCount++;
+        if (!cond)
+            throw new AssertionError();
+    }
+    
+    class Inner {
+        Inner() { assertTrue(true); }
+    }
+
+    void test() {
+        Runnable r1 = ()-> { new Inner(); };
+        r1.run();
+        Runnable r2 = ()-> { new Inner() {}; };
+        r2.run();
+        Runnable r3 = ()-> { class SubInner extends Inner {}; new SubInner(); };
+        r3.run();
+        Runnable r4 = ()-> { class SubInner extends Inner {}; new SubInner() {}; };
+        r4.run();
+        new Inner2().test();
+    }
+    
+    class Inner2 {
+        void test() {
+            Runnable r1 = ()-> { new Inner(); };
+            r1.run();
+            Runnable r2 = ()-> { new Inner() {}; };
+            r2.run();
+            Runnable r3 = ()-> { class SubInner extends Inner {}; new SubInner(); };
+            r3.run();
+            Runnable r4 = ()-> { class SubInner extends Inner {}; new SubInner() {}; };
+            r4.run();
+            new Inner3().test();
+        }
+        
+        class Inner3 {
+            void test() {
+                Runnable r1 = ()-> { new Inner(); };
+                r1.run();
+                Runnable r2 = ()-> { new Inner() {}; };
+                r2.run();
+                Runnable r3 = ()-> { class SubInner extends Inner {}; new SubInner(); };
+                r3.run();
+                Runnable r4 = ()-> { class SubInner extends Inner {}; new SubInner() {}; };
+                r4.run();
+            }
+        }
+    }
+    
+    public static void main(String[] args) {
+        new LambdaExpr11().test();
+        assertTrue(assertionCount == 12);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/LambdaExpr12.java	Tue Jan 10 16:16:57 2012 +0000
@@ -0,0 +1,64 @@
+/*
+ * 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 creating an inner class from a lambda does add a captured this
+ * @run main LambdaExpr12
+ */
+
+public class LambdaExpr12 {
+    
+    static int assertionCount = 0;
+
+    static void assertTrue(boolean cond) {
+        assertionCount++;
+        if (!cond)
+            throw new AssertionError();
+    }
+    
+    interface Getter<X> {
+        X get();
+    }
+    
+
+    interface Mapper<X,Y> {
+        Y map(X x);
+    }
+
+    void test() {
+        Mapper<String, Getter<Character>> mapper =
+                s -> new Getter<Character>() {
+                     @Override
+                     public Character get() {
+                         return s.charAt(0);
+                     }
+                };
+        assertTrue(mapper.map("First").get() == 'F');
+    }
+    
+    public static void main(String[] args) {
+        new LambdaExpr12().test();
+        assertTrue(assertionCount == 1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/javac/lambda/TargetType35.java	Tue Jan 10 16:16:57 2012 +0000
@@ -0,0 +1,66 @@
+/*
+ * 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 missing erasure on intersection supertype of generated lambda class
+ */
+public class TargetType35 {
+    
+    static int assertionCount = 0;
+
+    static void assertTrue(boolean cond) {
+        assertionCount++;
+        if (!cond)
+            throw new AssertionError();
+    }
+
+    interface A {}
+
+    interface B {}
+
+    static class C implements A, B {}
+
+    static class D implements A, B {}
+
+    interface SAM<Y, X> {
+        Y invoke(X arg);
+    }
+    
+    <Z> Z m(Z z) { return z; }
+    
+    void test(C c, D d) {
+        choose(c, d, x->x);
+        choose(c, d, this::m);
+    }
+
+    <T> void choose(T t1, T t2, SAM<T, T> t3) {
+        assertTrue(true);
+    }
+    
+    public static void main(String[] args)
+    {
+        new TargetType35().test(null, null);
+        assertTrue(assertionCount == 2);
+    }
+}