rfield@1380: /*
ksrini@2251: * Copyright (c) 2010, 2014, 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.JCMemberReference.ReferenceKind;
rfield@1380: import com.sun.tools.javac.tree.TreeMaker;
rfield@1380: import com.sun.tools.javac.tree.TreeTranslator;
jjg@1755: import com.sun.tools.javac.code.Attribute;
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@2381: import com.sun.tools.javac.code.Symbol.TypeSymbol;
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.MethodType;
rfield@2614: import com.sun.tools.javac.code.Type.TypeVar;
rfield@1380: import com.sun.tools.javac.code.Types;
rfield@1717: import com.sun.tools.javac.comp.LambdaToMethod.LambdaAnalyzerPreprocessor.*;
mcimadamore@1612: import com.sun.tools.javac.comp.Lower.BasicFreeVarCollector;
rfield@1380: import com.sun.tools.javac.jvm.*;
rfield@1380: import com.sun.tools.javac.util.*;
rfield@1380: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
rfield@1380: import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
rfield@1380:
ksrini@2155: import java.util.EnumMap;
rfield@1380: import java.util.HashMap;
rfield@2381: import java.util.HashSet;
rfield@1380: import java.util.LinkedHashMap;
rfield@1380: import java.util.Map;
rfield@2381: import java.util.Set;
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@2614: import javax.lang.model.type.TypeKind;
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:
ksrini@2155: private Attr attr;
mcimadamore@1817: private JCDiagnostic.Factory diags;
mcimadamore@1817: private Log log;
mcimadamore@1612: private Lower lower;
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@1717: private LambdaAnalyzerPreprocessor 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:
mcimadamore@1817: /** dump statistics about lambda code generation */
mcimadamore@1817: private boolean dumpLambdaToMethodStats;
mcimadamore@1817:
ksrini@2251: /** force serializable representation, for stress testing **/
ksrini@2251: private final boolean forceSerializable;
ksrini@2251:
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:
mcimadamore@1882: /** Flag for alternate metafactories indicating the lambda object requires multiple bridges */
mcimadamore@1882: public static final int FLAG_BRIDGES = 1 << 2;
mcimadamore@1882:
ksrini@2155: //
ksrini@2155: protected static final Context.Key unlambdaKey =
ksrini@2155: new Context.Key();
ksrini@2155:
ksrini@2155: public static LambdaToMethod instance(Context context) {
ksrini@2155: LambdaToMethod instance = context.get(unlambdaKey);
ksrini@2155: if (instance == null) {
ksrini@2155: instance = new LambdaToMethod(context);
ksrini@2155: }
ksrini@2155: return instance;
ksrini@2155: }
ksrini@2155: private LambdaToMethod(Context context) {
ksrini@2155: context.put(unlambdaKey, this);
ksrini@2155: diags = JCDiagnostic.Factory.instance(context);
ksrini@2155: log = Log.instance(context);
ksrini@2155: lower = Lower.instance(context);
ksrini@2155: names = Names.instance(context);
ksrini@2155: syms = Symtab.instance(context);
ksrini@2155: rs = Resolve.instance(context);
ksrini@2155: make = TreeMaker.instance(context);
ksrini@2155: types = Types.instance(context);
ksrini@2155: transTypes = TransTypes.instance(context);
ksrini@2155: analyzer = new LambdaAnalyzerPreprocessor();
ksrini@2155: Options options = Options.instance(context);
ksrini@2155: dumpLambdaToMethodStats = options.isSet("dumpLambdaToMethodStats");
ksrini@2155: attr = Attr.instance(context);
ksrini@2251: forceSerializable = options.isSet("forceSerializable");
ksrini@2155: }
ksrini@2155: //
ksrini@2155:
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:
jlahoda@2165: private final JCClassDecl clazz;
jlahoda@2165:
jlahoda@2165: private KlassInfo(JCClassDecl clazz) {
jlahoda@2165: this.clazz = clazz;
alundblad@2047: appendedMethodList = new ListBuffer<>();
rfield@1587: deserializeCases = new HashMap>();
rfield@1587: MethodType type = new MethodType(List.of(syms.serializedLambdaType), syms.objectType,
rfield@1587: List.nil(), syms.methodClass);
jlahoda@2165: deserMethodSym = makePrivateSyntheticMethod(STATIC, names.deserializeLambda, type, clazz.sym);
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: @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@1762: 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@1762: List translate(List trees, TranslationContext> newContext) {
alundblad@2047: ListBuffer buf = new ListBuffer<>();
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@1717: tree = analyzer.analyzeAndPreprocessClass(tree);
rfield@1380: }
rfield@1587: KlassInfo prevKlassInfo = kInfo;
rfield@1380: try {
jlahoda@2165: kInfo = new KlassInfo(tree);
rfield@1380: super.visitClassDef(tree);
rfield@1587: if (!kInfo.deserializeCases.isEmpty()) {
jlahoda@2165: int prevPos = make.pos;
jlahoda@2165: try {
jlahoda@2165: make.at(tree);
jlahoda@2165: kInfo.addMethod(makeDeserializeMethod(tree.sym));
jlahoda@2165: } finally {
jlahoda@2165: make.at(prevPos);
jlahoda@2165: }
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;
jlahoda@2733: MethodSymbol sym = localContext.translatedSym;
rfield@1380: MethodType lambdaType = (MethodType) sym.type;
rfield@1380:
jjg@1755: {
jjg@1969: Symbol owner = localContext.owner;
jjg@1755: ListBuffer ownerTypeAnnos = new ListBuffer();
jjg@1755: ListBuffer lambdaTypeAnnos = new ListBuffer();
jjg@1755:
jjg@1755: for (Attribute.TypeCompound tc : owner.getRawTypeAttributes()) {
jjg@1755: if (tc.position.onLambda == tree) {
jjg@1755: lambdaTypeAnnos.append(tc);
jjg@1755: } else {
jjg@1755: ownerTypeAnnos.append(tc);
jjg@1755: }
jjg@1755: }
jjg@1755: if (lambdaTypeAnnos.nonEmpty()) {
jjg@1802: owner.setTypeAttributes(ownerTypeAnnos.toList());
jjg@1802: sym.setTypeAttributes(lambdaTypeAnnos.toList());
jjg@1755: }
jjg@1755: }
jjg@1755:
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:
alundblad@2047: ListBuffer syntheticInits = new ListBuffer<>();
rfield@1380:
rfield@2607: if (localContext.methodReferenceReceiver != null) {
rfield@2607: syntheticInits.append(localContext.methodReferenceReceiver);
rfield@2607: } else 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
mcimadamore@1882: result = makeMetafactoryIndyCall(context, 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@2607: Symbol refSym = localContext.isSignaturePolymorphic()
rfield@2202: ? localContext.sigPolySym
rfield@2202: : tree.sym;
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();
vromero@2043: init = attr.makeNullCheck(init);
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@1882: result = makeMetafactoryIndyCall(localContext, 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 {
jlahoda@2165: int prevPos = make.pos;
jlahoda@2165: try {
jlahoda@2165: make.at(tree);
jlahoda@2165:
jlahoda@2165: LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
jlahoda@2165: JCTree ltree = lambdaContext.translate(tree);
jlahoda@2165: if (ltree != null) {
jlahoda@2165: result = ltree;
jlahoda@2165: } else {
jlahoda@2165: //access to untranslated symbols (i.e. compile-time constants,
jlahoda@2165: //members defined inside the lambda body, etc.) )
jlahoda@2165: super.visitIdent(tree);
jlahoda@2165: }
jlahoda@2165: } finally {
jlahoda@2165: make.at(prevPos);
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@2380: tree.init = translate(tree.init);
rfield@2380: tree.sym = (VarSymbol) lambdaContext.getSymbolMap(LOCAL_VAR).get(tree.sym);
rfield@2380: result = tree;
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);
jlahoda@2165: int prevPos = make.pos;
jlahoda@2165: try {
jlahoda@2165: result = make.at(tree).VarDef(xsym, init);
jlahoda@2165: } finally {
jlahoda@2165: make.at(prevPos);
jlahoda@2165: }
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);
jlahoda@2165: int prevPos = make.pos;
jlahoda@2165: try {
jlahoda@2165: if (isTarget_void) {
jlahoda@2165: //target is void:
jlahoda@2165: // BODY;
jlahoda@2165: JCStatement stat = make.at(expr).Exec(expr);
jlahoda@2165: return make.Block(0, List.of(stat));
jlahoda@2165: } else if (isLambda_void && isTarget_Void) {
jlahoda@2165: //void to Void conversion:
jlahoda@2165: // BODY; return null;
jlahoda@2165: ListBuffer stats = new ListBuffer<>();
jlahoda@2165: stats.append(make.at(expr).Exec(expr));
jlahoda@2165: stats.append(make.Return(make.Literal(BOT, null).setType(syms.botType)));
jlahoda@2165: return make.Block(0, stats.toList());
jlahoda@2165: } else {
jlahoda@2165: //non-void to non-void conversion:
jlahoda@2165: // return (TYPE)BODY;
jlahoda@2165: JCExpression retExpr = transTypes.coerce(attrEnv, expr, restype);
jlahoda@2165: return make.at(retExpr).Block(0, List.of(make.Return(retExpr)));
jlahoda@2165: }
jlahoda@2165: } finally {
jlahoda@2165: make.at(prevPos);
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) {
alundblad@2047: ListBuffer cases = new ListBuffer<>();
alundblad@2047: ListBuffer breaks = new ListBuffer<>();
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@1717: * @param cons The constructor symbol
rfield@1587: */
rfield@1717: JCNewClass makeNewClass(Type ctype, List args, Symbol cons) {
rfield@1587: JCNewClass tree = make.NewClass(null,
rfield@1587: null, make.QualIdent(ctype.tsym), args, null);
rfield@1717: tree.constructor = cons;
rfield@1587: tree.type = ctype;
rfield@1587: return tree;
rfield@1587: }
rfield@1587:
rfield@1717: /** Make an attributed class instance creation expression.
rfield@1717: * @param ctype The class type.
rfield@1717: * @param args The constructor arguments.
rfield@1717: */
rfield@1717: JCNewClass makeNewClass(Type ctype, List args) {
rfield@1717: return makeNewClass(ctype, args,
rfield@1717: rs.resolveConstructor(null, attrEnv, ctype, TreeInfo.types(args), List.nil()));
rfield@1717: }
rfield@1717:
rfield@1587: private void addDeserializationCase(int implMethodKind, Symbol refSym, Type targetType, MethodSymbol samSym,
rfield@1587: DiagnosticPosition pos, List
rfield@1380:
rfield@1380: /**
rfield@2607: * Converts a method reference which cannot be used directly into a lambda
rfield@1380: */
rfield@2607: private class MemberReferenceToLambda {
rfield@1380:
rfield@1380: private final JCMemberReference tree;
rfield@1380: private final ReferenceTranslationContext localContext;
rfield@2607: private final Symbol owner;
alundblad@2047: private final ListBuffer args = new ListBuffer<>();
alundblad@2047: private final ListBuffer params = new ListBuffer<>();
rfield@1380:
rfield@2607: private JCExpression receiverExpression = null;
rfield@2607:
rfield@2607: MemberReferenceToLambda(JCMemberReference tree, ReferenceTranslationContext localContext, Symbol owner) {
rfield@1380: this.tree = tree;
rfield@1380: this.localContext = localContext;
rfield@2607: this.owner = owner;
rfield@1380: }
rfield@1380:
rfield@2607: JCLambda lambda() {
rfield@1380: int prevPos = make.pos;
rfield@1380: try {
rfield@1380: make.at(tree);
rfield@1380:
rfield@2607: //body generation - this can be either a method call or a
rfield@2607: //new instance creation expression, depending on the member reference kind
rfield@2614: VarSymbol rcvr = addParametersReturnReceiver();
rfield@2607: JCExpression expr = (tree.getMode() == ReferenceMode.INVOKE)
rfield@2607: ? expressionInvoke(rcvr)
rfield@2607: : expressionNew();
rfield@1380:
rfield@2607: JCLambda slam = make.Lambda(params.toList(), expr);
rfield@2607: slam.targets = tree.targets;
rfield@2607: slam.type = tree.type;
rfield@2607: slam.pos = tree.pos;
rfield@2607: return slam;
rfield@1380: } finally {
rfield@1380: make.at(prevPos);
rfield@1380: }
rfield@1380: }
rfield@2607:
rfield@2614: /**
rfield@2614: * Generate the parameter list for the converted member reference.
rfield@2614: *
rfield@2614: * @return The receiver variable symbol, if any
rfield@2614: */
rfield@2614: VarSymbol addParametersReturnReceiver() {
rfield@2614: Type samDesc = localContext.bridgedRefSig();
rfield@2614: List samPTypes = samDesc.getParameterTypes();
rfield@2614: List descPTypes = tree.getDescriptorType(types).getParameterTypes();
rfield@2614:
rfield@2614: // Determine the receiver, if any
rfield@2614: VarSymbol rcvr;
rfield@2614: switch (tree.kind) {
rfield@2614: case BOUND:
rfield@2614: // The receiver is explicit in the method reference
rfield@2614: rcvr = addParameter("rec$", tree.getQualifierExpression().type, false);
rfield@2614: receiverExpression = attr.makeNullCheck(tree.getQualifierExpression());
rfield@2614: break;
rfield@2614: case UNBOUND:
rfield@2614: // The receiver is the first parameter, extract it and
rfield@2614: // adjust the SAM and unerased type lists accordingly
rfield@2614: rcvr = addParameter("rec$", samDesc.getParameterTypes().head, false);
rfield@2614: samPTypes = samPTypes.tail;
rfield@2614: descPTypes = descPTypes.tail;
rfield@2614: break;
rfield@2614: default:
rfield@2614: rcvr = null;
rfield@2614: break;
rfield@2614: }
rfield@2614: List implPTypes = tree.sym.type.getParameterTypes();
rfield@2614: int implSize = implPTypes.size();
rfield@2614: int samSize = samPTypes.size();
rfield@2614: // Last parameter to copy from referenced method, exclude final var args
rfield@2614: int last = localContext.needsVarArgsConversion() ? implSize - 1 : implSize;
rfield@2614:
rfield@2614: // Failsafe -- assure match-up
rfield@2614: boolean checkForIntersection = tree.varargsElement != null || implSize == descPTypes.size();
rfield@2614:
rfield@2614: // Use parameter types of the implementation method unless the unerased
rfield@2614: // SAM parameter type is an intersection type, in that case use the
rfield@2614: // erased SAM parameter type so that the supertype relationship
rfield@2614: // the implementation method parameters is not obscured.
rfield@2614: // Note: in this loop, the lists implPTypes, samPTypes, and descPTypes
rfield@2614: // are used as pointers to the current parameter type information
rfield@2614: // and are thus not usable afterwards.
rfield@2614: for (int i = 0; implPTypes.nonEmpty() && i < last; ++i) {
rfield@2614: // By default use the implementation method parmeter type
rfield@2614: Type parmType = implPTypes.head;
rfield@2614: // If the unerased parameter type is a type variable whose
rfield@2614: // bound is an intersection (eg. ) then
rfield@2614: // use the SAM parameter type
rfield@2614: if (checkForIntersection && descPTypes.head.getKind() == TypeKind.TYPEVAR) {
rfield@2614: TypeVar tv = (TypeVar) descPTypes.head;
rfield@2614: if (tv.bound.getKind() == TypeKind.INTERSECTION) {
rfield@2614: parmType = samPTypes.head;
rfield@2614: }
rfield@2614: }
rfield@2614: addParameter("x$" + i, parmType, true);
rfield@2614:
rfield@2614: // Advance to the next parameter
rfield@2614: implPTypes = implPTypes.tail;
rfield@2614: samPTypes = samPTypes.tail;
rfield@2614: descPTypes = descPTypes.tail;
rfield@2614: }
rfield@2614: // Flatten out the var args
rfield@2614: for (int i = last; i < samSize; ++i) {
rfield@2614: addParameter("xva$" + i, tree.varargsElement, true);
rfield@2614: }
rfield@2614:
rfield@2614: return rcvr;
rfield@2614: }
rfield@2614:
rfield@2607: JCExpression getReceiverExpression() {
rfield@2607: return receiverExpression;
rfield@2607: }
rfield@2607:
rfield@2607: private JCExpression makeReceiver(VarSymbol rcvr) {
rfield@2607: if (rcvr == null) return null;
rfield@2607: JCExpression rcvrExpr = make.Ident(rcvr);
dbuck@3102: Type rcvrType = tree.ownerAccessible ? tree.sym.enclClass().type : tree.expr.type;
rfield@2607: if (rcvrType == syms.arrayClass.type) {
rfield@2607: // Map the receiver type to the actually type, not just "array"
rfield@2607: rcvrType = tree.getQualifierExpression().type;
mcimadamore@1614: }
rfield@2607: if (!rcvr.type.tsym.isSubClass(rcvrType.tsym, types)) {
rfield@2607: rcvrExpr = make.TypeCast(make.Type(rcvrType), rcvrExpr).setType(rcvrType);
rfield@2607: }
rfield@2607: return rcvrExpr;
rfield@2607: }
rfield@1380:
rfield@1380: /**
rfield@2607: * determine the receiver of the method call - the receiver can
rfield@2607: * be a type qualifier, the synthetic receiver parameter or 'super'.
rfield@1380: */
rfield@2607: private JCExpression expressionInvoke(VarSymbol rcvr) {
rfield@1380: JCExpression qualifier =
aefimov@3076: (rcvr != null) ?
aefimov@3076: makeReceiver(rcvr) :
aefimov@3076: 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@2607: * Lambda body to use for a 'new'.
rfield@1380: */
rfield@2607: private JCExpression expressionNew() {
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: //create the instance creation expression
rfield@2607: //note that method reference syntax does not allow an explicit
rfield@2607: //enclosing class (so the enclosing class is null)
rfield@2607: JCNewClass newClass = make.NewClass(null,
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@2607: VarSymbol vsym = new VarSymbol(PARAMETER | SYNTHETIC, names.fromString(name), p, owner);
rfield@2607: vsym.pos = tree.pos;
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:
mcimadamore@1882: private MethodType typeToMethodType(Type mt) {
mcimadamore@1882: Type type = types.erasure(mt);
mcimadamore@1882: return new MethodType(type.getParameterTypes(),
mcimadamore@1882: type.getReturnType(),
mcimadamore@1882: type.getThrownTypes(),
mcimadamore@1882: syms.methodClass);
mcimadamore@1882: }
mcimadamore@1882:
rfield@1380: /**
rfield@1380: * Generate an indy method call to the meta factory
rfield@1380: */
mcimadamore@1882: private JCExpression makeMetafactoryIndyCall(TranslationContext> context,
mcimadamore@1882: int refKind, Symbol refSym, List indy_args) {
mcimadamore@1882: JCFunctionalExpression tree = context.tree;
rfield@1380: //determine the static bsm args
mcimadamore@1510: MethodSymbol samSym = (MethodSymbol) types.findDescriptorSymbol(tree.type.tsym);
rfield@1380: List