use of com.redhat.ceylon.model.typechecker.model.Type in project ceylon-compiler by ceylon.
the class ExpressionTransformer method getVarianceCastResult.
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<Type> satisfiedTypes = new LinkedList<Type>();
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 < satisfiedTypes.size(); i++) {
Type pt = satisfiedTypes.get(i);
for (int j = i + 1; j < satisfiedTypes.size(); j++) {
Type other = satisfiedTypes.get(j);
if (pt.isExactly(other) || haveSameErasure(pt, other)) {
satisfiedTypes.remove(j);
break;
}
}
}
// we need at least two instantiations
if (satisfiedTypes.size() <= 1)
return null;
boolean needsCast = false;
// we need at least one that differs
for (Type superType : satisfiedTypes) {
if (!exprType.isExactly(superType)) {
needsCast = true;
break;
}
}
// no cast needed if they are all the same type
if (!needsCast)
return null;
// find the better cast match
for (Type superType : satisfiedTypes) {
if (expectedType.isExactly(superType))
return new VarianceCastResult(superType);
}
// nothing better than a raw cast (Stef: not sure that can happen)
return RawCastVarianceResult;
}
use of com.redhat.ceylon.model.typechecker.model.Type in project ceylon-compiler by ceylon.
the class ExpressionTransformer method transformMemberExpression.
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");
}
// 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;
}
}
// (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);
} 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 (isWithinSuperInvocation() && ((Value) decl).isVariable() && ((Value) decl).isCaptured()) {
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 {
// not toplevel, not within method, must be a class member
selector = naming.selector((Function) decl);
}
}
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;
}
// cases
if (!mustUseParameter) {
qualExpr = addQualifierForObjectMembersOfInterface(expr, decl, qualExpr);
qualExpr = addInterfaceImplAccessorIfRequired(qualExpr, expr, decl);
qualExpr = addThisOrObjectQualifierIfRequired(qualExpr, expr, decl);
if (qualExpr == null && needDollarThis(expr)) {
qualExpr = makeQualifiedDollarThis((Tree.BaseMemberExpression) expr);
}
}
if (qualExpr == null && (decl.isStaticallyImportable() || (decl instanceof Value && Decl.isEnumeratedConstructor((Value) decl))) && // 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.<JCTree.JCExpression>nil(), result, List.<JCTree.JCExpression>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.<JCTree.JCExpression>nil(), result, List.<JCTree.JCExpression>nil());
}
}
}
}
if (transformer == null && decl instanceof TypedDeclaration && ((TypedDeclaration) decl).getType().isTypeConstructor() && !expr.getTypeArguments().getTypeModels().isEmpty()) {
// applying a type constructor
ListBuffer<JCExpression> tds = ListBuffer.lb();
for (Type t : expr.getTypeArguments().getTypeModels()) {
tds.add(makeReifiedTypeArgument(t));
}
result = make().Apply(null, makeQualIdent(result, Naming.Unfix.apply.toString()), List.<JCExpression>of(make().NewArray(make().Type(syms().ceylonTypeDescriptorType), List.<JCExpression>nil(), tds.toList())));
}
return result;
}
use of com.redhat.ceylon.model.typechecker.model.Type in project ceylon-compiler by ceylon.
the class ExpressionTransformer method transform.
// 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());
Interface compoundType = expr.getUnit().getOrdinalDeclaration();
Type valueType = getSupertype(expr.getTerm(), compoundType);
Type returnType = getMostPreciseType(term, getTypeArgument(valueType, 0));
List<JCVariableDecl> decls = List.nil();
List<JCStatement> stats = List.nil();
JCExpression result = null;
// we can optimise that case a bit sometimes
boolean boxResult = !canOptimise;
// (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.PLUS : JCTree.MINUS, make().Ident(varName), makeInteger(1));
successor = unAutoPromote(successor, returnType);
} else {
successor = make().Apply(null, makeSelect(make().Ident(varName), operator.ceylonMethod), List.<JCExpression>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 = transformAssignment(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.PLUS : JCTree.MINUS, make().Ident(varVName), makeInteger(1));
successor = unAutoPromote(successor, returnType);
} else {
successor = make().Apply(null, makeSelect(make().Ident(varVName), operator.ceylonMethod), List.<JCExpression>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 = transformAssignment(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");
}
return make().LetExpr(decls, stats, result);
}
use of com.redhat.ceylon.model.typechecker.model.Type in project ceylon-compiler by ceylon.
the class ExpressionTransformer method transform.
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.<JCExpression>of(reifiedArgumentsExpr, make().Literal(expr.getDeclaration().getName())));
} else {
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.<JCExpression>of(make().Literal(ctor.getName() == null ? "" : ctor.getName())));
if (Decl.isEnumeratedConstructor(ctor)) {
metamodelCall = make().TypeCast(makeJavaType(typeFact().getValueConstructorDeclarationType(), JT_RAW), metamodelCall);
} else /*else if (Decl.isDefaultConstructor(ctor)){
metamodelCall = make().TypeCast(
makeJavaType(typeFact().getDefaultConstructorDeclarationType(), JT_RAW), metamodelCall);
} */
{
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");
}
}
use of com.redhat.ceylon.model.typechecker.model.Type in project ceylon-compiler by ceylon.
the class ExpressionTransformer method transformSpreadOperator.
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<JCStatement> letStmts = ListBuffer.<JCStatement>lb();
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 srcIterableTypeExpr = makeJavaType(typeFact().getIterableType(srcElementType), JT_NO_PRIMITIVES);
JCExpression srcIterableExpr;
boolean isSuperOrSuperOf = false;
if (spreadMethodReferenceInner) {
srcIterableExpr = srcIterableName.makeIdent();
} else {
boolean isSuper = isSuper(expr.getPrimary());
isSuperOrSuperOf = isSuper || isSuperOf(expr.getPrimary());
if (isSuperOrSuperOf) {
// 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, typeFact().getIterableType(srcElementType));
}
}
// do not capture the iterable for super invocations: see above
if (!spreadMethodReferenceInner && !isSuperOrSuperOf) {
JCVariableDecl srcIterable = null;
srcIterable = makeVar(Flags.FINAL, srcIterableName, srcIterableTypeExpr, srcIterableExpr);
letStmts.prepend(srcIterable);
}
Type resultElementType = expr.getTarget().getType();
Type resultAbsentType = typeFact().getIteratedAbsentType(expr.getPrimary().getTypeModel());
// private Iterator<srcElementType> 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.<JCExpression>nil()));
Naming.SyntheticName iteratorResultName = varBaseName.suffixedBy(Suffix.$element$);
/* 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 {
JCExpression transformedElement = applyErasureAndBoxing(iteratorResultName.makeIdent(), typeFact().getAnythingType(), CodegenUtil.hasTypeErased(expr.getPrimary()), true, BoxingStrategy.BOXED, srcElementType, 0);
transformedElement = transformMemberExpression(expr, transformedElement, transformer);
// be handled by the previous recursion
if (spreadMethodReferenceOuter) {
return make().LetExpr(letStmts.toList(), transformedElement);
}
transformedElement = applyErasureAndBoxing(transformedElement, resultElementType, // 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(null, make().Type(syms().objectType));
nextMdb.body(List.of(makeVar(iteratorResultName, make().Type(syms().objectType), null), make().If(make().Unary(JCTree.NOT, make().TypeTest(make().Assign(iteratorResultName.makeIdent(), make().Apply(null, naming.makeQualIdent(srcIteratorName.makeIdent(), "next"), List.<JCExpression>nil())), make().Type(syms().ceylonFinishedType))), make().Block(0, List.<JCStatement>of(make().Exec(make().Assign(iteratorResultName.makeIdent(), transformedElement)))), null), make().Return(iteratorResultName.makeIdent())));
JCMethodDecl nextMethod = nextMdb.build();
// 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), List.of(srcIterator, nextMethod)));
MethodDefinitionBuilder iteratorMdb = MethodDefinitionBuilder.systemMethod(this, "iterator");
iteratorMdb.isOverride(true);
iteratorMdb.annotationFlags(Annotations.IGNORE);
iteratorMdb.modifiers(Flags.PUBLIC | Flags.FINAL);
iteratorMdb.resultType(null, makeJavaType(typeFact().getIteratorType(resultElementType)));
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.<JCTree>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.<JCExpression>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;
}
}
Aggregations