rfield@1380: /* mcimadamore@1595: * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. rfield@1380: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. rfield@1380: * rfield@1380: * This code is free software; you can redistribute it and/or modify it rfield@1380: * under the terms of the GNU General Public License version 2 only, as rfield@1380: * published by the Free Software Foundation. Oracle designates this rfield@1380: * particular file as subject to the "Classpath" exception as provided rfield@1380: * by Oracle in the LICENSE file that accompanied this code. rfield@1380: * rfield@1380: * This code is distributed in the hope that it will be useful, but WITHOUT rfield@1380: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or rfield@1380: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License rfield@1380: * version 2 for more details (a copy is included in the LICENSE file that rfield@1380: * accompanied this code). rfield@1380: * rfield@1380: * You should have received a copy of the GNU General Public License version rfield@1380: * 2 along with this work; if not, write to the Free Software Foundation, rfield@1380: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. rfield@1380: * rfield@1380: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA rfield@1380: * or visit www.oracle.com if you need additional information or have any rfield@1380: * questions. rfield@1380: */ rfield@1380: package com.sun.tools.javac.comp; rfield@1380: rfield@1380: import com.sun.tools.javac.tree.*; rfield@1380: import com.sun.tools.javac.tree.JCTree; rfield@1380: import com.sun.tools.javac.tree.JCTree.*; rfield@1380: import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; rfield@1380: import com.sun.tools.javac.tree.TreeMaker; rfield@1380: import com.sun.tools.javac.tree.TreeScanner; rfield@1380: import com.sun.tools.javac.tree.TreeTranslator; rfield@1380: import com.sun.tools.javac.code.Kinds; rfield@1587: import com.sun.tools.javac.code.Scope; rfield@1380: import com.sun.tools.javac.code.Symbol; rfield@1380: import com.sun.tools.javac.code.Symbol.ClassSymbol; rfield@1380: import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol; rfield@1380: import com.sun.tools.javac.code.Symbol.MethodSymbol; rfield@1380: import com.sun.tools.javac.code.Symbol.VarSymbol; rfield@1380: import com.sun.tools.javac.code.Symtab; rfield@1380: import com.sun.tools.javac.code.Type; rfield@1380: import com.sun.tools.javac.code.Type.ClassType; rfield@1380: import com.sun.tools.javac.code.Type.MethodType; rfield@1380: import com.sun.tools.javac.code.Types; rfield@1380: import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzer.*; rfield@1380: import com.sun.tools.javac.jvm.*; rfield@1380: import com.sun.tools.javac.util.*; rfield@1380: import com.sun.tools.javac.util.List; rfield@1380: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; rfield@1380: import com.sun.source.tree.MemberReferenceTree.ReferenceMode; rfield@1380: rfield@1380: import java.util.HashMap; rfield@1380: import java.util.LinkedHashMap; rfield@1380: import java.util.Map; rfield@1380: rfield@1380: import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*; rfield@1380: import static com.sun.tools.javac.code.Flags.*; rfield@1380: import static com.sun.tools.javac.code.Kinds.*; rfield@1587: import static com.sun.tools.javac.code.TypeTag.*; rfield@1380: import static com.sun.tools.javac.tree.JCTree.Tag.*; rfield@1380: rfield@1380: /** rfield@1380: * This pass desugars lambda expressions into static methods rfield@1380: * rfield@1380: *

This is NOT part of any supported API. rfield@1380: * If you write code that depends on this, you do so at your own risk. rfield@1380: * This code and its internal interfaces are subject to change or rfield@1380: * deletion without notice. rfield@1380: */ rfield@1380: public class LambdaToMethod extends TreeTranslator { rfield@1380: rfield@1380: private Names names; rfield@1380: private Symtab syms; rfield@1380: private Resolve rs; rfield@1380: private TreeMaker make; rfield@1380: private Types types; rfield@1380: private TransTypes transTypes; rfield@1380: private Env attrEnv; rfield@1380: rfield@1380: /** the analyzer scanner */ rfield@1380: private LambdaAnalyzer analyzer; rfield@1380: rfield@1380: /** map from lambda trees to translation contexts */ rfield@1380: private Map> contextMap; rfield@1380: rfield@1380: /** current translation context (visitor argument) */ rfield@1380: private TranslationContext context; rfield@1380: rfield@1587: /** info about the current class being processed */ rfield@1587: private KlassInfo kInfo; rfield@1587: rfield@1587: /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */ rfield@1587: public static final int FLAG_SERIALIZABLE = 1 << 0; rfield@1587: rfield@1587: /** Flag for alternate metafactories indicating the lambda object has multiple targets */ rfield@1587: public static final int FLAG_MARKERS = 1 << 1; rfield@1587: rfield@1587: private class KlassInfo { rfield@1587: rfield@1587: /** rfield@1587: * list of methods to append rfield@1587: */ rfield@1587: private ListBuffer appendedMethodList; rfield@1587: rfield@1587: /** rfield@1587: * list of deserialization cases rfield@1587: */ rfield@1587: private final Map> deserializeCases; rfield@1587: rfield@1587: /** rfield@1587: * deserialize method symbol rfield@1587: */ rfield@1587: private final MethodSymbol deserMethodSym; rfield@1587: rfield@1587: /** rfield@1587: * deserialize method parameter symbol rfield@1587: */ rfield@1587: private final VarSymbol deserParamSym; rfield@1587: rfield@1587: private KlassInfo(Symbol kSym) { rfield@1587: appendedMethodList = ListBuffer.lb(); rfield@1587: deserializeCases = new HashMap>(); rfield@1587: long flags = PRIVATE | STATIC | SYNTHETIC; rfield@1587: MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType, rfield@1587: List.nil(), syms.methodClass); rfield@1587: deserMethodSym = makeSyntheticMethod(flags, names.deserializeLambda, type, kSym); mcimadamore@1595: deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"), mcimadamore@1595: syms.serializedLambdaType, deserMethodSym); rfield@1587: } rfield@1587: rfield@1587: private void addMethod(JCTree decl) { rfield@1587: appendedMethodList = appendedMethodList.prepend(decl); rfield@1587: } rfield@1587: } rfield@1380: rfield@1380: // rfield@1380: private static final Context.Key unlambdaKey = rfield@1380: new Context.Key(); rfield@1380: rfield@1380: public static LambdaToMethod instance(Context context) { rfield@1380: LambdaToMethod instance = context.get(unlambdaKey); rfield@1380: if (instance == null) { rfield@1380: instance = new LambdaToMethod(context); rfield@1380: } rfield@1380: return instance; rfield@1380: } rfield@1380: rfield@1380: private LambdaToMethod(Context context) { rfield@1380: names = Names.instance(context); rfield@1380: syms = Symtab.instance(context); rfield@1380: rs = Resolve.instance(context); rfield@1380: make = TreeMaker.instance(context); rfield@1380: types = Types.instance(context); rfield@1380: transTypes = TransTypes.instance(context); rfield@1587: analyzer = new LambdaAnalyzer(); rfield@1380: } rfield@1380: // rfield@1380: rfield@1380: // rfield@1380: @Override rfield@1380: public T translate(T tree) { rfield@1380: TranslationContext newContext = contextMap.get(tree); rfield@1380: return translate(tree, newContext != null ? newContext : context); rfield@1380: } rfield@1380: rfield@1380: public T translate(T tree, TranslationContext newContext) { rfield@1380: TranslationContext prevContext = context; rfield@1380: try { rfield@1380: context = newContext; rfield@1380: return super.translate(tree); rfield@1380: } rfield@1380: finally { rfield@1380: context = prevContext; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: public List translate(List trees, TranslationContext newContext) { rfield@1380: ListBuffer buf = ListBuffer.lb(); rfield@1380: for (T tree : trees) { rfield@1380: buf.append(translate(tree, newContext)); rfield@1380: } rfield@1380: return buf.toList(); rfield@1380: } rfield@1380: rfield@1380: public JCTree translateTopLevelClass(Env env, JCTree cdef, TreeMaker make) { rfield@1380: this.make = make; rfield@1380: this.attrEnv = env; rfield@1380: this.context = null; rfield@1380: this.contextMap = new HashMap>(); rfield@1380: return translate(cdef); rfield@1380: } rfield@1380: // rfield@1380: rfield@1380: // rfield@1380: /** rfield@1380: * Visit a class. rfield@1380: * Maintain the translatedMethodList across nested classes. rfield@1380: * Append the translatedMethodList to the class after it is translated. rfield@1380: * @param tree rfield@1380: */ rfield@1380: @Override rfield@1380: public void visitClassDef(JCClassDecl tree) { rfield@1380: if (tree.sym.owner.kind == PCK) { rfield@1380: //analyze class rfield@1380: analyzer.analyzeClass(tree); rfield@1380: } rfield@1587: KlassInfo prevKlassInfo = kInfo; rfield@1380: try { rfield@1587: kInfo = new KlassInfo(tree.sym); rfield@1380: super.visitClassDef(tree); rfield@1587: if (!kInfo.deserializeCases.isEmpty()) { rfield@1587: kInfo.addMethod(makeDeserializeMethod(tree.sym)); rfield@1587: } rfield@1380: //add all translated instance methods here rfield@1587: List newMethods = kInfo.appendedMethodList.toList(); rfield@1587: tree.defs = tree.defs.appendList(newMethods); rfield@1587: for (JCTree lambda : newMethods) { rfield@1380: tree.sym.members().enter(((JCMethodDecl)lambda).sym); rfield@1380: } rfield@1380: result = tree; rfield@1380: } finally { rfield@1587: kInfo = prevKlassInfo; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Translate a lambda into a method to be inserted into the class. rfield@1380: * Then replace the lambda site with an invokedynamic call of to lambda rfield@1380: * meta-factory, which will use the lambda method. rfield@1380: * @param tree rfield@1380: */ rfield@1380: @Override rfield@1380: public void visitLambda(JCLambda tree) { rfield@1380: LambdaTranslationContext localContext = (LambdaTranslationContext)context; rfield@1380: MethodSymbol sym = (MethodSymbol)localContext.translatedSym; rfield@1380: MethodType lambdaType = (MethodType) sym.type; rfield@1380: rfield@1380: //create the method declaration hoisting the lambda body rfield@1380: JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field), rfield@1380: sym.name, rfield@1380: make.QualIdent(lambdaType.getReturnType().tsym), rfield@1380: List.nil(), rfield@1380: localContext.syntheticParams, rfield@1380: lambdaType.getThrownTypes() == null ? rfield@1380: List.nil() : rfield@1380: make.Types(lambdaType.getThrownTypes()), rfield@1380: null, rfield@1380: null); rfield@1380: lambdaDecl.sym = sym; rfield@1380: lambdaDecl.type = lambdaType; rfield@1380: rfield@1380: //translate lambda body rfield@1380: //As the lambda body is translated, all references to lambda locals, rfield@1380: //captured variables, enclosing members are adjusted accordingly rfield@1380: //to refer to the static method parameters (rather than i.e. acessing to rfield@1380: //captured members directly). rfield@1380: lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl)); rfield@1380: rfield@1380: //Add the method to the list of methods to be added to this class. rfield@1587: kInfo.addMethod(lambdaDecl); rfield@1380: rfield@1380: //now that we have generated a method for the lambda expression, rfield@1380: //we can translate the lambda into a method reference pointing to the newly rfield@1380: //created method. rfield@1380: // rfield@1380: //Note that we need to adjust the method handle so that it will match the rfield@1380: //signature of the SAM descriptor - this means that the method reference rfield@1380: //should be added the following synthetic arguments: rfield@1380: // rfield@1380: // * the "this" argument if it is an instance method rfield@1380: // * enclosing locals captured by the lambda expression rfield@1380: rfield@1380: ListBuffer syntheticInits = ListBuffer.lb(); rfield@1380: rfield@1380: if (!sym.isStatic()) { rfield@1380: syntheticInits.append(makeThis( rfield@1587: sym.owner.enclClass().asType(), rfield@1380: localContext.owner.enclClass())); rfield@1380: } rfield@1380: rfield@1380: //add captured locals rfield@1380: for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) { rfield@1380: if (fv != localContext.self) { rfield@1380: JCTree captured_local = make.Ident(fv).setType(fv.type); rfield@1380: syntheticInits.append((JCExpression) captured_local); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: //then, determine the arguments to the indy call rfield@1380: List indy_args = translate(syntheticInits.toList(), localContext.prev); rfield@1380: rfield@1380: //build a sam instance using an indy call to the meta-factory rfield@1380: int refKind = referenceKind(sym); rfield@1380: rfield@1380: //convert to an invokedynamic call rfield@1587: result = makeMetaFactoryIndyCall(tree, context.needsAltMetafactory(), context.isSerializable(), refKind, sym, indy_args); rfield@1380: } rfield@1380: rfield@1380: private JCIdent makeThis(Type type, Symbol owner) { rfield@1380: VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC, rfield@1380: names._this, rfield@1380: type, rfield@1380: owner); rfield@1380: return make.Ident(_this); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Translate a method reference into an invokedynamic call to the rfield@1380: * meta-factory. rfield@1380: * @param tree rfield@1380: */ rfield@1380: @Override rfield@1380: public void visitReference(JCMemberReference tree) { rfield@1380: ReferenceTranslationContext localContext = (ReferenceTranslationContext)context; rfield@1380: rfield@1380: //first determine the method symbol to be used to generate the sam instance rfield@1380: //this is either the method reference symbol, or the bridged reference symbol rfield@1380: Symbol refSym = localContext.needsBridge() ? rfield@1380: localContext.bridgeSym : rfield@1380: tree.sym; rfield@1380: rfield@1380: //build the bridge method, if needed rfield@1380: if (localContext.needsBridge()) { rfield@1380: bridgeMemberReference(tree, localContext); rfield@1380: } rfield@1380: rfield@1380: //the qualifying expression is treated as a special captured arg rfield@1380: JCExpression init; rfield@1380: switch(tree.kind) { rfield@1380: mcimadamore@1435: case IMPLICIT_INNER: /** Inner :: new */ mcimadamore@1435: case SUPER: /** super :: instMethod */ rfield@1380: init = makeThis( rfield@1587: localContext.owner.enclClass().asType(), rfield@1587: localContext.owner.enclClass()); rfield@1380: break; rfield@1380: mcimadamore@1435: case BOUND: /** Expr :: instMethod */ rfield@1380: init = tree.getQualifierExpression(); rfield@1380: break; rfield@1380: mcimadamore@1435: case UNBOUND: /** Type :: instMethod */ mcimadamore@1435: case STATIC: /** Type :: staticMethod */ mcimadamore@1435: case TOPLEVEL: /** Top level :: new */ mcimadamore@1496: case ARRAY_CTOR: /** ArrayType :: new */ rfield@1380: init = null; rfield@1380: break; rfield@1380: rfield@1380: default: rfield@1380: throw new InternalError("Should not have an invalid kind"); rfield@1380: } rfield@1380: rfield@1380: List indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev); rfield@1380: rfield@1380: rfield@1380: //build a sam instance using an indy call to the meta-factory rfield@1587: result = makeMetaFactoryIndyCall(tree, localContext.needsAltMetafactory(), localContext.isSerializable(), localContext.referenceKind(), refSym, indy_args); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Translate identifiers within a lambda to the mapped identifier rfield@1380: * @param tree rfield@1380: */ rfield@1380: @Override rfield@1380: public void visitIdent(JCIdent tree) { rfield@1380: if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) { rfield@1380: super.visitIdent(tree); rfield@1380: } else { rfield@1380: LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context; rfield@1380: if (lambdaContext.getSymbolMap(PARAM).containsKey(tree.sym)) { rfield@1380: Symbol translatedSym = lambdaContext.getSymbolMap(PARAM).get(tree.sym); rfield@1380: result = make.Ident(translatedSym).setType(tree.type); rfield@1380: } else if (lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { rfield@1380: Symbol translatedSym = lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym); rfield@1380: result = make.Ident(translatedSym).setType(tree.type); rfield@1587: } else if (lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) { rfield@1587: Symbol translatedSym = lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym); rfield@1587: result = make.Ident(translatedSym).setType(translatedSym.type); rfield@1380: } else if (lambdaContext.getSymbolMap(CAPTURED_VAR).containsKey(tree.sym)) { rfield@1380: Symbol translatedSym = lambdaContext.getSymbolMap(CAPTURED_VAR).get(tree.sym); rfield@1380: result = make.Ident(translatedSym).setType(tree.type); rfield@1380: } else { rfield@1380: if (tree.sym.owner.kind == Kinds.TYP) { rfield@1380: for (Map.Entry encl_entry : lambdaContext.getSymbolMap(CAPTURED_THIS).entrySet()) { rfield@1380: if (tree.sym.isMemberOf((ClassSymbol) encl_entry.getKey(), types)) { rfield@1380: JCExpression enclRef = make.Ident(encl_entry.getValue()); rfield@1380: result = tree.sym.name == names._this rfield@1380: ? enclRef.setType(tree.type) rfield@1380: : make.Select(enclRef, tree.sym).setType(tree.type); rfield@1380: result = tree; rfield@1380: return; rfield@1380: } rfield@1380: } rfield@1380: } rfield@1380: //access to untranslated symbols (i.e. compile-time constants, rfield@1380: //members defined inside the lambda body, etc.) ) rfield@1380: super.visitIdent(tree); rfield@1380: } rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitVarDef(JCVariableDecl tree) { rfield@1380: LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context; rfield@1380: if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) { rfield@1380: JCExpression init = translate(tree.init); rfield@1380: result = make.VarDef((VarSymbol)lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym), init); rfield@1587: } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) { rfield@1587: JCExpression init = translate(tree.init); rfield@1587: VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym); rfield@1587: result = make.VarDef(xsym, init); rfield@1587: // Replace the entered symbol for this variable rfield@1587: Scope sc = tree.sym.owner.members(); rfield@1587: if (sc != null) { rfield@1587: sc.remove(tree.sym); rfield@1587: sc.enter(xsym); rfield@1587: } rfield@1380: } else { rfield@1380: super.visitVarDef(tree); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: // rfield@1380: rfield@1380: // rfield@1380: rfield@1380: private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) { rfield@1380: return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ? rfield@1380: makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) : rfield@1380: makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally); rfield@1380: } rfield@1380: rfield@1380: private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) { rfield@1380: Type restype = lambdaMethodDecl.type.getReturnType(); rfield@1380: boolean isLambda_void = expr.type.hasTag(VOID); rfield@1380: boolean isTarget_void = restype.hasTag(VOID); rfield@1380: boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); rfield@1380: if (isTarget_void) { rfield@1380: //target is void: rfield@1380: // BODY; rfield@1380: JCStatement stat = make.Exec(expr); rfield@1380: return make.Block(0, List.of(stat)); rfield@1380: } else if (isLambda_void && isTarget_Void) { rfield@1380: //void to Void conversion: rfield@1380: // BODY; return null; rfield@1380: ListBuffer stats = ListBuffer.lb(); rfield@1380: stats.append(make.Exec(expr)); rfield@1380: stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); rfield@1380: return make.Block(0, stats.toList()); rfield@1380: } else { rfield@1380: //non-void to non-void conversion: rfield@1380: // return (TYPE)BODY; rfield@1380: JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype); rfield@1380: return make.Block(0, List.of(make.Return(retExpr))); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) { rfield@1380: final Type restype = lambdaMethodDecl.type.getReturnType(); rfield@1380: final boolean isTarget_void = restype.hasTag(VOID); rfield@1380: boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type); rfield@1380: rfield@1380: class LambdaBodyTranslator extends TreeTranslator { rfield@1380: rfield@1380: @Override rfield@1380: public void visitClassDef(JCClassDecl tree) { rfield@1380: //do NOT recurse on any inner classes rfield@1380: result = tree; rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitLambda(JCLambda tree) { rfield@1380: //do NOT recurse on any nested lambdas rfield@1380: result = tree; rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitReturn(JCReturn tree) { rfield@1380: boolean isLambda_void = tree.expr == null; rfield@1380: if (isTarget_void && !isLambda_void) { rfield@1380: //Void to void conversion: rfield@1380: // { TYPE $loc = RET-EXPR; return; } rfield@1380: VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym); rfield@1380: JCVariableDecl varDef = make.VarDef(loc, tree.expr); rfield@1380: result = make.Block(0, List.of(varDef, make.Return(null))); rfield@1380: } else if (!isTarget_void || !isLambda_void) { rfield@1380: //non-void to non-void conversion: rfield@1380: // return (TYPE)RET-EXPR; rfield@1380: tree.expr = transTypes.coerce(attrEnv, tree.expr, restype); rfield@1380: result = tree; rfield@1380: } else { rfield@1380: result = tree; rfield@1380: } rfield@1380: rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: JCBlock trans_block = new LambdaBodyTranslator().translate(block); rfield@1380: if (completeNormally && isTarget_Void) { rfield@1380: //there's no return statement and the lambda (possibly inferred) rfield@1380: //return type is java.lang.Void; emit a synthetic return statement rfield@1380: trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType))); rfield@1380: } rfield@1380: return trans_block; rfield@1380: } rfield@1380: rfield@1587: private JCMethodDecl makeDeserializeMethod(Symbol kSym) { rfield@1587: ListBuffer cases = ListBuffer.lb(); rfield@1587: ListBuffer breaks = ListBuffer.lb(); rfield@1587: for (Map.Entry> entry : kInfo.deserializeCases.entrySet()) { rfield@1587: JCBreak br = make.Break(null); rfield@1587: breaks.add(br); rfield@1587: List stmts = entry.getValue().append(br).toList(); rfield@1587: cases.add(make.Case(make.Literal(entry.getKey()), stmts)); rfield@1587: } rfield@1587: JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList()); rfield@1587: for (JCBreak br : breaks) { rfield@1587: br.target = sw; rfield@1587: } rfield@1587: JCBlock body = make.Block(0L, List.of( rfield@1587: sw, rfield@1587: make.Throw(makeNewClass( rfield@1587: syms.illegalArgumentExceptionType, rfield@1587: List.of(make.Literal("Invalid lambda deserialization")))))); rfield@1587: JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()), rfield@1587: names.deserializeLambda, rfield@1587: make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym), rfield@1587: List.nil(), rfield@1587: List.of(make.VarDef(kInfo.deserParamSym, null)), rfield@1587: List.nil(), rfield@1587: body, rfield@1587: null); rfield@1587: deser.sym = kInfo.deserMethodSym; rfield@1587: deser.type = kInfo.deserMethodSym.type; rfield@1587: //System.err.printf("DESER: '%s'\n", deser); rfield@1587: return deser; rfield@1587: } rfield@1587: rfield@1587: /** Make an attributed class instance creation expression. rfield@1587: * @param ctype The class type. rfield@1587: * @param args The constructor arguments. rfield@1587: */ rfield@1587: JCNewClass makeNewClass(Type ctype, List args) { rfield@1587: JCNewClass tree = make.NewClass(null, rfield@1587: null, make.QualIdent(ctype.tsym), args, null); rfield@1587: tree.constructor = rs.resolveConstructor( rfield@1587: null, attrEnv, ctype, TreeInfo.types(args), List.nil()); rfield@1587: tree.type = ctype; rfield@1587: return tree; rfield@1587: } rfield@1587: rfield@1587: private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym, rfield@1587: DiagnosticPosition pos, List staticArgs, MethodType indyType) { rfield@1587: String functionalInterfaceClass = classSig(targetType); rfield@1587: String functionalInterfaceMethodName = samSym.getSimpleName().toString(); rfield@1587: String functionalInterfaceMethodSignature = methodSig(types.erasure(samSym.type)); rfield@1587: String implClass = classSig(refSym.owner.type); rfield@1587: String implMethodName = refSym.getQualifiedName().toString(); rfield@1587: String implMethodSignature = methodSig(types.erasure(refSym.type)); rfield@1587: rfield@1587: JCExpression kindTest = eqTest(syms.intType, deserGetter("getImplMethodKind", syms.intType), make.Literal(implMethodKind)); rfield@1587: ListBuffer serArgs = ListBuffer.lb(); rfield@1587: int i = 0; rfield@1587: for (Type t : indyType.getParameterTypes()) { rfield@1587: List indexAsArg = ListBuffer.lb().append(make.Literal(i)).toList(); rfield@1587: List argTypes = ListBuffer.lb().append(syms.intType).toList(); rfield@1587: serArgs.add(make.TypeCast(types.erasure(t), deserGetter("getCapturedArg", syms.objectType, argTypes, indexAsArg))); rfield@1587: ++i; rfield@1587: } rfield@1587: JCStatement stmt = make.If( rfield@1587: deserTest(deserTest(deserTest(deserTest(deserTest( rfield@1587: kindTest, rfield@1587: "getFunctionalInterfaceClass", functionalInterfaceClass), rfield@1587: "getFunctionalInterfaceMethodName", functionalInterfaceMethodName), rfield@1587: "getFunctionalInterfaceMethodSignature", functionalInterfaceMethodSignature), rfield@1587: "getImplClass", implClass), rfield@1587: "getImplMethodSignature", implMethodSignature), rfield@1587: make.Return(makeIndyCall( rfield@1587: pos, rfield@1587: syms.lambdaMetafactory, rfield@1587: names.altMetaFactory, rfield@1587: staticArgs, indyType, serArgs.toList())), rfield@1587: null); rfield@1587: ListBuffer stmts = kInfo.deserializeCases.get(implMethodName); rfield@1587: if (stmts == null) { rfield@1587: stmts = ListBuffer.lb(); rfield@1587: kInfo.deserializeCases.put(implMethodName, stmts); rfield@1587: } rfield@1587: /**** rfield@1587: System.err.printf("+++++++++++++++++\n"); rfield@1587: System.err.printf("*functionalInterfaceClass: '%s'\n", functionalInterfaceClass); rfield@1587: System.err.printf("*functionalInterfaceMethodName: '%s'\n", functionalInterfaceMethodName); rfield@1587: System.err.printf("*functionalInterfaceMethodSignature: '%s'\n", functionalInterfaceMethodSignature); rfield@1587: System.err.printf("*implMethodKind: %d\n", implMethodKind); rfield@1587: System.err.printf("*implClass: '%s'\n", implClass); rfield@1587: System.err.printf("*implMethodName: '%s'\n", implMethodName); rfield@1587: System.err.printf("*implMethodSignature: '%s'\n", implMethodSignature); rfield@1587: ****/ rfield@1587: stmts.append(stmt); rfield@1587: } rfield@1587: rfield@1587: private JCExpression eqTest(Type argType, JCExpression arg1, JCExpression arg2) { rfield@1587: JCBinary testExpr = make.Binary(JCTree.Tag.EQ, arg1, arg2); rfield@1587: testExpr.operator = rs.resolveBinaryOperator(null, JCTree.Tag.EQ, attrEnv, argType, argType); rfield@1587: testExpr.setType(syms.booleanType); rfield@1587: return testExpr; rfield@1587: } rfield@1587: rfield@1587: private JCExpression deserTest(JCExpression prev, String func, String lit) { rfield@1587: MethodType eqmt = new MethodType(List.of(syms.objectType), syms.booleanType, List.nil(), syms.methodClass); rfield@1587: Symbol eqsym = rs.resolveQualifiedMethod(null, attrEnv, syms.objectType, names.equals, List.of(syms.objectType), List.nil()); rfield@1587: JCMethodInvocation eqtest = make.Apply( rfield@1587: List.nil(), rfield@1587: make.Select(deserGetter(func, syms.stringType), eqsym).setType(eqmt), rfield@1587: List.of(make.Literal(lit))); rfield@1587: eqtest.setType(syms.booleanType); rfield@1587: JCBinary compound = make.Binary(JCTree.Tag.AND, prev, eqtest); rfield@1587: compound.operator = rs.resolveBinaryOperator(null, JCTree.Tag.AND, attrEnv, syms.booleanType, syms.booleanType); rfield@1587: compound.setType(syms.booleanType); rfield@1587: return compound; rfield@1587: } rfield@1587: rfield@1587: private JCExpression deserGetter(String func, Type type) { rfield@1587: return deserGetter(func, type, List.nil(), List.nil()); rfield@1587: } rfield@1587: rfield@1587: private JCExpression deserGetter(String func, Type type, List argTypes, List args) { rfield@1587: MethodType getmt = new MethodType(argTypes, type, List.nil(), syms.methodClass); rfield@1587: Symbol getsym = rs.resolveQualifiedMethod(null, attrEnv, syms.serializedLambdaType, names.fromString(func), argTypes, List.nil()); rfield@1587: return make.Apply( rfield@1587: List.nil(), rfield@1587: make.Select(make.Ident(kInfo.deserParamSym).setType(syms.serializedLambdaType), getsym).setType(getmt), rfield@1587: args).setType(type); rfield@1587: } rfield@1587: rfield@1380: /** rfield@1380: * Create new synthetic method with given flags, name, type, owner rfield@1380: */ rfield@1380: private MethodSymbol makeSyntheticMethod(long flags, Name name, Type type, Symbol owner) { rfield@1380: return new MethodSymbol(flags | SYNTHETIC, name, type, owner); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Create new synthetic variable with given flags, name, type, owner rfield@1380: */ rfield@1380: private VarSymbol makeSyntheticVar(long flags, String name, Type type, Symbol owner) { rfield@1380: return makeSyntheticVar(flags, names.fromString(name), type, owner); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Create new synthetic variable with given flags, name, type, owner rfield@1380: */ rfield@1380: private VarSymbol makeSyntheticVar(long flags, Name name, Type type, Symbol owner) { rfield@1380: return new VarSymbol(flags | SYNTHETIC, name, type, owner); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Set varargsElement field on a given tree (must be either a new class tree rfield@1380: * or a method call tree) rfield@1380: */ rfield@1380: private void setVarargsIfNeeded(JCTree tree, Type varargsElement) { rfield@1380: if (varargsElement != null) { rfield@1380: switch (tree.getTag()) { rfield@1380: case APPLY: ((JCMethodInvocation)tree).varargsElement = varargsElement; break; rfield@1380: case NEWCLASS: ((JCNewClass)tree).varargsElement = varargsElement; break; rfield@1380: default: throw new AssertionError(); rfield@1380: } rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Convert method/constructor arguments by inserting appropriate cast rfield@1380: * as required by type-erasure - this is needed when bridging a lambda/method rfield@1380: * reference, as the bridged signature might require downcast to be compatible rfield@1380: * with the generated signature. rfield@1380: */ rfield@1380: private List convertArgs(Symbol meth, List args, Type varargsElement) { rfield@1380: Assert.check(meth.kind == Kinds.MTH); rfield@1380: List formals = types.erasure(meth.type).getParameterTypes(); rfield@1380: if (varargsElement != null) { rfield@1380: Assert.check((meth.flags() & VARARGS) != 0); rfield@1380: } rfield@1380: return transTypes.translateArgs(args, formals, varargsElement, attrEnv); rfield@1380: } rfield@1380: rfield@1380: // rfield@1380: rfield@1380: /** rfield@1380: * Generate an adapter method "bridge" for a method reference which cannot rfield@1380: * be used directly. rfield@1380: */ rfield@1380: private class MemberReferenceBridger { rfield@1380: rfield@1380: private final JCMemberReference tree; rfield@1380: private final ReferenceTranslationContext localContext; rfield@1380: private final ListBuffer args = ListBuffer.lb(); rfield@1380: private final ListBuffer params = ListBuffer.lb(); rfield@1380: rfield@1380: MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) { rfield@1380: this.tree = tree; rfield@1380: this.localContext = localContext; rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Generate the bridge rfield@1380: */ rfield@1380: JCMethodDecl bridge() { rfield@1380: int prevPos = make.pos; rfield@1380: try { rfield@1380: make.at(tree); rfield@1380: Type samDesc = localContext.bridgedRefSig(); rfield@1380: List samPTypes = samDesc.getParameterTypes(); rfield@1380: rfield@1380: //an extra argument is prepended to the signature of the bridge in case rfield@1380: //the member reference is an instance method reference (in which case rfield@1380: //the receiver expression is passed to the bridge itself). rfield@1380: Type recType = null; rfield@1380: switch (tree.kind) { rfield@1380: case IMPLICIT_INNER: rfield@1380: recType = tree.sym.owner.type.getEnclosingType(); rfield@1380: break; rfield@1380: case BOUND: rfield@1380: recType = tree.getQualifierExpression().type; rfield@1380: break; rfield@1380: case UNBOUND: rfield@1380: recType = samPTypes.head; rfield@1380: samPTypes = samPTypes.tail; rfield@1380: break; rfield@1380: } rfield@1380: rfield@1380: //generate the parameter list for the bridged member reference - the rfield@1380: //bridge signature will match the signature of the target sam descriptor rfield@1380: rfield@1380: VarSymbol rcvr = (recType == null) rfield@1380: ? null rfield@1380: : addParameter("rec$", recType, false); rfield@1380: rfield@1380: List refPTypes = tree.sym.type.getParameterTypes(); rfield@1380: int refSize = refPTypes.size(); rfield@1380: int samSize = samPTypes.size(); mcimadamore@1595: // Last parameter to copy from referenced method mcimadamore@1595: int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; rfield@1380: rfield@1380: List l = refPTypes; rfield@1380: // Use parameter types of the referenced method, excluding final var args rfield@1380: for (int i = 0; l.nonEmpty() && i < last; ++i) { rfield@1380: addParameter("x$" + i, l.head, true); rfield@1380: l = l.tail; rfield@1380: } rfield@1380: // Flatten out the var args rfield@1380: for (int i = last; i < samSize; ++i) { rfield@1380: addParameter("xva$" + i, tree.varargsElement, true); rfield@1380: } rfield@1380: rfield@1380: //generate the bridge method declaration rfield@1380: JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()), rfield@1380: localContext.bridgeSym.name, rfield@1380: make.QualIdent(samDesc.getReturnType().tsym), rfield@1380: List.nil(), rfield@1380: params.toList(), rfield@1380: tree.sym.type.getThrownTypes() == null rfield@1380: ? List.nil() rfield@1380: : make.Types(tree.sym.type.getThrownTypes()), rfield@1380: null, rfield@1380: null); rfield@1380: bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym; mcimadamore@1595: bridgeDecl.type = localContext.bridgeSym.type = mcimadamore@1595: types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList())); rfield@1380: rfield@1380: //bridge method body generation - this can be either a method call or a rfield@1380: //new instance creation expression, depending on the member reference kind rfield@1380: JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE) rfield@1380: ? bridgeExpressionInvoke(rcvr) rfield@1380: : bridgeExpressionNew(); rfield@1380: rfield@1380: //the body is either a return expression containing a method call, rfield@1380: //or the method call itself, depending on whether the return type of rfield@1380: //the bridge is non-void/void. rfield@1380: bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl); rfield@1380: rfield@1380: return bridgeDecl; rfield@1380: } finally { rfield@1380: make.at(prevPos); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * determine the receiver of the bridged method call - the receiver can rfield@1380: * be either the synthetic receiver parameter or a type qualifier; the rfield@1380: * original qualifier expression is never used here, as it might refer rfield@1380: * to symbols not available in the static context of the bridge rfield@1380: */ rfield@1380: private JCExpression bridgeExpressionInvoke(VarSymbol rcvr) { rfield@1380: JCExpression qualifier = rfield@1380: tree.sym.isStatic() ? rfield@1380: make.Type(tree.sym.owner.type) : rfield@1380: (rcvr != null) ? rfield@1380: make.Ident(rcvr) : rfield@1380: tree.getQualifierExpression(); rfield@1380: rfield@1380: //create the qualifier expression rfield@1380: JCFieldAccess select = make.Select(qualifier, tree.sym.name); rfield@1380: select.sym = tree.sym; rfield@1380: select.type = tree.sym.erasure(types); rfield@1380: rfield@1380: //create the method call expression rfield@1380: JCExpression apply = make.Apply(List.nil(), select, mcimadamore@1595: convertArgs(tree.sym, args.toList(), tree.varargsElement)). mcimadamore@1595: setType(tree.sym.erasure(types).getReturnType()); rfield@1380: rfield@1380: apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType()); rfield@1380: setVarargsIfNeeded(apply, tree.varargsElement); rfield@1380: return apply; rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * the enclosing expression is either 'null' (no enclosing type) or set rfield@1380: * to the first bridge synthetic parameter rfield@1380: */ rfield@1380: private JCExpression bridgeExpressionNew() { mcimadamore@1496: if (tree.kind == ReferenceKind.ARRAY_CTOR) { mcimadamore@1496: //create the array creation expression mcimadamore@1595: JCNewArray newArr = make.NewArray( mcimadamore@1595: make.Type(types.elemtype(tree.getQualifierExpression().type)), mcimadamore@1496: List.of(make.Ident(params.first())), mcimadamore@1496: null); mcimadamore@1496: newArr.type = tree.getQualifierExpression().type; mcimadamore@1496: return newArr; mcimadamore@1496: } else { mcimadamore@1496: JCExpression encl = null; mcimadamore@1496: switch (tree.kind) { mcimadamore@1496: case UNBOUND: mcimadamore@1496: case IMPLICIT_INNER: mcimadamore@1496: encl = make.Ident(params.first()); mcimadamore@1496: } mcimadamore@1496: mcimadamore@1496: //create the instance creation expression mcimadamore@1496: JCNewClass newClass = make.NewClass(encl, mcimadamore@1496: List.nil(), mcimadamore@1496: make.Type(tree.getQualifierExpression().type), mcimadamore@1496: convertArgs(tree.sym, args.toList(), tree.varargsElement), mcimadamore@1496: null); mcimadamore@1496: newClass.constructor = tree.sym; mcimadamore@1496: newClass.constructorType = tree.sym.erasure(types); mcimadamore@1496: newClass.type = tree.getQualifierExpression().type; mcimadamore@1496: setVarargsIfNeeded(newClass, tree.varargsElement); mcimadamore@1496: return newClass; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: private VarSymbol addParameter(String name, Type p, boolean genArg) { rfield@1380: VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym); rfield@1380: params.append(make.VarDef(vsym, null)); rfield@1380: if (genArg) { rfield@1380: args.append(make.Ident(vsym)); rfield@1380: } rfield@1380: return vsym; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Bridges a member reference - this is needed when: rfield@1380: * * Var args in the referenced method need to be flattened away rfield@1380: * * super is used rfield@1380: */ rfield@1380: private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) { rfield@1587: kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge()); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Generate an indy method call to the meta factory rfield@1380: */ rfield@1587: private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, boolean needsAltMetafactory, rfield@1587: boolean isSerializable, int refKind, Symbol refSym, List indy_args) { rfield@1380: //determine the static bsm args mcimadamore@1510: Type mtype = types.erasure(tree.descriptorType); mcimadamore@1510: MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym); rfield@1380: List staticArgs = List.of( mcimadamore@1595: new Pool.MethodHandle(ClassFile.REF_invokeInterface, mcimadamore@1595: types.findDescriptorSymbol(tree.type.tsym), types), vromero@1452: new Pool.MethodHandle(refKind, refSym, types), rfield@1380: new MethodType(mtype.getParameterTypes(), rfield@1380: mtype.getReturnType(), rfield@1380: mtype.getThrownTypes(), rfield@1380: syms.methodClass)); rfield@1380: rfield@1380: //computed indy arg types rfield@1380: ListBuffer indy_args_types = ListBuffer.lb(); rfield@1380: for (JCExpression arg : indy_args) { rfield@1380: indy_args_types.append(arg.type); rfield@1380: } rfield@1380: rfield@1380: //finally, compute the type of the indy call rfield@1380: MethodType indyType = new MethodType(indy_args_types.toList(), rfield@1380: tree.type, rfield@1380: List.nil(), rfield@1380: syms.methodClass); rfield@1380: rfield@1587: Name metafactoryName = needsAltMetafactory ? rfield@1587: names.altMetaFactory : names.metaFactory; rfield@1587: rfield@1587: if (needsAltMetafactory) { rfield@1587: ListBuffer markers = ListBuffer.lb(); rfield@1587: for (Symbol t : tree.targets.tail) { rfield@1587: if (t != syms.serializableType.tsym) { rfield@1587: markers.append(t); rfield@1587: } rfield@1587: } rfield@1587: int flags = isSerializable? FLAG_SERIALIZABLE : 0; rfield@1587: boolean hasMarkers = markers.nonEmpty(); rfield@1587: flags |= hasMarkers ? FLAG_MARKERS : 0; rfield@1587: staticArgs = staticArgs.append(flags); rfield@1587: if (hasMarkers) { rfield@1587: staticArgs = staticArgs.append(markers.length()); rfield@1587: staticArgs = staticArgs.appendList(markers.toList()); rfield@1587: } rfield@1587: if (isSerializable) { rfield@1587: addDeserializationCase(refKind, refSym, tree.type, samSym, rfield@1587: tree, staticArgs, indyType); rfield@1587: } rfield@1587: } rfield@1587: rfield@1587: return makeIndyCall(tree, syms.lambdaMetafactory, metafactoryName, staticArgs, indyType, indy_args); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Generate an indy method call with given name, type and static bootstrap rfield@1380: * arguments types rfield@1380: */ mcimadamore@1595: private JCExpression makeIndyCall(DiagnosticPosition pos, Type site, Name bsmName, mcimadamore@1595: List staticArgs, MethodType indyType, List indyArgs) { rfield@1380: int prevPos = make.pos; rfield@1380: try { rfield@1380: make.at(pos); rfield@1380: List bsm_staticArgs = List.of(syms.methodHandleLookupType, rfield@1380: syms.stringType, rfield@1380: syms.methodTypeType).appendList(bsmStaticArgToTypes(staticArgs)); rfield@1380: rfield@1380: Symbol bsm = rs.resolveInternalMethod(pos, attrEnv, site, rfield@1380: bsmName, bsm_staticArgs, List.nil()); rfield@1380: rfield@1380: DynamicMethodSymbol dynSym = rfield@1380: new DynamicMethodSymbol(names.lambda, rfield@1380: syms.noSymbol, mcimadamore@1595: bsm.isStatic() ? mcimadamore@1595: ClassFile.REF_invokeStatic : mcimadamore@1595: ClassFile.REF_invokeVirtual, rfield@1380: (MethodSymbol)bsm, rfield@1380: indyType, rfield@1380: staticArgs.toArray()); rfield@1380: rfield@1380: JCFieldAccess qualifier = make.Select(make.QualIdent(site.tsym), bsmName); rfield@1380: qualifier.sym = dynSym; rfield@1380: qualifier.type = indyType.getReturnType(); rfield@1380: rfield@1380: JCMethodInvocation proxyCall = make.Apply(List.nil(), qualifier, indyArgs); rfield@1380: proxyCall.type = indyType.getReturnType(); rfield@1380: return proxyCall; rfield@1380: } finally { rfield@1380: make.at(prevPos); rfield@1380: } rfield@1380: } rfield@1380: //where rfield@1380: private List bsmStaticArgToTypes(List args) { rfield@1380: ListBuffer argtypes = ListBuffer.lb(); rfield@1380: for (Object arg : args) { rfield@1380: argtypes.append(bsmStaticArgToType(arg)); rfield@1380: } rfield@1380: return argtypes.toList(); rfield@1380: } rfield@1380: rfield@1380: private Type bsmStaticArgToType(Object arg) { rfield@1380: Assert.checkNonNull(arg); rfield@1380: if (arg instanceof ClassSymbol) { rfield@1380: return syms.classType; rfield@1380: } else if (arg instanceof Integer) { rfield@1380: return syms.intType; rfield@1380: } else if (arg instanceof Long) { rfield@1380: return syms.longType; rfield@1380: } else if (arg instanceof Float) { rfield@1380: return syms.floatType; rfield@1380: } else if (arg instanceof Double) { rfield@1380: return syms.doubleType; rfield@1380: } else if (arg instanceof String) { rfield@1380: return syms.stringType; rfield@1380: } else if (arg instanceof Pool.MethodHandle) { rfield@1380: return syms.methodHandleType; rfield@1380: } else if (arg instanceof MethodType) { rfield@1380: return syms.methodTypeType; rfield@1380: } else { rfield@1380: Assert.error("bad static arg " + arg.getClass()); rfield@1380: return null; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Get the opcode associated with this method reference rfield@1380: */ rfield@1380: private int referenceKind(Symbol refSym) { rfield@1380: if (refSym.isConstructor()) { rfield@1380: return ClassFile.REF_newInvokeSpecial; rfield@1380: } else { rfield@1380: if (refSym.isStatic()) { rfield@1380: return ClassFile.REF_invokeStatic; rfield@1380: } else if (refSym.enclClass().isInterface()) { rfield@1380: return ClassFile.REF_invokeInterface; rfield@1380: } else { rfield@1380: return ClassFile.REF_invokeVirtual; rfield@1380: } rfield@1380: } rfield@1380: } rfield@1587: rfield@1380: // rfield@1380: rfield@1380: // \ rfield@1380: /** rfield@1380: * This visitor collects information about translation of a lambda expression. rfield@1380: * More specifically, it keeps track of the enclosing contexts and captured locals rfield@1380: * accessed by the lambda being translated (as well as other useful info). rfield@1380: */ rfield@1380: class LambdaAnalyzer extends TreeScanner { rfield@1380: rfield@1380: /** the frame stack - used to reconstruct translation info about enclosing scopes */ rfield@1380: private List frameStack; rfield@1380: rfield@1380: /** rfield@1380: * keep the count of lambda expression (used to generate unambiguous rfield@1380: * names) rfield@1380: */ rfield@1380: private int lambdaCount = 0; rfield@1380: rfield@1587: /** rfield@1587: * keep the count of lambda expression defined in given context (used to rfield@1587: * generate unambiguous names for serializable lambdas) rfield@1587: */ rfield@1587: private Map serializableLambdaCounts = rfield@1587: new HashMap(); rfield@1587: rfield@1587: /** rfield@1587: * maps for fake clinit symbols to be used as owners of lambda occurring in rfield@1587: * a static var init context rfield@1587: */ rfield@1587: private Map clinits = rfield@1587: new HashMap(); rfield@1587: rfield@1380: private void analyzeClass(JCClassDecl tree) { rfield@1380: frameStack = List.nil(); rfield@1380: scan(tree); rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitBlock(JCBlock tree) { rfield@1380: List prevStack = frameStack; rfield@1380: try { rfield@1380: if (frameStack.nonEmpty() && frameStack.head.tree.hasTag(CLASSDEF)) { rfield@1380: frameStack = frameStack.prepend(new Frame(tree)); rfield@1380: } rfield@1380: super.visitBlock(tree); rfield@1380: } rfield@1380: finally { rfield@1380: frameStack = prevStack; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitClassDef(JCClassDecl tree) { rfield@1380: List prevStack = frameStack; mcimadamore@1595: Map prevSerializableLambdaCount = mcimadamore@1595: serializableLambdaCounts; rfield@1587: Map prevClinits = clinits; rfield@1380: try { rfield@1587: serializableLambdaCounts = new HashMap(); rfield@1587: prevClinits = new HashMap(); rfield@1587: if (directlyEnclosingLambda() != null) { rfield@1380: tree.sym.owner = owner(); mcimadamore@1595: if (tree.sym.hasOuterInstance()) { mcimadamore@1595: //if a class is defined within a lambda, the lambda must capture mcimadamore@1595: //its enclosing instance (if any) mcimadamore@1595: ((LambdaTranslationContext) context()) mcimadamore@1595: .addSymbol(tree.sym.type.getEnclosingType().tsym, CAPTURED_THIS); rfield@1380: } rfield@1380: } rfield@1380: frameStack = frameStack.prepend(new Frame(tree)); rfield@1380: super.visitClassDef(tree); rfield@1380: } rfield@1380: finally { rfield@1380: frameStack = prevStack; rfield@1587: serializableLambdaCounts = prevSerializableLambdaCount; rfield@1587: clinits = prevClinits; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitIdent(JCIdent tree) { rfield@1587: if (context() != null && lambdaIdentSymbolFilter(tree.sym)) { rfield@1380: if (tree.sym.kind == VAR && rfield@1380: tree.sym.owner.kind == MTH && rfield@1380: tree.type.constValue() == null) { rfield@1380: TranslationContext localContext = context(); rfield@1380: while (localContext != null) { rfield@1380: if (localContext.tree.getTag() == LAMBDA) { rfield@1380: JCTree block = capturedDecl(localContext.depth, tree.sym); rfield@1380: if (block == null) break; mcimadamore@1595: ((LambdaTranslationContext)localContext) mcimadamore@1595: .addSymbol(tree.sym, CAPTURED_VAR); rfield@1380: } rfield@1380: localContext = localContext.prev; rfield@1380: } rfield@1380: } else if (tree.sym.owner.kind == TYP) { rfield@1380: TranslationContext localContext = context(); rfield@1380: while (localContext != null) { rfield@1380: if (localContext.tree.hasTag(LAMBDA)) { rfield@1380: JCTree block = capturedDecl(localContext.depth, tree.sym); rfield@1380: if (block == null) break; rfield@1380: switch (block.getTag()) { rfield@1380: case CLASSDEF: rfield@1380: JCClassDecl cdecl = (JCClassDecl)block; mcimadamore@1595: ((LambdaTranslationContext)localContext) mcimadamore@1595: .addSymbol(cdecl.sym, CAPTURED_THIS); rfield@1380: break; rfield@1380: default: rfield@1380: Assert.error("bad block kind"); rfield@1380: } rfield@1380: } rfield@1380: localContext = localContext.prev; rfield@1380: } rfield@1380: } rfield@1380: } rfield@1587: super.visitIdent(tree); rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitLambda(JCLambda tree) { rfield@1380: List prevStack = frameStack; rfield@1380: try { rfield@1380: LambdaTranslationContext context = (LambdaTranslationContext)makeLambdaContext(tree); rfield@1380: frameStack = frameStack.prepend(new Frame(tree)); rfield@1380: for (JCVariableDecl param : tree.params) { rfield@1380: context.addSymbol(param.sym, PARAM); rfield@1380: frameStack.head.addLocal(param.sym); rfield@1380: } rfield@1380: contextMap.put(tree, context); rfield@1380: scan(tree.body); rfield@1380: context.complete(); rfield@1380: } rfield@1380: finally { rfield@1380: frameStack = prevStack; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitMethodDef(JCMethodDecl tree) { rfield@1380: List prevStack = frameStack; rfield@1380: try { rfield@1380: frameStack = frameStack.prepend(new Frame(tree)); rfield@1380: super.visitMethodDef(tree); rfield@1380: } rfield@1380: finally { rfield@1380: frameStack = prevStack; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitNewClass(JCNewClass tree) { rfield@1380: if (lambdaNewClassFilter(context(), tree)) { mcimadamore@1595: ((LambdaTranslationContext) context()) mcimadamore@1595: .addSymbol(tree.type.getEnclosingType().tsym, CAPTURED_THIS); rfield@1380: } rfield@1380: super.visitNewClass(tree); rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitReference(JCMemberReference tree) { rfield@1380: scan(tree.getQualifierExpression()); rfield@1380: contextMap.put(tree, makeReferenceContext(tree)); rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitSelect(JCFieldAccess tree) { rfield@1380: if (context() != null && lambdaSelectSymbolFilter(tree.sym)) { rfield@1380: TranslationContext localContext = context(); rfield@1380: while (localContext != null) { rfield@1380: if (localContext.tree.hasTag(LAMBDA)) { rfield@1380: JCClassDecl clazz = (JCClassDecl)capturedDecl(localContext.depth, tree.sym); rfield@1380: if (clazz == null) break; rfield@1380: ((LambdaTranslationContext)localContext).addSymbol(clazz.sym, CAPTURED_THIS); rfield@1380: } rfield@1380: localContext = localContext.prev; rfield@1380: } rfield@1380: scan(tree.selected); rfield@1380: } else { rfield@1380: super.visitSelect(tree); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: @Override rfield@1380: public void visitVarDef(JCVariableDecl tree) { rfield@1587: TranslationContext context = context(); rfield@1587: LambdaTranslationContext ltc = (context != null && context instanceof LambdaTranslationContext)? rfield@1587: (LambdaTranslationContext)context : rfield@1587: null; rfield@1587: if (ltc != null) { rfield@1587: if (frameStack.head.tree.hasTag(LAMBDA)) { rfield@1587: ltc.addSymbol(tree.sym, LOCAL_VAR); rfield@1587: } rfield@1587: // Check for type variables (including as type arguments). rfield@1587: // If they occur within class nested in a lambda, mark for erasure rfield@1587: Type type = tree.sym.asType(); rfield@1587: if (inClassWithinLambda() && !types.isSameType(types.erasure(type), type)) { rfield@1587: ltc.addSymbol(tree.sym, TYPE_VAR); rfield@1587: } rfield@1380: } rfield@1587: rfield@1380: List prevStack = frameStack; rfield@1380: try { rfield@1380: if (tree.sym.owner.kind == MTH) { rfield@1380: frameStack.head.addLocal(tree.sym); rfield@1380: } rfield@1380: frameStack = frameStack.prepend(new Frame(tree)); rfield@1380: super.visitVarDef(tree); rfield@1380: } rfield@1380: finally { rfield@1380: frameStack = prevStack; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: private Name lambdaName() { rfield@1587: return names.lambda.append(names.fromString("" + lambdaCount++)); rfield@1587: } rfield@1587: rfield@1587: private Name serializedLambdaName(Symbol owner) { rfield@1587: StringBuilder buf = new StringBuilder(); rfield@1587: buf.append(names.lambda); rfield@1587: buf.append(owner.name); rfield@1587: buf.append('$'); rfield@1587: int methTypeHash = methodSig(owner.type).hashCode(); rfield@1587: buf.append(methTypeHash); rfield@1587: buf.append('$'); rfield@1587: String temp = buf.toString(); rfield@1587: Integer count = serializableLambdaCounts.get(temp); rfield@1587: if (count == null) { rfield@1587: count = 0; rfield@1587: } rfield@1587: buf.append(count++); rfield@1587: serializableLambdaCounts.put(temp, count); rfield@1587: return names.fromString(buf.toString()); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Return a valid owner given the current declaration stack rfield@1380: * (required to skip synthetic lambda symbols) rfield@1380: */ rfield@1380: private Symbol owner() { mcimadamore@1515: return owner(false); mcimadamore@1515: } mcimadamore@1515: mcimadamore@1515: @SuppressWarnings("fallthrough") mcimadamore@1515: private Symbol owner(boolean skipLambda) { rfield@1380: List frameStack2 = frameStack; rfield@1380: while (frameStack2.nonEmpty()) { rfield@1380: switch (frameStack2.head.tree.getTag()) { rfield@1380: case VARDEF: rfield@1380: if (((JCVariableDecl)frameStack2.head.tree).sym.isLocal()) { rfield@1380: frameStack2 = frameStack2.tail; rfield@1380: break; rfield@1380: } rfield@1380: JCClassDecl cdecl = (JCClassDecl)frameStack2.tail.head.tree; rfield@1587: return initSym(cdecl.sym, rfield@1587: ((JCVariableDecl)frameStack2.head.tree).sym.flags() & STATIC); rfield@1380: case BLOCK: rfield@1380: JCClassDecl cdecl2 = (JCClassDecl)frameStack2.tail.head.tree; rfield@1587: return initSym(cdecl2.sym, rfield@1587: ((JCBlock)frameStack2.head.tree).flags & STATIC); rfield@1380: case CLASSDEF: rfield@1380: return ((JCClassDecl)frameStack2.head.tree).sym; rfield@1380: case METHODDEF: rfield@1380: return ((JCMethodDecl)frameStack2.head.tree).sym; rfield@1380: case LAMBDA: mcimadamore@1515: if (!skipLambda) mcimadamore@1595: return ((LambdaTranslationContext)contextMap mcimadamore@1595: .get(frameStack2.head.tree)).translatedSym; rfield@1380: default: rfield@1380: frameStack2 = frameStack2.tail; rfield@1380: } rfield@1380: } rfield@1380: Assert.error(); rfield@1380: return null; rfield@1380: } rfield@1380: rfield@1587: private Symbol initSym(ClassSymbol csym, long flags) { rfield@1587: boolean isStatic = (flags & STATIC) != 0; rfield@1587: if (isStatic) { rfield@1587: //static clinits are generated in Gen - so we need to fake them rfield@1587: Symbol clinit = clinits.get(csym); rfield@1587: if (clinit == null) { rfield@1587: clinit = makeSyntheticMethod(STATIC, rfield@1587: names.clinit, rfield@1587: new MethodType(List.nil(), syms.voidType, List.nil(), syms.methodClass), rfield@1587: csym); rfield@1587: clinits.put(csym, clinit); rfield@1587: } rfield@1587: return clinit; rfield@1587: } else { rfield@1587: //get the first constructor and treat it as the instance init sym rfield@1587: for (Symbol s : csym.members_field.getElementsByName(names.init)) { rfield@1587: return s; rfield@1587: } rfield@1587: } rfield@1587: Assert.error("init not found"); rfield@1587: return null; rfield@1587: } rfield@1587: rfield@1587: private JCTree directlyEnclosingLambda() { rfield@1587: if (frameStack.isEmpty()) { rfield@1587: return null; rfield@1587: } rfield@1380: List frameStack2 = frameStack; rfield@1380: while (frameStack2.nonEmpty()) { rfield@1380: switch (frameStack2.head.tree.getTag()) { rfield@1380: case CLASSDEF: rfield@1380: case METHODDEF: rfield@1380: return null; rfield@1380: case LAMBDA: rfield@1380: return frameStack2.head.tree; rfield@1380: default: rfield@1380: frameStack2 = frameStack2.tail; rfield@1380: } rfield@1380: } rfield@1380: Assert.error(); rfield@1380: return null; rfield@1380: } rfield@1380: rfield@1587: private boolean inClassWithinLambda() { rfield@1587: if (frameStack.isEmpty()) { rfield@1587: return false; rfield@1587: } rfield@1587: List frameStack2 = frameStack; rfield@1587: boolean classFound = false; rfield@1587: while (frameStack2.nonEmpty()) { rfield@1587: switch (frameStack2.head.tree.getTag()) { rfield@1587: case LAMBDA: rfield@1587: return classFound; rfield@1587: case CLASSDEF: rfield@1587: classFound = true; rfield@1587: frameStack2 = frameStack2.tail; rfield@1587: break; rfield@1587: default: rfield@1587: frameStack2 = frameStack2.tail; rfield@1587: } rfield@1587: } rfield@1587: // No lambda rfield@1587: return false; rfield@1587: } rfield@1587: rfield@1380: /** rfield@1380: * Return the declaration corresponding to a symbol in the enclosing rfield@1380: * scope; the depth parameter is used to filter out symbols defined rfield@1380: * in nested scopes (which do not need to undergo capture). rfield@1380: */ rfield@1380: private JCTree capturedDecl(int depth, Symbol sym) { rfield@1380: int currentDepth = frameStack.size() - 1; rfield@1380: for (Frame block : frameStack) { rfield@1380: switch (block.tree.getTag()) { rfield@1380: case CLASSDEF: rfield@1380: ClassSymbol clazz = ((JCClassDecl)block.tree).sym; rfield@1380: if (sym.isMemberOf(clazz, types)) { rfield@1380: return currentDepth > depth ? null : block.tree; rfield@1380: } rfield@1380: break; rfield@1380: case VARDEF: rfield@1380: if (((JCVariableDecl)block.tree).sym == sym && rfield@1380: sym.owner.kind == MTH) { //only locals are captured rfield@1380: return currentDepth > depth ? null : block.tree; rfield@1380: } rfield@1380: break; rfield@1380: case BLOCK: rfield@1380: case METHODDEF: rfield@1380: case LAMBDA: rfield@1380: if (block.locals != null && block.locals.contains(sym)) { rfield@1380: return currentDepth > depth ? null : block.tree; rfield@1380: } rfield@1380: break; rfield@1380: default: rfield@1380: Assert.error("bad decl kind " + block.tree.getTag()); rfield@1380: } rfield@1380: currentDepth--; rfield@1380: } rfield@1380: return null; rfield@1380: } rfield@1380: rfield@1380: private TranslationContext context() { rfield@1380: for (Frame frame : frameStack) { rfield@1380: TranslationContext context = contextMap.get(frame.tree); rfield@1380: if (context != null) { rfield@1380: return context; rfield@1380: } rfield@1380: } rfield@1380: return null; rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * This is used to filter out those identifiers that needs to be adjusted rfield@1380: * when translating away lambda expressions rfield@1380: */ rfield@1380: private boolean lambdaIdentSymbolFilter(Symbol sym) { rfield@1380: return (sym.kind == VAR || sym.kind == MTH) rfield@1380: && !sym.isStatic() rfield@1380: && sym.name != names.init; rfield@1380: } rfield@1380: rfield@1380: private boolean lambdaSelectSymbolFilter(Symbol sym) { rfield@1380: return (sym.kind == VAR || sym.kind == MTH) && rfield@1380: !sym.isStatic() && rfield@1380: (sym.name == names._this || rfield@1380: sym.name == names._super); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * This is used to filter out those new class expressions that need to rfield@1380: * be qualified with an enclosing tree rfield@1380: */ rfield@1380: private boolean lambdaNewClassFilter(TranslationContext context, JCNewClass tree) { rfield@1380: if (context != null rfield@1380: && tree.encl == null rfield@1380: && tree.def == null rfield@1405: && !tree.type.getEnclosingType().hasTag(NONE)) { rfield@1380: Type encl = tree.type.getEnclosingType(); rfield@1380: Type current = context.owner.enclClass().type; rfield@1405: while (!current.hasTag(NONE)) { rfield@1380: if (current.tsym.isSubClass(encl.tsym, types)) { rfield@1380: return true; rfield@1380: } rfield@1380: current = current.getEnclosingType(); rfield@1380: } rfield@1380: return false; rfield@1380: } else { rfield@1380: return false; rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: private TranslationContext makeLambdaContext(JCLambda tree) { rfield@1380: return new LambdaTranslationContext(tree); rfield@1380: } rfield@1380: rfield@1380: private TranslationContext makeReferenceContext(JCMemberReference tree) { rfield@1380: return new ReferenceTranslationContext(tree); rfield@1380: } rfield@1380: rfield@1380: private class Frame { rfield@1380: final JCTree tree; rfield@1380: List locals; rfield@1380: rfield@1380: public Frame(JCTree tree) { rfield@1380: this.tree = tree; rfield@1380: } rfield@1380: rfield@1380: void addLocal(Symbol sym) { rfield@1380: if (locals == null) { rfield@1380: locals = List.nil(); rfield@1380: } rfield@1380: locals = locals.prepend(sym); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * This class is used to store important information regarding translation of rfield@1380: * lambda expression/method references (see subclasses). rfield@1380: */ mcimadamore@1510: private abstract class TranslationContext { rfield@1380: rfield@1380: /** the underlying (untranslated) tree */ rfield@1380: T tree; rfield@1380: rfield@1380: /** points to the adjusted enclosing scope in which this lambda/mref expression occurs */ rfield@1380: Symbol owner; rfield@1380: rfield@1380: /** the depth of this lambda expression in the frame stack */ rfield@1380: int depth; rfield@1380: rfield@1380: /** the enclosing translation context (set for nested lambdas/mref) */ rfield@1380: TranslationContext prev; rfield@1380: rfield@1380: TranslationContext(T tree) { rfield@1380: this.tree = tree; rfield@1380: this.owner = owner(); rfield@1380: this.depth = frameStack.size() - 1; rfield@1380: this.prev = context(); rfield@1380: } rfield@1587: rfield@1587: /** does this functional expression need to be created using alternate metafactory? */ rfield@1587: boolean needsAltMetafactory() { rfield@1587: return (tree.targets.length() > 1 || rfield@1587: isSerializable()); rfield@1587: } rfield@1587: rfield@1587: /** does this functional expression require serialization support? */ rfield@1587: boolean isSerializable() { rfield@1587: for (Symbol target : tree.targets) { rfield@1587: if (types.asSuper(target.type, syms.serializableType.tsym) != null) { rfield@1587: return true; rfield@1587: } rfield@1587: } rfield@1587: return false; rfield@1587: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * This class retains all the useful information about a lambda expression; rfield@1380: * the contents of this class are filled by the LambdaAnalyzer visitor, rfield@1380: * and the used by the main translation routines in order to adjust references rfield@1380: * to captured locals/members, etc. rfield@1380: */ rfield@1380: private class LambdaTranslationContext extends TranslationContext { rfield@1380: rfield@1380: /** variable in the enclosing context to which this lambda is assigned */ rfield@1380: Symbol self; rfield@1380: rfield@1380: /** map from original to translated lambda parameters */ rfield@1380: Map lambdaParams = new LinkedHashMap(); rfield@1380: rfield@1380: /** map from original to translated lambda locals */ rfield@1380: Map lambdaLocals = new LinkedHashMap(); rfield@1380: rfield@1380: /** map from variables in enclosing scope to translated synthetic parameters */ rfield@1380: Map capturedLocals = new LinkedHashMap(); rfield@1380: rfield@1380: /** map from class symbols to translated synthetic parameters (for captured member access) */ rfield@1380: Map capturedThis = new LinkedHashMap(); rfield@1380: rfield@1587: /** map from original to translated lambda locals */ rfield@1587: Map typeVars = new LinkedHashMap(); rfield@1587: rfield@1380: /** the synthetic symbol for the method hoisting the translated lambda */ rfield@1380: Symbol translatedSym; rfield@1380: rfield@1380: List syntheticParams; rfield@1380: rfield@1380: LambdaTranslationContext(JCLambda tree) { rfield@1380: super(tree); rfield@1380: Frame frame = frameStack.head; rfield@1380: if (frame.tree.hasTag(VARDEF)) { rfield@1380: self = ((JCVariableDecl)frame.tree).sym; rfield@1380: } rfield@1587: Name name = isSerializable() ? serializedLambdaName(owner) : lambdaName(); rfield@1587: this.translatedSym = makeSyntheticMethod(0, name, null, owner.enclClass()); rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Translate a symbol of a given kind into something suitable for the rfield@1380: * synthetic lambda body rfield@1380: */ rfield@1380: Symbol translate(String name, Symbol sym, LambdaSymbolKind skind) { rfield@1587: switch (skind) { rfield@1587: case CAPTURED_THIS: rfield@1587: return sym; // self represented rfield@1587: case TYPE_VAR: rfield@1587: // Just erase the type var mcimadamore@1595: return new VarSymbol(sym.flags(), names.fromString(name), mcimadamore@1595: types.erasure(sym.type), sym.owner); rfield@1587: default: rfield@1587: return makeSyntheticVar(FINAL, name, types.erasure(sym.type), translatedSym); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: void addSymbol(Symbol sym, LambdaSymbolKind skind) { rfield@1380: Map transMap = null; rfield@1380: String preferredName; rfield@1380: switch (skind) { rfield@1380: case CAPTURED_THIS: rfield@1380: transMap = capturedThis; rfield@1380: preferredName = "encl$" + capturedThis.size(); rfield@1380: break; rfield@1380: case CAPTURED_VAR: rfield@1380: transMap = capturedLocals; rfield@1380: preferredName = "cap$" + capturedLocals.size(); rfield@1380: break; rfield@1380: case LOCAL_VAR: rfield@1380: transMap = lambdaLocals; rfield@1380: preferredName = sym.name.toString(); rfield@1380: break; rfield@1380: case PARAM: rfield@1380: transMap = lambdaParams; rfield@1380: preferredName = sym.name.toString(); rfield@1380: break; rfield@1587: case TYPE_VAR: rfield@1587: transMap = typeVars; rfield@1587: preferredName = sym.name.toString(); rfield@1587: break; rfield@1380: default: throw new AssertionError(); rfield@1380: } rfield@1380: if (!transMap.containsKey(sym)) { rfield@1380: transMap.put(sym, translate(preferredName, sym, skind)); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: Map getSymbolMap(LambdaSymbolKind... skinds) { rfield@1380: LinkedHashMap translationMap = new LinkedHashMap(); rfield@1380: for (LambdaSymbolKind skind : skinds) { rfield@1380: switch (skind) { rfield@1380: case CAPTURED_THIS: rfield@1380: translationMap.putAll(capturedThis); rfield@1380: break; rfield@1380: case CAPTURED_VAR: rfield@1380: translationMap.putAll(capturedLocals); rfield@1380: break; rfield@1380: case LOCAL_VAR: rfield@1380: translationMap.putAll(lambdaLocals); rfield@1380: break; rfield@1380: case PARAM: rfield@1380: translationMap.putAll(lambdaParams); rfield@1380: break; rfield@1587: case TYPE_VAR: rfield@1587: translationMap.putAll(typeVars); rfield@1587: break; rfield@1380: default: throw new AssertionError(); rfield@1380: } rfield@1380: } rfield@1380: return translationMap; rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * The translatedSym is not complete/accurate until the analysis is rfield@1380: * finished. Once the analysis is finished, the translatedSym is rfield@1380: * "completed" -- updated with type information, access modifiers, rfield@1380: * and full parameter list. rfield@1380: */ rfield@1380: void complete() { rfield@1380: if (syntheticParams != null) { rfield@1380: return; rfield@1380: } rfield@1380: boolean inInterface = translatedSym.owner.isInterface(); rfield@1380: boolean thisReferenced = !getSymbolMap(CAPTURED_THIS).isEmpty(); rfield@1380: boolean needInstance = thisReferenced || inInterface; rfield@1380: rfield@1380: // If instance access isn't needed, make it static rfield@1380: // Interface methods much be public default methods, otherwise make it private mcimadamore@1595: translatedSym.flags_field = SYNTHETIC | (needInstance? 0 : STATIC) | mcimadamore@1595: (inInterface? PUBLIC | DEFAULT : PRIVATE); rfield@1380: rfield@1380: //compute synthetic params rfield@1380: ListBuffer params = ListBuffer.lb(); rfield@1380: rfield@1380: // The signature of the method is augmented with the following rfield@1380: // synthetic parameters: rfield@1380: // rfield@1380: // 1) reference to enclosing contexts captured by the lambda expression rfield@1380: // 2) enclosing locals captured by the lambda expression rfield@1380: for (Symbol thisSym : getSymbolMap(CAPTURED_VAR, PARAM).values()) { rfield@1380: params.append(make.VarDef((VarSymbol) thisSym, null)); rfield@1380: } rfield@1380: rfield@1380: syntheticParams = params.toList(); rfield@1380: rfield@1380: //prepend synthetic args to translated lambda method signature rfield@1587: translatedSym.type = types.createMethodTypeWithParameters( rfield@1587: generatedLambdaSig(), rfield@1380: TreeInfo.types(syntheticParams)); rfield@1380: } rfield@1380: rfield@1380: Type generatedLambdaSig() { mcimadamore@1510: return types.erasure(tree.descriptorType); rfield@1380: } rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * This class retains all the useful information about a method reference; rfield@1380: * the contents of this class are filled by the LambdaAnalyzer visitor, rfield@1380: * and the used by the main translation routines in order to adjust method rfield@1380: * references (i.e. in case a bridge is needed) rfield@1380: */ rfield@1380: private class ReferenceTranslationContext extends TranslationContext { rfield@1380: rfield@1380: final boolean isSuper; rfield@1380: final Symbol bridgeSym; rfield@1380: rfield@1380: ReferenceTranslationContext(JCMemberReference tree) { rfield@1380: super(tree); rfield@1380: this.isSuper = tree.hasKind(ReferenceKind.SUPER); rfield@1380: this.bridgeSym = needsBridge() rfield@1380: ? makeSyntheticMethod(isSuper ? 0 : STATIC, rfield@1380: lambdaName().append(names.fromString("$bridge")), null, rfield@1380: owner.enclClass()) rfield@1380: : null; rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Get the opcode associated with this method reference rfield@1380: */ rfield@1380: int referenceKind() { rfield@1380: return LambdaToMethod.this.referenceKind(needsBridge() ? bridgeSym : tree.sym); rfield@1380: } rfield@1380: rfield@1380: boolean needsVarArgsConversion() { rfield@1380: return tree.varargsElement != null; rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * @return Is this an array operation like clone() rfield@1380: */ rfield@1380: boolean isArrayOp() { rfield@1380: return tree.sym.owner == syms.arrayClass; rfield@1380: } rfield@1380: rfield@1380: /** rfield@1380: * Does this reference needs a bridge (i.e. var args need to be rfield@1380: * expanded or "super" is used) rfield@1380: */ rfield@1380: final boolean needsBridge() { rfield@1380: return isSuper || needsVarArgsConversion() || isArrayOp(); rfield@1380: } rfield@1380: rfield@1380: Type generatedRefSig() { rfield@1380: return types.erasure(tree.sym.type); rfield@1380: } rfield@1380: rfield@1380: Type bridgedRefSig() { mcimadamore@1510: return types.erasure(types.findDescriptorSymbol(tree.targets.head).type); rfield@1380: } rfield@1380: } rfield@1380: } rfield@1380: // rfield@1380: rfield@1380: enum LambdaSymbolKind { rfield@1380: CAPTURED_VAR, rfield@1380: CAPTURED_THIS, rfield@1380: LOCAL_VAR, rfield@1587: PARAM, rfield@1587: TYPE_VAR; rfield@1587: } rfield@1587: rfield@1587: /** rfield@1587: * **************************************************************** rfield@1587: * Signature Generation rfield@1587: * **************************************************************** rfield@1587: */ rfield@1587: rfield@1587: private String methodSig(Type type) { rfield@1587: L2MSignatureGenerator sg = new L2MSignatureGenerator(); rfield@1587: sg.assembleSig(type); rfield@1587: return sg.toString(); rfield@1587: } rfield@1587: rfield@1587: private String classSig(Type type) { rfield@1587: L2MSignatureGenerator sg = new L2MSignatureGenerator(); rfield@1587: sg.assembleClassSig(type); rfield@1587: return sg.toString(); rfield@1587: } rfield@1587: rfield@1587: /** rfield@1587: * Signature Generation rfield@1587: */ rfield@1587: private class L2MSignatureGenerator extends Types.SignatureGenerator { rfield@1587: rfield@1587: /** rfield@1587: * An output buffer for type signatures. rfield@1587: */ rfield@1587: StringBuilder sb = new StringBuilder(); rfield@1587: rfield@1587: L2MSignatureGenerator() { rfield@1587: super(types); rfield@1587: } rfield@1587: rfield@1587: @Override rfield@1587: protected void append(char ch) { rfield@1587: sb.append(ch); rfield@1587: } rfield@1587: rfield@1587: @Override rfield@1587: protected void append(byte[] ba) { rfield@1587: sb.append(new String(ba)); rfield@1587: } rfield@1587: rfield@1587: @Override rfield@1587: protected void append(Name name) { rfield@1587: sb.append(name.toString()); rfield@1587: } rfield@1587: rfield@1587: @Override rfield@1587: public String toString() { rfield@1587: return sb.toString(); rfield@1587: } rfield@1380: } rfield@1380: }