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