changeset 1144:a2d6528a494d

Stricter bridge logic generation for lambda/method reference translation. Bridges are generated if: *) signature of lambda does not match signature of erased SAM descriptor declaration *) signature of member reference does not match signature of erased SAM descriptor declaration
author mcimadamore
date Tue, 06 Sep 2011 11:34:14 +0100
parents 37f50cc161c6
children a4fc85ec18eb
files src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
diffstat 1 files changed, 124 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Mon Sep 05 11:52:45 2011 +0100
+++ b/src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java	Tue Sep 06 11:34:14 2011 +0100
@@ -207,11 +207,15 @@
         }
 
         if (useIndy) {
+            
+            Symbol sym = localContext.needsBridge() ?
+                    bridgeLambda(tree, localContext) :
+                    localContext.translatedSym;
 
             //first determine the static bsm args
             List<Object> bsm_staticArgs = List.of(
                 tree.targetType.tsym,
-                new Pool.MemberReference(ClassFile.REF_invokeStatic, localContext.translatedSym));
+                new Pool.MemberReference(ClassFile.REF_invokeStatic, sym));
 
             //then, determine the arguments to the indy call
             List<JCExpression> indy_args = List.of(Bool(localContext.isRecursive()),
@@ -307,7 +311,7 @@
      * to the symbol associated with the method reference to be bridged.
      */
     Symbol bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
-        Type samDesc = types.findDescriptor(tree.targetType);
+        Type samDesc = types.erasure(types.findDescriptor(tree.targetType.tsym).type);
 
         //generate the parameter list for the bridged member reference - the
         //bridge signature will match the signature of the target sam descriptor
@@ -315,7 +319,7 @@
         ListBuffer<JCVariableDecl> params = ListBuffer.lb();
         int i = 0;
         for (Type p : samDesc.getParameterTypes()) {
-            VarSymbol vsym = new VarSymbol(0, names.fromString("x$" + i), p, localContext.translatedSym);
+            VarSymbol vsym = new VarSymbol(0, names.fromString("x$" + i), p, localContext.bridgeSym);
             params.append(make.VarDef(vsym, null));
             if (!localContext.isUnbound() || i != 0) {
                 args.append(make.Ident(vsym));
@@ -326,13 +330,13 @@
         //the member reference is an instance method reference (in which case
         //the receiver expression is passed to the bridge itself).
         if (localContext.isInstanceRef() && !localContext.isSpecial()) {
-            VarSymbol vsym = new VarSymbol(0, names.fromString("rec$" + i), tree.getQualifierExpression().type, localContext.translatedSym);
+            VarSymbol vsym = new VarSymbol(0, names.fromString("rec$" + i), tree.getQualifierExpression().type, localContext.bridgeSym);
             params.prepend(make.VarDef(vsym, null));
         }
 
         //generate the bridge method declaration
         JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers((localContext.isSpecial() ? 0 : STATIC) | SYNTHETIC),
-                localContext.translatedSym.name,
+                localContext.bridgeSym.name,
                 make.QualIdent(samDesc.getReturnType().tsym),
                 List.<JCTypeParameter>nil(),
                 params.toList(),
@@ -341,8 +345,8 @@
                     make.Types(tree.sym.type.getThrownTypes()),
                 null,
                 null);
-        bridgeDecl.sym = (MethodSymbol)localContext.translatedSym;
-        bridgeDecl.type = localContext.translatedSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
+        bridgeDecl.sym = (MethodSymbol)localContext.bridgeSym;
+        bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
             
         //bridge method body generation - this can be either a method call or a
         //new instance creation expression, depending on the member reference kind
@@ -407,6 +411,80 @@
         }
         return bridgeDecl.sym;
     }
