src/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java

Thu, 21 Feb 2013 17:49:56 -0800

author
lana
date
Thu, 21 Feb 2013 17:49:56 -0800
changeset 1603
6118072811e5
parent 1597
f4fdd53f8b3e
child 1612
69cd2bfd4a31
permissions
-rw-r--r--

Merge

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 package com.sun.tools.javac.comp;
    27 import com.sun.tools.javac.tree.*;
    28 import com.sun.tools.javac.tree.JCTree;
    29 import com.sun.tools.javac.tree.JCTree.*;
    30 import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
    31 import com.sun.tools.javac.tree.TreeMaker;
    32 import com.sun.tools.javac.tree.TreeScanner;
    33 import com.sun.tools.javac.tree.TreeTranslator;
    34 import com.sun.tools.javac.code.Kinds;
    35 import com.sun.tools.javac.code.Scope;
    36 import com.sun.tools.javac.code.Symbol;
    37 import com.sun.tools.javac.code.Symbol.ClassSymbol;
    38 import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
    39 import com.sun.tools.javac.code.Symbol.MethodSymbol;
    40 import com.sun.tools.javac.code.Symbol.VarSymbol;
    41 import com.sun.tools.javac.code.Symtab;
    42 import com.sun.tools.javac.code.Type;
    43 import com.sun.tools.javac.code.Type.ClassType;
    44 import com.sun.tools.javac.code.Type.MethodType;
    45 import com.sun.tools.javac.code.Types;
    46 import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*;
    47 import com.sun.tools.javac.jvm.*;
    48 import com.sun.tools.javac.util.*;
    49 import com.sun.tools.javac.util.List;
    50 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
    51 import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
    53 import java.util.HashMap;
    54 import java.util.LinkedHashMap;
    55 import java.util.Map;
    57 import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
    58 import static com.sun.tools.javac.code.Flags.*;
    59 import static com.sun.tools.javac.code.Kinds.*;
    60 import static com.sun.tools.javac.code.TypeTag.*;
    61 import static com.sun.tools.javac.tree.JCTree.Tag.*;
    63 /**
    64  * This pass desugars lambda expressions into static methods
    65  *
    66  *  <p><b>This is NOT part of any supported API.
    67  *  If you write code that depends on this, you do so at your own risk.
    68  *  This code and its internal interfaces are subject to change or
    69  *  deletion without notice.</b>
    70  */
    71 public class LambdaToMethod extends TreeTranslator {
    73     private Names names;
    74     private Symtab syms;
    75     private Resolve rs;
    76     private TreeMaker make;
    77     private Types types;
    78     private TransTypes transTypes;
    79     private Env<AttrContext> attrEnv;
    81     /** the analyzer scanner */
    82     private LambdaAnalyzer analyzer;
    84     /** map from lambda trees to translation contexts */
    85     private Map<JCTree, TranslationContext<?>> contextMap;
    87     /** current translation context (visitor argument) */
    88     private TranslationContext<?> context;
    90     /** info about the current class being processed */
    91     private KlassInfo kInfo;
    93     /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
    94     public static final int FLAG_SERIALIZABLE = 1 << 0;
    96     /** Flag for alternate metafactories indicating the lambda object has multiple targets */
    97     public static final int FLAG_MARKERS = 1 << 1;
    99     private class KlassInfo {
   101         /**
   102          * list of methods to append
   103          */
   104         private ListBuffer<JCTree> appendedMethodList;
   106         /**
   107          * list of deserialization cases
   108          */
   109         private final Map<String, ListBuffer<JCStatement>> deserializeCases;
   111        /**
   112          * deserialize method symbol
   113          */
   114         private final MethodSymbol deserMethodSym;
   116         /**
   117          * deserialize method parameter symbol
   118          */
   119         private final VarSymbol deserParamSym;
   121         private KlassInfo(Symbol kSym) {
   122             appendedMethodList = ListBuffer.lb();
   123             deserializeCases = new HashMap<String, ListBuffer<JCStatement>>();
   124             long flags = PRIVATE | STATIC | SYNTHETIC;
   125             MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
   126                     List.<Type>nil(), syms.methodClass);
   127             deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym);
   128             deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
   129                     syms.serializedLambdaType, deserMethodSym);
   130         }
   132         private void addMethod(JCTree decl) {
   133             appendedMethodList = appendedMethodList.prepend(decl);
   134         }
   135     }
   137     // <editor-fold defaultstate="collapsed" desc="Instantiating">
   138     private static final Context.Key<LambdaToMethod> unlambdaKey =
   139             new Context.Key<LambdaToMethod>();
   141     public static LambdaToMethod instance(Context context) {
   142         LambdaToMethod instance = context.get(unlambdaKey);
   143         if (instance == null) {
   144             instance = new LambdaToMethod(context);
   145         }
   146         return instance;
   147     }
   149     private LambdaToMethod(Context context) {
   150         names = Names.instance(context);
   151         syms = Symtab.instance(context);
   152         rs = Resolve.instance(context);
   153         make = TreeMaker.instance(context);
   154         types = Types.instance(context);
   155         transTypes = TransTypes.instance(context);
   156         analyzer = new LambdaAnalyzer();
   157     }
   158     // </editor-fold>
   160     // <editor-fold defaultstate="collapsed" desc="translate methods">
   161     @Override
   162     public <T extends JCTree> T translate(T tree) {
   163         TranslationContext<?> newContext = contextMap.get(tree);
   164         return translate(tree, newContext != null ? newContext : context);
   165     }
   167     public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
   168         TranslationContext<?> prevContext = context;
   169         try {
   170             context = newContext;
   171             return super.translate(tree);
   172         }
   173         finally {
   174             context = prevContext;
   175         }
   176     }
   178     public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
   179         ListBuffer<T> buf = ListBuffer.lb();
   180         for (T tree : trees) {
   181             buf.append(translate(tree, newContext));
   182         }
   183         return buf.toList();
   184     }
   186     public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
   187         this.make = make;
   188         this.attrEnv = env;
   189         this.context = null;
   190         this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
   191         return translate(cdef);
   192     }
   193     // </editor-fold>
   195     // <editor-fold defaultstate="collapsed" desc="visitor methods">
   196     /**
   197      * Visit a class.
   198      * Maintain the translatedMethodList across nested classes.
   199      * Append the translatedMethodList to the class after it is translated.
   200      * @param tree
   201      */
   202     @Override
   203     public void visitClassDef(JCClassDecl tree) {
   204         if (tree.sym.owner.kind == PCK) {
   205             //analyze class
   206             analyzer.analyzeClass(tree);
   207         }
   208         KlassInfo prevKlassInfo = kInfo;
   209         try {
   210             kInfo = new KlassInfo(tree.sym);
   211             super.visitClassDef(tree);
   212             if (!kInfo.deserializeCases.isEmpty()) {
   213                 kInfo.addMethod(makeDeserializeMethod(tree.sym));
   214             }
   215             //add all translated instance methods here
   216             List<JCTree> newMethods = kInfo.appendedMethodList.toList();
   217             tree.defs = tree.defs.appendList(newMethods);
   218             for (JCTree lambda : newMethods) {
   219                 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
   220             }
   221             result = tree;
   222         } finally {
   223             kInfo = prevKlassInfo;
   224         }
   225     }
   227     /**
   228      * Translate a lambda into a method to be inserted into the class.
   229      * Then replace the lambda site with an invokedynamic call of to lambda
   230      * meta-factory, which will use the lambda method.
   231      * @param tree
   232      */
   233     @Override
   234     public void visitLambda(JCLambda tree) {
   235         LambdaTranslationContext localContext = (LambdaTranslationContext)context;
   236         MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
   237         MethodType lambdaType = (MethodType) sym.type;
   239         //create the method declaration hoisting the lambda body
   240         JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
   241                 sym.name,
   242                 make.QualIdent(lambdaType.getReturnType().tsym),
   243                 List.<JCTypeParameter>nil(),
   244                 localContext.syntheticParams,
   245                 lambdaType.getThrownTypes() == null ?
   246                     List.<JCExpression>nil() :
   247                     make.Types(lambdaType.getThrownTypes()),
   248                 null,
   249                 null);
   250         lambdaDecl.sym = sym;
   251         lambdaDecl.type = lambdaType;
   253         //translate lambda body
   254         //As the lambda body is translated, all references to lambda locals,
   255         //captured variables, enclosing members are adjusted accordingly
   256         //to refer to the static method parameters (rather than i.e. acessing to
   257         //captured members directly).
   258         lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
   260         //Add the method to the list of methods to be added to this class.
   261         kInfo.addMethod(lambdaDecl);
   263         //now that we have generated a method for the lambda expression,
   264         //we can translate the lambda into a method reference pointing to the newly
   265         //created method.
   266         //
   267         //Note that we need to adjust the method handle so that it will match the
   268         //signature of the SAM descriptor - this means that the method reference
   269         //should be added the following synthetic arguments:
   270         //
   271         // * the "this" argument if it is an instance method
   272         // * enclosing locals captured by the lambda expression
   274         ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
   276         if (!sym.isStatic()) {
   277             syntheticInits.append(makeThis(
   278                     sym.owner.enclClass().asType(),
   279                     localContext.owner.enclClass()));
   280         }
   282         //add captured locals
   283         for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
   284             if (fv != localContext.self) {
   285                 JCTree captured_local = make.Ident(fv).setType(fv.type);
   286                 syntheticInits.append((JCExpression) captured_local);
   287             }
   288         }
   290         //then, determine the arguments to the indy call
   291         List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
   293         //build a sam instance using an indy call to the meta-factory
   294         int refKind = referenceKind(sym);
   296         //convert to an invokedynamic call
   297         result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args);
   298     }
   300     private JCIdent makeThis(Type type, Symbol owner) {
   301         VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
   302                 names._this,
   303                 type,
   304                 owner);
   305         return make.Ident(_this);
   306     }
   308     /**
   309      * Translate a method reference into an invokedynamic call to the
   310      * meta-factory.
   311      * @param tree
   312      */
   313     @Override
   314     public void visitReference(JCMemberReference tree) {
   315         ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
   317         //first determine the method symbol to be used to generate the sam instance
   318         //this is either the method reference symbol, or the bridged reference symbol
   319         Symbol refSym = localContext.needsBridge() ?
   320             localContext.bridgeSym :
   321             tree.sym;
   323         //build the bridge method, if needed
   324         if (localContext.needsBridge()) {
   325             bridgeMemberReference(tree, localContext);
   326         }
   328         //the qualifying expression is treated as a special captured arg
   329         JCExpression init;
   330         switch(tree.kind) {
   332             case IMPLICIT_INNER:    /** Inner :: new */
   333             case SUPER:             /** super :: instMethod */
   334                 init = makeThis(
   335                     localContext.owner.enclClass().asType(),
   336                     localContext.owner.enclClass());
   337                 break;
   339             case BOUND:             /** Expr :: instMethod */
   340                 init = tree.getQualifierExpression();
   341                 break;
   343             case UNBOUND:           /** Type :: instMethod */
   344             case STATIC:            /** Type :: staticMethod */
   345             case TOPLEVEL:          /** Top level :: new */
   346             case ARRAY_CTOR:        /** ArrayType :: new */
   347                 init = null;
   348                 break;
   350             default:
   351                 throw new InternalError("Should not have an invalid kind");
   352         }
   354         List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
   357         //build a sam instance using an indy call to the meta-factory
   358         result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args);
   359     }
   361     /**
   362      * Translate identifiers within a lambda to the mapped identifier
   363      * @param tree
   364      */
   365     @Override
   366     public void visitIdent(JCIdent tree) {
   367         if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
   368             super.visitIdent(tree);
   369         } else {
   370             LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
   371             if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) {
   372                 Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym);
   373                 result = make.Ident(translatedSym).setType(tree.type);
   374             } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
   375                 Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
   376                 result = make.Ident(translatedSym).setType(tree.type);
   377             } else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
   378                 Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
   379                 result = make.Ident(translatedSym).setType(translatedSym.type);
   380             } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) {
   381                 Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym);
   382                 result = make.Ident(translatedSym).setType(tree.type);
   383             } else {
   384                 if (tree.sym.owner.kind == Kinds.TYP) {
   385                     for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) {
   386                         if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) {
   387                             JCExpression enclRef = make.Ident(encl_entry.getValue());
   388                             result = tree.sym.name == names._this
   389                                     ? enclRef.setType(tree.type)
   390                                     : make.Select(enclRef, tree.sym).setType(tree.type);
   391                             result = tree;
   392                             return;
   393                         }
   394                     }
   395                 }
   396                 //access to untranslated symbols (i.e. compile-time constants,
   397                 //members defined inside the lambda body, etc.) )
   398                 super.visitIdent(tree);
   399             }
   400         }
   401     }
   403     @Override
   404     public void visitVarDef(JCVariableDecl tree) {
   405         LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
   406         if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
   407             JCExpression init = translate(tree.init);
   408             result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
   409         } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
   410             JCExpression init = translate(tree.init);
   411             VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
   412             result = make.VarDef(xsym, init);
   413             // Replace the entered symbol for this variable
   414             Scope sc = tree.sym.owner.members();
   415             if (sc != null) {
   416                 sc.remove(tree.sym);
   417                 sc.enter(xsym);
   418             }
   419         } else {
   420             super.visitVarDef(tree);
   421         }
   422     }
   424     // </editor-fold>
   426     // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
   428     private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
   429         return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
   430                 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
   431                 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
   432     }
   434     private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
   435         Type restype = lambdaMethodDecl.type.getReturnType();
   436         boolean isLambda_void = expr.type.hasTag(VOID);
   437         boolean isTarget_void = restype.hasTag(VOID);
   438         boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
   439         if (isTarget_void) {
   440             //target is void:
   441             // BODY;
   442             JCStatement stat = make.Exec(expr);
   443             return make.Block(0, List.<JCStatement>of(stat));
   444         } else if (isLambda_void && isTarget_Void) {
   445             //void to Void conversion:
   446             // BODY; return null;
   447             ListBuffer<JCStatement> stats = ListBuffer.lb();
   448             stats.append(make.Exec(expr));
   449             stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
   450             return make.Block(0, stats.toList());
   451         } else {
   452             //non-void to non-void conversion:
   453             // return (TYPE)BODY;
   454             JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
   455             return make.Block(0, List.<JCStatement>of(make.Return(retExpr)));
   456         }
   457     }
   459     private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
   460         final Type restype = lambdaMethodDecl.type.getReturnType();
   461         final boolean isTarget_void = restype.hasTag(VOID);
   462         boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
   464         class LambdaBodyTranslator extends TreeTranslator {
   466             @Override
   467             public void visitClassDef(JCClassDecl tree) {
   468                 //do NOT recurse on any inner classes
   469                 result = tree;
   470             }
   472             @Override
   473             public void visitLambda(JCLambda tree) {
   474                 //do NOT recurse on any nested lambdas
   475                 result = tree;
   476             }
   478             @Override
   479             public void visitReturn(JCReturn tree) {
   480                 boolean isLambda_void = tree.expr == null;
   481                 if (isTarget_void && !isLambda_void) {
   482                     //Void to void conversion:
   483                     // { TYPE $loc = RET-EXPR; return; }
   484                     VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
   485                     JCVariableDecl varDef = make.VarDef(loc, tree.expr);
   486                     result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
   487                 } else if (!isTarget_void || !isLambda_void) {
   488                     //non-void to non-void conversion:
   489                     // return (TYPE)RET-EXPR;
   490                     tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
   491                     result = tree;
   492                 } else {
   493                     result = tree;
   494                 }
   496             }
   497         }
   499         JCBlock trans_block = new LambdaBodyTranslator().translate(block);
   500         if (completeNormally && isTarget_Void) {
   501             //there's no return statement and the lambda (possibly inferred)
   502             //return type is java.lang.Void; emit a synthetic return statement
   503             trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
   504         }
   505         return trans_block;
   506     }
   508     private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
   509         ListBuffer<JCCase> cases = ListBuffer.lb();
   510         ListBuffer<JCBreak> breaks = ListBuffer.lb();
   511         for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
   512             JCBreak br = make.Break(null);
   513             breaks.add(br);
   514             List<JCStatement> stmts = entry.getValue().append(br).toList();
   515             cases.add(make.Case(make.Literal(entry.getKey()), stmts));
   516         }
   517         JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
   518         for (JCBreak br : breaks) {
   519             br.target = sw;
   520         }
   521         JCBlock body = make.Block(0L, List.<JCStatement>of(
   522                 sw,
   523                 make.Throw(makeNewClass(
   524                     syms.illegalArgumentExceptionType,
   525                     List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
   526         JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
   527                         names.deserializeLambda,
   528                         make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
   529                         List.<JCTypeParameter>nil(),
   530                         List.of(make.VarDef(kInfo.deserParamSym, null)),
   531                         List.<JCExpression>nil(),
   532                         body,
   533                         null);
   534         deser.sym = kInfo.deserMethodSym;
   535         deser.type = kInfo.deserMethodSym.type;
   536         //System.err.printf("DESER: '%s'\n", deser);
   537         return deser;
   538     }
   540     /** Make an attributed class instance creation expression.
   541      *  @param ctype    The class type.
   542      *  @param args     The constructor arguments.
   543      */
   544     JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
   545         JCNewClass tree = make.NewClass(null,
   546             null, make.QualIdent(ctype.tsym), args, null);
   547         tree.constructor = rs.resolveConstructor(
   548             null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
   549         tree.type = ctype;
   550         return tree;
   551     }
   553     private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
   554             DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
   555         String functionalInterfaceClass = classSig(targetType);
   556         String functionalInterfaceMethodName = samSym.getSimpleName().toString();
   557         String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
   558         String implClass = classSig(refSym.owner.type);
   559         String implMethodName = refSym.getQualifiedName().toString();
   560         String implMethodSignature = methodSig(types.erasure(refSym.type));
   562         JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
   563         ListBuffer<JCExpression> serArgs = ListBuffer.lb();
   564         int i = 0;
   565         for (Type t : indyType.getParameterTypes()) {
   566             List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
   567             List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
   568             serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
   569             ++i;
   570         }
   571         JCStatement stmt = make.If(
   572                 deserTest(deserTest(deserTest(deserTest(deserTest(
   573                     kindTest,
   574                     "getFunctionalInterfaceClass", functionalInterfaceClass),
   575                     "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
   576                     "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
   577                     "getImplClass", implClass),
   578                     "getImplMethodSignature", implMethodSignature),
   579                 make.Return(makeIndyCall(
   580                     pos,
   581                     syms.lambdaMetafactory,
   582                     names.altMetaFactory,
   583                     staticArgs, indyType, serArgs.toList())),
   584                 null);
   585         ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
   586         if (stmts == null) {
   587             stmts = ListBuffer.lb();
   588             kInfo.deserializeCases.put(implMethodName, stmts);
   589         }
   590         /****
   591         System.err.printf("+++++++++++++++++\n");
   592         System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
   593         System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
   594         System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
   595         System.err.printf("*implMethodKind: %d\n", implMethodKind);
   596         System.err.printf("*implClass: '%s'\n", implClass);
   597         System.err.printf("*implMethodName: '%s'\n", implMethodName);
   598         System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
   599         ****/
   600         stmts.append(stmt);
   601     }
   603     private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
   604         JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
   605         testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
   606         testExpr.setType(syms.booleanType);
   607         return testExpr;
   608     }
   610     private JCExpression deserTest(JCExpression prev, String func, String lit) {
   611         MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
   612         Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
   613         JCMethodInvocation eqtest = make.Apply(
   614                 List.<JCExpression>nil(),
   615                 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
   616                 List.<JCExpression>of(make.Literal(lit)));
   617         eqtest.setType(syms.booleanType);
   618         JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
   619         compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
   620         compound.setType(syms.booleanType);
   621         return compound;
   622     }
   624     private JCExpression deserGetter(String func, Type type) {
   625         return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
   626     }
   628     private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
   629         MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
   630         Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
   631         return make.Apply(
   632                     List.<JCExpression>nil(),
   633                     make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
   634                     args).setType(type);
   635     }
   637     /**
   638      * Create new synthetic method with given flags, name, type, owner
   639      */
   640     private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
   641         return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
   642     }
   644     /**
   645      * Create new synthetic variable with given flags, name, type, owner
   646      */
   647     private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
   648         return makeSyntheticVar(flags, names.fromString(name), type, owner);
   649     }
   651     /**
   652      * Create new synthetic variable with given flags, name, type, owner
   653      */
   654     private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
   655         return new VarSymbol(flags | SYNTHETIC, name, type, owner);
   656     }
   658     /**
   659      * Set varargsElement field on a given tree (must be either a new class tree
   660      * or a method call tree)
   661      */
   662     private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
   663         if (varargsElement != null) {
   664             switch (tree.getTag()) {
   665                 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
   666                 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
   667                 default: throw new AssertionError();
   668             }
   669         }
   670     }
   672     /**
   673      * Convert method/constructor arguments by inserting appropriate cast
   674      * as required by type-erasure - this is needed when bridging a lambda/method
   675      * reference, as the bridged signature might require downcast to be compatible
   676      * with the generated signature.
   677      */
   678     private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
   679        Assert.check(meth.kind == Kinds.MTH);
   680        List<Type> formals = types.erasure(meth.type).getParameterTypes();
   681        if (varargsElement != null) {
   682            Assert.check((meth.flags() & VARARGS) != 0);
   683        }
   684        return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
   685     }
   687     // </editor-fold>
   689     /**
   690      * Generate an adapter method "bridge" for a method reference which cannot
   691      * be used directly.
   692      */
   693     private class MemberReferenceBridger {
   695         private final JCMemberReference tree;
   696         private final ReferenceTranslationContext localContext;
   697         private final ListBuffer<JCExpression> args = ListBuffer.lb();
   698         private final ListBuffer<JCVariableDecl> params = ListBuffer.lb();
   700         MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
   701             this.tree = tree;
   702             this.localContext = localContext;
   703         }
   705         /**
   706          * Generate the bridge
   707          */
   708         JCMethodDecl bridge() {
   709             int prevPos = make.pos;
   710             try {
   711                 make.at(tree);
   712                 Type samDesc = localContext.bridgedRefSig();
   713                 List<Type> samPTypes = samDesc.getParameterTypes();
   715                 //an extra argument is prepended to the signature of the bridge in case
   716                 //the member reference is an instance method reference (in which case
   717                 //the receiver expression is passed to the bridge itself).
   718                 Type recType = null;
   719                 switch (tree.kind) {
   720                     case IMPLICIT_INNER:
   721                         recType = tree.sym.owner.type.getEnclosingType();
   722                         break;
   723                     case BOUND:
   724                         recType = tree.getQualifierExpression().type;
   725                         break;
   726                     case UNBOUND:
   727                         recType = samPTypes.head;
   728                         samPTypes = samPTypes.tail;
   729                         break;
   730                 }
   732                 //generate the parameter list for the bridged member reference - the
   733                 //bridge signature will match the signature of the target sam descriptor
   735                 VarSymbol rcvr = (recType == null)
   736                         ? null
   737                         : addParameter("rec$", recType, false);
   739                 List<Type> refPTypes = tree.sym.type.getParameterTypes();
   740                 int refSize = refPTypes.size();
   741                 int samSize = samPTypes.size();
   742                 // Last parameter to copy from referenced method
   743                 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
   745                 List<Type> l = refPTypes;
   746                 // Use parameter types of the referenced method, excluding final var args
   747                 for (int i = 0; l.nonEmpty() && i < last; ++i) {
   748                     addParameter("x$" + i, l.head, true);
   749                     l = l.tail;
   750                 }
   751                 // Flatten out the var args
   752                 for (int i = last; i < samSize; ++i) {
   753                     addParameter("xva$" + i, tree.varargsElement, true);
   754                 }
   756                 //generate the bridge method declaration
   757                 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
   758                         localContext.bridgeSym.name,
   759                         make.QualIdent(samDesc.getReturnType().tsym),
   760                         List.<JCTypeParameter>nil(),
   761                         params.toList(),
   762                         tree.sym.type.getThrownTypes() == null
   763                         ? List.<JCExpression>nil()
   764                         : make.Types(tree.sym.type.getThrownTypes()),
   765                         null,
   766                         null);
   767                 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
   768                 bridgeDecl.type = localContext.bridgeSym.type =
   769                         types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
   771                 //bridge method body generation - this can be either a method call or a
   772                 //new instance creation expression, depending on the member reference kind
   773                 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
   774                         ? bridgeExpressionInvoke(rcvr)
   775                         : bridgeExpressionNew();
   777                 //the body is either a return expression containing a method call,
   778                 //or the method call itself, depending on whether the return type of
   779                 //the bridge is non-void/void.
   780                 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
   782                 return bridgeDecl;
   783             } finally {
   784                 make.at(prevPos);
   785             }
   786         }
   788         /**
   789          * determine the receiver of the bridged method call - the receiver can
   790          * be either the synthetic receiver parameter or a type qualifier; the
   791          * original qualifier expression is never used here, as it might refer
   792          * to symbols not available in the static context of the bridge
   793          */
   794         private JCExpression bridgeExpressionInvoke(VarSymbol rcvr) {
   795             JCExpression qualifier =
   796                     tree.sym.isStatic() ?
   797                         make.Type(tree.sym.owner.type) :
   798                         (rcvr != null) ?
   799                             make.Ident(rcvr) :
   800                             tree.getQualifierExpression();
   802             //create the qualifier expression
   803             JCFieldAccess select = make.Select(qualifier, tree.sym.name);
   804             select.sym = tree.sym;
   805             select.type = tree.sym.erasure(types);
   807             //create the method call expression
   808             JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
   809                     convertArgs(tree.sym, args.toList(), tree.varargsElement)).
   810                     setType(tree.sym.erasure(types).getReturnType());
   812             apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
   813             setVarargsIfNeeded(apply, tree.varargsElement);
   814             return apply;
   815         }
   817         /**
   818          * the enclosing expression is either 'null' (no enclosing type) or set
   819          * to the first bridge synthetic parameter
   820          */
   821         private JCExpression bridgeExpressionNew() {
   822             if (tree.kind == ReferenceKind.ARRAY_CTOR) {
   823                 //create the array creation expression
   824                 JCNewArray newArr = make.NewArray(
   825                         make.Type(types.elemtype(tree.getQualifierExpression().type)),
   826                         List.of(make.Ident(params.first())),
   827                         null);
   828                 newArr.type = tree.getQualifierExpression().type;
   829                 return newArr;
   830             } else {
   831                 JCExpression encl = null;
   832                 switch (tree.kind) {
   833                     case UNBOUND:
   834                     case IMPLICIT_INNER:
   835                         encl = make.Ident(params.first());
   836                 }
   838                 //create the instance creation expression
   839                 JCNewClass newClass = make.NewClass(encl,
   840                         List.<JCExpression>nil(),
   841                         make.Type(tree.getQualifierExpression().type),
   842                         convertArgs(tree.sym, args.toList(), tree.varargsElement),
   843                         null);
   844                 newClass.constructor = tree.sym;
   845                 newClass.constructorType = tree.sym.erasure(types);
   846                 newClass.type = tree.getQualifierExpression().type;
   847                 setVarargsIfNeeded(newClass, tree.varargsElement);
   848                 return newClass;
   849             }
   850         }
   852         private VarSymbol addParameter(String name, Type p, boolean genArg) {
   853             VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
   854             params.append(make.VarDef(vsym, null));
   855             if (genArg) {
   856                 args.append(make.Ident(vsym));
   857             }
   858             return vsym;
   859         }
   860     }
   862     /**
   863      * Bridges a member reference - this is needed when:
   864      * * Var args in the referenced method need to be flattened away
   865      * * super is used
   866      */
   867     private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
   868         kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
   869     }
   871     /**
   872      * Generate an indy method call to the meta factory
   873      */
   874     private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
   875             boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
   876         //determine the static bsm args
   877         Type mtype = types.erasure(tree.descriptorType);
   878         MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
   879         List<Object> staticArgs = List.<Object>of(
   880                 new Pool.MethodHandle(ClassFile.REF_invokeInterface,
   881                     types.findDescriptorSymbol(tree.type.tsym), types),
   882                 new Pool.MethodHandle(refKind, refSym, types),
   883                 new MethodType(mtype.getParameterTypes(),
   884                         mtype.getReturnType(),
   885                         mtype.getThrownTypes(),
   886                         syms.methodClass));
   888         //computed indy arg types
   889         ListBuffer<Type> indy_args_types = ListBuffer.lb();
   890         for (JCExpression arg : indy_args) {
   891             indy_args_types.append(arg.type);
   892         }
   894         //finally, compute the type of the indy call
   895         MethodType indyType = new MethodType(indy_args_types.toList(),
   896                 tree.type,
   897                 List.<Type>nil(),
   898                 syms.methodClass);
   900         Name metafactoryName = needsAltMetafactory ?
   901                 names.altMetaFactory : names.metaFactory;
   903         if (needsAltMetafactory) {
   904             ListBuffer<Object> markers = ListBuffer.lb();
   905             for (Symbol t : tree.targets.tail) {
   906                 if (t != syms.serializableType.tsym) {
   907                     markers.append(t);
   908                 }
   909             }
   910             int flags = isSerializable? FLAG_SERIALIZABLE : 0;
   911             boolean hasMarkers = markers.nonEmpty();
   912             flags |= hasMarkers ? FLAG_MARKERS : 0;
   913             staticArgs = staticArgs.append(flags);
   914             if (hasMarkers) {
   915                 staticArgs = staticArgs.append(markers.length());
   916                 staticArgs = staticArgs.appendList(markers.toList());
   917             }
   918             if (isSerializable) {
   919                 addDeserializationCase(refKind, refSym, tree.type, samSym,
   920                         tree, staticArgs, indyType);
   921             }
   922         }
   924         return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
   925     }
   927     /**
   928      * Generate an indy method call with given name, type and static bootstrap
   929      * arguments types
   930      */
   931     private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName,
   932             List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
   933         int prevPos = make.pos;
   934         try {
   935             make.at(pos);
   936             List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
   937                     syms.stringType,
   938                     syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
   940             Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
   941                     bsmName, bsm_staticArgs, List.<Type>nil());
   943             DynamicMethodSymbol dynSym =
   944                     new DynamicMethodSymbol(names.lambda,
   945                                             syms.noSymbol,
   946                                             bsm.isStatic() ?
   947                                                 ClassFile.REF_invokeStatic :
   948                                                 ClassFile.REF_invokeVirtual,
   949                                             (MethodSymbol)bsm,
   950                                             indyType,
   951                                             staticArgs.toArray());
   953             JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
   954             qualifier.sym = dynSym;
   955             qualifier.type = indyType.getReturnType();
   957             JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
   958             proxyCall.type = indyType.getReturnType();
   959             return proxyCall;
   960         } finally {
   961             make.at(prevPos);
   962         }
   963     }
   964     //where
   965     private List<Type> bsmStaticArgToTypes(List<Object> args) {
   966         ListBuffer<Type> argtypes = ListBuffer.lb();
   967         for (Object arg : args) {
   968             argtypes.append(bsmStaticArgToType(arg));
   969         }
   970         return argtypes.toList();
   971     }
   973     private Type bsmStaticArgToType(Object arg) {
   974         Assert.checkNonNull(arg);
   975         if (arg instanceof ClassSymbol) {
   976             return syms.classType;
   977         } else if (arg instanceof Integer) {
   978             return syms.intType;
   979         } else if (arg instanceof Long) {
   980             return syms.longType;
   981         } else if (arg instanceof Float) {
   982             return syms.floatType;
   983         } else if (arg instanceof Double) {
   984             return syms.doubleType;
   985         } else if (arg instanceof String) {
   986             return syms.stringType;
   987         } else if (arg instanceof Pool.MethodHandle) {
   988             return syms.methodHandleType;
   989         } else if (arg instanceof MethodType) {
   990             return syms.methodTypeType;
   991         } else {
   992             Assert.error("bad static arg " + arg.getClass());
   993             return null;
   994         }
   995     }
   997     /**
   998      * Get the opcode associated with this method reference
   999      */
  1000     private int referenceKind(Symbol refSym) {
  1001         if (refSym.isConstructor()) {
  1002             return ClassFile.REF_newInvokeSpecial;
  1003         } else {
  1004             if (refSym.isStatic()) {
  1005                 return ClassFile.REF_invokeStatic;
  1006             } else if (refSym.enclClass().isInterface()) {
  1007                 return ClassFile.REF_invokeInterface;
  1008             } else {
  1009                 return ClassFile.REF_invokeVirtual;
  1014     // </editor-fold>
  1016     // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\
  1017     /**
  1018      * This visitor collects information about translation of a lambda expression.
  1019      * More specifically, it keeps track of the enclosing contexts and captured locals
  1020      * accessed by the lambda being translated (as well as other useful info).
  1021      */
  1022     class LambdaAnalyzer extends TreeScanner {
  1024         /** the frame stack - used to reconstruct translation info about enclosing scopes */
  1025         private List<Frame> frameStack;
  1027         /**
  1028          * keep the count of lambda expression (used to generate unambiguous
  1029          * names)
  1030          */
  1031         private int lambdaCount = 0;
  1033         /**
  1034          * keep the count of lambda expression defined in given context (used to
  1035          * generate unambiguous names for serializable lambdas)
  1036          */
  1037         private Map<String, Integer> serializableLambdaCounts =
  1038                 new HashMap<String, Integer>();
  1040         /**
  1041          * maps for fake clinit symbols to be used as owners of lambda occurring in
  1042          * a static var init context
  1043          */
  1044         private Map<ClassSymbol, Symbol> clinits =
  1045                 new HashMap<ClassSymbol, Symbol>();
  1047         private void analyzeClass(JCClassDecl tree) {
  1048             frameStack = List.nil();
  1049             scan(tree);
  1052         @Override
  1053         public void visitBlock(JCBlock tree) {
  1054             List<Frame> prevStack = frameStack;
  1055             try {
  1056                 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
  1057                     frameStack = frameStack.prepend(new Frame(tree));
  1059                 super.visitBlock(tree);
  1061             finally {
  1062                 frameStack = prevStack;
  1066         @Override
  1067         public void visitClassDef(JCClassDecl tree) {
  1068             List<Frame> prevStack = frameStack;
  1069             Map<String, Integer> prevSerializableLambdaCount =
  1070                     serializableLambdaCounts;
  1071             Map<ClassSymbol, Symbol> prevClinits = clinits;
  1072             try {
  1073                 serializableLambdaCounts = new HashMap<String, Integer>();
  1074                 prevClinits = new HashMap<ClassSymbol, Symbol>();
  1075                 if (directlyEnclosingLambda() != null) {
  1076                     tree.sym.owner = owner();
  1077                     if (tree.sym.hasOuterInstance()) {
  1078                         //if a class is defined within a lambda, the lambda must capture
  1079                         //its enclosing instance (if any)
  1080                         ((LambdaTranslationContext) context())
  1081                                 .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
  1084                 frameStack = frameStack.prepend(new Frame(tree));
  1085                 super.visitClassDef(tree);
  1087             finally {
  1088                 frameStack = prevStack;
  1089                 serializableLambdaCounts = prevSerializableLambdaCount;
  1090                 clinits = prevClinits;
  1094         @Override
  1095         public void visitIdent(JCIdent tree) {
  1096             if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
  1097                 if (tree.sym.kind == VAR &&
  1098                         tree.sym.owner.kind == MTH &&
  1099                         tree.type.constValue() == null) {
  1100                     TranslationContext<?> localContext = context();
  1101                     while (localContext != null) {
  1102                         if (localContext.tree.getTag() == LAMBDA) {
  1103                             JCTree block = capturedDecl(localContext.depth, tree.sym);
  1104                             if (block == null) break;
  1105                             ((LambdaTranslationContext)localContext)
  1106                                     .addSymbol(tree.sym, CAPTURED_VAR);
  1108                         localContext = localContext.prev;
  1110                 } else if (tree.sym.owner.kind == TYP) {
  1111                     TranslationContext<?> localContext = context();
  1112                     while (localContext != null) {
  1113                         if (localContext.tree.hasTag(LAMBDA)) {
  1114                             JCTree block = capturedDecl(localContext.depth, tree.sym);
  1115                             if (block == null) break;
  1116                             switch (block.getTag()) {
  1117                                 case CLASSDEF:
  1118                                     JCClassDecl cdecl = (JCClassDecl)block;
  1119                                     ((LambdaTranslationContext)localContext)
  1120                                             .addSymbol(cdecl.sym, CAPTURED_THIS);
  1121                                     break;
  1122                                 default:
  1123                                     Assert.error("bad block kind");
  1126                         localContext = localContext.prev;
  1130             super.visitIdent(tree);
  1133         @Override
  1134         public void visitLambda(JCLambda tree) {
  1135             List<Frame> prevStack = frameStack;
  1136             try {
  1137                 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
  1138                 frameStack = frameStack.prepend(new Frame(tree));
  1139                 for (JCVariableDecl param : tree.params) {
  1140                     context.addSymbol(param.sym, PARAM);
  1141                     frameStack.head.addLocal(param.sym);
  1143                 contextMap.put(tree, context);
  1144                 scan(tree.body);
  1145                 context.complete();
  1147             finally {
  1148                 frameStack = prevStack;
  1152         @Override
  1153         public void visitMethodDef(JCMethodDecl tree) {
  1154             List<Frame> prevStack = frameStack;
  1155             try {
  1156                 frameStack = frameStack.prepend(new Frame(tree));
  1157                 super.visitMethodDef(tree);
  1159             finally {
  1160                 frameStack = prevStack;
  1164         @Override
  1165         public void visitNewClass(JCNewClass tree) {
  1166             if (lambdaNewClassFilter(context(), tree)) {
  1167                 ((LambdaTranslationContext) context())
  1168                         .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS);
  1170             super.visitNewClass(tree);
  1173         @Override
  1174         public void visitReference(JCMemberReference tree) {
  1175             scan(tree.getQualifierExpression());
  1176             contextMap.put(tree, makeReferenceContext(tree));
  1179         @Override
  1180         public void visitSelect(JCFieldAccess tree) {
  1181             if (context() != null && lambdaSelectSymbolFilter(tree.sym)) {
  1182                 TranslationContext<?> localContext = context();
  1183                 while (localContext != null) {
  1184                     if (localContext.tree.hasTag(LAMBDA)) {
  1185                         JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym);
  1186                         if (clazz == null) break;
  1187                         ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS);
  1189                     localContext = localContext.prev;
  1191                 scan(tree.selected);
  1192             } else {
  1193                 super.visitSelect(tree);
  1197         @Override
  1198         public void visitVarDef(JCVariableDecl tree) {
  1199             TranslationContext<?> context = context();
  1200             LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)?
  1201                     (LambdaTranslationContext)context :
  1202                     null;
  1203             if (ltc != null) {
  1204                 if (frameStack.head.tree.hasTag(LAMBDA)) {
  1205                     ltc.addSymbol(tree.sym, LOCAL_VAR);
  1207                 // Check for type variables (including as type arguments).
  1208                 // If they occur within class nested in a lambda, mark for erasure
  1209                 Type type = tree.sym.asType();
  1210                 if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) {
  1211                     ltc.addSymbol(tree.sym, TYPE_VAR);
  1215             List<Frame> prevStack = frameStack;
  1216             try {
  1217                 if (tree.sym.owner.kind == MTH) {
  1218                     frameStack.head.addLocal(tree.sym);
  1220                 frameStack = frameStack.prepend(new Frame(tree));
  1221                 super.visitVarDef(tree);
  1223             finally {
  1224                 frameStack = prevStack;
  1228         private Name lambdaName() {
  1229             return names.lambda.append(names.fromString("" + lambdaCount++));
  1232         private Name serializedLambdaName(Symbol owner) {
  1233             StringBuilder buf = new StringBuilder();
  1234             buf.append(names.lambda);
  1235             buf.append(owner.name);
  1236             buf.append('$');
  1237             int methTypeHash = methodSig(owner.type).hashCode();
  1238             buf.append(methTypeHash);
  1239             buf.append('$');
  1240             String temp = buf.toString();
  1241             Integer count = serializableLambdaCounts.get(temp);
  1242             if (count == null) {
  1243                 count = 0;
  1245             buf.append(count++);
  1246             serializableLambdaCounts.put(temp, count);
  1247             return names.fromString(buf.toString());
  1250         /**
  1251          * Return a valid owner given the current declaration stack
  1252          * (required to skip synthetic lambda symbols)
  1253          */
  1254         private Symbol owner() {
  1255             return owner(false);
  1258         @SuppressWarnings("fallthrough")
  1259         private Symbol owner(boolean skipLambda) {
  1260             List<Frame> frameStack2 = frameStack;
  1261             while (frameStack2.nonEmpty()) {
  1262                 switch (frameStack2.head.tree.getTag()) {
  1263                     case VARDEF:
  1264                         if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) {
  1265                             frameStack2 = frameStack2.tail;
  1266                             break;
  1268                         JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree;
  1269                         return initSym(cdecl.sym,
  1270                                 ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC);
  1271                     case BLOCK:
  1272                         JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree;
  1273                         return initSym(cdecl2.sym,
  1274                                 ((JCBlock)frameStack2.head.tree).flags & STATIC);
  1275                     case CLASSDEF:
  1276                         return ((JCClassDecl)frameStack2.head.tree).sym;
  1277                     case METHODDEF:
  1278                         return ((JCMethodDecl)frameStack2.head.tree).sym;
  1279                     case LAMBDA:
  1280                         if (!skipLambda)
  1281                             return ((LambdaTranslationContext)contextMap
  1282                                     .get(frameStack2.head.tree)).translatedSym;
  1283                     default:
  1284                         frameStack2 = frameStack2.tail;
  1287             Assert.error();
  1288             return null;
  1291         private Symbol initSym(ClassSymbol csym, long flags) {
  1292             boolean isStatic = (flags & STATIC) != 0;
  1293             if (isStatic) {
  1294                 //static clinits are generated in Gen - so we need to fake them
  1295                 Symbol clinit = clinits.get(csym);
  1296                 if (clinit == null) {
  1297                     clinit = makeSyntheticMethod(STATIC,
  1298                             names.clinit,
  1299                             new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
  1300                             csym);
  1301                     clinits.put(csym, clinit);
  1303                 return clinit;
  1304             } else {
  1305                 //get the first constructor and treat it as the instance init sym
  1306                 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
  1307                     return s;
  1310             Assert.error("init not found");
  1311             return null;
  1314         private JCTree directlyEnclosingLambda() {
  1315             if (frameStack.isEmpty()) {
  1316                 return null;
  1318             List<Frame> frameStack2 = frameStack;
  1319             while (frameStack2.nonEmpty()) {
  1320                 switch (frameStack2.head.tree.getTag()) {
  1321                     case CLASSDEF:
  1322                     case METHODDEF:
  1323                         return null;
  1324                     case LAMBDA:
  1325                         return frameStack2.head.tree;
  1326                     default:
  1327                         frameStack2 = frameStack2.tail;
  1330             Assert.error();
  1331             return null;
  1334         private boolean inClassWithinLambda() {
  1335             if (frameStack.isEmpty()) {
  1336                 return false;
  1338             List<Frame> frameStack2 = frameStack;
  1339             boolean classFound = false;
  1340             while (frameStack2.nonEmpty()) {
  1341                 switch (frameStack2.head.tree.getTag()) {
  1342                     case LAMBDA:
  1343                         return classFound;
  1344                     case CLASSDEF:
  1345                         classFound = true;
  1346                         frameStack2 = frameStack2.tail;
  1347                         break;
  1348                     default:
  1349                         frameStack2 = frameStack2.tail;
  1352             // No lambda
  1353             return false;
  1356         /**
  1357          * Return the declaration corresponding to a symbol in the enclosing
  1358          * scope; the depth parameter is used to filter out symbols defined
  1359          * in nested scopes (which do not need to undergo capture).
  1360          */
  1361         private JCTree capturedDecl(int depth, Symbol sym) {
  1362             int currentDepth = frameStack.size() - 1;
  1363             for (Frame block : frameStack) {
  1364                 switch (block.tree.getTag()) {
  1365                     case CLASSDEF:
  1366                         ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
  1367                         if (sym.isMemberOf(clazz, types)) {
  1368                             return currentDepth > depth ? null : block.tree;
  1370                         break;
  1371                     case VARDEF:
  1372                         if (((JCVariableDecl)block.tree).sym == sym &&
  1373                                 sym.owner.kind == MTH) { //only locals are captured
  1374                             return currentDepth > depth ? null : block.tree;
  1376                         break;
  1377                     case BLOCK:
  1378                     case METHODDEF:
  1379                     case LAMBDA:
  1380                         if (block.locals != null && block.locals.contains(sym)) {
  1381                             return currentDepth > depth ? null : block.tree;
  1383                         break;
  1384                     default:
  1385                         Assert.error("bad decl kind " + block.tree.getTag());
  1387                 currentDepth--;
  1389             return null;
  1392         private TranslationContext<?> context() {
  1393             for (Frame frame : frameStack) {
  1394                 TranslationContext<?> context = contextMap.get(frame.tree);
  1395                 if (context != null) {
  1396                     return context;
  1399             return null;
  1402         /**
  1403          *  This is used to filter out those identifiers that needs to be adjusted
  1404          *  when translating away lambda expressions
  1405          */
  1406         private boolean lambdaIdentSymbolFilter(Symbol sym) {
  1407             return (sym.kind == VAR || sym.kind == MTH)
  1408                     && !sym.isStatic()
  1409                     && sym.name != names.init;
  1412         private boolean lambdaSelectSymbolFilter(Symbol sym) {
  1413             return (sym.kind == VAR || sym.kind == MTH) &&
  1414                         !sym.isStatic() &&
  1415                         (sym.name == names._this ||
  1416                         sym.name == names._super);
  1419         /**
  1420          * This is used to filter out those new class expressions that need to
  1421          * be qualified with an enclosing tree
  1422          */
  1423         private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
  1424             if (context != null
  1425                     && tree.encl == null
  1426                     && tree.def == null
  1427                     && !tree.type.getEnclosingType().hasTag(NONE)) {
  1428                 Type encl = tree.type.getEnclosingType();
  1429                 Type current = context.owner.enclClass().type;
  1430                 while (!current.hasTag(NONE)) {
  1431                     if (current.tsym.isSubClass(encl.tsym, types)) {
  1432                         return true;
  1434                     current = current.getEnclosingType();
  1436                 return false;
  1437             } else {
  1438                 return false;
  1442         private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
  1443             return new LambdaTranslationContext(tree);
  1446         private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
  1447             return new ReferenceTranslationContext(tree);
  1450         private class Frame {
  1451             final JCTree tree;
  1452             List<Symbol> locals;
  1454             public Frame(JCTree tree) {
  1455                 this.tree = tree;
  1458             void addLocal(Symbol sym) {
  1459                 if (locals == null) {
  1460                     locals = List.nil();
  1462                 locals = locals.prepend(sym);
  1466         /**
  1467          * This class is used to store important information regarding translation of
  1468          * lambda expression/method references (see subclasses).
  1469          */
  1470         private abstract class TranslationContext<T extends JCFunctionalExpression> {
  1472             /** the underlying (untranslated) tree */
  1473             T tree;
  1475             /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
  1476             Symbol owner;
  1478             /** the depth of this lambda expression in the frame stack */
  1479             int depth;
  1481             /** the enclosing translation context (set for nested lambdas/mref) */
  1482             TranslationContext<?> prev;
  1484             TranslationContext(T tree) {
  1485                 this.tree = tree;
  1486                 this.owner = owner();
  1487                 this.depth = frameStack.size() - 1;
  1488                 this.prev = context();
  1491             /** does this functional expression need to be created using alternate metafactory? */
  1492             boolean needsAltMetafactory() {
  1493                 return (tree.targets.length() > 1 ||
  1494                         isSerializable());
  1497             /** does this functional expression require serialization support? */
  1498             boolean isSerializable() {
  1499                 for (Symbol target : tree.targets) {
  1500                     if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
  1501                         return true;
  1504                 return false;
  1508         /**
  1509          * This class retains all the useful information about a lambda expression;
  1510          * the contents of this class are filled by the LambdaAnalyzer visitor,
  1511          * and the used by the main translation routines in order to adjust references
  1512          * to captured locals/members, etc.
  1513          */
  1514         private class LambdaTranslationContext extends TranslationContext<JCLambda> {
  1516             /** variable in the enclosing context to which this lambda is assigned */
  1517             Symbol self;
  1519             /** map from original to translated lambda parameters */
  1520             Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
  1522             /** map from original to translated lambda locals */
  1523             Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
  1525             /** map from variables in enclosing scope to translated synthetic parameters */
  1526             Map<Symbol, Symbol> capturedLocals  = new LinkedHashMap<Symbol, Symbol>();
  1528             /** map from class symbols to translated synthetic parameters (for captured member access) */
  1529             Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
  1531             /** map from original to translated lambda locals */
  1532             Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
  1534             /** the synthetic symbol for the method hoisting the translated lambda */
  1535             Symbol translatedSym;
  1537             List<JCVariableDecl> syntheticParams;
  1539             LambdaTranslationContext(JCLambda tree) {
  1540                 super(tree);
  1541                 Frame frame = frameStack.head;
  1542                 if (frame.tree.hasTag(VARDEF)) {
  1543                     self = ((JCVariableDecl)frame.tree).sym;
  1545                 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
  1546                 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
  1549             /**
  1550              * Translate a symbol of a given kind into something suitable for the
  1551              * synthetic lambda body
  1552              */
  1553             Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) {
  1554                 switch (skind) {
  1555                     case CAPTURED_THIS:
  1556                         return sym;  // self represented
  1557                     case TYPE_VAR:
  1558                         // Just erase the type var
  1559                         return new VarSymbol(sym.flags(), names.fromString(name),
  1560                                 types.erasure(sym.type), sym.owner);
  1561                     default:
  1562                         return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
  1566             void addSymbol(Symbol sym, LambdaSymbolKind skind) {
  1567                 Map<Symbol, Symbol> transMap = null;
  1568                 String preferredName;
  1569                 switch (skind) {
  1570                     case CAPTURED_THIS:
  1571                         transMap = capturedThis;
  1572                         preferredName = "encl$" + capturedThis.size();
  1573                         break;
  1574                     case CAPTURED_VAR:
  1575                         transMap = capturedLocals;
  1576                         preferredName = "cap$" + capturedLocals.size();
  1577                         break;
  1578                     case LOCAL_VAR:
  1579                         transMap = lambdaLocals;
  1580                         preferredName = sym.name.toString();
  1581                         break;
  1582                     case PARAM:
  1583                         transMap = lambdaParams;
  1584                         preferredName = sym.name.toString();
  1585                         break;
  1586                     case TYPE_VAR:
  1587                         transMap = typeVars;
  1588                         preferredName = sym.name.toString();
  1589                         break;
  1590                     default: throw new AssertionError();
  1592                 if (!transMap.containsKey(sym)) {
  1593                     transMap.put(sym, translate(preferredName, sym, skind));
  1597             Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
  1598                 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
  1599                 for (LambdaSymbolKind skind : skinds) {
  1600                     switch (skind) {
  1601                         case CAPTURED_THIS:
  1602                             translationMap.putAll(capturedThis);
  1603                             break;
  1604                         case CAPTURED_VAR:
  1605                             translationMap.putAll(capturedLocals);
  1606                             break;
  1607                         case LOCAL_VAR:
  1608                             translationMap.putAll(lambdaLocals);
  1609                             break;
  1610                         case PARAM:
  1611                             translationMap.putAll(lambdaParams);
  1612                             break;
  1613                         case TYPE_VAR:
  1614                             translationMap.putAll(typeVars);
  1615                             break;
  1616                         default: throw new AssertionError();
  1619                 return translationMap;
  1622             /**
  1623              * The translatedSym is not complete/accurate until the analysis is
  1624              * finished.  Once the analysis is finished, the translatedSym is
  1625              * "completed" -- updated with type information, access modifiers,
  1626              * and full parameter list.
  1627              */
  1628             void complete() {
  1629                 if (syntheticParams != null) {
  1630                     return;
  1632                 boolean inInterface = translatedSym.owner.isInterface();
  1633                 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
  1634                 boolean needInstance = thisReferenced || inInterface;
  1636                 // If instance access isn't needed, make it static
  1637                 // Interface methods much be public default methods, otherwise make it private
  1638                 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) |
  1639                         (inInterface? PUBLIC | DEFAULT : PRIVATE);
  1641                 //compute synthetic params
  1642                 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
  1644                 // The signature of the method is augmented with the following
  1645                 // synthetic parameters:
  1646                 //
  1647                 // 1) reference to enclosing contexts captured by the lambda expression
  1648                 // 2) enclosing locals captured by the lambda expression
  1649                 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
  1650                     params.append(make.VarDef((VarSymbol) thisSym, null));
  1653                 syntheticParams = params.toList();
  1655                 //prepend synthetic args to translated lambda method signature
  1656                 translatedSym.type = types.createMethodTypeWithParameters(
  1657                         generatedLambdaSig(),
  1658                         TreeInfo.types(syntheticParams));
  1661             Type generatedLambdaSig() {
  1662                 return types.erasure(tree.descriptorType);
  1666         /**
  1667          * This class retains all the useful information about a method reference;
  1668          * the contents of this class are filled by the LambdaAnalyzer visitor,
  1669          * and the used by the main translation routines in order to adjust method
  1670          * references (i.e. in case a bridge is needed)
  1671          */
  1672         private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
  1674             final boolean isSuper;
  1675             final Symbol bridgeSym;
  1677             ReferenceTranslationContext(JCMemberReference tree) {
  1678                 super(tree);
  1679                 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
  1680                 this.bridgeSym = needsBridge()
  1681                         ? makeSyntheticMethod(isSuper ? 0 : STATIC,
  1682                                               lambdaName().append(names.fromString("$bridge")), null,
  1683                                               owner.enclClass())
  1684                         : null;
  1687             /**
  1688              * Get the opcode associated with this method reference
  1689              */
  1690             int referenceKind() {
  1691                 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
  1694             boolean needsVarArgsConversion() {
  1695                 return tree.varargsElement != null;
  1698             /**
  1699              * @return Is this an array operation like clone()
  1700              */
  1701             boolean isArrayOp() {
  1702                 return tree.sym.owner == syms.arrayClass;
  1705             boolean isPrivateConstructor() {
  1706                 return tree.sym.name == names.init &&
  1707                         (tree.sym.flags() & PRIVATE) != 0;
  1710             /**
  1711              * Does this reference needs a bridge (i.e. var args need to be
  1712              * expanded or "super" is used)
  1713              */
  1714             final boolean needsBridge() {
  1715                 return isSuper || needsVarArgsConversion() || isArrayOp() || isPrivateConstructor();
  1718             Type generatedRefSig() {
  1719                 return types.erasure(tree.sym.type);
  1722             Type bridgedRefSig() {
  1723                 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
  1727     // </editor-fold>
  1729     enum LambdaSymbolKind {
  1730         CAPTURED_VAR,
  1731         CAPTURED_THIS,
  1732         LOCAL_VAR,
  1733         PARAM,
  1734         TYPE_VAR;
  1737     /**
  1738      * ****************************************************************
  1739      * Signature Generation
  1740      * ****************************************************************
  1741      */
  1743     private String methodSig(Type type) {
  1744         L2MSignatureGenerator sg = new L2MSignatureGenerator();
  1745         sg.assembleSig(type);
  1746         return sg.toString();
  1749     private String classSig(Type type) {
  1750         L2MSignatureGenerator sg = new L2MSignatureGenerator();
  1751         sg.assembleClassSig(type);
  1752         return sg.toString();
  1755     /**
  1756      * Signature Generation
  1757      */
  1758     private class L2MSignatureGenerator extends Types.SignatureGenerator {
  1760         /**
  1761          * An output buffer for type signatures.
  1762          */
  1763         StringBuilder sb = new StringBuilder();
  1765         L2MSignatureGenerator() {
  1766             super(types);
  1769         @Override
  1770         protected void append(char ch) {
  1771             sb.append(ch);
  1774         @Override
  1775         protected void append(byte[] ba) {
  1776             sb.append(new String(ba));
  1779         @Override
  1780         protected void append(Name name) {
  1781             sb.append(name.toString());
  1784         @Override
  1785         public String toString() {
  1786             return sb.toString();

mercurial