/* * 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.sun.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.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.Term; 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.FunctionOrValue; 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; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; 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 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(); 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<ExpressionAndType> 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 (methodOrClass instanceof Function) { 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(((Class)methodOrClass).getType(), JT_RAW | 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)) { if (Decl.withinInterface(Decl.getConstructedClass(declaration))) { if (Strategy.generateInstantiator(declaration)) { actualPrimExpr = primaryExpr != null ? primaryExpr : gen.naming.makeQuotedThis(); } 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.<JCExpression>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.isCeylonCallable(primaryModel)){ // if it's not exactly a Callable we may have to unerase it to one Type expectedType; if (gen.typeFact().getNothingType().isExactly(primaryModel)) { expectedType = gen.typeFact().getCallableType(gen.typeFact().getNothingType()); } else { expectedType = primaryModel.getSupertype(gen.typeFact().getCallableDeclaration()); } return gen.expressionGen().applyErasureAndBoxing(actualPrimExpr, primaryModel, primary.getTypeErased(), !primary.getUnboxed(), BoxingStrategy.BOXED, expectedType, 0); } 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 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 BoxingStrategy getParameterBoxingStrategy(int argIndex); protected abstract boolean hasParameter(int argIndex); // 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 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<T>} objects and not * functional parameters, which have more info like parameter names and default values. */ class IndirectInvocation extends SimpleInvocation { private final java.util.List<Type> parameterTypes; private final java.util.List<Tree.Expression> 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<Type> parameterTypes; // if we have an unknown parameter list, like Callble<Ret,Args>, 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<Type> 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<Type> 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<Tree.Expression> argumentExpressions = new ArrayList<Tree.Expression>(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<ExpressionAndType> 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 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) && isJavaMethod() && 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 hasParameter(int argIndex) { return getParameter(argIndex) != null; } @Override protected void addReifiedArguments(ListBuffer<ExpressionAndType> result) { if (getPrimary().getTypeModel().isTypeConstructor()) { addTypeConstructorArguments(getPrimary().getTypeModel().getTypeArgumentList(), result); } else { addReifiedArguments(gen, producedReference, result); } } private void addTypeConstructorArguments( java.util.List<Type> typeArgumentList, ListBuffer<ExpressionAndType> 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<ExpressionAndType> result) { java.util.List<JCExpression> 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<Parameter> parameters; public PositionalInvocation( AbstractTransformer gen, Tree.Term primary, Declaration primaryDeclaration, Reference producedReference, Tree.InvocationExpression invocation, java.util.List<Parameter> parameters) { super(gen, primary, primaryDeclaration, producedReference, invocation.getTypeModel(), invocation); positional = invocation.getPositionalArgumentList(); this.parameters = parameters; } java.util.List<Parameter> 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) { return parameters.get(argIndex >= parameters.size() ? parameters.size()-1 : argIndex); } @Override protected int getNumArguments() { return getPositional().getPositionalArguments().size(); } @Override protected int getNumParameters() { return parameters.size(); } @Override protected boolean isSpread() { java.util.List<PositionalArgument> 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<ExpressionAndType> 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: * <p> * <code> * void f(){ * value callable = f; * } * </code> * </p> * 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<Parameter> callableParameters; private final java.util.List<Parameter> functionalParameters; private final int parameterCount; private boolean tempVars; private Naming.SyntheticName instanceFieldName; private boolean instanceFieldIsBoxed; 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; if(primary instanceof Tree.MemberOrTypeExpression) functional = (Functional) ((Tree.MemberOrTypeExpression) primary).getDeclaration(); else if(primary instanceof Tree.FunctionArgument) functional = ((Tree.FunctionArgument) primary).getDeclarationModel(); if(functional != null) callableParameters = functional.getFirstParameterList().getParameters(); else callableParameters = Collections.emptyList(); functionalParameters = parameterList.getParameters(); this.parameterCount = parameterCount; 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){ throw new BugException("I override getTransformedArgumentExpression(), so should never be called"); } @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) { return new TransformedInvocationPrimary(instanceFieldName != null ? instanceFieldName.makeIdent() : primaryExpr, selector); } public Constructor getConstructor() { return getConstructorFromPrimary(appliedReference().getDeclaration()); } } /** * 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; 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)); } @Override protected int getNumArguments() { return method.getFirstParameterList().getParameters().size(); } @Override protected int getNumParameters() { return method.getFirstParameterList().getParameters().size(); } @Override protected JCExpression getTransformedArgumentExpression(int argIndex) { Type exprType = getParameterType(argIndex); Parameter declaredParameter = ((Functional)getPrimaryDeclaration()).getFirstParameterList().getParameters().get(argIndex); JCExpression result = getParameterExpression(argIndex); result = gen.expressionGen().applyErasureAndBoxing( result, exprType, !getParameterUnboxed(argIndex), CodegenUtil.getBoxingStrategy(declaredParameter.getModel()), declaredParameter.getType()); return result; } @Override protected Parameter getParameter(int argIndex) { return method.getFirstParameterList().getParameters().get(argIndex); } @Override protected boolean isSpread() { return method.getFirstParameterList().getParameters().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 getTransformedArgumentExpression(), 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); } } /** * 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<ExpressionAndType> 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<JCStatement> vars = ListBuffer.lb(); private final Naming.SyntheticName callVarName; private final Naming.SyntheticName varBaseName; private final Set<String> argNames = new HashSet<String>(); private final TreeMap<Integer, Naming.SyntheticName> argsNamesByIndex = new TreeMap<Integer, Naming.SyntheticName>(); private final TreeMap<Integer, ExpressionAndType> argsAndTypes = new TreeMap<Integer, ExpressionAndType>(); private final Set<Parameter> bound = new HashSet<Parameter>(); 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<ExpressionAndType> result) { Reference ref = gen.resolveAliasesForReifiedTypeArguments(producedReference); if(!gen.supportsReified(ref.getDeclaration())) return; int tpCount = gen.getTypeParameters(ref).size(); for(int tpIndex = 0;tpIndex<tpCount;tpIndex++){ result.append(new ExpressionAndType(reifiedTypeArgName(tpIndex).makeIdent(), gen.makeTypeDescriptorType())); } } ListBuffer<JCStatement> getVars() { return vars; } Iterable<Naming.SyntheticName> getArgsNamesByIndex() { return argsNamesByIndex.values(); } Iterable<ExpressionAndType> 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<Tree.NamedArgument> namedArguments = namedArgumentList.getNamedArguments(); SequencedArgument sequencedArgument = namedArgumentList.getSequencedArgument(); java.util.List<ParameterList> paramLists = ((Functional)getPrimaryDeclaration()).getParameterLists(); java.util.List<Parameter> declaredParams = paramLists.get(0).getParameters(); appendVarsForNamedArguments(namedArguments, declaredParams); appendVarsForReifiedTypeArguments(); if(sequencedArgument != null) appendVarsForSequencedArguments(sequencedArgument, declaredParams); boolean hasDefaulted = appendVarsForDefaulted(declaredParams); if (hasDefaulted && !Strategy.defaultParameterMethodStatic(getPrimaryDeclaration()) && !Strategy.defaultParameterMethodOnOuter(getPrimaryDeclaration())) { vars.prepend(makeThis()); } gen.expressionGen().withinInvocation(prev); } private void appendVarsForReifiedTypeArguments() { java.util.List<JCExpression> 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<Parameter> declaredParams) { // FIXME: this is suspisciously similar to AbstractTransformer.makeIterable(java.util.List<Tree.PositionalArgument> 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 Type tupleType = AnalyzerUtil.getTupleType(sequencedArgument.getPositionalArguments(), gen.typeFact(), false); Type argumentsType = tupleType.getSupertype(gen.typeFact().getIterableDeclaration()); Type iteratedType = gen.typeFact().getIteratedType(argumentsType); Type absentType = gen.typeFact().getIteratedAbsentType(argumentsType); // 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; JCTree.JCExpression 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.<JCTree.JCStatement>of(varDecl)); } private JCExpression makeDefaultedArgumentMethodCall(Parameter param) { JCExpression thisExpr = null; switch (Strategy.defaultParameterMethodOwner(param.getModel())) { case SELF: case STATIC: break; case OUTER: if(getQmePrimary() != null && !Decl.isConstructor(getPrimaryDeclaration())) thisExpr = callVarName.makeIdent(); break; case OUTER_COMPANION: 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<JCExpression> makeVarRefArgumentList(Parameter param) { ListBuffer<JCExpression> names = ListBuffer.<JCExpression> lb(); 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;tpIndex<tpCount;tpIndex++){ names.append(reifiedTypeArgName(tpIndex).makeIdent()); } final int parameterIndex = parameterIndex(param); for (int ii = 0; ii < parameterIndex; ii++) { names.append(this.argsNamesByIndex.get(ii).makeIdent()); } return names.toList(); } /** Generates the argument name; namedArg may be null if no * argument was given explicitly */ private Naming.SyntheticName argName(Parameter param) { final int paramIndex = parameterIndex(param); //if (this.argNames.isEmpty()) { //this.argNames.addAll(Collections.<String>nCopies(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<Parameter> 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<Tree.NamedArgument> namedArguments, java.util.List<Parameter> 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<JCStatement> 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; } JCExpression typeExpr = gen.makeJavaType(type, jtFlags); JCExpression argExpr = gen.expressionGen().transformExpression(expr, boxType, type, exprFlags); JCVariableDecl varDecl = gen.makeVar(argName, typeExpr, argExpr); statements = ListBuffer.<JCStatement>of(varDecl); bind(declaredParam, argName, gen.makeJavaType(type, jtFlags), statements.toList()); } private void bindMethodArgument(Tree.MethodArgument methodArg, Parameter declaredParam, Naming.SyntheticName argName) { ListBuffer<JCStatement> statements; Function model = methodArg.getDeclarationModel(); List<JCStatement> body; boolean prevNoExpressionlessReturn = gen.statementGen().noExpressionlessReturn; boolean prevSyntheticClassBody = gen.expressionGen().withinSyntheticClassBody(Decl.isMpl(model) || gen.expressionGen().isWithinSyntheticClassBody()); try { 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.<JCStatement>of(returnStat); } } finally { gen.expressionGen().withinSyntheticClassBody(prevSyntheticClassBody); gen.statementGen().noExpressionlessReturn = prevNoExpressionlessReturn; } Type callableType = model.appliedReference(null, Collections.<Type>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(); JCExpression typeExpr = gen.makeJavaType(callableType, JT_RAW); JCVariableDecl varDecl = gen.makeVar(argName, typeExpr, callable); statements = ListBuffer.<JCStatement>of(varDecl); bind(declaredParam, argName, gen.makeJavaType(callableType), statements.toList()); } private void bindObjectArgument(Tree.ObjectArgument objectArg, Parameter declaredParam, Naming.SyntheticName argName) { ListBuffer<JCStatement> statements; List<JCTree> 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<JCStatement> statements; final Value model = attrArg.getDeclarationModel(); final String name = model.getName(); String className = Naming.getAttrClassName(model, 0); final List<JCTree> 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.<JCExpression>nil()); initValue = gen.expressionGen().applyErasureAndBoxing( initValue, nonWideningType, !CodegenUtil.isUnBoxed(nonWideningTypedRef.getDeclaration()), boxType, type); JCTree.JCVariableDecl var = gen.make().VarDef( gen.make().Modifiers(FINAL, List.<JCAnnotation>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<JCStatement> 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<JCStatement> toStmts(Tree.NamedArgument namedArg, final List<JCTree> listOfStatements) { final ListBuffer<JCStatement> result = ListBuffer.<JCStatement>lb(); 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.<JCStatement>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<Parameter> 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); defaultedParameterInstance = gen.naming.makeThis(); } 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.<JCExpression>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) { // 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)) { 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); } }