Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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.typechecker.analyzer.AnalyzerUtil.isIndirectInvocation;
import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.eliminateParensAndWidening;
import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.hasUncheckedNulls;
import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.unwrapExpressionUntilTerm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import com.redhat.ceylon.common.Backend;
import com.redhat.ceylon.compiler.java.codegen.Invocation.TransformedInvocationPrimary;
import com.redhat.ceylon.compiler.java.codegen.Naming.DeclNameFlag;
import com.redhat.ceylon.compiler.java.codegen.Naming.Substitution;
import com.redhat.ceylon.compiler.java.codegen.Naming.SyntheticName;
import com.redhat.ceylon.compiler.java.codegen.Operators.AssignmentOperatorTranslation;
import com.redhat.ceylon.compiler.java.codegen.Operators.OperatorTranslation;
import com.redhat.ceylon.compiler.java.codegen.Operators.OptimisationStrategy;
import com.redhat.ceylon.compiler.java.codegen.StatementTransformer.Cond;
import com.redhat.ceylon.compiler.java.codegen.StatementTransformer.CondList;
import com.redhat.ceylon.compiler.java.codegen.StatementTransformer.VarDefBuilder;
import com.redhat.ceylon.compiler.java.codegen.StatementTransformer.VarTrans;
import com.redhat.ceylon.compiler.java.codegen.recovery.HasErrorException;
import com.redhat.ceylon.compiler.typechecker.analyzer.Warning;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Expression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.LetExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.PositionalArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Term;
import com.redhat.ceylon.langtools.tools.javac.code.Flags;
import com.redhat.ceylon.langtools.tools.javac.code.TypeTag;
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.JCBlock;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCExpression;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCLiteral;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCMethodDecl;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCMethodInvocation;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCNewArray;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCNewClass;
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.JCTypeCast;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCUnary;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCVariableDecl;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.Tag;
import com.redhat.ceylon.langtools.tools.javac.util.Context;
import com.redhat.ceylon.langtools.tools.javac.util.Convert;
import com.redhat.ceylon.langtools.tools.javac.util.List;
import com.redhat.ceylon.langtools.tools.javac.util.ListBuffer;
import com.redhat.ceylon.langtools.tools.javac.util.Name;
import com.redhat.ceylon.model.loader.JvmBackendUtil;
import com.redhat.ceylon.model.loader.NamingBase.Prefix;
import com.redhat.ceylon.model.loader.NamingBase.Suffix;
import com.redhat.ceylon.model.loader.model.FieldValue;
import com.redhat.ceylon.model.loader.model.LazyInterface;
import com.redhat.ceylon.model.loader.model.OutputElement;
import com.redhat.ceylon.model.typechecker.model.Class;
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.Generic;
import com.redhat.ceylon.model.typechecker.model.Import;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.Package;
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.Referenceable;
import com.redhat.ceylon.model.typechecker.model.Scope;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeAlias;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypeParameter;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypedReference;
import com.redhat.ceylon.model.typechecker.model.UnionType;
import com.redhat.ceylon.model.typechecker.model.Unit;
import com.redhat.ceylon.model.typechecker.model.Value;
/**
* This transformer deals with expressions only
*/
public class ExpressionTransformer extends AbstractTransformer {
// flags for transformExpression
/**
* This implies inclusion of the JT_SATISFIES flags when
* constructing the type for a variance typecast.
*/
public static final int EXPR_FOR_COMPANION = 1;
/**
* The expected type has type parameters
* (so an extra typecast to the raw type will be required)
*/
public static final int EXPR_EXPECTED_TYPE_NOT_RAW = 1 << 1;
/**
* The expected type has type parameters with {@code satisfies}
* constraints (which may be erased, and thus a type cast may be required
* irrespective of the presence of type arguments)
*/
public static final int EXPR_EXPECTED_TYPE_HAS_CONSTRAINED_TYPE_PARAMETERS = 1 << 2;
/**
* Seems to be used when the expected and expression
* types have no supertype in common.
*/
public static final int EXPR_DOWN_CAST = 1 << 3;
/**
* Use this when the expression being passed contains code to check for nulls coming from Java
*/
public static final int EXPR_HAS_NULL_CHECK_FENCE = 1 << 4;
/**
* This implies inclusion of the JT_COMPANION flags when
* constructing the type casts.
*/
public static final int EXPR_WANTS_COMPANION = 1 << 5;
/**
* The expected type has type parameters with {@code satisfies}
* constraints which have a covariant type parameter that is used by other
* type parameter bounds, so we will always generate types where it is fixed rather
* than using wildcards and in some cases we need to cast to raw
*/
public static final int EXPR_EXPECTED_TYPE_HAS_DEPENDENT_COVARIANT_TYPE_PARAMETERS = 1 << 6;
/**
* Usually if a {@code long} to {@code int}, {@code short} or {@code byte}
* conversion is required we use a Util invocation so that a runtime check
* is performed.
*
* In some circumstances (e.g. annotations) we need to use a
* real typecast.
*/
public static final int EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK = 1 << 7;
/**
* Use this when the expression is to be passed to a Java field/setter/parameter which does not require null-safety
*/
public static final int EXPR_TARGET_ACCEPTS_NULL = 1 << 8;
public static final int EXPR_WIDEN_PRIM = 1 << 9;
/**
* Use this when the expression is guaranteed to not be a base member. This allows us to
* differentiate `null` (BaseMemberExpression) from other things of type "null" which get
* stored in temp vars erased to `Object`, for erasure and casts.
*/
public static final int EXPR_IS_NOT_BASE_MEMBER = 1 << 10;
/**
* Use this when you want the resulting expression coerced
*/
public static final int EXPR_IS_COERCED = 1 << 11;
static{
// only there to make sure this class is initialised before the enums defined in it, otherwise we
// get an initialisation error
Operators.init();
}
private boolean inStatement = false;
private boolean withinInvocation = false;
private boolean withinSyntheticClassBody = false;
/** The transformation for spread method references involves nested invocations of
* {@link #transformSpreadOperator(com.redhat.ceylon.compiler.typechecker.tree.Tree.QualifiedMemberExpression, TermTransformer)}
* and what we generate depends on the invocation. This field will be odd on
* the "outer" invocation (which generates the outer part of the tree)
* and even on the "inner" invocation (which generates the inner part of the tree)
*/
private Tree.QualifiedMemberOrTypeExpression spreading = null;
private Naming.SyntheticName memberPrimary = null;
private ClassOrInterface withinSuperInvocation = null;
private ClassOrInterface withinDefaultParameterExpression = null;
private Type expectedType;
private boolean coerced;
public static ExpressionTransformer getInstance(Context context) {
ExpressionTransformer trans = context.get(ExpressionTransformer.class);
if (trans == null) {
trans = new ExpressionTransformer(context);
context.put(ExpressionTransformer.class, trans);
}
return trans;
}
private ExpressionTransformer(Context context) {
super(context);
}
// Statement expressions
public JCStatement transform(Tree.ExpressionStatement tree) {
// ExpressionStatements do not return any value, therefore we don't care about the type of the expressions.
inStatement = true;
JCStatement result;
HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(tree.getExpression());
if (error != null) {
result = this.makeThrowUnresolvedCompilationError(error);
} else {
result = at(tree).Exec(transformExpression(tree.getExpression(), BoxingStrategy.INDIFFERENT, null));
}
inStatement = false;
return result;
}
public JCStatement transform(Tree.SpecifierStatement op) {
// SpecifierStatement do not return any value, therefore we don't care about the type of the expressions.
inStatement = true;
JCStatement result;
HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(op.getBaseMemberExpression());
if (error != null) {
result = this.makeThrowUnresolvedCompilationError(error);
} else if ((error = errors().getFirstExpressionErrorAndMarkBrokenness(op.getSpecifierExpression().getExpression())) != null) {
result = this.makeThrowUnresolvedCompilationError(error);
} else {
result = at(op).Exec(transformAssignment(op, op.getBaseMemberExpression(), op.getSpecifierExpression().getExpression()));
}
inStatement = false;
return result;
}
public JCExpression transform(Tree.SpecifierOrInitializerExpression expr,
BoxingStrategy boxing, Type expectedType) {
return transformExpression(expr.getExpression(), boxing, expectedType);
}
public JCExpression transform(Tree.SpecifierOrInitializerExpression expr,
TypedDeclaration decl) {
//TODO: is there missing logic for small here?
// see ClassTransformer.transform(AttributeDeclaration decl, ClassDefinitionBuilder classBuilder)
return transformExpression(expr.getExpression(),
CodegenUtil.getBoxingStrategy(decl), decl.getType(),
decl.hasUncheckedNullType() ? EXPR_TARGET_ACCEPTS_NULL : 0);
}
//
// Any sort of expression
JCExpression transformExpression(final TypedDeclaration declaration, final Tree.Term expr) {
// make sure we use the best declaration for boxing and type
TypedReference typedRef = getTypedReference(declaration);
TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
Type nonWideningType = nonWideningType(typedRef, nonWideningTypedRef);
// If this is a return statement in a MPL method we want to know
// the non-widening type of the innermost callable
if (declaration instanceof Functional
&& Decl.isMpl((Functional)declaration)) {
for (int i = ((Functional)declaration).getParameterLists().size(); i > 1; i--) {
nonWideningType = getReturnTypeOfCallable(nonWideningType);
}
}
// respect the refining definition of optionality
nonWideningType = propagateOptionality(declaration.getType(), nonWideningType);
BoxingStrategy boxing = CodegenUtil.getBoxingStrategy(nonWideningTypedRef.getDeclaration());
int flags = 0;
if(declaration.hasUncheckedNullType())
flags = ExpressionTransformer.EXPR_TARGET_ACCEPTS_NULL;
if (CodegenUtil.downcastForSmall(expr, declaration))
flags |= ExpressionTransformer.EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK;
return transformExpression(expr, boxing, nonWideningType, flags);
}
private Type propagateOptionality(Type type, Type nonWideningType) {
if(!isNull(type)){
if(isOptional(type)){
if(!isOptional(nonWideningType)){
return typeFact().getOptionalType(nonWideningType);
}
}else{
if(isOptional(nonWideningType)){
return typeFact().getDefiniteType(nonWideningType);
}
}
}
return nonWideningType;
}
JCExpression transformExpression(final Tree.Term expr) {
return transformExpression(expr, 0);
}
JCExpression transformExpression(final Tree.Term expr, int flags) {
return transformExpression(expr, BoxingStrategy.BOXED, expr.getTypeModel(), flags);
}
JCExpression transformExpression(final Tree.Term expr, BoxingStrategy boxingStrategy, Type expectedType) {
return transformExpression(expr, boxingStrategy, expectedType, 0);
}
JCExpression transformExpression(final Tree.Term expr, BoxingStrategy boxingStrategy,
Type expectedType, int flags) {
if (expr == null) {
return null;
}
at(expr);
if (inStatement && boxingStrategy != BoxingStrategy.INDIFFERENT) {
// We're not directly inside the ExpressionStatement anymore
inStatement = false;
}
// Cope with things like ((expr))
// FIXME: shouldn't that be in the visitor?
Tree.Term term = expr;
while (term instanceof Tree.Expression) {
term = ((Tree.Expression)term).getTerm();
}
JCExpression result;
if(term instanceof Tree.IfExpression){
flags |= EXPR_IS_NOT_BASE_MEMBER;
}
CeylonVisitor v = gen().visitor;
final ListBuffer prevDefs = v.defs;
final boolean prevInInitializer = v.inInitializer;
final ClassDefinitionBuilder prevClassBuilder = v.classBuilder;
final Type prevExpectedType = this.expectedType;
final boolean prevCoerced = this.coerced;
try {
v.defs = new ListBuffer();
v.inInitializer = false;
v.classBuilder = gen().current();
this.expectedType = expectedType;
this.coerced = (flags & EXPR_IS_COERCED) != 0;
term.visit(v);
if (v.hasResult()) {
result = v.getSingleResult();
if (result == null) {
throw new BugException(term, "visitor yielded multiple results");
}
} else {
throw new BugException(term, "visitor didn't yield any result");
}
} catch (BugException e) {
result = e.makeErroneous(this, expr);
} finally {
v.classBuilder = prevClassBuilder;
v.inInitializer = prevInInitializer;
v.defs = prevDefs;
this.coerced = prevCoerced;
this.expectedType = prevExpectedType;
}
if ((flags & EXPR_TARGET_ACCEPTS_NULL) == 0
&& expectedType != null
&& hasUncheckedNulls(expr)
&& expectedType.isSubtypeOf(typeFact().getObjectType())
&& !knownNullSafe(term)) {
result = utilInvocation().checkNull(result);
flags |= EXPR_HAS_NULL_CHECK_FENCE;
}
result = applyErasureAndBoxing(result, expr, boxingStrategy, expectedType, flags);
return result;
}
private static boolean knownNullSafe(Tree.Term term) {
if (term instanceof Tree.InvocationExpression) {
Tree.InvocationExpression ie =
(Tree.InvocationExpression) term;
Tree.Term p = ie.getPrimary();
if (p instanceof Tree.StaticMemberOrTypeExpression) {
Tree.StaticMemberOrTypeExpression bme =
(Tree.StaticMemberOrTypeExpression) p;
Declaration dec = bme.getDeclaration();
if (dec!=null) {
String qname =
dec.getQualifiedNameString();
return knownNullSafe.contains(qname);
}
}
}
return false;
}
private static java.util.List knownNullSafe = Arrays.asList(
"java.util::Objects.toString",
"java.util::Arrays.asList",
"java.util::Arrays.copyOf",
"java.util::Arrays.copyOfRange",
"java.util::Arrays.deepToString",
"java.util::Collections.unmodifiableList",
"java.util::Collections.unmodifiableMap",
"java.util::Collections.unmodifiableSet",
"java.util::Collections.unmodifiableSortedMap",
"java.util::Collections.unmodifiableSortedSet",
"java.util::Collections.synchronizedList",
"java.util::Collections.synchronizedMap",
"java.util::Collections.synchronizedSet",
"java.util::Collections.synchronizedSortedMap",
"java.util::Collections.synchronizedSortedSet",
"java.util::Collections.emptyIterator",
"java.util::Collections.emptyListIterator",
"java.util::Collections.emptyList",
"java.util::Collections.emptyMap",
"java.util::Collections.emptySet",
"java.util::Collections.emptySortedMap",
"java.util::Collections.emptySortedSet",
"java.util::Collections.singleton",
"java.util::Collections.singletonList",
"java.util::Collections.singletonMap",
"java.lang::String.substring",
"java.lang::String.toLowerCase",
"java.lang::String.toUpperCase",
"java.lang::String.intern",
"java.lang::String.replace",
"java.lang::String.replaceAll",
"java.lang::String.replaceFirst",
"java.lang::String.toCharArray",
"java.lang::String.getBytes",
"java.lang::String.getChars",
"java.lang::String.trim",
"java.lang::String.format",
"java.lang::String.join",
"java.lang::String.split",
"java.lang::String.copyValueOf",
"java.lang::String.valueOf",
"java.lang::Double.valueOf",
"java.lang::Float.valueOf",
"java.lang::Long.valueOf",
"java.lang::Integer.valueOf",
"java.lang::Short.valueOf",
"java.lang::Byte.valueOf",
"java.lang::Boolean.valueOf",
"java.lang::Character.valueOf",
"java.lang::Double.toString",
"java.lang::Float.toString",
"java.lang::Long.toString",
"java.lang::Integer.toString",
"java.lang::Short.toString",
"java.lang::Byte.toString",
"java.lang::Boolean.toString",
"java.lang::Character.toString"
);
JCExpression transform(Tree.FunctionArgument functionArg, Type expectedType) {
Type prevExpectedType = this.expectedType;
try{
this.expectedType = expectedType;
return transform(functionArg);
}finally{
this.expectedType = prevExpectedType;
}
}
JCExpression transform(Tree.FunctionArgument functionArg) {
Function model = functionArg.getDeclarationModel();
List body;
boolean prevNoExpressionlessReturn = statementGen().noExpressionlessReturn;
boolean prevSyntheticClassBody = expressionGen().withinSyntheticClassBody(true);
try {
statementGen().noExpressionlessReturn = isAnything(model.getType());
if (functionArg.getBlock() != null) {
body = statementGen().transformBlock(functionArg.getBlock());
if (!functionArg.getBlock().getDefinitelyReturns()) {
if (isAnything(model.getType())) {
body = body.append(make().Return(makeNull()));
} else {
body = body.append(make().Return(makeErroneous(functionArg.getBlock(), "compiler bug: non-void method does not definitely return")));
}
}
} else {
Tree.Expression expr = functionArg.getExpression();
JCExpression transExpr = expressionGen().transformExpression(expr);
JCReturn returnStat = make().Return(transExpr);
body = List.of(returnStat);
}
} finally {
expressionGen().withinSyntheticClassBody(prevSyntheticClassBody);
statementGen().noExpressionlessReturn = prevNoExpressionlessReturn;
}
Type callableType = functionArg.getTypeModel();
CallableBuilder callableBuilder = CallableBuilder.methodArgument(gen(),
functionArg,
model,
callableType,
Collections.singletonList(functionArg.getParameterLists().get(0)),
classGen().transformMplBody(functionArg.getParameterLists(), model, body));
callableBuilder.checkForFunctionalInterface(expectedType);
JCExpression result = callableBuilder.build();
result = applyErasureAndBoxing(result, callableType, true, BoxingStrategy.BOXED, expectedType);
return result;
}
//
// Boxing and erasure of expressions
private JCExpression applyErasureAndBoxing(JCExpression result, Tree.Term expr, BoxingStrategy boxingStrategy,
Type expectedType) {
return applyErasureAndBoxing(result, expr, boxingStrategy, expectedType, 0);
}
private JCExpression applyErasureAndBoxing(JCExpression result, Tree.Term expr, BoxingStrategy boxingStrategy,
Type expectedType, int flags) {
Type exprType = expr.getTypeModel();
if ((flags & EXPR_HAS_NULL_CHECK_FENCE) != 0) {
exprType = getNonNullType(exprType);
} else if (hasUncheckedNulls(expr) && !isOptional(exprType)) {
exprType = typeFact().getOptionalType(exprType);
}
boolean exprBoxed = !CodegenUtil.isUnBoxed(expr);
boolean exprErased = CodegenUtil.hasTypeErased(expr);
boolean exprUntrustedType = CodegenUtil.hasUntrustedType(expr);
boolean exprSmall = CodegenUtil.isSmall(expr);
return applyErasureAndBoxing(result, exprType, exprErased, exprBoxed, exprUntrustedType, exprSmall, boxingStrategy, expectedType, flags);
}
JCExpression applyErasureAndBoxing(JCExpression result, Type exprType,
boolean exprBoxed,
BoxingStrategy boxingStrategy, Type expectedType) {
return applyErasureAndBoxing(result, exprType, false, exprBoxed, boxingStrategy, expectedType, 0);
}
JCExpression applyErasureAndBoxing(JCExpression result, Type exprType,
boolean exprErased, boolean exprBoxed,
BoxingStrategy boxingStrategy, Type expectedType,
int flags) {
return applyErasureAndBoxing(result, exprType, exprErased, exprBoxed, false, boxingStrategy, expectedType, flags);
}
JCExpression applyErasureAndBoxing(JCExpression result, Type exprType,
boolean exprErased, boolean exprBoxed, boolean exprUntrustedType,
BoxingStrategy boxingStrategy, Type expectedType,
int flags) {
return applyErasureAndBoxing(result, exprType, exprErased, exprBoxed, exprUntrustedType, false,
boxingStrategy, expectedType, flags);
}
JCExpression applyErasureAndBoxing(JCExpression result, Type exprType,
boolean exprErased, boolean exprBoxed, boolean exprUntrustedType,
boolean exprSmall,
BoxingStrategy boxingStrategy, Type expectedType,
int flags) {
if(exprType != null)
exprType = exprType.resolveAliases();
if(expectedType != null)
expectedType = expectedType.resolveAliases();
boolean canCast = false;
boolean coerced = (flags & EXPR_IS_COERCED) != 0;
if (expectedType != null
// don't cast if we're coercing
&& !coerced
// don't add cast to an erased type
&& !willEraseToObject(expectedType)) {
// only try to cast boxed types, no point otherwise
if(exprBoxed){
boolean expectedTypeIsNotRaw = (flags & EXPR_EXPECTED_TYPE_NOT_RAW) != 0;
boolean expectedTypeHasConstrainedTypeParameters = (flags & EXPR_EXPECTED_TYPE_HAS_CONSTRAINED_TYPE_PARAMETERS) != 0;
boolean expectedTypeHasDependentCovariantTypeParameters = (flags & EXPR_EXPECTED_TYPE_HAS_DEPENDENT_COVARIANT_TYPE_PARAMETERS) != 0;
boolean downCast = (flags & EXPR_DOWN_CAST) != 0;
int companionFlags = (flags & EXPR_WANTS_COMPANION) != 0 ? AbstractTransformer.JT_COMPANION : 0;
// special case for returning Null expressions
if (isNull(exprType)){
// don't add cast for null
if(!isNullValue(exprType)
// well, unless it's a complex expression which stores that null value in
// temp vars whose type erase to Object
|| (flags & EXPR_IS_NOT_BASE_MEMBER) != 0
// include a cast even for null for interop and disambiguating bw overloads and null values
// of different types using the "of" operator
|| downCast){
// in some cases we may have an instance of Null, which is of type java.lang.Object, being
// returned in a context where we expect a String? (aka ceylon.language.String) so even though
// the instance at hand will really be null, we need a up-cast to it
JCExpression targetType = makeJavaType(expectedType, AbstractTransformer.JT_RAW | companionFlags);
result = make().TypeCast(targetType, result);
}
}else if(exprType.isExactlyNothing()){
// type param erasure
JCExpression targetType = makeJavaType(expectedType,
AbstractTransformer.JT_NO_PRIMITIVES | companionFlags);
result = make().TypeCast(make().QualIdent(syms().objectType.tsym), result);
result = make().TypeCast(targetType, result);
}else if(// expression was forcibly erased
exprErased
// expression type cannot be trusted to be true, most probably because we had to satisfy Java type parameter
// bounds that are different from what we think the expression type should be
|| exprUntrustedType
// if we have a covariant type parameter which is dependent and whose type arg contains erased type parameters
// we need a raw cast because it will be fixed rather than using a wildcard and there's a good chance
// we can't use proper subtyping rules to assign to it
// see https://github.com/ceylon/ceylon-compiler/issues/1557
|| expectedTypeHasDependentCovariantTypeParameters
// some type parameter somewhere needs a cast
|| needsCast(exprType, expectedType, expectedTypeIsNotRaw, expectedTypeHasConstrainedTypeParameters, downCast)
// if the exprType is raw and the expected type isn't
|| (exprType.isRaw() && (expectedTypeIsNotRaw || !isTurnedToRaw(expectedType)))){
// save this before we simplify it because we lose that flag doing so
boolean exprIsRaw = exprType.isRaw();
boolean expectedTypeIsRaw = isTurnedToRaw(expectedType) && !expectedTypeIsNotRaw;
// We will need a raw cast if either the expected type or the
// expression type has type parameters while the other hasn't
// (unless the other type is already raw)
if ((!exprIsRaw && hasTypeParameters(expectedType))
|| (downCast && !expectedTypeIsRaw && hasTypeParameters(exprType))) {
Type rawType = hasTypeParameters(expectedType) ? expectedType : exprType;
JCExpression rawTypeExpr = makeJavaType(rawType,
AbstractTransformer.JT_TYPE_ARGUMENT | AbstractTransformer.JT_RAW | companionFlags);
result = make().TypeCast(rawTypeExpr, result);
// expr is now raw
exprIsRaw = true;
// let's not add another downcast if we got a cast: one is enough
downCast = false;
// same for forced erasure
exprErased = false;
exprUntrustedType = false;
}
// simplify the type
// (without the underlying type, because the cast is always to a non-primitive)
boolean wasOptional = isOptional(expectedType);
exprType = simplifyType(expectedType).withoutUnderlyingType();
// if the expected type was optional, respect that otherwise it fubars boxing
if(wasOptional){
exprType = typeFact().getOptionalType(exprType);
}
// if the expr is not raw, we need a cast
// if the expr is raw:
// don't even try making an actual cast if there are bounded type parameters in play, because going raw is much safer
// also don't try making the cast if the expected type is raw because anything goes
boolean needsTypedCast = !exprIsRaw
|| (!expectedTypeHasConstrainedTypeParameters
&& !expectedTypeHasDependentCovariantTypeParameters
&& !expectedTypeIsRaw);
if(needsTypedCast
// make sure that downcasts get at least one cast
|| downCast
// same for forced erasure
|| exprUntrustedType){
// forced erasure may require a previous cast to Object if we were not able to insert a raw cast
// because for instance Sequential cannot be cast forcibly to Empty because Java is so smart
// it figures out that there's no intersection between the two types, but we know better
if(exprUntrustedType && !exprIsRaw){
result = make().TypeCast(syms().objectType, result);
}
// Do the actual cast
JCExpression targetType = makeJavaType(expectedType,
AbstractTransformer.JT_TYPE_ARGUMENT | companionFlags);
result = make().TypeCast(targetType, result);
}
}else
canCast = true;
}else
canCast = true;
}
// If expr type if Self and expected type is T we need to cast before any unboxing
if (exprType.getDeclaration().getSelfType() != null
&& expectedType != null
&& expectedType.isExactly(exprType.getTypeArguments().get(exprType.getDeclaration().getSelfType().getDeclaration()))) {
result = applySelfTypeCasts(result, exprType, exprBoxed, BoxingStrategy.BOXED, expectedType);
exprType = expectedType;
}
// we must do the boxing after the cast to the proper type
JCExpression ret = boxUnboxIfNecessary(result, exprBoxed ? BoxingStrategy.BOXED : BoxingStrategy.UNBOXED, exprType, boxingStrategy, expectedType);
// very special case for nothing that we need to "unbox" to a primitive type
if(exprType != null
&& exprType.isExactlyNothing()
&& boxingStrategy == BoxingStrategy.UNBOXED){
// in this case we have to use the expected type
ret = unboxType(ret, expectedType);
}
// now check if we need variance casts
if (canCast) {
ret = applyVarianceCasts(ret, exprType, exprBoxed, boxingStrategy, expectedType, flags);
}
ret = applySelfTypeCasts(ret, exprType, exprBoxed, boxingStrategy, expectedType);
ret = applyJavaTypeConversions(ret, exprType, expectedType, boxingStrategy, exprBoxed, exprSmall, flags);
// Don't rely on coerced member because for SOME reason this is called
// _after_ we reset it in transformExpression()
if(coerced)
ret = applyJavaCoercions(ret, exprType, expectedType);
return ret;
}
private JCExpression applyJavaCoercions(JCExpression ret, Type exprType, Type expectedType) {
if(expectedType == null)
return ret;
exprType = simplifyType(exprType);
expectedType = simplifyType(expectedType);
if(isCeylonString(exprType) && isJavaCharSequence(expectedType)){
// FIXME: only do this if boxed, or rather, do not box in the first place
return make().Apply(null, makeQualIdent(ret, "toString"), List.nil());
}
if(isJavaCharSequence(exprType) && expectedType.isExactly(typeFact().getStringDeclaration().getType())){
return make().Apply(null, makeQualIdent(ret, "toString"), List.nil());
}
if(isCeylonClassOrInterfaceModel(exprType)
&& isJavaClass(expectedType)
&& !(ret instanceof JCTree.JCFieldAccess && ret.toString().endsWith(".class"))){
// FIXME: perhaps cast as RAW?
JCTree arg = ret;
// try to turn (.ceylon.language.meta.model.Class)
// .ceylon.language.meta.typeLiteral_.typeLiteral(
// .com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor.klass(
// .com.redhat.ceylon.compiler.java.test.interop.LambdasJava.class))
// into Util.classErasure(.com.redhat.ceylon.compiler.java.test.interop.LambdasJava.class)
while(arg instanceof JCTree.JCTypeCast)
arg = ((JCTree.JCTypeCast)arg).getExpression();
if(arg instanceof JCTree.JCMethodInvocation){
JCExpression methodSelect = ((JCTree.JCMethodInvocation) arg).getMethodSelect();
if(methodSelect instanceof JCTree.JCFieldAccess){
JCTree.JCFieldAccess methodField = (JCTree.JCFieldAccess) methodSelect;
if(methodField.getIdentifier().toString().equals("typeLiteral")
&& methodField.getExpression() instanceof JCTree.JCFieldAccess
&& ((JCTree.JCFieldAccess)methodField.getExpression()).toString().equals(".ceylon.language.meta.typeLiteral_")){
JCExpression classLiteral = extractClassLiteralFromTypeDescriptor(((JCTree.JCMethodInvocation) arg).getArguments().get(0));
if(classLiteral != null)
// FIXME: pass type arg explicitly?
return utilInvocation().classErasure(classLiteral);
}
}
}
// FIXME: pass type arg explicitly?
return utilInvocation().javaClassForModel(ret);
}
return ret;
}
private JCExpression extractClassLiteralFromTypeDescriptor(JCExpression arg) {
if(arg instanceof JCTree.JCMethodInvocation){
JCExpression methodSelect = ((JCTree.JCMethodInvocation) arg).getMethodSelect();
if(methodSelect instanceof JCTree.JCFieldAccess){
JCTree.JCFieldAccess methodField = (JCTree.JCFieldAccess) methodSelect;
if(methodField.getIdentifier().toString().equals("klass")
&& methodField.getExpression() instanceof JCTree.JCFieldAccess
&& ((JCTree.JCFieldAccess)methodField.getExpression()).toString().equals(".com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor")){
return ((JCTree.JCMethodInvocation) arg).getArguments().get(0);
}
if(methodField.getIdentifier().toString().equals("member")
&& methodField.getExpression() instanceof JCTree.JCFieldAccess
&& ((JCTree.JCFieldAccess)methodField.getExpression()).toString().equals(".com.redhat.ceylon.compiler.java.runtime.model.TypeDescriptor")){
return extractClassLiteralFromTypeDescriptor(((JCTree.JCMethodInvocation) arg).getArguments().get(1));
}
}
}
return null;
}
boolean needsCast(Type exprType, Type expectedType,
boolean expectedTypeNotRaw,
boolean expectedTypeHasConstrainedTypeParameters,
boolean downCast) {
// error handling
if(exprType == null)
return false;
// make sure we work on definite types
exprType = simplifyType(exprType);
expectedType = simplifyType(expectedType);
// abort if both types are the same
if(exprType.isExactly(expectedType)){
// unless the expected type is parameterised with bounds because in that case we can't
// really trust the expected type
if(!expectedTypeHasConstrainedTypeParameters)
return false;
}
// now see about erasure
boolean eraseExprType = willEraseToObject(exprType);
boolean eraseExpectedType = willEraseToObject(expectedType);
// if we erase expected type we need no cast
if(eraseExpectedType){
// unless the expected type is parameterised with bounds that erasure to Object can't possibly satisfy
if(!expectedTypeHasConstrainedTypeParameters)
return false;
}
// if we erase the expr type we need a cast
if(eraseExprType)
return true;
// find their common type
Type commonType = exprType.getSupertype(expectedType.getDeclaration());
if(commonType == null || !(commonType.getDeclaration() instanceof ClassOrInterface)){
// we did not find any common type, but we may be downcasting, in which case we need a cast
return downCast;
}
// some times we can lose info due to an erased type parameter somewhere in the inheritance graph
if(lostTypeParameterInInheritance(exprType, commonType))
return true;
if(!expectedTypeNotRaw){
// the truth is that we don't really know if the expected type is raw or not, that flag only gets set
// if we know for sure that the expected type is NOT raw. if it's false we've no idea but we can check:
if(isTurnedToRaw(expectedType)){
return false;
}
// if the expected type is exactly the common type, they must have the same erasure
// note that we don't do that test if we know the expected type is not raw, because
// the common type could be erased
if(commonType.isExactly(expectedType))
return false;
}
//special case for Callable because only the first type param exists in Java, the rest is completely suppressed
boolean isCallable = isCeylonCallable(commonType);
// now see if the type parameters match
java.util.List commonTypeArgs = commonType.getTypeArgumentList();
java.util.List commonTps = commonType.getDeclaration().getTypeParameters();
java.util.List expectedTypeArgs = expectedType.getTypeArgumentList();
java.util.List expectedTps = expectedType.getDeclaration().getTypeParameters();
// check that we got them all otherwise we just don't know
if(commonTypeArgs.size() != expectedTypeArgs.size())
return false;
for(int i=0,n=commonTypeArgs.size(); i < n ; i++){
// apply the same logic to each type param: see if they would require a raw cast
Type commonTypeArg = commonTypeArgs.get(i);
Type expectedTypeArg = expectedTypeArgs.get(i);
if (hasDependentTypeParameters(commonTps, commonTps.get(i))
|| hasDependentTypeParameters(expectedTps, expectedTps.get(i))) {
// In this case makeJavaType() will have made the Java decl
// invariant in this type argument, so we will need a type cast
// if the type parameters are not identical:
if (!simplifyType(commonTypeArg).isExactly(simplifyType(expectedTypeArg))) {
return true;
}
}
if(needsCast(commonTypeArg, expectedTypeArg, expectedTypeNotRaw,
expectedTypeHasConstrainedTypeParameters,
downCast))
return true;
// stop after the first one for Callable
if(isCallable)
break;
}
if(commonType.getQualifyingType() != null
&& expectedType.getQualifyingType() != null){
return needsCast(commonType.getQualifyingType(),
expectedType.getQualifyingType(),
expectedTypeNotRaw,
expectedTypeHasConstrainedTypeParameters,
downCast);
}
return false;
}
private boolean lostTypeParameterInInheritance(Type exprType, Type commonType) {
if(exprType.getDeclaration() instanceof ClassOrInterface == false
|| commonType.getDeclaration() instanceof ClassOrInterface == false)
return false;
ClassOrInterface exprDecl = (ClassOrInterface) exprType.getDeclaration();
ClassOrInterface commonDecl = (ClassOrInterface) commonType.getDeclaration();
// do not search interfaces if the common declaration is a class, because interfaces cannot be subtypes of a class
boolean searchInterfaces = commonDecl instanceof Interface;
return lostTypeParameterInInheritance(exprDecl, commonDecl, searchInterfaces, false);
}
private boolean lostTypeParameterInInheritance(ClassOrInterface exprDecl, ClassOrInterface commonDecl, boolean searchInterfaces, boolean lostTypeParameter) {
// stop if we found the common decl
if(Decl.equal(exprDecl, commonDecl))
return lostTypeParameter;
if(searchInterfaces){
// find a match in interfaces
for(Type pt : exprDecl.getSatisfiedTypes()){
// FIXME: this is very heavy-handed because we consider that once we've lost a type parameter we've lost them all
// but we could optimise this by checking:
// 1/ which type parameter we've really lost
// 2/ if the type parameters we're passing to our super type actually depend in any way from type parameters we've lost
boolean lostTypeParameter2 = lostTypeParameter || isTurnedToRaw(pt);
pt = simplifyType(pt);
// it has to be an interface
Interface interf = (Interface) pt.getDeclaration();
if(lostTypeParameterInInheritance(interf, commonDecl, searchInterfaces, lostTypeParameter2))
return true;
}
}
// search for super classes
Type extendedType = exprDecl.getExtendedType();
if(extendedType != null){
// FIXME: see above
boolean lostTypeParameter2 = lostTypeParameter || isTurnedToRaw(extendedType);
extendedType = simplifyType(extendedType);
// it has to be a Class
Class extendedTypeDeclaration = (Class) extendedType.getDeclaration();
// looks like Object's superclass is Object, so stop right there
if(extendedTypeDeclaration != typeFact().getObjectDeclaration())
return lostTypeParameterInInheritance(extendedTypeDeclaration, commonDecl, searchInterfaces, lostTypeParameter2);
}
// didn't find it
return false;
}
private boolean hasTypeParameters(Type type) {
if (!type.getTypeArgumentList().isEmpty()) {
return true;
}
if (type.getDeclaration() instanceof UnionType
&& type.getCaseTypes() != null) {
for (Type ct : type.getCaseTypes()) {
if (hasTypeParameters(ct)) {
return true;
}
}
}
if(type.getQualifyingType() != null)
return hasTypeParameters(type.getQualifyingType());
return false;
}
private JCExpression applyVarianceCasts(JCExpression result, Type exprType,
boolean exprBoxed,
BoxingStrategy boxingStrategy, Type expectedType, int flags) {
// unboxed types certainly don't need casting for variance
if(exprBoxed || boxingStrategy == BoxingStrategy.BOXED){
VarianceCastResult varianceCastResult = getVarianceCastResult(expectedType, exprType);
if(varianceCastResult != null){
result = applyVarianceCasts(result, expectedType, varianceCastResult, flags);
}
}
return result;
}
private JCExpression applyVarianceCasts(JCExpression result, Type expectedType, VarianceCastResult varianceCastResult,
int flags) {
// Types with variance types need a type cast, let's start with a raw cast to get rid
// of Java's type system constraint (javac doesn't grok multiple implementations of the same
// interface with different type params, which the JVM allows)
int forCompanionMask = (flags & EXPR_FOR_COMPANION) != 0 ? JT_SATISFIES : 0;
int wantsCompanionMask = (flags & EXPR_WANTS_COMPANION) != 0 ? JT_COMPANION : 0;
JCExpression targetType = makeJavaType(expectedType, AbstractTransformer.JT_RAW | wantsCompanionMask);
// do not change exprType here since this is just a Java workaround
result = make().TypeCast(targetType, result);
// now, because a raw cast is losing a lot of info, can we do better?
if(varianceCastResult.isBetterCastAvailable()){
// let's recast that to something finer than a raw cast
targetType = makeJavaType(varianceCastResult.castType, AbstractTransformer.JT_TYPE_ARGUMENT | wantsCompanionMask | forCompanionMask);
result = make().TypeCast(targetType, result);
}
return result;
}
private JCExpression applySelfTypeCasts(JCExpression result, Type exprType,
boolean exprBoxed,
BoxingStrategy boxingStrategy, Type expectedType) {
if (expectedType == null) {
return result;
}
final Type selfType = exprType.getDeclaration().getSelfType();
if (selfType != null) {
if (selfType.isExactly(exprType) // self-type within its own scope
|| !exprType.isExactly(expectedType)) {
final Type castType = findTypeArgument(exprType, selfType.getDeclaration());
// the fact that the original expr was or not boxed doesn't mean the current result is boxed or not
// as boxing transformations occur before this method
boolean resultBoxed = boxingStrategy == BoxingStrategy.BOXED
|| (boxingStrategy == BoxingStrategy.INDIFFERENT && exprBoxed);
JCExpression targetType = makeJavaType(castType, resultBoxed ? AbstractTransformer.JT_TYPE_ARGUMENT : 0);
result = make().TypeCast(targetType, result);
}
}
return result;
}
private Type findTypeArgument(Type type, TypeDeclaration declaration) {
if(type == null)
return null;
Type typeArgument = type.getTypeArguments().get(declaration);
if(typeArgument != null)
return typeArgument;
return findTypeArgument(type.getQualifyingType(), declaration);
}
private JCExpression applyJavaTypeConversions(JCExpression ret, Type exprType, Type expectedType,
BoxingStrategy boxingStrategy, boolean exprBoxed, boolean exprSmall, int flags) {
if(exprType == null || boxingStrategy != BoxingStrategy.UNBOXED)
return ret;
Type definiteExprType = simplifyType(exprType);
if(definiteExprType == null)
return ret;
// ignore the underlying type of the expr type if it was boxed, since we must have unboxed it to
// something with no underlying type first
String convertFrom = exprBoxed ? null : definiteExprType.getUnderlyingType();
Type definiteExpectedType = null;
String convertTo = null;
if (expectedType != null) {
definiteExpectedType = simplifyType(expectedType);
convertTo = definiteExpectedType.getUnderlyingType();
}
if (convertTo == null && exprSmall && definiteExpectedType != null) {
if (definiteExpectedType.isInteger()) {
convertTo = "int";
} else if (definiteExpectedType.isFloat()) {
convertTo = "float";
} else if (definiteExpectedType.isCharacter()) {
convertTo = "char";
}
}
// check for identity conversion
if (convertFrom != null && convertFrom.equals(convertTo)) {
return ret;
}
if (isCeylonByte(definiteExpectedType) && isCeylonInteger(exprType)) {
if ((flags & EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK) == 0) {
if(ret instanceof JCTree.JCUnary){
JCTree.JCUnary unary = (JCTree.JCUnary)ret;
if(unary.getTag() == JCTree.Tag.NEG
&& unary.arg instanceof JCTree.JCLiteral){
Object value = ((JCTree.JCLiteral)unary.arg).value;
if(value instanceof Integer){
int val = (Integer)value;
// if it fits let's just leave it
if(val >= 0 && val <= -Byte.MIN_VALUE){
// in the case of -128 to 127 we don't need to cast to byte by using an int literal, but only for
// assignment, not for method calls, so it's simpler to always cast
return make().TypeCast(syms().byteType, ret);
}
}
}
}
}
ret = make().TypeCast(syms().byteType, ret);
} else {
if (convertTo != null) {
if(convertTo.equals("short")) {
if ((flags & EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK) == 0) {
ret = utilInvocation().toShort(ret);
} else {
ret = make().TypeCast(syms().shortType, ret);
}
} else if(convertTo.equals("int")) {
if ((flags & EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK) == 0) {
ret = utilInvocation().toInt(ret);
} else {
ret = make().TypeCast(syms().intType, ret);
}
} else if(convertTo.equals("float")) {
ret = make().TypeCast(syms().floatType, ret);
} else if(convertTo.equals("char")) {
ret = make().TypeCast(syms().charType, ret);
}
} else if (convertFrom != null
&& (flags & EXPR_WIDEN_PRIM) != 0) {
if (isCeylonInteger(exprType)
&& (convertFrom.equals("int")
|| convertFrom.equals("short")
|| convertFrom.equals("byte"))) {
ret = make().TypeCast(syms().longType, ret);
} else if (isCeylonFloat(exprType)&&
convertFrom.equals("float")) {
ret = make().TypeCast(syms().doubleType, ret);
}
}
}
return ret;
}
private final class InvocationTermTransformer implements TermTransformer {
private final Invocation invocation;
private final CallBuilder callBuilder;
private InvocationTermTransformer(
Invocation invocation,
CallBuilder callBuilder) {
this.invocation = invocation;
this.callBuilder = callBuilder;
}
@Override
public JCExpression transform(JCExpression primaryExpr, String selector) {
TransformedInvocationPrimary transformedPrimary = invocation.transformPrimary(primaryExpr, selector);
callBuilder.argumentsAndTypes(transformArgumentList(invocation, transformedPrimary, callBuilder));
JCExpression resultExpr;
if (invocation instanceof NamedArgumentInvocation) {
resultExpr = transformNamedArgumentInvocationOrInstantiation((NamedArgumentInvocation)invocation, callBuilder, transformedPrimary);
} else {
resultExpr = transformPositionalInvocationOrInstantiation(invocation, callBuilder, transformedPrimary);
}
return resultExpr;
}
}
private static class VarianceCastResult {
Type castType;
VarianceCastResult(Type castType){
this.castType = castType;
}
private VarianceCastResult(){}
boolean isBetterCastAvailable(){
return castType != null;
}
}
private static final VarianceCastResult RawCastVarianceResult = new VarianceCastResult();
private VarianceCastResult getVarianceCastResult(Type expectedType, Type exprType) {
// exactly the same type, doesn't need casting
if(expectedType == null || exprType.isExactly(expectedType))
return null;
// if we're not trying to put it into an interface, there's no need
if(!(expectedType.getDeclaration() instanceof Interface))
return null;
// the interface must have type arguments, otherwise we can't use raw types
if(expectedType.getTypeArguments().isEmpty())
return null;
// see if any of those type arguments has variance
boolean hasVariance = false;
for(TypeParameter t : expectedType.getTypeArguments().keySet()){
if(expectedType.isContravariant(t) || expectedType.isCovariant(t)){
hasVariance = true;
break;
}
}
if(!hasVariance)
return null;
// see if we're inheriting the interface twice with different type parameters
java.util.List satisfiedTypes = new LinkedList();
for(Type superType : simplifyType(exprType).getSupertypes()){
if(Decl.equal(superType.getDeclaration(), expectedType.getDeclaration()))
satisfiedTypes.add(superType);
}
// discard the supertypes that have the same erasure
for(int i=0;i tal1 = pt.getTypeArgumentList();
java.util.List tal2 = other.getTypeArgumentList();
if(tal1.size() != tal2.size())
return false;
for(int i=0;i arguments = List.of(reifiedArguments, ceylonLiteral(declaration.getName()));
JCExpression classModel = makeSelect(typeCall, "getDeclaredConstructor");
memberCall = make().Apply(null, classModel, arguments);
} else if(declaration instanceof Function){
// we need to get types for each type argument
JCExpression closedTypesExpr = null;
if(expr.getTypeArgumentList() != null) {
java.util.List typeModels = expr.getTypeArgumentList().getTypeModels();
if (typeModels!=null) {
closedTypesExpr = getClosedTypesSequential(typeModels);
}
}
// we also need type descriptors for ret and args
Type callableType = producedReference.getFullType();
JCExpression reifiedReturnTypeExpr = makeReifiedTypeArgument(typeFact().getCallableReturnType(callableType));
JCExpression reifiedArgumentsExpr = makeReifiedTypeArgument(typeFact().getCallableTuple(callableType));
List arguments;
if(closedTypesExpr != null)
arguments = List.of(reifiedContainerExpr, reifiedReturnTypeExpr, reifiedArgumentsExpr,
ceylonLiteral(declaration.getName()), closedTypesExpr);
else
arguments = List.of(reifiedContainerExpr, reifiedReturnTypeExpr, reifiedArgumentsExpr,
ceylonLiteral(declaration.getName()));
memberCall = make().Apply(null, makeSelect(typeCall, "getMethod"), arguments);
}else if(declaration instanceof Value){
JCExpression reifiedGetExpr = makeReifiedTypeArgument(producedReference.getType());
String getterName = "getAttribute";
Type ptype;
if(!((Value)declaration).isVariable())
ptype = typeFact().getNothingType();
else
ptype = producedReference.getType();
JCExpression reifiedSetExpr = makeReifiedTypeArgument(ptype);
memberCall = make().Apply(null, makeSelect(typeCall, getterName), List.of(reifiedContainerExpr, reifiedGetExpr, reifiedSetExpr,
ceylonLiteral(declaration.getName())));
}else{
return makeErroneous(expr, "Unsupported member type: "+declaration);
}
// if(objectMember){
// // now get the instance and bind it
// // I don't think we need any expected type since objects can't be erased
// JCExpression object = transformExpression(expr.getObjectExpression());
// // reset the location after we transformed the expression
// memberCall = at(expr).Apply(null, makeSelect(memberCall, "bind"), List.of(object));
// }
// cast the member call because we invoke it with no Java generics
memberCall = make().TypeCast(makeJavaType(expr.getTypeModel(), JT_RAW | JT_NO_PRIMITIVES), memberCall);
memberCall = make().TypeCast(makeJavaType(expr.getTypeModel(), JT_NO_PRIMITIVES), memberCall);
return memberCall;
}
}
JCExpression makeMemberValueOrFunctionDeclarationLiteral(Node node, Declaration declaration) {
return makeMemberValueOrFunctionDeclarationLiteral(node, declaration, true);
}
JCExpression makeMemberValueOrFunctionDeclarationLiteral(Node node, Declaration declaration, boolean f) {
// it's a member we get from its container declaration
if(declaration.getContainer() instanceof ClassOrInterface == false)
return makeErroneous(node, "compiler bug: " + declaration.getContainer() + " is not a supported type parameter container");
ClassOrInterface container = (ClassOrInterface) declaration.getContainer();
// use the generated class to get to the declaration literal
JCExpression metamodelCall = makeTypeDeclarationLiteral(container);
JCExpression metamodelCast = makeJavaType(typeFact().getLanguageModuleDeclarationTypeDeclaration(
Decl.isConstructor(declaration) ? "ClassDeclaration": "ClassOrInterfaceDeclaration").getType(),
JT_NO_PRIMITIVES);
metamodelCall = make().TypeCast(metamodelCast, metamodelCall);
String memberClassName;
String memberAccessor;
if(declaration instanceof Class)
memberClassName = "ClassDeclaration";
else if (Decl.isConstructor(declaration))
memberClassName = "ConstructorDeclaration";
else if(declaration instanceof Interface)
memberClassName = "InterfaceDeclaration";
else if(declaration instanceof Function)
memberClassName = "FunctionDeclaration";
else if(declaration instanceof Value){
memberClassName = "ValueDeclaration";
} else {
return makeErroneous(node, "compiler bug: " + declaration + " is not a supported declaration literal");
}
if (Decl.isConstructor(declaration))
memberAccessor = "getConstructorDeclaration";
else
memberAccessor = f ? "getMemberDeclaration" : "getDeclaredMemberDeclaration";
TypeDeclaration metamodelDecl = (TypeDeclaration) typeFact().getLanguageModuleDeclarationDeclaration(memberClassName);
JCExpression memberType = makeJavaType(metamodelDecl.getType());
JCExpression reifiedMemberType = makeReifiedTypeArgument(metamodelDecl.getType());
JCExpression memberCall = make().Apply(List.of(memberType),
makeSelect(metamodelCall, memberAccessor),
List.of(reifiedMemberType, ceylonLiteral(declaration.getName())));
return memberCall;
}
private JCExpression makeTopLevelValueOrFunctionDeclarationLiteral(Declaration declaration) {
// toplevel method or attribute: we need to fetch them from their module/package
Package pkg = Decl.getPackageContainer(declaration.getContainer());
// get the package
JCExpression packageCall = makePackageLiteralCall(pkg);
// now get the toplevel
String getter = Decl.isMethod(declaration) ? "getFunction" : "getValue";
JCExpression toplevelCall = make().Apply(null, makeSelect(packageCall, getter),
List.of(ceylonLiteral(declaration.getName())));
return toplevelCall;
}
private JCTree makeTopLevelValueOrFunctionLiteral(Tree.MemberLiteral expr) {
Declaration declaration = expr.getDeclaration();
JCExpression toplevelCall = makeTopLevelValueOrFunctionDeclarationLiteral(declaration);
if(!expr.getWantsDeclaration()){
ListBuffer closedTypeArgs = new ListBuffer();
// expr is of type Function or Value so we can get its type like that
JCExpression reifiedType = makeReifiedTypeArgument(expr.getTypeModel().getTypeArgumentList().get(0));
closedTypeArgs.append(reifiedType);
if(Decl.isMethod(declaration)){
// expr is of type Function so we can get its arguments type like that
Type argumentsType = typeFact().getCallableTuple(expr.getTypeModel());
JCExpression reifiedArguments = makeReifiedTypeArgument(argumentsType);
closedTypeArgs.append(reifiedArguments);
if(expr.getTypeArgumentList() != null){
java.util.List typeModels = expr.getTypeArgumentList().getTypeModels();
if(typeModels!=null){
JCExpression closedTypesExpr = getClosedTypesSequential(typeModels);
// must apply it
closedTypeArgs.append(closedTypesExpr);
}
}
}else{
JCExpression reifiedSet;
Type ptype;
if(!((Value)declaration).isVariable())
ptype = typeFact().getNothingType();
else
ptype = expr.getTypeModel().getTypeArgumentList().get(0);
reifiedSet = makeReifiedTypeArgument(ptype);
closedTypeArgs.append(reifiedSet);
}
toplevelCall = make().Apply(null,
makeSelect(toplevelCall, "apply"),
closedTypeArgs.toList());
// add cast
Type exprType = expr.getTypeModel().resolveAliases();
JCExpression typeClass = makeJavaType(exprType, JT_NO_PRIMITIVES);
JCExpression rawTypeClass = makeJavaType(exprType, JT_NO_PRIMITIVES | JT_RAW);
return make().TypeCast(typeClass, make().TypeCast(rawTypeClass, toplevelCall));
}
return toplevelCall;
}
private JCExpression makePackageLiteralCall(Package pkg) {
// get the module
Module module = pkg.getModule();
JCExpression moduleCall = makeModuleLiteralCall(module);
// now get the package
return make().Apply(null, makeSelect(moduleCall, "findPackage"),
List.of(ceylonLiteral(pkg.getNameAsString())));
}
private JCExpression makeModuleLiteralCall(Module module) {
JCExpression modulesGetIdent = naming.makeFQIdent("ceylon", "language", "meta", "modules_", "get_");
JCExpression modulesGet = make().Apply(null, modulesGetIdent, List.nil());
JCExpression call;
if(module.isDefaultModule()){
call = make().Apply(null, makeSelect(modulesGet, "getDefault"), List.nil());
}else{
call = make().Apply(null, makeSelect(modulesGet, "find"),
List.of(ceylonLiteral(module.getNameAsString()),
ceylonLiteral(module.getVersion())));
}
// make sure we handle missing modules gracefully
String version = module.getVersion();
return makeMetamodelInvocation("checkModule", List.of(call, ceylonLiteral(module.getNameAsString()), version == null ? makeNull() : ceylonLiteral(version)), null);
}
private JCExpression getClosedTypesSequential(java.util.List typeModels) {
ListBuffer closedTypes = new ListBuffer();
for (Type producedType : typeModels) {
closedTypes.add(makeTypeLiteralCall(producedType));
}
Type elementType = typeFact().getMetamodelTypeDeclaration().appliedType(null, Arrays.asList(typeFact().getAnythingType()));
// now wrap into a sequential
return makeSequence(closedTypes.toList(), elementType, CeylonTransformer.JT_CLASS_NEW);
}
private JCExpression makeTypeLiteralCall(Type producedType) {
JCExpression typeLiteralIdent = naming.makeFQIdent("ceylon", "language", "meta", "typeLiteral_", "typeLiteral");
JCExpression reifiedTypeArgument = makeReifiedTypeArgument(producedType.resolveAliases());
// note that we don't pass it a Java type argument since it's not used
return make().Apply(null, typeLiteralIdent, List.of(reifiedTypeArgument));
}
JCExpression makeTypeLiteralCall(Type type, boolean addCast, Type exprType) {
// construct a call to typeLiteral() and cast if required
JCExpression call = makeTypeLiteralCall(type);
if(addCast){
// if we have a type that is not nothingType and not Type, we need to cast
exprType = exprType.resolveAliases();
if(!exprType.isUnion()
&& !exprType.isExactly(typeFact().getMetamodelNothingTypeDeclaration().getType())
&& !exprType.isExactly(typeFact().getMetamodelTypeDeclaration().getType())){
JCExpression typeClass = makeJavaType(exprType, JT_NO_PRIMITIVES);
return make().TypeCast(typeClass, call);
}
}
return call;
}
public JCTree transform(Tree.TypeLiteral expr) {
at(expr);
if(!expr.getWantsDeclaration()){
if (expr.getDeclaration() instanceof Constructor) {
JCExpression classLiteral = makeTypeLiteralCall(expr.getType().getTypeModel().getQualifyingType(), false, expr.getTypeModel());
TypeDeclaration classModelDeclaration = (TypeDeclaration)typeFact().getLanguageModuleModelDeclaration(
expr.getType().getTypeModel().getQualifyingType().getDeclaration().isMember() ? "MemberClass" : "Class");
JCTypeCast typeCast = make().TypeCast(
makeJavaType(classModelDeclaration.appliedType(null,
List.of(expr.getType().getTypeModel().getQualifyingType(),
typeFact().getNothingType()))),
classLiteral);
Type callableType = expr.getTypeModel().getFullType();
JCExpression reifiedArgumentsExpr = makeReifiedTypeArgument(typeFact().getCallableTuple(callableType));
return make().Apply(null,
naming.makeQualIdent(typeCast, "getConstructor"),
List.of(
reifiedArgumentsExpr,
make().Literal(expr.getDeclaration().getName())));
} else {
if (coerced) {
Type t = expr.getType().getTypeModel();
if (!typeFact().isJavaObjectArrayType(t)
|| t.getTypeArgumentList().get(0).isClassOrInterface()) {
return makeSelect(makeJavaType(t, JT_NO_PRIMITIVES | JT_RAW ), "class");
}
}
return makeTypeLiteralCall(expr.getType().getTypeModel(), true, expr.getTypeModel());
}
}else if(expr.getDeclaration() instanceof TypeParameter){
// we must get it from its container
TypeParameter declaration = (TypeParameter)expr.getDeclaration();
Node node = expr;
return makeTypeParameterDeclaration(node, declaration);
}else if (expr.getDeclaration() instanceof Constructor
|| expr instanceof Tree.NewLiteral) {
Constructor ctor;
if (expr.getDeclaration() instanceof Constructor) {
ctor = (Constructor)expr.getDeclaration();
} else {
ctor = Decl.getDefaultConstructor((Class)expr.getDeclaration());
}
JCExpression metamodelCall = makeTypeDeclarationLiteral(Decl.getConstructedClass(ctor));
metamodelCall = make().TypeCast(
makeJavaType(typeFact().getClassDeclarationType(), JT_RAW), metamodelCall);
metamodelCall = make().Apply(null,
naming.makeQualIdent(metamodelCall, "getConstructorDeclaration"),
List.of(make().Literal(ctor.getName() == null ? "" : ctor.getName())));
if (Decl.isEnumeratedConstructor(ctor)) {
metamodelCall = make().TypeCast(
makeJavaType(typeFact().getValueConstructorDeclarationType(), JT_RAW), metamodelCall);
} /*else if (Decl.isDefaultConstructor(ctor)){
metamodelCall = make().TypeCast(
makeJavaType(typeFact().getDefaultConstructorDeclarationType(), JT_RAW), metamodelCall);
} */else {
metamodelCall = make().TypeCast(
makeJavaType(typeFact().getCallableConstructorDeclarationType(), JT_RAW), metamodelCall);
}
return metamodelCall;
}else if(expr.getDeclaration() instanceof ClassOrInterface
|| expr.getDeclaration() instanceof TypeAlias){
// use the generated class to get to the declaration literal
JCExpression metamodelCall = makeTypeDeclarationLiteral((TypeDeclaration) expr.getDeclaration());
Type exprType = expr.getTypeModel().resolveAliases();
// now cast if required
if(!exprType.isExactly(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("NestableDeclaration")).getType())){
JCExpression type = makeJavaType(exprType, JT_NO_PRIMITIVES);
return make().TypeCast(type, metamodelCall);
}
return metamodelCall;
}else{
return makeErroneous(expr, "compiler bug: " + expr.getDeclaration() + " is an unsupported declaration type");
}
}
/**
* Makes an expression equivalent to the result of {@code `given T`}
* @param node
* @param declaration
* @return
*/
JCExpression makeTypeParameterDeclaration(Node node,
TypeParameter declaration) {
Scope container = declaration.getContainer();
if(container instanceof Declaration){
JCExpression containerExpr;
Declaration containerDeclaration = (Declaration) container;
if(containerDeclaration instanceof ClassOrInterface
|| containerDeclaration instanceof TypeAlias){
JCExpression metamodelCall = makeTypeDeclarationLiteral((TypeDeclaration) containerDeclaration);
JCExpression metamodelCast = makeJavaType(typeFact().getLanguageModuleDeclarationTypeDeclaration("GenericDeclaration").getType(), JT_NO_PRIMITIVES);
containerExpr = make().TypeCast(metamodelCast, metamodelCall);
}else if(containerDeclaration.isToplevel()) {
containerExpr = makeTopLevelValueOrFunctionDeclarationLiteral(containerDeclaration);
}else{
containerExpr = makeMemberValueOrFunctionDeclarationLiteral(node, containerDeclaration);
}
// now it must be a ClassOrInterfaceDeclaration or a FunctionDeclaration, both of which have the method we need
return at(node).Apply(null, makeSelect(containerExpr, "getTypeParameterDeclaration"), List.of(ceylonLiteral(declaration.getName())));
}else{
return makeErroneous(node, "compiler bug: " + container + " is not a supported type parameter container");
}
}
JCExpression makeTypeDeclarationLiteral(TypeDeclaration declaration) {
JCExpression classLiteral = makeUnerasedClassLiteral(declaration);
return makeMetamodelInvocation("getOrCreateMetamodel", List.of(classLiteral), null);
}
public JCExpression transformStringExpression(Tree.StringTemplate expr) {
at(expr);
JCExpression builder;
builder = make().NewClass(null, null, naming.makeFQIdent("java","lang","StringBuilder"), List.nil(), null);
java.util.List literals = expr.getStringLiterals();
java.util.List expressions = expr.getExpressions();
for (int ii = 0; ii < literals.size(); ii += 1) {
Tree.StringLiteral literal = literals.get(ii);
if (!literal.getText().isEmpty()) {// ignore empty string literals
at(literal);
builder = make().Apply(null, makeSelect(builder, "append"), List.of(transform(literal)));
}
if (ii == expressions.size()) {
// The loop condition includes the last literal, so break out
// after that because we've already exhausted all the expressions
break;
}
Tree.Expression expression = expressions.get(ii);
at(expression);
// Here in both cases we don't need a type cast for erasure
if (isCeylonBasicType(expression.getTypeModel())
&& expression.getUnboxed()) {// TODO: Test should be erases to String, long, int, boolean, char, byte, float, double
// If erases to a Java primitive just call append, don't box it just to call format.
String method = isCeylonCharacter(expression.getTypeModel()) ? "appendCodePoint" : "append";
builder = make().Apply(null, makeSelect(builder, method), List.of(
transformExpression(expression, BoxingStrategy.UNBOXED, null)));
} else {
JCMethodInvocation formatted = make().Apply(null, makeSelect(transformExpression(expression), "toString"), List.nil());
builder = make().Apply(null, makeSelect(builder, "append"), List.of(formatted));
}
}
return make().Apply(null, makeSelect(builder, "toString"), List.nil());
}
JCExpression transform(Tree.SequenceEnumeration value) {
at(value);
if (value.getSequencedArgument() != null) {
Tree.SequencedArgument sequencedArgument = value.getSequencedArgument();
java.util.List list = sequencedArgument.getPositionalArguments();
if(list.isEmpty())
return makeErroneous(value, "compiler bug: empty iterable literal with sequenced arguments: "+value);
Type seqElemType = typeFact().getIteratedType(value.getTypeModel());
seqElemType = wrapInOptionalForInterop(seqElemType, expectedType, containsUncheckedNulls(list));
Type absentType = typeFact().getIteratedAbsentType(value.getTypeModel());
if (Strategy.useConstantIterable(sequencedArgument)) {
return makeConstantIterable(sequencedArgument, seqElemType, absentType, 0);
} else {
if(list.size() == 1 && list.get(0) instanceof Tree.Comprehension){
Type type = typeFact().getIterableType(seqElemType);
return expressionGen().transformComprehension((Tree.Comprehension) list.get(0), type);
}
return makeLazyIterable(sequencedArgument, seqElemType, absentType, 0);
}
} else {
return makeEmpty();
}
}
private boolean containsUncheckedNulls(java.util.List list) {
for(Tree.PositionalArgument arg : list){
if(arg instanceof Tree.ListedArgument){
if(containsUncheckedNulls(((Tree.ListedArgument) arg).getExpression()))
return true;
}else if(arg instanceof Tree.Comprehension){
if(containsUncheckedNulls((Tree.Comprehension)arg))
return true;
}else if(arg instanceof Tree.SpreadArgument){
if(containsUncheckedNulls(((Tree.SpreadArgument) arg).getExpression()))
return true;
}
}
return false;
}
private boolean containsUncheckedNulls(Tree.Term term){
if(term instanceof Tree.Expression){
return containsUncheckedNulls(((Tree.Expression) term).getTerm());
}else if(term instanceof Tree.SequenceEnumeration){
Tree.SequencedArgument sequencedArgument = ((Tree.SequenceEnumeration) term).getSequencedArgument();
if(sequencedArgument == null)
return false;
return containsUncheckedNulls(sequencedArgument.getPositionalArguments());
}else
return hasUncheckedNulls(term);
}
private boolean containsUncheckedNulls(Tree.Comprehension comp) {
Tree.ComprehensionClause clause = comp.getInitialComprehensionClause();
while(clause instanceof Tree.ExpressionComprehensionClause == false){
if(clause instanceof Tree.ForComprehensionClause)
clause = ((Tree.ForComprehensionClause) clause).getComprehensionClause();
else if(clause instanceof Tree.IfComprehensionClause)
clause = ((Tree.IfComprehensionClause) clause).getComprehensionClause();
else
return false;// error recovery
}
if(clause instanceof Tree.ExpressionComprehensionClause)
return containsUncheckedNulls(((Tree.ExpressionComprehensionClause) clause).getExpression());
return false;
}
public JCExpression transform(Tree.Tuple value) {
Tree.SequencedArgument sequencedArgument = value.getSequencedArgument();
if(sequencedArgument != null){
java.util.List args = sequencedArgument.getPositionalArguments();
return makeTuple(value.getTypeModel(), args);
}
// nothing in there
return makeEmpty();
}
private JCExpression sequentialEmptiness(JCExpression sequential,
Type expectedType, Type sequentialType) {
int flags = 0;
// make sure we detect that we're downcasting a sequential into a sequence if we know the comprehension is non-empty
if(expectedType.getSupertype(typeFact().getSequenceDeclaration()) != null)
flags = EXPR_DOWN_CAST;
return applyErasureAndBoxing(sequential, sequentialType, false, true, BoxingStrategy.BOXED, expectedType, flags);
}
public JCExpression comprehensionAsSequential(Tree.Comprehension comprehension, Type expectedType) {
JCExpression sequential = iterableToSequential(transformComprehension(comprehension));
Type elementType = comprehension.getInitialComprehensionClause().getTypeModel();
Type sequentialType = typeFact().getSequentialType(elementType);
return sequentialEmptiness(sequential, expectedType, sequentialType);
}
private JCExpression makeTuple(Type tupleType, java.util.List expressions) {
if (typeFact().isEmptyType(tupleType)) {
return makeEmpty();// A tuple terminated by empty
}
JCExpression tail = null;
List elems = List.nil();
for (int i = 0; i < expressions.size(); i++) {
Tree.PositionalArgument expr = expressions.get(i);
if (expr instanceof Tree.ListedArgument) {
JCExpression elem = transformExpression(((Tree.ListedArgument) expr).getExpression());
elems = elems.append(elem);
} else if (expr instanceof Tree.SpreadArgument) {
Tree.SpreadArgument spreadExpr = (Tree.SpreadArgument) expr;
// make sure we get a spread part of the right type
Type spreadType = spreadExpr.getExpression().getTypeModel();
Type sequentialSpreadType = null;
// try to get a Sequence
if (typeFact().isNonemptyIterableType(spreadType))
sequentialSpreadType = spreadType.getSupertype(typeFact().getSequenceDeclaration());
// failing that, try Sequential
if(sequentialSpreadType == null)
sequentialSpreadType = spreadType.getSupertype(typeFact().getSequentialDeclaration());
if(sequentialSpreadType != null){
tail = transformExpression(spreadExpr.getExpression(), BoxingStrategy.BOXED, sequentialSpreadType);
}else {
// must at least be an Iterable, Java Iterable, or Java array then
Type iterableSpreadType;
Type iteratedType;
if (typeFact().isIterableType(spreadType)) {
iterableSpreadType = spreadType.getSupertype(typeFact().getIterableDeclaration());
iteratedType = typeFact().getIteratedType(iterableSpreadType);
tail = transformExpression(spreadExpr.getExpression(), BoxingStrategy.BOXED, iterableSpreadType);
tail = utilInvocation().sequentialOf(makeReifiedTypeArgument(iteratedType), tail);
} else if (typeFact().isJavaIterableType(spreadType)) {
iterableSpreadType = spreadType.getSupertype(typeFact().getJavaIterableDeclaration());
iteratedType = typeFact().getJavaIteratedType(iterableSpreadType);
tail = transformExpression(spreadExpr.getExpression(), BoxingStrategy.BOXED, iterableSpreadType);
tail = utilInvocation().toIterable(
makeJavaType(iteratedType, JT_TYPE_ARGUMENT),
makeReifiedTypeArgument(iteratedType), tail);
tail = make().Apply(null, makeSelect(tail, "sequence"), List.nil());
} else if (typeFact().isJavaArrayType(spreadType)) {
if (typeFact().isJavaObjectArrayType(spreadType)) {
iterableSpreadType = spreadType.getSupertype(typeFact().getJavaObjectArrayDeclaration());
} else {//primitive
iterableSpreadType = spreadType;
}
iteratedType = typeFact().getJavaArrayElementType(iterableSpreadType);
tail = transformExpression(spreadExpr.getExpression(), BoxingStrategy.BOXED, iterableSpreadType);
if (typeFact().isJavaObjectArrayType(spreadType)) {
tail = utilInvocation().toIterable(
makeJavaType(iteratedType, JT_TYPE_ARGUMENT),
makeReifiedTypeArgument(iteratedType), tail);
} else {//primitive
tail = utilInvocation().toIterable(tail);
}
tail = make().Apply(null, makeSelect(tail, "sequence"), List.nil());
} else {
throw BugException.unhandledTypeCase(spreadType);
}
Type elementType = typeFact().getIteratedType(spreadExpr.getTypeModel());
Type sequentialType = typeFact().getSequentialType(elementType);
Type expectedType = spreadExpr.getTypeModel();
if (typeFact().isNonemptyIterableType(spreadExpr.getTypeModel())) {
expectedType = typeFact().getSequenceType(elementType);
} else if (typeFact().isIterableType(spreadExpr.getTypeModel())) {
expectedType = typeFact().getSequentialType(elementType);
}
tail = sequentialEmptiness(tail, expectedType, sequentialType);
}
} else if (expr instanceof Tree.Comprehension) {
Tree.Comprehension comp = (Tree.Comprehension) expr;
Type elementType = expr.getTypeModel();
Type expectedType = comp.getInitialComprehensionClause().getPossiblyEmpty()
? typeFact().getSequentialType(elementType)
: typeFact().getSequenceType(elementType);
tail = comprehensionAsSequential(comp, expectedType);
} else {
return makeErroneous(expr, "compiler bug: " + expr.getNodeType() + " is not a supported tuple argument");
}
}
if (!elems.isEmpty()) {
JCExpression reifiedTypeArg = makeReifiedTypeArgument(tupleType.getTypeArgumentList().get(0));
List args = List.of(reifiedTypeArg);
args = args.append(make().NewArray(make().Type(syms().objectType), List.nil(), elems));
if (tail != null) {
args = args.append(tail);
}
JCExpression typeExpr = makeJavaType(tupleType, JT_TYPE_ARGUMENT);
/* Tuple.instance(reifiedElement, new Object[]{elem, elem, elem}, tail) */
return make().TypeCast(typeExpr, make().Apply(
List.nil(),
naming.makeQualIdent(make().QualIdent(syms().ceylonTupleType.tsym), "instance"),
args));
} else {
return tail;
}
}
public JCTree transform(Tree.This expr) {
at(expr);
if (needDollarThis(expr.getScope())) {
return naming.makeQuotedThis();
}
if (isWithinSyntheticClassBody()) {
return naming.makeQualifiedThis(makeJavaType(expr.getTypeModel()));
}
return naming.makeThis();
}
public JCTree transform(Tree.Super expr) {
throw new BugException(expr, "unreachable");
}
public JCTree transform(Tree.Outer expr) {
at(expr);
Type outerClass = com.redhat.ceylon.model.typechecker.model.ModelUtil.getOuterClassOrInterface(expr.getScope());
return makeOuterExpr(outerClass);
}
JCExpression makeOuterExpr(Type outerClass) {
final TypeDeclaration outerDeclaration = outerClass.getDeclaration();
if (outerDeclaration instanceof Interface) {
return makeQualifiedDollarThis(outerClass);
}
return naming.makeQualifiedThis(makeJavaType(outerClass));
}
//
// Unary and Binary operators that can be overridden
//
// Unary operators
public JCExpression transform(Tree.NotOp op) {
// No need for an erasure cast since Term must be Boolean and we never need to erase that
JCExpression term = transformExpression(op.getTerm(), CodegenUtil.getBoxingStrategy(op), op.getTypeModel());
JCUnary jcu = at(op).Unary(JCTree.Tag.NOT, term);
return jcu;
}
public JCExpression transform(Tree.OfOp op) {
if (op.getTerm() instanceof Tree.Super) {
// This should be unreachable
throw new BugException(op, "unreachable");
}
Type expectedType = op.getType().getTypeModel();
if (expectedType.isExactlyNothing()) {
expectedType = typeFact().getNothingType();
}
return transformExpression(op.getTerm(), CodegenUtil.getBoxingStrategy(op), expectedType, EXPR_DOWN_CAST);
}
public JCExpression transform(Tree.IsOp op) {
// make sure we do not insert null checks if we're going to allow testing for null
Type expectedType = getOptionalTypeForInteropIfAllowed(op.getType().getTypeModel(), op.getTerm().getTypeModel(), op.getTerm());
// we don't need any erasure type cast for an "is" test
JCExpression expression = transformExpression(op.getTerm(), BoxingStrategy.BOXED, expectedType);
at(op);
Naming.SyntheticName varName = naming.temp();
JCExpression test = makeOptimizedTypeTest(null, varName, op.getType().getTypeModel(), op.getTerm().getTypeModel());
return makeLetExpr(varName, List.nil(), make().Type(syms().objectType), expression, test);
}
public JCTree transform(Tree.Nonempty op) {
// we don't need any erasure type cast for a "nonempty" test
JCExpression expression = transformExpression(op.getTerm());
at(op);
Naming.SyntheticName varName = naming.temp();
JCExpression test = makeNonEmptyTest(varName.makeIdent());
return makeLetExpr(varName, List.nil(), make().Type(syms().objectType), expression, test);
}
public JCTree transform(Tree.Exists op) {
// for the purpose of checking if something is null, we need it boxed and optional, otherwise
// for some Java calls if we consider it non-optional we will get an unwanted null check
Type termType = op.getTerm().getTypeModel();
if(!typeFact().isOptionalType(termType)){
termType = typeFact().getOptionalType(termType);
}
JCExpression expression = transformExpression(op.getTerm(), BoxingStrategy.BOXED, termType);
at(op);
return make().Binary(JCTree.Tag.NE, expression, makeNull());
}
public JCExpression transform(Tree.PositiveOp op) {
if (op.getTerm() instanceof Tree.NaturalLiteral) {
try {
Number l = literalValue(op);
if (l != null) {
if (op.getSmall()) {
return make().Literal((Integer)l);
} else {
return make().Literal(l.longValue());
}
}
} catch (ErroneousException e) {
// We should never get here since the error should have been
// reported by the UnsupportedVisitor and the containing statement
// replaced with a throw.
return e.makeErroneous(this);
}
}
return transformOverridableUnaryOperator(op, op.getUnit().getInvertableDeclaration());
}
public JCExpression transform(Tree.NegativeOp op) {
at(op);
if (op.getTerm() instanceof Tree.NaturalLiteral) {
try {
Number l = literalValue(op);
if (l != null) {
if (op.getSmall()) {
return make().Literal((Integer)l);
} else {
return make().Literal(l.longValue());
}
}
} catch (ErroneousException e) {
// We should never get here since the error should have been
// reported by the UnsupportedVisitor and the containing statement
// replaced with a throw.
return e.makeErroneous(this);
}
}
if(op.getTerm() instanceof Tree.QualifiedMemberExpression){
JCExpression ret = checkForByteLiterals((Tree.QualifiedMemberExpression)op.getTerm());
if(ret != null)
return at(op).Unary(JCTree.Tag.NEG, ret);
}
return transformOverridableUnaryOperator(op, op.getUnit().getInvertableDeclaration());
}
public JCExpression transform(Tree.UnaryOperatorExpression op) {
return transformOverridableUnaryOperator(op, (Type)null);
}
private JCExpression transformOverridableUnaryOperator(Tree.UnaryOperatorExpression op, Interface compoundType) {
Type leftType = getSupertype(op.getTerm(), compoundType);
return transformOverridableUnaryOperator(op, leftType);
}
private JCExpression transformOverridableUnaryOperator(Tree.UnaryOperatorExpression op, Type expectedType) {
at(op);
Tree.Term term = op.getTerm();
OperatorTranslation operator = Operators.getOperator(op.getClass());
if (operator == null) {
return makeErroneous(op, "compiler bug: " + op.getClass() + " is an unhandled operator class");
}
JCExpression ret;
if(operator.getUnOpOptimisationStrategy(op, op.getTerm(), this).useJavaOperator()){
// optimisation for unboxed types
JCExpression expr = transformExpression(term, BoxingStrategy.UNBOXED, expectedType, EXPR_WIDEN_PRIM);
// unary + is essentially a NOOP
if(operator == OperatorTranslation.UNARY_POSITIVE)
return expr;
ret = make().Unary(operator.javacOperator, expr);
ret = unAutoPromote(ret, op.getTypeModel(), op.getSmall());
} else {
if(operator == OperatorTranslation.UNARY_POSITIVE) {
// +x is essentiall a NOOP, but in this case the expected type
// is the self type of Invertible, so use the type of op
return transformExpression(term, BoxingStrategy.BOXED, op.getTypeModel());
}
ret = make().Apply(null, makeSelect(transformExpression(term, BoxingStrategy.BOXED, expectedType),
Naming.getGetterName(operator.getCeylonMethodName())), List. nil());
}
return ret;
}
//
// Binary operators
public JCExpression transform(Tree.NotEqualOp op) {
JCExpression expr = transformNotEqualNeedsNegating(op).build();
return at(op).Unary(JCTree.Tag.NOT, expr);
}
public BinOpTransformation transformNotEqualNeedsNegating(Tree.NotEqualOp op) {
OperatorTranslation operator = Operators.OperatorTranslation.BINARY_EQUAL;
//OptimisationStrategy optimisationStrategy = operator.getBinOpOptimisationStrategy(op, op.getLeftTerm(), op.getRightTerm(), this);
// we want it unboxed only if the operator is optimised
// we don't care about the left erased type, since equals() is on Object
//JCExpression left = transformExpression(op.getLeftTerm(), optimisationStrategy.getBoxingStrategy(), null, EXPR_WIDEN_PRIM);
// we don't care about the right erased type, since equals() is on Object
return transformOverridableBinaryOperator(op, operator, op.getLeftTerm().getTypeModel(), op.getRightTerm().getTypeModel());
}
public JCExpression transform(Tree.EqualOp op) {
// we don't care about the left/right type since they're both Object
return transformOverridableBinaryOperator(op, op.getLeftTerm().getTypeModel(), op.getRightTerm().getTypeModel()).build();
}
public JCExpression transform(Tree.SegmentOp op) {
// we need to get the range bound type
final Type type = getTypeArgument(getSupertype(op.getLeftTerm(), op.getUnit().getEnumerableDeclaration()));
JCExpression startExpr = transformExpression(op.getLeftTerm(), BoxingStrategy.BOXED, type);
JCExpression lengthExpr = transformExpression(op.getRightTerm(), BoxingStrategy.UNBOXED, typeFact().getIntegerType());
return make().Apply(List.of(makeJavaType(type, JT_TYPE_ARGUMENT)),
naming.makeLanguageFunction("measure"),
List.of(makeReifiedTypeArgument(type), startExpr, lengthExpr));
}
public JCExpression transform(Tree.RangeOp op) {
// we need to get the range bound type
final Type type = getTypeArgument(getSupertype(op.getLeftTerm(), op.getUnit().getEnumerableDeclaration()));
JCExpression lower = transformExpression(op.getLeftTerm(), BoxingStrategy.BOXED, type);
JCExpression upper = transformExpression(op.getRightTerm(), BoxingStrategy.BOXED, type);
return make().Apply(List.of(makeJavaType(type, JT_TYPE_ARGUMENT)),
naming.makeLanguageFunction("span"),
List.of(makeReifiedTypeArgument(type), lower, upper));
}
public JCExpression transform(Tree.EntryOp op) {
// no erasure cast needed for both terms
JCExpression key = transformExpression(op.getLeftTerm());
JCExpression elem = transformExpression(op.getRightTerm());
Type leftType = op.getLeftTerm().getTypeModel();
Type rightType = op.getRightTerm().getTypeModel();
Type entryType = typeFact().getEntryType(leftType, rightType);
JCExpression typeExpr = makeJavaType(entryType, CeylonTransformer.JT_CLASS_NEW);
return at(op).NewClass(null, null, typeExpr , List. of(makeReifiedTypeArgument(leftType), makeReifiedTypeArgument(rightType), key, elem), null);
}
public JCExpression transform(Tree.DefaultOp op) {
Term elseTerm = unwrapExpressionUntilTerm(op.getRightTerm());
Type typeModel = typeFact().denotableType(op.getTypeModel());
// make sure we do not insert null checks if we're going to allow testing for null
Type rightExpectedType = getOptionalTypeForInteropIfAllowed(expectedType, typeModel, elseTerm);
if (unwrapExpressionUntilTerm(op.getLeftTerm()) instanceof Tree.ThenOp) {
// Optimize cond then foo else bar (avoids unnecessary boxing in particular)
Tree.ThenOp then = (Tree.ThenOp)unwrapExpressionUntilTerm(op.getLeftTerm());
Term condTerm = then.getLeftTerm();
Term thenTerm = then.getRightTerm();
JCExpression cond = transformExpression(condTerm, BoxingStrategy.UNBOXED, condTerm.getTypeModel());
JCExpression thenpart = transformExpression(thenTerm, CodegenUtil.getBoxingStrategy(op),
rightExpectedType);
JCExpression elsepart = transformExpression(elseTerm, CodegenUtil.getBoxingStrategy(op),
rightExpectedType);
return make().Conditional(cond, thenpart, elsepart);
}
JCExpression left = transformExpression(op.getLeftTerm(), BoxingStrategy.BOXED, typeFact().getOptionalType(typeModel));
JCExpression right = transformExpression(elseTerm, BoxingStrategy.BOXED, rightExpectedType);
Naming.SyntheticName varName = naming.temp();
JCExpression varIdent = varName.makeIdent();
JCExpression test = at(op).Binary(JCTree.Tag.NE, varIdent, makeNull());
JCExpression cond = make().Conditional(test , varIdent, right);
JCExpression typeExpr = makeJavaType(typeModel, JT_NO_PRIMITIVES);
return makeLetExpr(varName, null, typeExpr, left, cond);
}
public JCTree transform(Tree.ThenOp op) {
JCExpression left = transformExpression(op.getLeftTerm(), BoxingStrategy.UNBOXED, typeFact().getBooleanType());
JCExpression right = transformExpression(op.getRightTerm(), CodegenUtil.getBoxingStrategy(op), op.getTypeModel());
return make().Conditional(left , right, makeNull());
}
public JCTree transform(Tree.InOp op) {
if (isCeylonInteger(op.getLeftTerm().getTypeModel())) {
if (op.getRightTerm() instanceof Tree.RangeOp
&& isCeylonInteger(((Tree.RangeOp)op.getRightTerm()).getLeftTerm().getTypeModel())
&& isCeylonInteger(((Tree.RangeOp)op.getRightTerm()).getRightTerm().getTypeModel())) {
return makeOptimizedInIntegerRange(op, syms().longType);
} else if (op.getRightTerm() instanceof Tree.SegmentOp
&& isCeylonInteger(((Tree.SegmentOp)op.getRightTerm()).getLeftTerm().getTypeModel())
&& isCeylonInteger(((Tree.SegmentOp)op.getRightTerm()).getRightTerm().getTypeModel())) {
// x in y:z with x, y, z all Integer
return makeOptimizedInIntegerOrCharacterMeasure(op, syms().ceylonIntegerType, syms().longType);
}
} else if (isCeylonCharacter(op.getLeftTerm().getTypeModel())) {
if (op.getRightTerm() instanceof Tree.RangeOp
&& isCeylonCharacter(((Tree.RangeOp)op.getRightTerm()).getLeftTerm().getTypeModel())
&& isCeylonCharacter(((Tree.RangeOp)op.getRightTerm()).getRightTerm().getTypeModel())) {
// x in y..z with x, y, z all Character
return makeOptimizedInCharacterRange(op);
} else if (op.getRightTerm() instanceof Tree.SegmentOp
&& isCeylonCharacter(((Tree.SegmentOp)op.getRightTerm()).getLeftTerm().getTypeModel())
&& isCeylonInteger(((Tree.SegmentOp)op.getRightTerm()).getRightTerm().getTypeModel())) {
// x in y:z with x, y both Character, z all Integer
return makeOptimizedInIntegerOrCharacterMeasure(op, syms().ceylonCharacterType, syms().intType);
}
}
JCExpression left = transformExpression(op.getLeftTerm(), BoxingStrategy.BOXED, typeFact().getObjectType());
JCExpression right = transformExpression(op.getRightTerm(), BoxingStrategy.BOXED, op.getRightTerm().getTypeModel()
.getSupertype(typeFact().getCategoryDeclaration()));
Naming.SyntheticName varName = naming.temp();
JCExpression varIdent = varName.makeIdent();
JCExpression contains = at(op).Apply(null, makeSelect(right, "contains"), List. of(varIdent));
JCExpression typeExpr = makeJavaType(op.getLeftTerm().getTypeModel(), JT_NO_PRIMITIVES);
return makeLetExpr(varName, null, typeExpr, left, contains);
}
protected JCTree makeOptimizedInIntegerOrCharacterMeasure(Tree.InOp op, com.redhat.ceylon.langtools.tools.javac.code.Type ceylonType, com.redhat.ceylon.langtools.tools.javac.code.Type javaType) {
Tree.SegmentOp rangeOp = (Tree.SegmentOp)op.getRightTerm();
SyntheticName xName = naming.temp("x");
SyntheticName yName = naming.temp("y");
SyntheticName zName = naming.temp("z");
SyntheticName wName = naming.temp("w");
JCExpression x = transformExpression(op.getLeftTerm(), BoxingStrategy.UNBOXED, typeFact().getObjectType());
JCExpression y = transformExpression(rangeOp.getLeftTerm(), BoxingStrategy.UNBOXED, rangeOp.getLeftTerm().getTypeModel());
JCExpression z = transformExpression(rangeOp.getRightTerm(), BoxingStrategy.UNBOXED, rangeOp.getRightTerm().getTypeModel());
JCExpression w = make().Apply(null,
naming.makeSelect(make().QualIdent(ceylonType.tsym), "offset"),
List.of(xName.makeIdent(),
yName.makeIdent()));
return make().LetExpr(List.of(
makeVar(xName, make().Type(javaType), x),
makeVar(yName, make().Type(javaType), y),
makeVar(zName, make().Type(syms().longType), z),
makeVar(wName, make().Type(syms().longType), w)),
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.GT, zName.makeIdent(), make().Literal(0L)),
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.LE, make().Literal(0L), wName.makeIdent()),
make().Binary(JCTree.Tag.LT, wName.makeIdent(), zName.makeIdent()))
));
}
protected JCTree makeOptimizedInIntegerRange(Tree.InOp op, com.redhat.ceylon.langtools.tools.javac.code.Type type) {
// x in y..z with x, y, z all Integer
com.redhat.ceylon.langtools.tools.javac.code.Type ceylonType = syms().ceylonIntegerType;
Tree.RangeOp rangeOp = (Tree.RangeOp)op.getRightTerm();
JCExpression x = transformExpression(op.getLeftTerm(), BoxingStrategy.UNBOXED, typeFact().getObjectType());
JCExpression first = transformExpression(rangeOp.getLeftTerm(), BoxingStrategy.UNBOXED, rangeOp.getLeftTerm().getTypeModel());
JCExpression last = transformExpression(rangeOp.getRightTerm(), BoxingStrategy.UNBOXED, rangeOp.getRightTerm().getTypeModel());
SyntheticName xName = naming.temp("x");
SyntheticName firstName = naming.temp("y");
SyntheticName lastName = naming.temp("z");
SyntheticName recursiveName = naming.temp("recursive");
return make().LetExpr(List.of(
makeVar(xName, make().Type(type), x),
makeVar(firstName, make().Type(type), first),
makeVar(lastName, make().Type(type), last),
makeVar(recursiveName, make().Type(syms().booleanType), make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.GT,
firstName.makeIdent(),
make().Binary(JCTree.Tag.PLUS, firstName.makeIdent(), make().Literal(1L))),
make().Binary(JCTree.Tag.GT,
make().Binary(JCTree.Tag.MINUS, lastName.makeIdent(), make().Literal(1L)),
lastName.makeIdent())))),
make().Conditional(recursiveName.makeIdent(),
// x.offset(first) <= last.offset(first)
make().Binary(JCTree.Tag.LE,
make().Apply(null,
naming.makeSelect(make().QualIdent(ceylonType.tsym), "offset"),
List.of(
xName.makeIdent(),
firstName.makeIdent())),
make().Apply(null,
naming.makeSelect(make().QualIdent(ceylonType.tsym), "offset"),
List.of(
lastName.makeIdent(),
firstName.makeIdent()))),
make().Binary(JCTree.Tag.OR,
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.LE, firstName.makeIdent(), xName.makeIdent()),
make().Binary(JCTree.Tag.LE, xName.makeIdent(), lastName.makeIdent())),
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.LE, lastName.makeIdent(), xName.makeIdent()),
make().Binary(JCTree.Tag.LE, xName.makeIdent(), firstName.makeIdent())
))));
}
protected JCTree makeOptimizedInCharacterRange(Tree.InOp op) {
com.redhat.ceylon.langtools.tools.javac.code.Type type = syms().intType;
com.redhat.ceylon.langtools.tools.javac.code.Type ceylonType = syms().ceylonCharacterType;
// x in y..z with x, y, z all Character
Tree.RangeOp rangeOp = (Tree.RangeOp)op.getRightTerm();
JCExpression x = transformExpression(op.getLeftTerm(), BoxingStrategy.UNBOXED, typeFact().getObjectType());
JCExpression first = transformExpression(rangeOp.getLeftTerm(), BoxingStrategy.UNBOXED, rangeOp.getLeftTerm().getTypeModel());
JCExpression last = transformExpression(rangeOp.getRightTerm(), BoxingStrategy.UNBOXED, rangeOp.getRightTerm().getTypeModel());
SyntheticName xName = naming.temp("x");
SyntheticName firstName = naming.temp("first");
SyntheticName lastName = naming.temp("last");
SyntheticName recursiveName = naming.temp("recursive");
return make().LetExpr(List.of(
makeVar(xName, make().Type(type), x),
makeVar(firstName, make().Type(type), first),
makeVar(lastName, make().Type(type), last),
// annoyingly then a Span evaluates `contains` (= `containsElement`)
// it first evaluates `recursive`,
// which uses `successor` and `predecessor` which throw on overflow
// so we have to replicate that **short-circuit** logic here
makeVar(recursiveName, make().Type(syms().booleanType), make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.GT,
firstName.makeIdent(),
make().Apply(null,
naming.makeSelect(make().QualIdent(ceylonType.tsym), "getSuccessor"),
List.of(firstName.makeIdent()))),
make().Binary(JCTree.Tag.GT,
make().Apply(null,
naming.makeSelect(make().QualIdent(ceylonType.tsym), "getPredecessor"),
List.of(lastName.makeIdent())),
lastName.makeIdent())))),
make().Conditional(make().Binary(JCTree.Tag.LT, firstName.makeIdent(), lastName.makeIdent()),
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.LE,
xName.makeIdent(),
lastName.makeIdent()),
make().Binary(JCTree.Tag.GE,
xName.makeIdent(),
firstName.makeIdent())),
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.GE,
xName.makeIdent(),
lastName.makeIdent()),
make().Binary(JCTree.Tag.LE,
xName.makeIdent(),
firstName.makeIdent()))
));
}
// Logical operators
public JCExpression transform(Tree.LogicalOp op) {
OperatorTranslation operator = Operators.getOperator(op.getClass());
if(operator == null){
return makeErroneous(op, "compiler bug: " + op.getNodeType() + " is not a supported logical operator");
}
// Both terms are Booleans and can't be erased to anything
JCExpression left = transformExpression(op.getLeftTerm(), BoxingStrategy.UNBOXED, null);
return transformLogicalOp(op, operator, left, op.getRightTerm());
}
private JCExpression transformLogicalOp(Node op, OperatorTranslation operator,
JCExpression left, Tree.Term rightTerm) {
// Both terms are Booleans and can't be erased to anything
JCExpression right = transformExpression(rightTerm, BoxingStrategy.UNBOXED, null);
return at(op).Binary(operator.javacOperator, left, right);
}
// Comparison operators
public JCExpression transform(Tree.IdenticalOp op){
// The only thing which might be unboxed is boolean, and we can follow the rules of == for optimising it,
// which are simple and require that both types be booleans to be unboxed, otherwise they must be boxed
OptimisationStrategy optimisationStrategy = OperatorTranslation.BINARY_EQUAL.getBinOpOptimisationStrategy(op, op.getLeftTerm(), op.getRightTerm(), this);
JCExpression left = transformExpression(op.getLeftTerm(), optimisationStrategy.getBoxingStrategy(), null);
JCExpression right = transformExpression(op.getRightTerm(), optimisationStrategy.getBoxingStrategy(), null);
return at(op).Binary(JCTree.Tag.EQ, left, right);
}
public JCExpression transform(Tree.ComparisonOp op) {
return transformOverridableBinaryOperator(op, op.getUnit().getComparableDeclaration()).build();
}
public JCExpression transform(Tree.CompareOp op) {
return transformOverridableBinaryOperator(op, op.getUnit().getComparableDeclaration()).build();
}
public JCExpression transform(Tree.WithinOp op) {
WithinTransformation within = new WithinTransformation(op);
SyntheticName middleName = naming.alias("middle");
List vars = List.of(makeVar(middleName,
within.makeMiddleType(),
within.makeMiddle()));
within.setLeft(within.makeLhs());
within.setMiddleName(middleName);
within.setRight(within.makeRhs());
JCExpression andExpr = within.build();
return make().LetExpr(vars, andExpr);
}
class WithinTransformation {
private Tree.WithinOp op;
private SyntheticName middleName;
private JCExpression left;
private JCExpression right;
private Term middleTerm;
private Tree.Bound lowerBound;
private Tree.Bound upperBound;
private OptimisationStrategy opt;
private Type middleType;
private Type lowerType;
private Type upperType;
public WithinTransformation(Tree.WithinOp op) {
this.op = op;
this.middleTerm = op.getTerm();
this.lowerBound = op.getLowerBound();// < or <=
this.upperBound = op.getUpperBound();// < or <=
this.middleType = getComparableType(middleTerm);
this.lowerType = getComparableType(lowerBound.getTerm());
this.upperType = getComparableType(upperBound.getTerm());
this.opt = getWithinOptimization(middleTerm, middleType,
lowerBound, lowerType,
upperBound, upperType);
}
public JCExpression makeLhs() {
return transformExpression(lowerBound.getTerm(), opt.getBoxingStrategy(), null);
}
public JCExpression makeLhsType() {
return makeJavaType(lowerType, opt.getBoxingStrategy() == BoxingStrategy.UNBOXED ? 0 : JT_NO_PRIMITIVES);
}
public BoxingStrategy getLhsTypeBoxed() {
return opt.getBoxingStrategy();
}
public JCExpression makeRhs() {
return transformExpression(upperBound.getTerm(), opt.getBoxingStrategy(), null);
}
public JCExpression makeRhsType() {
return makeJavaType(upperType, opt.getBoxingStrategy() == BoxingStrategy.UNBOXED ? 0 : JT_NO_PRIMITIVES);
}
public BoxingStrategy getRhsTypeBoxed() {
return opt.getBoxingStrategy();
}
public JCExpression makeMiddleType() {
return makeJavaType(middleType, opt.getBoxingStrategy() == BoxingStrategy.UNBOXED ? 0 : JT_NO_PRIMITIVES);
}
public JCExpression makeMiddle() {
return transformExpression(middleTerm, opt.getBoxingStrategy(), middleType);
}
public BoxingStrategy getMiddleBoxed() {
return opt.getBoxingStrategy();
}
public SyntheticName getMiddleName() {
return middleName;
}
public Type getLowerType() {
return lowerType;
}
public Type getMiddleType() {
return middleType;
}
public Type getUpperType() {
return upperType;
}
public void setMiddleName(SyntheticName middleName) {
this.middleName = middleName;
}
public JCExpression getLeft() {
return left;
}
public void setLeft(JCExpression left) {
this.left = left;
}
public JCExpression getRight() {
return right;
}
public void setRight(JCExpression right) {
this.right = right;
}
protected OperatorTranslation getOperatorTranslation(Tree.Bound lowerBound) {
return Operators.getOperator(lowerBound instanceof Tree.OpenBound ? Tree.SmallerOp.class : Tree.SmallAsOp.class);
}
private JCExpression transformWithin(Tree.WithinOp op,
OptimisationStrategy opt,
Tree.Term middleTerm,
SyntheticName middleName,
Type middleType,
JCExpression left,
Tree.Bound lowerBound,
Type lowerType,
JCExpression right,
Tree.Bound upperBound,
Type upperType) {
JCExpression lower = transformBound(lowerBound, middleName.makeIdent(),
middleType, left, lowerType, getOperatorTranslation(lowerBound), opt, middleTerm, false);
JCExpression upper = transformBound(upperBound, middleName.makeIdent(),
middleType, right, upperType, getOperatorTranslation(upperBound), opt, middleTerm, true);
at(op);
OperatorTranslation andOp = Operators.getOperator(Tree.AndOp.class);
OptimisationStrategy optimisationStrategy = OptimisationStrategy.OPTIMISE;
JCExpression andExpr = transformOverridableBinaryOperator(op, andOp,
optimisationStrategy, lower, upper, null, null, op.getTypeModel()).build();
return andExpr;
}
protected OptimisationStrategy getWithinOptimization(Tree.Term middleTerm, Type middleType,
Tree.Bound lowerBound, Type lowerType, Tree.Bound upperBound,
Type upperType) {
// If any of the terms is optimizable, then use optimized
Tree.Term lowerTerm = lowerBound.getTerm();
OperatorTranslation lowerOp = getOperatorTranslation(lowerBound);
Tree.Term upperTerm = upperBound.getTerm();
OperatorTranslation upperOp = getOperatorTranslation(upperBound);
OptimisationStrategy opt;
boolean optimizeLower = lowerOp.isTermOptimisable(lowerTerm, lowerType, ExpressionTransformer.this) == OptimisationStrategy.OPTIMISE
|| lowerOp.isTermOptimisable(middleTerm, middleType, ExpressionTransformer.this) == OptimisationStrategy.OPTIMISE;
boolean optimizeUpper = upperOp.isTermOptimisable(middleTerm, middleType, ExpressionTransformer.this) == OptimisationStrategy.OPTIMISE
|| upperOp.isTermOptimisable(upperTerm, upperType, ExpressionTransformer.this) == OptimisationStrategy.OPTIMISE;
if ((lowerType.isExactly(middleType)
&& middleType.isExactly(upperType)
&& (optimizeLower
||optimizeUpper))// if all same type and any optimizable
|| (optimizeLower // otherwise onle if all optimizable
&& optimizeUpper)) {
opt = OptimisationStrategy.OPTIMISE;
middleType.setUnderlyingType(middleTerm.getTypeModel().getUnderlyingType());
} else {
opt = OptimisationStrategy.NONE;
}
return opt;
}
private Type getComparableType(Tree.Term middleTerm) {
final Type middleSuper = getSupertype(middleTerm, typeFact().getComparableDeclaration());
Type middleType = middleSuper;
Type middleSelf = middleType.getDeclaration().getSelfType();
if (middleSelf != null) {
// Simplify Comparable to X
middleType = middleType.getTypeArguments().get(middleSelf.getDeclaration());
}
return middleType;
}
private JCExpression transformBound(Tree.Bound bound,
JCExpression endpoint1, Type middleType, JCExpression endpoint2, Type otherType,
final OperatorTranslation operator, final OptimisationStrategy optimisationStrategy,
Tree.Term middleTerm, boolean isUpper) {
final JCExpression left;
final JCExpression right;
Type leftType;
if (isUpper) {
left = endpoint1;
leftType = middleType;
right = endpoint2;
} else {
left = endpoint2;
leftType = otherType;
right = endpoint1;
}
at(bound);
return transformOverridableBinaryOperator(middleTerm, operator,
optimisationStrategy, left, right, null, leftType, null, bound.getUnit().getBooleanType()).build();
}
public JCExpression build() {
JCExpression andExpr = transformWithin(op, opt,
middleTerm, middleName, middleType,
left, lowerBound, lowerType,
right, upperBound, upperType);
return andExpr;
}
}
public JCExpression transform(Tree.ScaleOp op) {
OperatorTranslation operator = Operators.getOperator(Tree.ScaleOp.class);
Tree.Term scalableTerm = op.getRightTerm();
Type scalableTermType = getSupertype(scalableTerm, typeFact().getScalableDeclaration());
SyntheticName scaleableName = naming.alias("scalable");
JCVariableDecl scaleable = makeVar(scaleableName,
makeJavaType(scalableTermType, JT_NO_PRIMITIVES),
transformExpression(scalableTerm, BoxingStrategy.BOXED, scalableTermType));
Tree.Term scaleTerm = op.getLeftTerm();
SyntheticName scaleName = naming.alias("scale");
Type scaleType = getTypeArgument(scalableTermType, 0);
JCExpression scaleValue;
if (isCeylonInteger(scaleTerm.getTypeModel())
&& isCeylonFloat(scaleType)) {
// Disgusting coercion
scaleValue = transformExpression(scaleTerm, BoxingStrategy.UNBOXED, scalableTerm.getTypeModel());
scaleValue = boxType(scaleValue, typeFact().getFloatType());
} else {
scaleValue = transformExpression(scaleTerm, BoxingStrategy.BOXED, scaleType);
}
JCVariableDecl scale = makeVar(scaleName,
makeJavaType(scaleType, JT_NO_PRIMITIVES),
scaleValue);
at(op);
return make().LetExpr(List.of(scale, scaleable),
transformOverridableBinaryOperator(op, operator, OptimisationStrategy.NONE, scaleableName.makeIdent(), scaleName.makeIdent(), null, null, op.getTypeModel()).build());
}
// Arithmetic operators
public JCExpression transform(Tree.ArithmeticOp op) {
return transformOverridableBinaryOperator(op, op.getUnit().getNumericDeclaration()).build();
}
public JCExpression transform(Tree.SumOp op) {
return transformOverridableBinaryOperator(op, op.getUnit().getSummableDeclaration()).build();
}
public JCExpression transform(Tree.DifferenceOp op) {
return transformOverridableBinaryOperator(op, op.getUnit().getInvertableDeclaration()).build();
}
public JCExpression transform(Tree.RemainderOp op) {
return transformOverridableBinaryOperator(op, op.getUnit().getIntegralDeclaration()).build();
}
public JCExpression transform(Tree.PowerOp op) {
if (Strategy.inlinePowerAsMultiplication(op)) {
try {
Number power = getIntegerLiteralPower(op);
if(power != null)
return transformOptimizedIntegerPower(op.getLeftTerm(), power);
} catch (ErroneousException e) {
// fall through and let the default transformation handle this
}
}
return transformOverridableBinaryOperator(op, op.getUnit().getExponentiableDeclaration(), 1).build();
}
/**
* Returns the literal value of the power in the given power expression,
* or null if the power is not an integer literal (or negation of an
* integer literal)
* @throws ErroneousException
*/
static java.lang.Number getIntegerLiteralPower(Tree.PowerOp op)
throws ErroneousException {
java.lang.Number power;
Tree.Term term = unwrapExpressionUntilTerm(op.getRightTerm());
if (term instanceof Tree.NaturalLiteral) {
power = literalValue((Tree.NaturalLiteral)term);
} else if (term instanceof Tree.NegativeOp &&
((Tree.NegativeOp)term).getTerm() instanceof Tree.NaturalLiteral) {
power = literalValue((Tree.NegativeOp)term);
} else {
power = null;
}
return power;
}
private JCExpression transformOptimizedIntegerPower(Tree.Term base,
Number power_) {
JCExpression baseExpr = transformExpression(base, BoxingStrategy.UNBOXED, base.getTypeModel());
long power = power_.longValue();
if (power == 1) {
return baseExpr;
}
SyntheticName baseAlias = naming.alias("base");
JCExpression multiplications = baseAlias.makeIdent();
while (power > 1) {
power--;
multiplications = make().Binary(JCTree.Tag.MUL, multiplications, baseAlias.makeIdent());
}
return make().LetExpr(makeVar(baseAlias,
makeJavaType(base.getTypeModel()),
baseExpr),
multiplications);
}
public JCExpression transform(Tree.BitwiseOp op) {
Type leftType = getSupertype(op.getLeftTerm(), typeFact().getSetDeclaration());
Type rightType = getSupertype(op.getRightTerm(), typeFact().getSetDeclaration());
return transformOverridableBinaryOperator(op, leftType, rightType).build();
}
// Overridable binary operators
BinOpTransformation transformOverridableBinaryOperator(Tree.BinaryOperatorExpression op, Interface compoundType) {
return transformOverridableBinaryOperator(op, compoundType, 0);
}
private BinOpTransformation transformOverridableBinaryOperator(Tree.BinaryOperatorExpression op, Interface compoundType, int typeArgumentToUse) {
Type leftType;
Type rightType;
// we do have a special case which is when the LHS is Float and RHS is Integer and the typechecker allowed coercion
if(getSupertype(op.getLeftTerm(), typeFact().getFloatDeclaration()) != null
&& getSupertype(op.getRightTerm(), typeFact().getIntegerDeclaration()) != null){
// Also keep the LHS type as Float since it's final and the special methods wouldn't be found in any supertype of compountType
leftType = typeFact().getFloatType();
// keep the RHS type then, since floats are not integers, the whole thing is resolved in the Java impl of Float with
// special hidden operator methods
rightType = typeFact().getIntegerType();
} else {
final Type leftSuper = getSupertype(op.getLeftTerm(), compoundType);
leftType = leftSuper;
Type leftSelf = leftType.getDeclaration().getSelfType();
if (leftSelf != null
&& leftType.getTypeArguments().get(leftSelf.getDeclaration()).isSubtypeOf(leftSuper)) {
// Simplify Comparable to X
leftType = leftType.getTypeArguments().get(leftSelf.getDeclaration());
}
// the right type always only depends on the LHS so let's not try to find it on the right side because it may
// be undecidable: https://github.com/ceylon/ceylon-compiler/issues/1535
rightType = getTypeArgument(leftSuper, typeArgumentToUse);
// Another special case with coercion
if (leftType.isInteger() &&
rightType.isInteger() &&
op.getRightTerm().getTypeModel().isFloat()) {
rightType = op.getRightTerm().getTypeModel();
}
}
return transformOverridableBinaryOperator(op, leftType, rightType);
}
BinOpTransformation transformOverridableBinaryOperator(Tree.BinaryOperatorExpression op, Type leftType, Type rightType) {
OperatorTranslation operator = Operators.getOperator(op.getClass());
return transformOverridableBinaryOperator(op, operator, leftType, rightType);
}
protected BinOpTransformation transformOverridableBinaryOperator(Tree.BinaryOperatorExpression op,
OperatorTranslation operator, Type leftType, Type rightType) {
OptimisationStrategy optimisationStrategy = operator.getBinOpOptimisationStrategy(op,
op.getLeftTerm(), leftType, op.getRightTerm(), rightType, this);
at(op);
JCExpression left = transformExpression(op.getLeftTerm(), optimisationStrategy.getBoxingStrategy(), leftType, EXPR_WIDEN_PRIM);
JCExpression right = transformExpression(op.getRightTerm(), optimisationStrategy.getBoxingStrategy(), rightType, EXPR_WIDEN_PRIM);
return transformOverridableBinaryOperator(op, operator, optimisationStrategy, left, right, op.getLeftTerm(), op.getRightTerm(), op.getTypeModel());
}
private JCExpression transformOverridableBinaryOperator(Tree.Term opExpr,
Tree.Term leftTerm, Tree.Term rightTerm, Type rightType,
OperatorTranslation operator, OptimisationStrategy optimisationStrategy,
JCExpression left, Type expectedType) {
JCExpression right = transformExpression(rightTerm, optimisationStrategy.getBoxingStrategy(), rightType, EXPR_WIDEN_PRIM);
return transformOverridableBinaryOperator(opExpr, operator, optimisationStrategy, left, right, leftTerm, rightTerm, expectedType).build();
}
private BinOpTransformation transformOverridableBinaryOperator(
Tree.Term opExpr,
OperatorTranslation originalOperator,
OptimisationStrategy optimisationStrategy,
JCExpression left, JCExpression right,
Tree.Term leftTerm, Tree.Term rightTerm, Type expectedType) {
return transformOverridableBinaryOperator(opExpr, originalOperator, optimisationStrategy, left, right, leftTerm, leftTerm != null ? leftTerm.getTypeModel() : null, rightTerm, expectedType);
}
class BinOpTransformation {
private Type expectedType;
private Tree.Term opExpr;
private OperatorTranslation operator;
private OptimisationStrategy optimisationStrategy;
private Tree.Term leftTerm;
private JCExpression left;
private Type leftType;
private Tree.Term rightTerm;
private JCExpression right;
//private Type rightType;
public Tree.Term getOpExpr() {
return opExpr;
}
public Type getExpectedType() {
return expectedType;
}
public void setExpectedType(Type expectedType) {
this.expectedType = expectedType;
}
public void setOpExpr(Tree.Term opExpr) {
this.opExpr = opExpr;
}
public OperatorTranslation getOperator() {
return operator;
}
public void setOperator(OperatorTranslation operator) {
this.operator = operator;
}
public OptimisationStrategy getOptimisationStrategy() {
return optimisationStrategy;
}
public void setOptimisationStrategy(OptimisationStrategy optimisationStrategy) {
this.optimisationStrategy = optimisationStrategy;
}
public Tree.Term getLeftTerm() {
return leftTerm;
}
public void setLeftTerm(Tree.Term leftTerm) {
this.leftTerm = leftTerm;
}
public JCExpression getLeft() {
return left;
}
public void setLeft(JCExpression left) {
this.left = left;
}
public Type getLeftType() {
return leftType;
}
public void setLeftType(Type leftType) {
this.leftType = leftType;
}
public Tree.Term getRightTerm() {
return rightTerm;
}
public void setRightTerm(Tree.Term rightTerm) {
this.rightTerm = rightTerm;
}
public JCExpression getRight() {
return right;
}
public void setRight(JCExpression right) {
this.right = right;
}
public Type getRightType() {
return getRightTerm().getTypeModel();
}
public JCExpression build() {
JCExpression result = null;
// optimise if we can
if(optimisationStrategy.useJavaOperator()){
result = make().Binary(operator.javacOperator, left, right);
if (rightTerm != null) {
result = unAutoPromote(result, expectedType, opExpr.getSmall());
}
return result;
}
List args = List.of(right);
List typeArgs = null;
// Set operators need reified generics
if(operator == OperatorTranslation.BINARY_UNION
|| operator == OperatorTranslation.BINARY_INTERSECTION
|| operator == OperatorTranslation.BINARY_COMPLEMENT){
Type otherSetElementType = typeFact().getIteratedType(rightTerm.getTypeModel());
args = args.prepend(makeReifiedTypeArgument(otherSetElementType));
typeArgs = List.of(makeJavaType(otherSetElementType, JT_TYPE_ARGUMENT));
}
if (optimisationStrategy.useValueTypeMethod()) {
int flags = JT_NO_PRIMITIVES;
if (optimisationStrategy == OptimisationStrategy.OPTIMISE_VALUE_TYPE
&& leftType.getDeclaration().getSelfType() != null) {
leftType = leftType.getTypeArguments().get(leftType.getDeclaration().getSelfType().getDeclaration());
}
result = at(opExpr).Apply(typeArgs, naming.makeQualIdent(makeJavaType(leftType, flags), operator.getCeylonValueTypeMethodName()), args.prepend(left));
} else {
if ((operator == OperatorTranslation.BINARY_COMPARE
|| operator == OperatorTranslation.BINARY_SMALL_AS
|| operator == OperatorTranslation.BINARY_LARGE_AS
|| operator == OperatorTranslation.BINARY_SMALLER
|| operator == OperatorTranslation.BINARY_LARGER)
&& willEraseToObject(leftType)) {
left = make().TypeCast(makeJavaType(typeFact().getComparableDeclaration().getType(), JT_RAW), left);
args = List.of(make().TypeCast(makeJavaType(typeFact().getComparableDeclaration().getType(), JT_RAW), right));
}
result = at(opExpr).Apply(typeArgs, makeSelect(left, operator.getCeylonMethodName()), args);
}
return result;
}
}
private BinOpTransformation transformOverridableBinaryOperator(
Tree.Term opExpr,
OperatorTranslation operator,
OptimisationStrategy optimisationStrategy,
JCExpression left, JCExpression right,
Tree.Term leftTerm, Type leftType, Tree.Term rightTerm, Type expectedType) {
BinOpTransformation binop = new BinOpTransformation();
binop.setExpectedType(expectedType);
binop.setLeft(left);
binop.setLeftTerm(leftTerm);
binop.setLeftType(leftType);
binop.setOperator(operator);
binop.setOpExpr(opExpr);
binop.setOptimisationStrategy(optimisationStrategy);
binop.setRight(right);
binop.setRightTerm(rightTerm);
return binop;
}
//
// Operator-Assignment expressions
public JCExpression transform(final Tree.ArithmeticAssignmentOp op){
final AssignmentOperatorTranslation operator = Operators.getAssignmentOperator(op.getClass());
if(operator == null){
return makeErroneous(op, "compiler bug: "+op.getNodeType() + " is not a supported arithmetic assignment operator");
}
// see if we can optimise it
if(op.getUnboxed() && CodegenUtil.isDirectAccessVariable(op.getLeftTerm())){
return optimiseAssignmentOperator(op, operator);
}
// we can use unboxed types if both operands are unboxed
final boolean boxResult = !op.getUnboxed();
// find the proper type
Interface compoundType = op.getUnit().getNumericDeclaration();
if(op instanceof Tree.AddAssignOp){
compoundType = op.getUnit().getSummableDeclaration();
}else if(op instanceof Tree.SubtractAssignOp){
compoundType = op.getUnit().getInvertableDeclaration();
}else if(op instanceof Tree.RemainderAssignOp){
compoundType = op.getUnit().getIntegralDeclaration();
}
final Type leftType = getSupertype(op.getLeftTerm(), compoundType);
// Normally we don't look at the RHS type because it can lead to unknown types, but if we want to extract its
// underlying type we have to, and we deal with any eventual unknown type. Presumably unknown types will not have
// any useful underlying type anyways.
// Note that looking at the RHS allows us to not have the issue of using the LHS type wrongly for the RHS type when
// the LHS type is Float and the RHS type is Integer with implicit Float coercion
Type rightSupertype = getSupertype(op.getRightTerm(), compoundType);
if (rightSupertype == null || rightSupertype.isUnknown()) {
// supertype could be null if, e.g. right type is Nothing
rightSupertype = leftType;
}
Type rightTypeArgument = getTypeArgument(rightSupertype);
if(rightTypeArgument == null || rightTypeArgument.isUnknown())
rightTypeArgument = getTypeArgument(leftType);
final Type rightType = getMostPreciseType(op.getLeftTerm(), rightTypeArgument);
final Type resultType = getLeastPreciseType(op.getLeftTerm(), op.getRightTerm());
// we work on boxed types
return transformAssignAndReturnOperation(op, op.getLeftTerm(), boxResult,
op.getLeftTerm().getTypeModel(), resultType,
new AssignAndReturnOperationFactory(){
@Override
public JCExpression getNewValue(JCExpression previousValue) {
// make this call: previousValue OP RHS
JCExpression ret = transformOverridableBinaryOperator(op, op.getLeftTerm(), op.getRightTerm(), rightType,
operator.binaryOperator,
boxResult ? OptimisationStrategy.NONE : OptimisationStrategy.OPTIMISE,
previousValue, op.getTypeModel());
return ret;
}
});
}
public JCExpression transform(final Tree.BitwiseAssignmentOp op){
final AssignmentOperatorTranslation operator = Operators.getAssignmentOperator(op.getClass());
if(operator == null){
return makeErroneous(op, "compiler bug: "+op.getNodeType() +" is not a supported bitwise assignment operator");
}
Type valueType = op.getLeftTerm().getTypeModel();
final Type rightType = getSupertype(op.getRightTerm(), typeFact().getSetDeclaration());
return transformAssignAndReturnOperation(op, op.getLeftTerm(), false, valueType, valueType, new AssignAndReturnOperationFactory() {
@Override
public JCExpression getNewValue(JCExpression previousValue) {
JCExpression result = transformOverridableBinaryOperator(op, op.getLeftTerm(), op.getRightTerm(), rightType, operator.binaryOperator, OptimisationStrategy.NONE, previousValue, op.getTypeModel());
return result;
}
});
}
public JCExpression transform(final Tree.LogicalAssignmentOp op){
final AssignmentOperatorTranslation operator = Operators.getAssignmentOperator(op.getClass());
if(operator == null){
return makeErroneous(op, "compiler bug: "+op.getNodeType() + " is not a supported logical assignment operator");
}
// optimise if we can
if(CodegenUtil.isDirectAccessVariable(op.getLeftTerm())){
return optimiseAssignmentOperator(op, operator);
}
Type valueType = op.getLeftTerm().getTypeModel();
// we work on unboxed types
return transformAssignAndReturnOperation(op, op.getLeftTerm(), false,
valueType, valueType, new AssignAndReturnOperationFactory(){
@Override
public JCExpression getNewValue(JCExpression previousValue) {
// make this call: previousValue OP RHS
return transformLogicalOp(op, operator.binaryOperator,
previousValue, op.getRightTerm());
}
});
}
private JCExpression optimiseAssignmentOperator(final Tree.AssignmentOp op, final AssignmentOperatorTranslation operator) {
// we don't care about their types since they're unboxed and we know it
JCExpression left = transformExpression(op.getLeftTerm(), BoxingStrategy.UNBOXED, null);
JCExpression right = transformExpression(op.getRightTerm(), BoxingStrategy.UNBOXED, null);
return at(op).Assignop(operator.javacOperator, left, right);
}
// Postfix operator
public JCExpression transform(Tree.PostfixOperatorExpression expr) {
OperatorTranslation operator = Operators.getOperator(expr.getClass());
if(operator == null){
return makeErroneous(expr, "compiler bug "+expr.getNodeType() + " is not yet supported");
}
OptimisationStrategy optimisationStrategy = operator.getUnOpOptimisationStrategy(expr, expr.getTerm(), this);
boolean canOptimise = optimisationStrategy.useJavaOperator();
// only fully optimise if we don't have to access the getter/setter
if(canOptimise && CodegenUtil.isDirectAccessVariable(expr.getTerm())){
JCExpression term = transformExpression(expr.getTerm(), BoxingStrategy.UNBOXED, expr.getTypeModel(), EXPR_WIDEN_PRIM);
return at(expr).Unary(operator.javacOperator, term);
}
Tree.Term term = unwrapExpressionUntilTerm(expr.getTerm());
Type returnType = term.getTypeModel();
List decls = List.nil();
List stats = List.nil();
JCExpression result = null;
// we can optimise that case a bit sometimes
boolean boxResult = !canOptimise;
// attr++
// (let $tmp = attr; attr = $tmp.getSuccessor(); $tmp;)
if(term instanceof Tree.BaseMemberExpression
// special case for java statics Foo.attr where Foo does not need to be evaluated
|| (term instanceof Tree.QualifiedMemberExpression
&& ((Tree.QualifiedMemberExpression)term).getStaticMethodReference())){
JCExpression getter;
if(term instanceof Tree.BaseMemberExpression)
getter = transform((Tree.BaseMemberExpression)term, null);
else
getter = transformMemberExpression((Tree.QualifiedMemberExpression)term, null, null);
at(expr);
// Type $tmp = attr
JCExpression exprType = makeJavaType(returnType, boxResult ? JT_NO_PRIMITIVES : 0);
Name varName = naming.tempName("op");
// make sure we box the results if necessary
getter = applyErasureAndBoxing(getter, term, boxResult ? BoxingStrategy.BOXED : BoxingStrategy.UNBOXED, returnType);
JCVariableDecl tmpVar = make().VarDef(make().Modifiers(0), varName, exprType, getter);
decls = decls.prepend(tmpVar);
// attr = $tmp.getSuccessor()
JCExpression successor;
if(canOptimise){
// use +1/-1 if we can optimise a bit
successor = make().Binary(operator == OperatorTranslation.UNARY_POSTFIX_INCREMENT ? JCTree.Tag.PLUS : JCTree.Tag.MINUS,
make().Ident(varName), makeInteger(1));
successor = unAutoPromote(successor, returnType, expr.getSmall());
}else{
successor = make().Apply(null,
makeSelect(make().Ident(varName), operator.getCeylonMethodName()),
List.nil());
// make sure the result is boxed if necessary, the result of successor/predecessor is always boxed
successor = boxUnboxIfNecessary(successor, true, term.getTypeModel(), CodegenUtil.getBoxingStrategy(term));
}
JCExpression assignment = makeAssignment(expr, term, transformAssignmentLhs(expr, term), successor);
stats = stats.prepend(at(expr).Exec(assignment));
// $tmp
result = make().Ident(varName);
}
else if(term instanceof Tree.QualifiedMemberExpression){
// e.attr++
// (let $tmpE = e, $tmpV = $tmpE.attr; $tmpE.attr = $tmpV.getSuccessor(); $tmpV;)
Tree.QualifiedMemberExpression qualified = (Tree.QualifiedMemberExpression) term;
boolean isSuper = isSuperOrSuperOf(qualified.getPrimary());
boolean isPackage = isPackageQualified(qualified);
// transform the primary, this will get us a boxed primary
JCExpression e = transformQualifiedMemberPrimary(qualified);
at(expr);
// Type $tmpE = e
JCExpression exprType = makeJavaType(qualified.getTarget().getQualifyingType(), JT_NO_PRIMITIVES);
Name varEName = naming.tempName("opE");
JCVariableDecl tmpEVar = make().VarDef(make().Modifiers(0), varEName, exprType, e);
// Type $tmpV = $tmpE.attr
JCExpression attrType = makeJavaType(returnType, boxResult ? JT_NO_PRIMITIVES : 0);
Name varVName = naming.tempName("opV");
JCExpression getter;
if (isSuper) {
getter = transformMemberExpression(qualified, transformSuper(qualified), null);
} else if (isPackage) {
getter = transformMemberExpression(qualified, null, null);
} else {
getter = transformMemberExpression(qualified, make().Ident(varEName), null);
}
// make sure we box the results if necessary
getter = applyErasureAndBoxing(getter, term, boxResult ? BoxingStrategy.BOXED : BoxingStrategy.UNBOXED, returnType);
JCVariableDecl tmpVVar = make().VarDef(make().Modifiers(0), varVName, attrType, getter);
decls = decls.prepend(tmpVVar);
if (!isSuper && !isPackage) {
// define all the variables
decls = decls.prepend(tmpEVar);
}
// $tmpE.attr = $tmpV.getSuccessor()
JCExpression successor;
if(canOptimise){
// use +1/-1 if we can optimise a bit
successor = make().Binary(operator == OperatorTranslation.UNARY_POSTFIX_INCREMENT ? JCTree.Tag.PLUS : JCTree.Tag.MINUS,
make().Ident(varVName), makeInteger(1));
successor = unAutoPromote(successor, returnType, expr.getSmall());
}else{
successor = make().Apply(null,
makeSelect(make().Ident(varVName), operator.getCeylonMethodName()),
List.nil());
// make sure the result is boxed if necessary, the result of successor/predecessor is always boxed
successor = boxUnboxIfNecessary(successor, true, term.getTypeModel(), CodegenUtil.getBoxingStrategy(term));
}
JCExpression assignment = makeAssignment(expr, term,
qualifyLhs(expr, term, isSuper ? transformSuper(qualified) : make().Ident(varEName)), successor);
stats = stats.prepend(at(expr).Exec(assignment));
// $tmpV
result = make().Ident(varVName);
}else{
return makeErroneous(term, "compiler bug: " + term.getNodeType() + " is not supported yet");
}
// e?.attr++ is probably not legal
// a[i]++ is not for M1 but will be:
// (let $tmpA = a, $tmpI = i, $tmpV = $tmpA.item($tmpI); $tmpA.setItem($tmpI, $tmpV.getSuccessor()); $tmpV;)
// a?[i]++ is probably not legal
// a[i1..i1]++ and a[i1...]++ are probably not legal
// a[].attr++ and a[].e.attr++ are probably not legal
return make().LetExpr(decls, stats, result);
}
// Prefix operator
public JCExpression transform(final Tree.PrefixOperatorExpression expr) {
final OperatorTranslation operator = Operators.getOperator(expr.getClass());
if(operator == null){
return makeErroneous(expr, "compiler bug: "+expr.getNodeType() + " is not supported yet");
}
OptimisationStrategy optimisationStrategy = operator.getUnOpOptimisationStrategy(expr, expr.getTerm(), this);
final boolean canOptimise = optimisationStrategy.useJavaOperator();
Tree.Term term = expr.getTerm();
// only fully optimise if we don't have to access the getter/setter
if(canOptimise && CodegenUtil.isDirectAccessVariable(term)){
JCExpression jcTerm = transformExpression(term, BoxingStrategy.UNBOXED, expr.getTypeModel(), EXPR_WIDEN_PRIM);
return at(expr).Unary(operator.javacOperator, jcTerm);
}
Interface compoundType = expr.getUnit().getOrdinalDeclaration();
Type valueType = getSupertype(term, compoundType);
final Type returnType = term.getTypeModel();//getMostPreciseType(term, getTypeArgument(valueType, 0));
// we work on boxed types unless we could have optimised
return transformAssignAndReturnOperation(expr, term, !canOptimise,
term.getTypeModel(), returnType, new AssignAndReturnOperationFactory(){
@Override
public JCExpression getNewValue(JCExpression previousValue) {
// use +1/-1 if we can optimise a bit
if(canOptimise){
JCExpression ret = make().Binary(operator == OperatorTranslation.UNARY_PREFIX_INCREMENT ? JCTree.Tag.PLUS : JCTree.Tag.MINUS,
previousValue, makeInteger(1));
ret = unAutoPromote(ret, returnType, expr.getSmall());
return ret;
}
// make this call: previousValue.getSuccessor() or previousValue.getPredecessor()
return make().Apply(null, makeSelect(previousValue, operator.getCeylonMethodName()), List.nil());
}
});
}
//
// Function to deal with expressions that have side-effects
private interface AssignAndReturnOperationFactory {
JCExpression getNewValue(JCExpression previousValue);
}
private JCExpression transformAssignAndReturnOperation(Node operator, Tree.Term term,
boolean boxResult, Type valueType, Type returnType,
AssignAndReturnOperationFactory factory){
List decls = List.nil();
List stats = List.nil();
JCExpression result = null;
// attr
// (let $tmp = OP(attr); attr = $tmp; $tmp)
if(term instanceof Tree.BaseMemberExpression
|| (term instanceof Tree.IndexExpression)
// special case for java statics Foo.attr where Foo does not need to be evaluated
|| (term instanceof Tree.QualifiedMemberExpression
&& ((Tree.QualifiedMemberExpression)term).getStaticMethodReference())){
JCExpression getter;
if(term instanceof Tree.BaseMemberExpression)
getter = transform((Tree.BaseMemberExpression)term, null);
else if (term instanceof Tree.IndexExpression)
getter = null;
else
getter = transformMemberExpression((Tree.QualifiedMemberExpression)term, null, null);
at(operator);
// Type $tmp = OP(attr);
JCExpression exprType = makeJavaType(returnType, boxResult ? JT_NO_PRIMITIVES : 0);
Name varName = naming.tempName("op");
// make sure we box the results if necessary
getter = applyErasureAndBoxing(getter, term, boxResult ? BoxingStrategy.BOXED : BoxingStrategy.UNBOXED, valueType);
JCExpression newValue = factory.getNewValue(getter);
// no need to box/unbox here since newValue and $tmpV share the same boxing type
JCVariableDecl tmpVar = make().VarDef(make().Modifiers(0), varName, exprType, newValue);
decls = decls.prepend(tmpVar);
// attr = $tmp
// make sure the result is unboxed if necessary, $tmp may be boxed
JCExpression value = make().Ident(varName);
BoxingStrategy boxingStrategy = CodegenUtil.getBoxingStrategy(term);
value = applyErasureAndBoxing(value, returnType, boxResult, boxingStrategy, valueType);
JCExpression assignment = makeAssignment(operator, term, transformAssignmentLhs(operator, term), value);
stats = stats.prepend(at(operator).Exec(assignment));
// $tmp
// return, with the box type we asked for
result = make().Ident(varName);
}
else if(term instanceof Tree.QualifiedMemberExpression){
// e.attr
// (let $tmpE = e, $tmpV = OP($tmpE.attr); $tmpE.attr = $tmpV; $tmpV;)
Tree.QualifiedMemberExpression qualified = (Tree.QualifiedMemberExpression) term;
boolean isSuper = isSuperOrSuperOf(qualified.getPrimary());
// transform the primary, this will get us a boxed primary
JCExpression e = transformQualifiedMemberPrimary(qualified);
at(operator);
// Type $tmpE = e
JCExpression exprType = makeJavaType(qualified.getTarget().getQualifyingType(), JT_NO_PRIMITIVES);
Name varEName = naming.tempName("opE");
JCVariableDecl tmpEVar = make().VarDef(make().Modifiers(0), varEName, exprType, e);
// Type $tmpV = OP($tmpE.attr)
JCExpression attrType = makeJavaType(returnType, boxResult ? JT_NO_PRIMITIVES : 0);
Name varVName = naming.tempName("opV");
JCExpression getter = transformMemberExpression(qualified, isSuper ? transformSuper(qualified) : make().Ident(varEName), null);
// make sure we box the results if necessary
getter = applyErasureAndBoxing(getter, term, boxResult ? BoxingStrategy.BOXED : BoxingStrategy.UNBOXED, valueType);
JCExpression newValue = factory.getNewValue(getter);
// no need to box/unbox here since newValue and $tmpV share the same boxing type
JCVariableDecl tmpVVar = make().VarDef(make().Modifiers(0), varVName, attrType, newValue);
// define all the variables
decls = decls.prepend(tmpVVar);
if (!isSuper) {
decls = decls.prepend(tmpEVar);
}
// $tmpE.attr = $tmpV
// make sure $tmpV is unboxed if necessary
JCExpression value = make().Ident(varVName);
BoxingStrategy boxingStrategy = CodegenUtil.getBoxingStrategy(term);
value = applyErasureAndBoxing(value, returnType, boxResult, boxingStrategy, valueType);
JCExpression assignment = makeAssignment(operator, term,
qualifyLhs(operator, term, isSuper ? transformSuper(qualified) : make().Ident(varEName)), value);
stats = stats.prepend(at(operator).Exec(assignment));
// $tmpV
// return, with the box type we asked for
result = make().Ident(varVName);
}else{
return makeErroneous(operator, "compiler bug: " + term.getNodeType() + " is not a supported assign and return operator");
}
// OP(e?.attr) is probably not legal
// OP(a[i]) is not for M1 but will be:
// (let $tmpA = a, $tmpI = i, $tmpV = OP($tmpA.item($tmpI)); $tmpA.setItem($tmpI, $tmpV); $tmpV;)
// OP(a?[i]) is probably not legal
// OP(a[i1..i1]) and OP(a[i1...]) are probably not legal
// OP(a[].attr) and OP(a[].e.attr) are probably not legal
return make().LetExpr(decls, stats, result);
}
public JCExpression transform(Tree.Parameter param) {
// Transform the expression marking that we're inside a defaulted parameter for $this-handling
//needDollarThis = true;
JCExpression expr;
at(param);
if (Strategy.hasDefaultParameterValueMethod(param.getParameterModel())) {
Tree.SpecifierOrInitializerExpression spec = Decl.getDefaultArgument(param);
Scope container = param.getParameterModel().getModel().getContainer();
boolean classParameter = container instanceof ClassOrInterface;
ClassOrInterface oldWithinDefaultParameterExpression = withinDefaultParameterExpression;
if(classParameter)
withinDefaultParameterExpression((ClassOrInterface) container);
if (param instanceof Tree.FunctionalParameterDeclaration) {
Tree.FunctionalParameterDeclaration fpTree = (Tree.FunctionalParameterDeclaration) param;
Tree.SpecifierExpression lazy = (Tree.SpecifierExpression)spec;
Function fp = (Function)fpTree.getParameterModel().getModel();
expr = CallableBuilder.anonymous(gen(), param, (Function)fpTree.getTypedDeclaration().getDeclarationModel(), lazy.getExpression(),
((Tree.MethodDeclaration)fpTree.getTypedDeclaration()).getParameterLists(),
getTypeForFunctionalParameter(fp),
true).build();
} else {
expr = transformExpression(spec.getExpression(),
CodegenUtil.getBoxingStrategy(param.getParameterModel().getModel()),
param.getParameterModel().getModel().getTypedReference().getFullType());
}
if(classParameter)
withinDefaultParameterExpression(oldWithinDefaultParameterExpression);
} else {
expr = makeErroneous(param, "compiler bug: no default parameter value method");
}
//needDollarThis = false;
return expr;
}
protected final JCExpression transformArg(SimpleInvocation invocation, int argIndex) {
final Tree.Term expr = invocation.getArgumentExpression(argIndex);
if (invocation.hasParameter(argIndex)) {
Type type = invocation.getParameterType(argIndex);
if (invocation.isParameterSequenced(argIndex)
// Java methods need their underlying type preserved
&& !invocation.isJavaVariadicMethod()) {
if (!invocation.isArgumentSpread(argIndex)) {
// If the parameter is sequenced and the argument is not ...
// then the expected type of the *argument* is the type arg to Iterator
type = typeFact().getIteratedType(type);
} else if (invocation.getArgumentType(argIndex).getSupertype(typeFact().getSequentialDeclaration())
== null) {
// On the other hand, if the parameter is sequenced and the argument is spread,
// but not sequential, then transformArguments() will use getSequence(),
// so we only need to expect an Iterable type
type = com.redhat.ceylon.model.typechecker.model.ModelUtil.appliedType(
typeFact().getIterableDeclaration(),
typeFact().getIteratedType(type), typeFact().getIteratedAbsentType(type));
}
}
BoxingStrategy boxingStrategy = invocation.getParameterBoxingStrategy(argIndex);
int flags = 0;
if(!invocation.isParameterRaw(argIndex))
flags |= ExpressionTransformer.EXPR_EXPECTED_TYPE_NOT_RAW;
if(invocation.isParameterWithConstrainedTypeParameters(argIndex))
flags |= ExpressionTransformer.EXPR_EXPECTED_TYPE_HAS_CONSTRAINED_TYPE_PARAMETERS;
if(invocation.isParameterWithDependentCovariantTypeParameters(argIndex))
flags |= ExpressionTransformer.EXPR_EXPECTED_TYPE_HAS_DEPENDENT_COVARIANT_TYPE_PARAMETERS;
if (invocation.erasedArgument(unwrapExpressionUntilTerm(expr))) {
flags |= EXPR_DOWN_CAST;
}
if (!expr.getSmall() && invocation.getParameterSmall(argIndex)) {
flags |= ExpressionTransformer.EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK;
}
boolean coerced = invocation.isParameterCoerced(argIndex);
if(coerced){
flags |= ExpressionTransformer.EXPR_IS_COERCED;
}
JCExpression ret = transformExpression(expr,
boxingStrategy,
type, flags);
// We can coerce most SAMs in transformExpression, EXCEPT invocation
// calls which we do here.
Term term = Decl.unwrapExpressionsUntilTerm(expr);
if (coerced
&& term instanceof Tree.InvocationExpression
&& isFunctionalResult(term.getTypeModel())
&& checkForFunctionalInterface(type) != null) {
return transformFunctionalInterfaceBridge((Tree.InvocationExpression)term, ret, type);
}
return ret;
} else {
// Overloaded methods don't have a reference to a parameter
// so we have to treat them differently. Also knowing it's
// overloaded we know we're dealing with Java code so we unbox
Type type = expr.getTypeModel();
return expressionGen().transformExpression(expr,
BoxingStrategy.UNBOXED,
type);
}
}
private final List transformArgumentList(Invocation invocation, TransformedInvocationPrimary transformedPrimary, CallBuilder callBuilder) {
return transformArguments(invocation, transformedPrimary, callBuilder);
}
private final List transformArguments(Invocation invocation,
TransformedInvocationPrimary transformedPrimary, CallBuilder callBuilder) {
ListBuffer result = new ListBuffer();
withinInvocation(false);
// Explicit arguments
if (invocation instanceof SuperInvocation) {
withinSuperInvocation(((SuperInvocation)invocation).getSub());
// for super calls, implicit arguments must be within a super invocation guard
appendImplicitArguments(invocation, transformedPrimary, result);
result.addAll(transformArgumentsForSimpleInvocation((SimpleInvocation)invocation, callBuilder));
withinSuperInvocation(null);
} else {
appendImplicitArguments(invocation, transformedPrimary, result);
if (invocation instanceof NamedArgumentInvocation) {
result.addAll(transformArgumentsForNamedInvocation((NamedArgumentInvocation)invocation));
} else if (invocation instanceof CallableSpecifierInvocation) {
result.addAll(transformArgumentsForCallableSpecifier((CallableSpecifierInvocation)invocation));
} else if (invocation instanceof SimpleInvocation) {
if(invocation.isUnknownArguments())
result.add(transformUnknownArguments((SimpleInvocation) invocation, callBuilder));
else
result.addAll(transformArgumentsForSimpleInvocation((SimpleInvocation)invocation, callBuilder));
} else {
throw BugException.unhandledCase(invocation);
}
}
withinInvocation(true);
return result.toList();
}
private void appendImplicitArguments(
Invocation invocation,
TransformedInvocationPrimary transformedPrimary,
ListBuffer result) {
// Implicit arguments
// except for Java array constructors
Declaration primaryDeclaration = invocation.getPrimaryDeclaration();
Tree.Term primary = invocation.getPrimary();
if(primaryDeclaration instanceof Value == false){
if(primaryDeclaration instanceof Class == false
|| !isJavaArray(((Class) primaryDeclaration).getType())){
invocation.addReifiedArguments(result);
}
}
if (!(primary instanceof Tree.BaseTypeExpression)
&& !(primary instanceof Tree.QualifiedTypeExpression)
&& (!(primary instanceof Tree.QualifiedMemberExpression) || !(((Tree.QualifiedMemberExpression)primary).getMemberOperator() instanceof Tree.SpreadOp))
&& Invocation.onValueType(this, primary, primaryDeclaration)
&& transformedPrimary != null) {
result.add(new ExpressionAndType(transformedPrimary.expr,
makeJavaType(primary.getTypeModel())));
}
}
private ExpressionAndType transformUnknownArguments(SimpleInvocation invocation, CallBuilder callBuilder){
// doesn't really matter, assume Object, it's not used
Type iteratedType = typeFact().getObjectType();
// the single spread argument which is allowed
JCExpression rest = null;
ListBuffer initial = new ListBuffer();
for (int ii = 0; ii < invocation.getNumArguments(); ii++) {
if (invocation.isArgumentSpread(ii)) {
rest = invocation.getTransformedArgumentExpression(ii);
} else {
initial.add(invocation.getTransformedArgumentExpression(ii));
}
}
JCExpression expr;
if (initial.isEmpty()) {
expr = make().TypeCast(makeJavaType(typeFact().getSequentialDeclaration().getType(), JT_RAW), rest);
} else {
expr = utilInvocation().sequentialInstance(null, makeReifiedTypeArgument(iteratedType),
rest != null ? rest : makeEmptyAsSequential(true), initial.toList());
}
JCExpression type = makeJavaType(typeFact().getSequenceType(iteratedType).getType());
return new ExpressionAndType(expr, type);
}
private List transformArgumentsForSimpleInvocation(SimpleInvocation invocation, CallBuilder callBuilder) {
final Constructor superConstructor = invocation.getConstructor();
CtorDelegation constructorDelegation;
if (invocation instanceof SuperInvocation) {
constructorDelegation = ((SuperInvocation)invocation).getDelegation();
} else {
constructorDelegation = null;
}
List result = List.nil();
if (!(invocation instanceof SuperInvocation)
|| !((SuperInvocation)invocation).isDelegationDelegation() ) {
int numArguments = invocation.getNumArguments();
if (invocation.getNumParameters() == 0) {
// skip transforming arguments
// (Usually, numArguments would already be null, but it's possible to call a
// parameterless function with a *[] argument - see #1593.)
numArguments = 0;
}
boolean wrapIntoArray = false;
ListBuffer arrayWrap = new ListBuffer();
for (int argIndex = 0; argIndex < numArguments; argIndex++) {
BoxingStrategy boxingStrategy = invocation.getParameterBoxingStrategy(argIndex);
Type parameterType = invocation.getParameterType(argIndex);
// for Java methods of variadic primitives, it's better to wrap them ourselves into an array
// to avoid ambiguity of foo(1,2) for foo(int...) and foo(Object...) methods
if(!wrapIntoArray
&& invocation.isParameterJavaVariadic(argIndex)
&& boxingStrategy == BoxingStrategy.UNBOXED
&& willEraseToPrimitive(typeFact().getDefiniteType(parameterType))
&& !invocation.isSpread())
wrapIntoArray = true;
ExpressionAndType exprAndType;
if (invocation.isArgumentSpread(argIndex)) {
if (!invocation.isParameterSequenced(argIndex)) {
result = transformSpreadTupleArgument(invocation, callBuilder,
result, argIndex);
break;
}
if(invocation.isJavaVariadicMethod()){
// if it's a java method we need a special wrapping
exprAndType = transformSpreadArgument(invocation,
numArguments, argIndex, boxingStrategy,
parameterType);
argIndex = numArguments;
}else{
Type argType = invocation.getArgumentType(argIndex);
if (argType.getSupertype(typeFact().getSequentialDeclaration()) != null) {
exprAndType = transformArgument(invocation, argIndex,
boxingStrategy);
} else if (argType.getSupertype(typeFact().getIterableDeclaration()) != null) {
exprAndType = transformArgument(invocation, argIndex,
boxingStrategy);
JCExpression sequential = iterableToSequential(exprAndType.expression);
if(invocation.isParameterVariadicPlus(argIndex)){
Type iteratedType = typeFact().getIteratedType(argType);
sequential = utilInvocation().castSequentialToSequence(sequential, iteratedType);
}
exprAndType = new ExpressionAndType(sequential, exprAndType.type);
} else if (typeFact().isJavaArrayType(argType)) {
exprAndType = transformArgument(invocation, argIndex,
boxingStrategy);
JCExpression iterable;
if (typeFact().isJavaPrimitiveArrayType(argType)) {
iterable = utilInvocation().toIterable(
exprAndType.expression);
} else {
Type elementType = typeFact().getJavaArrayElementType(argType);
iterable = utilInvocation().toIterable(
makeJavaType(elementType, JT_TYPE_ARGUMENT),
makeReifiedTypeArgument(elementType),
exprAndType.expression);
}
exprAndType = new ExpressionAndType(
make().Apply(null,
makeSelect(iterable, "sequence"),
List.nil()),
makeJavaType(argType));
} else if (typeFact().isJavaIterableType(argType)) {
exprAndType = transformArgument(invocation, argIndex,
boxingStrategy);
Type elementType = typeFact().getJavaIteratedType(argType);
JCExpression iterable = utilInvocation().toIterable(
makeJavaType(elementType, JT_TYPE_ARGUMENT),
makeReifiedTypeArgument(elementType),
exprAndType.expression);
exprAndType = new ExpressionAndType(
make().Apply(null,
makeSelect(iterable, "sequence"),
List.nil()),
makeJavaType(argType));
} else {
exprAndType = new ExpressionAndType(makeErroneous(invocation.getNode(), "compiler bug: unexpected spread argument"), makeErroneous(invocation.getNode(), "compiler bug: unexpected spread argument"));
}
}
} else if (!invocation.isParameterSequenced(argIndex)
// if it's sequenced, Java and there's no spread at all, pass it along
|| (invocation.isParameterJavaVariadic(argIndex) && !invocation.isSpread())) {
exprAndType = transformArgument(invocation, argIndex,
boxingStrategy);
// Callable has a variadic 1-param method that if you invoke it with a Java Object[] will confuse javac and give
// preference to the variadic method instead of the $call$(Object) one, so we force a cast to Object to resolve it
// This is not required for primitive arrays since they are not Object[]
if(numArguments == 1
&& invocation.isIndirect()){
Type argumentType = invocation.getArgumentType(0);
if(isJavaObjectArray(argumentType)
|| isNull(argumentType)){
exprAndType = new ExpressionAndType(make().TypeCast(makeJavaType(typeFact().getObjectType()), exprAndType.expression),
exprAndType.type);
}
}else if(invocation.isParameterJavaVariadic(argIndex) && !invocation.isSpread()){
// in fact, the very same problem happens when passing null or object arrays to a java variadic method
Type argumentType = invocation.getArgumentType(argIndex);
if(isJavaObjectArray(argumentType)
|| isNull(argumentType)){
// remove any ambiguity
exprAndType = new ExpressionAndType(make().TypeCast(makeJavaType(parameterType), exprAndType.expression),
exprAndType.type);
}
}
} else {
// we must have a sequenced param
if(invocation.isSpread()){
exprAndType = transformSpreadArgument(invocation,
numArguments, argIndex, boxingStrategy,
parameterType);
argIndex = numArguments;
}else{
exprAndType = transformVariadicArgument(invocation,
numArguments, argIndex, parameterType);
argIndex = numArguments;
}
}
if(!wrapIntoArray) {
if (argIndex== 0
&& invocation.isCallable()
&& !invocation.isArgumentSpread(numArguments-1)
) {
exprAndType = new ExpressionAndType(
make().TypeCast(make().Type(syms().objectType), exprAndType.expression),
make().Type(syms().objectType));
}
result = result.append(exprAndType);
} else {
arrayWrap.append(exprAndType.expression);
}
}
if (invocation.isIndirect()
&& invocation.isParameterSequenced(numArguments)
&& !invocation.isArgumentSpread(numArguments-1)
&& ((IndirectInvocation)invocation).getNumParameters() > numArguments) {
// Calling convention for indirect variadic invocation's requires
// explicit variadic argument (can't use the overloading trick)
result = result.append(new ExpressionAndType(makeEmptyAsSequential(true), make().Erroneous()));
}
if(wrapIntoArray){
// must have at least one arg, so take the last one
Type parameterType = invocation.getParameterType(numArguments-1);
JCExpression arrayType = makeJavaType(parameterType, JT_RAW);
JCNewArray arrayExpr = make().NewArray(arrayType, List.nil(), arrayWrap.toList());
JCExpression arrayTypeExpr = make().TypeArray(makeJavaType(parameterType, JT_RAW));
result = result.append(new ExpressionAndType(arrayExpr, arrayTypeExpr));
}
} else {
for (Parameter p : constructorDelegation.getConstructor().getParameterList().getParameters()) {
result = result.append(new ExpressionAndType(naming.makeName(p.getModel(), Naming.NA_IDENT | Naming.NA_ALIASED), null));
}
}
boolean concreteDelegation = invocation instanceof SuperInvocation
&& ((SuperInvocation)invocation).getDelegation().isConcreteSelfDelegation();
if (superConstructor == null &&
concreteDelegation) {
Constructor delegateTo = ((SuperInvocation)invocation).getDelegation().getConstructor();
result = result.prepend(
new ExpressionAndType(naming.makeNamedConstructorName(delegateTo, true),
naming.makeNamedConstructorType(delegateTo, true)));
} else if (
superConstructor != null
&& constructorDelegation != null
&& constructorDelegation.isSelfDelegation()) {
result = result.prepend(
new ExpressionAndType(naming.makeNamedConstructorName(superConstructor, concreteDelegation),
naming.makeNamedConstructorType(superConstructor, concreteDelegation)));
} else if (
superConstructor != null
&& !Decl.isDefaultConstructor(superConstructor)
&& !Decl.isJavaArrayWith(superConstructor)
&& (invocation.getQmePrimary() instanceof Tree.QualifiedTypeExpression == false
|| !isCeylonCallable(((Tree.QualifiedTypeExpression)invocation.getQmePrimary()).getPrimary().getTypeModel()))) {
result = result.prepend(
new ExpressionAndType(naming.makeNamedConstructorName(superConstructor, concreteDelegation),
naming.makeNamedConstructorType(superConstructor, concreteDelegation)));
}
return result;
}
private ExpressionAndType transformVariadicArgument(
SimpleInvocation invocation, int numArguments, int argIndex,
Type parameterType) {
ExpressionAndType exprAndType;
final Type iteratedType = typeFact().getIteratedType(parameterType);
final JCExpression expr;
final JCExpression type;
// invoking f(a, b, c), where declared f(A a, B* b)
// collect each remaining argument and box with an ArraySequence
List x = List.nil();
for (int ii = argIndex ; ii < numArguments; ii++) {
x = x.append(invocation.getTransformedArgumentExpression(ii));
}
expr = makeSequence(x, iteratedType, JT_TYPE_ARGUMENT);
type = makeJavaType(typeFact().getSequenceType(iteratedType).getType());
exprAndType = new ExpressionAndType(expr, type);
return exprAndType;
}
private ExpressionAndType transformSpreadArgument(
SimpleInvocation invocation, int numArguments, int argIndex,
BoxingStrategy boxingStrategy, Type parameterType) {
ExpressionAndType exprAndType;
final Type iteratedType = typeFact().getIteratedType(parameterType);
final JCExpression expr;
final JCExpression type;
// optimise "*javaArray.iterable" into "javaArray" for java variadic parameters, since we can pass them just along
if(invocation.isJavaVariadicMethod()
&& numArguments == argIndex+1
&& !invocation.isArgumentComprehension(argIndex)){
Expression argumentExpression = invocation.getArgumentExpression(argIndex);
Term argument = Decl.unwrapExpressionsUntilTerm(argumentExpression);
if (argument instanceof Tree.QualifiedMemberExpression) {
Tree.QualifiedMemberExpression qualifiedMemberArgument = (Tree.QualifiedMemberExpression)argument;
if ("iterable".equals(qualifiedMemberArgument.getIdentifier().getText())
&& isJavaArray(qualifiedMemberArgument.getPrimary().getTypeModel())) {
// just pass the array as-is
// we don't care at all about unboxing or casting since we can't be dealing with boxing
// and we generate our own cast, at least for non-primitive arrays where it may be ambiguous,
// we could avoid the cast for non-type-parameter and non-Object arrays, but that's more expensive
// to check for
JCExpression primary = transformExpression(qualifiedMemberArgument.getPrimary());
type = makeJavaType(typeFact().getSequenceType(iteratedType).getType());
if(isJavaObjectArray(qualifiedMemberArgument.getPrimary().getTypeModel())){
expr = make().TypeCast(makeJavaType(qualifiedMemberArgument.getPrimary().getTypeModel()), primary);
}else{
expr = primary;
}
return new ExpressionAndType(expr, type);
}
}
}
// invoking f(a, *b), where declared f(A a, B* b)
// we can have several remaining arguments and the last one is spread
List x = List.nil();
for (int ii = argIndex ; ii < numArguments; ii++) {
JCExpression argExpr = invocation.getTransformedArgumentExpression(ii);
// the last parameter is spread and must be put first
if(ii < numArguments - 1){
x = x.append(argExpr);
}else{
// convert to a Sequential if required
Type argType = invocation.getArgumentType(ii);
if (typeFact().isJavaArrayType(argType)) {
String methodName;
if (typeFact().getJavaIntArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.IntArray.getIterable";
} else if (typeFact().getJavaShortArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.ShortArray.getIterable";
} else if (typeFact().getJavaLongArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.LongArray.getIterable";
} else if (typeFact().getJavaByteArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.ByteArray.getIterable";
} else if (typeFact().getJavaBooleanArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.BooleanArray.getIterable";
} else if (typeFact().getJavaCharArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.CharArray.getIterable";
} else if (typeFact().getJavaFloatArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.FloatArray.getIterable";
} else if (typeFact().getJavaDoubleArrayDeclaration().equals(argType.getDeclaration())) {
methodName = "com.redhat.ceylon.compiler.java.language.DoubleArray.getIterable";
} else {
methodName = "com.redhat.ceylon.compiler.java.language.ObjectArray.getIterable";
}
argExpr = make().Apply(null, naming.makeQuotedQualIdentFromString(methodName), List.of(argExpr));
}
if(!typeFact().isSequentialType(argType))
argExpr = iterableToSequential(argExpr);
x = x.prepend(argExpr);
}
}
if(invocation.isJavaVariadicMethod()){
// collect all the initial arguments and wrap into a Java array
// first arg is the spread part
JCExpression last = x.head;
// remove it from x
x = x.tail;
Type lastType = invocation.getArgumentType(numArguments-1);
// must translate it into a Util call
expr = sequenceToJavaArray(invocation, last, parameterType, boxingStrategy, lastType, x);
}else{
JCExpression typeExpr = makeJavaType(iteratedType, JT_TYPE_ARGUMENT);
JCExpression sequentialExpr = utilInvocation().sequentialInstance(typeExpr, makeReifiedTypeArgument(iteratedType), x.head, x.tail);
if (invocation.isParameterVariadicPlus(argIndex)) {
expr = utilInvocation().castSequentialToSequence(sequentialExpr, iteratedType);
} else {
expr = sequentialExpr;
}
}
type = makeJavaType(typeFact().getSequenceType(iteratedType).getType());
exprAndType = new ExpressionAndType(expr, type);
return exprAndType;
}
private List transformSpreadTupleArgument(
SimpleInvocation invocation, CallBuilder callBuilder,
List result, final int argIndex) {
BoxingStrategy boxingStrategy;
// Spread tuple Argument
// invoking f(*args), where declared f(A a, B a) (last param not sequenced)
final Tree.Expression tupleArgument = invocation.getArgumentExpression(argIndex);
final int minimumTupleArguments = typeFact().getTupleMinimumLength(tupleArgument.getTypeModel());
final boolean tupleUnbounded = typeFact().isTupleLengthUnbounded(tupleArgument.getTypeModel());
final Type callableType = invocation.getPrimary().getTypeModel().getFullType();
// Only evaluate the tuple expr once
SyntheticName tupleAlias = naming.alias("tuple");
JCExpression tupleType;
JCExpression tupleExpr = transformExpression(tupleArgument, BoxingStrategy.BOXED, null);
tupleType = makeJavaType(typeFact().getSequentialDeclaration().getType(), JT_RAW);
if (typeFact().isIterableType(tupleArgument.getTypeModel())) {
tupleExpr = make().TypeCast(makeJavaType(typeFact().getSequentialDeclaration().getType(), JT_RAW), tupleExpr);
} else if (typeFact().isJavaIterableType(tupleArgument.getTypeModel())) {
// need to convert j.l.Iterable to a c.l.Iterable
Type iteratedType = typeFact().getJavaIteratedType(tupleArgument.getTypeModel());
tupleExpr = utilInvocation().toIterable(
makeJavaType(iteratedType, JT_TYPE_ARGUMENT),
makeReifiedTypeArgument(iteratedType), tupleExpr);
tupleExpr = make().Apply(null, makeSelect(tupleExpr, "sequence"), List.nil());
} else if (typeFact().isJavaArrayType(tupleArgument.getTypeModel())) {
Type iteratedType = typeFact().getJavaArrayElementType(tupleArgument.getTypeModel());
if (typeFact().isJavaObjectArrayType(tupleArgument.getTypeModel())) {
tupleExpr = utilInvocation().toIterable(
makeJavaType(iteratedType, JT_TYPE_ARGUMENT),
makeReifiedTypeArgument(iteratedType), tupleExpr);
} else {//primitive
tupleExpr = utilInvocation().toIterable(tupleExpr);
}
tupleExpr = make().Apply(null, makeSelect(tupleExpr, "sequence"), List.nil());
} else {
throw BugException.unhandledTypeCase(tupleArgument.getTypeModel());
}
callBuilder.appendStatement(makeVar(tupleAlias, tupleType, tupleExpr));
if (callBuilder.getArgumentHandling() == 0) {
// XXX Hack: Only do this if we're not already doing
// something funky with arguments e.g. SpreadOp
callBuilder.argumentHandling(CallBuilder.CB_LET, naming.alias("spreadarg"));
}
callBuilder.voidMethod(invocation.getReturnType() == null
|| Decl.isUnboxedVoid(invocation.getPrimaryDeclaration())
|| isWithinSuperInvocation());
/* Cases:
*[] -> () => nothing
*[] -> (Integer=) => nothing
*[] -> (Integer*) => nothing
*[Integer] -> (Integer) => extract
*[Integer] -> (Integer=) => extract
*[Integer] -> (Integer*) => pass the tuple as-is
*[Integer*] -> (Integer*) => pass the tuple as-is
*[Integer+] -> (Integer*) => pass the tuple as-is
*[Integer] -> (Integer, Integer*) => extract and drop the tuple
*[Integer,Integer] -> (Integer, Integer) => extract
*[Integer,Integer] -> (Integer=, Integer=) => extract
*[Integer,Integer] -> (Integer, Integer*) => extract and pass the tuple rest
*[Integer,Integer*] -> (Integer, Integer*) => extract and pass the tuple rest
*[Integer,Integer+] -> (Integer, Integer*) => extract and pass the tuple rest
*/
int spreadArgIndex = argIndex;
final int maxParameters = getNumParametersOfCallable(callableType);
boolean variadic = maxParameters > 0 && invocation.isParameterSequenced(maxParameters-1);
// we extract from the tuple not more than we have tuple members, but even less than that if we don't
// have enough parameters to put them in
final int argumentsToExtract = Math.min(argIndex + minimumTupleArguments, variadic ? maxParameters - 1 : maxParameters);
for (; spreadArgIndex < argumentsToExtract; spreadArgIndex++) {
boxingStrategy = invocation.getParameterBoxingStrategy(spreadArgIndex);
Type paramType = getParameterTypeOfCallable(callableType, spreadArgIndex);
JCExpression tupleIndex = boxType(make().Literal((long)spreadArgIndex-argIndex),
typeFact().getIntegerType());
JCExpression tupleElement = make().Apply(null,
naming.makeQualIdent(tupleAlias.makeIdent(), "get"),
List.of(tupleIndex));
tupleElement = applyErasureAndBoxing(tupleElement,
typeFact().getAnythingType(),
true, boxingStrategy, paramType);
JCExpression argType = makeJavaType(paramType, boxingStrategy == BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0);
result = result.append(new ExpressionAndType(tupleElement, argType));
}
// if we're variadic AND
// - the tuple is unbounded (which means we must have an unknown number of elements left to pass)
// - OR the tuple is bounded but we did not pass them all
if (variadic
&& (tupleUnbounded || argumentsToExtract < (minimumTupleArguments + argIndex))) {
boxingStrategy = invocation.getParameterBoxingStrategy(spreadArgIndex);
Type paramType = getParameterTypeOfCallable(callableType, spreadArgIndex);
JCExpression tupleElement = tupleAlias.makeIdent();
// argIndex = 1, tuple = [Integer], params = [Integer, Integer*], spreadArgIndex = 1 => no span
// argIndex = 0, tuple = [Integer+], params = [Integer, Integer*], spreadArgIndex = 1 => spanFrom(1)
if(spreadArgIndex - argIndex > 0){
JCExpression tupleIndex = boxType(make().Literal((long)spreadArgIndex-argIndex),
typeFact().getIntegerType());
tupleElement = make().Apply(null, naming.makeQualIdent(tupleElement, "spanFrom"),
List.of(tupleIndex));
}
tupleElement = applyErasureAndBoxing(tupleElement,
typeFact().getAnythingDeclaration().getType(),
true, boxingStrategy, paramType);
JCExpression argType = makeJavaType(paramType, boxingStrategy == BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0);
JCExpression expr;
if(invocation.isJavaVariadicMethod()){
// no need to handle leading arguments since that is handled by transformSpreadArgument
// if ever we have leading arguments we never end up in this method
expr = sequenceToJavaArray(invocation, tupleElement, paramType, boxingStrategy, paramType, List.nil());
}else{
expr = tupleElement;
}
result = result.append(new ExpressionAndType(expr, argType));
} else if (variadic
&& invocation.isIndirect()
&& argumentsToExtract >= minimumTupleArguments
&& !tupleUnbounded) {
result = result.append(new ExpressionAndType(makeEmptyAsSequential(true), makeJavaType(typeFact().getSequenceType(typeFact().getAnythingDeclaration().getType()), JT_RAW)));
} else if (!variadic
&& tupleUnbounded
&& !invocation.isIndirect()){
result = result.append(new ExpressionAndType(tupleAlias.makeIdent(), tupleType));
}
return result;
}
private ExpressionAndType transformArgument(SimpleInvocation invocation,
int argIndex, BoxingStrategy boxingStrategy) {
ExpressionAndType exprAndType;
JCExpression expr = invocation.getTransformedArgumentExpression(argIndex);
Type paramType = invocation.getParameterType(argIndex);
JCExpression type = makeJavaType(paramType, boxingStrategy == BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0);
Class ctedClass = Decl.getConstructedClass(invocation.getPrimaryDeclaration());
if (argIndex == 0
&& typeFact().isOptionalType(paramType)
&& invocation.getArgumentType(argIndex).isSubtypeOf(typeFact().getNullType())
&& ctedClass != null && (ctedClass.hasConstructors()|| ctedClass.isSerializable())) {
// we've invoking the default constructor, whose first parameter has optional type
// with a null argument: That will be ambiguous wrt any named constructors
// with otherwise identical signitures, so we need a typecast to
// disambiguate
expr = make().TypeCast(makeJavaType(paramType, boxingStrategy == BoxingStrategy.BOXED ? JT_NO_PRIMITIVES : 0), expr);
}
exprAndType = new ExpressionAndType(expr, type);
return exprAndType;
}
private List transformArgumentsForNamedInvocation(NamedArgumentInvocation invocation) {
List result = List.nil();
for (ExpressionAndType argAndType : invocation.getArgumentsAndTypes()) {
result = result.append(argAndType);
}
return result;
}
private List transformArgumentsForCallableSpecifier(CallableSpecifierInvocation invocation) {
List result = List.nil();
int argIndex = 0;
for(Parameter parameter : invocation.getMethod().getFirstParameterList().getParameters()) {
Type exprType = expressionGen().getTypeForParameter(parameter, null, this.TP_TO_BOUND);
Parameter declaredParameter = invocation.getMethod().getFirstParameterList().getParameters().get(argIndex);
JCExpression arg = naming.makeName(parameter.getModel(), Naming.NA_IDENT);
arg = expressionGen().applyErasureAndBoxing(
arg,
exprType,
!parameter.getModel().getUnboxed(),
BoxingStrategy.BOXED,// Callables always have boxed params
declaredParameter.getType());
result = result.append(new ExpressionAndType(arg, makeJavaType(declaredParameter.getType())));
argIndex++;
}
return result;
}
public final JCExpression transformInvocation(final Invocation invocation) {
boolean prevFnCall = withinInvocation(true);
try {
final CallBuilder callBuilder = CallBuilder.instance(this);
if (invocation.getPrimary() instanceof Tree.StaticMemberOrTypeExpression){
transformTypeArguments(callBuilder,
(Tree.StaticMemberOrTypeExpression)invocation.getPrimary());
}
if (invocation instanceof CallableSpecifierInvocation) {
return transformCallableSpecifierInvocation(callBuilder, (CallableSpecifierInvocation)invocation);
} else {
at(invocation.getNode());
Tree.Term primary = Decl.unwrapExpressionsUntilTerm(invocation.getPrimary());
JCExpression result = transformTermForInvocation(primary, new InvocationTermTransformer(invocation, callBuilder));
return result;
}
} finally {
withinInvocation(prevFnCall);
}
}
protected JCExpression transformPositionalInvocationOrInstantiation(Invocation invocation, CallBuilder callBuilder, TransformedInvocationPrimary transformedPrimary) {
JCExpression resultExpr;
if (invocation.isMemberRefInvocation()) {
resultExpr = transformInvocation(invocation, callBuilder, transformedPrimary);
} else if (invocation.getPrimary() instanceof Tree.BaseTypeExpression) {
resultExpr = transformBaseInstantiation(invocation, callBuilder, transformedPrimary);
} else if (invocation.getPrimary() instanceof Tree.QualifiedTypeExpression) {
resultExpr = transformQualifiedInstantiation(invocation, callBuilder, transformedPrimary);
} else {
resultExpr = transformInvocation(invocation, callBuilder, transformedPrimary);
}
if(invocation.handleBoxing)
resultExpr = applyErasureAndBoxing(resultExpr, invocation.getReturnType(),
invocation.erased, !invocation.unboxed, invocation.boxingStrategy, invocation.getReturnType(), 0);
return resultExpr;
}
private JCExpression transformInvocation(Invocation invocation, CallBuilder callBuilder,
TransformedInvocationPrimary transformedPrimary) {
invocation.location(callBuilder);
boolean needsCast = false;
if (Decl.isConstructorPrimary(invocation.getPrimary())) {
Tree.StaticMemberOrTypeExpression qte = (Tree.StaticMemberOrTypeExpression)invocation.getPrimary();
// instantiator
Constructor ctor = Decl.getConstructor(qte.getDeclaration());
if (Strategy.generateInstantiator(ctor)) {
needsCast = Strategy.isInstantiatorUntyped(ctor);
if (qte instanceof Tree.QualifiedMemberExpression
&& ((Tree.QualifiedMemberExpression)qte).getPrimary() instanceof Tree.QualifiedTypeExpression
&& isCeylonCallable(getReturnTypeOfCallable(invocation.getPrimary().getTypeModel()))) {
callBuilder.invoke(naming.makeQualIdent(transformedPrimary.expr, "$call$"));
} else {
callBuilder.typeArguments(List.nil());
java.util.List typeModels = qte.getTypeArguments().getTypeModels();
if (typeModels!=null) {
for (Type tm : typeModels) {
callBuilder.typeArgument(makeJavaType(tm, AbstractTransformer.JT_TYPE_ARGUMENT));
}
}
callBuilder.invoke(naming.makeInstantiatorMethodName(transformedPrimary.expr, Decl.getConstructedClass(ctor)));
}
} else if (typeFact().isJavaArrayType(Decl.getConstructedClass(ctor).getType())) {
callBuilder.arrayWith(
invocation.getReturnType().getQualifyingType(),
makeJavaType(invocation.getReturnType(), JT_CLASS_NEW));
} else {
if (Decl.getConstructedClass(invocation.getPrimaryDeclaration()).isMember()
&& invocation.getPrimary() instanceof Tree.QualifiedMemberOrTypeExpression
&& !(((Tree.QualifiedMemberOrTypeExpression)invocation.getPrimary()).getPrimary() instanceof Tree.BaseTypeExpression)) {
callBuilder.instantiate(new ExpressionAndType(transformedPrimary.expr, null),
makeJavaType(invocation.getReturnType(), JT_CLASS_NEW | (transformedPrimary.expr == null ? 0 : JT_NON_QUALIFIED)));
} else {
callBuilder.instantiate(
makeJavaType(invocation.getReturnType(), JT_CLASS_NEW)/*transformedPrimary.expr*/);
}
}
} else
if(invocation.getQmePrimary() != null
&& isJavaArray(invocation.getQmePrimary().getTypeModel())
&& transformedPrimary.selector != null
&& (transformedPrimary.selector.equals("get")
|| transformedPrimary.selector.equals("set"))){
if(transformedPrimary.selector.equals("get"))
callBuilder.arrayRead(transformedPrimary.expr);
else if(transformedPrimary.selector.equals("set")){
callBuilder.arrayWrite(transformedPrimary.expr);
Type arrayType = invocation.getQmePrimary().getTypeModel().resolveAliases();
if(isJavaObjectArray(arrayType) && invocation instanceof PositionalInvocation){
Type elementType = arrayType.getTypeArgumentList().get(0);
Type argumentType = ((PositionalInvocation)invocation).getArgumentType(1);
if(!argumentType.isSubtypeOf(typeFact().getOptionalType(elementType)))
callBuilder.javaArrayWriteNeedsCast(true);
}
}else
return makeErroneous(invocation.getNode(), "compiler bug: extraneous array selector: "+transformedPrimary.selector);
} else if (invocation.isUnknownArguments()) {
// if we have an unknown parameter list, like Callble, need to prepend the callable
// to the argument list, and invoke Util.apply
// 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
JCExpression callableTypeExpr = makeJavaType(invocation.getPrimary().getTypeModel());
ExpressionAndType callableArg = new ExpressionAndType(transformedPrimary.expr, callableTypeExpr);
Type returnType = invocation.getReturnType();
JCExpression returnTypeExpr = makeJavaType(returnType, JT_NO_PRIMITIVES);
callBuilder.prependArgumentAndType(callableArg);
callBuilder.typeArgument(returnTypeExpr);
callBuilder.invoke(make().Select(make().QualIdent(syms().ceylonUtilType.tsym),
names().fromString("apply")));
} else if (invocation.isOnValueType()) {
JCExpression primTypeExpr = makeJavaType(invocation.getQmePrimary().getTypeModel(), JT_NO_PRIMITIVES | JT_VALUE_TYPE);
callBuilder.invoke(naming.makeQuotedQualIdent(primTypeExpr, transformedPrimary.selector));
} else {
callBuilder.invoke(naming.makeQuotedQualIdent(transformedPrimary.expr, transformedPrimary.selector));
}
JCExpression result = callBuilder.build();
if (needsCast) {
result = make().TypeCast(makeJavaType(invocation.getReturnType()), result);
}
return result;
}
private JCExpression transformQualifiedInstantiation(Invocation invocation, CallBuilder callBuilder,
TransformedInvocationPrimary transformedPrimary) {
Tree.QualifiedTypeExpression qte = (Tree.QualifiedTypeExpression)invocation.getPrimary();
Declaration declaration = qte.getDeclaration();
invocation.location(callBuilder);
if (Decl.isJavaStaticOrInterfacePrimary(invocation.getPrimary())) {
callBuilder.instantiate(transformedPrimary.expr);
} else if (!Strategy.generateInstantiator(declaration)) {
if (Decl.isConstructorPrimary(invocation.getPrimary())) {
if (Decl.getConstructedClass(invocation.getPrimaryDeclaration()).isMember()
/*&& invocation.getPrimary() instanceof Tree.QualifiedTypeExpression
&& !(((Tree.QualifiedTypeExpression)invocation.getPrimary()).getPrimary() instanceof Tree.BaseTypeExpression)*/) {
callBuilder.instantiate(new ExpressionAndType(transformedPrimary.expr, null),
makeJavaType(invocation.getReturnType(), JT_CLASS_NEW | (transformedPrimary.expr == null ? 0 : JT_NON_QUALIFIED)));
} else {
callBuilder.instantiate(
makeJavaType(invocation.getReturnType(), JT_CLASS_NEW)/*transformedPrimary.expr*/);
}
} else {
JCExpression qualifier;
JCExpression qualifierType;
if (declaration.getContainer() instanceof Interface) {
// When doing qualified invocation through an interface we need
// to get the companion.
Interface qualifyingInterface = (Interface)declaration.getContainer();
qualifier = transformedPrimary.expr;
qualifierType = makeJavaType(qualifyingInterface.getType(), JT_COMPANION);
} else {
qualifier = transformedPrimary.expr;
if (declaration.getContainer() instanceof TypeDeclaration) {
qualifierType = makeJavaType(((TypeDeclaration)declaration.getContainer()).getType());
} else {
qualifierType = null;
}
}
Type classType = (Type)qte.getTarget();
JCExpression type;
// special case for package-qualified things that are not really qualified
if(qualifier == null){
type = makeJavaType(classType, AbstractTransformer.JT_CLASS_NEW);
}else{
// Note: here we're not fully qualifying the class name because the JLS says that if "new" is qualified the class name
// is qualified relative to it
type = makeJavaType(classType, AbstractTransformer.JT_CLASS_NEW | AbstractTransformer.JT_NON_QUALIFIED);
}
callBuilder.instantiate(new ExpressionAndType(qualifier, qualifierType), type);
}
} else {
// instantiator
callBuilder.typeArguments(List.nil());
java.util.List typeModels = qte.getTypeArguments().getTypeModels();
if (typeModels!=null) {
for (Type tm : typeModels) {
callBuilder.typeArgument(makeJavaType(tm, AbstractTransformer.JT_TYPE_ARGUMENT));
}
}
callBuilder.invoke(naming.makeInstantiatorMethodName(transformedPrimary.expr, Decl.getConstructedClass(declaration)));
}
JCExpression result = callBuilder.build();
if (Strategy.isInstantiatorUntyped(declaration)) {
result = make().TypeCast(makeJavaType(invocation.getReturnType()), result);
}
return result;
}
private JCExpression transformBaseInstantiation(Invocation invocation, CallBuilder callBuilder,
TransformedInvocationPrimary transformedPrimary) {
JCExpression resultExpr;
Tree.BaseTypeExpression type = (Tree.BaseTypeExpression)invocation.getPrimary();
Declaration declaration = type.getDeclaration();
invocation.location(callBuilder);
if (Strategy.generateInstantiator(declaration)) {
resultExpr = callBuilder
.typeArguments(List.nil())
.invoke(naming.makeInstantiatorMethodName(transformedPrimary.expr, (Class)declaration))
.build();
if (Strategy.isInstantiatorUntyped(declaration)) {
// $new method declared to return Object, so needs typecast
resultExpr = make().TypeCast(makeJavaType(
((TypeDeclaration)declaration).getType()), resultExpr);
}
} else {
Type classType = (Type)type.getTarget();
if(isJavaArray(classType)){
JCExpression typeExpr = makeJavaType(classType, AbstractTransformer.JT_CLASS_NEW | AbstractTransformer.JT_RAW);
callBuilder.javaArrayInstance(typeExpr);
if(isJavaObjectArray(classType)){
Type elementType = classType.getTypeArgumentList().get(0);
MultidimensionalArray multiArray = getMultiDimensionalArrayInfo(elementType);
if(multiArray != null)
elementType = multiArray.type;
// if it is an array of Foo we need a raw instanciation and cast
// array of Foo is fine, array of Nothing too
if(elementType.getDeclaration() instanceof ClassOrInterface
|| elementType.isNothing()){
if(!elementType.getTypeArgumentList().isEmpty())
callBuilder.javaArrayInstanceNeedsCast(makeJavaType(classType, AbstractTransformer.JT_NO_PRIMITIVES));
}else{
// if it's an array of union, intersection or type param we need a runtime allocation
callBuilder.javaArrayInstanceIsGeneric(makeReifiedTypeArgument(elementType),
multiArray != null ? multiArray.dimension + 1 : 1);
}
}
}else{
if (Decl.isConstructor(classType.getDeclaration())) {
classType = classType.getExtendedType();
}
JCExpression typeExpr = makeJavaType(classType, AbstractTransformer.JT_CLASS_NEW);
callBuilder.instantiate(typeExpr);
}
resultExpr = callBuilder.build();
}
return resultExpr;
}
private JCExpression transformCallableSpecifierInvocation(CallBuilder callBuilder, CallableSpecifierInvocation invocation) {
at(invocation.getNode());
JCExpression result = callBuilder
.invoke(naming.makeQuotedQualIdent(invocation.getCallable(), Naming.getCallableMethodName(invocation.getMethod())))
.argumentsAndTypes(transformArgumentList(invocation, null, callBuilder))
.build();
if(invocation.handleBoxing)
result = applyErasureAndBoxing(result, invocation.getReturnType(),
invocation.erased, !invocation.unboxed, invocation.boxingStrategy, invocation.getReturnType(), 0);
return result;
}
private final void transformTypeArguments(
CallBuilder callBuilder,
Tree.StaticMemberOrTypeExpression mte) {
java.util.List tps = null;
Declaration declaration = mte.getDeclaration();
if (!mte.getTypeModel().isTypeConstructor()) {
tps = Strategy.getEffectiveTypeParameters(declaration);
} else {
for (TypeParameter tp : Strategy.getEffectiveTypeParameters(declaration)) {
callBuilder.typeArgument(makeJavaType(tp.getType(), JT_TYPE_ARGUMENT));
}
return;
}
if (tps != null) {
for (TypeParameter tp : tps) {
Type ta = mte.getTarget().getTypeArguments().get(tp);
java.util.List bounds = null;
boolean needsCastForBounds = false;
if(!tp.getSatisfiedTypes().isEmpty()){
bounds = new ArrayList(tp.getSatisfiedTypes().size());
for(Type bound : tp.getSatisfiedTypes()){
// substitute the right type arguments
bound = substituteTypeArgumentsForTypeParameterBound(mte.getTarget(), bound);
bounds.add(bound);
needsCastForBounds |= needsCast(ta, bound, false, false, false);
}
}
boolean hasMultipleBounds;
Type firstBound;
if(bounds != null){
hasMultipleBounds = bounds.size() > 1;
firstBound = bounds.isEmpty() ? null : bounds.get(0);
}else{
hasMultipleBounds = false;
firstBound = null;
}
if (willEraseToObject(ta) || needsCastForBounds) {
boolean boundsSelfDependent = isBoundsSelfDependant(tp);
if (hasDependentTypeParameters(tps, tp)
// if we must use the bounds and we have more than one, we cannot use one to satisfy them all
// and we cannot represent the intersection type in Java so give up
|| hasMultipleBounds
// if we are going to use the first bound and it is self-dependent, we will make it raw
|| boundsSelfDependent
|| (firstBound != null && willEraseToObject(firstBound))) {
// we just can't satisfy the bounds if there are more than one, just pray,
// BUT REMEMBER THERE IS NO SUCH THING AS A RAW METHOD CALL IN JAVA
// so at some point we'll have to introduce an intersection type AST node to satisfy multiple bounds
if(hasMultipleBounds){
callBuilder.typeArguments(List.nil());
return;
}
// if we have a bound
if(firstBound != null){
// if it's self-dependent we cannot satisfy it without a raw type
if(boundsSelfDependent)
callBuilder.typeArgument(makeJavaType(firstBound, JT_TYPE_ARGUMENT|JT_RAW));
else
callBuilder.typeArgument(makeJavaType(firstBound, JT_TYPE_ARGUMENT));
}else{
// no bound, let's go with Object then
callBuilder.typeArgument(makeJavaType(typeFact().getObjectType(), JT_TYPE_ARGUMENT));
}
}else if (firstBound == null) {
callBuilder.typeArgument(makeJavaType(ta, JT_TYPE_ARGUMENT));
} else {
callBuilder.typeArgument(makeJavaType(firstBound, JT_TYPE_ARGUMENT));
}
} else {
callBuilder.typeArgument(makeJavaType(ta, JT_TYPE_ARGUMENT));
}
}
}
}
boolean erasesTypeArguments(Reference producedReference) {
java.util.List tps = null;
Declaration declaration = producedReference.getDeclaration();
if (declaration instanceof Generic) {
tps = ((Generic)declaration).getTypeParameters();
}
if (tps != null) {
for (TypeParameter tp : tps) {
Type ta = producedReference.getTypeArguments().get(tp);
java.util.List bounds = null;
boolean needsCastForBounds = false;
if(!tp.getSatisfiedTypes().isEmpty()){
bounds = new ArrayList(tp.getSatisfiedTypes().size());
for(Type bound : tp.getSatisfiedTypes()){
// substitute the right type arguments
bound = substituteTypeArgumentsForTypeParameterBound(producedReference, bound);
bounds.add(bound);
needsCastForBounds |= needsCast(ta, bound, false, false, false);
}
}
if (willEraseToObject(ta) || needsCastForBounds) {
return true;
}
}
}
return false;
}
protected JCExpression transformNamedArgumentInvocationOrInstantiation(NamedArgumentInvocation invocation,
CallBuilder callBuilder,
TransformedInvocationPrimary transformedPrimary) {
JCExpression resultExpr = transformPositionalInvocationOrInstantiation(invocation, callBuilder, transformedPrimary);
// apply the default parameters
if (invocation.getVars() != null && !invocation.getVars().isEmpty()) {
if ((invocation.getReturnType() == null
|| Decl.isUnboxedVoid(invocation.getPrimaryDeclaration()))
&& !Decl.isMpl((Functional) invocation.getPrimaryDeclaration())) {
// void methods get wrapped like (let $arg$1=expr, $arg$0=expr in call($arg$0, $arg$1); null)
resultExpr = make().LetExpr(
invocation.getVars().append(make().Exec(resultExpr)).toList(),
makeNull());
} else {
// all other methods like (let $arg$1=expr, $arg$0=expr in call($arg$0, $arg$1))
resultExpr = make().LetExpr(
invocation.getVars().toList(),
resultExpr);
}
}
return resultExpr;
}
//
// Invocations
public void transformSuperInvocation(Tree.ExtendedType extendedType, ClassDefinitionBuilder classBuilder) {
HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(extendedType);
if (error != null) {
classBuilder.getInitBuilder().delegateCall(this.makeThrowUnresolvedCompilationError(error));
return;
}
if (extendedType.getInvocationExpression() != null
&& extendedType.getInvocationExpression().getPositionalArgumentList() != null) {
Declaration primaryDeclaration = ((Tree.MemberOrTypeExpression)extendedType.getInvocationExpression().getPrimary()).getDeclaration();
java.util.List paramLists = ((Functional)primaryDeclaration).getParameterLists();
if(paramLists.isEmpty()){
classBuilder.getInitBuilder().delegateCall(at(extendedType).Exec(makeErroneous(extendedType, "compiler bug: missing parameter list in extends clause: " + primaryDeclaration.getName() + " must be invoked")));
} else {
boolean prevFnCall = withinInvocation(true);
try {
JCStatement superExpr = transformConstructorDelegation(extendedType,
new CtorDelegation(null, primaryDeclaration),
extendedType.getInvocationExpression(), classBuilder, false);
classBuilder.getInitBuilder().delegateCall(superExpr);
} finally {
withinInvocation(prevFnCall);
}
}
}
}
/**
* Transform a delegated constructor call ({@code extends XXX()})
* which may be either a superclass initializer/constructor or a
* same-class constructor.
* @param extendedType
* @param delegation The kind of delegation
* @param invocation
* @param classBuilder
* @return
*/
JCStatement transformConstructorDelegation(Node extendedType,
CtorDelegation delegation,
Tree.InvocationExpression invocation, ClassDefinitionBuilder classBuilder, boolean forDelegationConstructor) {
if (delegation != null && delegation.isError()) {
return delegation.makeThrow(this);
}
Declaration primaryDeclaration = ((Tree.MemberOrTypeExpression)invocation.getPrimary()).getDeclaration();
java.util.List paramLists = ((Functional)primaryDeclaration).getParameterLists();
if(paramLists.isEmpty()){
classBuilder.getInitBuilder().delegateCall(at(extendedType).Exec(makeErroneous(extendedType, "compiler bug: super class " + primaryDeclaration.getName() + " is missing parameter list")));
return null;
}
SuperInvocation builder = new SuperInvocation(this,
classBuilder.getForDefinition(),
delegation, invocation,
paramLists.get(0), forDelegationConstructor);
CallBuilder callBuilder = CallBuilder.instance(this);
boolean prevFnCall = withinInvocation(true);
try {
if (invocation.getPrimary() instanceof Tree.StaticMemberOrTypeExpression){
transformTypeArguments(callBuilder,
(Tree.StaticMemberOrTypeExpression)invocation.getPrimary());
}
at(builder.getNode());
JCExpression expr = null;
Scope outerDeclaration;
if (Decl.isConstructor(primaryDeclaration)) {
outerDeclaration= builder.getPrimaryDeclaration().getContainer().getContainer();
} else {
outerDeclaration= builder.getPrimaryDeclaration().getContainer();
}
if ((Strategy.generateInstantiator(builder.getPrimaryDeclaration())
|| builder.getPrimaryDeclaration() instanceof Class)
&& outerDeclaration instanceof Interface
&& !((Interface)outerDeclaration).isJava()) {
// If the subclass is inner to an interface then it will be
// generated inner to the companion and we need to qualify the
// super(), *unless* the subclass is nested within the same
// interface as it's superclass.
Scope outer = builder.getSub().getContainer();
while (!(outer instanceof Package)) {
if (outer == outerDeclaration) {
expr = naming.makeSuper();
break;
}
outer = outer.getContainer();
}
if (expr == null) {
if (delegation.isSelfDelegation()) {
throw new BugException();
}
Interface iface = (Interface)outerDeclaration;
JCExpression superQual;
if (Decl.getClassOrInterfaceContainer(classBuilder.getForDefinition(), false) instanceof Interface) {
superQual = naming.makeCompanionAccessorCall(naming.makeQuotedThis(), iface);
} else {
superQual = naming.makeCompanionFieldName(iface);
}
expr = naming.makeQualifiedSuper(superQual);
}
} else {
expr = delegation.isSelfDelegation() ? naming.makeThis() : naming.makeSuper();
}
final List superArguments = transformSuperInvocationArguments(
classBuilder, builder, callBuilder);
JCExpression superExpr = callBuilder.invoke(expr)
.arguments(superArguments)
.build();
return at(extendedType).Exec(superExpr);
//classBuilder.getInitBuilder().superCall(at(extendedType).Exec(superExpr));
} finally {
withinInvocation(prevFnCall);
}
}
/**
* Transforms the arguments for the invocation of a superclass initializer
* (call to {@code super()}).
*
* This is complicated by the need to avoid
* #929, so when a backward branch is needed in the evaluation of any
* argument expression we generate methods on the companion class
* (one for each argument) to evaluate the arguments so that the uninitialized
* {@code this} is not on the operand stack.
*/
private List transformSuperInvocationArguments(
ClassDefinitionBuilder classBuilder, SuperInvocation invocation, CallBuilder callBuilder) {
// We could create a TransformedPrimary(expr, "super") here if needed
List superArgumentsAndTypes = transformArgumentList(invocation, null, callBuilder);
final List superArguments = ExpressionAndType.toExpressionList(superArgumentsAndTypes);
return superArguments;
}
public JCExpression transform(Tree.InvocationExpression ce) {
JCExpression ret = checkForInvocationExpressionOptimisation(ce);
if(ret != null)
return ret;
Tree.Term primary = Decl.unwrapExpressionsUntilTerm(ce.getPrimary());
Declaration primaryDeclaration = null;
Reference producedReference = null;
if (primary instanceof Tree.MemberOrTypeExpression) {
producedReference = ((Tree.MemberOrTypeExpression)primary).getTarget();
primaryDeclaration = ((Tree.MemberOrTypeExpression)primary).getDeclaration();
}
Invocation invocation;
if (ce.getPositionalArgumentList() != null) {
if ((isIndirectInvocation(ce, true)
|| isWithinDefaultParameterExpression(primaryDeclaration.getContainer()))
&& !Decl.isJavaStaticOrInterfacePrimary(ce.getPrimary())){
// indirect invocation
invocation = new IndirectInvocation(this,
primary, primaryDeclaration,
ce);
} else {
// direct invocation
java.util.List parameters = ((Functional)primaryDeclaration).getFirstParameterList().getParameters();
invocation = new PositionalInvocation(this,
primary, primaryDeclaration,producedReference,
ce,
parameters);
}
} else if (ce.getNamedArgumentList() != null) {
invocation = new NamedArgumentInvocation(this,
primary,
primaryDeclaration,
producedReference,
ce);
} else {
return makeErroneous(ce, "no arguments");
}
return transformInvocation(invocation);
}
public JCExpression transformFunctional(Tree.StaticMemberOrTypeExpression expr,
Functional functional, Type expectedType) {
return CallableBuilder.methodReference(gen(), expr,
functional.getFirstParameterList(), expectedType, expr.getTypeModel(), true);
}
public JCExpression transformFunctionalInterfaceBridge(Tree.StaticMemberOrTypeExpression expr,
Value functional, Type expectedType) {
ParameterList paramList = new ParameterList();
Type callableType = expr.getTypeModel().getSupertype(typeFact().getCallableDeclaration());
int i=0;
for(Type type : typeFact().getCallableArgumentTypes(callableType)){
Parameter param = new Parameter();
Value paramModel = new Value();
param.setModel(paramModel);
param.setName("arg"+i);
paramModel.setName("arg"+i);
paramModel.setType(type);
paramList.getParameters().add(param);
i++;
}
return CallableBuilder.methodReference(gen(), expr,
paramList, expectedType, expr.getTypeModel(), false);
}
public JCExpression transformFunctionalInterfaceBridge(Tree.InvocationExpression expr,
JCExpression primaryExpr, Type expectedType) {
ParameterList paramList = new ParameterList();
int i=0;
Type callableType = expr.getTypeModel().getSupertype(typeFact().getCallableDeclaration());
for(Type type : typeFact().getCallableArgumentTypes(callableType)){
Parameter param = new Parameter();
Value paramModel = new Value();
param.setModel(paramModel);
param.setName("arg"+i);
paramModel.setName("arg"+i);
paramModel.setType(type);
paramList.getParameters().add(param);
i++;
}
return CallableBuilder.callableToFunctionalInterface(gen(), expr,
paramList, expectedType, expr.getTypeModel(), false, primaryExpr);
}
public JCExpression transformCallableBridge(Tree.StaticMemberOrTypeExpression expr,
Value functional, Type expectedType) {
ParameterList paramList = new ParameterList();
// expr is a SAM
// expectedType is a Callable
TypedReference samRef = checkForFunctionalInterface(expr.getTypeModel());
TypedDeclaration samDecl = samRef.getDeclaration();
if(samDecl instanceof Value){
Parameter param = new Parameter();
Value paramModel = new Value();
param.setModel(paramModel);
param.setName("arg0");
paramModel.setName("arg0");
paramModel.setType(samRef.getType());
paramModel.setUnboxed(samDecl.getUnboxed());
// FIXME: other stuff like erasure?
paramList.getParameters().add(param);
}else{
int i=0;
for(Parameter samParam : ((Function)samDecl).getFirstParameterList().getParameters()){
TypedReference typedSamParam = samRef.getTypedParameter(samParam);
Parameter param = new Parameter();
Value paramModel = new Value();
param.setModel(paramModel);
param.setName("arg"+i);
paramModel.setName("arg"+i);
paramModel.setType(typedSamParam.getFullType());
// FIXME: other stuff like erasure?
paramModel.setUnboxed(typedSamParam.getDeclaration().getUnboxed());
paramList.getParameters().add(param);
i++;
}
}
// FIXME: this is cheating we should be assembling it from the SAM type
Type callableType = expectedType.getSupertype(typeFact().getCallableDeclaration());
return CallableBuilder.methodReference(gen(), expr,
paramList, expectedType, callableType, false);
}
//
// Member expressions
public static interface TermTransformer {
JCExpression transform(JCExpression primaryExpr, String selector);
}
// Qualified members
public JCExpression transform(Tree.QualifiedMemberExpression expr) {
// check for an optim
JCExpression ret = checkForQualifiedMemberExpressionOptimisation(expr);
if(ret != null)
return ret;
if (expr.getPrimary() instanceof Tree.BaseTypeExpression) {
Tree.BaseTypeExpression primary = (Tree.BaseTypeExpression)expr.getPrimary();
return transformMemberReference(expr, primary);
} else if (expr.getPrimary() instanceof Tree.QualifiedTypeExpression) {
Tree.QualifiedTypeExpression primary = (Tree.QualifiedTypeExpression)expr.getPrimary();
return transformMemberReference(expr, primary);
}
return transform(expr, null);
}
JCExpression transformMemberReference(
Tree.QualifiedMemberOrTypeExpression expr,
Tree.MemberOrTypeExpression primary) {
Declaration member = expr.getDeclaration();
Type qualifyingType = primary.getTypeModel();
Tree.TypeArguments typeArguments = expr.getTypeArguments();
Type expectedTypeIfCoerced = coerced ? expectedType : null;
boolean prevSyntheticClassBody = withinSyntheticClassBody(true);
try {
if (member.isStatic()) {
if (member instanceof Function) {
Function method = (Function)member;
Reference producedReference = expr.getTarget();//method.appliedReference(qualifyingType, typeArguments.getTypeModels());
return CallableBuilder.javaStaticMethodReference(
gen(),
expr,
expr.getTypeModel(),
method,
producedReference,
expectedTypeIfCoerced).build();
} else if (member instanceof FieldValue) {
return naming.makeName(
(TypedDeclaration)member, Naming.NA_FQ | Naming.NA_WRAPPER_UNQUOTED);
} else if (member instanceof Value) {
CallBuilder callBuilder = CallBuilder.instance(this);
JCExpression qualExpr = naming.makeTypeDeclarationExpression(null, (TypeDeclaration)member.getContainer(), DeclNameFlag.QUALIFIED);
Type primType = primary.getTarget().getType();
if (member.isStatic()
&& ModelUtil.isCeylonDeclaration(member)
&& !primType.getTypeArgumentList().isEmpty()) {
for (Type pt : primType.getTypeArgumentList()) {
callBuilder.typeArgument(makeJavaType(pt, JT_TYPE_ARGUMENT));
callBuilder.argument(makeReifiedTypeArgument(pt));
}
}
callBuilder.invoke(naming.makeQualifiedName(qualExpr, (TypedDeclaration)member, Naming.NA_GETTER | Naming.NA_MEMBER));
return callBuilder.build();
} else if (member instanceof Class) {
Reference producedReference = expr.getTarget();
return CallableBuilder.javaStaticMethodReference(
gen(),
expr,
expr.getTypeModel(),
(Class)member,
producedReference,
expectedTypeIfCoerced).build();
}
}
if (member instanceof Value) {
if (expr.getStaticMethodReference()
&& Decl.isEnumeratedConstructor((Value)member)) {
CallBuilder callBuilder = CallBuilder.instance(this);
JCExpression qualExpr;
Class class1 = (Class) member.getContainer();
if (class1.isToplevel() || class1.isStatic()) {
qualExpr = naming.makeTypeDeclarationExpression(null, class1.isStatic() ? (TypeDeclaration)class1.getContainer() : class1, DeclNameFlag.QUALIFIED);
callBuilder.invoke(naming.makeQualifiedName(qualExpr, (TypedDeclaration)member, Naming.NA_GETTER | Naming.NA_MEMBER));
} else if (class1.isMember()){
// Stef: this is fugly but I couldn't find better. This makes sure that Outer.Inner.enumeratedConstructor
// creates a Callable that returns the enumeratedConstructor given an outer instance
if (primary instanceof Tree.QualifiedMemberOrTypeExpression
&& (((Tree.QualifiedMemberOrTypeExpression) primary).getPrimary() instanceof Tree.BaseTypeExpression
|| ((Tree.QualifiedMemberOrTypeExpression) primary).getPrimary() instanceof Tree.QualifiedTypeExpression))
return CallableBuilder.unboundValueMemberReference(
gen(),
expr,
expr.getTypeModel(),
((TypedDeclaration)member),
expectedTypeIfCoerced).build();
else{
qualExpr = primary instanceof Tree.QualifiedMemberOrTypeExpression ? transformExpression(((Tree.QualifiedMemberOrTypeExpression)primary).getPrimary()) : null;
callBuilder.invoke(naming.makeQualifiedName(qualExpr, (TypedDeclaration)member, Naming.NA_GETTER | Naming.NA_MEMBER));
}
} else {
// Local enumerated constructor values are boxed
qualExpr = naming.makeQualifiedName(null, (TypedDeclaration)member, Naming.NA_Q_LOCAL_INSTANCE);
qualExpr = gen().makeSelect(qualExpr, naming.selector((TypedDeclaration)member));
callBuilder.fieldRead(qualExpr);
}
return callBuilder.build();
} else {
return CallableBuilder.unboundValueMemberReference(
gen(),
expr,
expr.getTypeModel(),
((TypedDeclaration)member),
expectedTypeIfCoerced).build();
}
} else if (Decl.isConstructor(member)) {
Reference producedReference = expr.getTarget();
return CallableBuilder.unboundFunctionalMemberReference(
gen(),
expr,
expr.getTypeModel(),
Decl.getConstructor(member),
producedReference,
expectedTypeIfCoerced);
} else if (member instanceof Function) {
Function method = (Function)member;
if (!method.isParameter()) {
Reference producedReference = method.appliedReference(qualifyingType, typeArguments.getTypeModels());
return CallableBuilder.unboundFunctionalMemberReference(
gen(),
expr,
expr.getTypeModel(),
method,
producedReference,
expectedTypeIfCoerced);
} else {
Reference producedReference = method.appliedReference(qualifyingType, typeArguments.getTypeModels());
return CallableBuilder.unboundFunctionalMemberReference(
gen(),
expr,
expr.getTypeModel(),
method,
producedReference,
expectedTypeIfCoerced);
}
} else if (member instanceof Class) {
Reference producedReference = expr.getTarget();
return CallableBuilder.unboundFunctionalMemberReference(
gen(),
expr,
expr.getTypeModel(),
(Class)member,
producedReference,
expectedTypeIfCoerced);
} else {
return makeErroneous(expr, "compiler bug: member reference of " + expr + " not supported yet");
}
} finally {
withinSyntheticClassBody(prevSyntheticClassBody);
}
}
private JCExpression transform(Tree.QualifiedMemberExpression expr, TermTransformer transformer) {
JCExpression result;
if (expr.getMemberOperator() instanceof Tree.SafeMemberOp) {
result = transformSafeMemberOperator(expr, transformer);
} else if (expr.getMemberOperator() instanceof Tree.SpreadOp) {
result = transformSpreadOperator(expr, transformer);
} else {
JCExpression primaryExpr = transformQualifiedMemberPrimary(expr);
result = transformMemberExpression(expr, primaryExpr, transformer);
}
return result;
}
private JCExpression transformSafeMemberOperator(Tree.QualifiedMemberOrTypeExpression expr, TermTransformer transformer) {
Naming.SyntheticName tmpVarName = naming.alias("safe");
JCExpression typeExpr = makeJavaType(expr.getTarget().getQualifyingType(), JT_NO_PRIMITIVES);
JCExpression transExpr = transformMemberExpression(expr, tmpVarName.makeIdent(), transformer);
if (isFunctionalResult(expr.getTypeModel())) {
return transExpr;
}
// the marker we get for boxing on a QME with a SafeMemberOp is always unboxed
// since it returns an optional type, but that doesn't tell us if the underlying
// expr is or not boxed
boolean isBoxed = expr.getDeclaration() instanceof TypeDeclaration
|| !CodegenUtil.isUnBoxed((TypedDeclaration)expr.getDeclaration());
transExpr = boxUnboxIfNecessary(transExpr, isBoxed, expr.getTarget().getType(), BoxingStrategy.BOXED);
JCExpression testExpr = make().Binary(JCTree.Tag.NE, tmpVarName.makeIdent(), makeNull());
JCExpression condExpr = make().Conditional(testExpr, transExpr, makeNull());
JCExpression primaryExpr = transformQualifiedMemberPrimary(expr);
return makeLetExpr(tmpVarName, null, typeExpr, primaryExpr, condExpr);
}
private JCExpression transformSpreadOperator(final Tree.QualifiedMemberOrTypeExpression expr, TermTransformer transformer) {
at(expr);
boolean spreadMethodReferenceOuter = !expr.equals(this.spreading) && !isWithinInvocation() && isCeylonCallableSubtype(expr.getTypeModel());
boolean spreadMethodReferenceInner = expr.equals(this.spreading) && isWithinInvocation();
Tree.QualifiedMemberOrTypeExpression oldSpreading = spreading;
if (spreadMethodReferenceOuter) {
spreading = expr;
}
try {
Naming.SyntheticName varBaseName = naming.alias("spread");
ListBuffer letStmts = new ListBuffer();
final Naming.SyntheticName srcIterableName;
if (spreadMethodReferenceInner) {
// use the var we initialized in the outer
srcIterableName = this.memberPrimary;
} else {
srcIterableName = varBaseName.suffixedBy(Suffix.$iterable$);
}
if (spreadMethodReferenceOuter) {
// if we're in the outer, note then name of the var for use in the inner.
this.memberPrimary = srcIterableName;
}
Naming.SyntheticName srcIteratorName = varBaseName.suffixedBy(Suffix.$iterator$);
Type srcElementType = expr.getTarget().getQualifyingType();
JCExpression srcIterableExpr;
boolean isSuperOrSuperOf = false;
Type srcIterableType;
if (typeFact().isIterableType(expr.getPrimary().getTypeModel())) {
srcIterableType = typeFact().getIterableType(srcElementType);
} else if (typeFact().isJavaIterableType(expr.getPrimary().getTypeModel())) {
srcIterableType = typeFact().getJavaIterableDeclaration().appliedType(null, Collections.singletonList(srcElementType));
} else if (typeFact().isJavaArrayType(expr.getPrimary().getTypeModel())) {
srcIterableType = expr.getPrimary().getTypeModel();
srcElementType = typeFact().getJavaArrayElementType(srcIterableType);
} else {
return makeErroneous(expr, "unhandled iterable type");
}
if (spreadMethodReferenceInner) {
srcIterableExpr = srcIterableName.makeIdent();
} else {
boolean isSuper = isSuper(expr.getPrimary());
isSuperOrSuperOf = isSuper || isSuperOf(expr.getPrimary());
if(isSuperOrSuperOf){
// in this case we can't capture the iterable because it may be a mixin impl class, but it's constant
// so we just refer to it later
if(isSuper){
Declaration member = expr.getPrimary().getTypeModel().getDeclaration().getMember("iterator", null, false);
srcIterableExpr = transformSuper(expr, (TypeDeclaration) member.getContainer());
}else
srcIterableExpr = transformSuperOf(expr, expr.getPrimary(), "iterator");
}else{
srcIterableExpr = transformExpression(expr.getPrimary(), BoxingStrategy.BOXED, srcIterableType);
}
}
// do not capture the iterable for super invocations: see above
if (!spreadMethodReferenceInner && !isSuperOrSuperOf) {
JCVariableDecl srcIterable = null;
JCExpression srcIterableTypeExpr = makeJavaType(srcIterableType, JT_NO_PRIMITIVES);
srcIterable = makeVar(Flags.FINAL, srcIterableName, srcIterableTypeExpr, srcIterableExpr);
letStmts.prepend(srcIterable);
}
/* public Object next() {
* Object result;
* if (!((result = iterator.next()) instanceof Finished)) {
* result = transformedMember(result);
* }
* return result;
*/
/* Any arguments in the member of the spread would get re-evaluated on each iteration
* so we need to shift them to the scope of the Let to ensure they're evaluated once.
*/
boolean aliasArguments = (transformer instanceof InvocationTermTransformer)
&& ((InvocationTermTransformer)transformer).invocation.getNode() instanceof Tree.InvocationExpression
&& ((Tree.InvocationExpression)((InvocationTermTransformer)transformer).invocation.getNode()).getPositionalArgumentList() != null;
if (aliasArguments) {
((InvocationTermTransformer)transformer).callBuilder.argumentHandling(
CallBuilder.CB_ALIAS_ARGS, varBaseName);
}
JCNewClass iterableClass;
boolean prevSyntheticClassBody = expressionGen().withinSyntheticClassBody(true);
try {
Naming.SyntheticName iteratorResultName = varBaseName.suffixedBy(Suffix.$element$);
JCExpression transformedElement = applyErasureAndBoxing(iteratorResultName.makeIdent(), typeFact().getAnythingType(), CodegenUtil.hasTypeErased(expr.getPrimary()),
true, BoxingStrategy.BOXED,
srcElementType, 0);
transformedElement = transformMemberExpression(expr, transformedElement, transformer);
// This short-circuit is here for spread invocations
// The code has been called recursively and the part after this if-statement will
// be handled by the previous recursion
if (spreadMethodReferenceOuter) {
return make().LetExpr(letStmts.toList(), transformedElement);
}
Type resultElementType = expr.getTarget().getType();
final Type resultAbsentType;
transformedElement = applyErasureAndBoxing(transformedElement, resultElementType,
// don't trust the erased flag of expr, as it reflects the result type of the overall spread expr,
// not necessarily of the applied member
expr.getTarget().getDeclaration() instanceof TypedDeclaration
? CodegenUtil.hasTypeErased((TypedDeclaration)expr.getTarget().getDeclaration())
: false,
!CodegenUtil.isUnBoxed(expr), BoxingStrategy.BOXED, resultElementType, 0);
MethodDefinitionBuilder nextMdb = MethodDefinitionBuilder.systemMethod(this, "next");
nextMdb.isOverride(true);
nextMdb.annotationFlags(Annotations.IGNORE);
nextMdb.modifiers(Flags.PUBLIC | Flags.FINAL);
nextMdb.resultType(new TransformedType(make().Type(syms().objectType)));
final List l;
if (typeFact().isIterableType(expr.getPrimary().getTypeModel())) {
// private Iterator iterator = srcIterableName.iterator();
JCVariableDecl srcIterator = makeVar(Flags.FINAL, srcIteratorName, makeJavaType(typeFact().getIteratorType(srcElementType)),
make().Apply(null,
// for super we do not capture it because we can't and it's constant anyways
naming.makeQualIdent(isSuperOrSuperOf ? srcIterableExpr : srcIterableName.makeIdent(), "iterator"),
List.nil()));
resultAbsentType = typeFact().getIteratedAbsentType(expr.getPrimary().getTypeModel());
nextMdb.body(List.of(
makeVar(iteratorResultName,
make().Type(syms().objectType), null),
make().If(
make().Unary(JCTree.Tag.NOT,
make().TypeTest(make().Assign(
iteratorResultName.makeIdent(),
make().Apply(null,
naming.makeQualIdent(srcIteratorName.makeIdent(), "next"),
List.nil())),
make().Type(syms().ceylonFinishedType))),
make().Block(0, List.of(make().Exec(make().Assign(iteratorResultName.makeIdent(),
transformedElement)))),
null),
make().Return(iteratorResultName.makeIdent())));
l = List.of(srcIterator, nextMdb.build());
} else if (typeFact().isJavaIterableType(expr.getPrimary().getTypeModel())) {
// private Iterator iterator = srcIterableName.iterator();
JCVariableDecl srcIterator = makeVar(Flags.PRIVATE|Flags.FINAL, srcIteratorName, makeJavaType(typeFact().getJavaIteratorType(srcElementType)),
make().Apply(null,
// for super we do not capture it because we can't and it's constant anyways
naming.makeQualIdent(isSuperOrSuperOf ? srcIterableExpr : srcIterableName.makeIdent(), "iterator"),
List.nil()));
resultAbsentType = typeFact().getNullType();
nextMdb.body(List.of(
make().If(
make().Apply(null,
naming.makeQualIdent(srcIteratorName.makeIdent(), "hasNext"),
List.nil()),
make().Block(0, List.of(
makeVar(iteratorResultName,
make().Type(syms().objectType),
make().Apply(null,
naming.makeQualIdent(srcIteratorName.makeIdent(), "next"),
List.nil())),
make().Return(transformedElement))),
make().Return(makeFinished()))));
l = List.of(srcIterator, nextMdb.build());
} else if (typeFact().isJavaArrayType(expr.getPrimary().getTypeModel())) {
resultAbsentType = typeFact().getNullType();
JCVariableDecl srcIndex = makeVar(Flags.PRIVATE, srcIteratorName, make().Type(syms().intType),
make().Literal(0));
JCExpression indexed = make().Indexed(
srcIterableName.makeIdent(),
make().Unary(Tag.POSTINC,
srcIteratorName.makeIdent()));
if (typeFact().isJavaPrimitiveArrayType(expr.getPrimary().getTypeModel())) {
indexed = applyErasureAndBoxing(indexed, srcElementType, false, BoxingStrategy.BOXED, srcElementType);
}
nextMdb.body(List.of(
make().If(
make().Binary(Tag.LT,
srcIteratorName.makeIdent(),
// for super we do not capture it because we can't and it's constant anyways
naming.makeQualIdent(isSuperOrSuperOf ? srcIterableExpr : srcIterableName.makeIdent(), "length")),
make().Block(0, List.of(
makeVar(iteratorResultName,
make().Type(syms().objectType),
indexed),
make().Return(transformedElement))),
make().Return(makeFinished()))));
l = List.of(srcIndex, nextMdb.build());
} else {
return makeErroneous(expr, "unhandled iterable type");
}
// new AbstractIterator()
JCNewClass iteratorClass = make().NewClass(null,
null,
make().TypeApply(make().QualIdent(syms().ceylonAbstractIteratorType.tsym),
List.of(makeJavaType(resultElementType, JT_TYPE_ARGUMENT))),
List.of(makeReifiedTypeArgument(resultElementType)),
make().AnonymousClassDef(make().Modifiers(0), l));
MethodDefinitionBuilder iteratorMdb = MethodDefinitionBuilder.systemMethod(this, "iterator");
iteratorMdb.isOverride(true);
iteratorMdb.annotationFlags(Annotations.IGNORE);
iteratorMdb.modifiers(Flags.PUBLIC | Flags.FINAL);
iteratorMdb.resultType(new TransformedType(makeJavaType(typeFact().getIteratorType(resultElementType)), makeAtNonNull()));
iteratorMdb.body(make().Return(iteratorClass));
// new AbstractIterable()
iterableClass = make().NewClass(null,
null,
make().TypeApply(make().QualIdent(syms().ceylonAbstractIterableType.tsym),
List.of(makeJavaType(resultElementType, JT_TYPE_ARGUMENT), makeJavaType(resultAbsentType, JT_TYPE_ARGUMENT))),
List.of(makeReifiedTypeArgument(resultElementType), makeReifiedTypeArgument(resultAbsentType)),
make().AnonymousClassDef(make().Modifiers(0), List.of(iteratorMdb.build())));
} finally {
expressionGen().withinSyntheticClassBody(prevSyntheticClassBody);
}
if (aliasArguments) {
letStmts = letStmts.appendList(((InvocationTermTransformer)transformer).callBuilder.getStatements());
}
JCMethodInvocation result = make().Apply(null,
naming.makeQualIdent(iterableClass, "sequence"),
List.nil());
JCExpression spread = letStmts.isEmpty() ? result : make().LetExpr(letStmts.toList(), result);
// Do we *statically* know the result must be a Sequence
final boolean primaryIsSequence = typeFact().isNonemptyIterableType(expr.getPrimary().getTypeModel());
Type returnElementType = expr.getTarget().getType();
if(primaryIsSequence){
int flags = EXPR_DOWN_CAST;
spread = applyErasureAndBoxing(spread,
typeFact().getSequentialType(returnElementType),
false,
true,
BoxingStrategy.BOXED,
primaryIsSequence ?
typeFact().getSequenceType(returnElementType)
: typeFact().getSequentialType(returnElementType),
flags);
}
return spread;
} finally {
spreading = oldSpreading;
}
}
JCExpression transformQualifiedMemberPrimary(Tree.QualifiedMemberOrTypeExpression expr) {
if(expr.getTarget() == null)
return makeErroneous(expr, "compiler bug: "
// make sure we don't die of a missing declaration too
+ (expr.getDeclaration() != null ? expr.getDeclaration().getName() : expr)
+ " has a null target");
// do not consider the primary to be an invocation since in foo.x() we're invoking x, not foo.
boolean previousWithinInvocation = withinInvocation(false);
try{
// consider package qualifiers as non-prefixed, we always qualify them anyways, this is
// only useful for the typechecker resolving
Tree.Primary primary = expr.getPrimary();
if (Decl.isConstructor(expr.getDeclaration())) {
Constructor ctor = Decl.getConstructor(expr.getDeclaration());
if (primary instanceof Tree.QualifiedMemberOrTypeExpression) {
// foo.Class.Ctor => foo
primary = ((Tree.QualifiedMemberOrTypeExpression)primary).getPrimary();
} else if (primary instanceof Tree.BaseMemberExpression) {
// foo.member.Ctor => foo
} else if (primary instanceof Tree.BaseTypeExpression) {
// Class.Ctor => null
return null;
}
}
if(isPackage(primary))
return null;
Type type = expr.getTarget().getQualifyingType();
if(expr.getMemberOperator() instanceof Tree.SafeMemberOp && !isOptional(type)){
Type optionalType = typeFact().getOptionalType(type);
if (optionalType.isCached()) {
optionalType = optionalType.clone();
}
optionalType.setUnderlyingType(type.getUnderlyingType());
type = optionalType;
}
BoxingStrategy boxing = expr.getMemberOperator() instanceof Tree.SafeMemberOp == false
&& Decl.isValueTypeDecl(primary)
&& CodegenUtil.isUnBoxed(primary)
? BoxingStrategy.UNBOXED : BoxingStrategy.BOXED;
JCExpression result;
if (isSuper(primary)) {
result = transformSuper(expr);
} else if (isSuperOf(primary)) {
result = transformSuperOf(expr, expr.getPrimary(), expr.getDeclaration().getName());
} else if (isThis(primary)
&& !expr.getDeclaration().isCaptured()
&& !expr.getDeclaration().isShared()
&& Decl.getDeclarationScope(expr.getScope()) instanceof Constructor) {
result = null;
} else if (Decl.isJavaStaticOrInterfacePrimary(primary)) {
// Java static field or method access
result = transformJavaStaticOrInterfaceMember((Tree.QualifiedMemberOrTypeExpression)primary, expr.getTypeModel());
} else {
result = transformExpression(primary, boxing, type);
}
return result;
}finally{
withinInvocation(previousWithinInvocation);
}
}
private JCExpression transformJavaStaticOrInterfaceMember(Tree.QualifiedMemberOrTypeExpression qmte, Type staticType) {
Declaration decl = qmte.getDeclaration();
if (decl instanceof FieldValue) {
Value member = (Value)decl;
return naming.makeName(member, Naming.NA_FQ | Naming.NA_WRAPPER_UNQUOTED);
} else if (decl instanceof Value) {
Value member = (Value)decl;
CallBuilder callBuilder = CallBuilder.instance(this);
Type qualifyingType = ((TypeDeclaration)member.getContainer()).getType();
callBuilder.invoke(naming.makeQualifiedName(
makeJavaType(qualifyingType, JT_RAW | JT_NO_PRIMITIVES),
member,
Naming.NA_GETTER | Naming.NA_MEMBER));
return utilInvocation().checkNull(callBuilder.build());
} else if (decl instanceof Function) {
Function method = (Function)decl;
final ParameterList parameterList = method.getFirstParameterList();
Type qualifyingType = qmte.getPrimary().getTypeModel();
Tree.TypeArguments typeArguments = qmte.getTypeArguments();
Reference producedReference = method.appliedReference(qualifyingType, typeArguments.getTypeModels());
return utilInvocation().checkNull(makeJavaStaticInvocation(gen(),
method, producedReference, parameterList));
} else if (decl instanceof Class) {
Class class_ = (Class)decl;
if (class_.isStatic()) {
return naming.makeTypeDeclarationExpression(null, class_, Naming.DeclNameFlag.QUALIFIED);
} else {
final ParameterList parameterList = class_.getFirstParameterList();
Reference producedReference = qmte.getTarget();
return utilInvocation().checkNull(makeJavaStaticInvocation(gen(),
class_, producedReference, parameterList));
}
} else if (decl instanceof Interface) {
return naming.makeTypeDeclarationExpression(null, (Interface)decl, Naming.DeclNameFlag.QUALIFIED);
} else {
return makeErroneous(qmte, "compiler bug: unsupported static");
}
}
JCExpression makeJavaStaticInvocation(CeylonTransformer gen,
final Functional methodOrClass,
Reference producedReference,
final ParameterList parameterList) {
CallBuilder callBuilder = CallBuilder.instance(gen);
if (methodOrClass instanceof Function) {
JCExpression fn;
if (Decl.isJavaArrayFrom((Declaration)methodOrClass)) {
fn = gen.makeUnwrapArray((Declaration)methodOrClass);
} else {
fn = naming.makeName(
(Function)methodOrClass, Naming.NA_FQ | Naming.NA_WRAPPER_UNQUOTED);
}
callBuilder.invoke(fn);
} else if (methodOrClass instanceof Class) {
callBuilder.instantiate(
gen.makeJavaType(((Class)methodOrClass).getType(), JT_RAW | JT_NO_PRIMITIVES));
}
ListBuffer reified = new ListBuffer();
DirectInvocation.addReifiedArguments(gen, producedReference, reified);
for (ExpressionAndType reifiedArgument : reified) {
callBuilder.argument(reifiedArgument.expression);
}
for (Parameter parameter : parameterList.getParameters()) {
callBuilder.argument(gen.naming.makeQuotedIdent(parameter.getName()));
}
JCExpression innerInvocation = callBuilder.build();
return innerInvocation;
}
/**
* Removes the parentheses from the given term
*/
static Tree.Term eliminateParens(Tree.Term term) {
while (term instanceof Tree.Expression) {
term = ((Tree.Expression) term).getTerm();
}
return term;
}
private static boolean isThis(Tree.Primary primary) {
return eliminateParensAndWidening(primary) instanceof Tree.This;
}
static boolean isPackage(Tree.Primary primary) {
return eliminateParens(primary) instanceof Tree.Package;
}
static boolean isPackageQualified(Tree.QualifiedMemberOrTypeExpression qmte) {
return isPackage(qmte.getPrimary());
}
/**
* Is the given primary a {@code super of Foo}
* expression (modulo parentheses and multiple {@code of}
*/
private static boolean isSuperOf(Tree.Primary primary) {
return primary instanceof Tree.Expression
&& eliminateParensAndWidening(((Tree.Expression)primary).getTerm()) instanceof Tree.Super;
}
/**
* Is the given primary a {@code super} expression
* (modulo parentheses)
*/
private static boolean isSuper(Tree.Primary primary) {
return eliminateParens(primary) instanceof Tree.Super;
}
/**
* Is the given primary a {@code super} or {@code super of Foo}
* expression (modulo parentheses and multiple {@code of}
*/
static boolean isSuperOrSuperOf(Tree.Primary primary) {
return isSuper(primary) || isSuperOf(primary);
}
private JCExpression transformSuperOf(Node node, Tree.Primary superPrimary, String forMemberName) {
Tree.Term superOf = eliminateParens(superPrimary);
if (!(superOf instanceof Tree.OfOp)) {
throw new BugException();
}
Tree.Type superType = ((Tree.OfOp)superOf).getType();
if (!(eliminateParens(((Tree.OfOp)superOf).getTerm()) instanceof Tree.Super)) {
throw new BugException();
}
TypeDeclaration inheritedFrom = superType.getTypeModel().getDeclaration();
if (inheritedFrom instanceof Interface) {
inheritedFrom = (TypeDeclaration)inheritedFrom.getMember(forMemberName, null, false).getContainer();
}
return widenSuper(node, inheritedFrom);
}
private JCExpression widenSuper(
Node superOfQualifiedExpr,
TypeDeclaration inheritedFrom) {
JCExpression result;
if (inheritedFrom instanceof Class) {
if(isWithinSyntheticClassBody()){
// super refers to the closest ClassOrInterface
Scope scope = superOfQualifiedExpr.getScope();
while (!(scope instanceof Package)) {
if (scope instanceof ClassOrInterface) {
break;
}
scope = scope.getContainer();
}
if(scope instanceof ClassOrInterface)
result = naming.makeQualifiedSuper(makeJavaType(((ClassOrInterface) scope).getType(), JT_RAW));
else
result = naming.makeSuper();
}else{
result = naming.makeSuper();
}
} else if (inheritedFrom instanceof Interface) {
Interface iface = (Interface)inheritedFrom;
JCExpression qualifier = null;
if (inheritedFrom instanceof LazyInterface
&& !((LazyInterface)inheritedFrom).isCeylon()) {
result = naming.makeQualifiedSuper(makeJavaType(inheritedFrom.getType(), JT_RAW));
} else
if (needDollarThis(superOfQualifiedExpr.getScope())) {
qualifier = naming.makeQuotedThis();
if (iface.equals(typeFact().getIdentifiableDeclaration())) {
result = naming.makeQualifiedSuper(qualifier);
} else {
result = naming.makeCompanionAccessorCall(qualifier, iface);
}
} else {
if (iface.equals(typeFact().getIdentifiableDeclaration())) {
result = naming.makeQualifiedSuper(qualifier);
} else {
result = naming.makeCompanionFieldName(iface);
}
}
if (Decl.isAncestorLocal(iface)) {
result = make().TypeCast(makeJavaType(iface.getType(), JT_COMPANION), result);
}
} else {
result = makeErroneous(superOfQualifiedExpr, "compiler bug: " + (inheritedFrom == null ? "null" : inheritedFrom.getClass().getName()) + " is an unhandled case in widen()");
}
return result;
}
public JCExpression transformSuper(Tree.QualifiedMemberOrTypeExpression expression) {
TypeDeclaration inheritedFrom = (TypeDeclaration)expression.getDeclaration().getContainer();
return transformSuper(expression, inheritedFrom);
}
public JCExpression transformSuper(Node node, TypeDeclaration superDeclaration) {
return widenSuper(node, superDeclaration);
}
// Base members
public JCExpression transform(Tree.BaseMemberExpression expr) {
return transform(expr, null);
}
private JCExpression transform(Tree.BaseMemberOrTypeExpression expr, TermTransformer transformer) {
return transformMemberExpression(expr, null, transformer);
}
// Type members
public JCExpression transform(Tree.QualifiedTypeExpression expr) {
if (expr.getPrimary() instanceof Tree.BaseTypeExpression) {
Tree.BaseTypeExpression primary = (Tree.BaseTypeExpression)expr.getPrimary();
return transformMemberReference(expr, primary);
} else if (expr.getPrimary() instanceof Tree.QualifiedTypeExpression) {
Tree.QualifiedTypeExpression primary = (Tree.QualifiedTypeExpression)expr.getPrimary();
return transformMemberReference(expr, primary);
}
return transform(expr, null);
}
public JCExpression transform(Tree.BaseTypeExpression expr) {
return transform(expr, null);
}
private JCExpression transform(Tree.QualifiedTypeExpression expr, TermTransformer transformer) {
if (expr.getMemberOperator() instanceof Tree.SafeMemberOp) {
return transformSafeMemberOperator(expr, transformer);
}
if (expr.getMemberOperator() instanceof Tree.SpreadOp) {
return transformSpreadOperator(expr, transformer);
}
JCExpression primaryExpr = transformQualifiedMemberPrimary(expr);
return transformMemberExpression(expr, primaryExpr, transformer);
}
// Generic code for all primaries
public JCExpression transformTermForInvocation(Tree.Term term, TermTransformer transformer) {
if (term instanceof Tree.QualifiedMemberExpression) {
return transform((Tree.QualifiedMemberExpression)term, transformer);
} else if (term instanceof Tree.BaseMemberExpression) {
return transform((Tree.BaseMemberExpression)term, transformer);
} else if (term instanceof Tree.BaseTypeExpression) {
return transform((Tree.BaseTypeExpression)term, transformer);
} else if (term instanceof Tree.QualifiedTypeExpression) {
return transform((Tree.QualifiedTypeExpression)term, transformer);
} else {
// do not consider our term to be part of an invocation, we want it to be a Callable
boolean oldWi = withinInvocation(false);
JCExpression primaryExpr;
try{
primaryExpr = transformExpression(term);
if (transformer != null) {
primaryExpr = transformer.transform(primaryExpr, null);
}
}finally{
withinInvocation(oldWi);
}
return primaryExpr;
}
}
private JCExpression transformMemberExpression(Tree.StaticMemberOrTypeExpression expr, JCExpression primaryExpr,
TermTransformer transformer) {
JCExpression result = null;
// do not throw, an error will already have been reported
Declaration decl = expr.getDeclaration();
if (decl == null) {
return makeErroneous(expr, "compiler bug: expression with no declaration");
}
// Try to find the original declaration, in case we have conditionals that refine the type of objects without us
// creating a tmp variable (in which case we have a substitution for it)
while(decl instanceof TypedDeclaration){
TypedDeclaration typedDecl = (TypedDeclaration) decl;
if(!naming.isSubstituted(decl) && typedDecl.getOriginalDeclaration() != null){
decl = ((TypedDeclaration) decl).getOriginalDeclaration();
}else{
break;
}
}
// Make sure we're using the correct declaration in case of natives
// (the header might look like a field while the implementation is a getter)
if (decl.isNativeHeader()) {
Declaration d = ModelUtil.getNativeDeclaration(decl, Backend.Java);
if (d != null) {
decl = d;
}
}
// Explanation: primaryExpr and qualExpr both specify what is to come before the selector
// but the important difference is that primaryExpr is used for those situations where
// the result comes from the actual Ceylon code while qualExpr is used for those situations
// where we need to refer to synthetic objects (like wrapper classes for toplevel methods)
JCExpression qualExpr = null;
String selector = null;
// true for Java interop using fields, and for super constructor parameters, which must use
// parameters rather than getter methods
boolean mustUseField = false;
// true for default parameter methods
boolean mustUseParameter = false;
if (decl instanceof Functional
&& (!(decl instanceof Class) || ((Class)decl).getParameterList() != null)
&& (!(decl instanceof Function) || !decl.isParameter()
|| functionalParameterRequiresCallable((Function)decl, expr))
&& isFunctionalResult(expr.getTypeModel())) {
result = transformFunctional(expr, (Functional)decl, expectedType);
} else if (coerced
&& decl instanceof Value
&& isFunctionalResult(expr.getTypeModel())
&& checkForFunctionalInterface(expectedType) != null) {
result = transformFunctionalInterfaceBridge(expr, (Value)decl, expectedType);
} else if (coerced
&& decl instanceof Functional
&& decl.isParameter()
&& isFunctionalResult(expr.getTypeModel())
&& checkForFunctionalInterface(expectedType) != null) {
result = transformFunctional(expr, (Functional)decl, expectedType);
// } else if (coerced
// && decl instanceof Value
// && isJavaFunctionalInterfaceResult(expr.getTypeModel())
// && expectedType != null
// && isCeylonCallable(expectedType)) {
// result = transformCallableBridge(expr, (Value)decl, expectedType);
} else if (Decl.isGetter(decl)) {
// invoke the getter
if (decl.isToplevel()) {
primaryExpr = null;
qualExpr = naming.makeName((Value)decl, Naming.NA_FQ | Naming.NA_WRAPPER | Naming.NA_MEMBER);
selector = null;
} else if (Decl.withinClassOrInterface(decl) && !Decl.isLocalToInitializer(decl)) {
selector = naming.selector((Value)decl);
} else {
// method local attr
if (!isRecursiveReference(expr)) {
primaryExpr = naming.makeQualifiedName(primaryExpr, (Value)decl, Naming.NA_Q_LOCAL_INSTANCE);
}
selector = naming.selector((Value)decl);
}
} else if (Decl.isValueOrSharedOrCapturedParam(decl)) {
if (decl.isToplevel()) {
// ERASURE
if (isNullValue(decl)) {
result = makeNull();
} else if (isBooleanTrue(decl)) {
result = makeBoolean(true);
} else if (isBooleanFalse(decl)) {
result = makeBoolean(false);
} else {
// it's a toplevel attribute
primaryExpr = naming.makeName((TypedDeclaration)decl, Naming.NA_FQ | Naming.NA_WRAPPER);
selector = naming.selector((TypedDeclaration)decl);
}
} else if (Decl.isClassAttribute(decl) || Decl.isClassParameter(decl)) {
mustUseField = Decl.isJavaField(decl)
|| (isWithinSuperInvocation()
&& primaryExpr == null
&& withinSuperInvocation == decl.getContainer());
mustUseParameter = (primaryExpr == null && isWithinDefaultParameterExpression(decl.getContainer()));
if (mustUseField || mustUseParameter){
if(decl instanceof FieldValue) {
selector = ((FieldValue)decl).getRealName();
} else if (decl.isParameter()) {
selector = Naming.getAliasedParameterName(((Value)decl).getInitializerParameter());
} else {
selector = decl.getName();
}
} else {
// invoke the getter, using the Java interop form of Util.getGetterName because this is the only case
// (Value inside a Class) where we might refer to JavaBean properties
selector = naming.selector((TypedDeclaration)decl);
}
} else if (decl.isCaptured() || decl.isShared()) {
TypedDeclaration typedDecl = ((TypedDeclaration)decl);
TypeDeclaration typeDecl = typedDecl.getType().getDeclaration();
mustUseField = Decl.isBoxedVariable((TypedDeclaration)decl);
if (Decl.isLocalNotInitializer(typeDecl)
&& typeDecl.isAnonymous()
// we need the box if it's a captured object
&& !typedDecl.isSelfCaptured()) {
// accessing a local 'object' declaration, so don't need a getter
} else if (decl.isCaptured()
&& !((TypedDeclaration) decl).isVariable()
// captured objects are never variable but need the box
&& !typedDecl.isSelfCaptured()) {
// accessing a local that is not getter wrapped
} else {
primaryExpr = naming.makeQualifiedName(primaryExpr, (TypedDeclaration)decl, Naming.NA_Q_LOCAL_INSTANCE);
selector = naming.selector((TypedDeclaration)decl);
}
}
} else if (Decl.isMethodOrSharedOrCapturedParam(decl)) {
mustUseParameter = (primaryExpr == null
&& decl.isParameter()
&& isWithinDefaultParameterExpression(decl.getContainer()));
if (!decl.isParameter()
&& (Decl.isLocalNotInitializer(decl) || (Decl.isLocalToInitializer(decl) && ((Function)decl).isDeferred()))) {
primaryExpr = null;
int flags = Naming.NA_MEMBER;
if (!isRecursiveReference(expr)) {
// Only want to quote the method name
// e.g. enum.$enum()
flags |= Naming.NA_WRAPPER_UNQUOTED;
}else if(!isReferenceInSameScope(expr)){
// always qualify it with this
flags |= Naming.NA_WRAPPER | Naming.NA_WRAPPER_WITH_THIS;
}
qualExpr = naming.makeName((Function)decl, flags);
selector = null;
} else if (decl.isToplevel()) {
primaryExpr = null;
qualExpr = naming.makeName((Function)decl, Naming.NA_FQ | Naming.NA_WRAPPER | Naming.NA_MEMBER);
selector = null;
} else if (!isWithinInvocation()) {
selector = null;
} else if (decl.isClassOrInterfaceMember()){
selector = naming.selector((Function)decl);
} else {
selector = null;
}
}
boolean isCtor = decl instanceof Function && ((Function)decl).getTypeDeclaration() instanceof Constructor;
if (result == null) {
boolean useGetter = !(decl instanceof Function || isCtor) && !mustUseField && !mustUseParameter;
if (qualExpr == null && selector == null
&& !(isCtor)) {
useGetter = Decl.isClassAttribute(decl) && CodegenUtil.isErasedAttribute(decl.getName());
if (useGetter) {
selector = naming.selector((TypedDeclaration)decl);
} else {
selector = naming.substitute(decl);
}
}
if (qualExpr == null) {
qualExpr = primaryExpr;
}
// FIXME: Stef has a strong suspicion that the four next methods
// should be merged since they all add a this qualifier in different
// cases
if(!mustUseParameter){
qualExpr = addQualifierForObjectMembersOfInterface(expr, decl, qualExpr);
qualExpr = addInterfaceImplAccessorIfRequired(qualExpr, expr, decl);
qualExpr = addThisOrObjectQualifierIfRequired(qualExpr, expr, decl);
if (qualExpr == null
&& expr instanceof Tree.BaseMemberExpression
&& needDollarThis(expr)) {
qualExpr = makeQualifiedDollarThis((Tree.BaseMemberExpression)expr);
}
}
boolean isEnumeratedConstructorGetter = false;
if((decl instanceof Value && Decl.isEnumeratedConstructor((Value)decl))){
Class constructedClass = Decl.getConstructedClass(decl);
// See CeylonVisitor.transformSingletonConstructor for that logic
if(constructedClass.isToplevel() || constructedClass.isClassMember())
isEnumeratedConstructorGetter = true;
else{
// Local enumerated constructor values are boxed
useGetter = false; // local class will use a field
qualExpr = naming.makeQualifiedName(primaryExpr, (TypedDeclaration)decl, Naming.NA_Q_LOCAL_INSTANCE);
selector = naming.selector((TypedDeclaration)decl);
}
}
if (qualExpr == null
&& (decl.isStatic() || isEnumeratedConstructorGetter)
// make sure we only do this for things contained in a type, as otherwise
// it breaks for qualified calls to static methods in interfaces in Java 8
// it only breaks for interfaces because they are statically importable
// and not classes
&& decl.getContainer() instanceof TypeDeclaration) {
qualExpr = naming.makeTypeDeclarationExpression(null, (TypeDeclaration)decl.getContainer(), DeclNameFlag.QUALIFIED);
}
if (Decl.isPrivateAccessRequiringUpcast(expr)) {
qualExpr = makePrivateAccessUpcast(expr, qualExpr);
}
if (transformer != null) {
if (decl instanceof TypedDeclaration
&& ((TypedDeclaration)decl).getType().isTypeConstructor()) {
// This is a bit of a hack, but we're "invoking a type constructor"
// so recurse to get the applied expression.
qualExpr = transformMemberExpression(expr, qualExpr, null);
selector = null;
}
result = transformer.transform(qualExpr, selector);
} else {
Tree.Primary qmePrimary = null;
if (expr instanceof Tree.QualifiedMemberOrTypeExpression) {
qmePrimary = ((Tree.QualifiedMemberOrTypeExpression)expr).getPrimary();
}
boolean safeMemberJavaArray = expr instanceof Tree.QualifiedMemberExpression
&& ((Tree.QualifiedMemberExpression)expr).getMemberOperator() instanceof Tree.SafeMemberOp
&& isJavaArray(qmePrimary.getTypeModel());
if ((safeMemberJavaArray || Decl.isValueTypeDecl(qmePrimary))
// Safe operators always work on boxed things, so don't use value types
&& (safeMemberJavaArray || (expr instanceof Tree.QualifiedMemberOrTypeExpression == false)
|| ((Tree.QualifiedMemberOrTypeExpression)expr).getMemberOperator() instanceof Tree.MemberOp)
// We never want to use value types on boxed things, unless they are java arrays
&& (CodegenUtil.isUnBoxed(qmePrimary) || isJavaArray(qmePrimary.getTypeModel()))
// Java arrays length property does not go via value types
&& (!isJavaArray(qmePrimary.getTypeModel())
|| (!"length".equals(selector) && !"hashCode".equals(selector)))) {
JCExpression primTypeExpr = makeJavaType(qmePrimary.getTypeModel(), JT_NO_PRIMITIVES | JT_VALUE_TYPE);
result = makeQualIdent(primTypeExpr, selector);
result = make().Apply(List.nil(),
result,
List.of(qualExpr));
} else if (expr instanceof Tree.QualifiedMemberOrTypeExpression
&& isThrowableMessage((Tree.QualifiedMemberOrTypeExpression)expr)) {
result = utilInvocation().throwableMessage(qualExpr);
} else if (expr instanceof Tree.QualifiedMemberOrTypeExpression
&& isThrowableSuppressed((Tree.QualifiedMemberOrTypeExpression)expr)) {
result = utilInvocation().suppressedExceptions(qualExpr);
} else {
result = makeQualIdent(qualExpr, selector);
if (useGetter) {
result = make().Apply(List.nil(),
result,
List.nil());
}
}
}
}
if (transformer == null
&& decl instanceof TypedDeclaration
&& ((TypedDeclaration)decl).getType().isTypeConstructor()
&& !expr.getTypeArguments().getTypeModels().isEmpty()) {
// applying a type constructor
ListBuffer tds = new ListBuffer();
for (Type t : expr.getTypeArguments().getTypeModels()) {
tds.add(makeReifiedTypeArgument(t));
}
result = make().Apply(
null,
makeQualIdent(result, Naming.Unfix.apply.toString()),
List.of(make().NewArray(make().Type(syms().ceylonTypeDescriptorType),
List.nil(),
tds.toList())));
}
return result;
}
/**
* We may need to force a qualified this prefix (direct or outer) in the following cases:
*
* - Required because of mixin inheritance with different type arguments (the same is already
* done for qualified references, but not for direct references)
* - The compiler generates anonymous local classes for things like
* Callables and Comprehensions. When referring to a member foo
* within one of those things we need a qualified {@code this}
* to ensure we're accessing the outer instances member, not
* a member of the anonymous local class that happens to have the same name.
*/
private JCExpression addThisOrObjectQualifierIfRequired(
JCExpression qualExpr, Tree.StaticMemberOrTypeExpression expr,
Declaration decl) {
// find out the real target
Declaration typeDecl;
if(Decl.isConstructor(decl))
typeDecl = Decl.getConstructedClass(decl);
else
typeDecl = decl;
if (qualExpr == null
// statics are not members that can be inherited
&& !decl.isStatic()
&& (!Decl.isConstructor(decl) || !Decl.isConstructor(typeDecl))
&& typeDecl.isMember()
// dodge variable refinements with assert/is (these will be turned to locals
// and have a name mapping)
&& expr.getTarget().getDeclaration() == decl
&& !Decl.isLocalToInitializer(typeDecl)
&& !isWithinSuperInvocation()) {
// First check whether the expression is captured from an enclosing scope
TypeDeclaration outer = Decl.getOuterScopeOfMemberInvocation(expr, typeDecl);
if (outer != null) {
Type targetType = expr.getTarget().getQualifyingType();
Type declarationContainerType = ((TypeDeclaration)outer).getType();
// check if we need a variance cast
VarianceCastResult varianceCastResult = getVarianceCastResult(targetType, declarationContainerType);
// if we are within a comprehension body, or if we need a variance cast
if(isWithinSyntheticClassBody() || varianceCastResult != null){
if (decl.isShared() && outer instanceof Interface) {
// always prefer qualified
qualExpr = makeQualifiedDollarThis(declarationContainerType);
} else {
// Class or companion class,
qualExpr = naming.makeQualifiedThis(makeJavaType(((TypeDeclaration)outer).getType(),
JT_RAW | (outer instanceof Interface ? JT_COMPANION : 0)));
}
// add the variance cast if required
if(varianceCastResult != null){
qualExpr = applyVarianceCasts(qualExpr, targetType, varianceCastResult, 0);
}
}
} else if (typeDecl.isClassOrInterfaceMember()){
ClassOrInterface container;
if(((ClassOrInterface)typeDecl.getContainer()).isAnonymous()
&& ((ClassOrInterface)typeDecl.getContainer()).isToplevel()) {
// easy
container = (Class)typeDecl.getContainer();
}else{
// find the import
Import foundImport = statementGen().findImport(expr, decl);
container = (Class) foundImport.getTypeDeclaration();
}
Value value = (Value)((Package)container.getContainer()).getMember(container.getName(), null, false);
qualExpr = make().Apply(null,
naming.makeName(value, Naming.NA_FQ | Naming.NA_WRAPPER | Naming.NA_MEMBER),
List.nil());
} else if (decl.isMember() && !expr.getStaticMethodReference()) {
throw new BugException(expr, decl.getQualifiedNameString() + " was unexpectedly a member");
}
}
return qualExpr;
}
/**
* §3.2.2 Every interface is a subtype of c.l.Object, so
* within an Interface {@code string} means {@code $this.toString()}
* @param expr
* @param decl
* @param qualExpr
* @return
*/
// Interface we must use $this's implementation of equals, hash and string
private JCExpression addQualifierForObjectMembersOfInterface(
Tree.StaticMemberOrTypeExpression expr, Declaration decl,
JCExpression qualExpr) {
if (expr instanceof Tree.BaseMemberExpression
&& qualExpr == null
&& typeFact().getObjectDeclaration().equals(Decl.getClassOrInterfaceContainer(decl))) {
Scope scope = expr.getScope();
while (Decl.isLocalNotInitializerScope(scope)) {
scope = scope.getContainer();
}
if (scope instanceof Interface) {
qualExpr = naming.makeQuotedThis();
}
}
return qualExpr;
}
/**
* Determines whether we need to generate an AbstractCallable when taking
* a method reference to a method that's declared as a FunctionalParameter
*/
private boolean functionalParameterRequiresCallable(Function functionalParameter, Tree.StaticMemberOrTypeExpression expr) {
if (!functionalParameter.isParameter()) {
throw new BugException();
}
boolean hasMethod = JvmBackendUtil.createMethod(functionalParameter);
if (!hasMethod) {
// A functional parameter that's not method wrapped will already be Callable-wrapped
return false;
}
// Optimization: If we're in a scope where the Callable field is visible
// we don't need to create a method ref
Scope scope = expr.getScope();
while (true) {
if (scope instanceof Package) {
break;
}
if (scope.equals(functionalParameter.getContainer())) {
return false;
}
scope = scope.getContainer();
}
// Otherwise we do require an AbstractCallable.
return true;
}
//
// Array access
private JCExpression addInterfaceImplAccessorIfRequired(JCExpression qualExpr, Tree.StaticMemberOrTypeExpression expr, Declaration decl) {
// Partial fix for https://github.com/ceylon/ceylon-compiler/issues/1023
// For interfaces we sometimes need to access either the interface instance or its $impl class
if (decl instanceof Constructor) {
decl = (Class)Decl.container(decl);
}
Scope declContainer = Decl.container(decl);
if(qualExpr != null
// this is only for interface containers
&& declContainer instanceof Interface
// we only ever need the $impl if the declaration is not shared
&& !decl.isShared()
&& (!(expr instanceof Tree.QualifiedMemberExpression)
|| !isSuperOrSuperOf(((Tree.QualifiedMemberExpression)expr).getPrimary()))){
Interface declaration = (Interface) declContainer;
// access the interface $impl instance
qualExpr = naming.makeCompanionAccessorCall(qualExpr, declaration);
// When the interface is local the accessor returns Object
// so we need to cast it to the type of the companion
if (Decl.isAncestorLocal(declaration)) {
Type type;
// try to find the best type
if(expr instanceof Tree.QualifiedMemberOrTypeExpression)
type = ((Tree.QualifiedMemberOrTypeExpression) expr).getPrimary().getTypeModel();
else
type = declaration.getType();
qualExpr = make().TypeCast(makeJavaType(type, JT_COMPANION), qualExpr);
}
}
return qualExpr;
}
private JCExpression makeQualifiedDollarThis(Tree.BaseMemberExpression expr) {
Declaration decl = expr.getDeclaration();
Interface interf = (Interface) Decl.getClassOrInterfaceContainer(decl);
// find the target container interface that is or satisfies the given interface
Scope scope = expr.getScope();
boolean needsQualified = false;
while(scope != null){
if(scope instanceof Interface){
if(Decl.equalScopeDecl(scope, interf) || ((Interface)scope).inherits(interf)){
break;
}
// we only need to qualify it if we're aiming for a $this of an outer interface than the interface we are caught in
needsQualified = true;
}
scope = scope.getContainer();
}
if(!needsQualified)
return naming.makeQuotedThis();
interf = (Interface) scope;
return makeQualifiedDollarThis(interf.getType());
}
private JCExpression makeQualifiedDollarThis(Type targetType){
JCExpression qualifiedCompanionThis = naming.makeQualifiedThis(makeJavaType(targetType, JT_COMPANION | JT_RAW));
return naming.makeQualifiedDollarThis(qualifiedCompanionThis);
}
boolean needDollarThis(Tree.StaticMemberOrTypeExpression expr) {
if (expr instanceof Tree.BaseMemberExpression
|| expr instanceof Tree.BaseTypeExpression) {
// We need to add a `$this` prefix to the member expression if:
// * The member was declared on an interface I and
// * The member is being used in the companion class of I or
// // REMOVED: some subinterface of I, and
// some member type of I, and
// * The member is shared (non-shared means its only on the companion class)
// FIXME: https://github.com/ceylon/ceylon-compiler/issues/1019
final Declaration decl = expr.getDeclaration();
if(!Decl.withinInterface(decl))
return false;
// Find the method/getter/setter where the expr is being used
Scope scope = expr.getScope();
while (scope != null){
// Is it being used in an interface (=> impl)
if(scope instanceof Interface && ((Interface) scope).getType().isSubtypeOf(scope.getDeclaringType(decl))) {
return decl.isShared();
}
scope = scope.getContainer();
}
}
return false;
}
private boolean needDollarThis(Scope scope) {
while (Decl.isLocalNotInitializerScope(scope)) {
scope = scope.getContainer();
}
return scope instanceof Interface;
}
/**
* Abstract method pattern for transforming
* indexed expressions: {@code foo[index]}
*/
abstract class AbstractIndexTransformer {
final Type primaryType;
final Type rightType;
final Type leftType;
final Type elementType;
AbstractIndexTransformer(Tree.IndexExpression access,
Type leftType,
Type rightType,
Type elementType) {
this.primaryType = access.getPrimary().getTypeModel();
this.leftType = leftType;
this.rightType = rightType;
this.elementType = elementType;
}
protected abstract String getGetterName();
protected abstract BoxingStrategy getIndexBoxing();
protected abstract Type leftTypeForGetCall();
protected JCExpression transformPrimary(Tree.IndexExpression indexExpr) {
final JCExpression lhs;
final String getter = getGetterName();
if(isSuper(indexExpr.getPrimary())) {
Declaration member = primaryType.getDeclaration().getMember(getter, null, false);
TypeDeclaration leftDeclaration = (TypeDeclaration) member.getContainer();
lhs = transformSuper(indexExpr, leftDeclaration);
} else if (isSuperOf(indexExpr.getPrimary())) {
lhs = transformSuperOf(indexExpr, indexExpr.getPrimary(), getter);
} else{
Type leftTypeForGetCall = leftTypeForGetCall();
lhs = transformExpression(indexExpr.getPrimary(), BoxingStrategy.BOXED, leftTypeForGetCall);
}
return lhs;
}
public JCExpression transform(Tree.IndexExpression indexExpr) {
JCExpression result = transformIndexed(indexExpr);
// Because tuple index access has the type of the indexed element
// (not the union of types in the sequential) a typecast may be required.
Type expectedType = indexExpr.getTypeModel();
int flags = 0;
if(!expectedType.isExactly(elementType)
// could be optional too, for regular Correspondence item access
&& !expectedType.isExactly(typeFact().getOptionalType(elementType)))
flags |= EXPR_DOWN_CAST;
result = applyErasureAndBoxing(result,
elementType,
CodegenUtil.hasTypeErased(indexExpr), true, BoxingStrategy.BOXED,
expectedType, flags);
return result;
}
protected JCExpression transformIndexed(Tree.IndexExpression indexExpr) {
JCExpression primaryExpr = makeSelect(transformPrimary(indexExpr), getGetterName());
JCExpression index = transformIndex((Tree.Element)indexExpr.getElementOrRange());
JCExpression result = at(indexExpr).Apply(List.nil(),
primaryExpr, List.of(index));
return result;
}
protected JCExpression transformIndex(Tree.Element element) {
// do the index
BoxingStrategy indexBs = getIndexBoxing();
JCExpression index = transformExpression(element.getExpression(), indexBs, rightType);
return index;
}
}
class CorrespondenceIndexTransformer extends AbstractIndexTransformer {
private boolean useGetFromFirst;
CorrespondenceIndexTransformer(Tree.IndexExpression indexExpr, Type leftType, Type rightType, Type sequentialElementType) {
super(indexExpr, leftType, rightType, sequentialElementType);
boolean isOnList = primaryType.isSubtypeOf(typeFact().getListDeclaration().appliedType(
null, Collections.singletonList(typeFact().getAnythingType())));
Tree.Primary primary = indexExpr.getPrimary();
boolean isSuper = isSuper(primary);
boolean isOnSuper = isSuper || isSuperOf(primary);
if (isOnList) {
// can we use getFromFirst() to avoid boxing the index?
useGetFromFirst = true;
if (isOnSuper) {
// this is super special: if we use the optim and call super we need to make sure that "getFromFirst" already
// has a concrete super implementation
Declaration member = primaryType.getDeclaration().getMember("getFromFirst", null, false);
if(member == null || member.isFormal()){
useGetFromFirst = false;
}
}
} else {
useGetFromFirst = false;
}
}
@Override
protected String getGetterName() {
return useGetFromFirst ? "getFromFirst": "get";
}
@Override
protected BoxingStrategy getIndexBoxing() {
return useGetFromFirst ? BoxingStrategy.UNBOXED : BoxingStrategy.BOXED;
}
@Override
protected Type leftTypeForGetCall() {
return useGetFromFirst ? primaryType.getSupertype(typeFact().getListDeclaration()) : leftType;
}
}
class JavaListIndexTransformer extends AbstractIndexTransformer {
JavaListIndexTransformer(Tree.IndexExpression indexExpr, Type leftType, Type rightType, Type sequentialElementType) {
super(indexExpr, leftType, rightType, sequentialElementType);
}
@Override
protected String getGetterName() {
return "get";
}
@Override
protected BoxingStrategy getIndexBoxing() {
return BoxingStrategy.UNBOXED;
}
@Override
protected Type leftTypeForGetCall() {
return leftType;
}
protected JCExpression transformIndexed(Tree.IndexExpression indexExpr) {
JCExpression listExpr = transformPrimary(indexExpr);
JCExpression index = transformIndex((Tree.Element)indexExpr.getElementOrRange());
SyntheticName listName = naming.temp("list");
SyntheticName indexName = naming.temp("index");
return at(indexExpr).LetExpr(
List.of(
makeVar(listName, makeJavaType(leftType), listExpr),
makeVar(indexName, make().Type(syms().intType), index)
),
make().Conditional(
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.GE, indexName.makeIdent(), make().Literal(0)),
make().Binary(JCTree.Tag.LT, indexName.makeIdent(), make().Apply(null,
naming.makeQualIdent(listName.makeIdent(), "size"),
List.nil()))),
at(indexExpr).Apply(List.nil(),
makeSelect(listName.makeIdent(), getGetterName()),
List.of(indexName.makeIdent())),
makeNull()));
}
}
class JavaMapIndexTransformer extends AbstractIndexTransformer {
JavaMapIndexTransformer(Tree.IndexExpression indexExpr, Type leftType, Type rightType, Type elementType) {
super(indexExpr, leftType, rightType, elementType);
}
@Override
protected String getGetterName() {
return "get";
}
@Override
protected BoxingStrategy getIndexBoxing() {
return BoxingStrategy.BOXED;
}
@Override
protected Type leftTypeForGetCall() {
return leftType;
}
}
class JavaArrayIndexTransformer extends AbstractIndexTransformer {
private Type returnType;
JavaArrayIndexTransformer(Tree.IndexExpression indexExpr, Type leftType, Type rightType, Type elementType) {
super(indexExpr, leftType, rightType, elementType);
returnType = indexExpr.getTypeModel();
}
@Override
protected JCExpression transformIndexed(Tree.IndexExpression indexExpr) {
JCExpression arrayExpr = transformPrimary(indexExpr);
JCExpression index = transformIndex((Tree.Element)indexExpr.getElementOrRange());
if(typeFact().isOptionalType(returnType)){
SyntheticName listName = naming.temp("array");
SyntheticName indexName = naming.temp("index");
return at(indexExpr).LetExpr(
List.of(
makeVar(listName, makeJavaType(leftType, JT_NO_PRIMITIVES), arrayExpr),
makeVar(indexName, make().Type(syms().intType), index)
),
make().Conditional(
make().Binary(JCTree.Tag.AND,
make().Binary(JCTree.Tag.GE, indexName.makeIdent(), make().Literal(0)),
make().Binary(JCTree.Tag.LT, indexName.makeIdent(),
naming.makeQualIdent(listName.makeIdent(), "length"))),
make().Indexed(listName.makeIdent(), indexName.makeIdent()),
makeNull()));
}else{
return at(indexExpr).Indexed(arrayExpr, index);
}
}
@Override
protected String getGetterName() {
return null;
}
@Override
protected BoxingStrategy getIndexBoxing() {
return BoxingStrategy.UNBOXED;
}
@Override
protected Type leftTypeForGetCall() {
return leftType;
}
}
public JCExpression transform(Tree.IndexExpression indexedExpr) {
// depends on the operator
Tree.ElementOrRange elementOrRange = indexedExpr.getElementOrRange();
if (elementOrRange instanceof Tree.Element) {
// foo[index] -- foo could be a Correspondence, Java List or Java Map
final AbstractIndexTransformer transformer;
final Type primaryType = indexedExpr.getPrimary().getTypeModel().resolveAliases();
Type leftType = primaryType.getSupertype(typeFact().getCorrespondenceDeclaration());
if (leftType != null) {
Type rightType = getTypeArgument(leftType, 0);
transformer = new CorrespondenceIndexTransformer(indexedExpr, leftType, rightType, getTypeArgument(leftType, 1));
} else {
leftType = primaryType.getSupertype(typeFact().getJavaListDeclaration());
if (leftType != null) {
Type rightType = typeFact().getIntegerType();
if (rightType.isCached()) {
rightType = rightType.clone();
}
rightType.setUnderlyingType("int");
transformer = new JavaListIndexTransformer(indexedExpr, leftType, rightType, getTypeArgument(leftType, 0));
} else {
leftType = primaryType.getSupertype(typeFact().getJavaMapDeclaration());
if (leftType != null) {
Type rightType = getTypeArgument(leftType, 0);
transformer = new JavaMapIndexTransformer(indexedExpr, leftType, rightType, getTypeArgument(leftType, 1));
} else if (isJavaArray(primaryType)) {
Type rightType = typeFact().getIntegerType();
if (rightType.isCached()) {
rightType = rightType.clone();
}
rightType.setUnderlyingType("int");
Type elementType;
if (isJavaObjectArray(primaryType)) {
leftType = primaryType.getSupertype(typeFact().getJavaObjectArrayDeclaration());
elementType = getTypeArgument(leftType, 0);
} else if (JvmBackendUtil.isJavaBooleanArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaBooleanArrayDeclaration().getType();
elementType = typeFact().getBooleanType();
} else if (JvmBackendUtil.isJavaByteArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaByteArrayDeclaration().getType();
elementType = typeFact().getByteType();
} else if (JvmBackendUtil.isJavaShortArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaShortArrayDeclaration().getType();
elementType = typeFact().getIntegerType();
} else if (JvmBackendUtil.isJavaIntArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaIntArrayDeclaration().getType();
elementType = typeFact().getIntegerType();
} else if (JvmBackendUtil.isJavaLongArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaLongArrayDeclaration().getType();
elementType = typeFact().getIntegerType();
} else if (JvmBackendUtil.isJavaFloatArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaFloatArrayDeclaration().getType();
elementType = typeFact().getFloatType();
} else if (JvmBackendUtil.isJavaDoubleArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaDoubleArrayDeclaration().getType();
elementType = typeFact().getFloatType();
} else if (JvmBackendUtil.isJavaCharArray(primaryType.getDeclaration())) {
leftType = typeFact().getJavaCharArrayDeclaration().getType();
elementType = typeFact().getCharacterType();
} else {
return makeErroneous(indexedExpr, "Unsupported primary for indexed expression");
}
transformer = new JavaArrayIndexTransformer(indexedExpr, leftType, rightType, elementType);
} else {
return makeErroneous(indexedExpr, "Unsupported primary for indexed expression");
}
}
}
return transformer.transform(indexedExpr);
} else {
// foo[start:end] or foo[start:length]
Type primaryType = indexedExpr.getPrimary().getTypeModel();
Type leftType = primaryType.getSupertype(typeFact().getRangedDeclaration());
Type rightType = getTypeArgument(leftType, 0);
Tree.ElementRange range = (Tree.ElementRange) indexedExpr.getElementOrRange();
// do the indices
JCExpression start = transformExpression(range.getLowerBound(), BoxingStrategy.BOXED, rightType);
// is this a span or segment?
String method;
final List args;
if (range.getLowerBound() != null
&& range.getLength() != null) {
method = "measure";
JCExpression length = transformExpression(range.getLength(), BoxingStrategy.UNBOXED, typeFact().getIntegerType());
args = List.of(start, length);
} else if (range.getLowerBound() == null) {
method = "spanTo";
JCExpression end = transformExpression(range.getUpperBound(), BoxingStrategy.BOXED, rightType);
args = List.of(end);
} else if (range.getUpperBound() == null) {
method = "spanFrom";
args = List.of(start);
} else if (range.getLowerBound() != null
&& range.getUpperBound() != null) {
method = "span";
JCExpression end = transformExpression(range.getUpperBound(), BoxingStrategy.BOXED, rightType);
args = List.of(start, end);
} else {
method = "unknown";
args = List.of(makeErroneous(range, "compiler bug: unhandled range"));
}
JCExpression lhs;
Tree.Primary primary = indexedExpr.getPrimary();
boolean isSuper = isSuper(primary);
if(isSuper || isSuperOf(primary)){
Declaration member = primaryType.getDeclaration().getMember(method, null, false);
TypeDeclaration leftDeclaration = (TypeDeclaration) member.getContainer();
if(isSuper)
lhs = transformSuper(indexedExpr, leftDeclaration);
else
lhs = transformSuperOf(indexedExpr, indexedExpr.getPrimary(), method);
}else{
int flags = 0;
// this is pretty much disgusting, but all I found. Given a Ranged type
// we don't notice that this type can't be cast to without a raw cast because it has constrained
// type params in its hierarchy. hasConstrainedTypeParameters only checks that type, and we want
// to check Ranged instead, which has those constraints, which is why we
// do it on the declaration type. Honestly this should probably rather be an inheritance check in
// applyErasureAndCasts but that'd be costly and complex and likely introduce lots of other issues.
// See https://github.com/ceylon/ceylon/issues/6365
if(leftType.getDeclaration() instanceof ClassOrInterface
// even more disgusting: https://github.com/ceylon/ceylon/issues/6450
&& !isCeylonString(primaryType)
&& hasConstrainedTypeParameters(leftType.getDeclaration().getType()))
flags |= EXPR_EXPECTED_TYPE_HAS_CONSTRAINED_TYPE_PARAMETERS;
lhs = transformExpression(indexedExpr.getPrimary(), BoxingStrategy.BOXED, leftType, flags);
}
JCExpression result;
// Because tuple open span access has the type of the indexed element
// (not a sequential of the union of types in the ranged) a typecast may be required.
Type rangedSpanType = getTypeArgument(leftType, 2);
Type expectedType = indexedExpr.getTypeModel();
int flags = 0;
if(!expectedType.isExactly(rangedSpanType)){
flags |= EXPR_DOWN_CAST;
// make sure we barf properly if we missed a heuristics
if(method.equals("spanFrom")){
// make a "Util.(lhs, start, end)" call
at(indexedExpr);
result = utilInvocation().tuple_spanFrom(args.prepend(lhs));
}else{
result = makeErroneous(indexedExpr, "compiler bug: only the spanFrom method should be specialised for Tuples");
}
}else{
// make a "lhs.(start, end)" call
result = at(indexedExpr).Apply(List.nil(),
makeSelect(lhs, method), args);
}
result = applyErasureAndBoxing(result,
rangedSpanType,
CodegenUtil.hasTypeErased(indexedExpr), true, BoxingStrategy.BOXED,
expectedType, flags);
return result;
}
}
//
// Assignment
public JCExpression transform(Tree.AssignOp op) {
return transformAssignment(op, op.getLeftTerm(), op.getRightTerm());
}
private JCExpression transformAssignment(Node op, Tree.Term leftTerm, Tree.Term rightTerm) {
// Remember and disable inStatement for RHS
boolean tmpInStatement = inStatement;
inStatement = false;
// FIXME: can this be anything else than a Tree.MemberOrTypeExpression or Tree.ParameterizedExpression or Tree.IndexExpression?
final JCExpression rhs;
BoxingStrategy boxing;
if (leftTerm instanceof Tree.MemberOrTypeExpression) {
TypedDeclaration decl = (TypedDeclaration) ((Tree.MemberOrTypeExpression)leftTerm).getDeclaration();
boxing = CodegenUtil.getBoxingStrategy(decl);
if (decl instanceof Value) {
Value val = (Value)decl;
if (val.getSetter() != null && val.getSetter().getUnboxed() != null) {
boxing = CodegenUtil.getBoxingStrategy(val.getSetter());
}
}
Type targetType = tmpInStatement ? leftTerm.getTypeModel() : rightTerm.getTypeModel();
// if we're dealing with widening do not trust the type of the declaration and get the real type
if(CodegenUtil.hasUntrustedType(decl)){
TypedReference typedRef = (TypedReference) ((Tree.MemberOrTypeExpression)leftTerm).getTarget();
TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
targetType = nonWideningType(typedRef, nonWideningTypedRef);
}
int flags = decl.hasUncheckedNullType() ? EXPR_TARGET_ACCEPTS_NULL : 0;
flags |= leftTerm.getSmall() && !rightTerm.getSmall() ? EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK : 0;
if (decl.isMember()
&& useFieldInAssignment(op, null, decl)
&& !Decl.isLocalToInitializer(decl)
&& useJavaBox(decl, ((TypedDeclaration)decl.getRefinedDeclaration()).getType())) {
boxing = BoxingStrategy.JAVA;
flags |= EXPR_HAS_NULL_CHECK_FENCE;
}
rhs = transformExpression(rightTerm, boxing, targetType, flags);
} else if (leftTerm instanceof Tree.IndexExpression) {
Tree.IndexExpression idx = (Tree.IndexExpression)leftTerm;
Unit unit = op.getUnit();
Type pt = idx.getPrimary().getTypeModel();
boxing = (unit.isJavaPrimitiveArrayType(pt)) ? BoxingStrategy.UNBOXED : BoxingStrategy.BOXED;
rhs = transformExpression(rightTerm, boxing, idx.getTypeModel(), 0);
} else if (leftTerm instanceof Tree.ParameterizedExpression) {
boxing = CodegenUtil.getBoxingStrategy(leftTerm);
Tree.ParameterizedExpression paramExpr = (Tree.ParameterizedExpression)leftTerm;
FunctionOrValue decl = (FunctionOrValue) ((Tree.MemberOrTypeExpression)paramExpr.getPrimary()).getDeclaration();
CallableBuilder callableBuilder = CallableBuilder.anonymous(
gen(),
paramExpr,
decl,
(Tree.Expression)rightTerm,
paramExpr.getParameterLists(),
paramExpr.getPrimary().getTypeModel(),
decl instanceof Function ? !((Function)decl).isDeferred() : false);
rhs = callableBuilder.build();
} else {
return makeErroneous(leftTerm, "compiler bug: left term of type '" + leftTerm.getClass().getSimpleName() + "' is not yet supported");
}
if (tmpInStatement) {
return makeAssignment(op, leftTerm, transformAssignmentLhs(op, leftTerm), rhs);
} else {
Type valueType = rightTerm.getTypeModel();
if(isNull(valueType))
valueType = leftTerm.getTypeModel();
return transformAssignAndReturnOperation(op, leftTerm, boxing == BoxingStrategy.BOXED,
leftTerm.getTypeModel(), valueType, new AssignAndReturnOperationFactory(){
@Override
public JCExpression getNewValue(JCExpression previousValue) {
return rhs;
}
});
}
}
private JCExpression transformAssignmentLhs(final Node op, Tree.Term leftTerm) {
// left hand side can be either BaseMemberExpression, QualifiedMemberExpression or array access (M2)
// TODO: array access (M2)
JCExpression lhs = null;
if(leftTerm instanceof Tree.BaseMemberExpression) {
if (needDollarThis((Tree.BaseMemberExpression)leftTerm)) {
lhs = naming.makeQuotedThis();
}
} else if(leftTerm instanceof Tree.QualifiedMemberExpression) {
Tree.QualifiedMemberExpression qualified = ((Tree.QualifiedMemberExpression)leftTerm);
if (isPackageQualified(qualified)) {
lhs = null;
} else if (isSuper(qualified.getPrimary())) {
lhs = transformSuper(qualified);
} else if (isSuperOf(qualified.getPrimary())) {
lhs = transformSuperOf(qualified, qualified.getPrimary(), qualified.getDeclaration().getName());
} else if (isThis(qualified.getPrimary())
&& !qualified.getDeclaration().isCaptured()
&& !qualified.getDeclaration().isShared() ) {
lhs = null;
} else if (!qualified.getDeclaration().isStatic()) {
lhs = transformExpression(qualified.getPrimary(), BoxingStrategy.BOXED, qualified.getTarget().getQualifyingType());
if (Decl.isPrivateAccessRequiringUpcast(qualified)) {
lhs = makePrivateAccessUpcast(qualified, lhs);
}
} else {
lhs = makeJavaType(((ClassOrInterface)qualified.getDeclaration().getContainer()).getType(), JT_RAW);
}
} else if(leftTerm instanceof Tree.ParameterizedExpression) {
lhs = null;
} else if(leftTerm instanceof Tree.IndexExpression) {
lhs = null;
} else {
return makeErroneous(op, "compiler bug: "+op.getNodeType() + " is not yet supported");
}
return qualifyLhs(op, leftTerm, lhs);
}
protected JCExpression qualifyLhs(final Node op, Tree.Term leftTerm, JCExpression lhs) {
TypedDeclaration decl;
if (leftTerm instanceof Tree.StaticMemberOrTypeExpression) {
decl = (TypedDeclaration) ((Tree.StaticMemberOrTypeExpression)leftTerm).getDeclaration();
lhs = addInterfaceImplAccessorIfRequired(lhs, (Tree.StaticMemberOrTypeExpression) leftTerm, decl);
lhs = addThisOrObjectQualifierIfRequired(lhs, (Tree.StaticMemberOrTypeExpression)leftTerm, decl);
} else if (leftTerm instanceof Tree.IndexExpression) {
// in this case lhs is null anyway, so let's discard it
return lhs;
} else if (leftTerm instanceof Tree.ParameterizedExpression) {
// instanceof Tree.ParameterizedExpression
decl = (TypedDeclaration) ((Tree.MemberOrTypeExpression)((Tree.ParameterizedExpression)leftTerm).getPrimary()).getDeclaration();
} else {
return makeErroneous(op, "Unexpected LHS in assignment: " + leftTerm.getNodeType());
}
if (decl.isToplevel()) {
// must use top level setter
lhs = naming.makeName(decl, Naming.NA_FQ | Naming.NA_WRAPPER);
} else if (Decl.isGetter(decl)) {
if (Decl.isTransient(decl) && !decl.isVariable()) {
} else {
// must use the setter
if (Decl.isLocal(decl)) {
lhs = naming.makeQualifiedName(lhs, decl, Naming.NA_WRAPPER | Naming.NA_SETTER);
} else if (decl.isStatic()) {
lhs = naming.makeTypeDeclarationExpression(null, (TypeDeclaration)decl.getContainer(), DeclNameFlag.QUALIFIED);
}
}
} else if (decl instanceof Function && Decl.isDeferred(decl)) {
} else if ((decl.isVariable() || decl.isLate()) && (Decl.isClassAttribute(decl))) {
} else if (decl.isVariable() && (decl.isCaptured() || decl.isShared())) {
// must use the qualified setter
if (Decl.isBoxedVariable(decl)) {
} else if (Decl.isLocalNotInitializer(decl)) {
lhs = naming.makeQualifiedName(lhs, decl, Naming.NA_WRAPPER);
}
}
return lhs;
}
private JCExpression adjustRhs(TypedDeclaration decl, JCExpression rhs) {
if (decl.isToplevel()) {
} else if (Decl.isGetter(decl)) {
if (Decl.isTransient(decl) && !decl.isVariable()) {
rhs = gen().transformAttributeGetter(decl, rhs);
}
}
return rhs;
}
private JCExpression makeAssignment(Node op, Tree.Term leftTerm,
final JCExpression lhs,
JCExpression rhs) {
TypedDeclaration decl;
if (leftTerm instanceof Tree.StaticMemberOrTypeExpression) {
decl = (TypedDeclaration) ((Tree.StaticMemberOrTypeExpression)leftTerm).getDeclaration();
} else if (leftTerm instanceof Tree.IndexExpression) {
// in this case lhs is null anyway, so let's discard it
return transformIndexAssignment(op, (Tree.IndexExpression)leftTerm, rhs);
} else if (leftTerm instanceof Tree.ParameterizedExpression) {
// instanceof Tree.ParameterizedExpression
decl = (TypedDeclaration) ((Tree.MemberOrTypeExpression)((Tree.ParameterizedExpression)leftTerm).getPrimary()).getDeclaration();
} else {
return makeErroneous(op, "Unexpected LHS in assignment: " + leftTerm.getNodeType());
}
rhs = adjustRhs(decl, rhs);
JCExpression varExpr = makeAssignmentVariable(op, lhs, decl);
JCExpression result;
if (varExpr != null) {
result = at(op).Assign(varExpr, rhs);
} else {
ListBuffer typeArguments = new ListBuffer();
ListBuffer reifiedTypeArguments = new ListBuffer();
if (decl.isStatic()
&& ModelUtil.isCeylonDeclaration(decl)) {
Type primType = ((Tree.StaticMemberOrTypeExpression)leftTerm).getTarget().getQualifyingType();
if (primType != null) {
for (Type pt : primType.getTypeArgumentList()) {
typeArguments.add(makeJavaType(pt, JT_TYPE_ARGUMENT));
reifiedTypeArguments.add(makeReifiedTypeArgument(pt));
}
}
}
String selector = Naming.selector(decl, Naming.NA_SETTER);
result = make().Apply(typeArguments.toList(),
makeQualIdent(lhs, selector),
reifiedTypeArguments.toList().append(rhs));
}
return result;
}
protected JCExpression makeAssignmentVariable(Node op, final JCExpression lhs, TypedDeclaration decl) {
JCExpression varExpr = null;
at(op);
if (decl.isToplevel()) {
} else if (Decl.isGetter(decl)) {
if (Decl.isTransient(decl) && !decl.isVariable()) {
varExpr = naming.makeQualifiedName(lhs, decl, Naming.NA_WRAPPER);
}
} else if (decl instanceof Function && Decl.isDeferred(decl)) {
if (Decl.isLocal(decl)) {
// Deferred method initialization of a local function
// The Callable field has the same name as the method, so use NA_MEMBER
varExpr = naming.makeQualifiedName(lhs, decl, Naming.NA_WRAPPER_UNQUOTED | Naming.NA_MEMBER);
} else {
// Deferred method initialization of a class function
varExpr = naming.makeQualifiedName(lhs, decl, Naming.NA_MEMBER);
}
} else if ((decl.isVariable() || decl.isLate()) && (Decl.isClassAttribute(decl))) {
// must use the setter, nothing to do, unless it's a java field
if(Decl.isJavaField(decl)){
if (decl.isStatic()) {
// static field
varExpr = naming.makeName(decl, Naming.NA_FQ | Naming.NA_WRAPPER_UNQUOTED);
}else{
// normal field
varExpr = naming.makeQualifiedName(lhs, decl, Naming.NA_IDENT);
}
}
} else if (decl.isVariable() && (decl.isCaptured() || decl.isShared())) {
// must use the qualified setter
if (Decl.isBoxedVariable(decl)) {
varExpr = naming.makeName(decl, Naming.NA_Q_LOCAL_INSTANCE | Naming.NA_MEMBER | Naming.NA_SETTER);
} else if (Decl.isLocalNotInitializer(decl)) {
} else if (isWithinSuperInvocation()
&& decl.isCaptured()
&& decl.isVariable()) {
varExpr = naming.makeUnquotedIdent(Naming.getAliasedParameterName(((Value)decl).getInitializerParameter()));
}
} else {
varExpr = naming.makeQualifiedName(lhs, decl, Naming.NA_IDENT);
}
return varExpr;
}
protected boolean useFieldInAssignment(Node op, final JCExpression lhs, TypedDeclaration decl) {
at(op);
if (decl.isToplevel()) {
} else if (Decl.isGetter(decl)) {
if (Decl.isTransient(decl) && !decl.isVariable()) {
return true;
}
} else if (decl instanceof Function && Decl.isDeferred(decl)) {
if (Decl.isLocal(decl)) {
return true;
} else {
return true;
}
} else if ((decl.isVariable() || decl.isLate()) && (Decl.isClassAttribute(decl))) {
// must use the setter, nothing to do, unless it's a java field
if(Decl.isJavaField(decl)){
if (decl.isStatic()) {
// static field
return true;
}else{
// normal field
return true;
}
}
} else if (decl.isVariable() && (decl.isCaptured() || decl.isShared())) {
// must use the qualified setter
if (Decl.isBoxedVariable(decl)) {
return false;
} else if (Decl.isLocalNotInitializer(decl)) {
} else if (isWithinSuperInvocation()
&& decl.isCaptured()
&& decl.isVariable()) {
return true;
}
} else {
return true;
}
return false;
}
private JCExpression transformIndexAssignment(Node op, Tree.IndexExpression idx, JCExpression rhs) {
JCExpression result = null;
if (idx.getElementOrRange() instanceof Tree.Element) {
Unit unit = op.getUnit();
Type pt = idx.getPrimary().getTypeModel();
Tree.Element elem = (Tree.Element)idx.getElementOrRange();
Type primaryType;
if ((primaryType = pt.getSupertype(unit.getIndexedCorrespondenceMutatorDeclaration())) != null) {
JCExpression iex = transformExpression(elem.getExpression(), BoxingStrategy.UNBOXED, elem.getExpression().getTypeModel());
result = makeIndexAssignMethod(idx, "set", iex, rhs, primaryType);
} else if ((primaryType = pt.getSupertype(unit.getKeyedCorrespondenceMutatorDeclaration())) != null) {
JCExpression iex = transformExpression(elem.getExpression(), BoxingStrategy.BOXED, elem.getExpression().getTypeModel());
result = makeIndexAssignMethod(idx, "put", iex, rhs, primaryType);
} else if ((primaryType = pt.getSupertype(unit.getJavaListDeclaration())) != null) {
JCExpression iex = transformExpression(elem.getExpression(), BoxingStrategy.UNBOXED, elem.getExpression().getTypeModel());
if (!elem.getExpression().getSmall()) {
iex = utilInvocation().toInt(iex);
}
result = makeIndexAssignMethod(idx, "set", iex, rhs, primaryType);
} else if ((primaryType = pt.getSupertype(unit.getJavaMapDeclaration())) != null) {
JCExpression iex = transformExpression(elem.getExpression(), BoxingStrategy.BOXED, elem.getExpression().getTypeModel());
result = makeIndexAssignMethod(idx, "put", iex, rhs, primaryType);
} else if ((primaryType = pt.getSupertype(unit.getJavaObjectArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaIntArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaShortArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaLongArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaByteArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaCharArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaBooleanArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaFloatArrayDeclaration())) != null
|| (primaryType = pt.getSupertype(unit.getJavaDoubleArrayDeclaration())) != null
) {
JCExpression lhs = transformExpression(idx.getPrimary(), BoxingStrategy.BOXED, primaryType);
JCExpression iex = transformExpression(elem.getExpression(), BoxingStrategy.UNBOXED, elem.getExpression().getTypeModel());
if (!elem.getExpression().getSmall()) {
iex = utilInvocation().toInt(iex);
}
return at(op).Assign(make().Indexed(lhs, iex), rhs);
} else {
return makeErroneous(idx, "compiler bug: index assignment for type '" + pt + "' is not supported");
}
return result;
} else {
return makeErroneous(idx, "compiler bug: ranged index assignment is not supported");
}
}
private JCExpression makeIndexAssignMethod(Tree.IndexExpression leftTerm, String method, JCExpression index, JCExpression rhs, Type expectedPrimaryType) {
JCExpression lhs = transformExpression(leftTerm.getPrimary(), BoxingStrategy.BOXED, expectedPrimaryType);
return make().Apply(List.nil(),
makeQualIdent(lhs, method),
List.of(index, rhs));
}
/** Creates an anonymous class that extends Iterable and implements the specified comprehension.
*/
public JCExpression transformComprehension(Tree.Comprehension comp) {
return transformComprehension(comp, null);
}
JCExpression transformComprehension(Tree.Comprehension comp, Type expectedType) {
Type elementType = comp.getInitialComprehensionClause().getTypeModel();
// get rid of anonymous types
elementType = typeFact().denotableType(elementType);
elementType = wrapInOptionalForInterop(elementType, expectedType, containsUncheckedNulls(comp));
return new ComprehensionTransformation(comp, elementType).transformComprehension();
}
private Type wrapInOptionalForInterop(Type elementType, Type expectedType, boolean containsUncheckedNull) {
if(expectedType != null && containsUncheckedNull && iteratesOverOptional(expectedType) && !typeFact().isOptionalType(elementType))
return typeFact().getOptionalType(elementType);
return elementType;
}
private boolean iteratesOverOptional(Type iterableType) {
Type seqElemType = typeFact().getIteratedType(iterableType);
return isOptional(seqElemType);
}
enum IterType {
CEYLON_ITERABLE {
@Override
JCExpression makeIteratorType(ExpressionTransformer gen, Type iterType) {
return gen.makeJavaType(gen.typeFact().getIteratorType(
gen.typeFact().getIteratedType(iterType)));
}
@Override
ListBuffer makeContext(ExpressionTransformer gen, ComprehensionTransformation ct,
Tree.ForIterator forIterator,
SyntheticName iterVar, SyntheticName itemVar, SyntheticName tmpItem, ListBuffer elseBody) {
ListBuffer contextBody = new ListBuffer();
//Assign the next item to an Object variable
contextBody.add(gen.make().VarDef(gen.make().Modifiers(Flags.FINAL), tmpItem.asName(),
gen.makeJavaType(gen.typeFact().getObjectType()),
gen.make().Apply(null, gen.makeSelect(iterVar.makeIdent(), "next"),
List.nil())));
//Then we check if it's exhausted
contextBody.add(gen.make().Exec(gen.make().Assign(itemVar.suffixedBy(Suffix.$exhausted$).makeIdent(),
gen.make().Binary(JCTree.Tag.EQ, tmpItem.makeIdent(), gen.makeFinished()))));
ListBuffer innerBody = new ListBuffer();
if (ct.idx>0) {
//Subsequent contexts run once for every iteration of the previous loop
//This will reset our previous context by getting a new iterator if the previous loop isn't done
innerBody.add(gen.make().Exec(gen.make().Assign(iterVar.makeIdent(), gen.makeNull())));
}else{
innerBody.add(gen.make().Return(gen.makeBoolean(false)));
}
//Assign the next item to the corresponding variables if not exhausted yet
contextBody.add(gen.make().If(itemVar.suffixedBy(Suffix.$exhausted$).makeIdent(),
gen.make().Block(0, innerBody.toList()),
gen.make().Block(0, elseBody.toList())));
return contextBody;
}
},
JAVA_ITERABLE {
@Override
JCExpression makeIteratorType(ExpressionTransformer gen, Type iterType) {
return gen.makeJavaType(gen.typeFact().getJavaIteratorType(
gen.typeFact().getJavaIteratedType(iterType)));
}
@Override
ListBuffer makeContext(ExpressionTransformer gen, ComprehensionTransformation ct,
Tree.ForIterator forIterator,
SyntheticName iterVar, SyntheticName itemVar, SyntheticName tmpItem,
ListBuffer elseBody) {
ListBuffer contextBody = new ListBuffer();
contextBody.add(gen.make().Exec(gen.make().Assign(itemVar.suffixedBy(Suffix.$exhausted$).makeIdent(),
gen.make().Unary(JCTree.Tag.NOT, gen.make().Apply(null, gen.makeSelect(iterVar.makeIdent(), "hasNext"),
List.nil())))));
ListBuffer innerBody = new ListBuffer();
if (ct.idx > 0) {
innerBody.add(gen.make().Exec(gen.make().Assign(iterVar.makeIdent(), gen.makeNull())));
} else {
innerBody.add(gen.make().Return(gen.makeBoolean(false)));
}
JCExpression nextInvocation = gen.make().Apply(null,
gen.makeSelect(iterVar.makeIdent(), "next"),
List.nil());
if (gen.statementGen().requiresNullCheck(forIterator)) {
nextInvocation = gen.utilInvocation().checkNull(nextInvocation);
}
contextBody.add(
gen.make().If(
itemVar.suffixedBy(Suffix.$exhausted$).makeIdent(),
gen.make().Block(0, innerBody.toList()),
gen.make().Block(0,
List.of(
gen.make().Exec(gen.make().Assign(itemVar.makeIdent(),
nextInvocation)),
gen.make().Return(gen.makeBoolean(true))))));
return contextBody;
}
},
JAVA_ARRAY {
@Override
void makeInitial(ExpressionTransformer gen, ComprehensionTransformation ct, SyntheticName iterVar, Type iterType, JCExpression iterableExpr) {
JCExpression iterTypeExpr = makeIteratorType(gen, iterType);
SyntheticName arrayVar = arrayVar(gen, ct);
ct.fields.add(gen.make().VarDef(gen.make().Modifiers(Flags.PRIVATE), iterVar.asName(), gen.make().Type(gen.syms().intType),
null));
ct.fields.add(gen.make().VarDef(gen.make().Modifiers(Flags.PRIVATE | Flags.FINAL), arrayVar.asName(), iterTypeExpr,
iterableExpr));
ct.initIterator = gen.make().Exec(gen.make().Assign(iterVar.makeIdent(), gen.make().Literal(0)));
}
protected SyntheticName arrayVar(ExpressionTransformer gen, ComprehensionTransformation ct) {
return gen.naming.synthetic(Prefix.$array$, ct.idx);
}
@Override
ListBuffer makeSubsequent(ExpressionTransformer gen,
ComprehensionTransformation ct,
Type iterType,
SyntheticName iterVar,
JCExpression iterableExpr) {
SyntheticName arrayVar = arrayVar(gen, ct);
ListBuffer block = new ListBuffer();
ct.fields.add(gen.make().VarDef(gen.make().Modifiers(Flags.PRIVATE), iterVar.asName(), gen.make().Type(gen.syms().intType),
null));
ct.fields.add(gen.make().VarDef(gen.make().Modifiers(Flags.PRIVATE), arrayVar.asName(), makeIteratorType(gen, iterType),
null));
block.appendList(List.of(
gen.make().If(gen.make().Binary(JCTree.Tag.NE, arrayVar.makeIdent(), gen.makeNull()),
gen.make().Return(gen.makeBoolean(true)),
null),
gen.make().If(gen.make().Unary(JCTree.Tag.NOT, gen.make().Apply(null, ct.ctxtName.makeIdentWithThis(), List.nil())),
gen.make().Return(gen.makeBoolean(false)),
null)));
block.appendList(ct.capture());
block.appendList(List.of(
gen.make().Exec(gen.make().Assign(arrayVar.makeIdent(),
iterableExpr)),
gen.make().Exec(gen.make().Assign(iterVar.makeIdent(), gen.make().Literal(0))),
gen.make().Return(gen.makeBoolean(true))));
return block;
}
@Override
JCExpression makeIteratorType(ExpressionTransformer gen, Type iterType) {
return gen.makeJavaType(iterType, JT_NO_PRIMITIVES);
}
@Override
ListBuffer makeContext(ExpressionTransformer gen, ComprehensionTransformation ct,
Tree.ForIterator forIterator,
SyntheticName iterVar, SyntheticName itemVar, SyntheticName tmpItem,
ListBuffer elseBody) {
ListBuffer contextBody = new ListBuffer();
Tree.SpecifierExpression specexpr = forIterator.getSpecifierExpression();
Type iterType = specexpr.getExpression().getTypeModel();
SyntheticName arrayVar = arrayVar(gen, ct);
contextBody.add(gen.make().Exec(gen.make().Assign(itemVar.suffixedBy(Suffix.$exhausted$).makeIdent(),
gen.make().Binary(JCTree.Tag.GE, iterVar.makeIdent(), gen.naming.makeSelect(arrayVar.makeIdent(), "length")))));
ListBuffer innerBody = new ListBuffer();
if (ct.idx > 0) {
innerBody.add(gen.make().Exec(gen.make().Assign(arrayVar.makeIdent(), gen.makeNull())));
} else {
innerBody.add(gen.make().Return(gen.makeBoolean(false)));
}
JCExpression indexed = gen.make().Indexed(arrayVar.makeIdent(),
gen.make().Unary(JCTree.Tag.POSTINC, iterVar.makeIdent()));
if (gen.statementGen().requiresNullCheck(forIterator)) {
indexed = gen.utilInvocation().checkNull(indexed);
}
indexed = gen.applyErasureAndBoxing(indexed,
gen.typeFact().getJavaArrayElementType(iterType),
gen.typeFact().getJavaObjectArrayDeclaration().equals(iterType.resolveAliases().getDeclaration()),
CodegenUtil.getBoxingStrategy(((Tree.ValueIterator) forIterator).getVariable().getDeclarationModel()),
((Tree.ValueIterator) forIterator).getVariable().getDeclarationModel().getType());
contextBody.add(
gen.make().If(
itemVar.suffixedBy(Suffix.$exhausted$).makeIdent(),
gen.make().Block(0, innerBody.toList()),
gen.make().Block(0,
List.of(
gen.make().Exec(gen.make().Assign(itemVar.makeIdent(),
indexed)),
gen.make().Return(gen.makeBoolean(true))))));
return contextBody;
}
};
/** Make the type expression for the Iterator */
abstract JCExpression makeIteratorType(ExpressionTransformer gen, Type iterType);
/**
* Transform the initial iterator, declaring and initializing fields within the
* given {@code ct}.
*/
void makeInitial(ExpressionTransformer gen,
ComprehensionTransformation ct, SyntheticName iterVar,
Type iterType, JCExpression iterableExpr) {
JCExpression iterTypeExpr = makeIteratorType(gen, iterType);
ct.fields.add(gen.make().VarDef(gen.make().Modifiers(Flags.PRIVATE | Flags.FINAL), iterVar.asName(), iterTypeExpr,
null));
ct.initIterator = gen.make().Exec(gen.make().Assign(iterVar.makeIdent(), gen.make().Apply(null, gen.makeSelect(iterableExpr, "iterator"),
List.nil())));
}
/**
* Transform the non-initial iterator, declaring fields within the
* given {@code ct}.
*/
ListBuffer makeSubsequent(ExpressionTransformer gen,
ComprehensionTransformation ct,
Type iterType, SyntheticName iterVar,
JCExpression iterableExpr) {
ListBuffer block = new ListBuffer();
ct.fields.add(gen.make().VarDef(gen.make().Modifiers(Flags.PRIVATE), iterVar.asName(), makeIteratorType(gen, iterType), null));
block.appendList(List.of(
gen.make().If(gen.make().Binary(JCTree.Tag.NE, iterVar.makeIdent(), gen.makeNull()),
gen.make().Return(gen.makeBoolean(true)),
null),
gen.make().If(gen.make().Unary(JCTree.Tag.NOT, gen.make().Apply(null, ct.ctxtName.makeIdentWithThis(), List.nil())),
gen.make().Return(gen.makeBoolean(false)),
null)));
block.appendList(ct.capture());
block.appendList(List.of(
gen.make().Exec(gen.make().Assign(iterVar.makeIdent(),
gen.make().Apply(null,
gen.makeSelect(iterableExpr, "iterator"),
List.nil()))),
gen.make().Return(gen.makeBoolean(true))
));
return block;
}
abstract ListBuffer makeContext(ExpressionTransformer gen, ComprehensionTransformation ct,
Tree.ForIterator forIterator, SyntheticName iterVar, SyntheticName itemVar, SyntheticName tmpItem, ListBuffer elseBody);
}
class ComprehensionTransformation {
private final Tree.Comprehension comp;
final Type targetIterType;
final Type absentIterType;
int idx = 0;
Tree.ExpressionComprehensionClause excc = null;
Naming.SyntheticName prevItemVar = null;
Naming.SyntheticName ctxtName = null;
Naming.SyntheticName lastIteratorCtxtName = null;
//Iterator fields
final ListBuffer fields = new ListBuffer();
final ListBuffer fieldSubst = new ListBuffer();
private JCExpression error;
private JCStatement initIterator;
// A list of variable declarations local to next(), $next$N()
// and $iterator$N() methods so that
// the variable captured by whatever gets transformed there holds the value
// at *that point* on the iteration, and not the (variable) value of
// the iterator. See #986, #2304
private ListBuffer valueCaptures = new ListBuffer();
public ComprehensionTransformation(final Tree.Comprehension comp, Type elementType) {
this.comp = comp;
targetIterType = typeFact().getIterableType(elementType);
absentIterType = comp.getInitialComprehensionClause().getFirstTypeModel();
}
public JCExpression transformComprehension() {
at(comp);
// make sure "this" will be qualified since we're introducing a new surrounding class
boolean oldWithinSyntheticClassBody = withinSyntheticClassBody(true);
try{
Tree.ComprehensionClause clause = comp.getInitialComprehensionClause();
while (clause != null) {
final Naming.SyntheticName iterVar = naming.synthetic(Prefix.$iterator$, idx);
Naming.SyntheticName itemVar = null;
if (clause instanceof Tree.ForComprehensionClause) {
final Tree.ForComprehensionClause fcl = (Tree.ForComprehensionClause)clause;
itemVar = transformForClause(fcl, iterVar);
if (error != null) {
return error;
}
clause = fcl.getComprehensionClause();
} else if (clause instanceof Tree.IfComprehensionClause) {
transformIfClause((Tree.IfComprehensionClause)clause);
if (error != null) {
return error;
}
clause = ((Tree.IfComprehensionClause)clause).getComprehensionClause();
itemVar = prevItemVar;
} else if (clause instanceof Tree.ExpressionComprehensionClause) {
//Just keep a reference to the expression
excc = (Tree.ExpressionComprehensionClause)clause;
at(excc);
clause = null;
} else {
return makeErroneous(clause, "compiler bug: comprehension clauses of type " + clause.getClass().getName() + " are not yet supported");
}
idx++;
if (itemVar != null) prevItemVar = itemVar;
}
Type iteratedType = typeFact().getIteratedType(targetIterType);
//Define the next() method for the Iterator
fields.add(makeNextMethod(iteratedType));
//Define the inner iterator class
JCMethodDecl getIterator = makeGetIterator(iteratedType);
JCExpression iterable = makeAnonymousIterable(iteratedType, getIterator);
for (Substitution subs : fieldSubst) {
subs.close();
}
return iterable;
}finally{
withinSyntheticClassBody(oldWithinSyntheticClassBody);
}
}
List capture() {
List result = List.nil();
for (VarDefBuilder var : valueCaptures) {
// reverse order, but who cares?
result = result.prepend(var.buildFromField());
}
return result;
}
/**
* Builds the {@code next()} method of the {@code AbstractIterator}
*/
private JCMethodDecl makeNextMethod(Type iteratedType) {
List of = List.of(make().Return(transformExpression(excc.getExpression(), BoxingStrategy.BOXED, iteratedType)));
of = of.prependList(capture());
JCStatement stmt = make().If(
make().Apply(null,
ctxtName.makeIdentWithThis(), List.nil()),
make().Block(0, of),
make().Return(makeFinished()));
return make().MethodDef(make().Modifiers(Flags.PUBLIC | Flags.FINAL), names().fromString("next"),
makeJavaType(typeFact().getObjectType()), List.nil(),
List.nil(), List.nil(), make().Block(0, List.of(stmt)), null);
}
/**
* Builds a {@code getIterator()} method which contains a local class
* extending {@code AbstractIterator} and initialises the iter$0 field
* to a new instance of that local class.
*
* Doesn't use an anonymous class due to #974.
* @param iteratedType
* @return
*/
private JCMethodDecl makeGetIterator(Type iteratedType) {
Type iteratorType = typeFact().getIteratorType(iteratedType);
JCExpression iteratorTypeExpr = make().TypeApply(makeIdent(syms().ceylonAbstractIteratorType),
List.of(makeJavaType(iteratedType, JT_TYPE_ARGUMENT)));
JCExpression iterator = at(comp).NewClass(null, List.nil(), iteratorTypeExpr,
List.