rfield@1380: /*
rfield@1380: * Copyright (c) 2010, 2012, 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.Flags;
rfield@1380: import com.sun.tools.javac.code.Kinds;
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@1380: import static com.sun.tools.javac.code.TypeTag.BOT;
rfield@1380: import static com.sun.tools.javac.code.TypeTag.NONE;
rfield@1380: import static com.sun.tools.javac.code.TypeTag.VOID;
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@1380: /** list of translated methods
rfield@1380: **/
rfield@1380: private ListBuffer translatedMethodList;
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@1380: this.analyzer = makeAnalyzer();
rfield@1380: }
rfield@1380:
rfield@1380: private LambdaAnalyzer makeAnalyzer() {
rfield@1380: return 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@1380: ListBuffer prevTranslated = translatedMethodList;
rfield@1380: try {
rfield@1380: translatedMethodList = ListBuffer.lb();
rfield@1380: super.visitClassDef(tree);
rfield@1380: //add all translated instance methods here
rfield@1380: tree.defs = tree.defs.appendList(translatedMethodList.toList());
rfield@1380: for (JCTree lambda : translatedMethodList) {
rfield@1380: tree.sym.members().enter(((JCMethodDecl)lambda).sym);
rfield@1380: }
rfield@1380: result = tree;
rfield@1380: } finally {
rfield@1380: translatedMethodList = prevTranslated;
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@1380: translatedMethodList = translatedMethodList.prepend(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@1380: sym.owner.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
mcimadamore@1510: result = makeMetaFactoryIndyCall(tree, 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@1380: localContext.owner.owner.asType(),
rfield@1380: localContext.owner);
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
mcimadamore@1510: result = makeMetaFactoryIndyCall(tree, 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@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@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@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();
rfield@1380: int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize; // Last parameter to copy from referenced method
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;
rfield@1380: bridgeDecl.type = localContext.bridgeSym.type = 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,
rfield@1380: convertArgs(tree.sym, args.toList(), tree.varargsElement)).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@1496: JCNewArray newArr = make.NewArray(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@1380: JCMethodDecl bridgeDecl = (new MemberReferenceBridger(tree, localContext).bridge());
rfield@1380: translatedMethodList = translatedMethodList.prepend(bridgeDecl);
rfield@1380: }
rfield@1380:
rfield@1380: /**
rfield@1380: * Generate an indy method call to the meta factory
rfield@1380: */
mcimadamore@1510: private JCExpression makeMetaFactoryIndyCall(JCFunctionalExpression tree, 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