com.redhat.ceylon.compiler.java.codegen.Invocation Maven / Gradle / Ivy
/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.redhat.ceylon.compiler.java.codegen;
import static com.redhat.ceylon.compiler.java.codegen.AbstractTransformer.JT_COMPANION;
import static com.redhat.ceylon.compiler.java.codegen.AbstractTransformer.JT_NO_PRIMITIVES;
import static com.redhat.ceylon.compiler.java.codegen.AbstractTransformer.JT_RAW;
import static com.redhat.ceylon.compiler.java.codegen.AbstractTransformer.JT_TYPE_ARGUMENT;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.FINAL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
import com.redhat.ceylon.compiler.java.codegen.AbstractTransformer.BoxingStrategy;
import com.redhat.ceylon.compiler.java.codegen.Strategy.DefaultParameterMethodOwner;
import com.redhat.ceylon.compiler.typechecker.analyzer.AnalyzerUtil;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Comprehension;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Expression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.FunctionArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PositionalArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PositionalArgumentList;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.QualifiedTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SequencedArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.StaticMemberOrTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Term;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCAnnotation;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCExpression;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCReturn;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCStatement;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCVariableDecl;
import com.redhat.ceylon.langtools.tools.javac.util.List;
import com.redhat.ceylon.langtools.tools.javac.util.ListBuffer;
import com.redhat.ceylon.compiler.typechecker.tree.TreeUtil;
import com.redhat.ceylon.model.loader.JvmBackendUtil;
import com.redhat.ceylon.model.loader.NamingBase.Suffix;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.ClassAlias;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Constructor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.ParameterList;
import com.redhat.ceylon.model.typechecker.model.Reference;
import com.redhat.ceylon.model.typechecker.model.Scope;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypedReference;
import com.redhat.ceylon.model.typechecker.model.Value;
abstract class Invocation {
static boolean onValueType(AbstractTransformer gen, Tree.Term primary, Declaration primaryDeclaration) {
// don't use the value type mechanism for optimised Java arrays get/set invocations
if (primary instanceof Tree.QualifiedMemberOrTypeExpression){
Tree.Primary qmePrimary = ((Tree.QualifiedMemberOrTypeExpression) primary).getPrimary();
if(qmePrimary != null
&& gen.isJavaArray(qmePrimary.getTypeModel())
&& (primaryDeclaration.getName().equals("get")
|| primaryDeclaration.getName().equals("set"))) {
return false;
} else {
return ((Tree.QualifiedMemberOrTypeExpression) primary).getMemberOperator() instanceof Tree.MemberOp
&& Decl.isValueTypeDecl(qmePrimary)
&& (CodegenUtil.isUnBoxed(qmePrimary) || gen.isJavaArray(qmePrimary.getTypeModel()));
}
} else {
return false;
}
}
protected final AbstractTransformer gen;
private final Node node;
private final Tree.Term primary;
private final Declaration primaryDeclaration;
private final Type returnType;
protected boolean handleBoxing;
protected boolean unboxed;
protected boolean erased;
protected BoxingStrategy boxingStrategy;
private final Tree.Primary qmePrimary;
private final boolean onValueType;
private boolean callable;
public boolean isCallable() {
return callable;
}
protected Invocation(AbstractTransformer gen,
Tree.Term primary, Declaration primaryDeclaration,
Type returnType, Node node) {
this.gen = gen;
this.primary = primary;
this.primaryDeclaration = primaryDeclaration;
this.returnType = returnType;
this.node = node;
if (primary instanceof Tree.QualifiedMemberOrTypeExpression){
this.qmePrimary = ((Tree.QualifiedMemberOrTypeExpression) primary).getPrimary();
} else {
this.qmePrimary = null;
}
this.onValueType = onValueType(gen, primary, primaryDeclaration)
&& (!(primary instanceof Tree.QualifiedMemberExpression) || !(((Tree.QualifiedMemberExpression)primary).getMemberOperator() instanceof Tree.SpreadOp));
}
public String toString() {
return getClass().getName() + " of " + node;
}
Node getNode() {
return node;
}
Tree.Term getPrimary() {
return primary;
}
Declaration getPrimaryDeclaration() {
return primaryDeclaration;
}
Type getReturnType() {
return returnType;
}
Tree.Primary getQmePrimary() {
return qmePrimary;
}
boolean isOnValueType() {
return onValueType;
}
protected Type getParameterTypeForValueType(Reference producedReference, Parameter param) {
// we need to find the interface for this method
Type paramType = param.getModel().getReference().getFullType().getType();
Scope paramContainer = param.getModel().getContainer();
if(paramContainer instanceof TypedDeclaration){
TypedDeclaration method = (TypedDeclaration) paramContainer;
if(method.getContainer() instanceof TypeDeclaration
&& !(method.getContainer() instanceof Constructor)){
TypeDeclaration container = (TypeDeclaration) method.getContainer();
Type qualifyingType = producedReference.getQualifyingType();
if(qualifyingType != null){
Type supertype = qualifyingType.getSupertype(container);
return paramType.substitute(supertype);
}
}
}
return paramType;
}
protected boolean isParameterRaw(Parameter param){
Type type = param.getType();
return type == null ? false : type.isRaw();
}
protected boolean isParameterWithConstrainedTypeParameters(Parameter param) {
return gen.hasConstrainedTypeParameters(param);
}
protected boolean isParameterWithDependentCovariantTypeParameters(Parameter param) {
Type type = param.getType();
return gen.hasDependentCovariantTypeParameters(type);
}
protected abstract void addReifiedArguments(ListBuffer result);
public final void setUnboxed(boolean unboxed) {
this.unboxed = unboxed;
}
public final void handleBoxing(boolean b) {
handleBoxing = b;
}
public final void setErased(boolean erased) {
this.erased = erased;
}
public final void setBoxingStrategy(BoxingStrategy boxingStrategy) {
this.boxingStrategy = boxingStrategy;
}
class TransformedInvocationPrimary {
final JCExpression expr;
final String selector;
TransformedInvocationPrimary(JCExpression expr, String selector) {
this.expr = expr;
this.selector = selector;
}
}
protected TransformedInvocationPrimary transformPrimary(JCExpression primaryExpr,
String selector) {
if (Decl.isJavaStaticOrInterfacePrimary(getPrimary())) {
Declaration methodOrClass = ((Tree.QualifiedMemberOrTypeExpression)getPrimary()).getDeclaration();
if (Decl.isJavaArrayFrom(methodOrClass)) {
JCExpression fn = gen.makeUnwrapArray(methodOrClass);
return new TransformedInvocationPrimary(
fn, null);
}
if (methodOrClass instanceof Function
&& getQmePrimary() instanceof Tree.BaseTypeExpression) {
return new TransformedInvocationPrimary(gen.naming.makeName(
(Function)methodOrClass, Naming.NA_FQ | Naming.NA_WRAPPER_UNQUOTED),
null);
} else if (methodOrClass instanceof Class) {
return new TransformedInvocationPrimary(
gen.makeJavaType(((Tree.StaticMemberOrTypeExpression)getPrimary()).getTarget().getType(), JT_NO_PRIMITIVES),
null);
}
}
if (isMemberRefInvocation()) {
JCExpression callable = gen.expressionGen().transformMemberReference((Tree.QualifiedMemberOrTypeExpression)getPrimary(), (Tree.MemberOrTypeExpression)getQmePrimary());
// The callable is a Callable we generate ourselves, it can never be erased to Object so there's no need to unerase
selector = Naming.getCallableMethodName();
return new TransformedInvocationPrimary(callable, selector);
}
JCExpression actualPrimExpr;
if (getPrimary() instanceof Tree.QualifiedTypeExpression
&& ((Tree.QualifiedTypeExpression)getPrimary()).getPrimary() instanceof Tree.BaseTypeExpression
&& !Decl.isConstructor(getPrimaryDeclaration())) {
actualPrimExpr = gen.naming.makeQualifiedThis(primaryExpr);
} else {
actualPrimExpr = primaryExpr;
}
if (getPrimary() instanceof Tree.BaseTypeExpression) {
Tree.BaseTypeExpression type = (Tree.BaseTypeExpression)getPrimary();
Declaration declaration = type.getDeclaration();
if (Strategy.generateInstantiator(declaration)) {
if (Decl.withinInterface(declaration)) {
if (primaryExpr != null) {
// if we have some other primary then respect that
actualPrimExpr = primaryExpr;
} else {
// if the class being instantiated is
// within a class we expect the instantiation to be
// accessible from `this`, so use null
// otherwise we must be in an companion class, so we
// need to qualify the instantiator invocation with $this
actualPrimExpr = type.getScope().getInheritingDeclaration(declaration) instanceof Class ? null : gen.naming.makeQuotedThis();
}
} else if (declaration.isToplevel()) {
actualPrimExpr = null;
}
// if the decl is not toplevel (but the primary is a base type
// we must be invoking a member imported from an object
// in which case the qualifer is needed.
}
if (Decl.isConstructor(declaration)) {
selector = null;
}
} else {
if (getPrimary() instanceof Tree.QualifiedMemberOrTypeExpression) {
Tree.QualifiedMemberOrTypeExpression type = (Tree.QualifiedMemberOrTypeExpression)getPrimary();
Declaration declaration = type.getDeclaration();
if (Decl.isConstructor(declaration)) {
Class constructedClass = Decl.getConstructedClass(declaration);
if (Decl.withinInterface(constructedClass)) {
if (Strategy.generateInstantiator(declaration)) {
// Stef: this is disgusting and some of that logic has to be duplicated for base member/type
// expressions. it reeks of special-cases that should not be
Interface containerInterface = (Interface)constructedClass.getContainer();
if(primaryExpr != null)
actualPrimExpr = primaryExpr;
else if(type.getPrimary() instanceof Tree.BaseTypeExpression
// if we are within an interface impl class, access it via our $this
&& gen.expressionGen().needDollarThis((Tree.BaseTypeExpression)type.getPrimary()))
actualPrimExpr = gen.naming.makeQuotedThis();
else
// otherwise we're within a class that implements that interface,
// access it via our companion
actualPrimExpr = gen.naming.makeCompanionFieldName(containerInterface);
} else {
actualPrimExpr = null;
}
}
}
} else if (getPrimary() instanceof Tree.BaseMemberOrTypeExpression) {
Tree.BaseMemberOrTypeExpression type = (Tree.BaseMemberOrTypeExpression)getPrimary();
Declaration declaration = type.getDeclaration();
if (Decl.isConstructor(declaration)) {
selector = null;
}
}
if (isIndirect()) {
if (getPrimaryDeclaration() != null
&& (Decl.isGetter(getPrimaryDeclaration())
|| Decl.isToplevel(getPrimaryDeclaration())
|| (Decl.isValueOrSharedOrCapturedParam(getPrimaryDeclaration())
&& Decl.isCaptured(getPrimaryDeclaration())
&& !Decl.isLocalNotInitializer(getPrimaryDeclaration())))) {
// We need to invoke the getter to obtain the Callable
actualPrimExpr = gen.make().Apply(null,
gen.naming.makeQualIdent(primaryExpr, selector),
List.nil());
} else if (selector != null) {
actualPrimExpr = gen.naming.makeQualIdent(primaryExpr, selector);
} else if (getPrimaryDeclaration() == null || !((TypedDeclaration)getPrimaryDeclaration()).getType().isTypeConstructor()) {
actualPrimExpr = gen.naming.makeQualifiedName(primaryExpr, (TypedDeclaration)getPrimaryDeclaration(), Naming.NA_MEMBER);
}
actualPrimExpr = unboxCallableIfNecessary(actualPrimExpr, getPrimary());
if (gen.isVariadicCallable(getPrimary().getTypeModel())) {
selector = Naming.getCallableVariadicMethodName();
this.callable = true;
} else {
selector = Naming.getCallableMethodName();
this.callable = true;
}
// If it's indirect the primary might be erased
actualPrimExpr = gen.expressionGen().applyErasureAndBoxing(actualPrimExpr,
getPrimary().getTypeModel(),
getPrimary().getTypeErased(),
true, // boxed
BoxingStrategy.BOXED,
getPrimary().getTypeModel(),
0);
} else if ((getPrimaryDeclaration() instanceof Function
&& ((Function)getPrimaryDeclaration()).isParameter()// i.e. functional parameter
&& (!JvmBackendUtil.createMethod((Function)getPrimaryDeclaration())) // not class member, or not shared/captured
// we may create a method, but if we're accessing it from a default parameter expression
// we need to access the Callable parameter, no the member method
|| gen.expressionGen().isWithinDefaultParameterExpression(getPrimaryDeclaration().getContainer()))) {
if (selector != null) {
actualPrimExpr = gen.naming.makeQualIdent(primaryExpr, selector);
} else {
actualPrimExpr = gen.naming.makeQualifiedName(primaryExpr, (TypedDeclaration)getPrimaryDeclaration(), Naming.NA_MEMBER);
}
actualPrimExpr = unboxCallableIfNecessary(actualPrimExpr, getPrimary());
if (gen.isVariadicCallable(getPrimary().getTypeModel())) {
selector = Naming.getCallableVariadicMethodName();
this.callable = true;
} else {
selector = Naming.getCallableMethodName();
this.callable = true;
}
}
}
return new TransformedInvocationPrimary(actualPrimExpr, selector);
}
protected JCExpression unboxCallableIfNecessary(JCExpression actualPrimExpr, Tree.Term primary) {
Type primaryModel = primary.getTypeModel();
if (gen.willEraseToObject(primaryModel)) {
actualPrimExpr = gen.make().TypeCast(gen.makeQuotedQualIdentFromString("ceylon.language.Callable"), actualPrimExpr);
}
return actualPrimExpr;
}
boolean isMemberRefInvocation() {
return false;
}
public boolean isIndirect() {
return false;
}
public void location(CallBuilder callBuilder) {
callBuilder.location(getNode());
}
public boolean isUnknownArguments() {
return false;
}
public Constructor getConstructor() {
Declaration primaryDeclaration = getPrimaryDeclaration();
return getConstructorFromPrimary(primaryDeclaration);
}
protected Constructor getConstructorFromPrimary(
Declaration primaryDeclaration) {
if (Decl.isConstructor(primaryDeclaration)) {
primaryDeclaration = Decl.getConstructor(primaryDeclaration);
}
if (primaryDeclaration instanceof Constructor) {
return (Constructor)primaryDeclaration;
} else if (primaryDeclaration instanceof ClassAlias) {
TypeDeclaration aliasCtor = ((ClassAlias) primaryDeclaration).getConstructor();
while (aliasCtor instanceof ClassAlias) {
aliasCtor = ((ClassAlias) aliasCtor).getConstructor();
}
if (aliasCtor instanceof Constructor) {
return (Constructor)aliasCtor;
} else {
return null;
}
} else if (primaryDeclaration instanceof Class
&& Decl.getDefaultConstructor((Class)primaryDeclaration) != null) {
return Decl.getDefaultConstructor((Class)primaryDeclaration);
} else {
return null;
}
}
protected boolean erasedArgument(Tree.Term expr) {
// technically expr.getTypeErased() is all we need
// but it usually results in unnecessary casting of null
// the exception to that is if and switch expressions
// with all branches being null.
return expr.getTypeErased()
&& gen.isNullValue(expr.getTypeModel())
&& (expr instanceof Tree.SwitchExpression
||expr instanceof Tree.IfExpression);
}
}
abstract class SimpleInvocation extends Invocation {
public SimpleInvocation(AbstractTransformer gen, Tree.Term primary,
Declaration primaryDeclaration, Type returnType, Node node) {
super(gen, primary, primaryDeclaration, returnType, node);
}
protected abstract boolean isParameterVariadicStar(int argIndex);
protected abstract boolean isParameterVariadicPlus(int argIndex);
protected final boolean isParameterSequenced(int argIndex) {
return isParameterVariadicStar(argIndex) || isParameterVariadicPlus(argIndex);
}
protected final boolean isParameterJavaVariadic(int argIndex) {
return isParameterSequenced(argIndex) && isJavaVariadicMethod();
}
protected abstract Type getParameterType(int argIndex);
//protected abstract String getParameterName(int argIndex);
protected abstract JCExpression getParameterExpression(int argIndex);
protected abstract boolean isArgumentComprehension(int argIndex);
protected abstract boolean getParameterUnboxed(int argIndex);
protected abstract boolean getParameterSmall(int argIndex);
protected abstract BoxingStrategy getParameterBoxingStrategy(int argIndex);
protected abstract boolean hasParameter(int argIndex);
// to be overridden
public boolean isParameterCoerced(int argIndex) {
return false;
}
// to be overridden
protected boolean isParameterRaw(int argIndex) {
return false;
}
// to be overridden
protected boolean isParameterWithConstrainedTypeParameters(int argIndex) {
return false;
}
// to be overridden
protected boolean isParameterWithDependentCovariantTypeParameters(int argIndex) {
return false;
}
/** Gets the number of arguments actually being supplied */
protected abstract int getNumArguments();
/** Gets the number of parameters that are available */
protected abstract int getNumParameters();
/**
* Gets the transformed expression supplying the argument value for the
* given argument index
*/
//protected abstract JCExpression getTransformedArgumentExpression(int argIndex);
protected abstract boolean isSpread();
protected abstract boolean isArgumentSpread(int argIndex);
/**
* For subclasses if the target method doesn't support default values for variadic
* using overloading.
*/
protected boolean requiresEmptyForVariadic() {
return false;
}
protected boolean isJavaVariadicMethod() {
if(isJavaMethod())
return true;
if(getPrimaryDeclaration() instanceof Function) {
Declaration refinedDeclaration = JvmBackendUtil.getTopmostRefinedDeclaration(getPrimaryDeclaration());
// variadic params are not propagated to constructors
if(refinedDeclaration instanceof Function)
return gen.isJavaMethod((Function) refinedDeclaration);
}
return false;
}
private boolean isJavaMethod() {
if(getPrimaryDeclaration() instanceof Function) {
return gen.isJavaMethod((Function) getPrimaryDeclaration());
} else if (getPrimaryDeclaration() instanceof Class) {
return gen.isJavaCtor((Class) getPrimaryDeclaration());
}
return false;
}
protected abstract Tree.Expression getArgumentExpression(int argIndex);
protected Type getArgumentType(int argIndex) {
return getArgumentExpression(argIndex).getTypeModel();
}
protected abstract JCExpression getTransformedArgumentExpression(int argIndex);
}
/**
* Generates calls to Callable methods. This is for regular {@code Callable} objects and not
* functional parameters, which have more info like parameter names and default values.
*/
class IndirectInvocation extends SimpleInvocation {
private final java.util.List parameterTypes;
private final java.util.List argumentExpressions;
private final Comprehension comprehension;
private final boolean variadic;
private final boolean spread;
private final boolean unknownArguments;
public IndirectInvocation(
AbstractTransformer gen,
Tree.Term primary,
Declaration primaryDeclaration,
Tree.InvocationExpression invocation) {
super(gen, primary, primaryDeclaration, invocation.getTypeModel(), invocation);
Type callableType = primary.getTypeModel();
this.unknownArguments = gen.isUnknownArgumentsCallable(callableType);
final java.util.List parameterTypes;
// if we have an unknown parameter list, like Callble, we can't look at parameter types
// note that ATM the typechecker only allows a single argument to be passed in spread form in this
// case so we don't need to look at parameter types
if(!this.unknownArguments){
// find the parameter types
final java.util.List tas = new ArrayList<>();
tas.add(gen.getReturnTypeOfCallable(callableType));
for (int ii = 0, l = gen.getNumParametersOfCallable(callableType); ii < l; ii++) {
tas.add(gen.getParameterTypeOfCallable(callableType, ii));
}
this.variadic = gen.isVariadicCallable(callableType);
//final java.util.List tas = primary.getTypeModel().getTypeArgumentList();
parameterTypes = tas.subList(1, tas.size());
}else{
this.variadic = false; // we don't know
parameterTypes = Collections.emptyList();
}
PositionalArgumentList positionalArgumentList = invocation.getPositionalArgumentList();
final java.util.List argumentExpressions = new ArrayList(positionalArgumentList.getPositionalArguments().size());
boolean spread = false;
Comprehension comprehension = null;
for (Tree.PositionalArgument argument : positionalArgumentList.getPositionalArguments()) {
if(argument instanceof Tree.ListedArgument)
argumentExpressions.add(((Tree.ListedArgument)argument).getExpression());
else if(argument instanceof Tree.SpreadArgument){
argumentExpressions.add(((Tree.SpreadArgument)argument).getExpression());
spread = true;
}else{
comprehension = (Comprehension) argument;
}
}
this.spread = spread;
this.comprehension = comprehension;
this.argumentExpressions = argumentExpressions;
this.parameterTypes = parameterTypes;
}
@Override
public boolean isIndirect() {
return true;
}
@Override
boolean isMemberRefInvocation() {
return CodegenUtil.isMemberReferenceInvocation((Tree.InvocationExpression)getNode());
}
@Override
public int getNumParameters() {
return parameterTypes.size();
}
@Override
protected void addReifiedArguments(ListBuffer result) {
// can never be parameterised
}
@Override
protected boolean isParameterVariadicStar(int argIndex) {
return variadic && argIndex >= parameterTypes.size() - 1;
}
@Override
protected boolean isParameterVariadicPlus(int argIndex) {
return variadic && argIndex >= parameterTypes.size() - 1;
}
@Override
protected Type getParameterType(int argIndex) {
// in the Java code, all Callable.call() params are of type Object so let's not
// pretend they are typed, this saves a lot of casting.
// except for sequenced parameters where we do care about the iterated type
if(isParameterSequenced(argIndex)){
if (isArgumentSpread(argIndex)
&& isParameterVariadicPlus(argIndex)) {
// We might end up calling Util.sequentialInstance to handle
// the spread and if the spread argument is empty, we need to
// transform it to something of sequential type so we can
// call sequentialInstance().
// No worries if we don't end up calling sequentialInstance(),
// because the call it indirect, so not type-safe at the Java level anyway
return gen.typeFact().getSequentialType(gen.typeFact().getIteratedType(parameterTypes.get(parameterTypes.size()-1)));
} else {
return parameterTypes.get(parameterTypes.size()-1);
}
}
return gen.typeFact().getObjectType();
}
@Override
protected JCExpression getParameterExpression(int argIndex) {
return gen.naming.makeQuotedIdent("arg" + argIndex);
}
@Override
protected boolean getParameterUnboxed(int argIndex) {
return false;
}
@Override
protected BoxingStrategy getParameterBoxingStrategy(int argIndex) {
return BoxingStrategy.BOXED;
}
@Override
protected boolean getParameterSmall(int argIndex) {
return false;
}
@Override
protected boolean hasParameter(int argIndex) {
return true;
}
@Override
protected int getNumArguments() {
return argumentExpressions.size() + (comprehension != null ? 1 : 0);
}
@Override
protected boolean isSpread() {
return comprehension != null || spread;
}
@Override
public boolean isUnknownArguments(){
return unknownArguments;
}
@Override
protected boolean isArgumentSpread(int argIndex) {
if(spread) // spread args must be last argument
return argIndex == argumentExpressions.size() - 1;
if(comprehension != null) // comprehension must be last
return argIndex == argumentExpressions.size();
return false;
}
@Override
protected boolean isArgumentComprehension(int argIndex){
// comprehensions are listed as last argument after all argumentExpressions
return comprehension != null && argIndex == argumentExpressions.size();
}
@Override
protected Tree.Expression getArgumentExpression(int argIndex) {
return argumentExpressions.get(argIndex);
}
@Override
protected Type getArgumentType(int argIndex) {
if (argIndex == argumentExpressions.size() && comprehension != null) {
return gen.typeFact().getSequentialType(comprehension.getTypeModel());
}
return super.getArgumentType(argIndex);
}
@Override
protected JCExpression getTransformedArgumentExpression(int argIndex) {
if (argIndex == argumentExpressions.size() && comprehension != null) {
Type type = getParameterType(argIndex);
return gen.expressionGen().comprehensionAsSequential(comprehension, type);
}
Tree.Expression expr = getArgumentExpression(argIndex);
if (expr.getTerm() instanceof FunctionArgument) {
FunctionArgument farg = (FunctionArgument)expr.getTerm();
return gen.expressionGen().transform(farg, getParameterType(argIndex));
}
return gen.expressionGen().transformArg(this, argIndex);
}
@Override
public void location(CallBuilder callBuilder) {
callBuilder.location(((Tree.InvocationExpression)getNode()).getPositionalArgumentList());
}
}
/**
* An abstract implementation of InvocationBuilder support invocation
* via positional arguments. Supports with sequenced arguments but not
* defaulted arguments.
*/
abstract class DirectInvocation extends SimpleInvocation {
private final Reference producedReference;
protected DirectInvocation(
AbstractTransformer gen,
Tree.Term primary,
Declaration primaryDeclaration,
Reference producedReference, Type returnType,
Node node) {
super(gen, primary, primaryDeclaration, returnType, node);
this.producedReference = producedReference;
}
protected Reference appliedReference() {
return producedReference;
}
/**
* Gets the Parameter corresponding to the given argument
* @param argIndex
* @return
*/
protected abstract Parameter getParameter(int argIndex);
@Override
protected boolean isParameterVariadicStar(int argIndex) {
return getParameter(argIndex).isSequenced() && !getParameter(argIndex).isAtLeastOne();
}
@Override
protected boolean isParameterVariadicPlus(int argIndex) {
return getParameter(argIndex).isSequenced() && getParameter(argIndex).isAtLeastOne();
}
@Override
protected Type getParameterType(int argIndex) {
int flags = AbstractTransformer.TP_TO_BOUND;
if(isParameterSequenced(argIndex)
&& isJavaVariadicMethod()
&& isSpread())
flags |= AbstractTransformer.TP_SEQUENCED_TYPE;
return gen.expressionGen().getTypeForParameter(getParameter(argIndex), appliedReference(), flags);
}
@Override
protected JCExpression getParameterExpression(int argIndex) {
return gen.naming.makeName(getParameter(argIndex).getModel(), Naming.NA_MEMBER);
}
@Override
protected boolean getParameterUnboxed(int argIndex) {
return getParameter(argIndex).getModel().getUnboxed();
}
@Override
protected BoxingStrategy getParameterBoxingStrategy(int argIndex) {
Parameter param = getParameter(argIndex);
if (isOnValueType() && Decl.isValueTypeDecl(getParameterTypeForValueType(producedReference, param))) {
return BoxingStrategy.UNBOXED;
}
return CodegenUtil.getBoxingStrategy(param.getModel());
}
@Override
protected boolean getParameterSmall(int argIndex) {
Parameter param = getParameter(argIndex);
return Decl.isSmall(param.getModel());
}
@Override
protected boolean hasParameter(int argIndex) {
return getParameter(argIndex) != null;
}
@Override
protected void addReifiedArguments(ListBuffer result) {
if (getPrimary().getTypeModel().isTypeConstructor()) {
addTypeConstructorArguments(getPrimary().getTypeModel().getTypeArgumentList(), result);
} else {
addReifiedArguments(gen, producedReference, result);
}
}
private void addTypeConstructorArguments(
java.util.List typeArgumentList,
ListBuffer result) {
int ii = 0;
for(Type reifiedTypeArg : typeArgumentList)
result.append(new ExpressionAndType(
gen.make().Indexed(
gen.makeUnquotedIdent("applied"),
gen.make().Literal(ii++)),
gen.makeTypeDescriptorType()));
}
static void addReifiedArguments(AbstractTransformer gen, Reference producedReference, ListBuffer result) {
java.util.List reifiedTypeArgs = gen.makeReifiedTypeArguments(producedReference);
for(JCExpression reifiedTypeArg : reifiedTypeArgs)
result.append(new ExpressionAndType(reifiedTypeArg, gen.makeTypeDescriptorType()));
}
}
/**
* InvocationBuilder used for 'normal' method and initializer invocations via
* positional arguments. Supports sequenced and defaulted arguments.
*/
class PositionalInvocation extends DirectInvocation {
private final Tree.PositionalArgumentList positional;
private final java.util.List parameters;
public PositionalInvocation(
AbstractTransformer gen,
Tree.Term primary,
Declaration primaryDeclaration,
Reference producedReference, Tree.InvocationExpression invocation,
java.util.List parameters) {
super(gen, primary, unfake(primaryDeclaration), unfake(producedReference), invocation.getTypeModel(), invocation);
positional = invocation.getPositionalArgumentList();
this.parameters = parameters;
}
private static Declaration unfake(Declaration primaryDeclaration) {
if(primaryDeclaration.isCoercionPoint()){
if(primaryDeclaration instanceof Function)
return ((Function)primaryDeclaration).getRealFunction();
return ((Class)primaryDeclaration).getRealClass();
}
return primaryDeclaration;
}
private static Reference unfake(Reference producedReference) {
Declaration declaration = producedReference.getDeclaration();
if(declaration.isCoercionPoint()){
if(declaration instanceof Function){
Function realFunction = ((Function)declaration).getRealFunction();
return realFunction.appliedTypedReference(producedReference.getQualifyingType(), producedReference.getTypeArgumentList());
}else{
Class realClass = ((Class)declaration).getRealClass();
return realClass.appliedType(producedReference.getQualifyingType(), producedReference.getTypeArgumentList());
}
}
return producedReference;
}
java.util.List getParameters() {
return parameters;
}
Tree.PositionalArgumentList getPositional() {
return positional;
}
@Override
protected Tree.Expression getArgumentExpression(int argIndex) {
PositionalArgument arg = getPositional().getPositionalArguments().get(argIndex);
if(arg instanceof Tree.ListedArgument)
return ((Tree.ListedArgument) arg).getExpression();
if(arg instanceof Tree.SpreadArgument)
return ((Tree.SpreadArgument) arg).getExpression();
throw new BugException("argument expression is " + arg.getNodeType());
}
@Override
protected boolean isArgumentComprehension(int argIndex){
PositionalArgument arg = getPositional().getPositionalArguments().get(argIndex);
return arg instanceof Tree.Comprehension;
}
@Override
protected Type getArgumentType(int argIndex) {
PositionalArgument arg = getPositional().getPositionalArguments().get(argIndex);
if (arg instanceof Tree.Comprehension) {
return gen.typeFact().getSequentialType(arg.getTypeModel());
}
return arg.getTypeModel();
}
@Override
protected JCExpression getTransformedArgumentExpression(int argIndex) {
PositionalArgument arg = getPositional().getPositionalArguments().get(argIndex);
// FIXME: I don't like much this weird special case here
if(arg instanceof Tree.ListedArgument){
Tree.Expression expr = ((Tree.ListedArgument) arg).getExpression();
if (expr.getTerm() instanceof FunctionArgument) {
FunctionArgument farg = (FunctionArgument)expr.getTerm();
return gen.expressionGen().transform(farg, getParameterType(argIndex));
}
}
// special case for comprehensions which are not expressions
if(arg instanceof Tree.Comprehension){
Type type = getParameterType(argIndex);
return gen.expressionGen().comprehensionAsSequential((Comprehension) arg, type);
}
return gen.expressionGen().transformArg(this, argIndex);
}
@Override
protected Parameter getParameter(int argIndex) {
int realIndex = argIndex >= parameters.size() ? parameters.size()-1 : argIndex;
Parameter param = parameters.get(realIndex);
if(param.getModel().isCoercionPoint()){
Parameter realParameter = ((Functional)getPrimaryDeclaration()).getFirstParameterList().getParameters().get(realIndex);
return realParameter;
}
return param;
}
@Override
public boolean isParameterCoerced(int argIndex) {
// FIXME: suboptimal copy
Parameter param = parameters.get(argIndex >= parameters.size() ? parameters.size()-1 : argIndex);
return param.getModel().isCoercionPoint();
}
@Override
protected int getNumArguments() {
return getPositional().getPositionalArguments().size();
}
@Override
protected int getNumParameters() {
return parameters.size();
}
@Override
protected boolean isSpread() {
java.util.List args = getPositional().getPositionalArguments();
if(args.isEmpty())
return false;
PositionalArgument last = args.get(args.size()-1);
return last instanceof Tree.SpreadArgument || last instanceof Tree.Comprehension;
}
@Override
protected boolean isArgumentSpread(int argIndex) {
PositionalArgument arg = getPositional().getPositionalArguments().get(argIndex);
return arg instanceof Tree.SpreadArgument || arg instanceof Tree.Comprehension;
}
@Override
protected boolean isParameterRaw(int argIndex){
return isParameterRaw(getParameter(argIndex));
}
@Override
protected boolean isParameterWithConstrainedTypeParameters(int argIndex) {
return isParameterWithConstrainedTypeParameters(getParameter(argIndex));
}
@Override
protected boolean isParameterWithDependentCovariantTypeParameters(int argIndex) {
return isParameterWithDependentCovariantTypeParameters(getParameter(argIndex));
}
protected boolean hasDefaultArgument(int ii) {
return getParameters().get(ii).isDefaulted();
}
@Override
public void location(CallBuilder callBuilder) {
callBuilder.location(positional);
}
}
/**
* InvocationBuilder used for constructing invocations of {@code super()}
* when creating constructors.
*/
class SuperInvocation extends PositionalInvocation {
static Declaration unaliasedPrimaryDeclaration(Tree.InvocationExpression invocation) {
Declaration declaration = ((Tree.MemberOrTypeExpression)invocation.getPrimary()).getDeclaration();
if (declaration instanceof ClassAlias) {
Type et = ((ClassAlias) declaration).getExtendedType();
if (et!=null) {
declaration = et.getDeclaration();
}
}
return declaration;
}
private final ClassOrInterface sub;
private CtorDelegation delegation;
private boolean delegationDelegation;
SuperInvocation(AbstractTransformer gen,
ClassOrInterface sub,
CtorDelegation delegation,
Tree.InvocationExpression invocation,
ParameterList parameterList,
boolean delegationDelegation) {
super(gen,
invocation.getPrimary(),
unaliasedPrimaryDeclaration(invocation),
((Tree.MemberOrTypeExpression)invocation.getPrimary()).getTarget(),
invocation,
parameterList.getParameters());
this.sub = sub;
this.delegation = delegation;
this.delegationDelegation = delegationDelegation;
}
CtorDelegation getDelegation() {
return delegation;
}
ClassOrInterface getSub() {
return sub;
}
@Override
public Constructor getConstructor() {
// For the constructor we need the possibly-aliased primary declaration
Declaration primaryDeclaration = ((Tree.MemberOrTypeExpression)getPrimary()).getDeclaration();
return getConstructorFromPrimary(primaryDeclaration);
}
public boolean isDelegationDelegation() {
return delegationDelegation;
}
@Override
protected void addReifiedArguments(ListBuffer result) {
if (!isDelegationDelegation()) {
super.addReifiedArguments(result);
} else {
addReifiedArguments(gen, sub.getReference(), result);
}
}
}
/**
* InvocationBuilder for constructing the invocation of a method reference
* used when implementing {@code Callable.call()}.
*
* This will be used when you do:
*
*
* void f(){
* value callable = f;
* }
*
*
* And will generate the code required to put inside the Callable's {@code $call} method to
* invoke {@code f}: {@code f();}. The generation of the Callable or its methods is not done here.
*/
class CallableInvocation extends DirectInvocation {
private final java.util.List callableParameters;
private final java.util.List functionalParameters;
private final int parameterCount;
private boolean tempVars;
private Naming.SyntheticName instanceFieldName;
private boolean instanceFieldIsBoxed;
private boolean isCallableToFunctionalInterfaceBridge;
public CallableInvocation(
AbstractTransformer gen, Naming.SyntheticName primaryName, boolean primaryIsBoxed, Tree.Term primary,
Declaration primaryDeclaration, Reference producedReference, Type returnType,
Tree.Term expr, ParameterList parameterList, int parameterCount, boolean tempVars) {
super(gen, primary, primaryDeclaration, producedReference, returnType, expr);
this.instanceFieldName = primaryName;
this.instanceFieldIsBoxed = primaryIsBoxed;
Functional functional = null;
boolean useParameterList = false;
if(primary instanceof Tree.MemberOrTypeExpression){
Declaration declaration = ((Tree.MemberOrTypeExpression) primary).getDeclaration();
if(declaration instanceof Functional)
functional = (Functional) declaration;
else{
// inherit params of a Callable value ref, for SAM wrapper
useParameterList = true;
}
}else if(primary instanceof Tree.FunctionArgument)
functional = ((Tree.FunctionArgument) primary).getDeclarationModel();
else if(primary instanceof Tree.InvocationExpression){
useParameterList = true;
isCallableToFunctionalInterfaceBridge = true;
}
if(useParameterList)
callableParameters = parameterList.getParameters();
else if(functional != null)
callableParameters = functional.getFirstParameterList().getParameters();
else
callableParameters = Collections.emptyList();
functionalParameters = parameterList.getParameters();
this.parameterCount = parameterCount;
// FIXME: only do this if not Callable subtype?
TypedReference samRef = gen.checkForFunctionalInterface(producedReference.getType());
if(samRef != null){
TypedDeclaration samDeclaration = samRef.getDeclaration();
setUnboxed(samDeclaration.getUnboxed());
}else{
setUnboxed(expr.getUnboxed());
}
setBoxingStrategy(BoxingStrategy.BOXED);// Must be boxed because non-primitive return type
handleBoxing(true);
if (producedReference.getDeclaration() instanceof TypedDeclaration) {
TypedDeclaration tdecl = (TypedDeclaration) producedReference.getDeclaration();
setErased(CodegenUtil.hasTypeErased(tdecl)|| CodegenUtil.hasTypeErased(primary));
}
this.tempVars = tempVars;
}
@Override
boolean isOnValueType() {
return super.isOnValueType() && !instanceFieldIsBoxed;
}
@Override
protected int getNumArguments() {
return parameterCount;
}
@Override
protected int getNumParameters() {
return functionalParameters.size();
}
@Override
protected boolean isSpread() {
return isParameterSequenced(getNumArguments() - 1);
}
@Override
protected boolean isArgumentSpread(int argIndex) {
return isSpread() && argIndex == getNumArguments() - 1;
}
@Override
protected boolean isArgumentComprehension(int argIndex){
return true;
}
@Override
protected JCExpression getTransformedArgumentExpression(int argIndex) {
Parameter param = callableParameters.get(argIndex);
// note: we don't deal with unboxing here, as that is taken care of already by CallableBuilder by unboxing the
// Callable arguments into unboxed local vars if required and if it's a value type
String paramName;
if (tempVars) {
paramName = Naming.getCallableTempVarName(param);
} else if (getPrimaryDeclaration() instanceof Class &&
((Class)getPrimaryDeclaration()).hasConstructors()) {
paramName = Naming.getAliasedParameterName(param);
} else {
paramName = param.getName();
}
return gen.makeUnquotedIdent(paramName);
}
@Override
protected Parameter getParameter(int index) {
return functionalParameters.get(index);
}
@Override
protected Expression getArgumentExpression(int argIndex) {
throw new BugException("I override getTransformedArgumentExpression(), so should never be called");
}
@Override
protected Type getArgumentType(int argIndex) {
Parameter param = callableParameters.get(argIndex);
return getParameterTypeForValueType(appliedReference(), param);
}
@Override
public void location(CallBuilder callBuilder) {
callBuilder.location(null);
}
protected TransformedInvocationPrimary transformPrimary(JCExpression primaryExpr,
String selector) {
JCExpression transformedPrimary = instanceFieldName != null ? instanceFieldName.makeIdent() : primaryExpr;
String transformedSelector = selector;
if(getPrimaryDeclaration() instanceof Value){
if(transformedPrimary == null)
transformedPrimary = gen.makeUnquotedIdent(selector);
else
transformedPrimary = gen.makeQualIdent(primaryExpr, selector);
Type primaryType = ((Value)getPrimaryDeclaration()).getType();
TypedReference samRef = gen.checkForFunctionalInterface(primaryType);
if(samRef != null)
transformedSelector = samRef.getDeclaration().getName();
else
transformedSelector = Naming.getCallableMethodName();
}else if(getPrimaryDeclaration() instanceof Function
&& getPrimaryDeclaration().isParameter()
// This is fishy at best
&& transformedPrimary == null){
// FIXME: there's a good chance this should be merged with the previous block
transformedPrimary = gen.makeUnquotedIdent(selector);
transformedSelector = Naming.getCallableMethodName();
}else if(isCallableToFunctionalInterfaceBridge){
transformedSelector = Naming.getCallableMethodName();
}
return new TransformedInvocationPrimary(transformedPrimary, transformedSelector);
}
public Constructor getConstructor() {
return getConstructorFromPrimary(appliedReference().getDeclaration());
}
@Override
protected void addReifiedArguments(ListBuffer result) {
if(!isCallableToFunctionalInterfaceBridge)
super.addReifiedArguments(result);
}
}
/**
* InvocationBuilder for methods specified with a method reference. This builds the specifier invocation
* within the body of the specified method.
*
* For example for {@code void foo(); foo = f;} we generate: {@code f()} that you would then place into
* the generated method for {@code foo}.
*/
class MethodReferenceSpecifierInvocation extends DirectInvocation {
private final Function method;
private boolean variadic;
private java.util.List targetParameters;
private java.util.List sourceParameters;
public MethodReferenceSpecifierInvocation(
AbstractTransformer gen, Tree.Primary primary,
Declaration primaryDeclaration,
Reference producedReference, Function method, Tree.SpecifierExpression node) {
super(gen, primary, primaryDeclaration, producedReference, method.getType(), node);
this.method = method;
setUnboxed(primary.getUnboxed());
setBoxingStrategy(CodegenUtil.getBoxingStrategy(method));
this.sourceParameters = method.getFirstParameterList().getParameters();
this.targetParameters = ((Functional)getPrimaryDeclaration()).getFirstParameterList().getParameters();
this.variadic = getPrimaryDeclaration().isVariadic();
if(!targetParameters.isEmpty()){
Parameter lastParam = targetParameters.get(targetParameters.size() - 1);
this.variadic |= lastParam.isSequenced();
}
}
@Override
protected int getNumArguments() {
return sourceParameters.size();
}
@Override
protected int getNumParameters() {
return targetParameters.size();
}
@Override
protected JCExpression getTransformedArgumentExpression(int argIndex) {
Type exprType = getArgumentType(argIndex);
Parameter declaredParameter = getParameter(argIndex);
Parameter declaredArgument = sourceParameters.get(argIndex);
JCExpression result = gen.naming.makeName(declaredArgument.getModel(), Naming.NA_MEMBER);
BoxingStrategy boxingStrategy;
if(variadic){
boxingStrategy = BoxingStrategy.BOXED;
}else{
boxingStrategy = CodegenUtil.getBoxingStrategy(declaredParameter.getModel());
}
result = gen.expressionGen().applyErasureAndBoxing(
result,
exprType,
!CodegenUtil.isUnBoxed(declaredArgument.getModel()),
boxingStrategy,
declaredParameter.getType());
return result;
}
@Override
protected Parameter getParameter(int argIndex) {
if(variadic && argIndex >= targetParameters.size())
return targetParameters.get(targetParameters.size() - 1);
return targetParameters.get(argIndex);
}
@Override
protected boolean isSpread() {
return sourceParameters.get(getNumArguments() - 1).isSequenced();
}
@Override
protected boolean isArgumentSpread(int argIndex) {
return isSpread() && argIndex == getNumArguments() - 1;
}
@Override
protected Expression getArgumentExpression(int argIndex) {
throw new BugException("I override getArgumentExpression(), so should never be called");
}
@Override
protected boolean isArgumentComprehension(int argIndex){
throw new BugException("I override getTransformedArgumentExpression(), so should never be called");
}
@Override
public void location(CallBuilder callBuilder) {
callBuilder.location(null);
}
@Override
protected Type getArgumentType(int argIndex) {
Parameter param = sourceParameters.get(argIndex);
return getParameterTypeForValueType(appliedReference(), param);
}
}
/**
* InvocationBuilder for methods specified eagerly with a Callable. This builds the Callable invocation
* within the body of the specified method.
*
* For example for {@code void foo(); foo = f;} we generate: {@code f.$call()} that you would then place into
* the generated method for {@code foo}.
*/
class CallableSpecifierInvocation extends Invocation {
private final Function method;
private final JCExpression callable;
private final Term callableTerm;
public CallableSpecifierInvocation(
AbstractTransformer gen,
Function method,
JCExpression callableExpr,
Tree.Term callableTerm,
Node node) {
super(gen, null, null, method.getType(), node);
this.callable = callableExpr;
this.callableTerm = callableTerm;
this.method = method;
// Because we're calling a callable, and they always return a
// boxed result
setUnboxed(false);
setBoxingStrategy(method.getUnboxed() ? BoxingStrategy.UNBOXED : BoxingStrategy.BOXED);
}
@Override
protected void addReifiedArguments(ListBuffer result) {
// nothing required here
}
JCExpression getCallable() {
if(callableTerm != null)
return unboxCallableIfNecessary(callable, callableTerm);
return callable;
}
Function getMethod() {
return method;
}
}
/**
* InvocationBuilder for 'normal' method and initializer invocations
* using named arguments.
*/
class NamedArgumentInvocation extends Invocation {
private final Tree.NamedArgumentList namedArgumentList;
private final ListBuffer vars = new ListBuffer();
private final Naming.SyntheticName callVarName;
private final Naming.SyntheticName varBaseName;
private final Set argNames = new HashSet();
private final TreeMap argsNamesByIndex = new TreeMap();
private final TreeMap argsAndTypes = new TreeMap();
private final Set bound = new HashSet();
private Reference producedReference;
public NamedArgumentInvocation(
AbstractTransformer gen, Tree.Term primary,
Declaration primaryDeclaration,
Reference producedReference,
Tree.InvocationExpression invocation) {
super(gen, primary, primaryDeclaration, invocation.getTypeModel(), invocation);
this.producedReference = producedReference;
namedArgumentList = invocation.getNamedArgumentList();
varBaseName = gen.naming.alias("arg");
callVarName = varBaseName.suffixedBy(Suffix.$callable$);
}
@Override
protected void addReifiedArguments(ListBuffer result) {
Reference ref = gen.resolveAliasesForReifiedTypeArguments(producedReference);
if(!gen.supportsReified(ref.getDeclaration()))
return;
int tpCount = gen.getTypeParameters(ref).size();
for(int tpIndex = 0;tpIndex getVars() {
return vars;
}
Iterable getArgsNamesByIndex() {
return argsNamesByIndex.values();
}
Iterable getArgumentsAndTypes() {
return argsAndTypes.values();
}
/**
* Constructs the vars used in the Let expression
*/
private void buildVars() {
if (getPrimaryDeclaration() == null) {
return;
}
boolean prev = gen.expressionGen().withinInvocation(false);
java.util.List namedArguments = namedArgumentList.getNamedArguments();
SequencedArgument sequencedArgument = namedArgumentList.getSequencedArgument();
java.util.List paramLists = ((Functional)getPrimaryDeclaration()).getParameterLists();
java.util.List declaredParams = paramLists.get(0).getParameters();
appendVarsForNamedArguments(namedArguments, declaredParams);
appendVarsForReifiedTypeArguments();
if(sequencedArgument != null)
appendVarsForSequencedArguments(sequencedArgument, declaredParams);
boolean hasDefaulted = appendVarsForDefaulted(declaredParams);
DefaultParameterMethodOwner owner = Strategy.defaultParameterMethodOwner(getPrimaryDeclaration());
if (hasDefaulted
&& owner != DefaultParameterMethodOwner.STATIC
&& owner != DefaultParameterMethodOwner.OUTER
&& owner != DefaultParameterMethodOwner.OUTER_COMPANION) {
vars.prepend(makeThis());
}
gen.expressionGen().withinInvocation(prev);
}
private void appendVarsForReifiedTypeArguments() {
java.util.List reifiedTypeArgs = gen.makeReifiedTypeArguments(producedReference);
int index = 0;
for(JCExpression reifiedTypeArg : reifiedTypeArgs){
Naming.SyntheticName argName = reifiedTypeArgName(index);
JCVariableDecl varDecl = gen.makeVar(argName, gen.makeTypeDescriptorType(), reifiedTypeArg);
this.vars.append(varDecl);
index++;
}
}
private void appendVarsForSequencedArguments(Tree.SequencedArgument sequencedArgument, java.util.List declaredParams) {
// FIXME: this is suspisciously similar to AbstractTransformer.makeIterable(java.util.List list, Type seqElemType)
// and possibly needs to be merged
Parameter parameter = sequencedArgument.getParameter();
Type parameterType = parameterType(parameter, parameter.getType(), gen.TP_TO_BOUND);
// find out the individual type, we use the argument type for the value, and the param type for the temp variable
gen.at(sequencedArgument);
Type tupleType = AnalyzerUtil.getTupleType(sequencedArgument.getPositionalArguments(), gen.typeFact(), false);
// we can't just generate types like Foo> if the target type param is not raw because the bounds will
// not match, so we go raw, we also ignore primitives naturally
int flags = JT_RAW | JT_NO_PRIMITIVES;
Type argumentsType;
Type iteratedType;
Type absentType;
if (gen.typeFact().isIterableType(tupleType)) {
argumentsType = tupleType.getSupertype(gen.typeFact().getIterableDeclaration());
iteratedType = gen.typeFact().getIteratedType(argumentsType);
absentType = gen.typeFact().getIteratedAbsentType(argumentsType);
} else if (gen.typeFact().isJavaIterableType(tupleType)) {
argumentsType = tupleType.getSupertype(gen.typeFact().getJavaIterableDeclaration());
iteratedType = gen.typeFact().getJavaIteratedType(argumentsType);
absentType = gen.typeFact().getNullType();
} else if (gen.typeFact().isJavaObjectArrayType(tupleType)) {
argumentsType = tupleType.getSupertype(gen.typeFact().getJavaObjectArrayDeclaration());
iteratedType = gen.typeFact().getJavaArrayElementType(argumentsType);
absentType = gen.typeFact().getNullType();
} else if (gen.typeFact().isJavaPrimitiveArrayType(tupleType)) {
argumentsType = tupleType.getSupertype(gen.typeFact().getJavaObjectArrayDeclaration());
iteratedType = gen.typeFact().getJavaArrayElementType(tupleType);
absentType = gen.typeFact().getNullType();
} else {
throw BugException.unhandledTypeCase(tupleType);
}
JCTree.JCExpression sequenceValue;
if (Strategy.useConstantIterable(sequencedArgument)) {
sequenceValue = gen.makeConstantIterable(sequencedArgument, iteratedType, absentType, flags);
} else {
sequenceValue = gen.makeLazyIterable(sequencedArgument, iteratedType, absentType, flags);
}
JCTree.JCExpression sequenceType = gen.makeJavaType(parameterType, flags);
Naming.SyntheticName argName = argName(parameter);
JCTree.JCVariableDecl varDecl = gen.makeVar(argName, sequenceType, sequenceValue);
gen.at(getPrimary());
bind(parameter, argName, gen.makeJavaType(parameterType, flags), List.of(varDecl));
}
private JCExpression makeDefaultedArgumentMethodCall(Parameter param) {
JCExpression thisExpr = null;
switch (Strategy.defaultParameterMethodOwner(param.getModel())) {
case SELF:
break;
case STATIC:
if (param.getDeclaration().isStaticallyImportable()) {
thisExpr = gen.makeStaticQualifier(param.getDeclaration());
}
break;
case OUTER:
if(getQmePrimary() != null && !Decl.isConstructor(getPrimaryDeclaration()))
thisExpr = callVarName.makeIdent();
break;
case OUTER_COMPANION:
if (getQmePrimary() != null && !Decl.isConstructor(getPrimaryDeclaration())) {
thisExpr = callVarName.makeIdent();
}
break;
case INIT_COMPANION:
thisExpr = varBaseName.suffixedBy(Suffix.$argthis$).makeIdent();
if (isOnValueType()) {
thisExpr = gen.boxType(thisExpr, getQmePrimary().getTypeModel());
}
break;
}
JCExpression defaultValueMethodName = gen.naming.makeDefaultedParamMethod(thisExpr, param);
JCExpression argExpr = gen.at(getNode()).Apply(null,
defaultValueMethodName,
makeVarRefArgumentList(param));
return argExpr;
}
// Make a list of ($arg0, $arg1, ... , $argN)
// or ($arg$this$, $arg0, $arg1, ... , $argN)
private List makeVarRefArgumentList(Parameter param) {
ListBuffer names = new ListBuffer();
if (!Strategy.defaultParameterMethodStatic(getPrimaryDeclaration())
&& Strategy.defaultParameterMethodTakesThis(param.getModel())) {
names.append(varBaseName.suffixedBy(Suffix.$argthis$).makeIdent());
}
// put all the required reified type args too
Reference ref = gen.resolveAliasesForReifiedTypeArguments(producedReference);
int tpCount = gen.getTypeParameters(ref).size();
for(int tpIndex = 0;tpIndexnCopies(parameterList(param).size(), null));
//}
final Naming.SyntheticName argName = varBaseName.suffixedBy(paramIndex);
if (this.argsNamesByIndex.containsValue(argName)) {
throw new BugException();
}
//if (!this.argNames.add(argName)) {
// throw new BugException();
//}
return argName;
}
/** Generates the argument name; namedArg may be null if no
* argument was given explicitly */
private Naming.SyntheticName reifiedTypeArgName(int index) {
return varBaseName.suffixedBy(Suffix.$reified$, index);
}
private java.util.List parameterList(Parameter param) {
Functional functional = (Functional)param.getDeclaration();
return functional.getFirstParameterList().getParameters();
}
private int parameterIndex(Parameter param) {
return parameterList(param).indexOf(param);
}
private Type parameterType(Parameter declaredParam, Type pt, int flags) {
if(declaredParam == null)
return pt;
return gen.getTypeForParameter(declaredParam, producedReference, flags);
}
private void appendVarsForNamedArguments(
java.util.List namedArguments,
java.util.List declaredParams) {
// Assign vars for each named argument given
for (Tree.NamedArgument namedArg : namedArguments) {
gen.at(namedArg);
Parameter declaredParam = namedArg.getParameter();
Naming.SyntheticName argName = argName(declaredParam);
if (namedArg instanceof Tree.SpecifiedArgument) {
bindSpecifiedArgument((Tree.SpecifiedArgument)namedArg, declaredParam, argName);
} else if (namedArg instanceof Tree.MethodArgument) {
bindMethodArgument((Tree.MethodArgument)namedArg, declaredParam, argName);
} else if (namedArg instanceof Tree.ObjectArgument) {
bindObjectArgument((Tree.ObjectArgument)namedArg, declaredParam, argName);
} else if (namedArg instanceof Tree.AttributeArgument) {
bindAttributeArgument((Tree.AttributeArgument)namedArg, declaredParam, argName);
} else {
throw BugException.unhandledNodeCase(namedArg);
}
}
}
private void bindSpecifiedArgument(Tree.SpecifiedArgument specifiedArg,
Parameter declaredParam, Naming.SyntheticName argName) {
ListBuffer statements;
Tree.Expression expr = specifiedArg.getSpecifierExpression().getExpression();
Type type = parameterType(declaredParam, expr.getTypeModel(), gen.TP_TO_BOUND);
final BoxingStrategy boxType = getNamedParameterBoxingStrategy(declaredParam);
int jtFlags = 0;
int exprFlags = 0;
if(boxType == BoxingStrategy.BOXED)
jtFlags |= JT_TYPE_ARGUMENT;
if(!isParameterRaw(declaredParam)) {
exprFlags |= ExpressionTransformer.EXPR_EXPECTED_TYPE_NOT_RAW;
}
if(isParameterWithConstrainedTypeParameters(declaredParam)) {
exprFlags |= ExpressionTransformer.EXPR_EXPECTED_TYPE_HAS_CONSTRAINED_TYPE_PARAMETERS;
// we can't just generate types like Foo> if the target type param is not raw because the bounds will
// not match, so we go raw
jtFlags |= JT_RAW;
}
if(isParameterWithDependentCovariantTypeParameters(declaredParam)) {
exprFlags |= ExpressionTransformer.EXPR_EXPECTED_TYPE_HAS_DEPENDENT_COVARIANT_TYPE_PARAMETERS;
}
if (erasedArgument(TreeUtil.unwrapExpressionUntilTerm(expr))) {
exprFlags |= ExpressionTransformer.EXPR_DOWN_CAST;
}
if (CodegenUtil.downcastForSmall(expr, specifiedArg.getParameter().getModel())) {
exprFlags |= ExpressionTransformer.EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK;
}
JCExpression typeExpr = gen.makeJavaType(type, jtFlags);
gen.at(specifiedArg);
JCExpression argExpr = gen.expressionGen().transformExpression(expr, boxType, type, exprFlags);
gen.at(specifiedArg);
JCVariableDecl varDecl = gen.makeVar(argName, typeExpr, argExpr);
statements = ListBuffer.of(varDecl);
bind(declaredParam, argName, gen.makeJavaType(type, jtFlags), statements.toList());
}
private void bindMethodArgument(Tree.MethodArgument methodArg,
Parameter declaredParam, Naming.SyntheticName argName) {
ListBuffer statements;
Function model = methodArg.getDeclarationModel();
List body;
boolean prevNoExpressionlessReturn = gen.statementGen().noExpressionlessReturn;
boolean prevSyntheticClassBody = gen.expressionGen().withinSyntheticClassBody(Decl.isMpl(model) || gen.expressionGen().isWithinSyntheticClassBody());
try {
gen.expressionGen().withinSyntheticClassBody(true);
gen.statementGen().noExpressionlessReturn = gen.isAnything(model.getType());
if (methodArg.getBlock() != null) {
body = gen.statementGen().transformBlock(methodArg.getBlock());
if (!methodArg.getBlock().getDefinitelyReturns()) {
if (gen.isAnything(model.getType())) {
body = body.append(gen.make().Return(gen.makeNull()));
} else {
body = body.append(gen.make().Return(gen.makeErroneous(methodArg.getBlock(), "compiler bug: non-void method does not definitely return")));
}
}
} else {
Expression expr = methodArg.getSpecifierExpression().getExpression();
BoxingStrategy boxing = CodegenUtil.getBoxingStrategy(model);
Type type = model.getType();
JCExpression transExpr = gen.expressionGen().transformExpression(expr, boxing, type);
JCReturn returnStat = gen.make().Return(transExpr);
body = List.of(returnStat);
}
} finally {
gen.expressionGen().withinSyntheticClassBody(prevSyntheticClassBody);
gen.statementGen().noExpressionlessReturn = prevNoExpressionlessReturn;
}
Type callableType = model.appliedReference(null, Collections.emptyList()).getFullType();
CallableBuilder callableBuilder = CallableBuilder.methodArgument(gen.gen(),
methodArg,
model,
callableType,
Collections.singletonList(methodArg.getParameterLists().get(0)),
gen.classGen().transformMplBody(methodArg.getParameterLists(), model, body));
JCExpression callable = callableBuilder.build();
gen.at(methodArg);
JCExpression typeExpr = gen.makeJavaType(callableType, JT_RAW);
JCVariableDecl varDecl = gen.makeVar(argName, typeExpr, callable);
statements = ListBuffer.of(varDecl);
bind(declaredParam, argName, gen.makeJavaType(callableType), statements.toList());
}
private void bindObjectArgument(Tree.ObjectArgument objectArg,
Parameter declaredParam, Naming.SyntheticName argName) {
ListBuffer statements;
List object = gen.classGen().transformObjectArgument(objectArg);
// No need to worry about boxing (it cannot be a boxed type)
JCVariableDecl varDecl = gen.makeLocalIdentityInstance(argName.getName(), Naming.quoteClassName(objectArg.getIdentifier().getText()), false);
statements = toStmts(objectArg, object).append(varDecl);
bind(declaredParam, argName, gen.makeJavaType(objectArg.getType().getTypeModel()), statements.toList());
}
private void bindAttributeArgument(Tree.AttributeArgument attrArg,
Parameter declaredParam, Naming.SyntheticName argName) {
ListBuffer statements;
final Value model = attrArg.getDeclarationModel();
final String name = model.getName();
String className = Naming.getAttrClassName(model, 0);
final List attrClass = gen.gen().transformAttribute(model, name, className, null, attrArg.getBlock(), attrArg.getSpecifierExpression(), null, null);
TypedReference typedRef = gen.getTypedReference(model);
TypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef);
Type nonWideningType = gen.nonWideningType(typedRef, nonWideningTypedRef);
Type type = parameterType(declaredParam, model.getType(), 0);
final BoxingStrategy boxType = getNamedParameterBoxingStrategy(declaredParam);
JCExpression initValue = gen.make().Apply(null,
gen.makeSelect(gen.makeUnquotedIdent(className), Naming.getGetterName(model)),
List.nil());
initValue = gen.expressionGen().applyErasureAndBoxing(
initValue,
nonWideningType,
!CodegenUtil.isUnBoxed(nonWideningTypedRef.getDeclaration()),
boxType,
type);
JCTree.JCVariableDecl var = gen.make().VarDef(
gen.make().Modifiers(FINAL, List.nil()),
argName.asName(),
gen.makeJavaType(type, boxType==BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0),
initValue);
statements = toStmts(attrArg, attrClass).append(var);
bind(declaredParam, argName, gen.makeJavaType(type, boxType==BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0),
statements.toList());
}
private void bind(Parameter param, Naming.SyntheticName argName, JCExpression argType, List statements) {
this.vars.appendList(statements);
this.argsAndTypes.put(parameterIndex(param), new ExpressionAndType(argName.makeIdent(), argType));
this.argsNamesByIndex.put(parameterIndex(param), argName);
this.bound.add(param);
}
private ListBuffer toStmts(Tree.NamedArgument namedArg, final List listOfStatements) {
final ListBuffer result = new ListBuffer();
for (JCTree tree : listOfStatements) {
if (tree instanceof JCStatement) {
result.append((JCStatement)tree);
} else {
result.append(gen.make().Exec(gen.makeErroneous(namedArg, "compiler bug: attempt to put a non-statement in a let")));
}
}
return result;
}
private final void appendDefaulted(Parameter param, JCExpression argExpr) {
// we can't just generate types like Foo> if the target type param is not raw because the bounds will
// not match, so we go raw
int flags = JT_RAW;
if (getNamedParameterBoxingStrategy(param) == BoxingStrategy.BOXED) {
flags |= JT_TYPE_ARGUMENT;
}
Type type = gen.getTypeForParameter(param, producedReference, gen.TP_TO_BOUND);
Naming.SyntheticName argName = argName(param);
JCExpression typeExpr = gen.makeJavaType(type, flags);
JCVariableDecl varDecl = gen.makeVar(argName, typeExpr, argExpr);
bind(param, argName, gen.makeJavaType(type, flags), List.of(varDecl));
}
private BoxingStrategy getNamedParameterBoxingStrategy(Parameter param) {
if (param != null) {
if (isOnValueType() && Decl.isValueTypeDecl(getParameterTypeForValueType(producedReference, param))) {
return BoxingStrategy.UNBOXED;
}
return CodegenUtil.getBoxingStrategy(param.getModel());
} else {
return BoxingStrategy.UNBOXED;
}
}
private boolean appendVarsForDefaulted(java.util.List declaredParams) {
boolean hasDefaulted = false;
if (!Decl.isOverloaded(getPrimaryDeclaration())) {
// append any arguments for defaulted parameters
for (Parameter param : declaredParams) {
if (bound.contains(param)) {
continue;
}
final JCExpression argExpr;
if (Strategy.hasDefaultParameterValueMethod(param)) {
// special handling for "element" optional param of java array constructors
if(getPrimaryDeclaration() instanceof Class
&& gen.isJavaArray(((Class)getPrimaryDeclaration()).getType())){
// default values are hard-coded to Java default values, and are actually ignored
continue;
}else if(getQmePrimary() != null
&& gen.isJavaArray(getQmePrimary().getTypeModel())){
// we support array methods with optional parameters
if(getPrimaryDeclaration() instanceof Function
&& getPrimaryDeclaration().getName().equals("copyTo")){
if(param.getName().equals("sourcePosition")
|| param.getName().equals("destinationPosition")){
argExpr = gen.makeInteger(0);
hasDefaulted |= true;
}else if(param.getName().equals("length")){
argExpr = gen.makeSelect(varBaseName.suffixedBy(Suffix.$argthis$).makeIdent(), "length");
hasDefaulted |= true;
}else{
argExpr = gen.makeErroneous(this.getNode(), "compiler bug: argument to copyTo method of java array type not supported: "+param.getName());
}
}else{
argExpr = gen.makeErroneous(this.getNode(), "compiler bug: virtual method of java array type not supported: "+getPrimaryDeclaration());
}
}else{
argExpr = makeDefaultedArgumentMethodCall(param);
hasDefaulted |= true;
}
} else if (Strategy.hasEmptyDefaultArgument(param)) {
argExpr = gen.makeEmptyAsSequential(true);
} else if(gen.typeFact().isIterableType(param.getType())){
// must be an iterable we need to fill with empty
// FIXME: deal with this erasure bug later
argExpr = gen.make().TypeCast(gen.makeJavaType(gen.typeFact().getIterableDeclaration().getType(), AbstractTransformer.JT_RAW), gen.makeEmpty());
} else {
// more expensive but worth a try
Type appliedType = gen.getTypeForParameter(param, producedReference, AbstractTransformer.TP_TO_BOUND);
if(gen.typeFact().isIterableType(appliedType)){
argExpr = gen.make().TypeCast(gen.makeJavaType(gen.typeFact().getIterableDeclaration().getType(), AbstractTransformer.JT_RAW), gen.makeEmpty());
}else{
argExpr = gen.makeErroneous(this.getNode(), "compiler bug: missing argument, and parameter is not defaulted");
}
}
appendDefaulted(param, argExpr);
}
}
return hasDefaulted;
}
private final JCVariableDecl makeThis() {
// first append $this
JCExpression defaultedParameterInstance;
// TODO Fix how we figure out the thisType, because it's doesn't
// handle type parameters correctly
// we used to use thisType = gen.getThisType(getPrimaryDeclaration());
final JCExpression thisType;
Reference target = ((Tree.MemberOrTypeExpression)getPrimary()).getTarget();
if (getPrimary() instanceof Tree.BaseMemberExpression
&& !gen.expressionGen().isWithinSyntheticClassBody()) {
if (Decl.withinClassOrInterface(getPrimaryDeclaration())) {
// a member method
thisType = gen.makeJavaType(target.getQualifyingType(),
JT_NO_PRIMITIVES | (getPrimaryDeclaration().isInterfaceMember() && !getPrimaryDeclaration().isShared()
? JT_COMPANION : 0));
TypeDeclaration outer = Decl.getOuterScopeOfMemberInvocation((StaticMemberOrTypeExpression) getPrimary(), getPrimaryDeclaration());
if (outer instanceof Interface
&& getPrimaryDeclaration().isShared()) {
defaultedParameterInstance = gen.naming.makeQuotedThis();
} else {
defaultedParameterInstance = gen.naming.makeQualifiedThis(gen.makeJavaType(((TypeDeclaration)outer).getType(), JT_NO_PRIMITIVES | (getPrimaryDeclaration().isInterfaceMember() && !getPrimaryDeclaration().isShared() ? JT_COMPANION : 0)));
}
} else {
// a local or toplevel function
thisType = gen.naming.makeName((TypedDeclaration)getPrimaryDeclaration(), Naming.NA_WRAPPER);
defaultedParameterInstance = gen.naming.makeName((TypedDeclaration)getPrimaryDeclaration(), Naming.NA_MEMBER);
}
} else if (getPrimary() instanceof Tree.BaseTypeExpression
|| getPrimary() instanceof Tree.QualifiedTypeExpression) {
TypeDeclaration declaration = (TypeDeclaration)((Tree.MemberOrTypeExpression) getPrimary()).getDeclaration();
thisType = gen.makeJavaType(declaration.getType(), JT_COMPANION);
defaultedParameterInstance = gen.make().NewClass(
null,
null,
gen.makeJavaType(declaration.getType(), JT_COMPANION),
List.nil(), null);
} else {
if (isOnValueType()) {
thisType = gen.makeJavaType(target.getQualifyingType());
} else {
thisType = gen.makeJavaType(target.getQualifyingType(), JT_NO_PRIMITIVES);
}
defaultedParameterInstance = callVarName.makeIdent();
}
JCVariableDecl thisDecl = gen.makeVar(varBaseName.suffixedBy(Suffix.$argthis$),
thisType,
defaultedParameterInstance);
return thisDecl;
}
@Override
protected TransformedInvocationPrimary transformPrimary(JCExpression primaryExpr,
String selector) {
// We need to build the vars before transforming the primary, because the primary is just a var
buildVars();
JCExpression result;
TransformedInvocationPrimary actualPrimExpr = super.transformPrimary(primaryExpr, selector);
result = actualPrimExpr.expr;
if (vars != null
&& !vars.isEmpty()
&& primaryExpr != null
&& selector != null
&& !getPrimaryDeclaration().isStatic()) {
// Prepare the first argument holding the primary for the call
Type type = ((Tree.MemberOrTypeExpression)getPrimary()).getTarget().getQualifyingType();
JCExpression varType;
if (isOnValueType()) {
varType = gen.makeJavaType(getQmePrimary().getTypeModel());
} else {
int jtFlags = JT_NO_PRIMITIVES;
if (getPrimary() instanceof QualifiedTypeExpression
&& !getPrimaryDeclaration().isShared()
&& type.getDeclaration() instanceof Interface) {
jtFlags |= JT_COMPANION;
} else if (
getPrimary() instanceof Tree.StaticMemberOrTypeExpression
&& Decl.isPrivateAccessRequiringCompanion((Tree.StaticMemberOrTypeExpression)getPrimary())) {
jtFlags |= JT_COMPANION;
}
if (ExpressionTransformer.isSuperOrSuperOf(this.getQmePrimary())
&& (!(this.getPrimary() instanceof Tree.QualifiedMemberExpression)
|| ((Tree.QualifiedMemberExpression)this.getPrimary()).getMemberOperator() instanceof Tree.MemberOp)) {
jtFlags |= JT_COMPANION;
}
varType = gen.makeJavaType(type, jtFlags);
}
vars.prepend(gen.makeVar(callVarName, varType, result));
result = callVarName.makeIdent();
}
final Constructor ctor = getConstructor();
if (ctor != null
&& !Decl.isDefaultConstructor(ctor)
&& !Decl.isJavaArrayWith(ctor)) {
argsAndTypes.put(-1,
new ExpressionAndType(gen.naming.makeNamedConstructorName(ctor, false),
gen.naming.makeNamedConstructorType(ctor, false)));
}
return new TransformedInvocationPrimary(result, actualPrimExpr.selector);
}
@Override
public void location(CallBuilder callBuilder) {
callBuilder.location(namedArgumentList);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy