aoqi@0: /*
aoqi@0: * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0: package com.sun.tools.javac.comp;
aoqi@0:
aoqi@0: import com.sun.tools.javac.tree.*;
aoqi@0: import com.sun.tools.javac.tree.JCTree.*;
aoqi@0: import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
aoqi@0: import com.sun.tools.javac.tree.TreeMaker;
aoqi@0: import com.sun.tools.javac.tree.TreeTranslator;
aoqi@0: import com.sun.tools.javac.code.Attribute;
aoqi@0: import com.sun.tools.javac.code.Kinds;
aoqi@0: import com.sun.tools.javac.code.Scope;
aoqi@0: import com.sun.tools.javac.code.Symbol;
aoqi@0: import com.sun.tools.javac.code.Symbol.ClassSymbol;
aoqi@0: import com.sun.tools.javac.code.Symbol.DynamicMethodSymbol;
aoqi@0: import com.sun.tools.javac.code.Symbol.MethodSymbol;
aoqi@0: import com.sun.tools.javac.code.Symbol.TypeSymbol;
aoqi@0: import com.sun.tools.javac.code.Symbol.VarSymbol;
aoqi@0: import com.sun.tools.javac.code.Symtab;
aoqi@0: import com.sun.tools.javac.code.Type;
aoqi@0: import com.sun.tools.javac.code.Type.MethodType;
aoqi@0: import com.sun.tools.javac.code.Types;
aoqi@0: import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
aoqi@0: import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
aoqi@0: import com.sun.tools.javac.jvm.*;
aoqi@0: import com.sun.tools.javac.util.*;
aoqi@0: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
aoqi@0: import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
aoqi@0:
aoqi@0: import java.util.EnumMap;
aoqi@0: import java.util.HashMap;
aoqi@0: import java.util.HashSet;
aoqi@0: import java.util.LinkedHashMap;
aoqi@0: import java.util.Map;
aoqi@0: import java.util.Set;
aoqi@0:
aoqi@0: import static com.sun.tools.javac.comp.LambdaToMethod.LambdaSymbolKind.*;
aoqi@0: import static com.sun.tools.javac.code.Flags.*;
aoqi@0: import static com.sun.tools.javac.code.Kinds.*;
aoqi@0: import static com.sun.tools.javac.code.TypeTag.*;
aoqi@0: import static com.sun.tools.javac.tree.JCTree.Tag.*;
aoqi@0:
aoqi@0: /**
aoqi@0: * This pass desugars lambda expressions into static methods
aoqi@0: *
aoqi@0: *
This is NOT part of any supported API.
aoqi@0: * If you write code that depends on this, you do so at your own risk.
aoqi@0: * This code and its internal interfaces are subject to change or
aoqi@0: * deletion without notice.
aoqi@0: */
aoqi@0: public class LambdaToMethod extends TreeTranslator {
aoqi@0:
aoqi@0: private Attr attr;
aoqi@0: private JCDiagnostic.Factory diags;
aoqi@0: private Log log;
aoqi@0: private Lower lower;
aoqi@0: private Names names;
aoqi@0: private Symtab syms;
aoqi@0: private Resolve rs;
aoqi@0: private TreeMaker make;
aoqi@0: private Types types;
aoqi@0: private TransTypes transTypes;
aoqi@0: private Env attrEnv;
aoqi@0:
aoqi@0: /** the analyzer scanner */
aoqi@0: private LambdaAnalyzerPreprocessor analyzer;
aoqi@0:
aoqi@0: /** map from lambda trees to translation contexts */
aoqi@0: private Map> contextMap;
aoqi@0:
aoqi@0: /** current translation context (visitor argument) */
aoqi@0: private TranslationContext> context;
aoqi@0:
aoqi@0: /** info about the current class being processed */
aoqi@0: private KlassInfo kInfo;
aoqi@0:
aoqi@0: /** dump statistics about lambda code generation */
aoqi@0: private boolean dumpLambdaToMethodStats;
aoqi@0:
aoqi@0: /** force serializable representation, for stress testing **/
aoqi@0: private final boolean forceSerializable;
aoqi@0:
aoqi@0: /** Flag for alternate metafactories indicating the lambda object is intended to be serializable */
aoqi@0: public static final int FLAG_SERIALIZABLE = 1 << 0;
aoqi@0:
aoqi@0: /** Flag for alternate metafactories indicating the lambda object has multiple targets */
aoqi@0: public static final int FLAG_MARKERS = 1 << 1;
aoqi@0:
aoqi@0: /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
aoqi@0: public static final int FLAG_BRIDGES = 1 << 2;
aoqi@0:
aoqi@0: //
aoqi@0: protected static final Context.Key unlambdaKey =
aoqi@0: new Context.Key();
aoqi@0:
aoqi@0: public static LambdaToMethod instance(Context context) {
aoqi@0: LambdaToMethod instance = context.get(unlambdaKey);
aoqi@0: if (instance == null) {
aoqi@0: instance = new LambdaToMethod(context);
aoqi@0: }
aoqi@0: return instance;
aoqi@0: }
aoqi@0: private LambdaToMethod(Context context) {
aoqi@0: context.put(unlambdaKey, this);
aoqi@0: diags = JCDiagnostic.Factory.instance(context);
aoqi@0: log = Log.instance(context);
aoqi@0: lower = Lower.instance(context);
aoqi@0: names = Names.instance(context);
aoqi@0: syms = Symtab.instance(context);
aoqi@0: rs = Resolve.instance(context);
aoqi@0: make = TreeMaker.instance(context);
aoqi@0: types = Types.instance(context);
aoqi@0: transTypes = TransTypes.instance(context);
aoqi@0: analyzer = new LambdaAnalyzerPreprocessor();
aoqi@0: Options options = Options.instance(context);
aoqi@0: dumpLambdaToMethodStats = options.isSet("dumpLambdaToMethodStats");
aoqi@0: attr = Attr.instance(context);
aoqi@0: forceSerializable = options.isSet("forceSerializable");
aoqi@0: }
aoqi@0: //
aoqi@0:
aoqi@0: private class KlassInfo {
aoqi@0:
aoqi@0: /**
aoqi@0: * list of methods to append
aoqi@0: */
aoqi@0: private ListBuffer appendedMethodList;
aoqi@0:
aoqi@0: /**
aoqi@0: * list of deserialization cases
aoqi@0: */
aoqi@0: private final Map> deserializeCases;
aoqi@0:
aoqi@0: /**
aoqi@0: * deserialize method symbol
aoqi@0: */
aoqi@0: private final MethodSymbol deserMethodSym;
aoqi@0:
aoqi@0: /**
aoqi@0: * deserialize method parameter symbol
aoqi@0: */
aoqi@0: private final VarSymbol deserParamSym;
aoqi@0:
aoqi@0: private final JCClassDecl clazz;
aoqi@0:
aoqi@0: private KlassInfo(JCClassDecl clazz) {
aoqi@0: this.clazz = clazz;
aoqi@0: appendedMethodList = new ListBuffer<>();
aoqi@0: deserializeCases = new HashMap>();
aoqi@0: MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
aoqi@0: List.nil(), syms.methodClass);
aoqi@0: deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym);
aoqi@0: deserParamSym = new VarSymbol(FINAL, names.fromString("lambda"),
aoqi@0: syms.serializedLambdaType, deserMethodSym);
aoqi@0: }
aoqi@0:
aoqi@0: private void addMethod(JCTree decl) {
aoqi@0: appendedMethodList = appendedMethodList.prepend(decl);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: //
aoqi@0: @Override
aoqi@0: public T translate(T tree) {
aoqi@0: TranslationContext> newContext = contextMap.get(tree);
aoqi@0: return translate(tree, newContext != null ? newContext : context);
aoqi@0: }
aoqi@0:
aoqi@0: T translate(T tree, TranslationContext> newContext) {
aoqi@0: TranslationContext> prevContext = context;
aoqi@0: try {
aoqi@0: context = newContext;
aoqi@0: return super.translate(tree);
aoqi@0: }
aoqi@0: finally {
aoqi@0: context = prevContext;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: List translate(List trees, TranslationContext> newContext) {
aoqi@0: ListBuffer buf = new ListBuffer<>();
aoqi@0: for (T tree : trees) {
aoqi@0: buf.append(translate(tree, newContext));
aoqi@0: }
aoqi@0: return buf.toList();
aoqi@0: }
aoqi@0:
aoqi@0: public JCTree translateTopLevelClass(Env env, JCTree cdef, TreeMaker make) {
aoqi@0: this.make = make;
aoqi@0: this.attrEnv = env;
aoqi@0: this.context = null;
aoqi@0: this.contextMap = new HashMap>();
aoqi@0: return translate(cdef);
aoqi@0: }
aoqi@0: //
aoqi@0:
aoqi@0: //
aoqi@0: /**
aoqi@0: * Visit a class.
aoqi@0: * Maintain the translatedMethodList across nested classes.
aoqi@0: * Append the translatedMethodList to the class after it is translated.
aoqi@0: * @param tree
aoqi@0: */
aoqi@0: @Override
aoqi@0: public void visitClassDef(JCClassDecl tree) {
aoqi@0: if (tree.sym.owner.kind == PCK) {
aoqi@0: //analyze class
aoqi@0: tree = analyzer.analyzeAndPreprocessClass(tree);
aoqi@0: }
aoqi@0: KlassInfo prevKlassInfo = kInfo;
aoqi@0: try {
aoqi@0: kInfo = new KlassInfo(tree);
aoqi@0: super.visitClassDef(tree);
aoqi@0: if (!kInfo.deserializeCases.isEmpty()) {
aoqi@0: int prevPos = make.pos;
aoqi@0: try {
aoqi@0: make.at(tree);
aoqi@0: kInfo.addMethod(makeDeserializeMethod(tree.sym));
aoqi@0: } finally {
aoqi@0: make.at(prevPos);
aoqi@0: }
aoqi@0: }
aoqi@0: //add all translated instance methods here
aoqi@0: List newMethods = kInfo.appendedMethodList.toList();
aoqi@0: tree.defs = tree.defs.appendList(newMethods);
aoqi@0: for (JCTree lambda : newMethods) {
aoqi@0: tree.sym.members().enter(((JCMethodDecl)lambda).sym);
aoqi@0: }
aoqi@0: result = tree;
aoqi@0: } finally {
aoqi@0: kInfo = prevKlassInfo;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Translate a lambda into a method to be inserted into the class.
aoqi@0: * Then replace the lambda site with an invokedynamic call of to lambda
aoqi@0: * meta-factory, which will use the lambda method.
aoqi@0: * @param tree
aoqi@0: */
aoqi@0: @Override
aoqi@0: public void visitLambda(JCLambda tree) {
aoqi@0: LambdaTranslationContext localContext = (LambdaTranslationContext)context;
aoqi@0: MethodSymbol sym = (MethodSymbol)localContext.translatedSym;
aoqi@0: MethodType lambdaType = (MethodType) sym.type;
aoqi@0:
aoqi@0: {
aoqi@0: Symbol owner = localContext.owner;
aoqi@0: ListBuffer ownerTypeAnnos = new ListBuffer();
aoqi@0: ListBuffer lambdaTypeAnnos = new ListBuffer();
aoqi@0:
aoqi@0: for (Attribute.TypeCompound tc : owner.getRawTypeAttributes()) {
aoqi@0: if (tc.position.onLambda == tree) {
aoqi@0: lambdaTypeAnnos.append(tc);
aoqi@0: } else {
aoqi@0: ownerTypeAnnos.append(tc);
aoqi@0: }
aoqi@0: }
aoqi@0: if (lambdaTypeAnnos.nonEmpty()) {
aoqi@0: owner.setTypeAttributes(ownerTypeAnnos.toList());
aoqi@0: sym.setTypeAttributes(lambdaTypeAnnos.toList());
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: //create the method declaration hoisting the lambda body
aoqi@0: JCMethodDecl lambdaDecl = make.MethodDef(make.Modifiers(sym.flags_field),
aoqi@0: sym.name,
aoqi@0: make.QualIdent(lambdaType.getReturnType().tsym),
aoqi@0: List.nil(),
aoqi@0: localContext.syntheticParams,
aoqi@0: lambdaType.getThrownTypes() == null ?
aoqi@0: List.nil() :
aoqi@0: make.Types(lambdaType.getThrownTypes()),
aoqi@0: null,
aoqi@0: null);
aoqi@0: lambdaDecl.sym = sym;
aoqi@0: lambdaDecl.type = lambdaType;
aoqi@0:
aoqi@0: //translate lambda body
aoqi@0: //As the lambda body is translated, all references to lambda locals,
aoqi@0: //captured variables, enclosing members are adjusted accordingly
aoqi@0: //to refer to the static method parameters (rather than i.e. acessing to
aoqi@0: //captured members directly).
aoqi@0: lambdaDecl.body = translate(makeLambdaBody(tree, lambdaDecl));
aoqi@0:
aoqi@0: //Add the method to the list of methods to be added to this class.
aoqi@0: kInfo.addMethod(lambdaDecl);
aoqi@0:
aoqi@0: //now that we have generated a method for the lambda expression,
aoqi@0: //we can translate the lambda into a method reference pointing to the newly
aoqi@0: //created method.
aoqi@0: //
aoqi@0: //Note that we need to adjust the method handle so that it will match the
aoqi@0: //signature of the SAM descriptor - this means that the method reference
aoqi@0: //should be added the following synthetic arguments:
aoqi@0: //
aoqi@0: // * the "this" argument if it is an instance method
aoqi@0: // * enclosing locals captured by the lambda expression
aoqi@0:
aoqi@0: ListBuffer syntheticInits = new ListBuffer<>();
aoqi@0:
aoqi@0: if (!sym.isStatic()) {
aoqi@0: syntheticInits.append(makeThis(
aoqi@0: sym.owner.enclClass().asType(),
aoqi@0: localContext.owner.enclClass()));
aoqi@0: }
aoqi@0:
aoqi@0: //add captured locals
aoqi@0: for (Symbol fv : localContext.getSymbolMap(CAPTURED_VAR).keySet()) {
aoqi@0: if (fv != localContext.self) {
aoqi@0: JCTree captured_local = make.Ident(fv).setType(fv.type);
aoqi@0: syntheticInits.append((JCExpression) captured_local);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: //then, determine the arguments to the indy call
aoqi@0: List indy_args = translate(syntheticInits.toList(), localContext.prev);
aoqi@0:
aoqi@0: //build a sam instance using an indy call to the meta-factory
aoqi@0: int refKind = referenceKind(sym);
aoqi@0:
aoqi@0: //convert to an invokedynamic call
aoqi@0: result = makeMetafactoryIndyCall(context, refKind, sym, indy_args);
aoqi@0: }
aoqi@0:
aoqi@0: private JCIdent makeThis(Type type, Symbol owner) {
aoqi@0: VarSymbol _this = new VarSymbol(PARAMETER | FINAL | SYNTHETIC,
aoqi@0: names._this,
aoqi@0: type,
aoqi@0: owner);
aoqi@0: return make.Ident(_this);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Translate a method reference into an invokedynamic call to the
aoqi@0: * meta-factory.
aoqi@0: * @param tree
aoqi@0: */
aoqi@0: @Override
aoqi@0: public void visitReference(JCMemberReference tree) {
aoqi@0: ReferenceTranslationContext localContext = (ReferenceTranslationContext)context;
aoqi@0:
aoqi@0: //first determine the method symbol to be used to generate the sam instance
aoqi@0: //this is either the method reference symbol, or the bridged reference symbol
aoqi@0: Symbol refSym = localContext.needsBridge()
aoqi@0: ? localContext.bridgeSym
aoqi@0: : localContext.isSignaturePolymorphic()
aoqi@0: ? localContext.sigPolySym
aoqi@0: : tree.sym;
aoqi@0:
aoqi@0: //build the bridge method, if needed
aoqi@0: if (localContext.needsBridge()) {
aoqi@0: bridgeMemberReference(tree, localContext);
aoqi@0: }
aoqi@0:
aoqi@0: //the qualifying expression is treated as a special captured arg
aoqi@0: JCExpression init;
aoqi@0: switch(tree.kind) {
aoqi@0:
aoqi@0: case IMPLICIT_INNER: /** Inner :: new */
aoqi@0: case SUPER: /** super :: instMethod */
aoqi@0: init = makeThis(
aoqi@0: localContext.owner.enclClass().asType(),
aoqi@0: localContext.owner.enclClass());
aoqi@0: break;
aoqi@0:
aoqi@0: case BOUND: /** Expr :: instMethod */
aoqi@0: init = tree.getQualifierExpression();
aoqi@0: init = attr.makeNullCheck(init);
aoqi@0: break;
aoqi@0:
aoqi@0: case UNBOUND: /** Type :: instMethod */
aoqi@0: case STATIC: /** Type :: staticMethod */
aoqi@0: case TOPLEVEL: /** Top level :: new */
aoqi@0: case ARRAY_CTOR: /** ArrayType :: new */
aoqi@0: init = null;
aoqi@0: break;
aoqi@0:
aoqi@0: default:
aoqi@0: throw new InternalError("Should not have an invalid kind");
aoqi@0: }
aoqi@0:
aoqi@0: List indy_args = init==null? List.nil() : translate(List.of(init), localContext.prev);
aoqi@0:
aoqi@0:
aoqi@0: //build a sam instance using an indy call to the meta-factory
aoqi@0: result = makeMetafactoryIndyCall(localContext, localContext.referenceKind(), refSym, indy_args);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Translate identifiers within a lambda to the mapped identifier
aoqi@0: * @param tree
aoqi@0: */
aoqi@0: @Override
aoqi@0: public void visitIdent(JCIdent tree) {
aoqi@0: if (context == null || !analyzer.lambdaIdentSymbolFilter(tree.sym)) {
aoqi@0: super.visitIdent(tree);
aoqi@0: } else {
aoqi@0: int prevPos = make.pos;
aoqi@0: try {
aoqi@0: make.at(tree);
aoqi@0:
aoqi@0: LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
aoqi@0: JCTree ltree = lambdaContext.translate(tree);
aoqi@0: if (ltree != null) {
aoqi@0: result = ltree;
aoqi@0: } else {
aoqi@0: //access to untranslated symbols (i.e. compile-time constants,
aoqi@0: //members defined inside the lambda body, etc.) )
aoqi@0: super.visitIdent(tree);
aoqi@0: }
aoqi@0: } finally {
aoqi@0: make.at(prevPos);
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void visitVarDef(JCVariableDecl tree) {
aoqi@0: LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
aoqi@0: if (context != null && lambdaContext.getSymbolMap(LOCAL_VAR).containsKey(tree.sym)) {
aoqi@0: tree.init = translate(tree.init);
aoqi@0: tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
aoqi@0: result = tree;
aoqi@0: } else if (context != null && lambdaContext.getSymbolMap(TYPE_VAR).containsKey(tree.sym)) {
aoqi@0: JCExpression init = translate(tree.init);
aoqi@0: VarSymbol xsym = (VarSymbol)lambdaContext.getSymbolMap(TYPE_VAR).get(tree.sym);
aoqi@0: int prevPos = make.pos;
aoqi@0: try {
aoqi@0: result = make.at(tree).VarDef(xsym, init);
aoqi@0: } finally {
aoqi@0: make.at(prevPos);
aoqi@0: }
aoqi@0: // Replace the entered symbol for this variable
aoqi@0: Scope sc = tree.sym.owner.members();
aoqi@0: if (sc != null) {
aoqi@0: sc.remove(tree.sym);
aoqi@0: sc.enter(xsym);
aoqi@0: }
aoqi@0: } else {
aoqi@0: super.visitVarDef(tree);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: //
aoqi@0:
aoqi@0: //
aoqi@0:
aoqi@0: private JCBlock makeLambdaBody(JCLambda tree, JCMethodDecl lambdaMethodDecl) {
aoqi@0: return tree.getBodyKind() == JCLambda.BodyKind.EXPRESSION ?
aoqi@0: makeLambdaExpressionBody((JCExpression)tree.body, lambdaMethodDecl) :
aoqi@0: makeLambdaStatementBody((JCBlock)tree.body, lambdaMethodDecl, tree.canCompleteNormally);
aoqi@0: }
aoqi@0:
aoqi@0: private JCBlock makeLambdaExpressionBody(JCExpression expr, JCMethodDecl lambdaMethodDecl) {
aoqi@0: Type restype = lambdaMethodDecl.type.getReturnType();
aoqi@0: boolean isLambda_void = expr.type.hasTag(VOID);
aoqi@0: boolean isTarget_void = restype.hasTag(VOID);
aoqi@0: boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
aoqi@0: int prevPos = make.pos;
aoqi@0: try {
aoqi@0: if (isTarget_void) {
aoqi@0: //target is void:
aoqi@0: // BODY;
aoqi@0: JCStatement stat = make.at(expr).Exec(expr);
aoqi@0: return make.Block(0, List.of(stat));
aoqi@0: } else if (isLambda_void && isTarget_Void) {
aoqi@0: //void to Void conversion:
aoqi@0: // BODY; return null;
aoqi@0: ListBuffer stats = new ListBuffer<>();
aoqi@0: stats.append(make.at(expr).Exec(expr));
aoqi@0: stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
aoqi@0: return make.Block(0, stats.toList());
aoqi@0: } else {
aoqi@0: //non-void to non-void conversion:
aoqi@0: // return (TYPE)BODY;
aoqi@0: JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
aoqi@0: return make.at(retExpr).Block(0, List.of(make.Return(retExpr)));
aoqi@0: }
aoqi@0: } finally {
aoqi@0: make.at(prevPos);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private JCBlock makeLambdaStatementBody(JCBlock block, final JCMethodDecl lambdaMethodDecl, boolean completeNormally) {
aoqi@0: final Type restype = lambdaMethodDecl.type.getReturnType();
aoqi@0: final boolean isTarget_void = restype.hasTag(VOID);
aoqi@0: boolean isTarget_Void = types.isSameType(restype, types.boxedClass(syms.voidType).type);
aoqi@0:
aoqi@0: class LambdaBodyTranslator extends TreeTranslator {
aoqi@0:
aoqi@0: @Override
aoqi@0: public void visitClassDef(JCClassDecl tree) {
aoqi@0: //do NOT recurse on any inner classes
aoqi@0: result = tree;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void visitLambda(JCLambda tree) {
aoqi@0: //do NOT recurse on any nested lambdas
aoqi@0: result = tree;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void visitReturn(JCReturn tree) {
aoqi@0: boolean isLambda_void = tree.expr == null;
aoqi@0: if (isTarget_void && !isLambda_void) {
aoqi@0: //Void to void conversion:
aoqi@0: // { TYPE $loc = RET-EXPR; return; }
aoqi@0: VarSymbol loc = makeSyntheticVar(0, names.fromString("$loc"), tree.expr.type, lambdaMethodDecl.sym);
aoqi@0: JCVariableDecl varDef = make.VarDef(loc, tree.expr);
aoqi@0: result = make.Block(0, List.of(varDef, make.Return(null)));
aoqi@0: } else if (!isTarget_void || !isLambda_void) {
aoqi@0: //non-void to non-void conversion:
aoqi@0: // return (TYPE)RET-EXPR;
aoqi@0: tree.expr = transTypes.coerce(attrEnv, tree.expr, restype);
aoqi@0: result = tree;
aoqi@0: } else {
aoqi@0: result = tree;
aoqi@0: }
aoqi@0:
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: JCBlock trans_block = new LambdaBodyTranslator().translate(block);
aoqi@0: if (completeNormally && isTarget_Void) {
aoqi@0: //there's no return statement and the lambda (possibly inferred)
aoqi@0: //return type is java.lang.Void; emit a synthetic return statement
aoqi@0: trans_block.stats = trans_block.stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
aoqi@0: }
aoqi@0: return trans_block;
aoqi@0: }
aoqi@0:
aoqi@0: private JCMethodDecl makeDeserializeMethod(Symbol kSym) {
aoqi@0: ListBuffer cases = new ListBuffer<>();
aoqi@0: ListBuffer breaks = new ListBuffer<>();
aoqi@0: for (Map.Entry> entry : kInfo.deserializeCases.entrySet()) {
aoqi@0: JCBreak br = make.Break(null);
aoqi@0: breaks.add(br);
aoqi@0: List stmts = entry.getValue().append(br).toList();
aoqi@0: cases.add(make.Case(make.Literal(entry.getKey()), stmts));
aoqi@0: }
aoqi@0: JCSwitch sw = make.Switch(deserGetter("getImplMethodName", syms.stringType), cases.toList());
aoqi@0: for (JCBreak br : breaks) {
aoqi@0: br.target = sw;
aoqi@0: }
aoqi@0: JCBlock body = make.Block(0L, List.of(
aoqi@0: sw,
aoqi@0: make.Throw(makeNewClass(
aoqi@0: syms.illegalArgumentExceptionType,
aoqi@0: List.of(make.Literal("Invalid lambda deserialization"))))));
aoqi@0: JCMethodDecl deser = make.MethodDef(make.Modifiers(kInfo.deserMethodSym.flags()),
aoqi@0: names.deserializeLambda,
aoqi@0: make.QualIdent(kInfo.deserMethodSym.getReturnType().tsym),
aoqi@0: List.nil(),
aoqi@0: List.of(make.VarDef(kInfo.deserParamSym, null)),
aoqi@0: List.nil(),
aoqi@0: body,
aoqi@0: null);
aoqi@0: deser.sym = kInfo.deserMethodSym;
aoqi@0: deser.type = kInfo.deserMethodSym.type;
aoqi@0: //System.err.printf("DESER: '%s'\n", deser);
aoqi@0: return deser;
aoqi@0: }
aoqi@0:
aoqi@0: /** Make an attributed class instance creation expression.
aoqi@0: * @param ctype The class type.
aoqi@0: * @param args The constructor arguments.
aoqi@0: * @param cons The constructor symbol
aoqi@0: */
aoqi@0: JCNewClass makeNewClass(Type ctype, List args, Symbol cons) {
aoqi@0: JCNewClass tree = make.NewClass(null,
aoqi@0: null, make.QualIdent(ctype.tsym), args, null);
aoqi@0: tree.constructor = cons;
aoqi@0: tree.type = ctype;
aoqi@0: return tree;
aoqi@0: }
aoqi@0:
aoqi@0: /** Make an attributed class instance creation expression.
aoqi@0: * @param ctype The class type.
aoqi@0: * @param args The constructor arguments.
aoqi@0: */
aoqi@0: JCNewClass makeNewClass(Type ctype, List args) {
aoqi@0: return makeNewClass(ctype, args,
aoqi@0: rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil()));
aoqi@0: }
aoqi@0:
aoqi@0: private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
aoqi@0: DiagnosticPosition pos, List
aoqi@0:
aoqi@0: /**
aoqi@0: * Generate an adapter method "bridge" for a method reference which cannot
aoqi@0: * be used directly.
aoqi@0: */
aoqi@0: private class MemberReferenceBridger {
aoqi@0:
aoqi@0: private final JCMemberReference tree;
aoqi@0: private final ReferenceTranslationContext localContext;
aoqi@0: private final ListBuffer args = new ListBuffer<>();
aoqi@0: private final ListBuffer params = new ListBuffer<>();
aoqi@0:
aoqi@0: MemberReferenceBridger(JCMemberReference tree, ReferenceTranslationContext localContext) {
aoqi@0: this.tree = tree;
aoqi@0: this.localContext = localContext;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Generate the bridge
aoqi@0: */
aoqi@0: JCMethodDecl bridge() {
aoqi@0: int prevPos = make.pos;
aoqi@0: try {
aoqi@0: make.at(tree);
aoqi@0: Type samDesc = localContext.bridgedRefSig();
aoqi@0: List samPTypes = samDesc.getParameterTypes();
aoqi@0:
aoqi@0: //an extra argument is prepended to the signature of the bridge in case
aoqi@0: //the member reference is an instance method reference (in which case
aoqi@0: //the receiver expression is passed to the bridge itself).
aoqi@0: Type recType = null;
aoqi@0: switch (tree.kind) {
aoqi@0: case IMPLICIT_INNER:
aoqi@0: recType = tree.sym.owner.type.getEnclosingType();
aoqi@0: break;
aoqi@0: case BOUND:
aoqi@0: recType = tree.getQualifierExpression().type;
aoqi@0: break;
aoqi@0: case UNBOUND:
aoqi@0: recType = samPTypes.head;
aoqi@0: samPTypes = samPTypes.tail;
aoqi@0: break;
aoqi@0: }
aoqi@0:
aoqi@0: //generate the parameter list for the bridged member reference - the
aoqi@0: //bridge signature will match the signature of the target sam descriptor
aoqi@0:
aoqi@0: VarSymbol rcvr = (recType == null)
aoqi@0: ? null
aoqi@0: : addParameter("rec$", recType, false);
aoqi@0:
aoqi@0: List refPTypes = tree.sym.type.getParameterTypes();
aoqi@0: int refSize = refPTypes.size();
aoqi@0: int samSize = samPTypes.size();
aoqi@0: // Last parameter to copy from referenced method
aoqi@0: int last = localContext.needsVarArgsConversion() ? refSize - 1 : refSize;
aoqi@0:
aoqi@0: List l = refPTypes;
aoqi@0: // Use parameter types of the referenced method, excluding final var args
aoqi@0: for (int i = 0; l.nonEmpty() && i < last; ++i) {
aoqi@0: addParameter("x$" + i, l.head, true);
aoqi@0: l = l.tail;
aoqi@0: }
aoqi@0: // Flatten out the var args
aoqi@0: for (int i = last; i < samSize; ++i) {
aoqi@0: addParameter("xva$" + i, tree.varargsElement, true);
aoqi@0: }
aoqi@0:
aoqi@0: //generate the bridge method declaration
aoqi@0: JCMethodDecl bridgeDecl = make.MethodDef(make.Modifiers(localContext.bridgeSym.flags()),
aoqi@0: localContext.bridgeSym.name,
aoqi@0: make.QualIdent(samDesc.getReturnType().tsym),
aoqi@0: List.nil(),
aoqi@0: params.toList(),
aoqi@0: tree.sym.type.getThrownTypes() == null
aoqi@0: ? List.nil()
aoqi@0: : make.Types(tree.sym.type.getThrownTypes()),
aoqi@0: null,
aoqi@0: null);
aoqi@0: bridgeDecl.sym = (MethodSymbol) localContext.bridgeSym;
aoqi@0: bridgeDecl.type = localContext.bridgeSym.type =
aoqi@0: types.createMethodTypeWithParameters(samDesc, TreeInfo.types(params.toList()));
aoqi@0:
aoqi@0: //bridge method body generation - this can be either a method call or a
aoqi@0: //new instance creation expression, depending on the member reference kind
aoqi@0: JCExpression bridgeExpr = (tree.getMode() == ReferenceMode.INVOKE)
aoqi@0: ? bridgeExpressionInvoke(makeReceiver(rcvr))
aoqi@0: : bridgeExpressionNew();
aoqi@0:
aoqi@0: //the body is either a return expression containing a method call,
aoqi@0: //or the method call itself, depending on whether the return type of
aoqi@0: //the bridge is non-void/void.
aoqi@0: bridgeDecl.body = makeLambdaExpressionBody(bridgeExpr, bridgeDecl);
aoqi@0:
aoqi@0: return bridgeDecl;
aoqi@0: } finally {
aoqi@0: make.at(prevPos);
aoqi@0: }
aoqi@0: }
aoqi@0: //where
aoqi@0: private JCExpression makeReceiver(VarSymbol rcvr) {
aoqi@0: if (rcvr == null) return null;
aoqi@0: JCExpression rcvrExpr = make.Ident(rcvr);
aoqi@0: Type rcvrType = tree.sym.enclClass().type;
aoqi@0: if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
aoqi@0: rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
aoqi@0: }
aoqi@0: return rcvrExpr;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * determine the receiver of the bridged method call - the receiver can
aoqi@0: * be either the synthetic receiver parameter or a type qualifier; the
aoqi@0: * original qualifier expression is never used here, as it might refer
aoqi@0: * to symbols not available in the static context of the bridge
aoqi@0: */
aoqi@0: private JCExpression bridgeExpressionInvoke(JCExpression rcvr) {
aoqi@0: JCExpression qualifier =
aoqi@0: tree.sym.isStatic() ?
aoqi@0: make.Type(tree.sym.owner.type) :
aoqi@0: (rcvr != null) ?
aoqi@0: rcvr :
aoqi@0: tree.getQualifierExpression();
aoqi@0:
aoqi@0: //create the qualifier expression
aoqi@0: JCFieldAccess select = make.Select(qualifier, tree.sym.name);
aoqi@0: select.sym = tree.sym;
aoqi@0: select.type = tree.sym.erasure(types);
aoqi@0:
aoqi@0: //create the method call expression
aoqi@0: JCExpression apply = make.Apply(List.nil(), select,
aoqi@0: convertArgs(tree.sym, args.toList(), tree.varargsElement)).
aoqi@0: setType(tree.sym.erasure(types).getReturnType());
aoqi@0:
aoqi@0: apply = transTypes.coerce(apply, localContext.generatedRefSig().getReturnType());
aoqi@0: setVarargsIfNeeded(apply, tree.varargsElement);
aoqi@0: return apply;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * the enclosing expression is either 'null' (no enclosing type) or set
aoqi@0: * to the first bridge synthetic parameter
aoqi@0: */
aoqi@0: private JCExpression bridgeExpressionNew() {
aoqi@0: if (tree.kind == ReferenceKind.ARRAY_CTOR) {
aoqi@0: //create the array creation expression
aoqi@0: JCNewArray newArr = make.NewArray(
aoqi@0: make.Type(types.elemtype(tree.getQualifierExpression().type)),
aoqi@0: List.of(make.Ident(params.first())),
aoqi@0: null);
aoqi@0: newArr.type = tree.getQualifierExpression().type;
aoqi@0: return newArr;
aoqi@0: } else {
aoqi@0: JCExpression encl = null;
aoqi@0: switch (tree.kind) {
aoqi@0: case UNBOUND:
aoqi@0: case IMPLICIT_INNER:
aoqi@0: encl = make.Ident(params.first());
aoqi@0: }
aoqi@0:
aoqi@0: //create the instance creation expression
aoqi@0: JCNewClass newClass = make.NewClass(encl,
aoqi@0: List.nil(),
aoqi@0: make.Type(tree.getQualifierExpression().type),
aoqi@0: convertArgs(tree.sym, args.toList(), tree.varargsElement),
aoqi@0: null);
aoqi@0: newClass.constructor = tree.sym;
aoqi@0: newClass.constructorType = tree.sym.erasure(types);
aoqi@0: newClass.type = tree.getQualifierExpression().type;
aoqi@0: setVarargsIfNeeded(newClass, tree.varargsElement);
aoqi@0: return newClass;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private VarSymbol addParameter(String name, Type p, boolean genArg) {
aoqi@0: VarSymbol vsym = new VarSymbol(0, names.fromString(name), p, localContext.bridgeSym);
aoqi@0: params.append(make.VarDef(vsym, null));
aoqi@0: if (genArg) {
aoqi@0: args.append(make.Ident(vsym));
aoqi@0: }
aoqi@0: return vsym;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Bridges a member reference - this is needed when:
aoqi@0: * * Var args in the referenced method need to be flattened away
aoqi@0: * * super is used
aoqi@0: */
aoqi@0: private void bridgeMemberReference(JCMemberReference tree, ReferenceTranslationContext localContext) {
aoqi@0: kInfo.addMethod(new MemberReferenceBridger(tree, localContext).bridge());
aoqi@0: }
aoqi@0:
aoqi@0: private MethodType typeToMethodType(Type mt) {
aoqi@0: Type type = types.erasure(mt);
aoqi@0: return new MethodType(type.getParameterTypes(),
aoqi@0: type.getReturnType(),
aoqi@0: type.getThrownTypes(),
aoqi@0: syms.methodClass);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Generate an indy method call to the meta factory
aoqi@0: */
aoqi@0: private JCExpression makeMetafactoryIndyCall(TranslationContext> context,
aoqi@0: int refKind, Symbol refSym, List indy_args) {
aoqi@0: JCFunctionalExpression tree = context.tree;
aoqi@0: //determine the static bsm args
aoqi@0: MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
aoqi@0: List