+    
+    /**
+     * Bridges a lambda static method - this is needed whenever the signature of the
+     * erased SAM descriptor declaration does not match with the signature of the
+     * translated lambda static method. The body of the bridge will simply contain a
+     * method call to the symbol associated with the lambda static method to be bridged.
+     * Note: an alternative would have been to use erased types in the lambda static method
+     * and to add explicit cast to the non-erased types - but, in doing so, we loose
+     * immutability of the attributed lambda body AST.
+     */
+    Symbol bridgeLambda(JCLambda tree, LambdaTranslationContext localContext) {
+        Type samDesc = types.erasure(types.findDescriptor(tree.targetType.tsym).type);
+
+        //generate the parameter list for the bridged member reference - the
+        //bridge signature will match the signature of the target sam descriptor
+        ListBuffer<JCExpression> args = ListBuffer.lb();
+        ListBuffer<JCVariableDecl> params = ListBuffer.lb();
+        int i = 0;
+        
+        for (Symbol thisSym : localContext.capturedThis.values()) {
+            VarSymbol vsym = new VarSymbol(0, names.fromString("x$" + i++), thisSym.type, localContext.bridgeSym);
+            params.append(make.VarDef(vsym, null));
+            args.append(make.Ident(vsym));
+        }
+        //add captured locals
+        for (Symbol fv : localContext.capturedLocals.values()) {
+            VarSymbol vsym = new VarSymbol(0, names.fromString("x$" + i++), fv.type, localContext.bridgeSym);
+            params.append(make.at(tree.pos).VarDef(vsym, null));
+            args.append(make.Ident(vsym));
+        }
+        //add lambda parameter types
+        for (Type p : samDesc.getParameterTypes()) {
+            VarSymbol vsym = new VarSymbol(0, names.fromString("x$" + i++), p, localContext.translatedSym);
+            params.append(make.VarDef(vsym, null));
+            args.append(make.Ident(vsym));
+        }
+
+        //generate the bridge method declaration
+        JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(STATIC | SYNTHETIC),
+                localContext.bridgeSym.name,
+                make.QualIdent(samDesc.getReturnType().tsym),
+                List.<JCTypeParameter>nil(),
+                params.toList(),
+                tree.sym.type.getThrownTypes() == null ?
+                    List.<JCExpression>nil() :
+                    make.Types(tree.sym.type.getThrownTypes()),
+                null,
+                null);
+        bridgeDecl.sym = (MethodSymbol)localContext.bridgeSym;
+        bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
+            
+        //bridge method body generation - this is a method call to the bridged lambda method
+
+        //create the qualifier expression
+        JCFieldAccess select = make.Select(
+                make.Type(localContext.bridgeSym.owner.type),
+                localContext.translatedSym.name);
+        select.sym = localContext.translatedSym;
+        select.type = localContext.translatedSym.type;
+
+        //create the method call expression
+        JCMethodInvocation apply = make.Apply(List.<JCExpression>nil(), select, args.toList(), false);
+        apply.type = samDesc.getReturnType();
+        
+        //the body is either a return expression containing a method call,
+        //or the method call itself, depending on whether the return type of
+        //the bridge is non-void/void.
+        bridgeDecl.body = make.Block(0, List.<JCStatement>of((apply.type.tag != TypeTags.VOID) ?
+            make.Return(apply) :
+            make.Exec(apply)));
+        
+        trans_static = trans_static.prepend(bridgeDecl);
+        return bridgeDecl.sym;
+    }
 
     @Override
     public void visitIdent(JCIdent tree) {
@@ -700,13 +778,13 @@
      * This class is used to store important information regarding translation of
      * lambda expression/method references (see subclasses).
      */
-    static class TranslationContext<T extends JCTree> {
+    static abstract class TranslationContext<T extends JCTree> {
 
         /** the underlying (untranslated) tree */
         T tree;
-
-        /** the synthetic symbol for the static method hoisting the translated lambda/mref */
-        Symbol translatedSym;
+        
+        /** the synthetic symbol for bridging the SAM method signature */
+        Symbol bridgeSym;
 
         /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
         Symbol owner;
@@ -718,16 +796,20 @@
         TranslationContext prev;
 
         TranslationContext(T tree,
-                           Name name,
                            Symbol owner,
                            int depth,
                            TranslationContext prev) {
-            this.translatedSym = new MethodSymbol(SYNTHETIC | STATIC, name, null, owner.outermostClass());
             this.tree = tree;
             this.owner = owner;
             this.depth = depth;
             this.prev = prev;
         }
+        
+        final Symbol makeSyntheticMethod(long flags, Name name, Symbol owner) {
+            return new MethodSymbol(flags | SYNTHETIC, name, null, owner);
+        }
+        
+        abstract boolean needsBridge();
     }
 
     /**
@@ -736,7 +818,10 @@
      * and the used by the main translation routines in order to adjust references
      * to captured locals/members, etc.
      */
-    class LambdaTranslationContext extends TranslationContext<JCLambda> {
+    final class LambdaTranslationContext extends TranslationContext<JCLambda> {
+        
+        /** the synthetic symbol for the static method hoisting the translated lambda */
+        Symbol translatedSym;
         
         /** variable in the enclosing context to which this lambda is assigned */
         Symbol self;
@@ -759,8 +844,12 @@
                            Symbol self,
                            int depth,
                            TranslationContext prev) {
-            super(lambdaTree, name, owner, depth, prev);
+            super(lambdaTree, owner, depth, prev);
+            this.translatedSym = makeSyntheticMethod(STATIC, name, owner.outermostClass());
             this.self = self;
+            if (needsBridge()) {
+                this.bridgeSym = makeSyntheticMethod(STATIC, name.append(names.fromString("$bridge")), owner.outermostClass());
+            }
         }
 
         /**
@@ -807,12 +896,19 @@
         }
 
         /**
-         * Does the lambd associated with this context refers to itself?
+         * Does the lambda associated with this context refers to itself?
          */
         boolean isRecursive() {
             return self != null &&
                     capturedLocals.containsKey(self);
         }
+
+        @Override
+        boolean needsBridge() {
+            return !types.isSameType(
+                    types.erasure(types.findDescriptor(tree.targetType)),
+                    types.erasure(types.findDescriptor(tree.targetType.tsym).type));
+        }
     }
 
     /**
@@ -821,17 +917,18 @@
      * and the used by the main translation routines in order to adjust method
      * references (i.e. in case a bridge is needed)
      */
-    class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
+    final class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
 
         ReferenceTranslationContext(JCMemberReference refTree,
                            Name name,
                            Symbol owner,
                            int depth,
                            TranslationContext prev) {
-            super(refTree, name, owner, depth, prev);
-            if (isSpecial()) {
-                translatedSym.flags_field &= ~STATIC;
-                translatedSym.owner = owner.enclClass();
+            super(refTree, owner, depth, prev);
+            if (needsBridge()) {
+                this.bridgeSym = makeSyntheticMethod(isSpecial() ? 0 : STATIC,
+                        name.append(names.fromString("$bridge")),
+                        isSpecial() ? owner.enclClass() : owner.outermostClass());
             }
         }
 
@@ -839,7 +936,7 @@
          * Get the opcode associated with this method reference
          */
         int referenceKind() {
-            Symbol refSym = needsBridge() ? translatedSym : tree.sym;
+            Symbol refSym = needsBridge() ? bridgeSym : tree.sym;
             if (tree.getQualifierExpression().getTag() == JCTree.IDENT && !needsBridge() && isSpecial()) {
                 return Pool.MemberReference.specialRefKind(refSym.isConstructor());
             } else if (refSym.isConstructor()) {
@@ -855,11 +952,11 @@
          * descriptor
          */
         boolean needsBridge() {
-            Type samDesc = types.findDescriptor(tree.targetType);
-            if (isUnbound()) {
-                samDesc = types.createMethodTypeWithParameters(samDesc, samDesc.getParameterTypes().tail);
-            }
-            return !types.hasSameArgs(types.erasure(samDesc), types.erasure(tree.sym.type));
+            boolean sigMismatch = !types.isSameType(
+                    types.erasure(types.findDescriptor(tree.targetType.tsym).type),
+                    types.erasure(types.memberType(tree.targetType, tree.sym)));
+            return (isUnbound() || isSpecial() ||
+                    sigMismatch);
         }
 
         /**