com.redhat.ceylon.compiler.java.codegen.ClassTransformer Maven / Gradle / Ivy
/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.redhat.ceylon.compiler.java.codegen;
import static com.redhat.ceylon.compiler.java.codegen.Naming.DeclNameFlag.QUALIFIED;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.ABSTRACT;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.FINAL;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.INTERFACE;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.PRIVATE;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.PROTECTED;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.PUBLIC;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.STATIC;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.TRANSIENT;
import static com.redhat.ceylon.langtools.tools.javac.code.Flags.VARARGS;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.antlr.runtime.Token;
import com.redhat.ceylon.compiler.java.codegen.MethodDefinitionBuilder.NonWideningParam;
import com.redhat.ceylon.compiler.java.codegen.MethodDefinitionBuilder.WideningRules;
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.StatementTransformer.DeferredSpecification;
import com.redhat.ceylon.compiler.java.codegen.Strategy.DefaultParameterMethodOwner;
import com.redhat.ceylon.compiler.java.codegen.recovery.Drop;
import com.redhat.ceylon.compiler.java.codegen.recovery.Errors;
import com.redhat.ceylon.compiler.java.codegen.recovery.Generate;
import com.redhat.ceylon.compiler.java.codegen.recovery.HasErrorException;
import com.redhat.ceylon.compiler.java.codegen.recovery.PrivateConstructorOnly;
import com.redhat.ceylon.compiler.java.codegen.recovery.ThrowerCatchallConstructor;
import com.redhat.ceylon.compiler.java.codegen.recovery.ThrowerMethod;
import com.redhat.ceylon.compiler.java.codegen.recovery.TransformationPlan;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AnnotationList;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AttributeDeclaration;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AttributeGetterDefinition;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AttributeSetterDefinition;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BaseMemberExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.FunctionArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.LazySpecifierExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.MethodDeclaration;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SequencedArgument;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierOrInitializerExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.SpecifierStatement;
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.jvm.Target;
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.JCBinary;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCBlock;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCCase;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCExpression;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCExpressionStatement;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCFieldAccess;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCIdent;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCMethodInvocation;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCNewClass;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCPrimitiveTypeTree;
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.JCSwitch;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCThrow;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCVariableDecl;
import com.redhat.ceylon.langtools.tools.javac.util.Context;
import com.redhat.ceylon.langtools.tools.javac.util.List;
import com.redhat.ceylon.langtools.tools.javac.util.ListBuffer;
import com.redhat.ceylon.model.loader.NamingBase;
import com.redhat.ceylon.model.loader.NamingBase.Suffix;
import com.redhat.ceylon.model.loader.NamingBase.Unfix;
import com.redhat.ceylon.model.loader.model.AnnotationTarget;
import com.redhat.ceylon.model.loader.model.JavaBeanValue;
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.ClassAlias;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Constructor;
import com.redhat.ceylon.model.typechecker.model.ControlBlock;
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.Interface;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
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.Scope;
import com.redhat.ceylon.model.typechecker.model.Setter;
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.Unit;
import com.redhat.ceylon.model.typechecker.model.Value;
/**
* This transformer deals with class/interface declarations
*/
public class ClassTransformer extends AbstractTransformer {
private static final Comparator DeclarationComparator = new Comparator(){
@Override
public int compare(Declaration a, Declaration b) {
int cmp = a.getQualifiedNameString().compareTo(b.getQualifiedNameString());
if (cmp != 0) {
return cmp;
}
// getters and setters are distinct, but have the same name
if (a.getDeclarationKind() != null && b.getDeclarationKind() != null) {
cmp = a.getDeclarationKind().compareTo(b.getDeclarationKind());
if (cmp != 0) {
return cmp;
}
}
if (a instanceof Function
&& b instanceof Function) {
// They could be java overloaded methods, so we have to compare parameter lists
Function afn = (Function)a;
Function bfn = (Function)b;
if (afn.getParameterLists() != null && !afn.getParameterLists().isEmpty()
&& bfn.getParameterLists() != null && !bfn.getParameterLists().isEmpty()) {
java.util.List apl = afn.getFirstParameterList().getParameters();
java.util.List bpl = bfn.getFirstParameterList().getParameters();
if (apl.size() < bpl.size()) {
return -1;
} else if (apl.size() > bpl.size()) {
return 1;
} else {
Iterator aiter = apl.iterator();
Iterator biter = bpl.iterator();
while (aiter.hasNext()) {
if (biter.hasNext()) {
// make sure to compare parameter *types*, not the parameters themselves
cmp = this.compare(aiter.next().getModel().getTypeDeclaration(),
biter.next().getModel().getTypeDeclaration());
if (cmp != 0) {
return cmp;
}
} else {
return 1;
}
}
if (biter.hasNext()) {
return -1;
}
}
} else if (afn.getParameterLists() != null && !afn.getParameterLists().isEmpty()
&& (bfn.getParameterLists() == null || bfn.getParameterLists().isEmpty())) {
return -1;
} else if ((afn.getParameterLists() == null || afn.getParameterLists().isEmpty())
&& bfn.getParameterLists() != null && !bfn.getParameterLists().isEmpty()) {
return 1;
} else {
return 0;
}
}
return cmp;
}
};
public static ClassTransformer getInstance(Context context) {
ClassTransformer trans = context.get(ClassTransformer.class);
if (trans == null) {
trans = new ClassTransformer(context);
context.put(ClassTransformer.class, trans);
}
return trans;
}
private ClassTransformer(Context context) {
super(context);
}
public List transform(final Tree.ClassOrInterface def) {
final ClassOrInterface model = def.getDeclarationModel();
if (model.isToplevel()
&& isEe(model)) {
replaceModifierTransformation(new EeModifierTransformation());
}
// we only create types for aliases so they can be imported with the model loader
// and since we can't import local declarations let's just not create those types
// in that case
if(model.isAlias()
&& Decl.isAncestorLocal(def))
return List.nil();
naming.clearSubstitutions(model);
final String javaClassName;
String ceylonClassName = def.getIdentifier().getText();
if (def instanceof Tree.AnyInterface) {
javaClassName = naming.makeTypeDeclarationName(model, QUALIFIED).replaceFirst(".*\\.", "");
} else {
javaClassName = Naming.quoteClassName(ceylonClassName);
}
ClassDefinitionBuilder instantiatorImplCb;
ClassDefinitionBuilder instantiatorDeclCb;
if (Decl.withinInterface(model)) {
instantiatorImplCb = gen().current().getCompanionBuilder((Interface)model.getContainer());
instantiatorDeclCb = gen().current();
} else {
instantiatorImplCb = gen().current();
instantiatorDeclCb = null;
}
ClassDefinitionBuilder classBuilder = ClassDefinitionBuilder
.klass(this, javaClassName, ceylonClassName, Decl.isLocal(model))
.forDefinition(model)
.hasDelegatingConstructors(CodegenUtil.hasDelegatingConstructors(def));
classBuilder.getInitBuilder().deprecated(model.isDeprecated());
// Very special case for Anything
if ("ceylon.language::Anything".equals(model.getQualifiedNameString())) {
classBuilder.extending(model.getType(), null);
}
if (def instanceof Tree.AnyClass) {
classBuilder.getInitBuilder().modifiers(modifierTransformation().constructor(model));
Tree.AnyClass classDef = (Tree.AnyClass)def;
Class cls = classDef.getDeclarationModel();
// Member classes need a instantiator method
boolean generateInstantiator = Strategy.generateInstantiator(cls);
if (!cls.hasConstructors()) {
classBuilder.getInitBuilder().userAnnotations(expressionGen().transformAnnotations(OutputElement.CONSTRUCTOR, def));
}
if(generateInstantiator
&& !cls.hasConstructors()
&& !cls.hasEnumerated()){
if (!cls.isStatic()) {
classBuilder.getInitBuilder().modifiers(PROTECTED);
}
generateInstantiators(classBuilder, cls, null, instantiatorDeclCb, instantiatorImplCb, classDef, classDef.getParameterList());
}
classBuilder.annotations(expressionGen().transformAnnotations(OutputElement.TYPE, def));
if(def instanceof Tree.ClassDefinition){
transformClass((Tree.ClassDefinition)def, cls, classBuilder, classDef.getParameterList(), generateInstantiator, instantiatorDeclCb, instantiatorImplCb);
}else{
// class alias
classBuilder.getInitBuilder().modifiers(PRIVATE);
transformClassAlias((Tree.ClassDeclaration)def, classBuilder);
}
addMissingUnrefinedMembers(def, cls, classBuilder);
}
if (def instanceof Tree.AnyInterface) {
classBuilder.annotations(expressionGen().transformAnnotations(OutputElement.TYPE, def));
if(def instanceof Tree.InterfaceDefinition){
transformInterface(def, (Interface)model, classBuilder);
}else{
// interface alias
classBuilder.annotations(makeAtAlias(model.getExtendedType(), null));
classBuilder.isAlias(true);
}
classBuilder.isDynamic(model.isDynamic());
}
// make sure we set the container in case we move it out
addAtContainer(classBuilder, model);
transformTypeParameters(classBuilder, model);
// Transform the class/interface members
List childDefs = visitClassOrInterfaceDefinition(def, classBuilder);
// everything else is synthetic
at(null);
TransformationPlan plan = errors().hasDeclarationError(def);
if (plan instanceof ThrowerCatchallConstructor) {
classBuilder.broken();
MethodDefinitionBuilder initBuilder = classBuilder.noInitConstructor().addConstructor(model.isDeprecated());
initBuilder.body(statementGen().makeThrowUnresolvedCompilationError(plan.getErrorMessage().getMessage()));
// Although we have the class pl which we could use we don't know
// that it won't collide with the default named constructor's pl
// which would cause a javac error about two constructors with the same sig
// so we generate a Object... here. There's still a risk of collision though
// when the default constructor has pl (ObjectArray).
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.implicitParameter(this, "ignored");
pdb.modifiers(VARARGS);
pdb.type(new TransformedType(make().TypeArray(make().Type(syms().objectType))));
initBuilder.parameter(pdb);
} else if (plan instanceof PrivateConstructorOnly) {
classBuilder.broken();
MethodDefinitionBuilder initBuilder = classBuilder.noInitConstructor().addConstructor(model.isDeprecated());
initBuilder.body(statementGen().makeThrowUnresolvedCompilationError(plan.getErrorMessage().getMessage()));
initBuilder.modifiers(PRIVATE);
}
// If it's a Class without initializer parameters...
if (Strategy.generateMain(def)) {
// ... then add a main() method
classBuilder.method(makeMainForClass(model));
}
classBuilder
.modelAnnotations(model.getAnnotations())
.modifiers(modifierTransformation().classFlags(model))
.satisfies(model.getSatisfiedTypes())
.caseTypes(model.getCaseTypes(), model.getSelfType())
.defs((List)childDefs);
// aliases and native headers don't need a $getType method
if(!model.isAlias()){
// only classes get a $getType method
if(model instanceof Class)
classBuilder.addGetTypeMethod(model.getType());
if(supportsReifiedAlias(model))
classBuilder.reifiedAlias(model.getType());
}
// Now, once all the fields have been added,
// we can add things which depend on knowing all the fields
if (Strategy.generateJpaCtor(def) && plan instanceof Generate) {
buildJpaConstructor((Class)def.getDeclarationModel(), classBuilder);
}
if (model instanceof Class
&& !(model instanceof ClassAlias)
&& plan instanceof Generate) {
Class c = (Class)model;
if (Strategy.introduceJavaIoSerializable(c, typeFact().getJavaIoSerializable())) {
classBuilder.introduce(make().QualIdent(syms().serializableType.tsym));
if (Strategy.useSerializationProxy(c)
&& noValueConstructorErrors((Tree.ClassDefinition)def)) {
at(def);
addWriteReplace(c, classBuilder);
}
}
serialization(c, classBuilder);
}
// reset position before initializer constructor is generated.
at(def);
classBuilder.at(def);
List result;
if (Decl.isAnnotationClass(def)) {
ListBuffer trees = new ListBuffer();
trees.addAll(transformAnnotationClass((Tree.AnyClass)def));
transformAnnotationClassConstructor((Tree.AnyClass)def, classBuilder);
// you only need that method if you satisfy Annotation which is erased to j.l.a.Annotation
if(model.inherits(typeFact().getAnnotationDeclaration()))
classBuilder.addAnnotationTypeMethod(model.getType());
trees.addAll(classBuilder.build());
result = trees.toList();
} else {
result = classBuilder.build();
}
if (model.isToplevel()
&& isEe(model)) {
replaceModifierTransformation(new ModifierTransformation());
}
return result;
}
private boolean noValueConstructorErrors(Tree.ClassDefinition def) {
for (Tree.Statement s : def.getClassBody().getStatements()) {
if (s instanceof Tree.Constructor
|| s instanceof Tree.Enumerated) {
if (errors().hasAnyError((Tree.Declaration)s)) {
return false;
}
}
}
return true;
}
/**
* Adds a write replace method which replaces value constructor instances
* with a SerializationProxy
* @param model
* @param classBuilder
*/
protected void addWriteReplace(final Class model,
ClassDefinitionBuilder classBuilder) {
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, "writeReplace");
mdb.resultType(new TransformedType(make().Type(syms().objectType), null, makeAtNonNull()));
mdb.modifiers(PRIVATE | FINAL);
ListBuffer stmts = new ListBuffer();
SyntheticName name = naming.synthetic(Unfix.$name$);
stmts.add(makeVar(FINAL, name, make().Type(syms().stringType), null));
if (model.hasEnumerated()) {
JCStatement tail;
if (Decl.hasOnlyValueConstructors(model)) {
tail = make().Throw(statementGen().makeNewEnumeratedTypeError("Instance not of any constructor"));
} else {
tail = make().Return(naming.makeThis());
}
for (Declaration member : model.getMembers()) {
if (Decl.isValueConstructor(member) ) {
Value val = (Value)member;
tail = make().If(
make().Binary(JCTree.Tag.EQ, naming.makeThis(),
naming.getValueConstructorFieldName(val).makeIdent()),
make().Block(0, List.of(make().Exec(make().Assign(name.makeIdent(), make().Literal(Naming.getGetterName(member)))))),
tail);
}
}
stmts.add(tail);
} else if (model.isAnonymous()) {
stmts.add(make().Exec(make().Assign(name.makeIdent(), make().Literal(Naming.getGetterName((Value)model.getContainer().getDirectMember(model.getName(), null, false))))));
} else {
throw new BugException("Unsupported need for writeReplace()");
}
// final String name;
// if(this == instA) {
// name = "getInstA";
// } // ... else { throw new
// return new SerializationProxy(outer, Foo.clazz, name);
List args = List.of(name.makeIdent());
if (model.isMember() && !model.isStatic()) {
ClassOrInterface outer = (ClassOrInterface)model.getContainer();
args = args.prepend(makeClassLiteral(outer.getType()));
args = args.prepend(naming.makeQualifiedThis(naming.makeTypeDeclarationExpression(null, outer, DeclNameFlag.QUALIFIED)));
} else {
args = args.prepend(makeClassLiteral(model.getType()));
}
stmts.add(make().Return(make().NewClass(null, null,
make().QualIdent(syms().ceylonSerializationProxyType.tsym),
args,
null)));
mdb.body(stmts.toList());
classBuilder.method(mdb);
}
protected void buildJpaConstructor(Class model, ClassDefinitionBuilder classBuilder) {
MethodDefinitionBuilder ctor = classBuilder.addConstructor(model.isDeprecated());
ctor.modelAnnotations(makeAtJpa());
ctor.modelAnnotations(makeAtIgnore());
ctor.modifiers(modifierTransformation().jpaConstructor(model));
for (TypeParameter tp : model.getTypeParameters()) {
ctor.reifiedTypeParameter(tp);
}
final ListBuffer stmts = new ListBuffer();
// invoke super (or this if
ListBuffer superArgs = new ListBuffer();
if (model.isSerializable()) {
superArgs.add(make().TypeCast(make().QualIdent(syms().ceylonSerializationType.tsym),
makeNull()));
for (JCExpression ta : makeReifiedTypeArguments(model.getType())) {
superArgs.add(ta);
}
} else {
for (JCExpression ta : makeReifiedTypeArguments(model.getExtendedType())) {
superArgs.add(ta);
}
}
stmts.add(make().Exec(make().Apply(null,
model.isSerializable() ? naming.makeThis() : naming.makeSuper(),
superArgs.toList())));
if (!model.isSerializable()) {
buildFieldInits(model, classBuilder, stmts);
}
ctor.body(stmts.toList());
}
/**
* Recover from members not being refined in the class hierarchy
* by generating a stub method that throws.
*/
private void addMissingUnrefinedMembers(
Node def,
Class classModel,
ClassDefinitionBuilder classBuilder) {
for (Reference unrefined : classModel.getUnimplementedFormals()) {
Declaration formalMember = unrefined.getDeclaration();//classModel.getMember(memberName, null, false);
String errorMessage = "formal member '"+formalMember.getName()+"' of '"+((TypeDeclaration)formalMember.getContainer()).getName()+"' not implemented in class hierarchy";
java.util.List params = new java.util.ArrayList();
if (formalMember instanceof Generic) {
for (TypeParameter tp: ((Generic) formalMember).getTypeParameters()) {
params.add(tp.getType());
}
}
if (formalMember instanceof Value) {
addRefinedThrowerAttribute(classBuilder, errorMessage, def, classModel,
(Value)formalMember);
} else if (formalMember instanceof Function) {
addRefinedThrowerMethod(classBuilder, errorMessage, classModel,
(Function)formalMember);
} else if (formalMember instanceof Class
&& formalMember.isClassMember()) {
addRefinedThrowerInstantiatorMethod(classBuilder, errorMessage, classModel,
(Class)formalMember, unrefined);
}
// formal member class of interface handled in
// makeDelegateToCompanion()
}
}
private void addRefinedThrowerInstantiatorMethod(ClassDefinitionBuilder classBuilder,
String message, ClassOrInterface classModel, Class formalClass, Reference unrefined) {
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this,
naming.getInstantiatorMethodName(formalClass));
mdb.modifiers(modifierTransformation().classFlags(formalClass) &~ABSTRACT);
for (TypeParameter tp: formalClass.getTypeParameters()) {
mdb.typeParameter(tp);
mdb.reifiedTypeParameter(tp);
}
for (Parameter formalP : formalClass.getParameterList().getParameters()) {
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, formalP.getName());
pdb.sequenced(formalP.isSequenced());
pdb.defaulted(formalP.isDefaulted());
pdb.type(new TransformedType(makeJavaType(unrefined.getTypedParameter(formalP).getType())));
mdb.parameter(pdb);
}
mdb.resultType(makeJavaType(unrefined.getType()), null);
mdb.body(makeThrowUnresolvedCompilationError(message));
classBuilder.method(mdb);
}
private Iterable> overloads(Functional f) {
java.util.List> result = new ArrayList>();
result.add(f.getFirstParameterList().getParameters());
int ii = 0;
for (Parameter p : f.getFirstParameterList().getParameters()) {
if (p.isDefaulted()
|| (p.isSequenced() && !p.isAtLeastOne())) {
result.add(f.getFirstParameterList().getParameters().subList(0, ii));
}
ii++;
}
return result;
}
private void addRefinedThrowerMethod(ClassDefinitionBuilder classBuilder,
String error, ClassOrInterface classModel,
Function formalMethod) {
Function refined = refineMethod(classModel,
classModel.getType().getTypedMember(formalMethod, Collections.emptyList()),
classModel, formalMethod, classModel.getUnit());
// TODO When the method in inherited from an interface and is defaulted
// we need to generate a DPM as well, otherwise the class is missing
// the DPM and javac barfs.
for (java.util.List parameterList : overloads(refined)) {
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.method(this, refined);
mdb.isOverride(true);
mdb.modifiers(modifierTransformation().method(refined));
for (TypeParameter tp: formalMethod.getTypeParameters()) {
mdb.typeParameter(tp);
mdb.reifiedTypeParameter(tp);
}
for (Parameter param : parameterList) {
mdb.parameter(null, param, null, 0, WideningRules.NONE);
}
mdb.resultType(refined, 0);
mdb.body(makeThrowUnresolvedCompilationError(error));
classBuilder.method(mdb);
}
}
private Class refineClass(
Scope container,
Reference pr,
ClassOrInterface classModel,
Class formalClass,
Unit unit) {
Class refined = new Class();
refined.setActual(true);
refined.setShared(formalClass.isShared());
refined.setContainer(container);
refined.setExtendedType(formalClass.getType());
refined.setDeprecated(formalClass.isDeprecated());
refined.setName(formalClass.getName());
refined.setRefinedDeclaration(formalClass.getRefinedDeclaration());
refined.setScope(container);
//refined.setType(pr.getType());
refined.setUnit(unit);
for (ParameterList formalPl : formalClass.getParameterLists()) {
ParameterList refinedPl = new ParameterList();
for (Parameter formalP : formalPl.getParameters()){
Parameter refinedP = new Parameter();
refinedP.setAtLeastOne(formalP.isAtLeastOne());
refinedP.setDeclaration(refined);
refinedP.setDefaulted(formalP.isDefaulted());
refinedP.setDeclaredAnything(formalP.isDeclaredAnything());
refinedP.setHidden(formalP.isHidden());
refinedP.setSequenced(formalP.isSequenced());
refinedP.setName(formalP.getName());
final TypedReference typedParameter = pr.getTypedParameter(formalP);
FunctionOrValue paramModel;
if (formalP.getModel() instanceof Value) {
Value paramValueModel = refineValue((Value)formalP.getModel(), typedParameter, refined, classModel.getUnit());
paramValueModel.setInitializerParameter(refinedP);
paramModel = paramValueModel;
} else {
Function paramFunctionModel = refineMethod(refined, typedParameter, classModel, (Function)formalP.getModel(), unit);
paramFunctionModel.setInitializerParameter(refinedP);
paramModel = paramFunctionModel;
}
refinedP.setModel(paramModel);
refinedPl.getParameters().add(refinedP);
}
refined.addParameterList(refinedPl);
}
return refined;
}
private Function refineMethod(
Scope container,
TypedReference pr,
ClassOrInterface classModel,
Function formalMethod,
Unit unit) {
Function refined = new Function();
refined.setActual(true);
refined.setShared(formalMethod.isShared());
refined.setContainer(container);
refined.setDefault(true);// in case there are subclasses
refined.setDeferred(false);
refined.setDeprecated(formalMethod.isDeprecated());
refined.setName(formalMethod.getName());
refined.setRefinedDeclaration(formalMethod.getRefinedDeclaration());
refined.setScope(container);
refined.setType(pr.getType());
refined.setUnit(unit);
refined.setUnboxed(formalMethod.getUnboxed());
refined.setUntrustedType(formalMethod.getUntrustedType());
refined.setTypeErased(formalMethod.getTypeErased());
ArrayList refinedTp = new ArrayList();;
for (TypeParameter formalTp : formalMethod.getTypeParameters()) {
refinedTp.add(formalTp);
}
refined.setTypeParameters(refinedTp);
for (ParameterList formalPl : formalMethod.getParameterLists()) {
ParameterList refinedPl = new ParameterList();
for (Parameter formalP : formalPl.getParameters()){
Parameter refinedP = new Parameter();
refinedP.setAtLeastOne(formalP.isAtLeastOne());
refinedP.setDeclaration(refined);
refinedP.setDefaulted(formalP.isDefaulted());
refinedP.setDeclaredAnything(formalP.isDeclaredAnything());
refinedP.setHidden(formalP.isHidden());
refinedP.setSequenced(formalP.isSequenced());
refinedP.setName(formalP.getName());
final TypedReference typedParameter = pr.getTypedParameter(formalP);
FunctionOrValue paramModel;
if (formalP.getModel() instanceof Value) {
Value paramValueModel = refineValue((Value)formalP.getModel(), typedParameter, refined, classModel.getUnit());
paramValueModel.setInitializerParameter(refinedP);
paramModel = paramValueModel;
} else {
Function paramFunctionModel = refineMethod(refined, typedParameter, classModel, (Function)formalP.getModel(), unit);
paramFunctionModel.setInitializerParameter(refinedP);
paramModel = paramFunctionModel;
}
refinedP.setModel(paramModel);
refinedPl.getParameters().add(refinedP);
}
refined.addParameterList(refinedPl);
}
return refined;
}
/**
* Adds a getter (and possibly a setter) to {@code classBuilder}
* which throws
* @param classBuilder The class builder to add the method to
* @param error The message
* @param classModel The class which doesn't refine {@code formalAttribute}
* @param formalAttribute The formal attribute that hasn't been refined in {@code classModel}
*/
private void addRefinedThrowerAttribute(
ClassDefinitionBuilder classBuilder, String error,
Node node,
ClassOrInterface classModel, Value formalAttribute) {
Value refined = refineValue(formalAttribute, formalAttribute.appliedTypedReference(null, null), classModel, classModel.getUnit());
AttributeDefinitionBuilder getterBuilder = AttributeDefinitionBuilder.getter(this, refined.getName(), refined);
getterBuilder.skipField();
getterBuilder.modifiers(modifierTransformation().getterSetter(refined, false));
getterBuilder.getterBlock(make().Block(0, List.of(makeThrowUnresolvedCompilationError(error))));
classBuilder.attribute(getterBuilder);
if (formalAttribute.isVariable()) {
AttributeDefinitionBuilder setterBuilder = AttributeDefinitionBuilder.setter(this, node, refined.getName(), refined);
setterBuilder.skipField();
setterBuilder.modifiers(modifierTransformation().getterSetter(refined, false));
setterBuilder.setterBlock(make().Block(0, List.of(makeThrowUnresolvedCompilationError(error))));
classBuilder.attribute(setterBuilder);
}
}
private Value refineValue(Value formalAttribute,
TypedReference producedValue,
Scope container,
Unit unit) {
Value refined = new Value();
refined.setActual(true);
refined.setContainer(container);
refined.setName(formalAttribute.getName());
refined.setRefinedDeclaration(formalAttribute.getRefinedDeclaration());
refined.setScope(container);
refined.setVariable(formalAttribute.isVariable());
refined.setShared(formalAttribute.isShared());
refined.setTransient(formalAttribute.isTransient());
refined.setType(producedValue.getType());// TODO
refined.setTypeErased(formalAttribute.getTypeErased());
refined.setUnboxed(formalAttribute.getUnboxed());
refined.setUntrustedType(formalAttribute.getUntrustedType());
refined.setUnit(unit);
return refined;
}
private void transformClassAlias(final Tree.ClassDeclaration def,
ClassDefinitionBuilder classBuilder) {
ClassAlias model = (ClassAlias)def.getDeclarationModel();
Type aliasedClass = model.getExtendedType();
TypeDeclaration classOrCtor = def.getClassSpecifier().getType().getDeclarationModel();
while (classOrCtor instanceof ClassAlias) {
classOrCtor = ((ClassAlias)classOrCtor).getConstructor();
}
classBuilder.annotations(makeAtAlias(aliasedClass, classOrCtor instanceof Constructor ? (Constructor)classOrCtor : null));
classBuilder.isAlias(true);
MethodDefinitionBuilder instantiator = transformClassAliasInstantiator(
def, model, aliasedClass);
ClassDefinitionBuilder cbInstantiator = null;
switch (Strategy.defaultParameterMethodOwner(model)) {
case STATIC:
cbInstantiator = classBuilder;
break;
case OUTER:
cbInstantiator = classBuilder.getContainingClassBuilder();
break;
case OUTER_COMPANION:
cbInstantiator = classBuilder.getContainingClassBuilder().getCompanionBuilder(Decl.getClassOrInterfaceContainer(model, true));
break;
default:
throw BugException.unhandledEnumCase(Strategy.defaultParameterMethodOwner(model));
}
cbInstantiator.method(instantiator);
}
/**
* Builds the instantiator method for a class aliases. In 1.0 you can't
* actually invoke these, they exist just so there's somewhere to put the
* class alias annotations. In 1.2 (when we fix #1295) the
* instantiators will actually do something.
*/
private MethodDefinitionBuilder transformClassAliasInstantiator(
final Tree.AnyClass def, Class model, Type aliasedClass) {
MethodDefinitionBuilder instantiator = MethodDefinitionBuilder.systemMethod(this, NamingBase.getAliasInstantiatorMethodName(model));
int f = 0;
if (Strategy.defaultParameterMethodStatic(def.getDeclarationModel())) {
f = STATIC;
}
instantiator.modifiers((modifierTransformation().classFlags(model) & ~FINAL)| f);
for (TypeParameter tp : typeParametersOfAllContainers(model, false)) {
instantiator.typeParameter(tp);
}
transformTypeParameters(instantiator, model);
instantiator.resultType(new TransformedType(makeJavaType(aliasedClass), null, makeAtNonNull()));
instantiator.annotationFlags(Annotations.MODEL_AND_USER | Annotations.IGNORE);
// We need to reify the parameters, at least so they have reified annotations
for (final Tree.Parameter param : def.getParameterList().getParameters()) {
// Overloaded instantiators
Parameter paramModel = param.getParameterModel();
at(param);
transformParameter(instantiator, param, paramModel, Decl.getMemberDeclaration(def, param));
}
instantiator.body(make().Throw(makeNewClass(makeJavaType(typeFact().getExceptionType(), JT_CLASS_NEW))));
return instantiator;
}
/**
* Generates a constructor for an annotation class which takes the
* annotation type as parameter.
* @param classBuilder
*/
private void transformAnnotationClassConstructor(
Tree.AnyClass def,
ClassDefinitionBuilder classBuilder) {
Class klass = def.getDeclarationModel();
MethodDefinitionBuilder annoCtor = classBuilder.addConstructor(klass.isDeprecated());
annoCtor.ignoreModelAnnotations();
// constructors are never final
annoCtor.modifiers(modifierTransformation().classFlags(klass) & ~FINAL);
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, "anno");
pdb.type(new TransformedType(makeJavaType(klass.getType(), JT_ANNOTATION), null, makeAtNonNull()));
annoCtor.parameter(pdb);
// It's up to the caller to invoke value() on the Java annotation for a sequenced
// annotation
ListBuffer args = new ListBuffer();
for (Tree.Parameter parameter : def.getParameterList().getParameters()) {
at(parameter);
Parameter parameterModel = parameter.getParameterModel();
JCExpression annoAttr = make().Apply(null, naming.makeQuotedQualIdent(naming.makeUnquotedIdent("anno"),
parameter.getParameterModel().getName()),
List.nil());
Type parameterType = parameterModel.getType();
JCExpression argExpr;
if (typeFact().isIterableType(parameterType)
&& !isCeylonString(parameterType)) {
// Convert from array to Sequential
Type iteratedType = typeFact().getIteratedType(parameterType);
boolean nonEmpty = typeFact().isNonemptyIterableType(parameterType);
if (isCeylonBasicType(iteratedType)) {
argExpr = utilInvocation().sequentialWrapperBoxed(annoAttr);
} else if (Decl.isAnnotationClass(iteratedType.getDeclaration())) {
// Can't use Util.sequentialAnnotation becase we need to 'box'
// the Java annotations in their Ceylon annotation class
argExpr = make().Apply(null, naming.makeUnquotedIdent(naming.getAnnotationSequenceMethodName()), List.of(annoAttr));
ListBuffer stmts = new ListBuffer();
SyntheticName array = naming.synthetic(Unfix.$array$);
SyntheticName sb = naming.synthetic(Unfix.$sb$);
SyntheticName index = naming.synthetic(Unfix.$index$);
SyntheticName element = naming.synthetic(Unfix.$element$);
stmts.append(makeVar(FINAL, sb,
make().TypeArray(make().Type(syms().objectType)),
make().NewArray(make().Type(syms().objectType), List.of(naming.makeQualIdent(array.makeIdent(), "length")), null)));
stmts.append(makeVar(index,
make().Type(syms().intType),
make().Literal(0)));
stmts.append(make().ForeachLoop(
makeVar(element, makeJavaType(iteratedType, JT_ANNOTATION), null),
array.makeIdent(),
make().Exec(make().Assign(
make().Indexed(sb.makeIdent(),
make().Unary(JCTree.Tag.POSTINC, index.makeIdent())),
instantiateAnnotationClass(iteratedType, element.makeIdent())))));
stmts.append(make().Return(
make().NewClass(null,
null,
make().QualIdent(syms().ceylonTupleType.tsym),
List.of(makeReifiedTypeArgument(iteratedType),
sb.makeIdent(),
makeEmpty(),
make().Literal(false)),
null)));
classBuilder.method(
MethodDefinitionBuilder.systemMethod(this, naming.getAnnotationSequenceMethodName())
.ignoreModelAnnotations()
.modifiers(PRIVATE | STATIC)
.resultType(new TransformedType(makeJavaType(typeFact().getSequentialType(iteratedType)), null, makeAtNonNull()))
.parameter(ParameterDefinitionBuilder.systemParameter(this, array.getName())
.type(new TransformedType(make().TypeArray(makeJavaType(iteratedType, JT_ANNOTATION)))))
.body(stmts.toList()));
} else if (isCeylonMetamodelDeclaration(iteratedType)) {
argExpr = makeMetamodelInvocation("parseMetamodelReferences",
List.of(makeReifiedTypeArgument(iteratedType), annoAttr),
List.of(makeJavaType(iteratedType, JT_TYPE_ARGUMENT)));
} else if (Decl.isEnumeratedTypeWithAnonCases(iteratedType)) {
argExpr = makeMetamodelInvocation("parseEnumerationReferences",
List.of(makeReifiedTypeArgument(iteratedType), annoAttr),
List.of(makeJavaType(iteratedType, JT_TYPE_ARGUMENT)));
} else {
argExpr = makeErroneous(parameter, "compiler bug");
}
if (nonEmpty) {
argExpr = make().TypeCast(makeJavaType(parameterType), argExpr);
}
} else if (Decl.isAnnotationClass(parameterType.getDeclaration())) {
argExpr = instantiateAnnotationClass(parameterType, annoAttr);
} else if (isCeylonMetamodelDeclaration(parameterType)) {
argExpr = makeMetamodelInvocation("parseMetamodelReference",
List.of(annoAttr),
List.of(makeJavaType(parameterType, JT_TYPE_ARGUMENT)));
} else if (Decl.isEnumeratedTypeWithAnonCases(parameterType)) {
argExpr = makeMetamodelInvocation("parseEnumerationReference",
List.of(annoAttr),
null);
} else {
argExpr = annoAttr;
argExpr = expressionGen().applyErasureAndBoxing(annoAttr, parameterType.withoutUnderlyingType(), false, BoxingStrategy.UNBOXED, parameterType);
}
args.add(argExpr);
}
annoCtor.body(at(def).Exec(
make().Apply(null, naming.makeThis(), args.toList())));
}
private JCNewClass instantiateAnnotationClass(
Type annotationClass,
JCExpression javaAnnotationInstance) {
return make().NewClass(null, null, makeJavaType(annotationClass),
List.of(javaAnnotationInstance), null);
}
/**
* Transforms an annotation class into a Java annotation type.
*
* annotation class Foo(String s, Integer i=1) {}
*
* is transformed into
*
* @Retention(RetentionPolicy.RUNTIME)
* @interface Foo$annotation$ {
* String s();
* long i() default 1;
* }
*
* If the annotation class is a subtype of SequencedAnnotation a wrapper
* annotation is also generated:
*
* @Retention(RetentionPolicy.RUNTIME)
* @interface Foo$annotations${
* Foo$annotation$[] value();
* }
*
*/
private List transformAnnotationClass(Tree.AnyClass def) {
Class klass = (Class)def.getDeclarationModel();
String annotationName = Naming.suffixName(Suffix.$annotation$, klass.getName());
ClassDefinitionBuilder annoBuilder = ClassDefinitionBuilder.klass(this, annotationName, null, false);
// annotations are never explicitly final in Java
annoBuilder.modifiers(Flags.ANNOTATION | Flags.INTERFACE | (modifierTransformation().classFlags(klass) & ~FINAL));
annoBuilder.getInitBuilder().modifiers(modifierTransformation().classFlags(klass) & ~FINAL);
annoBuilder.annotations(makeAtRetention(RetentionPolicy.RUNTIME));
annoBuilder.annotations(makeAtIgnore());
annoBuilder.annotations(expressionGen().transformAnnotations(OutputElement.ANNOTATION_TYPE, def));
if (isSequencedAnnotation(klass)) {
if (getTarget().compareTo(Target.JDK1_8) >= 0) {
annoBuilder.annotations(makeAtRepeatable(klass.getType()));
annoBuilder.annotations(transformAnnotationConstraints(klass));
} else {
annoBuilder.annotations(makeAtAnnotationTarget(EnumSet.noneOf(AnnotationTarget.class)));
}
} else {
annoBuilder.annotations(transformAnnotationConstraints(klass));
}
for (Tree.Parameter p : def.getParameterList().getParameters()) {
Parameter parameterModel = p.getParameterModel();
annoBuilder.method(makeAnnotationMethod(p));
}
List result;
if (isSequencedAnnotation(klass)) {
result = annoBuilder.build();
String wrapperName = Naming.suffixName(Suffix.$annotations$, klass.getName());
ClassDefinitionBuilder sequencedBuilder = ClassDefinitionBuilder.klass(this, wrapperName, null, false);
// annotations are never explicitely final in Java
sequencedBuilder.modifiers(Flags.ANNOTATION | Flags.INTERFACE | (modifierTransformation().classFlags(klass) & ~FINAL));
sequencedBuilder.annotations(makeAtRetention(RetentionPolicy.RUNTIME));
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, naming.getSequencedAnnotationMethodName());
mdb.annotationFlags(Annotations.MODEL_AND_USER);
mdb.modifiers(PUBLIC | ABSTRACT);
mdb.resultType(new TransformedType(make().TypeArray(makeJavaType(klass.getType(), JT_ANNOTATION)), null, makeAtNonNull()));
mdb.noBody();
ClassDefinitionBuilder sequencedAnnotation = sequencedBuilder.method(mdb);
sequencedAnnotation.annotations(transformAnnotationConstraints(klass));
sequencedAnnotation.annotations(makeAtIgnore());
result = result.appendList(sequencedAnnotation.build());
} else {
result = annoBuilder.build();
}
return result;
}
private List makeAtRetention(RetentionPolicy retentionPolicy) {
return List.of(
make().Annotation(
make().Type(syms().retentionType),
List.of(naming.makeQuotedQualIdent(make().Type(syms().retentionPolicyType), retentionPolicy.name()))));
}
private List makeAtRepeatable(Type containerClassLiteral) {
return List.of(
make().Annotation(
make().Type(syms().repeatableType),
List.of(makeClassLiteral(containerClassLiteral, JT_ANNOTATIONS))));
}
/**
* Makes {@code @java.lang.annotation.Target(types)}
* where types are the given element types.
*/
private List makeAtAnnotationTarget(EnumSet types) {
List typeExprs = List.nil();
for (AnnotationTarget type : types) {
typeExprs = typeExprs.prepend(naming.makeQuotedQualIdent(make().Type(syms().elementTypeType), type.name()));
}
return List.of(
make().Annotation(
make().Type(syms().targetType),
List.of(make().NewArray(null, null, typeExprs))));
}
private List transformAnnotationConstraints(Class klass) {
TypeDeclaration meta = (TypeDeclaration)typeFact().getLanguageModuleDeclaration("ConstrainedAnnotation");
Type constrainedType = klass.getType().getSupertype(meta);
EnumSet types = EnumSet.noneOf(AnnotationTarget.class);
if (constrainedType != null) {
Type programElement = constrainedType.getTypeArgumentList().get(2);
if (programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("InterfaceDeclaration")).getType())
|| programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("ClassDeclaration")).getType())
|| programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("ClassWithInitializerDeclaration")).getType())
|| programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("ClassWithConstructorsDeclaration")).getType())
|| programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("Package")).getType())
|| programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("Module")).getType())) {
types.add(AnnotationTarget.TYPE);
}
if (programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("ValueDeclaration")).getType())
|| programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("FunctionDeclaration")).getType())) {
types.add(AnnotationTarget.METHOD);
types.add(AnnotationTarget.PARAMETER);
}
if (programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("CallableConstructorDeclaration")).getType())) {
types.add(AnnotationTarget.CONSTRUCTOR);
}
if (programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("ValueConstructorDeclaration")).getType())) {
types.add(AnnotationTarget.METHOD);
}
if (programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("Import")).getType())) {
types.add(AnnotationTarget.FIELD);
}
if (programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("SetterDeclaration")).getType())) {
types.add(AnnotationTarget.METHOD);
}
if (programElement.covers(((TypeDeclaration)typeFact().getLanguageModuleDeclarationDeclaration("AliasDeclaration")).getType())) {
types.add(AnnotationTarget.TYPE);
}
}
return makeAtAnnotationTarget(types);
}
private JCExpression transformAnnotationParameterDefault(Tree.Parameter p) {
Tree.SpecifierOrInitializerExpression defaultArgument = Decl.getDefaultArgument(p);
Tree.Expression defaultExpression = defaultArgument.getExpression();
Tree.Term term = defaultExpression.getTerm();
JCExpression defaultLiteral = null;
if (term instanceof Tree.Literal
&& !(term instanceof Tree.QuotedLiteral)) {
defaultLiteral = expressionGen().transform((Tree.Literal)term);
} else if (term instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme = (Tree.BaseMemberExpression)term;
Declaration decl = bme.getDeclaration();
if (isBooleanTrue(decl)) {
defaultLiteral = makeBoolean(true);
} else if (isBooleanFalse(decl)) {
defaultLiteral = makeBoolean(false);
} else if (typeFact().isEmptyType(bme.getTypeModel())) {
defaultLiteral = make().NewArray(null, null, List.nil());
} else if (Decl.isAnonCaseOfEnumeratedType(bme)) {
defaultLiteral = makeClassLiteral(bme.getTypeModel());
} else {
defaultLiteral = make().Literal(bme.getDeclaration().getQualifiedNameString());
}
} else if (term instanceof Tree.MemberOrTypeExpression) {
Tree.MemberOrTypeExpression mte = (Tree.MemberOrTypeExpression)term;
defaultLiteral = make().Literal(mte.getDeclaration().getQualifiedNameString());
} else if (term instanceof Tree.SequenceEnumeration) {
Tree.SequenceEnumeration seq = (Tree.SequenceEnumeration)term;
SequencedArgument sequencedArgument = seq.getSequencedArgument();
defaultLiteral = makeArrayInitializer(sequencedArgument);
} else if (term instanceof Tree.Tuple) {
Tree.Tuple seq = (Tree.Tuple)term;
SequencedArgument sequencedArgument = seq.getSequencedArgument();
defaultLiteral = makeArrayInitializer(sequencedArgument);
} else if (term instanceof Tree.InvocationExpression) {
// Allow invocations of annotation constructors, so long as they're
// themselves being invoked with permitted arguments
Tree.InvocationExpression invocation = (Tree.InvocationExpression)term;
try {
defaultLiteral = AnnotationInvocationVisitor.transform(expressionGen(), invocation);
} catch (BugException e) {
defaultLiteral = e.makeErroneous(this, invocation);
}
} else if (term instanceof Tree.MemberLiteral) {
defaultLiteral = expressionGen().makeMetaLiteralStringLiteralForAnnotation((Tree.MemberLiteral) term);
} else if (term instanceof Tree.TypeLiteral) {
defaultLiteral = expressionGen().makeMetaLiteralStringLiteralForAnnotation((Tree.TypeLiteral) term);
}
if (defaultLiteral == null) {
defaultLiteral = makeErroneous(p, "compiler bug: " + p.getParameterModel().getName() + " has an unsupported defaulted parameter expression");
}
return defaultLiteral;
}
private JCExpression transformAnnotationMethodType(Tree.Parameter parameter) {
Type parameterType = parameter.getParameterModel().getType();
JCExpression type = null;
if (isScalarAnnotationParameter(parameterType)) {
type = makeJavaType(parameterType.withoutUnderlyingType(), JT_ANNOTATION);
} else if (isMetamodelReference(parameterType)) {
type = make().Type(syms().stringType);
} else if (Decl.isEnumeratedTypeWithAnonCases(parameterType)) {
type = makeJavaClassTypeBounded(parameterType);
} else if (typeFact().isIterableType(parameterType)) {
Type iteratedType = typeFact().getIteratedType(parameterType);
if (isScalarAnnotationParameter(iteratedType)) {
JCExpression scalarType = makeJavaType(iteratedType, JT_ANNOTATION);
type = make().TypeArray(scalarType);
} else if (isMetamodelReference(iteratedType)) {
JCExpression scalarType = make().Type(syms().stringType);
type = make().TypeArray(scalarType);
} else if (Decl.isEnumeratedTypeWithAnonCases(iteratedType)) {
JCExpression scalarType = makeJavaClassTypeBounded(iteratedType);
type = make().TypeArray(scalarType);
}
}
if (type == null) {
type = makeErroneous(parameter, "compiler bug: " + parameter.getParameterModel().getName() + " has an unsupported annotation parameter type");
}
return type;
}
private boolean isMetamodelReference(Type parameterType) {
return isCeylonMetamodelDeclaration(parameterType);
}
/**
* Makes a new Array expression suitable for use in initializing a Java array
* using as elements the positional arguments
* of the given {@link Tree.SequencedArgument} (which must
* be {@link Tree.ListedArgument}s).
*
*
* Whatever[] w = { listedArg1, listedArg2, ... }
* // ^---------- this bit ---------^
*
*
* @param sequencedArgument
* @return The array initializer expression
*/
JCExpression makeArrayInitializer(
Tree.SequencedArgument sequencedArgument) {
JCExpression defaultLiteral;
ListBuffer elements = new ListBuffer();
if (sequencedArgument != null) {
for (Tree.PositionalArgument arg : sequencedArgument.getPositionalArguments()) {
if (arg instanceof Tree.ListedArgument) {
Tree.ListedArgument la = (Tree.ListedArgument)arg;
elements.append(expressionGen().transformExpression(la.getExpression().getTerm(), BoxingStrategy.UNBOXED, la.getExpression().getTypeModel()));
} else {
elements = null;
break;
}
}
}
defaultLiteral = elements == null ? null :
make().NewArray(null, List.nil(), elements.toList());
return defaultLiteral;
}
private boolean isScalarAnnotationParameter(Type parameterType) {
return isCeylonBasicType(parameterType)
|| Decl.isAnnotationClass(parameterType.getDeclaration());
}
private List visitClassOrInterfaceDefinition(Node def, ClassDefinitionBuilder classBuilder) {
// Transform the class/interface members
CeylonVisitor visitor = gen().visitor;
final ListBuffer prevDefs = visitor.defs;
final boolean prevInInitializer = visitor.inInitializer;
final ClassDefinitionBuilder prevClassBuilder = visitor.classBuilder;
final boolean prevInSynthetic = gen().expressionGen().withinSyntheticClassBody(false);
try {
visitor.defs = new ListBuffer();
visitor.inInitializer = true;
visitor.classBuilder = classBuilder;
def.visitChildren(visitor);
return (List)visitor.getResult().toList();
} finally {
visitor.classBuilder = prevClassBuilder;
visitor.inInitializer = prevInInitializer;
visitor.defs = prevDefs;
gen().expressionGen().withinSyntheticClassBody(prevInSynthetic);
naming.closeScopedSubstitutions(def.getScope());
}
}
private void generateInstantiators(ClassDefinitionBuilder classBuilder,
Class cls, Constructor ctor, ClassDefinitionBuilder instantiatorDeclCb,
ClassDefinitionBuilder instantiatorImplCb, Tree.Declaration node, Tree.ParameterList pl) {
// TODO Instantiators on companion classes
if (Decl.isEnumeratedConstructor(ctor)) {
return;
}
ParameterList parameterList = ctor != null ? ctor.getFirstParameterList() : cls.getParameterList();
if (Decl.withinInterface(cls)) {
DefaultedArgumentOverload overloaded = new DefaultedArgumentInstantiator(daoAbstract, cls, ctor, instantiatorDeclCb.isCompanionBuilder());
MethodDefinitionBuilder instBuilder = overloaded.makeOverload(
parameterList,
null,
cls.getTypeParameters());
instantiatorDeclCb.method(instBuilder);
}
if (!Decl.withinInterface(cls)
|| !cls.isFormal()) {
DefaultedArgumentOverload overloaded = new DefaultedArgumentInstantiator(!cls.isFormal() ? new DaoThis(node, pl) : daoAbstract,
cls, ctor, instantiatorImplCb.isCompanionBuilder());
MethodDefinitionBuilder instBuilder = overloaded.makeOverload(
parameterList,
null,
cls.getTypeParameters());
instantiatorImplCb.method(instBuilder);
}
}
private void makeAttributeForValueParameter(ClassDefinitionBuilder classBuilder,
Tree.Parameter parameterTree, Tree.TypedDeclaration memberTree) {
Parameter decl = parameterTree.getParameterModel();
if (!(decl.getModel() instanceof Value)) {
return;
}
final Value value = (Value)decl.getModel();
if (decl.getDeclaration() instanceof Constructor) {
classBuilder.field(PUBLIC | FINAL, decl.getName(), makeJavaType(decl.getType()),
null, false, expressionGen().transformAnnotations(OutputElement.FIELD, memberTree));
classBuilder.getInitBuilder().init(make().Exec(make().Assign(naming.makeQualIdent(naming.makeThis(), decl.getName()), naming.makeName(value, Naming.NA_IDENT))));
} else if (parameterTree instanceof Tree.ValueParameterDeclaration
&& (value.isShared() || value.isCaptured())) {
makeFieldForParameter(classBuilder, decl, memberTree);
AttributeDefinitionBuilder adb = AttributeDefinitionBuilder.getter(this, decl.getName(), decl.getModel());
adb.modifiers(classGen().modifierTransformation().getterSetter(decl.getModel(), false));
adb.userAnnotations(expressionGen().transformAnnotations(OutputElement.GETTER, memberTree));
classBuilder.attribute(adb);
if (value.isVariable()) {
AttributeDefinitionBuilder setter = AttributeDefinitionBuilder.setter(this, parameterTree, decl.getName(), decl.getModel());
setter.modifiers(classGen().modifierTransformation().getterSetter(decl.getModel(), false));
//setter.userAnnotations(expressionGen().transform(AnnotationTarget.SETTER, memberTree.getAnnotationList()));
classBuilder.attribute(setter);
}
} else if (decl.isHidden()
// TODO Isn't this always true here? We know this is a parameter to a Class
&& (decl.getDeclaration() instanceof TypeDeclaration)) {
Declaration member = CodegenUtil.findMethodOrValueForParam(decl);
if (Strategy.createField(decl, (Value)member)) {
// The field itself is created by when we transform the AttributeDeclaration
// but it has to be initialized here so all the fields are initialized in parameter order
at(parameterTree);
JCExpression parameterExpr = makeUnquotedIdent(Naming.getAliasedParameterName(decl));
TypedReference typedRef = getTypedReference(value);
TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
Type paramType = nonWideningType(typedRef, nonWideningTypedRef);
AttributeDefinitionBuilder adb = AttributeDefinitionBuilder.field(this, memberTree, value.getName(), value, false);
if (!paramType.isExactly(decl.getType())) {
// The parameter type follows normal erasure rules, not affected by inheritance
// but the attribute respects non-widening rules, so we may need to cast
// the parameter to the field type (see #1728)
parameterExpr = make().TypeCast(adb.valueFieldType(), parameterExpr);
}
at(parameterTree);
BoxingStrategy exprBoxed = CodegenUtil.isUnBoxed((TypedDeclaration)member) ? BoxingStrategy.UNBOXED : BoxingStrategy.BOXED;
adb.initialValue(parameterExpr, exprBoxed);
classBuilder.getInitBuilder().init(adb.buildInit(true));
}
}
}
private void makeFieldForParameter(ClassDefinitionBuilder classBuilder,
Parameter decl, Tree.Declaration annotated) {
FunctionOrValue model = decl.getModel();
AttributeDefinitionBuilder adb = AttributeDefinitionBuilder.field(this, annotated, decl.getName(), decl.getModel(), false);
adb.fieldAnnotations(makeAtIgnore().prependList(expressionGen().transformAnnotations(OutputElement.FIELD, annotated)));
adb.modifiers(modifierTransformation().transformClassParameterDeclFlagsField(decl, annotated));
BoxingStrategy exprBoxed = CodegenUtil.isUnBoxed(model) ? BoxingStrategy.UNBOXED : BoxingStrategy.BOXED;
BoxingStrategy boxingStrategy = useJavaBox(model, model.getType())
&& javaBoxExpression(model.getType(), model.getType())? BoxingStrategy.JAVA : exprBoxed;
JCExpression paramExpr = boxUnboxIfNecessary(naming.makeName(model, Naming.NA_IDENT_PARAMETER_ALIASED),
exprBoxed,
simplifyType(model.getType()),
boxingStrategy,
simplifyType(model.getType()));
adb.initialValue(paramExpr, boxingStrategy);
classBuilder.defs((List)adb.buildFields());
classBuilder.getInitBuilder().init(adb.buildInit(true));
}
/**
* Transform the parameter and its annotations and add it to the given builder
*/
private void transformParameter(ParameterizedBuilder> classBuilder,
Tree.Parameter p, Parameter param, Tree.TypedDeclaration member) {
JCExpression type = makeJavaType(param.getModel(), param.getType(), 0);
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.explicitParameter(this, param);
pdb.at(p);
pdb.aliasName(Naming.getAliasedParameterName(param));
if (Naming.aliasConstructorParameterName(param.getModel())) {
naming.addVariableSubst(param.getModel(), naming.suffixName(Suffix.$param$, param.getName()));
}
pdb.sequenced(param.isSequenced());
pdb.defaulted(param.isDefaulted());
pdb.type(new TransformedType(type,
makeJavaTypeAnnotations(param.getModel()),
makeNullabilityAnnotations(param.getModel())));
pdb.modifiers(modifierTransformation().transformClassParameterDeclFlags(param));
if (!(param.getModel().isShared() || param.getModel().isCaptured())) {
// We load the model for shared parameters from the corresponding member
pdb.modelAnnotations(param.getModel().getAnnotations());
}
if (member != null) {
pdb.userAnnotations(expressionGen().transformAnnotations(OutputElement.PARAMETER, member));
} else if (p instanceof Tree.ParameterDeclaration &&
Decl.isConstructor(param.getDeclaration())) {
pdb.userAnnotations(expressionGen().transformAnnotations(OutputElement.PARAMETER, ((Tree.ParameterDeclaration)p).getTypedDeclaration()));
}
if (/*classBuilder instanceof ClassDefinitionBuilder
&&*/ pdb.requiresBoxedVariableDecl()) {
((ClassDefinitionBuilder)classBuilder).getInitBuilder().init(pdb.buildBoxedVariableDecl());
}
classBuilder.parameter(pdb);
}
private void capturedReifiedTypeParameters(ClassOrInterface model,
ClassDefinitionBuilder classBuilder) {
if (model.isStatic()) {
ClassOrInterface outer = (ClassOrInterface)model.getContainer();
capturedReifiedTypeParameters(outer, classBuilder);
classBuilder.reifiedTypeParameters(outer.getTypeParameters());
}
}
private void transformClass(
Tree.AnyClass def,
Class model,
ClassDefinitionBuilder classBuilder,
Tree.ParameterList paramList,
boolean generateInstantiator,
ClassDefinitionBuilder instantiatorDeclCb,
ClassDefinitionBuilder instantiatorImplCb) {
// do reified type params first
//java.util.List typeParameters = typeParametersOfAllContainers(model, false);
//for(TypeParameter tp : typeParameters){
// classBuilder.typeParameter(tp, false);
//}
//capturedReifiedTypeParameters(model, classBuilder);
classBuilder.reifiedTypeParameters(Strategy.getEffectiveTypeParameters(model));
if (def.getParameterList() != null) {
TransformationPlan error = errors().hasDeclarationAndMarkBrokenness(def);
if (error instanceof ThrowerCatchallConstructor) {
InitializerBuilder initBuilder = classBuilder.getInitBuilder();
initBuilder.init(make().If(make().Literal(true),statementGen().makeThrowUnresolvedCompilationError(error.getErrorMessage().getMessage()), null));
}
for (Tree.Parameter param : def.getParameterList().getParameters()) {
Tree.TypedDeclaration member = def != null ? Decl.getMemberDeclaration(def, param) : null;
makeAttributeForValueParameter(classBuilder, param, member);
makeMethodForFunctionalParameter(classBuilder, param, member);
}
transformClassOrCtorParameters(def, model, null, def, def.getParameterList(), false,
classBuilder,
classBuilder.getInitBuilder(),
generateInstantiator, instantiatorDeclCb,
instantiatorImplCb);
}
satisfaction(def.getSatisfiedTypes(), model, classBuilder);
at(def);
// Generate the inner members list for model loading
addAtMembers(classBuilder, model, def);
addAtLocalDeclarations(classBuilder, def);
// Make sure top types satisfy reified type
addReifiedTypeInterface(classBuilder, model);
}
private void transformClassOrCtorParameters(
Tree.AnyClass def,
Class cls,
Constructor constructor,
Tree.Declaration node,
Tree.ParameterList paramList,
boolean delegationConstructor,
ClassDefinitionBuilder classBuilder,
ParameterizedBuilder> constructorBuilder,
boolean generateInstantiator,
ClassDefinitionBuilder instantiatorDeclCb,
ClassDefinitionBuilder instantiatorImplCb) {
for (final Tree.Parameter param : paramList.getParameters()) {
// Overloaded instantiators
Parameter paramModel = param.getParameterModel();
Parameter refinedParam = CodegenUtil.findParamForDecl(
(TypedDeclaration)CodegenUtil.getTopmostRefinedDeclaration(param.getParameterModel().getModel()));
at(param);
Tree.TypedDeclaration member = def != null ? Decl.getMemberDeclaration(def, param) : null;
// transform the parameter and its annotations
transformParameter(constructorBuilder, param, paramModel, member);
if (Strategy.hasDefaultParameterValueMethod(paramModel)
|| Strategy.hasDefaultParameterOverload(paramModel)
|| (generateInstantiator
&& refinedParam != null
&& (Strategy.hasDefaultParameterValueMethod(refinedParam)
|| Strategy.hasDefaultParameterOverload(refinedParam)))) {
ClassDefinitionBuilder cbForDevaultValues;
ClassDefinitionBuilder cbForDevaultValuesDecls = null;
switch (Strategy.defaultParameterMethodOwner(constructor != null ? constructor : cls)) {
case STATIC:
cbForDevaultValues = classBuilder;
break;
case OUTER:
cbForDevaultValues = classBuilder.getContainingClassBuilder();
break;
case OUTER_COMPANION:
cbForDevaultValues = classBuilder.getContainingClassBuilder().getCompanionBuilder(Decl.getClassOrInterfaceContainer(cls, true));
if ((constructor == null || constructor.isShared())
&& cls.isShared()) {
cbForDevaultValuesDecls = classBuilder.getContainingClassBuilder();
}
break;
default:
cbForDevaultValues = classBuilder.getCompanionBuilder(cls);
}
if (!delegationConstructor) {
if ((Strategy.hasDefaultParameterValueMethod(paramModel)
|| (refinedParam != null && Strategy.hasDefaultParameterValueMethod(refinedParam)))) {
if (!generateInstantiator || Decl.equal(refinedParam, paramModel)) {
// transform the default value into a method
cbForDevaultValues.method(makeParamDefaultValueMethod(false, constructor != null ? constructor : cls, paramList, param));
if (cbForDevaultValuesDecls != null) {
cbForDevaultValuesDecls.method(makeParamDefaultValueMethod(true, constructor != null ? constructor : cls, paramList, param));
}
} else if (Strategy.hasDelegatedDpm(cls) && cls.getContainer() instanceof Class) {
// generate a dpm which delegates to the companion
java.util.List parameters = paramList.getModel().getParameters();
MethodDefinitionBuilder mdb =
makeDelegateToCompanion((Interface)cls.getRefinedDeclaration().getContainer(),
constructor != null ? constructor.getReference() : cls.getReference(),
((TypeDeclaration)cls.getContainer()).getType(),
FINAL | (modifierTransformation().classFlags(cls) & ~ABSTRACT),
List.nil(), Collections.>emptyList(),
paramModel.getType().getFullType(),
Naming.getDefaultedParamMethodName(cls, paramModel),
parameters.subList(0, parameters.indexOf(paramModel)),
false,
Naming.getDefaultedParamMethodName(cls, paramModel),
param.getParameterModel());
cbForDevaultValues.method(mdb);
}
}
}
boolean addOverloadedConstructor = false;
if (generateInstantiator) {
if (Decl.withinInterface(cls)) {
MethodDefinitionBuilder instBuilder = new DefaultedArgumentInstantiator(daoAbstract, cls, constructor,
instantiatorDeclCb.isCompanionBuilder()).makeOverload(
paramList.getModel(),
param.getParameterModel(),
cls.getTypeParameters());
instantiatorDeclCb.method(instBuilder);
}
if (!Decl.withinInterface(cls) || !cls.isFormal()) {
MethodDefinitionBuilder instBuilder = new DefaultedArgumentInstantiator(new DaoThis(node, paramList), cls, constructor,
instantiatorImplCb.isCompanionBuilder()).makeOverload(
paramList.getModel(),
param.getParameterModel(),
cls.getTypeParameters());
instantiatorImplCb.method(instBuilder);
} else {
addOverloadedConstructor = true;
}
} else {
addOverloadedConstructor = true;
}
if (addOverloadedConstructor) {
// Add overloaded constructors for defaulted parameter
MethodDefinitionBuilder overloadBuilder;
DefaultedArgumentConstructor dac;
if (constructor != null) {
dac = new DefaultedArgumentConstructor(classBuilder.addConstructor(constructor.isDeprecated()), constructor, node, paramList, delegationConstructor);
} else {
dac = new DefaultedArgumentConstructor(classBuilder.addConstructor(cls.isDeprecated()), cls, node, paramList, delegationConstructor);
}
overloadBuilder = dac.makeOverload(
paramList.getModel(),
param.getParameterModel(),
cls.getTypeParameters());
}
}
}
}
private ParameterDefinitionBuilder makeConstructorNameParameter(Constructor ctor) {
return makeConstructorNameParameter(ctor, DeclNameFlag.QUALIFIED);
}
private ParameterDefinitionBuilder makeConstructorNameParameter(Constructor ctor, DeclNameFlag... flags) {
Class clz = (Class)ctor.getContainer();
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.implicitParameter(this, Naming.Unfix.$name$.toString());
pdb.ignored();
JCExpression type = naming.makeTypeDeclarationExpression(null, ctor, flags);
pdb.type(new TransformedType(type, null, makeAtNullable()));
return pdb;
}
/**
* Add extra constructor and methods required for serialization
*/
private void serialization(Class model, ClassDefinitionBuilder classBuilder) {
if (!model.isSerializable()) {
return;
}
at(null);
classBuilder.serializable();
serializationConstructor(model, classBuilder);
serializationReferences(model, classBuilder);
serializationGet(model, classBuilder);
serializationSet(model, classBuilder);
}
private boolean hasField(Declaration member) {
if (member instanceof Value) {
Value value = (Value)member;
if (!value.isTransient()
&& !value.isFormal()
&& !ModelUtil.isConstructor(value)
&& (value.isShared() || value.isCaptured())) {
return true;
}
} /*else if (member instanceof Function) {
Function function = (Function)member;
if (function.isShortcutRefinement()
|| (function.isDeferred() && function.isCaptured())
|| function.isParameter()
&& (function.isCaptured()
|| function.isShared()
|| function.isActual())) {
return true;
}
}*/
return false;
}
/**
* Generates the serialization constructor
* with signature {@code ($Serialization$)} which:
*
* - invokes {@code super()}, if the super class is also
* serializable,
* - initializes all companion instance fields to a
* newly instantiated companion instance,
* - initializes all reified type argument fields to null,
* - initializes all reference attribute fields to null,
* - initializesall primitive attribute fields to a default
* value (basically some kind of 0)
*
*/
private void serializationConstructor(Class model,
ClassDefinitionBuilder classBuilder) {
MethodDefinitionBuilder ctor = classBuilder.addConstructor(model.isDeprecated());
ctor.ignoreModelAnnotations();
ctor.modifiers(PUBLIC);
ParameterDefinitionBuilder serializationPdb = ParameterDefinitionBuilder.systemParameter(this, "ignored");
serializationPdb.modifiers(FINAL);
serializationPdb.type(new TransformedType(make().Type(syms().ceylonSerializationType), null));
ctor.parameter(serializationPdb);
for (TypeParameter tp : model.getTypeParameters()) {
ctor.reifiedTypeParameter(tp);
}
final ListBuffer stmts = new ListBuffer();
if (extendsSerializable(model)) {
// invoke super
ListBuffer superArgs = new ListBuffer();
superArgs.add(naming.makeUnquotedIdent("ignored"));
for (JCExpression ta : makeReifiedTypeArguments(model.getExtendedType())) {
superArgs.add(ta);
}
stmts.add(make().Exec(make().Apply(null,
naming.makeSuper(),
superArgs.toList())));
}
buildFieldInits(model, classBuilder, stmts);
ctor.body(stmts.toList());
}
protected void buildFieldInits(Class model,
ClassDefinitionBuilder classBuilder,
final ListBuffer stmts) {
final HashSet excludeFields = new HashSet();
// initialize reified type arguments to according to parameters
for (TypeParameter tp : model.getTypeParameters()) {
excludeFields.add(naming.getTypeArgumentDescriptorName(tp));
stmts.add(makeReifiedTypeParameterAssignment(tp));
}
// initialize companion instances to a new companion instance
if (!model.getSatisfiedTypes().isEmpty()) {
SatisfactionVisitor visitor = new SatisfactionVisitor() {
@Override
public void satisfiesDirectly(Class model, Interface iface, boolean alreadySatisfied) {
if (!alreadySatisfied) {
assignCompanion(model, iface);
}
}
@Override
public void satisfiesIndirectly(Class model, Interface iface, boolean alreadySatisfied) {
if (!alreadySatisfied) {
assignCompanion(model, iface);
}
}
private void assignCompanion(Class model, Interface iface) {
if (hasImpl(iface)
&& excludeFields.add(getCompanionFieldName(iface))) {
stmts.add(makeCompanionInstanceAssignment(model, iface, model.getType().getSupertype(iface)));
}
}
@Override
public void satisfiesIndirectlyViaClass(Class model,
Interface iface, Class via, boolean alreadySatisfied) {
// don't care
}
};
walkSatisfiedInterfaces(model, model.getType(), visitor);
}
// initialize attribute fields to null or a zero
appendDefaultFieldInits(classBuilder, stmts, excludeFields);
}
protected void appendDefaultFieldInits(ClassDefinitionBuilder model,
final ListBuffer stmts,
Collection excludeFields) {
for (JCVariableDecl field : model.getFields()) {
String fieldName = field.name.toString();
if (excludeFields != null && excludeFields.contains(fieldName)) {
continue;
}
if (field.mods != null
&& (field.mods.flags & STATIC) != 0) {
continue;
}
// initialize all reference fields to null and all primitive
// fields to a default value.
JCExpression nullOrZero;
if (field.vartype instanceof JCPrimitiveTypeTree) {
switch (((JCPrimitiveTypeTree)field.vartype).typetag) {
case BYTE:
case SHORT:
case INT:
nullOrZero = make().Literal(0);
break;
case LONG:
nullOrZero = make().Literal(0L);
break;
case FLOAT:
nullOrZero = make().Literal(0.0f);
break;
case DOUBLE:
nullOrZero = make().Literal(0.0);
break;
case BOOLEAN:
nullOrZero = make().Literal(false);
break;
case CHAR:
nullOrZero = make().Literal(TypeTag.CHAR, 0);
break;
default:
throw new RuntimeException();
}
}else {
nullOrZero = makeNull();
}
stmts.add(make().Exec(make().Assign(
naming.makeQualIdent(naming.makeThis(), fieldName),
nullOrZero)));
}
}
static interface SatisfactionVisitor {
/** The class satisfies the interface directly */
void satisfiesDirectly(Class model, Interface iface, boolean alreadySatisfied);
/** The class satisfies the interface indirectly via interfaces only */
void satisfiesIndirectly(Class model, Interface iface, boolean alreadySatisfied);
/** The class satisfies the interface indirectly via the given superclass */
void satisfiesIndirectlyViaClass(Class model, Interface iface, Class via, boolean alreadySatisfied);
}
private void walkSatisfiedInterfaces(final Class model,
Type type,
SatisfactionVisitor visitor) {
walkSatisfiedInterfacesInternal(model, type, null, visitor, new HashSet());
}
private void walkSatisfiedInterfacesInternal(final Class model,
Type type,
Class via,
SatisfactionVisitor visitor,
Set satisfiedInterfaces) {
type = type.resolveAliases();
Type ext = type.getExtendedType();
if (ext != null) {
// recurse up this extended class
walkSatisfiedInterfacesInternal(model, ext, (Class)ext.getDeclaration(), visitor, satisfiedInterfaces);
}
for (Type sat : type.getSatisfiedTypes()) {
Interface iface = (Interface)sat.getDeclaration();
if (iface.getType().isExactly(typeFact().getIdentifiableDeclaration().getType())) {
return;
}
// recurse up this satisfies interface
walkSatisfiedInterfacesInternal(model, sat, via, visitor, satisfiedInterfaces);
boolean alreadySatisfied = !satisfiedInterfaces.add((Interface)model.getType().getSupertype(iface).getDeclaration());
if (via != null) {
visitor.satisfiesIndirectlyViaClass(model, iface, via, alreadySatisfied);
} else if (model.getType().equals(type)) {
visitor.satisfiesDirectly(model, iface, alreadySatisfied);
} else {
visitor.satisfiesIndirectly(model, iface, alreadySatisfied);
}
}
}
private boolean extendsSerializable(Class model) {
return !typeFact().getObjectType().isExactly(model.getExtendedType())
&& !typeFact().getBasicType().isExactly(model.getExtendedType());
}
/**
* Generates the {@code $deserialize$()} method to deserialize
* the classes state, which:
*
* - invokes {@code super.$deserialize$()}, if the super class is also
* serializable,
* - assigns each reified type argument in the
* class by invoking {@code dted.getTypeArgument()},
* - assigns each field in the
* class by invoking {@code dted.getValue()}.
*
*/
private void serializationReferences(Class model,
ClassDefinitionBuilder classBuilder) {
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, Unfix.$references$.toString());
mdb.isOverride(true);
mdb.ignoreModelAnnotations();
mdb.modifiers(PUBLIC);
mdb.resultType(new TransformedType(make().TypeApply(naming.makeQuotedFQIdent("java.util.Collection"),
List.of(make().Type(syms().ceylonReachableReferenceType))), null, makeAtNonNull()));
ListBuffer stmts = new ListBuffer();
// TODO this is all static information, but the method itself needs to be
// callable virtually, so we should cache it somehow.
SyntheticName r = naming.synthetic(Unfix.reference);
if (extendsSerializable(model)) {
// prepend the invocation of super.$serialize$()
stmts.add(makeVar(r,
make().TypeApply(naming.makeQuotedFQIdent("java.util.Collection"),
List.of(make().Type(syms().ceylonReachableReferenceType))),
make().Apply(null,
naming.makeQualIdent(naming.makeSuper(), Unfix.$references$.toString()),
List.nil())));
} else {
stmts.add(makeVar(r,
make().TypeApply(naming.makeQuotedFQIdent("java.util.Collection"),
List.of(make().Type(syms().ceylonReachableReferenceType))),
make().NewClass(null, null,
make().TypeApply(
naming.makeQuotedFQIdent("java.util.ArrayList"),
List.of(make().Type(syms().ceylonReachableReferenceType))),
List.nil(),
null)));
}
if (model.isMember()) {
JCExpressionStatement outer = make().Exec(make().Apply(null,
naming.makeQualIdent(r.makeIdent(), "add"),
List.of(
make().Apply(null,
naming.makeQualIdent(make().Type(syms().ceylonOuterImplType), "get_"), List.nil()))));
stmts.add(outer);
}
for (Declaration member : model.getMembers()) {
if (hasField(member)) {
// Obtain a ValueDeclaration
JCExpression valueDeclaration = expressionGen().makeMemberValueOrFunctionDeclarationLiteral(null, member, false);
// Create a MemberImpl
JCExpression mi = make().NewClass(null, null,
make().QualIdent(syms().ceylonMemberImplType.tsym),
List.of(valueDeclaration),
null);
JCExpressionStatement attribute = make().Exec(make().Apply(null,
naming.makeQualIdent(r.makeIdent(), "add"),
List.of(mi)));
stmts.add(attribute);
}
}
stmts.add(make().Return(r.makeIdent()));
mdb.body(stmts.toList());
classBuilder.method(mdb);
}
private void serializationGet(Class model,
ClassDefinitionBuilder classBuilder) {
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, Unfix.$get$.toString());
mdb.isOverride(true);
mdb.ignoreModelAnnotations();
mdb.modifiers(PUBLIC);
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, Unfix.reference.toString());
pdb.modifiers(FINAL);
pdb.type(new TransformedType(make().Type(syms().ceylonReachableReferenceType), null, makeAtNonNull()));
mdb.parameter(pdb);
mdb.resultType(new TransformedType(make().Type(syms().objectType), null, makeAtNonNull()));
/*
* public void $set$(Object reference, Object instance) {
* switch((String)reference) {
* case ("attr1")
* return ...;
* // ... other fields of this class
* case ("lateAttr1")
* if (!$init$lateAttr1) {
* return ceylon.language.serialization.uninitializedLateValue.get_();
* }
* return ...;
* case (null):
* return Outer.this;
* default:
* return super.get(reference);
*/
ListBuffer cases = new ListBuffer();
boolean[] needsLookup = new boolean[]{false};
for (Declaration member : model.getMembers()) {
if (hasField(member)) {
if (member instanceof Function)
continue; // TODO: This class is not serializable
ListBuffer caseStmts = new ListBuffer();
if (member instanceof Value
&& ((Value)member).isLate()) {
// TODO this should be encapsulated so the ADB and this
// code can just call something common
JCExpression test = AttributeDefinitionBuilder.field(this, null, member.getName(), (Value)member, false).buildUninitTest();
if (test != null) {
caseStmts.add(make().If(
test,
make().Return(makeLanguageSerializationValue("uninitializedLateValue")), null));
}
}
caseStmts.add(make().Return(makeSerializationGetter((Value)member)));
cases.add(make().Case(make().Literal(member.getQualifiedNameString()), caseStmts.toList()));
}
}
SyntheticName reference = naming.synthetic(Unfix.reference);
ListBuffer defaultCase = new ListBuffer();
if (extendsSerializable(model)) {
// super.get(reference);
defaultCase.add(make().Return(make().Apply(null,
naming.makeQualIdent(naming.makeSuper(), Unfix.$get$.toString()),
List.of(reference.makeIdent()))));
} else {
// throw (or pass to something else to throw, based on policy)
defaultCase.add(make().Throw(make().NewClass(null, null,
naming.makeQuotedFQIdent("java.lang.RuntimeException"),
List.of(make().Literal("unknown attribute")),
null)));
}
cases.add(make().Case(null, defaultCase.toList()));
ListBuffer stmts = new ListBuffer();
if (needsLookup[0]) {
// if we needed to use a lookup object to reset final fields,
// prepend that variable
stmts.add(makeVar(FINAL,
"lookup",
naming.makeQualIdent(make().Type(syms().methodHandlesType), "Lookup"),
make().Apply(null, naming.makeQuotedFQIdent("java.lang.invoke.MethodHandles.lookup"), List.nil())));
}
JCSwitch swtch = make().Switch(
make().Apply(null,
naming.makeSelect(make().Apply(null,
naming.makeSelect(
make().TypeCast(make().Type(syms().ceylonMemberType), reference.makeIdent()),
"getAttribute"),
List.nil()),
"getQualifiedName"),
List.nil()),
cases.toList());
if (model.isMember()
&& !model.getExtendedType().getDeclaration().isMember()) {
stmts.add(make().If(make().TypeTest(reference.makeIdent(),
make().Type(syms().ceylonOuterType)),
make().Return(expressionGen().makeOuterExpr(((TypeDeclaration)model.getContainer()).getType())),
swtch));
} else {
stmts.add(swtch);
}
mdb.body(stmts.toList());
classBuilder.method(mdb);
}
private JCExpression makeSerializationGetter(Value value) {
JCExpression result;
if (value.isToplevel() || value.isLate()) {// XXX duplicates logic in AttributeDefinitionBuilder
// We use the setter for late values, since that will allocate
// the array if needed.
result = make().Apply(null,
naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_MEMBER | Naming.NA_GETTER),
List.nil());
} else {
// We bypass the setter
// if (value.isVariable()) {
result = naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_IDENT);
/*} else {
// The field will have final modifier, so we need some
// jiggery pokery to reset it.
requiredLookup[0] = true;
String fieldName = value.getName();
JCExpression fieldType = makeJavaType(value.getType());//TODO probably wrong
result = makeReassignFinalField(fieldType, fieldName, newValue);
}*/
}
result = expressionGen().applyErasureAndBoxing(result, value.getType(),
!CodegenUtil.isUnBoxed(value) ,
BoxingStrategy.BOXED, value.getType());
return result;
}
private void serializationSet(Class model,
ClassDefinitionBuilder classBuilder) {
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.systemMethod(this, Unfix.$set$.toString());
mdb.isOverride(true);
mdb.ignoreModelAnnotations();
mdb.modifiers(PUBLIC);
ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, Unfix.reference.toString());
pdb.modifiers(FINAL);
pdb.type(new TransformedType(make().Type(syms().ceylonReachableReferenceType), null, makeAtNonNull()));
mdb.parameter(pdb);
ParameterDefinitionBuilder pdb2 = ParameterDefinitionBuilder.systemParameter(this, Unfix.instance.toString());
pdb2.modifiers(FINAL);
pdb2.type(new TransformedType(make().Type(syms().objectType), null, makeAtNonNull()));
mdb.parameter(pdb2);
//mdb.resultType(null, naming.makeQuotedFQIdent("java.util.Collection"));
/*
* public void $set$(Object reference, Object instance) {
* switch((String)reference) {
* case ("attr1")
* this.field1 = ...;
* break;
* // ... other fields of this class
* default:
* super.set(reference, instance);
*/
SyntheticName reference = naming.synthetic(Unfix.reference);
SyntheticName instance = naming.synthetic(Unfix.instance);
ListBuffer cases = new ListBuffer();
boolean[] needsLookup = new boolean[]{false};
for (Declaration member : model.getMembers()) {
if (hasField(member)) {
if (member instanceof Function)
continue; // TODO: This class is not serializable
ListBuffer caseStmts = new ListBuffer();
if (member instanceof Value
&& ((Value)member).isLate()) {
caseStmts.add(make().If(make().TypeTest(instance.makeIdent(),
make().Type(syms().ceylonUninitializedLateValueType)),
make().Break(null), null));
}
caseStmts.add(makeDeserializationAssignment((Value)member, needsLookup));
caseStmts.add(make().Break(null));
cases.add(make().Case(make().Literal(member.getQualifiedNameString()), caseStmts.toList()));
}
}
ListBuffer defaultCase = new ListBuffer();
if (extendsSerializable(model)) {
// super.set(reference, instance);
defaultCase.add(make().Exec(make().Apply(null,
naming.makeQualIdent(naming.makeSuper(), Unfix.$set$.toString()),
List.of(reference.makeIdent(), instance.makeIdent()))));
} else {
// throw (or pass to something else to throw, based on policy)
defaultCase.add(make().Throw(make().NewClass(null, null,
naming.makeQuotedFQIdent("java.lang.RuntimeException"),
List.of(make().Literal("unknown attribute")),
null)));
}
cases.add(make().Case(null, defaultCase.toList()));
ListBuffer stmts = new ListBuffer();
if (needsLookup[0]) {
// if we needed to use a lookup object to reset final fields,
// prepend that variable
stmts.add(makeVar(FINAL,
"lookup",
naming.makeQualIdent(make().Type(syms().methodHandlesType), "Lookup"),
make().Apply(null, naming.makeQuotedFQIdent("java.lang.invoke.MethodHandles.lookup"), List.nil())));
}
JCSwitch swtch = make().Switch(
make().Apply(null, naming.makeSelect(make().Apply(null, naming.makeSelect(make().TypeCast(make().Type(syms().ceylonMemberType),
reference.makeIdent()), "getAttribute"),
List.nil()), "getQualifiedName"),
List.nil()),
cases.toList());
stmts.add(make().If(make().TypeTest(reference.makeIdent(),
make().Type(syms().ceylonMemberType)),
swtch,
make().Throw(make().NewClass(null, null,
make().Type(syms().ceylonAssertionErrorType),
List.of(make().Binary(JCTree.Tag.PLUS,
make().Literal("unexpected reachable reference "), reference.makeIdent())),
null))));
mdb.body(stmts.toList());
classBuilder.method(mdb);
}
private JCStatement makeDeserializationAssignment(Value value, boolean[] requiredLookup) {
boolean isValueType = Decl.isValueTypeDecl(simplifyType(value.getType()));
Naming.SyntheticName n = naming.synthetic(Unfix.instance);
JCExpression newValue = make().TypeCast(makeJavaType(value.getType(), JT_NO_PRIMITIVES), n.makeIdent());
if (isValueType) {
newValue = expressionGen().applyErasureAndBoxing(newValue,
value.getType(), true, CodegenUtil.getBoxingStrategy(value),
value.getType());
} else {
// We need to obtain the instance from the reference
// but we don't need the instance to be fully deserialized
}
final JCStatement assignment;
if (value.isToplevel() || value.isLate()) {// XXX duplicates logic in AttributeDefinitionBuilder
// We use the setter for late values, since that will allocate
// the array if needed.
assignment = make().Exec(make().Apply(null,
naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_MEMBER | Naming.NA_SETTER),
List.of(newValue)));
} else {
// We bypass the setter
if (value.isVariable()) {
assignment = make().Exec(make().Assign(
naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_IDENT),
newValue));
} else {
// The field will have final modifier, so we need some
// jiggery pokery to reset it.
requiredLookup[0] = true;
String fieldName = value.getName();
JCExpression fieldType = makeJavaType(value.getType());//TODO probably wrong
assignment = makeReassignFinalField(fieldType, fieldName, newValue);
}
}
return assignment;
}
private JCStatement makeReassignFinalField(JCExpression fieldType,
String fieldName, JCExpression newValue) {
final JCStatement assignment;
JCExpression mhExpr = utilInvocation().setter(
naming.makeUnquotedIdent("lookup"),
//naming.makeQualIdent(makeJavaType(((Class)value.getContainer()).getType(), JT_NO_PRIMITIVES), "class"),
make().Literal(fieldName)// TODO field name should encapsulated
);
JCExpression expr = make().Apply(null,
naming.makeQualIdent(mhExpr, "invokeExact"),
List.of(naming.makeThis(),
make().TypeCast(fieldType, newValue)));// We always typecast here, due to method handle
assignment = make().Exec(expr);
return assignment;
}
private JCExpression makeValueDeclaration(Value value) {
return expressionGen().makeMemberValueOrFunctionDeclarationLiteral(null, value, false);
}
/**
* Generate a method for a shared FunctionalParameter which delegates to the Callable
* @param klass
* @param annotations */
private void makeMethodForFunctionalParameter(
ClassDefinitionBuilder classBuilder,
Tree.Parameter paramTree, Tree.TypedDeclaration memberDecl) {
Parameter paramModel = paramTree.getParameterModel();
if (Strategy.createMethod(paramModel)) {
Tree.MethodDeclaration methodDecl = (Tree.MethodDeclaration)memberDecl;
makeFieldForParameter(classBuilder, paramModel, memberDecl);
Function method = (Function)paramModel.getModel();
java.util.List parameters = method.getFirstParameterList().getParameters();
CallBuilder callBuilder = CallBuilder.instance(this).invoke(
naming.makeQualIdent(naming.makeName(method, Naming.NA_IDENT),
Naming.getCallableMethodName(method)));
for (Parameter parameter : parameters) {
JCExpression parameterExpr = naming.makeName(parameter.getModel(), Naming.NA_IDENT);
parameterExpr = expressionGen().applyErasureAndBoxing(parameterExpr, parameter.getType(),
!CodegenUtil.isUnBoxed(parameter.getModel()), BoxingStrategy.BOXED,
parameter.getType());
callBuilder.argument(parameterExpr);
}
JCExpression expr = callBuilder.build();
JCStatement body;
if (isVoid(memberDecl) && Decl.isUnboxedVoid(method) && !Strategy.useBoxedVoid(method)) {
body = make().Exec(expr);
} else {
expr = expressionGen().applyErasureAndBoxing(expr, paramModel.getType(), true, CodegenUtil.getBoxingStrategy(method), paramModel.getType());
body = make().Return(expr);
}
classBuilder.methods(transformMethod(method, null, methodDecl, methodDecl.getParameterLists(),
methodDecl,
true, method.isActual(), true,
List.of(body), new DaoThis(methodDecl, methodDecl.getParameterLists().get(0)), false));
}
}
private void addReifiedTypeInterface(ClassDefinitionBuilder classBuilder, ClassOrInterface model) {
if(model.getExtendedType() == null || willEraseToObject(model.getExtendedType()) || !Decl.isCeylon(model.getExtendedType().getDeclaration()))
classBuilder.reifiedType();
}
private void transformInterface(Tree.ClassOrInterface def,
Interface model,
ClassDefinitionBuilder classBuilder) {
// Copy all the qualifying type's type parameters into the interface
java.util.List typeParameters = typeParametersOfAllContainers(model, false);
for(TypeParameter tp : typeParameters){
classBuilder.typeParameter(tp, false);
}
if(model.isCompanionClassNeeded()){
classBuilder.method(makeCompanionAccessor(model, model.getType(), null, false));
// Build the companion class
buildCompanion(def, (Interface)model, classBuilder);
}
addAmbiguousMembers(classBuilder, model);
// Generate the inner members list for model loading
addAtMembers(classBuilder, model, def);
addAtLocalDeclarations(classBuilder, def);
}
private void addAmbiguousMembers(ClassDefinitionBuilder classBuilder, Interface model) {
// only if we refine more than one interface
java.util.List satisfiedTypes = model.getSatisfiedTypes();
if(satisfiedTypes.size() <= 1)
return;
Set satisfiedInterfaces = new HashSet();
for(Type interfaceDecl : model.getSatisfiedTypes()){
collectInterfaces((Interface) interfaceDecl.getDeclaration(), satisfiedInterfaces);
}
Set ambiguousInterfaces = new HashSet();
for(Interface satisfiedInterface : satisfiedInterfaces){
if(isInheritedWithDifferentTypeArguments(satisfiedInterface, model.getType()) != null){
ambiguousInterfaces.add(satisfiedInterface);
}
}
Set treated = new HashSet();
for(Interface ambiguousInterface : ambiguousInterfaces){
for(Declaration member : ambiguousInterface.getMembers()){
String name = member.getName();
// skip if already handled
if(treated.contains(name))
continue;
// skip if it's implemented directly
if(model.getDirectMember(name, null, false) != null){
treated.add(name);
continue;
}
// find if we have different implementations in two direct interfaces
LOOKUP:
for(int i=0;iemptyList());
Reference typedMember2 = secondInterface.getTypedReference(member2, Collections.emptyList());
Type type1 = simplifyType(typedMember1.getType());
Type type2 = simplifyType(typedMember2.getType());
if(!type1.isExactly(type2)){
// treat it and stop looking for other interfaces
addAmbiguousMember(classBuilder, model, name);
break LOOKUP;
}
}
}
// that member has no conflict
treated.add(name);
}
}
}
private void addAmbiguousMember(ClassDefinitionBuilder classBuilder, Interface model, String name) {
Declaration member = model.getMember(name, null, false);
Type satisfiedType = model.getType().getSupertype(model);
if (member instanceof Class) {
Class klass = (Class)member;
if (Strategy.generateInstantiator(member)
&& !klass.hasConstructors()) {
// instantiator method implementation
generateInstantiatorDelegate(classBuilder, satisfiedType,
model, klass, null, model.getType(), false);
}
if (klass.hasConstructors()) {
for (Declaration m : klass.getMembers()) {
if (m instanceof Constructor
&& Strategy.generateInstantiator(m)) {
Constructor ctor = (Constructor)m;
generateInstantiatorDelegate(classBuilder, satisfiedType,
model, klass, ctor, model.getType(), false);
}
}
}
}else if (member instanceof Function) {
Function method = (Function)member;
final TypedReference typedMember = satisfiedType.getTypedMember(method, Collections.emptyList());
java.util.List> producedTypeParameterBounds = producedTypeParameterBounds(
typedMember, method);
final java.util.List typeParameters = method.getTypeParameters();
final java.util.List parameters = method.getFirstParameterList().getParameters();
for (Parameter param : parameters) {
if (Strategy.hasDefaultParameterOverload(param)) {
MethodDefinitionBuilder overload = new DefaultedArgumentMethodTyped(null, MethodDefinitionBuilder.method(this, method), typedMember, true)
.makeOverload(
method.getFirstParameterList(),
param,
typeParameters);
overload.modifiers(PUBLIC | ABSTRACT);
classBuilder.method(overload);
}
}
final MethodDefinitionBuilder concreteMemberDelegate = makeDelegateToCompanion(null,
typedMember,
model.getType(),
PUBLIC | ABSTRACT,
method.getTypeParameters(),
producedTypeParameterBounds,
typedMember.getType(),
naming.selector(method),
method.getFirstParameterList().getParameters(),
((Function) member).getTypeErased(),
null,
null,
false);
classBuilder.method(concreteMemberDelegate);
} else if (member instanceof Value
|| member instanceof Setter) {
TypedDeclaration attr = (TypedDeclaration)member;
final TypedReference typedMember = satisfiedType.getTypedMember(attr, Collections.emptyList());
if (member instanceof Value) {
final MethodDefinitionBuilder getterDelegate = makeDelegateToCompanion(null,
typedMember,
model.getType(),
PUBLIC | ABSTRACT,
Collections.emptyList(),
Collections.>emptyList(),
typedMember.getType(),
Naming.getGetterName(attr),
Collections.emptyList(),
attr.getTypeErased(),
null,
null,
false);
classBuilder.method(getterDelegate);
}
if (member instanceof Setter) {
final MethodDefinitionBuilder setterDelegate = makeDelegateToCompanion(null,
typedMember,
model.getType(),
PUBLIC | ABSTRACT,
Collections.emptyList(),
Collections.>emptyList(),
typeFact().getAnythingType(),
Naming.getSetterName(attr),
Collections.singletonList(((Setter)member).getParameter()),
((Setter) member).getTypeErased(),
null,
null,
false);
classBuilder.method(setterDelegate);
}
}
}
private void addAtMembers(ClassDefinitionBuilder classBuilder, ClassOrInterface model,
Tree.ClassOrInterface def) {
List members = List.nil();
for(Declaration member : model.getMembers()){
if(member instanceof ClassOrInterface == false
&& member instanceof TypeAlias == false){
continue;
}
TypeDeclaration innerType = (TypeDeclaration) member;
Tree.Declaration innerTypeTree = findInnerType(def, innerType.getName());
if(innerTypeTree != null) {
TransformationPlan plan = errors().hasDeclarationAndMarkBrokenness(innerTypeTree);
if (plan instanceof Drop) {
continue;
}
}
if(innerType.isAlias()
&& innerTypeTree != null
&& Decl.isAncestorLocal(innerTypeTree))
// for the same reason we do not generate aliases in transform(ClassOrInterface def) let's not list them
continue;
JCAnnotation atMember;
// interfaces are moved to toplevel so they can lose visibility of member types if they are local
if(Decl.isLocal(model) && model instanceof Interface)
atMember = makeAtMember(innerType.getName());
else
atMember = makeAtMember(innerType.getType());
members = members.prepend(atMember);
}
classBuilder.annotations(makeAtMembers(members));
}
private Tree.Declaration findInnerType(Tree.ClassOrInterface def, String name) {
Tree.Body body;
if(def instanceof Tree.ClassDefinition)
body = ((Tree.ClassDefinition) def).getClassBody();
else if(def instanceof Tree.InterfaceDefinition)
body = ((Tree.InterfaceDefinition) def).getInterfaceBody();
else
return null;
for(Node node : body.getStatements()){
if(node instanceof Tree.Declaration
&& ((Tree.Declaration) node).getIdentifier() != null
&& ((Tree.Declaration) node).getIdentifier().getText().equals(name))
return (Tree.Declaration) node;
}
return null;
}
private void addAtLocalDeclarations(ClassDefinitionBuilder classBuilder, Tree.ClassOrInterface tree) {
classBuilder.annotations(makeAtLocalDeclarations(tree));
}
private void addAtContainer(ClassDefinitionBuilder classBuilder, TypeDeclaration model) {
Scope scope = Decl.getNonSkippedContainer((Scope)model);
Scope declarationScope = Decl.getFirstDeclarationContainer((Scope)model);
boolean inlineObjectInToplevelAttr = Decl.isTopLevelObjectExpressionType(model);
if(scope == null || (scope instanceof Package && !inlineObjectInToplevelAttr) && scope == declarationScope)
return;
if(scope instanceof ClassOrInterface
&& scope == declarationScope
&& !inlineObjectInToplevelAttr
// we do not check for types inside initialiser section which are private and not captured because we treat them as members
&& !(model instanceof Interface && Decl.hasLocalNotInitializerAncestor(model))){
ClassOrInterface container = (ClassOrInterface) scope;
List atContainer = makeAtContainer(container.getType(), model.isStatic());
classBuilder.annotations(atContainer);
}else{
if(model instanceof Interface)
classBuilder.annotations(makeLocalContainerPath((Interface) model));
Declaration declarationContainer = getDeclarationContainer(model);
classBuilder.annotations(makeAtLocalDeclaration(model.getQualifier(), declarationContainer == null));
}
}
private void satisfaction(Tree.SatisfiedTypes satisfied, final Class model, ClassDefinitionBuilder classBuilder) {
Set satisfiedInterfaces = new HashSet();
// start by saying that we already satisfied each interface from superclasses
Type superClass = model.getExtendedType();
while(superClass != null){
for(Type interfaceDecl : superClass.getSatisfiedTypes()){
collectInterfaces((Interface) interfaceDecl.getDeclaration(), satisfiedInterfaces);
}
superClass = superClass.getExtendedType();
}
// now satisfy each new interface
if (satisfied != null) {
for (Tree.StaticType type : satisfied.getTypes()) {
try {
Type satisfiedType = type.getTypeModel();
TypeDeclaration decl = satisfiedType.getDeclaration();
if (!(decl instanceof Interface)) {
continue;
}
// make sure we get the right instantiation of the interface
satisfiedType = model.getType().getSupertype(decl);
concreteMembersFromSuperinterfaces(model, classBuilder, satisfiedType, satisfiedInterfaces);
} catch (BugException e) {
e.addError(type);
}
}
}
// now find the set of interfaces we implemented twice with more refined type parameters
if(model.getExtendedType() != null){
// reuse that Set
satisfiedInterfaces.clear();
for(Type interfaceDecl : model.getSatisfiedTypes()){
collectInterfaces((Interface) interfaceDecl.getDeclaration(), satisfiedInterfaces);
}
if(!satisfiedInterfaces.isEmpty()){
// sort it to facilitate test comparisons that work in JDK7 and 8
ArrayList sortedInterfaces = new ArrayList(satisfiedInterfaces.size());
sortedInterfaces.addAll(satisfiedInterfaces);
Collections.sort(sortedInterfaces, DeclarationComparator);
// now see if we refined them
for(Interface iface : sortedInterfaces){
// skip those we can't do anything about
if(!supportsReified(iface) || !CodegenUtil.isCompanionClassNeeded(iface))
continue;
Type thisType = model.getType().getSupertype(iface);
Type superClassType = model.getExtendedType().getSupertype(iface);
if(thisType != null
&& superClassType != null
&& !thisType.isExactly(superClassType)
&& thisType.isSubtypeOf(superClassType)){
// we're refining it
classBuilder.refineReifiedType(thisType);
}
}
}
}
}
private void collectInterfaces(Interface interfaceDecl, Set satisfiedInterfaces) {
if(satisfiedInterfaces.add(interfaceDecl)){
for(Type newInterfaceDecl : interfaceDecl.getSatisfiedTypes()){
if (!(newInterfaceDecl.isUnknown())) {
collectInterfaces((Interface) newInterfaceDecl.getDeclaration(), satisfiedInterfaces);
}
}
}
}
/**
* Generates companion fields ($Foo$impl) and methods
*/
private void concreteMembersFromSuperinterfaces(final Class model,
ClassDefinitionBuilder classBuilder,
Type satisfiedType, Set satisfiedInterfaces) {
satisfiedType = satisfiedType.resolveAliases();
Interface iface = (Interface)satisfiedType.getDeclaration();
if (satisfiedInterfaces.contains(iface)
|| iface.getType().isExactly(typeFact().getIdentifiableDeclaration().getType())) {
return;
}
// If there is no $impl (e.g. implementing a Java interface)
// then don't instantiate it...
if (hasImpl(iface)) {
// ... otherwise for each satisfied interface,
// instantiate an instance of the
// companion class in the constructor and assign it to a
// $Interface$impl field
transformInstantiateCompanions(classBuilder,
model, iface, satisfiedType);
}
if(!Decl.isCeylon(iface)){
// let's not try to implement CMI for Java interfaces
return;
}
// For each super interface
for (Declaration member : sortedMembers(iface.getMembers())) {
if (member instanceof Class) {
Class klass = (Class)member;
final Type typedMember = satisfiedType.getTypeMember(klass, Collections.emptyList());
if (Strategy.generateInstantiator(member)
&& !klass.hasConstructors()
&& !model.isFormal()
&& needsCompanionDelegate(model, typedMember)
&& model.getDirectMember(member.getName(), null, false) == null) {
// instantiator method implementation
generateInstantiatorDelegate(classBuilder, satisfiedType,
iface, klass, null, model.getType(), !member.isFormal());
}
if (klass.hasConstructors()) {
for (Declaration m : klass.getMembers()) {
if (m instanceof Constructor
&& Strategy.generateInstantiator(m)) {
Constructor ctor = (Constructor)m;
generateInstantiatorDelegate(classBuilder, satisfiedType,
iface, klass, ctor, model.getType(), true);
}
}
}
}
// type aliases are on the $impl class
if(member instanceof TypeAlias)
continue;
if (Strategy.onlyOnCompanion(member)) {
// non-shared interface methods don't need implementing
// (they're just private methods on the $impl)
continue;
}
if (member instanceof Function) {
Function method = (Function)member;
final TypedReference typedMember = satisfiedType.getTypedMember(method, typesOfTypeParameters(method.getTypeParameters()));
Declaration sub = (Declaration)model.getMember(method.getName(), getSignatureIfRequired(typedMember), false, true);
if (sub instanceof Function/* && !sub.isAbstraction()*/) {
Function subMethod = (Function)sub;
if (subMethod.getParameterLists().isEmpty()) {
continue;
}
java.util.List> producedTypeParameterBounds = producedTypeParameterBounds(
typedMember, subMethod);
// final TypedReference refinedTypedMember = model.getType().getTypedMember(subMethod, Collections.emptyList());
final java.util.List typeParameters = subMethod.getTypeParameters();
final java.util.List parameters = subMethod.getFirstParameterList().getParameters();
boolean hasOverloads = false;
if (!satisfiedInterfaces.contains((Interface)method.getContainer())) {
for (Parameter param : parameters) {
if (Strategy.hasDefaultParameterValueMethod(param)
&& CodegenUtil.getTopmostRefinedDeclaration(param.getModel()).getContainer().equals(member)) {
final TypedReference typedParameter = typedMember.getTypedParameter(param);
// If that method has a defaulted parameter,
// we need to generate a default value method
// which also delegates to the $impl
final MethodDefinitionBuilder defaultValueDelegate = makeDelegateToCompanion(iface,
typedMember,
model.getType(),
modifierTransformation().defaultValueMethodBridge(),
typeParameters, producedTypeParameterBounds,
typedParameter.getFullType(),
Naming.getDefaultedParamMethodName(method, param),
parameters.subList(0, parameters.indexOf(param)),
param.getModel().getTypeErased(),
null,
param);
classBuilder.method(defaultValueDelegate);
}
if (Strategy.hasDefaultParameterOverload(param)) {
if ((method.isDefault() || method.isShared() && !method.isFormal())
&& Decl.equal(method, subMethod)) {
MethodDefinitionBuilder overload = new DefaultedArgumentMethodTyped(new DaoThis((Tree.AnyMethod)null, null), MethodDefinitionBuilder.method(this, subMethod), typedMember, true)
.makeOverload(
subMethod.getFirstParameterList(),
param,
typeParameters);
classBuilder.method(overload);
}
hasOverloads = true;
}
}
}
// if it has the *most refined* default concrete member,
// then generate a method on the class
// delegating to the $impl instance
if (needsCompanionDelegate(model, typedMember)) {
final MethodDefinitionBuilder concreteMemberDelegate = makeDelegateToCompanion(iface,
typedMember,
model.getType(),
modifierTransformation().methodBridge(method),
typeParameters,
producedTypeParameterBounds,
typedMember.getType(),
naming.selector(method),
method.getFirstParameterList().getParameters(),
((Function) member).getTypeErased(),
null,
null);
classBuilder.method(concreteMemberDelegate);
}
if (hasOverloads
&& (method.isDefault() || method.isShared() && !method.isFormal())
&& Decl.equal(method, subMethod)) {
final MethodDefinitionBuilder canonicalMethod = makeDelegateToCompanion(iface,
typedMember,
model.getType(),
modifierTransformation().canonicalMethodBridge(),
subMethod.getTypeParameters(),
producedTypeParameterBounds,
typedMember.getType(),
Naming.selector(method, Naming.NA_CANONICAL_METHOD),
method.getFirstParameterList().getParameters(),
((Function) member).getTypeErased(),
naming.selector(method),
null);
classBuilder.method(canonicalMethod);
}
}
} else if (member instanceof Value
|| member instanceof Setter) {
TypedDeclaration attr = (TypedDeclaration)member;
final TypedReference typedMember = satisfiedType.getTypedMember(attr, null);
if (needsCompanionDelegate(model, typedMember)) {
Setter setter = (member instanceof Setter) ? (Setter)member : null;
if (member instanceof Value) {
Value getter = (Value)member;
if (member instanceof JavaBeanValue) {
setter = ((Value) member).getSetter();
}
final MethodDefinitionBuilder getterDelegate = makeDelegateToCompanion(iface,
typedMember,
model.getType(),
modifierTransformation().getterBridge(getter),
Collections.emptyList(),
Collections.>emptyList(),
typedMember.getType(),
Naming.getGetterName(getter),
Collections.emptyList(),
getter.getTypeErased(),
null,
null);
classBuilder.method(getterDelegate);
}
if (setter != null) {
final MethodDefinitionBuilder setterDelegate = makeDelegateToCompanion(iface,
satisfiedType.getTypedMember(setter, null),
model.getType(),
modifierTransformation().setterBridge(setter),
Collections.emptyList(),
Collections.>emptyList(),
typeFact().getAnythingType(),
Naming.getSetterName(attr),
Collections.singletonList(setter.getParameter()),
setter.getTypeErased(),
null,
null);
classBuilder.method(setterDelegate);
}
if (Decl.isValue(member)
&& ((Value)attr).isVariable()) {
// I don't *think* this can happen because although a
// variable Value can be declared on an interface it
// will need to we refined as a Getter+Setter on a
// subinterface in order for there to be a method in a
// $impl to delegate to
throw new BugException("assertion failed: " + member.getQualifiedNameString() + " was unexpectedly a variable value");
}
}
} else {
Reference typedMember = member instanceof TypeDeclaration
? satisfiedType.getTypeMember((TypeDeclaration)member, Collections.emptyList())
: satisfiedType.getTypedMember((TypedDeclaration)member, Collections.emptyList());
if (needsCompanionDelegate(model, typedMember)) {
throw new BugException("unhandled concrete interface member " + member.getQualifiedNameString() + " " + member.getClass());
}
}
}
// Add $impl instances for the whole interface hierarchy
satisfiedInterfaces.add(iface);
for (Type sat : iface.getSatisfiedTypes()) {
sat = model.getType().getSupertype(sat.getDeclaration());
concreteMembersFromSuperinterfaces(model, classBuilder, sat, satisfiedInterfaces);
}
}
private java.util.List typesOfTypeParameters(java.util.List list) {
ArrayList result = new ArrayList(list.size());
for (TypeParameter tp : list) {
result.add(tp.getType());
}
return result;
}
private Iterable sortedMembers(java.util.List members) {
TreeSet set = new TreeSet(DeclarationComparator);
set.addAll(members);
return set;
}
private java.util.List> producedTypeParameterBounds(
final Reference typedMember, Generic subMethod) {
java.util.List> producedTypeParameterBounds = new ArrayList>(subMethod.getTypeParameters().size());
for (TypeParameter tp : subMethod.getTypeParameters()) {
java.util.List satisfiedTypes = tp.getType().getSatisfiedTypes();
ArrayList bounds = new ArrayList<>(satisfiedTypes.size());
for (Type bound : satisfiedTypes) {
if (typedMember instanceof Type) {
bounds.add(bound.substitute((Type) typedMember));
}
else if (typedMember instanceof TypedReference) {
bounds.add(bound.substitute((TypedReference) typedMember));
}
}
producedTypeParameterBounds.add(bounds);
}
return producedTypeParameterBounds;
}
private void generateInstantiatorDelegate(
ClassDefinitionBuilder classBuilder, Type satisfiedType,
Interface iface, Class klass, Constructor ctor, Type currentType, boolean includeBody) {
Type typeMember = satisfiedType.getTypeMember(klass, klass.getType().getTypeArgumentList());
if (ctor != null) {
typeMember = ctor.appliedType(typeMember, Collections.emptyList());
}
java.util.List typeParameters = klass.getTypeParameters();
java.util.List parameters = (ctor != null ? ctor.getParameterLists() : klass.getParameterLists()).get(0).getParameters();
long flags = modifierTransformation().instantiatorBridgeFlags(includeBody);
String instantiatorMethodName = naming.getInstantiatorMethodName(klass);
for (Parameter param : parameters) {
if (Strategy.hasDefaultParameterValueMethod(param)
&& !klass.isActual()) {
final TypedReference typedParameter = typeMember.getTypedParameter(param);
// If that method has a defaulted parameter,
// we need to generate a default value method
// which also delegates to the $impl
final MethodDefinitionBuilder defaultValueDelegate = makeDelegateToCompanion(iface,
typeMember,
currentType,
flags,
typeParameters,
producedTypeParameterBounds(typeMember, klass),
typedParameter.getFullType(),
Naming.getDefaultedParamMethodName(ctor != null ? ctor : klass, param),
parameters.subList(0, parameters.indexOf(param)),
param.getModel().getTypeErased(),
null,
param,
includeBody);
classBuilder.method(defaultValueDelegate);
}
if (Strategy.hasDefaultParameterOverload(param)) {
final MethodDefinitionBuilder overload = makeDelegateToCompanion(iface,
typeMember,
currentType,
flags,
typeParameters,
producedTypeParameterBounds(typeMember, klass),
typeMember.getType(),
instantiatorMethodName,
parameters.subList(0, parameters.indexOf(param)),
false,
null,
null,
includeBody);
classBuilder.method(overload);
}
}
final MethodDefinitionBuilder overload = makeDelegateToCompanion(iface,
typeMember,
currentType,
flags,
typeParameters,
producedTypeParameterBounds(typeMember, klass),
typeMember.getType(),
instantiatorMethodName,
parameters,
false,
null,
null,
includeBody);
classBuilder.method(overload);
}
private boolean needsCompanionDelegate(final Class model, Reference ref) {
final boolean mostRefined;
Declaration member = ref.getDeclaration();
java.util.List sig = getSignatureIfRequired(ref);
Declaration m = model.getMember(member.getName(), sig, false, true);
if (member instanceof Setter && Decl.isGetter(m)) {
mostRefined = member.equals(((Value)m).getSetter());
} else {
mostRefined = member.equals(m);
}
return mostRefined
&& (member.isDefault() || !member.isFormal());
}
private java.util.List getSignatureIfRequired(Reference ref) {
if(requiresMemberSignatureMatch(ref.getDeclaration()))
return ModelUtil.getSignature(ref);
return null;
}
private boolean requiresMemberSignatureMatch(Declaration declaration) {
if(declaration instanceof Function){
java.util.List parameterLists = ((Functional)declaration).getParameterLists();
if(parameterLists != null && parameterLists.size() == 1){
ParameterList parameterList = parameterLists.get(0);
for(Parameter param : parameterList.getParameters()){
if(param.getModel().isFunctional())
return false;
}
}
return declaration.isAbstraction() || declaration.isOverloaded();
}
return false;
}
/**
* Generates a method which delegates to the companion instance $Foo$impl
*/
private MethodDefinitionBuilder makeDelegateToCompanion(Interface iface,
Reference typedMember,
Type currentType,
final long mods,
final java.util.List typeParameters,
final java.util.List> producedTypeParameterBounds,
final Type methodType,
final String methodName,
final java.util.List parameters,
boolean typeErased,
final String targetMethodName,
Parameter param) {
return makeDelegateToCompanion(iface, typedMember, currentType, mods, typeParameters,
producedTypeParameterBounds, methodType, methodName, parameters, typeErased, targetMethodName, param, true);
}
/**
* Generates a method which delegates to the companion instance $Foo$impl
*/
private MethodDefinitionBuilder makeDelegateToCompanion(Interface iface,
Reference typedMember,
Type currentType,
final long mods,
final java.util.List typeParameters,
final java.util.List> producedTypeParameterBounds,
final Type methodType,
final String methodName,
final java.util.List parameters,
boolean typeErased,
final String targetMethodName,
Parameter defaultedParam,
boolean includeBody) {
final MethodDefinitionBuilder concreteWrapper = MethodDefinitionBuilder.systemMethod(gen(), methodName);
concreteWrapper.modifiers(mods);
concreteWrapper.ignoreModelAnnotations();
if ((mods & PRIVATE) == 0) {
concreteWrapper.isOverride(true);
}
if(typeParameters != null) {
concreteWrapper.reifiedTypeParametersFromModel(typeParameters);
}
Iterator> iterator = producedTypeParameterBounds.iterator();
if(typeParameters != null) {
for (TypeParameter tp : typeParameters) {
concreteWrapper.typeParameter(tp, iterator.next());
}
}
boolean explicitReturn = false;
Declaration member = (defaultedParam != null ? typedMember.getTypedParameter(defaultedParam) : typedMember).getDeclaration();
Type returnType = null;
if (!isAnything(methodType)
|| ((member instanceof Function || member instanceof Value) && !Decl.isUnboxedVoid(member))
|| (member instanceof Function && Strategy.useBoxedVoid((Function)member))) {
explicitReturn = true;
if(CodegenUtil.isHashAttribute(member)){
// delegates for hash attributes are int
concreteWrapper.resultType(new TransformedType(make().Type(syms().intType)));
returnType = typedMember.getType();
}else if (typedMember instanceof TypedReference
&& defaultedParam == null) {
TypedReference typedRef = (TypedReference) typedMember;
// This is very much like for method refinement: if the supertype is erased -> go raw.
// Except for some reason we only need to do it with multiple inheritance with different type
// arguments, so let's not go overboard
int flags = 0;
if(CodegenUtil.hasTypeErased((TypedDeclaration)member.getRefinedDeclaration()) ||
CodegenUtil.hasTypeErased((TypedDeclaration)member)
&& isInheritedTwiceWithDifferentTypeArguments(currentType, iface)){
flags |= AbstractTransformer.JT_RAW;
}
concreteWrapper.resultTypeNonWidening(currentType, typedRef, typedMember.getType(), flags);
// FIXME: this is redundant with what we computed in the previous line in concreteWrapper.resultTypeNonWidening
TypedReference nonWideningTypedRef = gen().nonWideningTypeDecl(typedRef, currentType);
returnType = gen().nonWideningType(typedRef, nonWideningTypedRef);
} else if (defaultedParam != null) {
TypedReference typedParameter = typedMember.getTypedParameter(defaultedParam);
NonWideningParam nonWideningParam = concreteWrapper.getNonWideningParam(typedParameter,
currentType.getDeclaration() instanceof Class ? WideningRules.FOR_MIXIN : WideningRules.NONE);
returnType = nonWideningParam.nonWideningType;
if(member instanceof Function)
returnType = typeFact().getCallableType(returnType);
concreteWrapper.resultType(new TransformedType(makeJavaType(returnType, nonWideningParam.flags)));
} else {
concreteWrapper.resultType(new TransformedType(makeJavaType((Type)typedMember)));
returnType = (Type) typedMember;
}
}
ListBuffer arguments = new ListBuffer();
if(typeParameters != null){
for(TypeParameter tp : typeParameters){
arguments.add(naming.makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp)));
}
}
Declaration declaration = typedMember.getDeclaration();
if (declaration instanceof Constructor
&& !Decl.isDefaultConstructor((Constructor)declaration)
&& defaultedParam == null) {
concreteWrapper.parameter(makeConstructorNameParameter((Constructor)declaration));
arguments.add(naming.makeUnquotedIdent(Unfix.$name$));
}
int ii = 0;
for (Parameter param : parameters) {
Parameter parameter;
if (declaration instanceof Functional) {
parameter = ((Functional)declaration).getFirstParameterList().getParameters().get(ii++);
} else if (declaration instanceof Setter){
parameter = ((Setter)declaration).getParameter();
} else {
throw BugException.unhandledDeclarationCase(declaration);
}
final TypedReference typedParameter = typedMember.getTypedParameter(parameter);
concreteWrapper.parameter(null, param, typedParameter, null, FINAL, WideningRules.FOR_MIXIN);
arguments.add(naming.makeName(param.getModel(), Naming.NA_MEMBER | Naming.NA_ALIASED));
}
if(includeBody){
JCExpression qualifierThis = makeUnquotedIdent(getCompanionFieldName(iface));
// if the best satisfied type is not the one we think we implement, we may need to cast
// our impl accessor to get the expected bounds of the qualifying type
if(explicitReturn){
Type javaType = getBestSatisfiedType(currentType, iface);
Type ceylonType = typedMember.getQualifyingType();
// don't even bother if the impl accessor is turned to raw because casting it to raw doesn't help
if(!isTurnedToRaw(ceylonType)
// if it's exactly the same we don't need any cast
&& !javaType.isExactly(ceylonType))
// this will add the proper cast to the impl accessor
qualifierThis = expressionGen().applyErasureAndBoxing(qualifierThis, currentType,
false, true, BoxingStrategy.BOXED, ceylonType,
ExpressionTransformer.EXPR_WANTS_COMPANION);
}
JCExpression expr = make().Apply(
null, // TODO Type args
makeSelect(qualifierThis, (targetMethodName != null) ? targetMethodName : methodName),
arguments.toList());
if (isUnimplementedMemberClass(currentType, typedMember)) {
concreteWrapper.body(makeThrowUnresolvedCompilationError(
// TODO encapsulate the error message
"formal member '"+declaration.getName()+"' of '"+iface.getName()+"' not implemented in class hierarchy"));
current().broken();
} else if (!explicitReturn) {
concreteWrapper.body(gen().make().Exec(expr));
} else {
// deal with erasure and stuff
BoxingStrategy boxingStrategy;
boolean exprBoxed;
if(member instanceof TypedDeclaration){
TypedDeclaration typedDecl = (TypedDeclaration) member;
exprBoxed = !CodegenUtil.isUnBoxed(typedDecl);
boxingStrategy = CodegenUtil.getBoxingStrategy(typedDecl);
}else{
// must be a class or interface
exprBoxed = true;
boxingStrategy = BoxingStrategy.UNBOXED;
}
// if our interface impl is turned to raw, the whole call will be seen as raw by javac, so we may need
// to force an additional cast
if(isTurnedToRaw(typedMember.getQualifyingType())
// see note in BoxingVisitor.visit(QualifiedMemberExpression) about mixin super calls and variant type args
// in invariant locations
|| needsRawCastForMixinSuperCall(iface, methodType)
|| needsCastForErasedInstantiator(iface, methodName, member))
typeErased = true;
expr = gen().expressionGen().applyErasureAndBoxing(expr, methodType, typeErased,
exprBoxed, boxingStrategy,
returnType, 0);
concreteWrapper.body(gen().make().Return(expr));
}
}
return concreteWrapper;
}
protected boolean needsCastForErasedInstantiator(Interface iface,
final String methodName, Declaration member) {
return Decl.isAncestorLocal(iface) && Decl.isAncestorLocal(member)
&& methodName.endsWith(NamingBase.Suffix.$new$.toString());
}
private boolean isUnimplementedMemberClass(Type currentType, Reference typedMember) {
if (typedMember instanceof Type
&& currentType.getDeclaration() instanceof Class) {// member class
for (Reference formal : ((Class)currentType.getDeclaration()).getUnimplementedFormals()) {
if (formal.getDeclaration().equals(typedMember.getDeclaration())) {
return true;
}
}
}
return false;
}
private boolean isInheritedTwiceWithDifferentTypeArguments(Type currentType, Interface iface) {
Type firstSatisfiedType = getFirstSatisfiedType(currentType, iface);
Type supertype = currentType.getSupertype(iface);
return !supertype.isExactly(firstSatisfiedType);
}
private Boolean hasImpl(Interface iface) {
if (gen().willEraseToObject(iface.getType())) {
return false;
}
// Java interfaces never have companion classes
if (iface instanceof LazyInterface
&& !((LazyInterface)iface).isCeylon()){
return false;
}
return CodegenUtil.isCompanionClassNeeded(iface);
}
private void transformInstantiateCompanions(
ClassDefinitionBuilder classBuilder,
Class model,
Interface iface,
Type satisfiedType) {
at(null);
// make sure we get the first type that java will find when it looks up
final Type bestSatisfiedType = getBestSatisfiedType(model.getType(), iface);
classBuilder.getInitBuilder().init(makeCompanionInstanceAssignment(model, iface, satisfiedType));
classBuilder.field(PROTECTED | FINAL, getCompanionFieldName(iface),
makeJavaType(bestSatisfiedType, AbstractTransformer.JT_COMPANION | JT_SATISFIES), null, false,
makeAtIgnore());
classBuilder.method(makeCompanionAccessor(iface, bestSatisfiedType, model, true));
}
/**
* Returns the companion instances assignment expression used in the constructor,
* e.g.
*
* this.$ceylon$language$Enumerable$this$ = new .ceylon.language.Enumerable$impl<.com.redhat.ceylon.compiler.java.test.structure.klass.SerializableEnumerable>(.com.redhat.ceylon.compiler.java.test.structure.klass.SerializableEnumerable.$TypeDescriptor$, this);
*
* @param classBuilder
* @return
*/
private JCExpressionStatement makeCompanionInstanceAssignment(final Class model,
final Interface iface, final Type satisfiedType) {
final Type bestSatisfiedType = getBestSatisfiedType(model.getType(), iface);
JCExpression containerInstance = null;
if(!Decl.isToplevel(iface) && !Decl.isLocal(iface)){
// if it's a member type we need to qualify the new instance with its $impl container
ClassOrInterface interfaceContainer = Decl.getClassOrInterfaceContainer(iface, false);
if(interfaceContainer instanceof Interface){
ClassOrInterface modelContainer = model;
// first try to find exactly the interface we are looking for
while((modelContainer = Decl.getClassOrInterfaceContainer(modelContainer, false)) != null
&& !modelContainer.equals(interfaceContainer)){
// keep searching
}
// then find one that inherits it
if(modelContainer == null){
modelContainer = model;
while((modelContainer = Decl.getClassOrInterfaceContainer(modelContainer, false)) != null
&& modelContainer.getType().getSupertype(interfaceContainer) == null){
// keep searching
}
}
if (modelContainer == null) {
throw new BugException("Could not find container that satisfies interface "
+ iface.getQualifiedNameString() + " to find qualifying instance for companion instance for "
+ model.getQualifiedNameString());
}
// if it's an interface we just qualify it properly
if(modelContainer instanceof Interface){
JCExpression containerType = makeJavaType(modelContainer.getType(), JT_COMPANION | JT_SATISFIES | JT_RAW);
containerInstance = makeSelect(containerType, "this");
}else{
// it's a class: find the right field used for the interface container impl
String containerFieldName = getCompanionFieldName((Interface)interfaceContainer);
JCExpression containerType = makeJavaType(modelContainer.getType(), JT_SATISFIES);
containerInstance = makeSelect(makeSelect(containerType, "this"), containerFieldName);
}
}
}
List state = List.nil();
// pass all reified type info to the constructor
for(JCExpression t : makeReifiedTypeArguments(satisfiedType)){
state = state.append(t);
}
// pass the instance of this
state = state.append( expressionGen().applyErasureAndBoxing(naming.makeThis(),
model.getType(), false, true, BoxingStrategy.BOXED,
bestSatisfiedType, ExpressionTransformer.EXPR_FOR_COMPANION));
final JCExpression ifaceImplType;
if(!Decl.isToplevel(iface) && !Decl.isLocal(iface)
&& Decl.getClassOrInterfaceContainer(iface, false) instanceof Interface){
ifaceImplType = makeJavaType(bestSatisfiedType, JT_COMPANION | JT_CLASS_NEW | JT_NON_QUALIFIED);
} else {
ifaceImplType = makeJavaType(bestSatisfiedType, JT_COMPANION | JT_CLASS_NEW);
}
JCExpression newInstance = make().NewClass(containerInstance,
null,
ifaceImplType,
state,
null);
JCExpressionStatement companionInstanceAssign = make().Exec(make().Assign(
makeSelect("this", getCompanionFieldName(iface)),// TODO Use qualified name for quoting?
newInstance));
return companionInstanceAssign;
}
private MethodDefinitionBuilder makeCompanionAccessor(Interface iface, Type satisfiedType,
Class currentType, boolean forImplementor) {
MethodDefinitionBuilder thisMethod = MethodDefinitionBuilder.systemMethod(
this, naming.getCompanionAccessorName(iface));
thisMethod.noModelAnnotations();
JCExpression typeExpr;
if (!forImplementor && Decl.isAncestorLocal(iface)) {
// For a local interface the return type cannot be a local
// companion class, because that won't be visible at the
// top level, so use Object instead
typeExpr = make().Type(syms().objectType);
} else {
typeExpr = makeJavaType(satisfiedType, JT_COMPANION);
}
thisMethod.resultType(new TransformedType(typeExpr, null, makeAtNonNull()));
if (forImplementor) {
thisMethod.isOverride(true);
} else {
thisMethod.ignoreModelAnnotations();
}
thisMethod.modifiers(PUBLIC);
if (forImplementor) {
thisMethod.body(make().Return(naming.makeCompanionFieldName(iface)));
} else {
thisMethod.noBody();
}
return thisMethod;
}
private Type getBestSatisfiedType(Type currentType, Interface iface) {
Type refinedSuperType = currentType.getSupertype(iface);
Type firstSatisfiedType = getFirstSatisfiedType(currentType, iface);
// in the very special case of the first satisfied type having type arguments erased to Object and
// the most refined one having free type parameters, we prefer the one with free type parameters
// because Java prefers it and it's in range with what nonWideningType does
Map refinedTAs = refinedSuperType.getTypeArguments();
Map firstTAs = firstSatisfiedType.getTypeArguments();
for(TypeParameter tp : iface.getTypeParameters()){
Type refinedTA = refinedTAs.get(tp);
Type firstTA = firstTAs.get(tp);
if(willEraseToObject(firstTA) && isTypeParameter(refinedTA))
return refinedSuperType;
}
return firstSatisfiedType;
}
private Type getFirstSatisfiedType(Type currentType, Interface iface) {
Type found = null;
TypeDeclaration currentDecl = currentType.getDeclaration();
if (Decl.equal(currentDecl, iface)) {
return currentType;
}
if(currentType.getExtendedType() != null){
Type supertype = currentType.getSupertype(currentType.getExtendedType().getDeclaration());
found = getFirstSatisfiedType(supertype, iface);
if(found != null)
return found;
}
for(Type superInterfaceType : currentType.getSatisfiedTypes()){
found = getFirstSatisfiedType(superInterfaceType, iface);
if(found != null)
return found;
}
return null;
}
private void buildCompanion(final Tree.ClassOrInterface def,
final Interface model, ClassDefinitionBuilder classBuilder) {
at(def);
// Give the $impl companion a $this field...
classBuilder.getCompanionBuilder2(model);
}
private List makeLocalContainerPath(Interface model) {
List path = List.nil();
Scope container = model.getContainer();
while(container != null
&& container instanceof Package == false){
if(container instanceof Declaration)
path = path.prepend(((Declaration) container).getPrefixedName());
container = container.getContainer();
}
return makeAtLocalContainer(path, model.isCompanionClassNeeded() ? model.getJavaCompanionClassName() : null);
}
public List transformRefinementSpecifierStatement(SpecifierStatement op, ClassDefinitionBuilder classBuilder) {
List result = List.nil();
// Check if this is a shortcut form of formal attribute refinement
if (op.getRefinement()) {
Tree.Term baseMemberTerm = op.getBaseMemberExpression();
if(baseMemberTerm instanceof Tree.ParameterizedExpression)
baseMemberTerm = ((Tree.ParameterizedExpression)baseMemberTerm).getPrimary();
Tree.BaseMemberExpression expr = (BaseMemberExpression) baseMemberTerm;
Declaration decl = expr.getDeclaration();
if (Decl.isValue(decl) || Decl.isGetter(decl)) {
// Now build a "fake" declaration for the attribute
Tree.AttributeDeclaration attrDecl = new Tree.AttributeDeclaration(null);
attrDecl.setDeclarationModel((Value)decl);
attrDecl.setIdentifier(expr.getIdentifier());
attrDecl.setScope(op.getScope());
attrDecl.setSpecifierOrInitializerExpression(op.getSpecifierExpression());
attrDecl.setAnnotationList(makeShortcutRefinementAnnotationTrees());
// Make sure the boxing information is set correctly
BoxingDeclarationVisitor v = new CompilerBoxingDeclarationVisitor(this);
v.visit(attrDecl);
// Generate the attribute
transform(attrDecl, classBuilder);
} else if (decl instanceof Function) {
// Now build a "fake" declaration for the method
Tree.MethodDeclaration methDecl = new Tree.MethodDeclaration(null);
Function m = (Function)decl;
methDecl.setDeclarationModel(m);
methDecl.setIdentifier(expr.getIdentifier());
methDecl.setScope(op.getScope());
methDecl.setAnnotationList(makeShortcutRefinementAnnotationTrees());
Tree.SpecifierExpression specifierExpression = op.getSpecifierExpression();
methDecl.setSpecifierExpression(specifierExpression);
if(specifierExpression instanceof Tree.LazySpecifierExpression == false){
Tree.Expression expression = specifierExpression.getExpression();
Tree.Term expressionTerm = Decl.unwrapExpressionsUntilTerm(expression);
// we can optimise lambdas and static method calls
if(!CodegenUtil.canOptimiseMethodSpecifier(expressionTerm, m)){
// we need a field to save the callable value
String name = naming.getMethodSpecifierAttributeName(m);
JCExpression specifierType = makeJavaType(expression.getTypeModel());
JCExpression specifier = expressionGen().transformExpression(expression);
classBuilder.field(PRIVATE | FINAL, name, specifierType, specifier, false);
}
}
java.util.List parameterListTrees = null;
if(op.getBaseMemberExpression() instanceof Tree.ParameterizedExpression){
parameterListTrees = new ArrayList<>(m.getParameterLists().size());
parameterListTrees.addAll(((Tree.ParameterizedExpression)op.getBaseMemberExpression()).getParameterLists());
Tree.Term term = specifierExpression.getExpression().getTerm();
// mpl refined by single pl with anonymous functions
// we bring each anonymous function pl up to the mpl method
// and give it the given block of expr as it's specifier
while (term instanceof Tree.FunctionArgument
&& m.getParameterLists().size() > 1) {
FunctionArgument functionArgument = (Tree.FunctionArgument)term;
specifierExpression.setExpression(functionArgument.getExpression());
parameterListTrees.addAll(functionArgument.getParameterLists());
term = functionArgument.getExpression().getTerm();
}
}
int plIndex = 0;
// copy from formal declaration
for (ParameterList pl : m.getParameterLists()) {
Tree.ParameterList parameterListTree = null;
if(parameterListTrees != null)
parameterListTree = parameterListTrees.get(plIndex++);
Tree.ParameterList tpl = new Tree.ParameterList(null);
tpl.setModel(pl);
int pIndex = 0;
for (Parameter p : pl.getParameters()) {
Tree.Parameter parameterTree = null;
if(parameterListTree != null)
parameterTree = parameterListTree.getParameters().get(pIndex++);
Tree.Parameter tp = null;
if (p.getModel() instanceof Value) {
Tree.ValueParameterDeclaration tvpd = new Tree.ValueParameterDeclaration(null);
if(parameterTree != null)
tvpd.setTypedDeclaration(((Tree.ParameterDeclaration)parameterTree).getTypedDeclaration());
tvpd.setParameterModel(p);
tp = tvpd;
} else if (p.getModel() instanceof Function) {
Tree.FunctionalParameterDeclaration tfpd = new Tree.FunctionalParameterDeclaration(null);
if(parameterTree != null)
tfpd.setTypedDeclaration(((Tree.ParameterDeclaration)parameterTree).getTypedDeclaration());
tfpd.setParameterModel(p);
tp = tfpd;
} else {
throw BugException.unhandledDeclarationCase(p.getModel());
}
tp.setScope(p.getDeclaration().getContainer());
//tp.setIdentifier(makeIdentifier(p.getName()));
tpl.addParameter(tp);
}
methDecl.addParameterList(tpl);
}
// Make sure the boxing information is set correctly
BoxingDeclarationVisitor v = new CompilerBoxingDeclarationVisitor(this);
v.visit(methDecl);
// Generate the method
classBuilder.method(methDecl, Errors.GENERATE);
}
} else {
// Normal case, just generate the specifier statement
result = result.append(expressionGen().transform(op));
}
Tree.Term term = op.getBaseMemberExpression();
if (term instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme = (Tree.BaseMemberExpression)term;
DeferredSpecification ds = statementGen().getDeferredSpecification(bme.getDeclaration());
if (ds != null && needsInnerSubstitution(term.getScope(), bme.getDeclaration())){
result = result.append(ds.openInnerSubstitution());
}
}
return result;
}
private AnnotationList makeShortcutRefinementAnnotationTrees() {
AnnotationList annotationList = new AnnotationList(null);
Tree.Annotation shared = new Tree.Annotation(null);
Tree.BaseMemberExpression sharedPrimary = new Tree.BaseMemberExpression(null);
sharedPrimary.setDeclaration(typeFact().getLanguageModuleDeclaration("shared"));
shared.setPrimary(sharedPrimary);
annotationList.addAnnotation(shared);
Tree.Annotation actual = new Tree.Annotation(null);
Tree.BaseMemberExpression actualPrimary = new Tree.BaseMemberExpression(null);
actualPrimary.setDeclaration(typeFact().getLanguageModuleDeclaration("actual"));
actual.setPrimary(actualPrimary);
annotationList.addAnnotation(actual);
return annotationList;
}
/**
* We only need an inner substitution if we're within that substitution's scope
*/
private boolean needsInnerSubstitution(Scope scope, Declaration declaration) {
while(scope != null && scope instanceof Package == false){
if(scope instanceof ControlBlock){
Set specifiedValues = ((ControlBlock) scope).getSpecifiedValues();
if(specifiedValues != null && specifiedValues.contains(declaration))
return true;
}
scope = scope.getScope();
}
return false;
}
public void transform(Tree.AttributeDeclaration decl, ClassDefinitionBuilder classBuilder) {
final Value model = decl.getDeclarationModel();
boolean lazy = decl.getSpecifierOrInitializerExpression() instanceof LazySpecifierExpression;
boolean useField = Strategy.useField(model) && !lazy;
String attrName = decl.getIdentifier().getText();
boolean memoized = Decl.isMemoized(decl);
// Only a non-formal or a concrete-non-lazy attribute has a corresponding field
// and if a captured class parameter exists with the same name we skip this part as well
Parameter parameter = CodegenUtil.findParamForDecl(decl);
boolean createField = Strategy.createField(parameter, model) && !lazy;
boolean concrete = Decl.withinInterface(decl)
&& decl.getSpecifierOrInitializerExpression() != null;
JCThrow err = null;
JCExpression memoizedInitialValue = null;
if (!lazy &&
(concrete ||
(!Decl.isFormal(decl)
&& !decl.getDeclarationModel().isJavaNative()
&& createField))) {
TypedReference typedRef = getTypedReference(model);
TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
Type nonWideningType = nonWideningType(typedRef, nonWideningTypedRef);
if (Decl.isIndirect(decl)) {
attrName = Naming.getAttrClassName(model, 0);
nonWideningType = getGetterInterfaceType(model);
}
JCExpression initialValue = null;
BoxingStrategy boxingStrategy = null;
if (decl.getSpecifierOrInitializerExpression() != null) {
Tree.Expression expression = decl.getSpecifierOrInitializerExpression().getExpression();
HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(expression.getTerm());
int flags = CodegenUtil.downcastForSmall(expression, model) ? ExpressionTransformer.EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK : 0;
flags |= model.hasUncheckedNullType() ? ExpressionTransformer.EXPR_TARGET_ACCEPTS_NULL : 0;
if (error != null) {
initialValue = null;
err = makeThrowUnresolvedCompilationError(error.getErrorMessage().getMessage());
} else {
boxingStrategy = useJavaBox(model, nonWideningType)
&& javaBoxExpression(expression.getTypeModel(), nonWideningType) ? BoxingStrategy.JAVA : CodegenUtil.getBoxingStrategy(model);
initialValue = expressionGen().transformExpression(expression,
boxingStrategy,
model.isStatic() && nonWideningType.isTypeParameter() ? typeFact().getAnythingType() : nonWideningType, flags);
}
}
if (memoized) {
memoizedInitialValue = initialValue;
initialValue = makeDefaultExprForType(nonWideningType);
}
int flags = 0;
if (!CodegenUtil.isUnBoxed(nonWideningTypedRef.getDeclaration())) {
flags |= JT_NO_PRIMITIVES;
}
long modifiers = (useField) ? modifierTransformation().field(decl) : modifierTransformation().localVar(decl);
// If the attribute is really from a parameter then don't generate a field
// (makeAttributeForValueParameter() or makeMethodForFunctionalParameter()
// does it in those cases)
if (parameter == null
|| parameter.isHidden()) {
JCExpression type;
if (model.isStatic() && nonWideningType.isTypeParameter()) {
type = make().Type(syms().objectType);
} else {
type = makeJavaType(nonWideningType, flags);
}
if (concrete) {
classBuilder.getCompanionBuilder((TypeDeclaration)model.getContainer()).field(modifiers, attrName, type, initialValue, !useField);
} else {
List annos = makeAtIgnore().prependList(expressionGen().transformAnnotations(OutputElement.FIELD, decl));
if (classBuilder.hasDelegatingConstructors()) {
annos = annos.prependList(makeAtNoInitCheck());
}
// fields should be ignored, they are accessed by the getters
if (err == null) {
// TODO This should really be using AttributeDefinitionBuilder some how
if (useField) {
AttributeDefinitionBuilder adb = AttributeDefinitionBuilder.field(this, null, attrName, model, Decl.isIndirect(decl)).
fieldAnnotations(annos).
initialValue(initialValue, boxingStrategy).
fieldVisibilityModifiers(modifiers).
modifiers(modifiers);
classBuilder.defs((List)adb.
buildFields());
List buildInit = adb.buildInit(false);
if (!buildInit.isEmpty()) {
if (model.isStatic()) {
classBuilder.defs(make().Block(STATIC, buildInit));
} else {
classBuilder.getInitBuilder().init((List)buildInit);
}
}
} else if (!memoized) {
classBuilder.field(modifiers, attrName, type, initialValue, !useField, annos);
if (!isEe(model) && model.isLate() && CodegenUtil.needsLateInitField(model, typeFact())) {
classBuilder.field(PRIVATE | Flags.VOLATILE | Flags.TRANSIENT, Naming.getInitializationFieldName(attrName),
make().Type(syms().booleanType),
make().Literal(false), false, makeAtIgnore());
}
}
}
}
}
// A shared attribute might be initialized in a for statement, so
// we might need a def-assignment subst for it
JCStatement outerSubs = statementGen().openOuterSubstitutionIfNeeded(
decl.getDeclarationModel(), model.getType(), 0);
if (outerSubs != null) {
classBuilder.getInitBuilder().init(outerSubs);
}
}
boolean withinInterface = Decl.withinInterface(decl);
if (useField || withinInterface || lazy) {
if (!withinInterface || model.isShared()) {
// Generate getter in main class or interface (when shared)
at(decl.getType());
AttributeDefinitionBuilder getter = makeGetter(decl, false, lazy, memoizedInitialValue);
if (err != null) {
getter.getterBlock(make().Block(0, List.of(err)));
}
classBuilder.attribute(getter);
}
if (withinInterface && lazy) {
// Generate getter in companion class
classBuilder.getCompanionBuilder((Interface)decl.getDeclarationModel().getContainer()).attribute(makeGetter(decl, true, lazy, null));
}
if (Decl.isVariable(decl) || Decl.isLate(decl)) {
if (!withinInterface || model.isShared()) {
// Generate setter in main class or interface (when shared)
classBuilder.attribute(makeSetter(decl, false, lazy, memoizedInitialValue));
}
if (withinInterface && lazy) {
// Generate setter in companion class
classBuilder.getCompanionBuilder((Interface)decl.getDeclarationModel().getContainer()).attribute(makeSetter(decl, true, lazy, null));
}
}
}
}
public AttributeDefinitionBuilder transform(AttributeSetterDefinition decl, boolean forCompanion) {
if (Strategy.onlyOnCompanion(decl.getDeclarationModel()) && !forCompanion) {
return null;
}
String name = decl.getIdentifier().getText();
final AttributeDefinitionBuilder builder = AttributeDefinitionBuilder
/*
* We use the getter as TypedDeclaration here because this is the same type but has a refined
* declaration we can use to make sure we're not widening the attribute type.
*/
.setter(this, decl, name, decl.getDeclarationModel().getGetter())
.modifiers(modifierTransformation().getterSetter(decl.getDeclarationModel(), forCompanion));
// companion class members are never actual no matter what the Declaration says
if(forCompanion)
builder.notActual();
if (Decl.withinClass(decl) || forCompanion) {
JCBlock setterBlock = makeSetterBlock(decl.getDeclarationModel(), decl.getBlock(), decl.getSpecifierExpression());
builder.setterBlock(setterBlock);
} else {
builder.isFormal(true);
}
builder.userAnnotationsSetter(expressionGen().transformAnnotations(OutputElement.SETTER, decl));
return builder;
}
public AttributeDefinitionBuilder transform(AttributeGetterDefinition decl, boolean forCompanion) {
if (Strategy.onlyOnCompanion(decl.getDeclarationModel()) && !forCompanion) {
return null;
}
String name = decl.getIdentifier().getText();
//expressionGen().transform(decl.getAnnotationList());
final AttributeDefinitionBuilder builder = AttributeDefinitionBuilder
.getter(this, name, decl.getDeclarationModel())
.modifiers(modifierTransformation().getterSetter(decl.getDeclarationModel(), forCompanion));
// companion class members are never actual no matter what the Declaration says
if(forCompanion)
builder.notActual();
if (Decl.withinClass(decl) || forCompanion) {
JCBlock body = statementGen().transform(decl.getBlock());
builder.getterBlock(body);
} else {
builder.isFormal(true);
}
builder.userAnnotations(expressionGen().transformAnnotations(OutputElement.GETTER, decl));
return builder;
}
/**
* Encapsulates the modifiers we use for various things.
*/
class ModifierTransformation {
protected long declarationSharedFlags(Declaration decl){
return Decl.isShared(decl) && !Decl.isAncestorLocal(decl) ? PUBLIC : 0;
}
public long jpaConstructor(Class model) {
return PROTECTED;
}
public long canonicalMethodBridge() {
return PRIVATE;
}
public long methodBridge(Function method) {
return PUBLIC | (method.isDefault() ? 0 : FINAL);
}
public long setterBridge(Setter setter) {
return PUBLIC | (setter.getGetter().isDefault() ? 0 : FINAL);
}
public long getterBridge(Value attr) {
return PUBLIC | (attr.isDefault() ? 0 : FINAL);
}
public long defaultValueMethodBridge() {
return PUBLIC | FINAL;
}
public long instantiatorBridgeFlags(boolean includeBody) {
long flags = PUBLIC;
if(includeBody)
flags |= FINAL;
else
flags |= ABSTRACT;
return flags;
}
public long classFlags(ClassOrInterface cdecl) {
int result = 0;
result |= declarationSharedFlags(cdecl);
// aliases cannot be abstract, especially since they're just placeholders
result |= (cdecl instanceof Class) && (cdecl.isAbstract() || cdecl.isFormal()) && !cdecl.isAlias() ? ABSTRACT : 0;
result |= (cdecl instanceof Interface) ? INTERFACE : 0;
// aliases are always final placeholders, final classes are also final
result |= (cdecl instanceof Class) && (cdecl.isAlias() || cdecl.isFinal()) ? FINAL : 0;
result |= cdecl.isStatic() ? STATIC : 0;
if (isJavaStrictfp(cdecl)) {
result |= Flags.STRICTFP;
}
return result;
}
public long constructor(ClassOrInterface cdecl) {
return declarationSharedFlags(cdecl);
}
public int constructor(Constructor ctor) {
return Decl.isShared(ctor)
&& !Decl.isAncestorLocal(ctor)
&& !ctor.isAbstract()
&& !Decl.isEnumeratedConstructor(ctor)? PUBLIC : PRIVATE;
}
public long typeAlias(TypeAlias decl) {
int result = 0;
result |= declarationSharedFlags(decl);
result |= FINAL;
result |= decl.isStatic() ? STATIC : 0;
return result;
}
public long method(Function def) {
int result = 0;
if (def.isToplevel()) {
result |= def.isShared() ? PUBLIC : 0;
result |= STATIC;
} else if (Decl.isLocalNotInitializer(def)) {
result |= def.isShared() ? PUBLIC : 0;
} else {
result |= def.isShared() ? PUBLIC : PRIVATE;
result |= def.isFormal() && !def.isDefault() ? ABSTRACT : 0;
result |= !(def.isFormal() || def.isDefault() || def.getContainer() instanceof Interface) ? FINAL : 0;
result |= def.isStatic() ? STATIC : 0;
}
if (isJavaSynchronized(def)) {
result |= Flags.SYNCHRONIZED;
}
if (isJavaStrictfp(def)) {
result |= Flags.STRICTFP;
}
return result;
}
private boolean containsInteropAnnotation(Tree.AnnotationList annos, String annotationName) {
for (Tree.Annotation anno : annos.getAnnotations()) {
Declaration declaration = ((Tree.MemberOrTypeExpression)anno.getPrimary()).getDeclaration();
if (declaration != null && annotationName.equals(declaration.getQualifiedNameString())) {
return true;
}
}
return false;
}
public long field(Tree.AttributeDeclaration cdecl) {
int result = 0;
result |= Decl.isVariable(cdecl) || Decl.isLate(cdecl) ? 0 : FINAL;
result |= cdecl.getDeclarationModel().isStatic() ? STATIC : 0;
if(!CodegenUtil.hasCompilerAnnotation(cdecl, "packageProtected"))
result |= PRIVATE;
if (isJavaTransient(cdecl.getDeclarationModel())) {
result |= Flags.TRANSIENT;
}
if (isJavaVolatile(cdecl.getDeclarationModel())) {
result |= Flags.VOLATILE;
}
return result;
}
public long localVar(Tree.AttributeDeclaration cdecl) {
int result = 0;
result |= Decl.isVariable(cdecl) ? 0 : FINAL;
result |= cdecl.getDeclarationModel().isStatic() ? STATIC : 0;
return result;
}
/**
* Returns the modifier flags to be used for the getter & setter for the
* given attribute-like declaration.
* @param tdecl attribute-like declaration (Value, Getter, Parameter etc)
* @param forCompanion Whether the getter/setter is on a companion type
* @return The modifier flags.
*/
public long getterSetter(TypedDeclaration tdecl, boolean forCompanion) {
if (tdecl instanceof Setter) {
// Spec says: A setter may not be annotated shared, default or
// actual. The visibility and refinement modifiers of an attribute
// with a setter are specified by annotating the matching getter.
tdecl = ((Setter)tdecl).getGetter();
}
int result = 0;
result |= tdecl.isShared() ? PUBLIC : PRIVATE;
result |= ((tdecl.isFormal() && !tdecl.isDefault()) && !forCompanion) ? ABSTRACT : 0;
result |= !(tdecl.isFormal() || tdecl.isDefault() || Decl.withinInterface(tdecl)) || forCompanion ? FINAL : 0;
result |= tdecl.isStatic() ? STATIC : 0;
if (isJavaSynchronized(tdecl)) {
result |= Flags.SYNCHRONIZED;
}
if (isJavaNative(tdecl)) {
result |= Flags.NATIVE;
}
if (isJavaStrictfp(tdecl)) {
result |= Flags.STRICTFP;
}
return result;
}
public long object(Value cdecl) {
int result = 0;
result |= FINAL;
result |= !Decl.isAncestorLocal(cdecl) && Decl.isShared(cdecl) ? PUBLIC : 0;
result |= cdecl.isStatic() ? STATIC : 0;
if (isJavaStrictfp(cdecl)) {
result |= Flags.STRICTFP;
}
return result;
}
public long defaultParameterMethodOverload(Function method, DaoBody daoBody) {
long mods = method(method) & ~Flags.NATIVE;
if (daoBody instanceof DaoAbstract == false) {
mods &= ~ABSTRACT;
}
if (daoBody instanceof DaoCompanion) {
mods |= FINAL;
};
return mods;
}
public long defaultParameterInstantiatorOverload(Class klass) {
// remove the FINAL bit in case it gets set, because that is valid for a class decl, but
// not for a method if in an interface
long modifiers = classFlags(klass) & ~FINAL;
// when refining a member class of an interface because the
// instantiator method is declared in the companion interface it is
// effectively public.
if (klass instanceof Class && klass.isActual()) {
modifiers &= ~(PRIVATE | PROTECTED);
modifiers |= PUBLIC;
}
// alias classes cannot be abstract since they're placeholders, but it's possible to have formal class aliases
// and the instantiator method needs the abstract bit
if(klass.isFormal() && klass.isAlias())
modifiers |= ABSTRACT;
return modifiers;
}
public long defaultParameterConstructorOverload(Class klass) {
return constructor(klass) & (PUBLIC | PRIVATE | PROTECTED);
}
public long defaultParameterMethod(boolean noBody, Declaration container) {
int modifiers = 0;
if (noBody) {
modifiers |= PUBLIC | ABSTRACT;
} else if (container == null
|| !(container instanceof Class
&& Strategy.defaultParameterMethodStatic(container))) {
// initializers can override parameter defaults
modifiers |= FINAL;
}
if (container != null && container.isShared()) {
modifiers |= PUBLIC;
} else if (container == null || (!container.isToplevel()
&& !noBody)){
modifiers |= PRIVATE;
}
boolean staticMethod = container != null && Strategy.defaultParameterMethodStatic(container);
if (staticMethod) {
// static default parameter methods should be consistently public so that if non-shared class Top and
// shared class Bottom which extends Top both have the same default param name, we don't get an error
// if the Bottom class tries to "hide" a static public method with a private one
modifiers &= ~PRIVATE;
modifiers |= STATIC | PUBLIC;
}
if (isJavaStrictfp(container)) {
modifiers |= Flags.STRICTFP;
}
return modifiers;
}
public long transformClassParameterDeclFlags(Parameter param) {
return param.getModel().isVariable() ? 0 : FINAL;
}
public long transformClassParameterDeclFlagsField(Parameter param, Tree.Declaration annotated) {
long result = transformClassParameterDeclFlags(param) | PRIVATE;
if (isJavaTransient(annotated.getDeclarationModel())) {
result |= Flags.TRANSIENT;
}
if (isJavaVolatile(annotated.getDeclarationModel())) {
result |= Flags.VOLATILE;
}
return result;
}
}
/**
* In EE mode we change the modifiers:
*
* - We don't generate
final
methods
* - The implict no-args constructor is generated as public, not protected
*
*/
private class EeModifierTransformation extends ModifierTransformation {
@Override
public long jpaConstructor(Class model) {
return model.isShared() ? PUBLIC : super.jpaConstructor(model);
}
@Override
public long method(Function def) {
long result = super.method(def);
return result & (~FINAL);
}
@Override
public long defaultParameterMethodOverload(Function method, DaoBody daoBody) {
long result = super.defaultParameterMethodOverload(method, daoBody);
return result & (~FINAL);
}
@Override
public long getterSetter(TypedDeclaration tdecl, boolean forCompanion) {
long result = super.getterSetter(tdecl, forCompanion);
if (!forCompanion) {
return result & (~FINAL);
} else {
return result;
}
}
@Override
public long methodBridge(Function method) {
long result = super.methodBridge(method);
return result & (~FINAL);
}
@Override
public long setterBridge(Setter setter) {
long result = super.setterBridge(setter);
return result & (~FINAL);
}
@Override
public long getterBridge(Value attr) {
long result = super.getterBridge(attr);
return result & (~FINAL);
}
@Override
public long defaultValueMethodBridge() {
long result = super.defaultValueMethodBridge();
return result & (~FINAL);
}
@Override
public long instantiatorBridgeFlags(boolean includeBody) {
long result = super.instantiatorBridgeFlags(includeBody);
return result & (~FINAL);
}
@Override
public long defaultParameterMethod(boolean noBody, Declaration container) {
long result = super.defaultParameterMethod(noBody, container);
return result & (~FINAL);
}
}
private ModifierTransformation modifierTransformation = new ModifierTransformation();
ModifierTransformation modifierTransformation() {
return modifierTransformation;
}
ModifierTransformation replaceModifierTransformation(ModifierTransformation mt) {
ModifierTransformation old = this.modifierTransformation;
this.modifierTransformation = mt;
return old;
}
private AttributeDefinitionBuilder makeGetterOrSetter(Tree.AttributeDeclaration decl, boolean forCompanion, boolean lazy,
AttributeDefinitionBuilder builder, boolean isGetter) {
at(decl);
if (forCompanion || lazy) {
SpecifierOrInitializerExpression specOrInit = decl.getSpecifierOrInitializerExpression();
if (specOrInit != null) {
HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(specOrInit.getExpression());
if (error != null) {
builder.getterBlock(make().Block(0, List.of(this.makeThrowUnresolvedCompilationError(error))));
} else {
Value declarationModel = decl.getDeclarationModel();
TypedReference typedRef = getTypedReference(declarationModel);
TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef);
Type nonWideningType = nonWideningType(typedRef, nonWideningTypedRef);
int flags = 0;
if(declarationModel.hasUncheckedNullType())
flags |= ExpressionTransformer.EXPR_TARGET_ACCEPTS_NULL;
if (CodegenUtil.downcastForSmall(specOrInit.getExpression(), decl.getDeclarationModel()))
flags |= ExpressionTransformer.EXPR_UNSAFE_PRIMITIVE_TYPECAST_OK;
JCExpression expr = expressionGen().transformExpression(specOrInit.getExpression(),
CodegenUtil.getBoxingStrategy(declarationModel),
nonWideningType,
flags);
expr = convertToIntIfHashAttribute(declarationModel, expr);
builder.getterBlock(make().Block(0, List.of(make().Return(expr))));
}
} else {
JCExpression accessor = naming.makeQualifiedName(
naming.makeQuotedThis(),
decl.getDeclarationModel(),
Naming.NA_MEMBER | (isGetter ? Naming.NA_GETTER : Naming.NA_SETTER));
if (isGetter) {
builder.getterBlock(make().Block(0, List.of(make().Return(
make().Apply(
null,
accessor,
List.nil())))));
} else {
List args = List.of(naming.makeName(decl.getDeclarationModel(), Naming.NA_MEMBER | Naming.NA_IDENT));
builder.setterBlock(make().Block(0, List.of(make().Exec(
make().Apply(
null,
accessor,
args)))));
}
}
}
if(forCompanion)
builder.notActual();
return builder
.modifiers(modifierTransformation().getterSetter(decl.getDeclarationModel(), forCompanion))
.isFormal((Decl.isFormal(decl) || Decl.withinInterface(decl)) && !forCompanion)
.isJavaNative(decl.getDeclarationModel().isJavaNative());
}
private AttributeDefinitionBuilder makeGetter(Tree.AttributeDeclaration decl, boolean forCompanion, boolean lazy, JCExpression memoizedInitialValue) {
at(decl);
String attrName = decl.getIdentifier().getText();
AttributeDefinitionBuilder getter = AttributeDefinitionBuilder
.getter(this, attrName, decl.getDeclarationModel(), memoizedInitialValue);
if(!decl.getDeclarationModel().isInterfaceMember()
|| (decl.getDeclarationModel().isShared() ^ forCompanion))
getter.userAnnotations(expressionGen().transformAnnotations(OutputElement.GETTER, decl));
else
getter.ignoreAnnotations();
if (Decl.isIndirect(decl)) {
getter.getterBlock(generateIndirectGetterBlock(decl.getDeclarationModel()));
}
return makeGetterOrSetter(decl, forCompanion, lazy, getter, true);
}
private JCTree.JCBlock generateIndirectGetterBlock(Value v) {
JCTree.JCExpression returnExpr;
returnExpr = naming.makeQualIdent(naming.makeName(v, Naming.NA_WRAPPER), "get_");
returnExpr = make().Apply(null, returnExpr, List.nil());
JCReturn returnValue = make().Return(returnExpr);
List stmts = List.of(returnValue);
JCTree.JCBlock block = make().Block(0L, stmts);
return block;
}
private AttributeDefinitionBuilder makeSetter(Tree.AttributeDeclaration decl, boolean forCompanion, boolean lazy, JCExpression memoizedInitialValue) {
at(decl);
String attrName = decl.getIdentifier().getText();
AttributeDefinitionBuilder setter = AttributeDefinitionBuilder.setter(this, decl, attrName, decl.getDeclarationModel(), memoizedInitialValue);
setter.userAnnotationsSetter(expressionGen().transformAnnotations(OutputElement.SETTER, decl));
return makeGetterOrSetter(decl, forCompanion, lazy, setter, false);
}
public List transformWrappedMethod(Tree.AnyMethod def, TransformationPlan plan) {
final Function model = def.getDeclarationModel();
if (model.isParameter()) {
return List.nil();
}
naming.clearSubstitutions(model);
// Generate a wrapper class for the method
String name = def.getIdentifier().getText();
ClassDefinitionBuilder builder = ClassDefinitionBuilder.methodWrapper(this, name, Decl.isShared(def), isJavaStrictfp(model));
// Make sure it's Java Serializable (except toplevels which we never instantiate)
if(!model.isToplevel())
builder.introduce(make().QualIdent(syms().serializableType.tsym));
if (Decl.isAnnotationConstructor(def)) {
AnnotationInvocation ai = ((AnnotationInvocation)def.getDeclarationModel().getAnnotationConstructor());
if (ai != null) {
builder.annotations(List.of(makeAtAnnotationInstantiation(ai)));
builder.annotations(makeExprAnnotations(def, ai));
}
}
builder.methods(classGen().transform(def, plan, builder));
// Toplevel method
if (Strategy.generateMain(def)) {
// Add a main() method
builder.method(makeMainForFunction(model));
}
if(Decl.isLocal(model) || Decl.isToplevel(model)){
builder.annotations(makeAtLocalDeclarations(def));
}
if(Decl.isLocal(model)){
builder.annotations(makeAtLocalDeclaration(model.getQualifier(), false));
}
builder.at(def);
List result = builder.build();
if (Decl.isLocal(def)) {
// Inner method
JCVariableDecl call = at(def).VarDef(
make().Modifiers(FINAL),
naming.getSyntheticInstanceName(model),
naming.makeSyntheticClassname(model),
makeSyntheticInstance(model));
result = result.append(call);
}
//if (Decl.isAnnotationConstructor(def)) {
//result = result.prependList(transformAnnotationConstructorType(def));
//}
return result;
}
/**
* Make the {@code @*Exprs} annotations to hold the literal arguments
* to the invocation.
*/
private List makeExprAnnotations(Tree.AnyMethod def,
AnnotationInvocation ai) {
AnnotationInvocation ctor = (AnnotationInvocation)def.getDeclarationModel().getAnnotationConstructor();
return ai.makeExprAnnotations(expressionGen(), ctor, List.nil());
}
public JCAnnotation makeAtAnnotationInstantiation(AnnotationInvocation invocation) {
return invocation.encode(this, new ListBuffer());
}
private MethodDefinitionBuilder makeAnnotationMethod(Tree.Parameter parameter) {
Parameter parameterModel = parameter.getParameterModel();
JCExpression type = transformAnnotationMethodType(parameter);
JCExpression defaultValue = parameterModel.isDefaulted() ? transformAnnotationParameterDefault(parameter) : null;
MethodDefinitionBuilder mdb = MethodDefinitionBuilder.method(this, parameterModel.getModel(), Naming.NA_ANNOTATION_MEMBER);
if (isMetamodelReference(parameterModel.getType())
||
(typeFact().isIterableType(parameterModel.getType())
&& isMetamodelReference(typeFact().getIteratedType(parameterModel.getType())))) {
mdb.modelAnnotations(List.of(make().Annotation(make().Type(syms().ceylonAtDeclarationReferenceType),
List.nil())));
} else if (Decl.isEnumeratedTypeWithAnonCases(parameterModel.getType())
||
(typeFact().isIterableType(parameterModel.getType())
&& Decl.isEnumeratedTypeWithAnonCases(typeFact().getIteratedType(parameterModel.getType())))) {
mdb.modelAnnotations(List.of(make().Annotation(make().Type(syms().ceylonAtEnumerationReferenceType),
List.nil())));
}
mdb.modifiers(PUBLIC | ABSTRACT);
mdb.resultType(new TransformedType(type));
mdb.defaultValue(defaultValue);
mdb.noBody();
return mdb;
}
public List transform(Tree.AnyMethod def, TransformationPlan plan, ClassDefinitionBuilder classBuilder) {
if (def.getDeclarationModel().isParameter()) {
return List.nil();
}
if (plan instanceof ThrowerMethod) {
addRefinedThrowerMethod(classBuilder,
plan.getErrorMessage().getMessage(),
(Class)def.getDeclarationModel().getContainer(),
(Function)def.getDeclarationModel().getRefinedDeclaration());
return List.nil();
}
// Transform the method body of the 'inner-most method'
boolean prevSyntheticClassBody = expressionGen().withinSyntheticClassBody(Decl.isMpl(def.getDeclarationModel())
|| Decl.isLocalNotInitializer(def)
|| expressionGen().isWithinSyntheticClassBody());
List body = transformMethodBody(def);
expressionGen().withinSyntheticClassBody(prevSyntheticClassBody);
return transform(def, classBuilder, body);
}
List transform(Tree.AnyMethod def,
ClassDefinitionBuilder classBuilder, List body) {
final Function model = def.getDeclarationModel();
List result = List.nil();
if (!Decl.withinInterface(model)) {
// Transform to the class
boolean refinedResultType = !model.getType().isExactly(
((TypedDeclaration)model.getRefinedDeclaration()).getType());
result = transformMethod(def,
true,
true,
true,
transformMplBodyUnlessSpecifier(def, model, body),
refinedResultType
&& !Decl.withinInterface(model.getRefinedDeclaration())? new DaoSuper() : new DaoThis(def, def.getParameterLists().get(0)),
!Strategy.defaultParameterMethodOnSelf(model)
&& !Strategy.defaultParameterMethodStatic(model));
} else {// Is within interface
// Transform the definition to the companion class, how depends
// on what kind of method it is
List companionDefs;
if (def instanceof Tree.MethodDeclaration) {
final SpecifierExpression specifier = ((Tree.MethodDeclaration) def).getSpecifierExpression();
if (specifier == null) {
// formal or abstract
// (still need overloads and DPMs on the companion)
companionDefs = transformMethod(def,
false,
true,
true,
null,
new DaoCompanion(def, def.getParameterLists().get(0)),
false);
} else {
companionDefs = transformMethod(def,
true,
false,
!model.isShared(),
transformMplBodyUnlessSpecifier(def, model, body),
new DaoCompanion(def, def.getParameterLists().get(0)),
false);
}
} else if (def instanceof Tree.MethodDefinition) {
companionDefs = transformMethod(def,
true,
false,
!model.isShared(),
transformMplBodyUnlessSpecifier(def, model, body),
new DaoCompanion(def, def.getParameterLists().get(0)),
false);
} else {
throw BugException.unhandledNodeCase(def);
}
if(!companionDefs.isEmpty())
classBuilder.getCompanionBuilder((TypeDeclaration)model.getContainer())
.methods(companionDefs);
// Transform the declaration to the target interface
// but only if it's shared and not java native
if (Decl.isShared(model)
&& !model.isJavaNative()) {
result = transformMethod(def,
true,
true,
true,
null,
daoAbstract,
!Strategy.defaultParameterMethodOnSelf(model));
}
}
return result;
}
/**
* Transforms a method, generating default argument overloads and
* default value methods
* @param def The method
* @param model The method model
* @param methodName The method name
* @param transformMethod Whether the method itself should be transformed.
* @param actualAndAnnotations Whether the method itself is actual and has
* model annotations
* @param body The body of the method (or null for an abstract method)
* @param daoTransformation The default argument overload transformation
* @param transformDefaultValues Whether to generate default value methods
* @param defaultValuesBody Whether the default value methods should have a body
*/
private List transformMethod(Tree.AnyMethod def,
boolean transformMethod, boolean actual, boolean includeAnnotations, List body,
DaoBody daoTransformation,
boolean defaultValuesBody) {
return transformMethod(def.getDeclarationModel(),
def.getTypeParameterList(),
def,
def.getParameterLists(),
def,
transformMethod, actual, includeAnnotations, body,
daoTransformation,
defaultValuesBody);
}
private List transformMethod(
final Function methodModel,
Tree.TypeParameterList typeParameterList,
Tree.AnyMethod node,
java.util.List parameterLists,
Tree.Declaration annotated,
boolean transformMethod, boolean actual, boolean includeAnnotations, List body,
DaoBody daoTransformation,
boolean defaultValuesBody) {
ListBuffer lb = new ListBuffer();
Declaration refinedDeclaration = methodModel.getRefinedDeclaration();
final MethodDefinitionBuilder methodBuilder = MethodDefinitionBuilder.method(this, methodModel);
/*
if (typeParameterList != null) {
for (Tree.TypeParameterDeclaration param : typeParameterList.getTypeParameterDeclarations()) {
TypeDeclaration container = (TypeDeclaration)param.getDeclarationModel().getContainer();
methodBuilder.typeParameter(param.getDeclarationModel());
//ClassDefinitionBuilder companionBuilder = methodBuilder.getCompanionBuilder(container);
//if/(companionBuilder != null)
// companionBuilder.typeParameter(param);
}
}*/
// do the reified type param arguments
if (gen().supportsReified(methodModel)) {
methodBuilder.reifiedTypeParameters(Strategy.getEffectiveTypeParameters(methodModel));
}
if (methodModel.getParameterLists().size() > 1) {
methodBuilder.mpl(methodModel.getParameterLists());
}
boolean hasOverloads = false;
Tree.ParameterList parameterList = parameterLists.get(0);
int flags = 0;
if (rawParameters(methodModel)) {
flags |= JT_RAW;
}
for (final Tree.Parameter parameter : parameterList.getParameters()) {
Parameter parameterModel = parameter.getParameterModel();
List annotations = null;
if (includeAnnotations){
Tree.TypedDeclaration typedDeclaration = Decl.getMemberDeclaration(annotated, parameter);
// it can be null in the case of specifier refinement with no param list, but which we still optimise
// to a real method
// f = function(Integer param) => 2;
if(typedDeclaration != null)
annotations = expressionGen().transformAnnotations(OutputElement.PARAMETER, typedDeclaration);
}
//methodModel.getTypedReference().getTypedParameter(parameterModel).getType()
//parameterModel.getModel().getTypedReference().getType()
methodBuilder.parameter(parameter, parameterModel, annotations, flags, WideningRules.CAN_WIDEN);
if (Strategy.hasDefaultParameterValueMethod(parameterModel)
|| Strategy.hasDefaultParameterOverload(parameterModel)) {
if (Decl.equal(refinedDeclaration, methodModel)
|| (!Decl.withinInterface(methodModel) && body != null)
|| Decl.withinInterface(methodModel) && daoTransformation instanceof DaoCompanion == false) {
if (daoTransformation != null && (daoTransformation instanceof DaoCompanion == false || body != null)) {
DaoBody daoTrans = (body == null && !methodModel.isJavaNative()) ? daoAbstract : new DaoThis(node, parameterList);
MethodDefinitionBuilder overloadedMethod = new DefaultedArgumentMethod(daoTrans, MethodDefinitionBuilder.method(this, methodModel), methodModel)
.makeOverload(
parameterList.getModel(),
parameter.getParameterModel(),
Strategy.getEffectiveTypeParameters(methodModel));
overloadedMethod.location(null);
lb.append(overloadedMethod);
}
if (Decl.equal(refinedDeclaration, methodModel)
&& Strategy.hasDefaultParameterValueMethod(parameterModel)) {
lb.append(makeParamDefaultValueMethod(defaultValuesBody, methodModel, parameterList, parameter));
}
}
hasOverloads = true;
}
}
// Determine if we need to generate a "canonical" method
boolean createCanonical = hasOverloads
&& Decl.withinClassOrInterface(methodModel)
&& (body != null || methodModel.isJavaNative());
if (createCanonical) {
// Creates the private "canonical" method containing the actual body
MethodDefinitionBuilder canonicalMethod = new CanonicalMethod(daoTransformation, methodModel, body)
.makeOverload(
parameterList.getModel(),
null,
Strategy.getEffectiveTypeParameters(methodModel));
lb.append(canonicalMethod);
}
if (transformMethod) {
methodBuilder.modifiers(modifierTransformation().method(methodModel) | (methodModel.isJavaNative() && !createCanonical ? Flags.NATIVE : 0));
if (actual) {
methodBuilder.isOverride(methodModel.isActual());
}
if (includeAnnotations) {
methodBuilder.userAnnotations(expressionGen().transformAnnotations(OutputElement.METHOD, annotated));
methodBuilder.modelAnnotations(methodModel.getAnnotations());
if (!methodModel.isDefault() && isEe(methodModel)) {
methodBuilder.modelAnnotations(makeAtFinal());
}
} else {
methodBuilder.ignoreModelAnnotations();
}
methodBuilder.resultType(methodModel, 0);
copyTypeParameters(methodModel, methodBuilder);
if (createCanonical) {
// Creates method that redirects to the "canonical" method containing the actual body
MethodDefinitionBuilder overloadedMethod = new CanonicalMethod(new DaoThis(node, parameterList), methodBuilder, methodModel)
.makeOverload(
parameterList.getModel(),
null,
Strategy.getEffectiveTypeParameters(methodModel));
lb.append(overloadedMethod);
} else {
if (body != null) {
// Construct the outermost method using the body we've built so far
methodBuilder.body(body);
} else {
methodBuilder.noBody();
}
lb.append(methodBuilder);
}
}
return lb.toList();
}
List transformMplBodyUnlessSpecifier(Tree.AnyMethod def,
Function model,
List body) {
if (def instanceof Tree.MethodDeclaration) {
Tree.SpecifierExpression specifier = ((Tree.MethodDeclaration)def).getSpecifierExpression();
if (specifier == null) {
return body;
} else if (!(specifier instanceof Tree.LazySpecifierExpression)) {
if (!CodegenUtil.canOptimiseMethodSpecifier(specifier.getExpression().getTerm(), model)) {
return body;
}
}
}
return transformMplBody(def.getParameterLists(), model, body);
}
/**
* Constructs all but the outer-most method of a {@code Function} with
* multiple parameter lists
* @param model The {@code Function} model
* @param body The inner-most body
*/
List transformMplBody(java.util.List parameterListsTree,
Function model,
List body) {
Type resultType = model.getType();
for (int index = model.getParameterLists().size() - 1; index > 0; index--) {
ParameterList pl = model.getParameterLists().get(index);
resultType = gen().typeFact().getCallableType(List.of(resultType, typeFact().getParameterTypesAsTupleType(pl.getParameters(), model.getReference())));
CallableBuilder cb = CallableBuilder.mpl(gen(), resultType, pl, parameterListsTree.get(index), body);
body = List.of(make().Return(cb.build()));
}
return body;
}
private List transformMethodBody(Tree.AnyMethod def) {
List body = null;
final Function model = def.getDeclarationModel();
if (model.isDeferred()) {
// Uninitialized or deferred initialized method => Make a Callable field
String fieldName = naming.selector(model);
final Parameter initializingParameter = CodegenUtil.findParamForDecl(def);
int mods = PRIVATE;
JCExpression initialValue;
if (initializingParameter != null) {
mods |= FINAL;
initialValue = makeUnquotedIdent(Naming.getAliasedParameterName(initializingParameter));
} else {
// The field isn't initialized by a parameter, but later in the block
initialValue = makeNull();
}
Type callableType = model.getReference().getFullType();
current().field(mods, fieldName, makeJavaType(callableType), initialValue, false);
Invocation invocation = new CallableSpecifierInvocation(
this,
model,
makeUnquotedIdent(fieldName),
// we don't have to give a Term here because it's used for casting the Callable in case of callable erasure,
// but with deferred methods we can't define them so that they are erased so we're good
null,
def);
invocation.handleBoxing(true);
JCExpression call = expressionGen().transformInvocation(invocation);
JCStatement stmt;
if (!isVoid(def) || !Decl.isUnboxedVoid(model) || Strategy.useBoxedVoid((Function)model)) {
stmt = make().Return(call);
} else {
stmt = make().Exec(call);
}
JCStatement result;
if (initializingParameter == null) {
// If the field isn't initialized by a parameter we have to
// cope with the possibility that it's never initialized
final JCBinary cond = make().Binary(JCTree.Tag.EQ, makeUnquotedIdent(fieldName), makeNull());
final JCStatement throw_ = make().Throw(make().NewClass(null, null,
makeIdent(syms().ceylonUninitializedMethodErrorType),
List.nil(),
null));
result = make().If(cond, throw_, stmt);
} else {
result = stmt;
}
return List.of(result);
} else if (def instanceof Tree.MethodDefinition) {
body = transformMethodBlock((Tree.MethodDefinition)def);
} else if (def instanceof MethodDeclaration
&& ((MethodDeclaration) def).getSpecifierExpression() != null) {
body = transformSpecifiedMethodBody((MethodDeclaration)def, ((MethodDeclaration) def).getSpecifierExpression());
}
return body;
}
private List transformMethodBlock(
final Tree.MethodDefinition def) {
final Function model = def.getDeclarationModel();
final Tree.Block block = def.getBlock();
List body;
boolean prevNoExpressionlessReturn = statementGen().noExpressionlessReturn;
Substitution substitution = null;
JCStatement varDef = null;
Parameter lastParameter = Decl.getLastParameterFromFirstParameterList(model);
if(lastParameter != null
&& Decl.isJavaVariadicIncludingInheritance(lastParameter)){
SyntheticName alias = naming.alias(lastParameter.getName());
substitution = naming.addVariableSubst(lastParameter.getModel(), alias.getName());
varDef = substituteSequentialForJavaVariadic(alias, lastParameter);
}
try {
statementGen().noExpressionlessReturn = Decl.isMpl(model) || Strategy.useBoxedVoid(model);
body = statementGen().transformBlock(block);
} finally {
statementGen().noExpressionlessReturn = prevNoExpressionlessReturn;
if(substitution != null)
substitution.close();
}
// We void methods need to have their Callables return null
// so adjust here.
HasErrorException error = errors().getFirstErrorBlock(block);
if ((Decl.isMpl(model) || Strategy.useBoxedVoid(model))
&& !block.getDefinitelyReturns() && error == null) {
if (Decl.isUnboxedVoid(model)) {
body = body.append(make().Return(makeNull()));
} else {
body = body.append(make().Return(makeErroneous(block, "compiler bug: non-void method doesn't definitely return")));
}
}
if(varDef != null)
body = body.prepend(varDef);
return body;
}
private JCStatement substituteSequentialForJavaVariadic(SyntheticName alias, Parameter lastParameter) {
JCExpression seqType = makeJavaType(lastParameter.getType());
Type seqElemType = typeFact().getIteratedType(lastParameter.getType());
JCExpression init = javaVariadicToSequential(seqElemType, lastParameter);
return make().VarDef(make().Modifiers(FINAL), alias.asName(), seqType , init);
}
List transformSpecifiedMethodBody(Tree.MethodDeclaration def, SpecifierExpression specifierExpression) {
final Function model = def.getDeclarationModel();
Tree.MethodDeclaration methodDecl = def;
boolean isLazy = specifierExpression instanceof Tree.LazySpecifierExpression;
boolean returnNull = false;
JCExpression bodyExpr;
Tree.Term term = null;
if (specifierExpression != null
&& specifierExpression.getExpression() != null) {
term = Decl.unwrapExpressionsUntilTerm(specifierExpression.getExpression());
HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(term);
if (error != null) {
return List.of(this.makeThrowUnresolvedCompilationError(error));
}
}
if (!isLazy && term instanceof Tree.FunctionArgument) {
// Function specified with lambda: Don't bother generating a
// Callable, just transform the expr to use as the method body.
Tree.FunctionArgument fa = (Tree.FunctionArgument)term;
Type resultType = model.getType();
returnNull = Decl.isUnboxedVoid(model);
final java.util.List lambdaParams = fa.getParameterLists().get(0).getParameters();
final java.util.List defParams = def.getParameterLists().get(0).getParameters();
List substitutions = List.nil();
for (int ii = 0; ii < lambdaParams.size(); ii++) {
substitutions = substitutions.append(naming.addVariableSubst(
(TypedDeclaration)lambdaParams.get(ii).getParameterModel().getModel(),
defParams.get(ii).getParameterModel().getName()));
}
List