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

Mon, 18 Feb 2013 14:33:25 +0000

author
vromero
date
Mon, 18 Feb 2013 14:33:25 +0000
changeset 1588
2620c953e9fe
parent 1587
f1f605f85850
child 1595
d686d8a7eb78
permissions
-rw-r--r--

6563143: javac should issue a warning for overriding equals without hashCode
Reviewed-by: jjg, mcimadamore

     1 /*
     2  * Copyright (c) 2010, 2012, 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"), syms.serializedLambdaType, deserMethodSym);
   129         }
   131         private void addMethod(JCTree decl) {
   132             appendedMethodList = appendedMethodList.prepend(decl);
   133         }
   134     }
   136     // <editor-fold defaultstate="collapsed" desc="Instantiating">
   137     private static final Context.Key<LambdaToMethod> unlambdaKey =
   138             new Context.Key<LambdaToMethod>();
   140     public static LambdaToMethod instance(Context context) {
   141         LambdaToMethod instance = context.get(unlambdaKey);
   142         if (instance == null) {
   143             instance = new LambdaToMethod(context);
   144         }
   145         return instance;
   146     }
   148     private LambdaToMethod(Context context) {
   149         names = Names.instance(context);
   150         syms = Symtab.instance(context);
   151         rs = Resolve.instance(context);
   152         make = TreeMaker.instance(context);
   153         types = Types.instance(context);
   154         transTypes = TransTypes.instance(context);
   155         analyzer = new LambdaAnalyzer();
   156     }
   157     // </editor-fold>
   159     // <editor-fold defaultstate="collapsed" desc="translate methods">
   160     @Override
   161     public <T extends JCTree> T translate(T tree) {
   162         TranslationContext<?> newContext = contextMap.get(tree);
   163         return translate(tree, newContext != null ? newContext : context);
   164     }
   166     public <T extends JCTree> T translate(T tree, TranslationContext<?> newContext) {
   167         TranslationContext<?> prevContext = context;
   168         try {
   169             context = newContext;
   170             return super.translate(tree);
   171         }
   172         finally {
   173             context = prevContext;
   174         }
   175     }
   177     public <T extends JCTree> List<T> translate(List<T> trees, TranslationContext<?> newContext) {
   178         ListBuffer<T> buf = ListBuffer.lb();
   179         for (T tree : trees) {
   180             buf.append(translate(tree, newContext));
   181         }
   182         return buf.toList();
   183     }
   185     public JCTree translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) {
   186         this.make = make;
   187         this.attrEnv = env;
   188         this.context = null;
   189         this.contextMap = new HashMap<JCTree, TranslationContext<?>>();
   190         return translate(cdef);
   191     }
   192     // </editor-fold>
   194     // <editor-fold defaultstate="collapsed" desc="visitor methods">
   195     /**
   196      * Visit a class.
   197      * Maintain the translatedMethodList across nested classes.
   198      * Append the translatedMethodList to the class after it is translated.
   199      * @param tree
   200      */
   201     @Override
   202     public void visitClassDef(JCClassDecl tree) {
   203         if (tree.sym.owner.kind == PCK) {
   204             //analyze class
   205             analyzer.analyzeClass(tree);
   206         }
   207         KlassInfo prevKlassInfo = kInfo;
   208         try {
   209             kInfo = new KlassInfo(tree.sym);
   210             super.visitClassDef(tree);
   211             if (!kInfo.deserializeCases.isEmpty()) {
   212                 kInfo.addMethod(makeDeserializeMethod(tree.sym));
   213             }
   214             //add all translated instance methods here
   215             List<JCTree> newMethods = kInfo.appendedMethodList.toList();
   216             tree.defs = tree.defs.appendList(newMethods);
   217             for (JCTree lambda : newMethods) {
   218                 tree.sym.members().enter(((JCMethodDecl)lambda).sym);
   219             }
   220             result = tree;
   221         } finally {
   222             kInfo = prevKlassInfo;
   223         }
   224     }
   226     /**
   227      * Translate a lambda into a method to be inserted into the class.
   228      * Then replace the lambda site with an invokedynamic call of to lambda
   229      * meta-factory, which will use the lambda method.
   230      * @param tree
   231      */
   232     @Override
   233     public void visitLambda(JCLambda tree) {
   234         LambdaTranslationContext localContext = (LambdaTranslationContext)context;
   235         MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
   236         MethodType lambdaType = (MethodType) sym.type;
   238         //create the method declaration hoisting the lambda body
   239         JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
   240                 sym.name,
   241                 make.QualIdent(lambdaType.getReturnType().tsym),
   242                 List.<JCTypeParameter>nil(),
   243                 localContext.syntheticParams,
   244                 lambdaType.getThrownTypes() == null ?
   245                     List.<JCExpression>nil() :
   246                     make.Types(lambdaType.getThrownTypes()),
   247                 null,
   248                 null);
   249         lambdaDecl.sym = sym;
   250         lambdaDecl.type = lambdaType;
   252         //translate lambda body
   253         //As the lambda body is translated, all references to lambda locals,
   254         //captured variables, enclosing members are adjusted accordingly
   255         //to refer to the static method parameters (rather than i.e. acessing to
   256         //captured members directly).
   257         lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
   259         //Add the method to the list of methods to be added to this class.
   260         kInfo.addMethod(lambdaDecl);
   262         //now that we have generated a method for the lambda expression,
   263         //we can translate the lambda into a method reference pointing to the newly
   264         //created method.
   265         //
   266         //Note that we need to adjust the method handle so that it will match the
   267         //signature of the SAM descriptor - this means that the method reference
   268         //should be added the following synthetic arguments:
   269         //
   270         // * the "this" argument if it is an instance method
   271         // * enclosing locals captured by the lambda expression
   273         ListBuffer<JCExpression> syntheticInits = ListBuffer.lb();
   275         if (!sym.isStatic()) {
   276             syntheticInits.append(makeThis(
   277                     sym.owner.enclClass().asType(),
   278                     localContext.owner.enclClass()));
   279         }
   281         //add captured locals
   282         for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
   283             if (fv != localContext.self) {
   284                 JCTree captured_local = make.Ident(fv).setType(fv.type);
   285                 syntheticInits.append((JCExpression) captured_local);
   286             }
   287         }
   289         //then, determine the arguments to the indy call
   290         List<JCExpression> indy_args = translate(syntheticInits.toList(), localContext.prev);
   292         //build a sam instance using an indy call to the meta-factory
   293         int refKind = referenceKind(sym);
   295         //convert to an invokedynamic call
   296         result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args);
   297     }
   299     private JCIdent makeThis(Type type, Symbol owner) {
   300         VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
   301                 names._this,
   302                 type,
   303                 owner);
   304         return make.Ident(_this);
   305     }
   307     /**
   308      * Translate a method reference into an invokedynamic call to the
   309      * meta-factory.
   310      * @param tree
   311      */
   312     @Override
   313     public void visitReference(JCMemberReference tree) {
   314         ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
   316         //first determine the method symbol to be used to generate the sam instance
   317         //this is either the method reference symbol, or the bridged reference symbol
   318         Symbol refSym = localContext.needsBridge() ?
   319             localContext.bridgeSym :
   320             tree.sym;
   322         //build the bridge method, if needed
   323         if (localContext.needsBridge()) {
   324             bridgeMemberReference(tree, localContext);
   325         }
   327         //the qualifying expression is treated as a special captured arg
   328         JCExpression init;
   329         switch(tree.kind) {
   331             case IMPLICIT_INNER:    /** Inner :: new */
   332             case SUPER:             /** super :: instMethod */
   333                 init = makeThis(
   334                     localContext.owner.enclClass().asType(),
   335                     localContext.owner.enclClass());
   336                 break;
   338             case BOUND:             /** Expr :: instMethod */
   339                 init = tree.getQualifierExpression();
   340                 break;
   342             case UNBOUND:           /** Type :: instMethod */
   343             case STATIC:            /** Type :: staticMethod */
   344             case TOPLEVEL:          /** Top level :: new */
   345             case ARRAY_CTOR:        /** ArrayType :: new */
   346                 init = null;
   347                 break;
   349             default:
   350                 throw new InternalError("Should not have an invalid kind");
   351         }
   353         List<JCExpression> indy_args = init==null? List.<JCExpression>nil() : translate(List.of(init), localContext.prev);
   356         //build a sam instance using an indy call to the meta-factory
   357         result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args);
   358     }
   360     /**
   361      * Translate identifiers within a lambda to the mapped identifier
   362      * @param tree
   363      */
   364     @Override
   365     public void visitIdent(JCIdent tree) {
   366         if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
   367             super.visitIdent(tree);
   368         } else {
   369             LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
   370             if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) {
   371                 Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym);
   372                 result = make.Ident(translatedSym).setType(tree.type);
   373             } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
   374                 Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
   375                 result = make.Ident(translatedSym).setType(tree.type);
   376             } else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
   377                 Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
   378                 result = make.Ident(translatedSym).setType(translatedSym.type);
   379             } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) {
   380                 Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym);
   381                 result = make.Ident(translatedSym).setType(tree.type);
   382             } else {
   383                 if (tree.sym.owner.kind == Kinds.TYP) {
   384                     for (Map.Entry<Symbol, Symbol> encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) {
   385                         if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) {
   386                             JCExpression enclRef = make.Ident(encl_entry.getValue());
   387                             result = tree.sym.name == names._this
   388                                     ? enclRef.setType(tree.type)
   389                                     : make.Select(enclRef, tree.sym).setType(tree.type);
   390                             result = tree;
   391                             return;
   392                         }
   393                     }
   394                 }
   395                 //access to untranslated symbols (i.e. compile-time constants,
   396                 //members defined inside the lambda body, etc.) )
   397                 super.visitIdent(tree);
   398             }
   399         }
   400     }
   402     @Override
   403     public void visitVarDef(JCVariableDecl tree) {
   404         LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
   405         if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
   406             JCExpression init = translate(tree.init);
   407             result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init);
   408         } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
   409             JCExpression init = translate(tree.init);
   410             VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
   411             result = make.VarDef(xsym, init);
   412             // Replace the entered symbol for this variable
   413             Scope sc = tree.sym.owner.members();
   414             if (sc != null) {
   415                 sc.remove(tree.sym);
   416                 sc.enter(xsym);
   417             }
   418         } else {
   419             super.visitVarDef(tree);
   420         }
   421     }
   423     // </editor-fold>
   425     // <editor-fold defaultstate="collapsed" desc="Translation helper methods">
   427     private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
   428         return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
   429                 makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
   430                 makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
   431     }
   433     private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
   434         Type restype = lambdaMethodDecl.type.getReturnType();
   435         boolean isLambda_void = expr.type.hasTag(VOID);
   436         boolean isTarget_void = restype.hasTag(VOID);
   437         boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
   438         if (isTarget_void) {
   439             //target is void:
   440             // BODY;
   441             JCStatement stat = make.Exec(expr);
   442             return make.Block(0, List.<JCStatement>of(stat));
   443         } else if (isLambda_void && isTarget_Void) {
   444             //void to Void conversion:
   445             // BODY; return null;
   446             ListBuffer<JCStatement> stats = ListBuffer.lb();
   447             stats.append(make.Exec(expr));
   448             stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
   449             return make.Block(0, stats.toList());
   450         } else {
   451             //non-void to non-void conversion:
   452             // return (TYPE)BODY;
   453             JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
   454             return make.Block(0, List.<JCStatement>of(make.Return(retExpr)));
   455         }
   456     }
   458     private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
   459         final Type restype = lambdaMethodDecl.type.getReturnType();
   460         final boolean isTarget_void = restype.hasTag(VOID);
   461         boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
   463         class LambdaBodyTranslator extends TreeTranslator {
   465             @Override
   466             public void visitClassDef(JCClassDecl tree) {
   467                 //do NOT recurse on any inner classes
   468                 result = tree;
   469             }
   471             @Override
   472             public void visitLambda(JCLambda tree) {
   473                 //do NOT recurse on any nested lambdas
   474                 result = tree;
   475             }
   477             @Override
   478             public void visitReturn(JCReturn tree) {
   479                 boolean isLambda_void = tree.expr == null;
   480                 if (isTarget_void && !isLambda_void) {
   481                     //Void to void conversion:
   482                     // { TYPE $loc = RET-EXPR; return; }
   483                     VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
   484                     JCVariableDecl varDef = make.VarDef(loc, tree.expr);
   485                     result = make.Block(0, List.<JCStatement>of(varDef, make.Return(null)));
   486                 } else if (!isTarget_void || !isLambda_void) {
   487                     //non-void to non-void conversion:
   488                     // return (TYPE)RET-EXPR;
   489                     tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
   490                     result = tree;
   491                 } else {
   492                     result = tree;
   493                 }
   495             }
   496         }
   498         JCBlock trans_block = new LambdaBodyTranslator().translate(block);
   499         if (completeNormally && isTarget_Void) {
   500             //there's no return statement and the lambda (possibly inferred)
   501             //return type is java.lang.Void; emit a synthetic return statement
   502             trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
   503         }
   504         return trans_block;
   505     }
   507     private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
   508         ListBuffer<JCCase> cases = ListBuffer.lb();
   509         ListBuffer<JCBreak> breaks = ListBuffer.lb();
   510         for (Map.Entry<String, ListBuffer<JCStatement>> entry : kInfo.deserializeCases.entrySet()) {
   511             JCBreak br = make.Break(null);
   512             breaks.add(br);
   513             List<JCStatement> stmts = entry.getValue().append(br).toList();
   514             cases.add(make.Case(make.Literal(entry.getKey()), stmts));
   515         }
   516         JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
   517         for (JCBreak br : breaks) {
   518             br.target = sw;
   519         }
   520         JCBlock body = make.Block(0L, List.<JCStatement>of(
   521                 sw,
   522                 make.Throw(makeNewClass(
   523                     syms.illegalArgumentExceptionType,
   524                     List.<JCExpression>of(make.Literal("Invalid lambda deserialization"))))));
   525         JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
   526                         names.deserializeLambda,
   527                         make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
   528                         List.<JCTypeParameter>nil(),
   529                         List.of(make.VarDef(kInfo.deserParamSym, null)),
   530                         List.<JCExpression>nil(),
   531                         body,
   532                         null);
   533         deser.sym = kInfo.deserMethodSym;
   534         deser.type = kInfo.deserMethodSym.type;
   535         //System.err.printf("DESER: '%s'\n", deser);
   536         return deser;
   537     }
   539     /** Make an attributed class instance creation expression.
   540      *  @param ctype    The class type.
   541      *  @param args     The constructor arguments.
   542      */
   543     JCNewClass makeNewClass(Type ctype, List<JCExpression> args) {
   544         JCNewClass tree = make.NewClass(null,
   545             null, make.QualIdent(ctype.tsym), args, null);
   546         tree.constructor = rs.resolveConstructor(
   547             null, attrEnv, ctype, TreeInfo.types(args), List.<Type>nil());
   548         tree.type = ctype;
   549         return tree;
   550     }
   552     private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
   553             DiagnosticPosition pos, List<Object> staticArgs, MethodType indyType) {
   554         String functionalInterfaceClass = classSig(targetType);
   555         String functionalInterfaceMethodName = samSym.getSimpleName().toString();
   556         String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type));
   557         String implClass = classSig(refSym.owner.type);
   558         String implMethodName = refSym.getQualifiedName().toString();
   559         String implMethodSignature = methodSig(types.erasure(refSym.type));
   561         JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind));
   562         ListBuffer<JCExpression> serArgs = ListBuffer.lb();
   563         int i = 0;
   564         for (Type t : indyType.getParameterTypes()) {
   565             List<JCExpression> indexAsArg = ListBuffer.<JCExpression>lb().append(make.Literal(i)).toList();
   566             List<Type> argTypes = ListBuffer.<Type>lb().append(syms.intType).toList();
   567             serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg)));
   568             ++i;
   569         }
   570         JCStatement stmt = make.If(
   571                 deserTest(deserTest(deserTest(deserTest(deserTest(
   572                     kindTest,
   573                     "getFunctionalInterfaceClass", functionalInterfaceClass),
   574                     "getFunctionalInterfaceMethodName", functionalInterfaceMethodName),
   575                     "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature),
   576                     "getImplClass", implClass),
   577                     "getImplMethodSignature", implMethodSignature),
   578                 make.Return(makeIndyCall(
   579                     pos,
   580                     syms.lambdaMetafactory,
   581                     names.altMetaFactory,
   582                     staticArgs, indyType, serArgs.toList())),
   583                 null);
   584         ListBuffer<JCStatement> stmts = kInfo.deserializeCases.get(implMethodName);
   585         if (stmts == null) {
   586             stmts = ListBuffer.lb();
   587             kInfo.deserializeCases.put(implMethodName, stmts);
   588         }
   589         /****
   590         System.err.printf("+++++++++++++++++\n");
   591         System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass);
   592         System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName);
   593         System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature);
   594         System.err.printf("*implMethodKind: %d\n", implMethodKind);
   595         System.err.printf("*implClass: '%s'\n", implClass);
   596         System.err.printf("*implMethodName: '%s'\n", implMethodName);
   597         System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature);
   598         ****/
   599         stmts.append(stmt);
   600     }
   602     private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) {
   603         JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2);
   604         testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType);
   605         testExpr.setType(syms.booleanType);
   606         return testExpr;
   607     }
   609     private JCExpression deserTest(JCExpression prev, String func, String lit) {
   610         MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.<Type>nil(), syms.methodClass);
   611         Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.<Type>nil());
   612         JCMethodInvocation eqtest = make.Apply(
   613                 List.<JCExpression>nil(),
   614                 make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt),
   615                 List.<JCExpression>of(make.Literal(lit)));
   616         eqtest.setType(syms.booleanType);
   617         JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest);
   618         compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType);
   619         compound.setType(syms.booleanType);
   620         return compound;
   621     }
   623     private JCExpression deserGetter(String func, Type type) {
   624         return deserGetter(func, type, List.<Type>nil(), List.<JCExpression>nil());
   625     }
   627     private JCExpression deserGetter(String func, Type type, List<Type> argTypes, List<JCExpression> args) {
   628         MethodType getmt = new MethodType(argTypes, type, List.<Type>nil(), syms.methodClass);
   629         Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.<Type>nil());
   630         return make.Apply(
   631                     List.<JCExpression>nil(),
   632                     make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt),
   633                     args).setType(type);
   634     }
   636     /**
   637      * Create new synthetic method with given flags, name, type, owner
   638      */
   639     private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) {
   640         return new MethodSymbol(flags | SYNTHETIC, name, type, owner);
   641     }
   643     /**
   644      * Create new synthetic variable with given flags, name, type, owner
   645      */
   646     private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) {
   647         return makeSyntheticVar(flags, names.fromString(name), type, owner);
   648     }
   650     /**
   651      * Create new synthetic variable with given flags, name, type, owner
   652      */
   653     private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) {
   654         return new VarSymbol(flags | SYNTHETIC, name, type, owner);
   655     }
   657     /**
   658      * Set varargsElement field on a given tree (must be either a new class tree
   659      * or a method call tree)
   660      */
   661     private void setVarargsIfNeeded(JCTree tree, Type varargsElement) {
   662         if (varargsElement != null) {
   663             switch (tree.getTag()) {
   664                 case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break;
   665                 case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break;
   666                 default: throw new AssertionError();
   667             }
   668         }
   669     }
   671     /**
   672      * Convert method/constructor arguments by inserting appropriate cast
   673      * as required by type-erasure - this is needed when bridging a lambda/method
   674      * reference, as the bridged signature might require downcast to be compatible
   675      * with the generated signature.
   676      */
   677     private List<JCExpression> convertArgs(Symbol meth, List<JCExpression> args, Type varargsElement) {
   678        Assert.check(meth.kind == Kinds.MTH);
   679        List<Type> formals = types.erasure(meth.type).getParameterTypes();
   680        if (varargsElement != null) {
   681            Assert.check((meth.flags() & VARARGS) != 0);
   682        }
   683        return transTypes.translateArgs(args, formals, varargsElement, attrEnv);
   684     }
   686     // </editor-fold>
   688     /**
   689      * Generate an adapter method "bridge" for a method reference which cannot
   690      * be used directly.
   691      */
   692     private class MemberReferenceBridger {
   694         private final JCMemberReference tree;
   695         private final ReferenceTranslationContext localContext;
   696         private final ListBuffer<JCExpression> args = ListBuffer.lb();
   697         private final ListBuffer<JCVariableDecl> params = ListBuffer.lb();
   699         MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
   700             this.tree = tree;
   701             this.localContext = localContext;
   702         }
   704         /**
   705          * Generate the bridge
   706          */
   707         JCMethodDecl bridge() {
   708             int prevPos = make.pos;
   709             try {
   710                 make.at(tree);
   711                 Type samDesc = localContext.bridgedRefSig();
   712                 List<Type> samPTypes = samDesc.getParameterTypes();
   714                 //an extra argument is prepended to the signature of the bridge in case
   715                 //the member reference is an instance method reference (in which case
   716                 //the receiver expression is passed to the bridge itself).
   717                 Type recType = null;
   718                 switch (tree.kind) {
   719                     case IMPLICIT_INNER:
   720                         recType = tree.sym.owner.type.getEnclosingType();
   721                         break;
   722                     case BOUND:
   723                         recType = tree.getQualifierExpression().type;
   724                         break;
   725                     case UNBOUND:
   726                         recType = samPTypes.head;
   727                         samPTypes = samPTypes.tail;
   728                         break;
   729                 }
   731                 //generate the parameter list for the bridged member reference - the
   732                 //bridge signature will match the signature of the target sam descriptor
   734                 VarSymbol rcvr = (recType == null)
   735                         ? null
   736                         : addParameter("rec$", recType, false);
   738                 List<Type> refPTypes = tree.sym.type.getParameterTypes();
   739                 int refSize = refPTypes.size();
   740                 int samSize = samPTypes.size();
   741                 int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;   // Last parameter to copy from referenced method
   743                 List<Type> l = refPTypes;
   744                 // Use parameter types of the referenced method, excluding final var args
   745                 for (int i = 0; l.nonEmpty() && i < last; ++i) {
   746                     addParameter("x$" + i, l.head, true);
   747                     l = l.tail;
   748                 }
   749                 // Flatten out the var args
   750                 for (int i = last; i < samSize; ++i) {
   751                     addParameter("xva$" + i, tree.varargsElement, true);
   752                 }
   754                 //generate the bridge method declaration
   755                 JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
   756                         localContext.bridgeSym.name,
   757                         make.QualIdent(samDesc.getReturnType().tsym),
   758                         List.<JCTypeParameter>nil(),
   759                         params.toList(),
   760                         tree.sym.type.getThrownTypes() == null
   761                         ? List.<JCExpression>nil()
   762                         : make.Types(tree.sym.type.getThrownTypes()),
   763                         null,
   764                         null);
   765                 bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
   766                 bridgeDecl.type = localContext.bridgeSym.type = types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
   768                 //bridge method body generation - this can be either a method call or a
   769                 //new instance creation expression, depending on the member reference kind
   770                 JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
   771                         ? bridgeExpressionInvoke(rcvr)
   772                         : bridgeExpressionNew();
   774                 //the body is either a return expression containing a method call,
   775                 //or the method call itself, depending on whether the return type of
   776                 //the bridge is non-void/void.
   777                 bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
   779                 return bridgeDecl;
   780             } finally {
   781                 make.at(prevPos);
   782             }
   783         }
   785         /**
   786          * determine the receiver of the bridged method call - the receiver can
   787          * be either the synthetic receiver parameter or a type qualifier; the
   788          * original qualifier expression is never used here, as it might refer
   789          * to symbols not available in the static context of the bridge
   790          */
   791         private JCExpression bridgeExpressionInvoke(VarSymbol rcvr) {
   792             JCExpression qualifier =
   793                     tree.sym.isStatic() ?
   794                         make.Type(tree.sym.owner.type) :
   795                         (rcvr != null) ?
   796                             make.Ident(rcvr) :
   797                             tree.getQualifierExpression();
   799             //create the qualifier expression
   800             JCFieldAccess select = make.Select(qualifier, tree.sym.name);
   801             select.sym = tree.sym;
   802             select.type = tree.sym.erasure(types);
   804             //create the method call expression
   805             JCExpression apply = make.Apply(List.<JCExpression>nil(), select,
   806                     convertArgs(tree.sym, args.toList(), tree.varargsElement)).setType(tree.sym.erasure(types).getReturnType());
   808             apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
   809             setVarargsIfNeeded(apply, tree.varargsElement);
   810             return apply;
   811         }
   813         /**
   814          * the enclosing expression is either 'null' (no enclosing type) or set
   815          * to the first bridge synthetic parameter
   816          */
   817         private JCExpression bridgeExpressionNew() {
   818             if (tree.kind == ReferenceKind.ARRAY_CTOR) {
   819                 //create the array creation expression
   820                 JCNewArray newArr = make.NewArray(make.Type(types.elemtype(tree.getQualifierExpression().type)),
   821                         List.of(make.Ident(params.first())),
   822                         null);
   823                 newArr.type = tree.getQualifierExpression().type;
   824                 return newArr;
   825             } else {
   826                 JCExpression encl = null;
   827                 switch (tree.kind) {
   828                     case UNBOUND:
   829                     case IMPLICIT_INNER:
   830                         encl = make.Ident(params.first());
   831                 }
   833                 //create the instance creation expression
   834                 JCNewClass newClass = make.NewClass(encl,
   835                         List.<JCExpression>nil(),
   836                         make.Type(tree.getQualifierExpression().type),
   837                         convertArgs(tree.sym, args.toList(), tree.varargsElement),
   838                         null);
   839                 newClass.constructor = tree.sym;
   840                 newClass.constructorType = tree.sym.erasure(types);
   841                 newClass.type = tree.getQualifierExpression().type;
   842                 setVarargsIfNeeded(newClass, tree.varargsElement);
   843                 return newClass;
   844             }
   845         }
   847         private VarSymbol addParameter(String name, Type p, boolean genArg) {
   848             VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
   849             params.append(make.VarDef(vsym, null));
   850             if (genArg) {
   851                 args.append(make.Ident(vsym));
   852             }
   853             return vsym;
   854         }
   855     }
   857     /**
   858      * Bridges a member reference - this is needed when:
   859      * * Var args in the referenced method need to be flattened away
   860      * * super is used
   861      */
   862     private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
   863         kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
   864     }
   866     /**
   867      * Generate an indy method call to the meta factory
   868      */
   869     private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory,
   870             boolean isSerializable, int refKind, Symbol refSym, List<JCExpression> indy_args) {
   871         //determine the static bsm args
   872         Type mtype = types.erasure(tree.descriptorType);
   873         MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
   874         List<Object> staticArgs = List.<Object>of(
   875                 new Pool.MethodHandle(ClassFile.REF_invokeInterface, types.findDescriptorSymbol(tree.type.tsym), types),
   876                 new Pool.MethodHandle(refKind, refSym, types),
   877                 new MethodType(mtype.getParameterTypes(),
   878                         mtype.getReturnType(),
   879                         mtype.getThrownTypes(),
   880                         syms.methodClass));
   882         //computed indy arg types
   883         ListBuffer<Type> indy_args_types = ListBuffer.lb();
   884         for (JCExpression arg : indy_args) {
   885             indy_args_types.append(arg.type);
   886         }
   888         //finally, compute the type of the indy call
   889         MethodType indyType = new MethodType(indy_args_types.toList(),
   890                 tree.type,
   891                 List.<Type>nil(),
   892                 syms.methodClass);
   894         Name metafactoryName = needsAltMetafactory ?
   895                 names.altMetaFactory : names.metaFactory;
   897         if (needsAltMetafactory) {
   898             ListBuffer<Object> markers = ListBuffer.lb();
   899             for (Symbol t : tree.targets.tail) {
   900                 if (t != syms.serializableType.tsym) {
   901                     markers.append(t);
   902                 }
   903             }
   904             int flags = isSerializable? FLAG_SERIALIZABLE : 0;
   905             boolean hasMarkers = markers.nonEmpty();
   906             flags |= hasMarkers ? FLAG_MARKERS : 0;
   907             staticArgs = staticArgs.append(flags);
   908             if (hasMarkers) {
   909                 staticArgs = staticArgs.append(markers.length());
   910                 staticArgs = staticArgs.appendList(markers.toList());
   911             }
   912             if (isSerializable) {
   913                 addDeserializationCase(refKind, refSym, tree.type, samSym,
   914                         tree, staticArgs, indyType);
   915             }
   916         }
   918         return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args);
   919     }
   921     /**
   922      * Generate an indy method call with given name, type and static bootstrap
   923      * arguments types
   924      */
   925     private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, List<Object> staticArgs, MethodType indyType, List<JCExpression> indyArgs) {
   926         int prevPos = make.pos;
   927         try {
   928             make.at(pos);
   929             List<Type> bsm_staticArgs = List.of(syms.methodHandleLookupType,
   930                     syms.stringType,
   931                     syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs));
   933             Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site,
   934                     bsmName, bsm_staticArgs, List.<Type>nil());
   936             DynamicMethodSymbol dynSym =
   937                     new DynamicMethodSymbol(names.lambda,
   938                                             syms.noSymbol,
   939                                             bsm.isStatic() ? ClassFile.REF_invokeStatic : ClassFile.REF_invokeVirtual,
   940                                             (MethodSymbol)bsm,
   941                                             indyType,
   942                                             staticArgs.toArray());
   944             JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName);
   945             qualifier.sym = dynSym;
   946             qualifier.type = indyType.getReturnType();
   948             JCMethodInvocation proxyCall = make.Apply(List.<JCExpression>nil(), qualifier, indyArgs);
   949             proxyCall.type = indyType.getReturnType();
   950             return proxyCall;
   951         } finally {
   952             make.at(prevPos);
   953         }
   954     }
   955     //where
   956     private List<Type> bsmStaticArgToTypes(List<Object> args) {
   957         ListBuffer<Type> argtypes = ListBuffer.lb();
   958         for (Object arg : args) {
   959             argtypes.append(bsmStaticArgToType(arg));
   960         }
   961         return argtypes.toList();
   962     }
   964     private Type bsmStaticArgToType(Object arg) {
   965         Assert.checkNonNull(arg);
   966         if (arg instanceof ClassSymbol) {
   967             return syms.classType;
   968         } else if (arg instanceof Integer) {
   969             return syms.intType;
   970         } else if (arg instanceof Long) {
   971             return syms.longType;
   972         } else if (arg instanceof Float) {
   973             return syms.floatType;
   974         } else if (arg instanceof Double) {
   975             return syms.doubleType;
   976         } else if (arg instanceof String) {
   977             return syms.stringType;
   978         } else if (arg instanceof Pool.MethodHandle) {
   979             return syms.methodHandleType;
   980         } else if (arg instanceof MethodType) {
   981             return syms.methodTypeType;
   982         } else {
   983             Assert.error("bad static arg " + arg.getClass());
   984             return null;
   985         }
   986     }
   988     /**
   989      * Get the opcode associated with this method reference
   990      */
   991     private int referenceKind(Symbol refSym) {
   992         if (refSym.isConstructor()) {
   993             return ClassFile.REF_newInvokeSpecial;
   994         } else {
   995             if (refSym.isStatic()) {
   996                 return ClassFile.REF_invokeStatic;
   997             } else if (refSym.enclClass().isInterface()) {
   998                 return ClassFile.REF_invokeInterface;
   999             } else {
  1000                 return ClassFile.REF_invokeVirtual;
  1005     // </editor-fold>
  1007     // <editor-fold defaultstate="collapsed" desc="Lambda/reference analyzer">\
  1008     /**
  1009      * This visitor collects information about translation of a lambda expression.
  1010      * More specifically, it keeps track of the enclosing contexts and captured locals
  1011      * accessed by the lambda being translated (as well as other useful info).
  1012      */
  1013     class LambdaAnalyzer extends TreeScanner {
  1015         /** the frame stack - used to reconstruct translation info about enclosing scopes */
  1016         private List<Frame> frameStack;
  1018         /**
  1019          * keep the count of lambda expression (used to generate unambiguous
  1020          * names)
  1021          */
  1022         private int lambdaCount = 0;
  1024         /**
  1025          * keep the count of lambda expression defined in given context (used to
  1026          * generate unambiguous names for serializable lambdas)
  1027          */
  1028         private Map<String, Integer> serializableLambdaCounts =
  1029                 new HashMap<String, Integer>();
  1031         /**
  1032          * maps for fake clinit symbols to be used as owners of lambda occurring in
  1033          * a static var init context
  1034          */
  1035         private Map<ClassSymbol, Symbol> clinits =
  1036                 new HashMap<ClassSymbol, Symbol>();
  1038         private void analyzeClass(JCClassDecl tree) {
  1039             frameStack = List.nil();
  1040             scan(tree);
  1043         @Override
  1044         public void visitBlock(JCBlock tree) {
  1045             List<Frame> prevStack = frameStack;
  1046             try {
  1047                 if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) {
  1048                     frameStack = frameStack.prepend(new Frame(tree));
  1050                 super.visitBlock(tree);
  1052             finally {
  1053                 frameStack = prevStack;
  1057         @Override
  1058         public void visitClassDef(JCClassDecl tree) {
  1059             List<Frame> prevStack = frameStack;
  1060             Map<String, Integer> prevSerializableLambdaCount = serializableLambdaCounts;
  1061             Map<ClassSymbol, Symbol> prevClinits = clinits;
  1062             try {
  1063                 serializableLambdaCounts = new HashMap<String, Integer>();
  1064                 prevClinits = new HashMap<ClassSymbol, Symbol>();
  1065                 if (directlyEnclosingLambda() != null) {
  1066                     tree.sym.owner = owner();
  1067                     LambdaTranslationContext lambdaContext = (LambdaTranslationContext) contextMap.get(directlyEnclosingLambda());
  1068                     Type encl = lambdaContext.enclosingType();
  1069                     if (encl.hasTag(NONE)) {
  1070                         //if the translated lambda body occurs in a static context,
  1071                         //any class declaration within it must be made static
  1072                         //@@@TODO: What about nested classes within lambda?
  1073                         tree.sym.flags_field |= STATIC;
  1074                         ((ClassType) tree.sym.type).setEnclosingType(Type.noType);
  1075                     } else {
  1076                         //if the translated lambda body is in an instance context
  1077                         //the enclosing type of any class declaration within it
  1078                         //must be updated to point to the new enclosing type (if any)
  1079                         ((ClassType) tree.sym.type).setEnclosingType(encl);
  1082                 frameStack = frameStack.prepend(new Frame(tree));
  1083                 super.visitClassDef(tree);
  1085             finally {
  1086                 frameStack = prevStack;
  1087                 serializableLambdaCounts = prevSerializableLambdaCount;
  1088                 clinits = prevClinits;
  1090             if (!tree.sym.isStatic() && directlyEnclosingLambda() != null) {
  1091                 // Any (non-static) class defined within a lambda is an implicit 'this' reference
  1092                 // because its constructor will reference the enclosing class
  1093                 ((LambdaTranslationContext) context()).addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS);
  1097         @Override
  1098         public void visitIdent(JCIdent tree) {
  1099             if (context() != null && lambdaIdentSymbolFilter(tree.sym)) {
  1100                 if (tree.sym.kind == VAR &&
  1101                         tree.sym.owner.kind == MTH &&
  1102                         tree.type.constValue() == null) {
  1103                     TranslationContext<?> localContext = context();
  1104                     while (localContext != null) {
  1105                         if (localContext.tree.getTag() == LAMBDA) {
  1106                             JCTree block = capturedDecl(localContext.depth, tree.sym);
  1107                             if (block == null) break;
  1108                             ((LambdaTranslationContext)localContext).addSymbol(tree.sym, CAPTURED_VAR);
  1110                         localContext = localContext.prev;
  1112                 } else if (tree.sym.owner.kind == TYP) {
  1113                     TranslationContext<?> localContext = context();
  1114                     while (localContext != null) {
  1115                         if (localContext.tree.hasTag(LAMBDA)) {
  1116                             JCTree block = capturedDecl(localContext.depth, tree.sym);
  1117                             if (block == null) break;
  1118                             switch (block.getTag()) {
  1119                                 case CLASSDEF:
  1120                                     JCClassDecl cdecl = (JCClassDecl)block;
  1121                                     ((LambdaTranslationContext)localContext).addSymbol(cdecl.sym, CAPTURED_THIS);
  1122                                     break;
  1123                                 default:
  1124                                     Assert.error("bad block kind");
  1127                         localContext = localContext.prev;
  1131             super.visitIdent(tree);
  1134         @Override
  1135         public void visitLambda(JCLambda tree) {
  1136             List<Frame> prevStack = frameStack;
  1137             try {
  1138                 LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree);
  1139                 frameStack = frameStack.prepend(new Frame(tree));
  1140                 for (JCVariableDecl param : tree.params) {
  1141                     context.addSymbol(param.sym, PARAM);
  1142                     frameStack.head.addLocal(param.sym);
  1144                 contextMap.put(tree, context);
  1145                 scan(tree.body);
  1146                 context.complete();
  1148             finally {
  1149                 frameStack = prevStack;
  1153         @Override
  1154         public void visitMethodDef(JCMethodDecl tree) {
  1155             List<Frame> prevStack = frameStack;
  1156             try {
  1157                 frameStack = frameStack.prepend(new Frame(tree));
  1158                 super.visitMethodDef(tree);
  1160             finally {
  1161                 frameStack = prevStack;
  1165         @Override
  1166         public void visitNewClass(JCNewClass tree) {
  1167             if (lambdaNewClassFilter(context(), tree)) {
  1168                 ((LambdaTranslationContext) context()).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.get(frameStack2.head.tree)).translatedSym;
  1282                     default:
  1283                         frameStack2 = frameStack2.tail;
  1286             Assert.error();
  1287             return null;
  1290         private Symbol initSym(ClassSymbol csym, long flags) {
  1291             boolean isStatic = (flags & STATIC) != 0;
  1292             if (isStatic) {
  1293                 //static clinits are generated in Gen - so we need to fake them
  1294                 Symbol clinit = clinits.get(csym);
  1295                 if (clinit == null) {
  1296                     clinit = makeSyntheticMethod(STATIC,
  1297                             names.clinit,
  1298                             new MethodType(List.<Type>nil(), syms.voidType, List.<Type>nil(), syms.methodClass),
  1299                             csym);
  1300                     clinits.put(csym, clinit);
  1302                 return clinit;
  1303             } else {
  1304                 //get the first constructor and treat it as the instance init sym
  1305                 for (Symbol s : csym.members_field.getElementsByName(names.init)) {
  1306                     return s;
  1309             Assert.error("init not found");
  1310             return null;
  1313         private JCTree directlyEnclosingLambda() {
  1314             if (frameStack.isEmpty()) {
  1315                 return null;
  1317             List<Frame> frameStack2 = frameStack;
  1318             while (frameStack2.nonEmpty()) {
  1319                 switch (frameStack2.head.tree.getTag()) {
  1320                     case CLASSDEF:
  1321                     case METHODDEF:
  1322                         return null;
  1323                     case LAMBDA:
  1324                         return frameStack2.head.tree;
  1325                     default:
  1326                         frameStack2 = frameStack2.tail;
  1329             Assert.error();
  1330             return null;
  1333         private boolean inClassWithinLambda() {
  1334             if (frameStack.isEmpty()) {
  1335                 return false;
  1337             List<Frame> frameStack2 = frameStack;
  1338             boolean classFound = false;
  1339             while (frameStack2.nonEmpty()) {
  1340                 switch (frameStack2.head.tree.getTag()) {
  1341                     case LAMBDA:
  1342                         return classFound;
  1343                     case CLASSDEF:
  1344                         classFound = true;
  1345                         frameStack2 = frameStack2.tail;
  1346                         break;
  1347                     default:
  1348                         frameStack2 = frameStack2.tail;
  1351             // No lambda
  1352             return false;
  1355         /**
  1356          * Return the declaration corresponding to a symbol in the enclosing
  1357          * scope; the depth parameter is used to filter out symbols defined
  1358          * in nested scopes (which do not need to undergo capture).
  1359          */
  1360         private JCTree capturedDecl(int depth, Symbol sym) {
  1361             int currentDepth = frameStack.size() - 1;
  1362             for (Frame block : frameStack) {
  1363                 switch (block.tree.getTag()) {
  1364                     case CLASSDEF:
  1365                         ClassSymbol clazz = ((JCClassDecl)block.tree).sym;
  1366                         if (sym.isMemberOf(clazz, types)) {
  1367                             return currentDepth > depth ? null : block.tree;
  1369                         break;
  1370                     case VARDEF:
  1371                         if (((JCVariableDecl)block.tree).sym == sym &&
  1372                                 sym.owner.kind == MTH) { //only locals are captured
  1373                             return currentDepth > depth ? null : block.tree;
  1375                         break;
  1376                     case BLOCK:
  1377                     case METHODDEF:
  1378                     case LAMBDA:
  1379                         if (block.locals != null && block.locals.contains(sym)) {
  1380                             return currentDepth > depth ? null : block.tree;
  1382                         break;
  1383                     default:
  1384                         Assert.error("bad decl kind " + block.tree.getTag());
  1386                 currentDepth--;
  1388             return null;
  1391         private TranslationContext<?> context() {
  1392             for (Frame frame : frameStack) {
  1393                 TranslationContext<?> context = contextMap.get(frame.tree);
  1394                 if (context != null) {
  1395                     return context;
  1398             return null;
  1401         /**
  1402          *  This is used to filter out those identifiers that needs to be adjusted
  1403          *  when translating away lambda expressions
  1404          */
  1405         private boolean lambdaIdentSymbolFilter(Symbol sym) {
  1406             return (sym.kind == VAR || sym.kind == MTH)
  1407                     && !sym.isStatic()
  1408                     && sym.name != names.init;
  1411         private boolean lambdaSelectSymbolFilter(Symbol sym) {
  1412             return (sym.kind == VAR || sym.kind == MTH) &&
  1413                         !sym.isStatic() &&
  1414                         (sym.name == names._this ||
  1415                         sym.name == names._super);
  1418         /**
  1419          * This is used to filter out those new class expressions that need to
  1420          * be qualified with an enclosing tree
  1421          */
  1422         private boolean lambdaNewClassFilter(TranslationContext<?> context, JCNewClass tree) {
  1423             if (context != null
  1424                     && tree.encl == null
  1425                     && tree.def == null
  1426                     && !tree.type.getEnclosingType().hasTag(NONE)) {
  1427                 Type encl = tree.type.getEnclosingType();
  1428                 Type current = context.owner.enclClass().type;
  1429                 while (!current.hasTag(NONE)) {
  1430                     if (current.tsym.isSubClass(encl.tsym, types)) {
  1431                         return true;
  1433                     current = current.getEnclosingType();
  1435                 return false;
  1436             } else {
  1437                 return false;
  1441         private TranslationContext<JCLambda> makeLambdaContext(JCLambda tree) {
  1442             return new LambdaTranslationContext(tree);
  1445         private TranslationContext<JCMemberReference> makeReferenceContext(JCMemberReference tree) {
  1446             return new ReferenceTranslationContext(tree);
  1449         private class Frame {
  1450             final JCTree tree;
  1451             List<Symbol> locals;
  1453             public Frame(JCTree tree) {
  1454                 this.tree = tree;
  1457             void addLocal(Symbol sym) {
  1458                 if (locals == null) {
  1459                     locals = List.nil();
  1461                 locals = locals.prepend(sym);
  1465         /**
  1466          * This class is used to store important information regarding translation of
  1467          * lambda expression/method references (see subclasses).
  1468          */
  1469         private abstract class TranslationContext<T extends JCFunctionalExpression> {
  1471             /** the underlying (untranslated) tree */
  1472             T tree;
  1474             /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */
  1475             Symbol owner;
  1477             /** the depth of this lambda expression in the frame stack */
  1478             int depth;
  1480             /** the enclosing translation context (set for nested lambdas/mref) */
  1481             TranslationContext<?> prev;
  1483             TranslationContext(T tree) {
  1484                 this.tree = tree;
  1485                 this.owner = owner();
  1486                 this.depth = frameStack.size() - 1;
  1487                 this.prev = context();
  1490             /** does this functional expression need to be created using alternate metafactory? */
  1491             boolean needsAltMetafactory() {
  1492                 return (tree.targets.length() > 1 ||
  1493                         isSerializable());
  1496             /** does this functional expression require serialization support? */
  1497             boolean isSerializable() {
  1498                 for (Symbol target : tree.targets) {
  1499                     if (types.asSuper(target.type, syms.serializableType.tsym) != null) {
  1500                         return true;
  1503                 return false;
  1507         /**
  1508          * This class retains all the useful information about a lambda expression;
  1509          * the contents of this class are filled by the LambdaAnalyzer visitor,
  1510          * and the used by the main translation routines in order to adjust references
  1511          * to captured locals/members, etc.
  1512          */
  1513         private class LambdaTranslationContext extends TranslationContext<JCLambda> {
  1515             /** variable in the enclosing context to which this lambda is assigned */
  1516             Symbol self;
  1518             /** map from original to translated lambda parameters */
  1519             Map<Symbol, Symbol> lambdaParams = new LinkedHashMap<Symbol, Symbol>();
  1521             /** map from original to translated lambda locals */
  1522             Map<Symbol, Symbol> lambdaLocals = new LinkedHashMap<Symbol, Symbol>();
  1524             /** map from variables in enclosing scope to translated synthetic parameters */
  1525             Map<Symbol, Symbol> capturedLocals  = new LinkedHashMap<Symbol, Symbol>();
  1527             /** map from class symbols to translated synthetic parameters (for captured member access) */
  1528             Map<Symbol, Symbol> capturedThis = new LinkedHashMap<Symbol, Symbol>();
  1530             /** map from original to translated lambda locals */
  1531             Map<Symbol, Symbol> typeVars = new LinkedHashMap<Symbol, Symbol>();
  1533             /** the synthetic symbol for the method hoisting the translated lambda */
  1534             Symbol translatedSym;
  1536             List<JCVariableDecl> syntheticParams;
  1538             LambdaTranslationContext(JCLambda tree) {
  1539                 super(tree);
  1540                 Frame frame = frameStack.head;
  1541                 if (frame.tree.hasTag(VARDEF)) {
  1542                     self = ((JCVariableDecl)frame.tree).sym;
  1544                 Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName();
  1545                 this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass());
  1548             /**
  1549              * Translate a symbol of a given kind into something suitable for the
  1550              * synthetic lambda body
  1551              */
  1552             Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) {
  1553                 switch (skind) {
  1554                     case CAPTURED_THIS:
  1555                         return sym;  // self represented
  1556                     case TYPE_VAR:
  1557                         // Just erase the type var
  1558                         return new VarSymbol(sym.flags(), names.fromString(name), types.erasure(sym.type), sym.owner);
  1559                     default:
  1560                         return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym);
  1564             void addSymbol(Symbol sym, LambdaSymbolKind skind) {
  1565                 Map<Symbol, Symbol> transMap = null;
  1566                 String preferredName;
  1567                 switch (skind) {
  1568                     case CAPTURED_THIS:
  1569                         transMap = capturedThis;
  1570                         preferredName = "encl$" + capturedThis.size();
  1571                         break;
  1572                     case CAPTURED_VAR:
  1573                         transMap = capturedLocals;
  1574                         preferredName = "cap$" + capturedLocals.size();
  1575                         break;
  1576                     case LOCAL_VAR:
  1577                         transMap = lambdaLocals;
  1578                         preferredName = sym.name.toString();
  1579                         break;
  1580                     case PARAM:
  1581                         transMap = lambdaParams;
  1582                         preferredName = sym.name.toString();
  1583                         break;
  1584                     case TYPE_VAR:
  1585                         transMap = typeVars;
  1586                         preferredName = sym.name.toString();
  1587                         break;
  1588                     default: throw new AssertionError();
  1590                 if (!transMap.containsKey(sym)) {
  1591                     transMap.put(sym, translate(preferredName, sym, skind));
  1595             Map<Symbol, Symbol> getSymbolMap(LambdaSymbolKind... skinds) {
  1596                 LinkedHashMap<Symbol, Symbol> translationMap = new LinkedHashMap<Symbol, Symbol>();
  1597                 for (LambdaSymbolKind skind : skinds) {
  1598                     switch (skind) {
  1599                         case CAPTURED_THIS:
  1600                             translationMap.putAll(capturedThis);
  1601                             break;
  1602                         case CAPTURED_VAR:
  1603                             translationMap.putAll(capturedLocals);
  1604                             break;
  1605                         case LOCAL_VAR:
  1606                             translationMap.putAll(lambdaLocals);
  1607                             break;
  1608                         case PARAM:
  1609                             translationMap.putAll(lambdaParams);
  1610                             break;
  1611                         case TYPE_VAR:
  1612                             translationMap.putAll(typeVars);
  1613                             break;
  1614                         default: throw new AssertionError();
  1617                 return translationMap;
  1620             /**
  1621              * The translatedSym is not complete/accurate until the analysis is
  1622              * finished.  Once the analysis is finished, the translatedSym is
  1623              * "completed" -- updated with type information, access modifiers,
  1624              * and full parameter list.
  1625              */
  1626             void complete() {
  1627                 if (syntheticParams != null) {
  1628                     return;
  1630                 boolean inInterface = translatedSym.owner.isInterface();
  1631                 boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty();
  1632                 boolean needInstance = thisReferenced || inInterface;
  1634                 // If instance access isn't needed, make it static
  1635                 // Interface methods much be public default methods, otherwise make it private
  1636                 translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) | (inInterface? PUBLIC | DEFAULT : PRIVATE);
  1638                 //compute synthetic params
  1639                 ListBuffer<JCVariableDecl> params = ListBuffer.lb();
  1641                 // The signature of the method is augmented with the following
  1642                 // synthetic parameters:
  1643                 //
  1644                 // 1) reference to enclosing contexts captured by the lambda expression
  1645                 // 2) enclosing locals captured by the lambda expression
  1646                 for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) {
  1647                     params.append(make.VarDef((VarSymbol) thisSym, null));
  1650                 syntheticParams = params.toList();
  1652                 //prepend synthetic args to translated lambda method signature
  1653                 translatedSym.type = types.createMethodTypeWithParameters(
  1654                         generatedLambdaSig(),
  1655                         TreeInfo.types(syntheticParams));
  1658             Type enclosingType() {
  1659                 return owner.isStatic() ?
  1660                         Type.noType :
  1661                         owner.enclClass().type;
  1664             Type generatedLambdaSig() {
  1665                 return types.erasure(tree.descriptorType);
  1669         /**
  1670          * This class retains all the useful information about a method reference;
  1671          * the contents of this class are filled by the LambdaAnalyzer visitor,
  1672          * and the used by the main translation routines in order to adjust method
  1673          * references (i.e. in case a bridge is needed)
  1674          */
  1675         private class ReferenceTranslationContext extends TranslationContext<JCMemberReference> {
  1677             final boolean isSuper;
  1678             final Symbol bridgeSym;
  1680             ReferenceTranslationContext(JCMemberReference tree) {
  1681                 super(tree);
  1682                 this.isSuper = tree.hasKind(ReferenceKind.SUPER);
  1683                 this.bridgeSym = needsBridge()
  1684                         ? makeSyntheticMethod(isSuper ? 0 : STATIC,
  1685                                               lambdaName().append(names.fromString("$bridge")), null,
  1686                                               owner.enclClass())
  1687                         : null;
  1690             /**
  1691              * Get the opcode associated with this method reference
  1692              */
  1693             int referenceKind() {
  1694                 return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym);
  1697             boolean needsVarArgsConversion() {
  1698                 return tree.varargsElement != null;
  1701             /**
  1702              * @return Is this an array operation like clone()
  1703              */
  1704             boolean isArrayOp() {
  1705                 return tree.sym.owner == syms.arrayClass;
  1708             /**
  1709              * Does this reference needs a bridge (i.e. var args need to be
  1710              * expanded or "super" is used)
  1711              */
  1712             final boolean needsBridge() {
  1713                 return isSuper || needsVarArgsConversion() || isArrayOp();
  1716             Type generatedRefSig() {
  1717                 return types.erasure(tree.sym.type);
  1720             Type bridgedRefSig() {
  1721                 return types.erasure(types.findDescriptorSymbol(tree.targets.head).type);
  1725     // </editor-fold>
  1727     enum LambdaSymbolKind {
  1728         CAPTURED_VAR,
  1729         CAPTURED_THIS,
  1730         LOCAL_VAR,
  1731         PARAM,
  1732         TYPE_VAR;
  1735     /**
  1736      * ****************************************************************
  1737      * Signature Generation
  1738      * ****************************************************************
  1739      */
  1741     private String methodSig(Type type) {
  1742         L2MSignatureGenerator sg = new L2MSignatureGenerator();
  1743         sg.assembleSig(type);
  1744         return sg.toString();
  1747     private String classSig(Type type) {
  1748         L2MSignatureGenerator sg = new L2MSignatureGenerator();
  1749         sg.assembleClassSig(type);
  1750         return sg.toString();
  1753     /**
  1754      * Signature Generation
  1755      */
  1756     private class L2MSignatureGenerator extends Types.SignatureGenerator {
  1758         /**
  1759          * An output buffer for type signatures.
  1760          */
  1761         StringBuilder sb = new StringBuilder();
  1763         L2MSignatureGenerator() {
  1764             super(types);
  1767         @Override
  1768         protected void append(char ch) {
  1769             sb.append(ch);
  1772         @Override
  1773         protected void append(byte[] ba) {
  1774             sb.append(new String(ba));
  1777         @Override
  1778         protected void append(Name name) {
  1779             sb.append(name.toString());
  1782         @Override
  1783         public String toString() {
  1784             return sb.toString();

mercurial