All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.ceylon.compiler.java.codegen.ClassTransformer Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * 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.recovery.Drop;
import com.redhat.ceylon.compiler.java.codegen.recovery.Errors;
import com.redhat.ceylon.compiler.java.codegen.recovery.HasErrorException;
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.TypeTags;
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.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();
        
        // 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));
        
        // 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(transformConstructorDeclFlags(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()){
                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);
        
        // 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) {
            MethodDefinitionBuilder initBuilder = classBuilder.addConstructor();
            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(make().TypeArray(make().Type(syms().objectType)), null);
            initBuilder.parameter(pdb);
        } 
        
        // 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(transformClassDeclFlags(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)) {
            buildJpaConstructor((Class)def.getDeclarationModel(), classBuilder);
        }
        
        if (model instanceof Class
                && !(model instanceof ClassAlias)) {
            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)) {
                    addWriteReplace(c, classBuilder);
                }
            }
            serialization(c, classBuilder);
        }
        
        // reset position before initializer constructor is generated. 
        at(def);
        List result;
        if (Decl.isAnnotationClass(def)) {
            ListBuffer trees = ListBuffer.lb();
            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();
        }
        
        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(null, make().Type(syms().objectType));
        mdb.modifiers(PRIVATE | FINAL);
        ListBuffer stmts = ListBuffer.lb();
        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.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()) {
            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();
        ctor.modelAnnotations(makeAtJpa());
        ctor.modelAnnotations(makeAtIgnore());
        ctor.modifiers(PROTECTED);
        for (TypeParameter tp : model.getTypeParameters()) {
            ctor.reifiedTypeParameter(tp);
        }
        
        final ListBuffer stmts = ListBuffer.lb();
        
        // invoke super (or this if
        ListBuffer superArgs = ListBuffer.lb();
        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, 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(transformClassDeclFlags(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(makeJavaType(unrefined.getTypedParameter(formalP).getType()), null);
            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(transformMethodDeclFlags(refined));
            for (TypeParameter tp: formalMethod.getTypeParameters()) {
                mdb.typeParameter(tp);
                mdb.reifiedTypeParameter(tp);
            }
            for (Parameter param : parameterList) {
                mdb.parameter(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,
            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(transformAttributeGetSetDeclFlags(refined, false));
        getterBuilder.getterBlock(make().Block(0, List.of(makeThrowUnresolvedCompilationError(error))));
        classBuilder.attribute(getterBuilder);
        if (formalAttribute.isVariable()) {
            AttributeDefinitionBuilder setterBuilder = AttributeDefinitionBuilder.setter(this, refined.getName(), refined);
            setterBuilder.skipField();
            setterBuilder.modifiers(transformAttributeGetSetDeclFlags(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((transformClassDeclFlags(model) & ~FINAL)| f);
        for (TypeParameter tp : typeParametersOfAllContainers(model, true)) {
            instantiator.typeParameter(tp);
        }
        
        instantiator.resultType(null, makeJavaType(aliasedClass));
        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();
        annoCtor.ignoreModelAnnotations();
        // constructors are never final
        annoCtor.modifiers(transformClassDeclFlags(klass) & ~FINAL);
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(this, "anno");
        pdb.type(makeJavaType(klass.getType(), JT_ANNOTATION), null);
        annoCtor.parameter(pdb);
        
        // It's up to the caller to invoke value() on the Java annotation for a sequenced
        // annotation
        
        ListBuffer args = ListBuffer.lb();
        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 = ListBuffer.lb();
                    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.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(null, makeJavaType(typeFact().getSequentialType(iteratedType)))
                                .parameter(ParameterDefinitionBuilder.systemParameter(this, array.getName())
                                        .type(make().TypeArray(makeJavaType(iteratedType, JT_ANNOTATION)), null))
                                .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 | (transformClassDeclFlags(klass) & ~FINAL)); annoBuilder.getInitBuilder().modifiers(transformClassDeclFlags(klass) & ~FINAL); annoBuilder.annotations(makeAtRetention(RetentionPolicy.RUNTIME)); annoBuilder.annotations(makeAtIgnore()); annoBuilder.annotations(expressionGen().transformAnnotations(OutputElement.ANNOTATION_TYPE, def)); for (Tree.Parameter p : def.getParameterList().getParameters()) { Parameter parameterModel = p.getParameterModel(); annoBuilder.method(makeAnnotationMethod(p)); } List result; if (isSequencedAnnotation(klass)) { result = annoBuilder.annotations(makeAtAnnotationTarget(EnumSet.noneOf(AnnotationTarget.class))).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 | (transformClassDeclFlags(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(null, make().TypeArray(makeJavaType(klass.getType(), JT_ANNOTATION))); mdb.noBody(); ClassDefinitionBuilder sequencedAnnotation = sequencedBuilder.method(mdb); sequencedAnnotation.annotations(transformAnnotationConstraints(klass)); sequencedAnnotation.annotations(makeAtIgnore()); result = result.appendList(sequencedAnnotation.build()); } else { result = annoBuilder.annotations(transformAnnotationConstraints(klass)).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())))); } /** * 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 = ListBuffer.lb(); 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().transformAttributeGetSetDeclFlags(decl.getModel(), false)); adb.userAnnotations(expressionGen().transformAnnotations(OutputElement.GETTER, memberTree)); classBuilder.attribute(adb); if (value.isVariable()) { AttributeDefinitionBuilder setter = AttributeDefinitionBuilder.setter(this, decl.getName(), decl.getModel()); setter.modifiers(classGen().transformAttributeGetSetDeclFlags(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 JCExpression parameterExpr = makeUnquotedIdent(Naming.getAliasedParameterName(decl)); TypedReference typedRef = getTypedReference(value); TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef); Type paramType = nonWideningType(typedRef, nonWideningTypedRef); 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(classGen().transformClassParameterType(decl), parameterExpr); } classBuilder.getInitBuilder().init(make().Exec( make().Assign(naming.makeQualifiedName(naming.makeThis(), value, Naming.NA_IDENT), parameterExpr))); } } } private int transformClassParameterDeclFlags(Parameter param) { return param.getModel().isVariable() ? 0 : FINAL; } private void makeFieldForParameter(ClassDefinitionBuilder classBuilder, Parameter decl, Tree.Declaration annotated) { FunctionOrValue model = decl.getModel(); classBuilder.defs(make().VarDef(make().Modifiers(transformClassParameterDeclFlags(decl) | PRIVATE, makeAtIgnore().prependList(expressionGen().transformAnnotations(OutputElement.FIELD, annotated))), names().fromString(Naming.quoteFieldName(decl.getName())), classGen().transformClassParameterType(decl), null)); classBuilder.getInitBuilder().init(make().Exec(make().Assign( naming.makeQualifiedName(naming.makeThis(), model, Naming.NA_IDENT), naming.makeName(model, Naming.NA_IDENT_PARAMETER_ALIASED)))); } /** * 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.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(type, makeJavaTypeAnnotations(param.getModel())); pdb.modifiers(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 transformClass( Tree.AnyClass def, Class model, ClassDefinitionBuilder classBuilder, Tree.ParameterList paramList, boolean generateInstantiator, ClassDefinitionBuilder instantiatorDeclCb, ClassDefinitionBuilder instantiatorImplCb) { // do reified type params first classBuilder.reifiedTypeParameters(model.getTypeParameters()); 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(), paramModel.getModel().appliedTypedReference(cls.getType(), null), ((TypeDeclaration)cls.getContainer()).getType(), FINAL | (transformClassDeclFlags(cls) & ~ABSTRACT), List.nil(), Collections.>emptyList(), paramModel.getType().getFullType(), Naming.getDefaultedParamMethodName(cls, paramModel), parameters.subList(0, parameters.indexOf(paramModel)), false, Naming.getDefaultedParamMethodName(cls, paramModel), DelegateType.FOR_DEFAULT_VALUE); 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, node, paramList, delegationConstructor); } else { dac = new DefaultedArgumentConstructor(classBuilder.addConstructor(), 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(type, null); 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(); ctor.ignoreModelAnnotations(); ctor.modifiers(PUBLIC); ParameterDefinitionBuilder serializationPdb = ParameterDefinitionBuilder.systemParameter(this, "ignored"); serializationPdb.modifiers(FINAL); serializationPdb.type(make().Type(syms().ceylonSerializationType), null); ctor.parameter(serializationPdb); for (TypeParameter tp : model.getTypeParameters()) { ctor.reifiedTypeParameter(tp); } final ListBuffer stmts = ListBuffer.lb(); if (extendsSerializable(model)) { // invoke super ListBuffer superArgs = ListBuffer.lb(); 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 TypeTags.BYTE: case TypeTags.SHORT: case TypeTags.INT: nullOrZero = make().Literal(0); break; case TypeTags.LONG: nullOrZero = make().Literal(0L); break; case TypeTags.FLOAT: nullOrZero = make().Literal(0.0f); break; case TypeTags.DOUBLE: nullOrZero = make().Literal(0.0); break; case TypeTags.BOOLEAN: nullOrZero = make().Literal(false); break; case TypeTags.CHAR: nullOrZero = make().Literal('\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(null, make().TypeApply(naming.makeQuotedFQIdent("java.util.Collection"), List.of(make().Type(syms().ceylonReachableReferenceType)))); ListBuffer stmts = ListBuffer.lb(); // 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(make().Type(syms().ceylonReachableReferenceType), null); mdb.parameter(pdb); mdb.resultType(null, make().Type(syms().objectType)); /* * 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 = ListBuffer.lb(); 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 = ListBuffer.lb(); 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; if (CodegenUtil.needsLateInitField((Value)member, typeFact())) { test = make().Unary(JCTree.NOT, naming.makeUnquotedIdent(Naming.getInitializationFieldName(member.getName()))); } else { test = make().Binary(JCTree.EQ, naming.makeQualifiedName(naming.makeThis(), (Value)member, Naming.NA_IDENT), makeNull()); } 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 = ListBuffer.lb(); 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 = ListBuffer.lb(); 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(make().Type(syms().ceylonReachableReferenceType), null); mdb.parameter(pdb); ParameterDefinitionBuilder pdb2 = ParameterDefinitionBuilder.systemParameter(this, Unfix.instance.toString()); pdb2.modifiers(FINAL); pdb2.type(make().Type(syms().objectType), null); 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 = ListBuffer.lb(); 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 = ListBuffer.lb(); 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 = ListBuffer.lb(); 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 = ListBuffer.lb(); 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.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(); } /** * Transforms the type of the given class parameter * @param decl * @return */ JCExpression transformClassParameterType(Parameter parameter) { FunctionOrValue decl = parameter.getModel(); if (!(decl.getContainer() instanceof Class)) { throw new BugException("expected parameter of Class"); } JCExpression type; FunctionOrValue attr = decl; if (!Decl.isTransient(attr)) { TypedReference typedRef = getTypedReference(attr); TypedReference nonWideningTypedRef = nonWideningTypeDecl(typedRef); Type paramType = nonWideningType(typedRef, nonWideningTypedRef); type = makeJavaType(nonWideningTypedRef.getDeclaration(), paramType, 0); } else { Type paramType = decl.getType(); type = makeJavaType(decl, paramType, 0); } return type; } 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, DelegateType.OTHER, 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, DelegateType.OTHER, 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, DelegateType.OTHER, 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()); 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); } } } } enum DelegateType { FOR_DEFAULT_VALUE, OTHER; } /** * 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, Collections.emptyList()); 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, typedParameter, model.getType(), PUBLIC | FINAL, typeParameters, producedTypeParameterBounds, typedParameter.getFullType(), Naming.getDefaultedParamMethodName(method, param), parameters.subList(0, parameters.indexOf(param)), param.getModel().getTypeErased(), null, DelegateType.FOR_DEFAULT_VALUE); 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(), PUBLIC | (method.isDefault() ? 0 : FINAL), typeParameters, producedTypeParameterBounds, typedMember.getType(), naming.selector(method), method.getFirstParameterList().getParameters(), ((Function) member).getTypeErased(), null, DelegateType.OTHER); classBuilder.method(concreteMemberDelegate); } if (hasOverloads && (method.isDefault() || method.isShared() && !method.isFormal()) && Decl.equal(method, subMethod)) { final MethodDefinitionBuilder canonicalMethod = makeDelegateToCompanion(iface, typedMember, model.getType(), PRIVATE, subMethod.getTypeParameters(), producedTypeParameterBounds, typedMember.getType(), Naming.selector(method, Naming.NA_CANONICAL_METHOD), method.getFirstParameterList().getParameters(), ((Function) member).getTypeErased(), naming.selector(method), DelegateType.OTHER); 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) { if (member instanceof JavaBeanValue) { setter = ((Value) member).getSetter(); } final MethodDefinitionBuilder getterDelegate = makeDelegateToCompanion(iface, typedMember, model.getType(), PUBLIC | (attr.isDefault() ? 0 : FINAL), Collections.emptyList(), Collections.>emptyList(), typedMember.getType(), Naming.getGetterName(attr), Collections.emptyList(), attr.getTypeErased(), null, DelegateType.OTHER); classBuilder.method(getterDelegate); } if (setter != null) { final MethodDefinitionBuilder setterDelegate = makeDelegateToCompanion(iface, satisfiedType.getTypedMember(setter, null), model.getType(), PUBLIC | (setter.getGetter().isDefault() ? 0 : FINAL), Collections.emptyList(), Collections.>emptyList(), typeFact().getAnythingType(), Naming.getSetterName(attr), Collections.singletonList(setter.getParameter()), setter.getTypeErased(), null, DelegateType.OTHER); 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 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 = PUBLIC; if(includeBody) flags |= FINAL; else flags |= ABSTRACT; 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, typedParameter, 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, DelegateType.FOR_DEFAULT_VALUE, 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, DelegateType.OTHER, includeBody); classBuilder.method(overload); } } final MethodDefinitionBuilder overload = makeDelegateToCompanion(iface, typeMember, currentType, flags, typeParameters, producedTypeParameterBounds(typeMember, klass), typeMember.getType(), instantiatorMethodName, parameters, false, null, DelegateType.OTHER, 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, DelegateType delegateType) { return makeDelegateToCompanion(iface, typedMember, currentType, mods, typeParameters, producedTypeParameterBounds, methodType, methodName, parameters, typeErased, targetMethodName, delegateType, 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, DelegateType delegateType, 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 = 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(null, make().Type(syms().intType)); returnType = typedMember.getType(); }else if (typedMember instanceof TypedReference) { TypedReference typedRef = (TypedReference) typedMember; if(delegateType == DelegateType.OTHER){ // 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{ // for default value NonWideningParam nonWideningParam = concreteWrapper.getNonWideningParam(typedRef, currentType.getDeclaration() instanceof Class ? WideningRules.FOR_MIXIN : WideningRules.NONE); returnType = nonWideningParam.nonWideningType; if(member instanceof Function) returnType = typeFact().getCallableType(returnType); concreteWrapper.resultType(null, makeJavaType(returnType, nonWideningParam.flags)); } } else { concreteWrapper.resultType(null, makeJavaType((Type)typedMember)); returnType = (Type) typedMember; } } ListBuffer arguments = ListBuffer.lb(); if(typeParameters != null){ for(TypeParameter tp : typeParameters){ arguments.add(naming.makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp))); } } if (typedMember.getDeclaration() instanceof Constructor && !Decl.isDefaultConstructor((Constructor)typedMember.getDeclaration())) { concreteWrapper.parameter(makeConstructorNameParameter((Constructor)typedMember.getDeclaration())); arguments.add(naming.makeUnquotedIdent(Unfix.$name$)); } for (Parameter param : parameters) { final TypedReference typedParameter = typedMember.getTypedParameter(param); concreteWrapper.parameter(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 '"+typedMember.getDeclaration().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(); 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 thisMethod.resultType(null, make().Type(syms().objectType)); } else { thisMethod.resultType(null, makeJavaType(satisfiedType, JT_COMPANION)); } 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(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(); // 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; if (!lazy && (concrete || (!Decl.isFormal(decl) && 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; if (decl.getSpecifierOrInitializerExpression() != null) { Value declarationModel = model; initialValue = expressionGen().transformExpression(decl.getSpecifierOrInitializerExpression().getExpression(), CodegenUtil.getBoxingStrategy(declarationModel), nonWideningType); } int flags = 0; if (!CodegenUtil.isUnBoxed(nonWideningTypedRef.getDeclaration())) { flags |= JT_NO_PRIMITIVES; } JCExpression type = makeJavaType(nonWideningType, flags); int modifiers = (useField) ? transformAttributeFieldDeclFlags(decl) : transformLocalDeclFlags(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()) { 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 classBuilder.field(modifiers, attrName, type, initialValue, !useField, annos); if (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) classBuilder.attribute(makeGetter(decl, false, lazy)); } if (withinInterface && lazy) { // Generate getter in companion class classBuilder.getCompanionBuilder((Interface)decl.getDeclarationModel().getContainer()).attribute(makeGetter(decl, true, lazy)); } 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)); } if (withinInterface && lazy) { // Generate setter in companion class classBuilder.getCompanionBuilder((Interface)decl.getDeclarationModel().getContainer()).attribute(makeSetter(decl, true, lazy)); } } } } 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, name, decl.getDeclarationModel().getGetter()) .modifiers(transformAttributeGetSetDeclFlags(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(transformAttributeGetSetDeclFlags(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; } private int transformDeclarationSharedFlags(Declaration decl){ return Decl.isShared(decl) && !Decl.isAncestorLocal(decl) ? PUBLIC : 0; } private int transformClassDeclFlags(ClassOrInterface cdecl) { int result = 0; result |= transformDeclarationSharedFlags(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; return result; } private int transformConstructorDeclFlags(ClassOrInterface cdecl) { return transformDeclarationSharedFlags(cdecl); } int transformConstructorDeclFlags(Constructor ctor) { return Decl.isShared(ctor) && !Decl.isAncestorLocal(ctor) && !ctor.isAbstract() && !Decl.isEnumeratedConstructor(ctor)? PUBLIC : PRIVATE; } private int transformTypeAliasDeclFlags(TypeAlias decl) { int result = 0; result |= transformDeclarationSharedFlags(decl); result |= FINAL; return result; } private int transformMethodDeclFlags(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; } return result; } private int transformAttributeFieldDeclFlags(Tree.AttributeDeclaration cdecl) { int result = 0; result |= Decl.isVariable(cdecl) || Decl.isLate(cdecl) ? 0 : FINAL; result |= PRIVATE; return result; } private int transformLocalDeclFlags(Tree.AttributeDeclaration cdecl) { int result = 0; result |= Decl.isVariable(cdecl) ? 0 : FINAL; 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. */ int transformAttributeGetSetDeclFlags(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; return result; } private int transformObjectDeclFlags(Value cdecl) { int result = 0; result |= FINAL; result |= !Decl.isAncestorLocal(cdecl) && Decl.isShared(cdecl) ? PUBLIC : 0; return result; } 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); JCExpression expr = expressionGen().transformExpression(specOrInit.getExpression(), CodegenUtil.getBoxingStrategy(declarationModel), nonWideningType); 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(transformAttributeGetSetDeclFlags(decl.getDeclarationModel(), forCompanion)) .isFormal((Decl.isFormal(decl) || Decl.withinInterface(decl)) && !forCompanion); } private AttributeDefinitionBuilder makeGetter(Tree.AttributeDeclaration decl, boolean forCompanion, boolean lazy) { at(decl); String attrName = decl.getIdentifier().getText(); AttributeDefinitionBuilder getter = AttributeDefinitionBuilder .getter(this, attrName, decl.getDeclarationModel()); 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) { at(decl); String attrName = decl.getIdentifier().getText(); AttributeDefinitionBuilder setter = AttributeDefinitionBuilder.setter(this, attrName, decl.getDeclarationModel()); 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)); 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)); } 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, ListBuffer.lb()); } 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(null, 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)); } 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 if (Decl.isShared(model)) { 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 = ListBuffer.lb(); Declaration refinedDeclaration = methodModel.getRefinedDeclaration(); final MethodDefinitionBuilder methodBuilder = MethodDefinitionBuilder.method(this, methodModel); // do the reified type param arguments if (gen().supportsReified(methodModel)) { methodBuilder.reifiedTypeParameters(methodModel.getTypeParameters()); } 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); } methodBuilder.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) ? daoAbstract : new DaoThis(node, parameterList); MethodDefinitionBuilder overloadedMethod = new DefaultedArgumentMethod(daoTrans, MethodDefinitionBuilder.method(this, methodModel), methodModel) .makeOverload( parameterList.getModel(), parameter.getParameterModel(), methodModel.getTypeParameters()); 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; if (createCanonical) { // Creates the private "canonical" method containing the actual body MethodDefinitionBuilder canonicalMethod = new CanonicalMethod(daoTransformation, methodModel, body) .makeOverload( parameterList.getModel(), null, methodModel.getTypeParameters()); lb.append(canonicalMethod); } if (transformMethod) { methodBuilder.modifiers(transformMethodDeclFlags(methodModel)); if (actual) { methodBuilder.isOverride(methodModel.isActual()); } if (includeAnnotations) { methodBuilder.userAnnotations(expressionGen().transformAnnotations(OutputElement.METHOD, annotated)); methodBuilder.modelAnnotations(methodModel.getAnnotations()); } 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, methodModel.getTypeParameters()); 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.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; try { statementGen().noExpressionlessReturn = Decl.isMpl(model) || Strategy.useBoxedVoid(model); body = statementGen().transformBlock(block); } finally { statementGen().noExpressionlessReturn = prevNoExpressionlessReturn; } // 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"))); } } return body; } List transformSpecifiedMethodBody(Tree.MethodDeclaration def, SpecifierExpression specifierExpression) { final Function model = def.getDeclarationModel(); List body; 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 = isAnything(resultType) && fa.getExpression().getUnboxed(); 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())); } bodyExpr = gen().expressionGen().transformExpression(fa.getExpression(), returnNull ? BoxingStrategy.INDIFFERENT : CodegenUtil.getBoxingStrategy(model), resultType); for (Substitution subs : substitutions) { subs.close(); } } else if (!isLazy && typeFact().isCallableType(term.getTypeModel())) { returnNull = isAnything(term.getTypeModel()) && term.getUnboxed(); Function method = methodDecl.getDeclarationModel(); boolean lazy = specifierExpression instanceof Tree.LazySpecifierExpression; boolean inlined = CodegenUtil.canOptimiseMethodSpecifier(term, method); Invocation invocation; if ((lazy || inlined) && term instanceof Tree.MemberOrTypeExpression && ((Tree.MemberOrTypeExpression)term).getDeclaration() instanceof Functional) { Declaration primaryDeclaration = ((Tree.MemberOrTypeExpression)term).getDeclaration(); Reference producedReference = ((Tree.MemberOrTypeExpression)term).getTarget(); invocation = new MethodReferenceSpecifierInvocation( this, (Tree.MemberOrTypeExpression)term, primaryDeclaration, producedReference, method, specifierExpression); } else if (!lazy && !inlined) { // must be a callable we stored String name = naming.getMethodSpecifierAttributeName(method); invocation = new CallableSpecifierInvocation( this, method, naming.makeUnquotedIdent(name), term, term); } else if (isCeylonCallableSubtype(term.getTypeModel())) { invocation = new CallableSpecifierInvocation( this, method, expressionGen().transformExpression(term), term, term); } else { throw new BugException(term, "unhandled primary: " + term == null ? "null" : term.getNodeType()); } invocation.handleBoxing(true); invocation.setErased(CodegenUtil.hasTypeErased(term) || getReturnTypeOfCallable(term.getTypeModel()).isNothing()); bodyExpr = expressionGen().transformInvocation(invocation); } else { bodyExpr = expressionGen().transformExpression(model, term); // The innermost of an MPL method declared void needs to return null returnNull = Decl.isUnboxedVoid(model) && Decl.isMpl(model); } if (!Decl.isUnboxedVoid(model) || Decl.isMpl(model) || Strategy.useBoxedVoid(model)) { if (returnNull) { body = List.of(make().Exec(bodyExpr), make().Return(makeNull())); } else { body = List.of(make().Return(bodyExpr)); } } else { body = List.of(make().Exec(bodyExpr)); } return body; } private boolean isVoid(Tree.Declaration def) { return isVoid(def.getDeclarationModel()); } private boolean isVoid(Declaration def) { if (def instanceof Function) { return gen().isAnything(((Function)def).getType()); } else if (def instanceof Class || def instanceof Constructor) { // Consider classes void since ctors don't require a return statement return true; } throw BugException.unhandledDeclarationCase(def); } /** * Generate a body for the overload method which delegates to the * canonical method using a let to spubstitute defaulted parameters */ private static int OL_BODY_DELEGATE_CANONICAL = 1<<0; /** * Modifies OL_BODY_DELEGATE_CANONICAL so that the method is suitable * for a companion class. */ private static int OL_COMPANION = 1<<1; /** * Modifies OL_BODY_DELEGATE_CANONICAL so that the canonical method on the * interface instantce, {@code $this}, is called, rather than the * canonical method on {@code this}. */ private static int OL_DELEGATE_INTERFACE_INSTANCE = 1<<2; /** * Abstraction over possible transformations for the body of an overloaded * declaration which supplies a defaulted argument. * @see DefaultedArgumentOverload */ abstract class DaoBody { protected List makeTypeArguments(DefaultedArgumentOverload ol) { if (ol.defaultParameterMethodOnSelf() || ol.defaultParameterMethodOnOuter()) { return List.nil(); } else if (ol.defaultParameterMethodStatic()){ Functional f = (Functional)ol.getModel(); return typeArguments(f instanceof Constructor ? (Class)((Constructor)f).getContainer() : f); } else { return List.nil(); } } abstract void makeBody(DefaultedArgumentOverload overloaded, MethodDefinitionBuilder overloadBuilder, ParameterList parameterList, Parameter currentParameter, java.util.List typeParameterList); JCExpression makeMethodNameQualifier() { return null; } } /** * a body-less (i.e. abstract) transformation. */ private class DaoAbstract extends DaoBody { @Override public void makeBody(DefaultedArgumentOverload overloaded, MethodDefinitionBuilder overloadBuilder, ParameterList parameterList, Parameter currentParameter, java.util.List typeParameterList) { overloadBuilder.noBody(); } }; final DaoAbstract daoAbstract = new DaoAbstract(); /** * a transformation for an overloaded * default parameter body which delegates to the "canonical" * method/constructor using a {@code let} expresssion * to substitute defaulted arguments */ private class DaoThis extends DaoBody { private final Tree.Declaration declTree; private final Token firstExecutable; private final Tree.ParameterList pl; public DaoThis(Tree.Declaration invocation, Tree.ParameterList pl) { this.declTree = invocation; this.firstExecutable = pl != null ? pl.getEndToken() : null; this.pl = pl; } @Override public void makeBody(DefaultedArgumentOverload overloaded, MethodDefinitionBuilder overloadBuilder, ParameterList parameterList, Parameter currentParameter, java.util.List typeParameterList) { ListBuffer delegateArgs = ListBuffer.lb(); overloaded.appendImplicitArgumentsDelegate(typeParameterList, delegateArgs); ListBuffer dpmArgs = ListBuffer.lb(); overloaded.appendImplicitArgumentsDpm(typeParameterList, dpmArgs); ListBuffer vars = ListBuffer.lb(); boolean initedVars = false; boolean useDefault = false; for (Parameter parameterModel : parameterList.getParameters()) { if (currentParameter != null && Decl.equal(parameterModel, currentParameter)) { useDefault = true; } if (useDefault) { at(parameterLocation(parameterModel)); JCExpression defaultArgument; if (Strategy.hasDefaultParameterValueMethod(parameterModel)) { if (!initedVars) { // Only call init vars if we actually invoke a defaulted param method overloaded.initVars(currentParameter, vars); } initedVars = true; JCExpression defaultValueMethodName = naming.makeDefaultedParamMethod(overloaded.makeDefaultArgumentValueMethodQualifier(), parameterModel); defaultArgument = make().Apply(makeTypeArguments(overloaded), defaultValueMethodName, ListBuffer.lb().appendList(dpmArgs).toList()); } else if (Strategy.hasEmptyDefaultArgument(parameterModel)) { defaultArgument = makeEmptyAsSequential(true); } else { defaultArgument = makeErroneous(null, "compiler bug: parameter " + parameterModel.getName() + " has an unsupported default value"); } Naming.SyntheticName varName = naming.temp(parameterModel.getName()); Type paramType = overloaded.parameterType(parameterModel); vars.append(makeVar(varName, makeJavaType(paramType, CodegenUtil.isUnBoxed(parameterModel.getModel()) ? 0 : JT_NO_PRIMITIVES), defaultArgument)); at(null); delegateArgs.add(varName.makeIdent()); dpmArgs.add(varName.makeIdent()); } else { delegateArgs.add(naming.makeName(parameterModel.getModel(), Naming.NA_MEMBER | Naming.NA_ALIASED)); dpmArgs.add(naming.makeName(parameterModel.getModel(), Naming.NA_MEMBER | Naming.NA_ALIASED)); } } makeBody(overloaded, overloadBuilder, delegateArgs, vars); } private Node parameterLocation(Parameter parameterModel) { if (this.pl != null) { for (Tree.Parameter p : pl.getParameters()) { if (p.getParameterModel().equals(parameterModel)) { return p; } } } return null; } protected final void makeBody(DefaultedArgumentOverload overloaded, MethodDefinitionBuilder overloadBuilder, ListBuffer args, ListBuffer vars) { at(pl, firstExecutable); JCExpression invocation = overloaded.makeInvocation(args); Declaration model = overloaded.getModel();// TODO Yuk if (!isVoid(model) // MPL overloads always return a Callable || (model instanceof Functional && Decl.isMpl((Functional) model)) || (model instanceof Function && !(Decl.isUnboxedVoid(model))) || (model instanceof Function && Strategy.useBoxedVoid((Function)model)) || Strategy.generateInstantiator(model) && overloaded instanceof DefaultedArgumentInstantiator) { if (!vars.isEmpty()) { invocation = at(declTree).LetExpr(vars.toList(), invocation); } overloadBuilder.body(make().Return(invocation)); } else { vars.append(at(pl, firstExecutable).Exec(invocation)); invocation = at(declTree).LetExpr(vars.toList(), makeNull()); overloadBuilder.body(make().Exec(invocation)); } } } /** * specialises {@link DaoThis} for transforming declarations for companion classes */ private class DaoCompanion extends DaoThis { DaoCompanion(Tree.AnyMethod invocation, Tree.ParameterList pl) { super(invocation, pl); } @Override protected final List makeTypeArguments(DefaultedArgumentOverload ol) { return List.nil(); } } /** * a transformation for an overloaded * default parameter method body which delegates * to the super class. This is used when we need to refine the return * type of a DPM. */ private class DaoSuper extends DaoBody { JCExpression makeMethodNameQualifier() { return naming.makeSuper(); } @Override void makeBody( DefaultedArgumentOverload overloaded, MethodDefinitionBuilder overloadBuilder, ParameterList parameterList, Parameter currentParameter, java.util.List typeParameterList) { ListBuffer args = ListBuffer.lb(); for (Parameter parameter : parameterList.getParameters()) { if (currentParameter != null && Decl.equal(parameter, currentParameter)) { break; } args.add(naming.makeUnquotedIdent(parameter.getName())); } JCExpression superCall = overloaded.makeInvocation(args); /*JCMethodInvocation superCall = make().Apply(null, naming.makeQualIdent(naming.makeSuper(), ((Function)overloaded.getModel()).getName()), args.toList());*/ JCExpression refinedType = makeJavaType(((Function)overloaded.getModel()).getType(), JT_NO_PRIMITIVES); overloadBuilder.body(make().Return(make().TypeCast(refinedType, superCall))); } } /** * A base class for transformations used for Ceylon declarations * which have defaulted parameters. We generate an overloaded * method/constructor whose implementation which supplies the default * argument and delegates to the "canonical" method. * * Subclasses specialise for the different kinds of declaration, and * a separate set of classes handle the various transformations for the * body of an overloaded declaration (see {@link DaoBody}) */ abstract class DefaultedArgumentOverload { protected final DaoBody daoBody; protected final MethodDefinitionBuilder overloadBuilder; /** * @param daoBody for the body, or null if no body required */ protected DefaultedArgumentOverload(DaoBody daoBody, MethodDefinitionBuilder overloadBuilder){ this.daoBody = daoBody; this.overloadBuilder = overloadBuilder; } protected abstract long getModifiers(); protected abstract JCExpression makeMethodName(); protected abstract void resultType(); protected abstract void typeParameters(); protected void parameters(ParameterList parameterList, Parameter currentParameter) { for (Parameter parameter : parameterList.getParameters()) { if (currentParameter != null && Decl.equal(parameter, currentParameter)) { break; } overloadBuilder.parameter(parameter, null, 0, getWideningRules(parameter)); } } protected void appendImplicitParameters(java.util.List typeParameterList) { if(typeParameterList != null){ overloadBuilder.reifiedTypeParameters(typeParameterList); } } protected abstract void appendImplicitArgumentsDelegate(java.util.List typeParameterList, ListBuffer args); protected abstract void appendImplicitArgumentsDpm(java.util.List typeParameterList, ListBuffer args); protected Type parameterType(Parameter parameterModel) { NonWideningParam nonWideningParam = overloadBuilder.getNonWideningParam(parameterModel.getModel(), getWideningRules(parameterModel)); Type paramType = nonWideningParam.nonWideningType; if (parameterModel.getModel() instanceof Function) paramType = typeFact().getCallableType(paramType); return paramType; } private WideningRules getWideningRules(Parameter parameterModel) { // static methods don't have to refine anything from the supertype so they're not bound to // widening issues return defaultParameterMethodStatic() ? WideningRules.NONE : WideningRules.CAN_WIDEN; } protected abstract void initVars(Parameter currentParameter, ListBuffer vars); protected final boolean defaultParameterMethodOnSelf() { return Strategy.defaultParameterMethodOnSelf(getModel()); } protected final boolean defaultParameterMethodOnOuter() { return Strategy.defaultParameterMethodOnOuter(getModel()); } protected final boolean defaultParameterMethodStatic() { return Strategy.defaultParameterMethodStatic(getModel()); } protected abstract Declaration getModel(); protected JCExpression makeInvocation(ListBuffer args) { final JCExpression methName = makeMethodName(); return make().Apply(List.nil(), methName, args.toList()); } /** Returns the qualiifier to use when invoking the default parameter value method */ protected abstract JCIdent makeDefaultArgumentValueMethodQualifier(); /** * Generates an overloaded method or constructor. */ public MethodDefinitionBuilder makeOverload ( ParameterList parameterList, Parameter currentParameter, java.util.List typeParameterList) { // Make the declaration // need annotations for BC, but the method isn't really there overloadBuilder.ignoreModelAnnotations(); overloadBuilder.modifiers(getModifiers()); resultType(); typeParameters(); appendImplicitParameters(typeParameterList); parameters(parameterList, currentParameter); // Make the body, but only if we want one. null means we want a formal method // TODO MPL // TODO Type args on method call if(daoBody != null){ daoBody.makeBody(this, overloadBuilder, parameterList, currentParameter, typeParameterList); } return overloadBuilder; } } /** * A transformation for generating overloaded methods for * defaulted arguments. */ class DefaultedArgumentMethod extends DefaultedArgumentOverload { private final Function method; DefaultedArgumentMethod(DaoBody daoBody, MethodDefinitionBuilder mdb, Function method) { super(daoBody, mdb); this.method = method; } @Override protected Function getModel() { return method; } @Override protected long getModifiers() { long mods = transformMethodDeclFlags(method); if (daoBody instanceof DaoAbstract == false) { mods &= ~ABSTRACT; } if (daoBody instanceof DaoCompanion) { mods |= FINAL; } return mods; } @Override protected final JCExpression makeMethodName() { int flags = Naming.NA_MEMBER; if (Decl.withinClassOrInterface(method)) { flags |= Naming.NA_CANONICAL_METHOD; } return naming.makeQualifiedName(daoBody.makeMethodNameQualifier(), method, flags); } @Override protected void resultType() { overloadBuilder.resultType(method, 0); } @Override protected void typeParameters() { copyTypeParameters(method, overloadBuilder); } @Override protected void appendImplicitArgumentsDelegate(java.util.List typeParameterList, ListBuffer args) { if(typeParameterList != null){ // we pass the reified type parameters along for(TypeParameter tp : typeParameterList){ args.append(makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp))); } } } @Override protected void appendImplicitArgumentsDpm(java.util.List typeParameterList, ListBuffer args) { appendImplicitArgumentsDelegate(typeParameterList, args); } @Override protected void initVars(Parameter currentParameter, ListBuffer vars) { } @Override protected JCIdent makeDefaultArgumentValueMethodQualifier() { return null; } } /** * A transformation for generating overloaded methods for * defaulted arguments. */ class DefaultedArgumentMethodTyped extends DefaultedArgumentMethod { private TypedReference typedMember; private boolean forMixin; DefaultedArgumentMethodTyped(DaoBody daoBody, MethodDefinitionBuilder mdb, TypedReference typedMember, boolean forMixin) { super(daoBody, mdb, (Function)typedMember.getDeclaration()); this.typedMember = typedMember; this.forMixin = forMixin; } @Override protected void resultType() { if (!isAnything(getModel().getType()) || !Decl.isUnboxedVoid(getModel()) || Strategy.useBoxedVoid((Function)getModel())) { TypedReference typedRef = (TypedReference) typedMember; overloadBuilder.resultTypeNonWidening(typedMember.getQualifyingType(), typedRef, typedMember.getType(), 0); } else { super.resultType(); } } @Override protected void parameters(ParameterList parameterList, Parameter currentParameter) { for (Parameter param : parameterList.getParameters()) { if (currentParameter != null && Decl.equal(param, currentParameter)) { break; } final TypedReference typedParameter = typedMember.getTypedParameter(param); overloadBuilder.parameter(param, typedParameter, null, 0, forMixin ? WideningRules.FOR_MIXIN : WideningRules.CAN_WIDEN); } } @Override protected Type parameterType(Parameter parameter) { final TypedReference typedParameter = typedMember.getTypedParameter(parameter); NonWideningParam nonWideningParam = overloadBuilder.getNonWideningParam(typedParameter, forMixin ? WideningRules.FOR_MIXIN : WideningRules.CAN_WIDEN); Type paramType = nonWideningParam.nonWideningType; if (parameter.getModel() instanceof Function) { paramType = typeFact().getCallableType(paramType); } return paramType; } @Override public MethodDefinitionBuilder makeOverload( ParameterList parameterList, Parameter currentParameter, java.util.List typeParameterList) { overloadBuilder.isOverride(true); return super.makeOverload(parameterList, currentParameter, typeParameterList); } } /** * A transformation for generating the canonical method used by the * defaulted argument overload methods */ class CanonicalMethod extends DefaultedArgumentMethod { private List body; private boolean useBody; CanonicalMethod(DaoBody daoBody, MethodDefinitionBuilder mdb, Function method) { super(daoBody, mdb, method); useBody = false; } CanonicalMethod(DaoBody daoBody, Function method, List body) { super(daoBody, MethodDefinitionBuilder.method(ClassTransformer.this, method, Naming.NA_CANONICAL_METHOD), method); this.body = body; useBody = true; } @Override protected long getModifiers() { long mods = super.getModifiers(); if (useBody) { mods = mods & ~PUBLIC & ~FINAL | PRIVATE; } return mods; } /** * Generates a canonical method */ @Override public MethodDefinitionBuilder makeOverload ( ParameterList parameterList, Parameter currentParameter, java.util.List typeParameterList) { if (!useBody) { daoBody.makeBody(this, overloadBuilder, parameterList, currentParameter, typeParameterList); } else { // Make the declaration // need annotations for BC, but the method isn't really there overloadBuilder.ignoreModelAnnotations(); overloadBuilder.modifiers(getModifiers()); resultType(); typeParameters(); appendImplicitParameters(typeParameterList); parameters(parameterList, currentParameter); if (body != null) { // Construct the outermost method using the body we've built so far overloadBuilder.body(body); } else { overloadBuilder.noBody(); } } return overloadBuilder; } } /** * A base class for transformations that generate overloaded declarations for * defaulted arguments. */ abstract class DefaultedArgumentClass extends DefaultedArgumentOverload { protected final Class klass; protected final Constructor constructor; protected Naming.SyntheticName companionInstanceName = null; private final boolean delegationConstructor; DefaultedArgumentClass(DaoBody daoBody, MethodDefinitionBuilder mdb, Class klass, Constructor constructor, boolean delegationConstructor) { super(daoBody, mdb); this.klass = klass; this.constructor = constructor; this.delegationConstructor = delegationConstructor; } @Override protected final Declaration getModel() { return constructor != null ? constructor : klass; } @Override protected void appendImplicitParameters(java.util.List typeParameterList) { super.appendImplicitParameters(typeParameterList); if (constructor != null) { if (delegationConstructor) { overloadBuilder.parameter(makeConstructorNameParameter(constructor, DeclNameFlag.QUALIFIED, DeclNameFlag.DELEGATION)); } else if (!Decl.isDefaultConstructor(constructor)){ overloadBuilder.parameter(makeConstructorNameParameter(constructor)); } } } @Override protected void appendImplicitArgumentsDelegate(java.util.List typeParameterList, ListBuffer args) { appendImplicitArgumentsDpm(typeParameterList, args); if (constructor != null && !Decl.isDefaultConstructor(constructor)) { args.append(naming.makeUnquotedIdent(Unfix.$name$)); } } @Override protected void appendImplicitArgumentsDpm(java.util.List typeParameterList, ListBuffer args) { if(typeParameterList != null){ // we pass the reified type parameters along for(TypeParameter tp : typeParameterList){ args.append(makeUnquotedIdent(naming.getTypeArgumentDescriptorName(tp))); } } } @Override protected void initVars(Parameter currentParameter, ListBuffer vars) { if (!Strategy.defaultParameterMethodStatic(klass) && !Strategy.defaultParameterMethodOnOuter(klass) && currentParameter != null) { companionInstanceName = naming.temp("impl"); vars.append(makeVar(companionInstanceName, makeJavaType(klass.getType(), AbstractTransformer.JT_COMPANION), make().NewClass(null, null, makeJavaType(klass.getType(), AbstractTransformer.JT_CLASS_NEW | AbstractTransformer.JT_COMPANION), List.nil(), null))); } } @Override protected JCIdent makeDefaultArgumentValueMethodQualifier() { if (defaultParameterMethodOnOuter()){ // if we're refining a class we can't declare new default values, so we should get // them from the instance rather than the outer interface impl if(getModel().isActual() && getModel().getContainer() instanceof Interface) return naming.makeUnquotedIdent("$this"); return null; }else if (defaultParameterMethodOnSelf() || daoBody instanceof DaoCompanion) { return null; } else if (defaultParameterMethodStatic()){ return null; } else { return companionInstanceName.makeIdent(); } } } /** * A transformation for generating overloaded constructors for * defaulted arguments. */ class DefaultedArgumentConstructor extends DefaultedArgumentClass { DefaultedArgumentConstructor(MethodDefinitionBuilder mdb, Class klass, Tree.Declaration node, Tree.ParameterList pl, boolean delegationConstructor) { super(new DaoThis(node, pl), mdb, klass, null, delegationConstructor); } DefaultedArgumentConstructor(MethodDefinitionBuilder mdb, Constructor constructor, Tree.Declaration node, Tree.ParameterList pl, boolean delegationConstructor) { super(new DaoThis(node, pl), mdb, (Class)constructor.getContainer(), constructor, delegationConstructor); } @Override protected long getModifiers() { return transformConstructorDeclFlags(klass) & (PUBLIC | PRIVATE | PROTECTED); } @Override protected JCExpression makeMethodName() { return naming.makeQualifiedThis(daoBody.makeMethodNameQualifier()); } @Override protected void resultType() { // Constructor has no result type } @Override protected void typeParameters() { // Constructor has type parameters } } /** * A transformation for generating overloaded instantiator methods for * defaulted arguments. */ class DefaultedArgumentInstantiator extends DefaultedArgumentClass { private boolean forCompanionClass; DefaultedArgumentInstantiator(DaoBody daoBody, Class klass, Constructor ctor, boolean forCompanionClass) { super(daoBody, MethodDefinitionBuilder.systemMethod(ClassTransformer.this, naming.getInstantiatorMethodName(klass)), klass, ctor, false); this.forCompanionClass = forCompanionClass; } @Override protected long getModifiers() { // 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 = transformClassDeclFlags(klass) & ~FINAL; // when refining a member class of an interface because the // instantiator method is declared inb 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; } @Override protected JCExpression makeMethodName() { return naming.makeInstantiatorMethodName(daoBody.makeMethodNameQualifier(), klass); } @Override protected void resultType() { /* Not actually part of the return type */ overloadBuilder.ignoreModelAnnotations(); Type et = klass.getExtendedType(); if (!forCompanionClass && !klass.isAlias() && Strategy.generateInstantiator(et.getDeclaration()) && klass.isActual()) { overloadBuilder.isOverride(true); } /**/ JCExpression resultType; Type type = klass.isAlias() ? klass.getExtendedType() : klass.getType(); if (Strategy.isInstantiatorUntyped(klass)) { // We can't expose a local type name to a place it's not visible resultType = make().Type(syms().objectType); } else { resultType = makeJavaType(type); } overloadBuilder.resultType(null, resultType); } @Override protected void typeParameters() { for (TypeParameter tp : typeParametersForInstantiator(klass)) { overloadBuilder.typeParameter(tp); } } @Override protected void appendImplicitArgumentsDelegate(java.util.List typeParameterList, ListBuffer args) { Type type = klass.isAlias() ? klass.getExtendedType() : klass.getType(); type = type.resolveAliases(); // fetch the type parameters from the klass we're instantiating itself if any for(Type pt : type.getTypeArgumentList()){ args.append(makeReifiedTypeArgument(pt)); } if (constructor != null && !Decl.isDefaultConstructor(constructor)) { args.append(naming.makeUnquotedIdent(Unfix.$name$)); } } @Override protected JCExpression makeInvocation(ListBuffer args) { Type type = klass.isAlias() ? klass.getExtendedType() : klass.getType(); return make().NewClass(null, null, makeJavaType(type, JT_CLASS_NEW | JT_NON_QUALIFIED), args.toList(), null); } } /** * When generating an instantiator method if the inner class has a type * parameter with the same name as a type parameter of an outer type, then the * instantiator method shouldn't declare its own type parameter of that * name -- it should use the captured one. This method filters out the * type parameters of the inner class which are the same as type parameters * of the outer class so that they can be captured. */ private java.util.List typeParametersForInstantiator(final Class model) { java.util.List filtered = new ArrayList(); java.util.List tps = model.getTypeParameters(); if (tps != null) { for (TypeParameter tp : tps) { boolean omit = false; Scope s = model.getContainer(); while (!(s instanceof Package)) { if (s instanceof Generic) { for (TypeParameter outerTp : ((Generic)s).getTypeParameters()) { if (tp.getName().equals(outerTp.getName())) { omit = true; } } } s = s.getContainer(); } if (!omit) { filtered.add(tp); } } } return filtered; } private java.util.List typeParametersOfAllContainers(final ClassOrInterface model, boolean includeModelTypeParameters) { java.util.List> r = new ArrayList>(1); Scope s = model.getContainer(); while (!(s instanceof Package)) { if (s instanceof Generic) { r.add(0, ((Generic)s).getTypeParameters()); } s = s.getContainer(); } Set names = new HashSet(); for(TypeParameter tp : model.getTypeParameters()){ names.add(tp.getName()); } java.util.List result = new ArrayList(1); for (java.util.List tps : r) { for(TypeParameter tp : tps){ if(names.add(tp.getName())){ result.add(tp); } } } if(includeModelTypeParameters) result.addAll(model.getTypeParameters()); return result; } /** * Creates a (possibly abstract) method for retrieving the value for a * defaulted parameter * @param typeParameterList */ MethodDefinitionBuilder makeParamDefaultValueMethod(boolean noBody, Declaration container, Tree.ParameterList params, Tree.Parameter currentParam) { at(currentParam); Parameter parameter = currentParam.getParameterModel(); if (!Strategy.hasDefaultParameterValueMethod(parameter)) { throw new BugException(); } MethodDefinitionBuilder methodBuilder = MethodDefinitionBuilder.systemMethod(this, Naming.getDefaultedParamMethodName(container, parameter)); methodBuilder.ignoreModelAnnotations(); if (container != null && Decl.isAnnotationConstructor(container)) { AnnotationInvocation ac = (AnnotationInvocation)((Function)container).getAnnotationConstructor(); for (AnnotationConstructorParameter acp : ac.getConstructorParameters()) { if (acp.getParameter().equals(parameter) && acp.getDefaultArgument() != null) { methodBuilder.userAnnotations(acp.getDefaultArgument().makeDpmAnnotations(expressionGen())); } } } 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 = 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; } methodBuilder.modifiers(modifiers); if (container instanceof Constructor) { copyTypeParameters((Class)container.getContainer(), methodBuilder); methodBuilder.reifiedTypeParameters(((Class)container.getContainer()).getTypeParameters()); } else if(container instanceof Generic) { // make sure reified type parameters are accepted copyTypeParameters((Generic)container, methodBuilder); methodBuilder.reifiedTypeParameters(((Generic)container).getTypeParameters()); } WideningRules wideningRules = !staticMethod && container instanceof Class ? WideningRules.CAN_WIDEN : WideningRules.NONE; // Add any of the preceding parameters as parameters to the method for (Tree.Parameter p : params.getParameters()) { if (p.equals(currentParam)) { break; } at(p); methodBuilder.parameter(p.getParameterModel(), null, 0, wideningRules); } // The method's return type is the same as the parameter's type NonWideningParam nonWideningParam = methodBuilder.getNonWideningParam(currentParam.getParameterModel().getModel(), wideningRules); methodBuilder.resultType(nonWideningParam.nonWideningDecl, nonWideningParam.nonWideningType, nonWideningParam.flags); // The implementation of the method if (noBody) { methodBuilder.noBody(); } else { HasErrorException error = errors().getFirstExpressionErrorAndMarkBrokenness(Decl.getDefaultArgument(currentParam).getExpression()); if (error != null) { methodBuilder.body(this.makeThrowUnresolvedCompilationError(error)); } else { java.util.List copiedTypeParameters = null; if(container instanceof Generic) { copiedTypeParameters = ((Generic) container).getTypeParameters(); if(copiedTypeParameters != null) addTypeParameterSubstitution(copiedTypeParameters); } try{ JCExpression expr = expressionGen().transform(currentParam); JCBlock body = at(currentParam).Block(0, List. of(at(currentParam).Return(expr))); methodBuilder.block(body); }finally{ if(copiedTypeParameters != null) popTypeParameterSubstitution(); } } } return methodBuilder; } public List transformObjectDefinition(Tree.ObjectDefinition def, ClassDefinitionBuilder containingClassBuilder) { return transformObject(def, def, def.getSatisfiedTypes(), def.getDeclarationModel(), def.getAnonymousClass(), containingClassBuilder, Decl.isLocalNotInitializer(def)); } public List transformObjectArgument(Tree.ObjectArgument def) { return transformObject(def, null, def.getSatisfiedTypes(), def.getDeclarationModel(), def.getAnonymousClass(), null, false); } public List transformObjectExpression(Tree.ObjectExpression def) { return transformObject(def, null, def.getSatisfiedTypes(), null, def.getAnonymousClass(), null, false); } private List transformObject(Node def, Tree.Declaration annotated, Tree.SatisfiedTypes satisfiesTypes, Value model, Class klass, ClassDefinitionBuilder containingClassBuilder, boolean makeLocalInstance) { naming.clearSubstitutions(klass); String name = klass.getName(); String javaClassName = Naming.quoteClassName(name); ClassDefinitionBuilder objectClassBuilder = ClassDefinitionBuilder.object( this, javaClassName, name, Decl.isLocal(klass)).forDefinition(klass); if (Strategy.introduceJavaIoSerializable(klass, typeFact().getJavaIoSerializable())) { objectClassBuilder.introduce(make().QualIdent(syms().serializableType.tsym)); if (def instanceof Tree.ObjectDefinition && klass.isMember() && (klass.isShared() || klass.isCaptured() || model.isCaptured())) { addWriteReplace(klass, objectClassBuilder); } } makeReadResolve(objectClassBuilder, klass, model); // Make sure top types satisfy reified type addReifiedTypeInterface(objectClassBuilder, klass); if(supportsReifiedAlias(klass)) objectClassBuilder.reifiedAlias(klass.getType()); CeylonVisitor visitor = gen().visitor; final ListBuffer prevDefs = visitor.defs; final boolean prevInInitializer = visitor.inInitializer; final ClassDefinitionBuilder prevClassBuilder = visitor.classBuilder; List childDefs; try { visitor.defs = new ListBuffer(); visitor.inInitializer = true; visitor.classBuilder = objectClassBuilder; def.visitChildren(visitor); childDefs = (List)visitor.getResult().toList(); } finally { visitor.classBuilder = prevClassBuilder; visitor.inInitializer = prevInInitializer; visitor.defs = prevDefs; } addMissingUnrefinedMembers(def, klass, objectClassBuilder); satisfaction(satisfiesTypes, klass, objectClassBuilder); serialization(klass, objectClassBuilder); if (model != null && Decl.isToplevel(model) && def instanceof Tree.ObjectDefinition) { // generate a field and getter AttributeDefinitionBuilder builder = AttributeDefinitionBuilder // TODO attr build take a JCExpression className .wrapped(this, null, objectClassBuilder, model.getName(), model, true) .userAnnotations(makeAtIgnore()) .userAnnotationsSetter(makeAtIgnore()) .immutable() .initialValue(makeNewClass(naming.makeName(model, Naming.NA_FQ | Naming.NA_WRAPPER))) .is(PUBLIC, Decl.isShared(klass)) .is(STATIC, true); if (annotated != null) { builder.fieldAnnotations(expressionGen().transformAnnotations(OutputElement.FIELD, annotated)); builder.userAnnotations(expressionGen().transformAnnotations(OutputElement.GETTER, annotated)); } objectClassBuilder.defs(builder.build()); } if (annotated != null) { objectClassBuilder.annotations(expressionGen().transformAnnotations(OutputElement.TYPE, annotated)); objectClassBuilder.getInitBuilder().userAnnotations(expressionGen().transformAnnotations(OutputElement.CONSTRUCTOR, annotated)); } // make sure we set the container in case we move it out addAtContainer(objectClassBuilder, klass); objectClassBuilder .annotations(makeAtObject()) .satisfies(klass.getSatisfiedTypes()) .defs((List)childDefs); objectClassBuilder.getInitBuilder().modifiers(PRIVATE); objectClassBuilder.addGetTypeMethod(klass.getType()); if(model != null) objectClassBuilder .modelAnnotations(model.getAnnotations()) .modifiers(transformObjectDeclFlags(model)); List result = objectClassBuilder.build(); if (makeLocalInstance) { if(model.isSelfCaptured()){ // if it's captured we need to box it and define the var before the class, so it can access it JCNewClass newInstance = makeNewClass(objectClassBuilder.getClassName(), false, null); JCFieldAccess setter = naming.makeSelect(Naming.getLocalValueName(model), Naming.getSetterName(model)); JCStatement assign = make().Exec(make().Assign(setter, newInstance)); result = result.prepend(assign); JCVariableDecl localDecl = makeVariableBoxDecl(null, model); result = result.prepend(localDecl); }else{ // not captured, we can define the var after the class JCVariableDecl localDecl = makeLocalIdentityInstance(name, objectClassBuilder.getClassName(), false); result = result.append(localDecl); } } else if (model != null && Decl.withinClassOrInterface(model)) { boolean generateGetter = Decl.isCaptured(model); JCExpression type = makeJavaType(klass.getType()); if (generateGetter) { int modifiers = TRANSIENT | PRIVATE; JCExpression initialValue = makeNull(); containingClassBuilder.field(modifiers, name, type, initialValue, false); AttributeDefinitionBuilder getter = AttributeDefinitionBuilder .getter(this, name, model) .modifiers(transformAttributeGetSetDeclFlags(model, false)); if (def instanceof Tree.ObjectDefinition) { getter.userAnnotations(expressionGen().transformAnnotations(OutputElement.GETTER, ((Tree.ObjectDefinition)def))); } ListBuffer stmts = ListBuffer.lb(); stmts.add(make().If(make().Binary(JCTree.EQ, naming.makeUnquotedIdent(Naming.quoteFieldName(name)), makeNull()), make().Exec(make().Assign( naming.makeUnquotedIdent(Naming.quoteFieldName(name)), makeNewClass(makeJavaType(klass.getType()), null))), null)); stmts.add(make().Return(naming.makeUnquotedIdent(Naming.quoteFieldName(name)))); getter.getterBlock(make().Block(0, stmts.toList())); result = result.appendList(getter.build()); } else { int modifiers = FINAL; JCExpression initialValue = makeNewClass(makeJavaType(klass.getType()), null); containingClassBuilder.field(modifiers, name, type, initialValue, true); } } return result; } private void makeReadResolve(ClassDefinitionBuilder objectClassBuilder, Class cls, Value model) { if (Strategy.addReadResolve(cls)) { MethodDefinitionBuilder readResolve = MethodDefinitionBuilder.systemMethod(this, "readResolve"); readResolve.modifiers(PRIVATE); readResolve.resultType(null, make().Type(syms().objectType)); JCExpression apply; if (cls.isToplevel()) { apply = make().Apply(null, naming.makeQualifiedName(naming.makeTypeDeclarationExpression(null, cls, DeclNameFlag.QUALIFIED), model, Naming.NA_MEMBER | Naming.NA_GETTER), List.nil()); } else { apply = makeNull(); } readResolve.body(make().Return(apply)); objectClassBuilder.method(readResolve); } } /** * Makes a {@code main()} method which calls the given top-level method * @param def */ private MethodDefinitionBuilder makeMainForClass(ClassOrInterface model) { at(null); List arguments = List.nil(); if(model.isAlias()){ TypeDeclaration constr = ((ClassAlias)model).getConstructor(); if(constr instanceof Constructor){ // must pass the constructor name arg arguments = List.of(naming.makeNamedConstructorName((Constructor) constr, false)); } model = (ClassOrInterface) model.getExtendedType().getDeclaration(); } JCExpression nameId = makeJavaType(model.getType(), JT_RAW); arguments = makeBottomReifiedTypeParameters(model.getTypeParameters(), arguments); JCNewClass expr = make().NewClass(null, null, nameId, arguments, null); return makeMainMethod(model, expr); } /** * Makes a {@code main()} method which calls the given top-level method * @param method */ private MethodDefinitionBuilder makeMainForFunction(Function method) { at(null); JCExpression qualifiedName = naming.makeName(method, Naming.NA_FQ | Naming.NA_WRAPPER | Naming.NA_MEMBER); List arguments = makeBottomReifiedTypeParameters(method.getTypeParameters(), List.nil()); MethodDefinitionBuilder mainMethod = makeMainMethod(method, make().Apply(null, qualifiedName, arguments)); return mainMethod; } private List makeBottomReifiedTypeParameters( java.util.List typeParameters, List initialArguments) { List arguments = initialArguments; for(int i=typeParameters.size()-1;i>=0;i--){ arguments = arguments.prepend(gen().makeNothingTypeDescriptor()); } return arguments; } /** * Makes a {@code main()} method which calls the given callee * (a no-args method or class) * @param decl * @param callee */ private MethodDefinitionBuilder makeMainMethod(Declaration decl, JCExpression callee) { // Add a main() method MethodDefinitionBuilder methbuilder = MethodDefinitionBuilder .main(this) .ignoreModelAnnotations(); // Add call to process.setupArguments JCExpression argsId = makeUnquotedIdent("args"); JCMethodInvocation processExpr = make().Apply(null, naming.makeLanguageValue("process"), List.nil()); methbuilder.body(make().Exec(make().Apply(null, makeSelect(processExpr, "setupArguments"), List.of(argsId)))); // Add call to toplevel method methbuilder.body(make().Exec(callee)); return methbuilder; } void copyTypeParameters(Generic def, MethodDefinitionBuilder methodBuilder) { if (def.getTypeParameters() != null) { for (TypeParameter t : def.getTypeParameters()) { methodBuilder.typeParameter(t); } } } public List transform(final Tree.TypeAliasDeclaration def) { final TypeAlias model = def.getDeclarationModel(); // 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(Decl.isAncestorLocal(def)) return List.nil(); naming.clearSubstitutions(model); String ceylonClassName = def.getIdentifier().getText(); final String javaClassName = Naming.quoteClassName(def.getIdentifier().getText()); ClassDefinitionBuilder classBuilder = ClassDefinitionBuilder .klass(this, javaClassName, ceylonClassName, Decl.isLocal(model)); // class alias classBuilder.getInitBuilder().modifiers(PRIVATE); classBuilder.annotations(makeAtTypeAlias(model.getExtendedType())); classBuilder.annotations(expressionGen().transformAnnotations(OutputElement.TYPE, def)); classBuilder.isAlias(true); // make sure we set the container in case we move it out addAtContainer(classBuilder, model); visitClassOrInterfaceDefinition(def, classBuilder); return classBuilder .modelAnnotations(model.getAnnotations()) .modifiers(transformTypeAliasDeclFlags(model)) .satisfies(model.getSatisfiedTypes()) .build(); } /** * Makes a named constructor * @param that * @param classBuilder * @param mods * @param ctorName * @param ctorBody * @param declFlags * @return */ public List makeNamedConstructor( Tree.Declaration that, Tree.ParameterList parameterList, Constructor ctor, ClassDefinitionBuilder classBuilder, boolean generateInstantiator, int mods, boolean atIgnoreCtor, String ctorName, List ctorBody, DeclNameFlag... declFlags) { ListBuffer result = ListBuffer.lb(); Class clz = (Class)ctor.getContainer(); at(that); MethodDefinitionBuilder ctorDb = MethodDefinitionBuilder.constructor(this); ClassDefinitionBuilder decl = null; ClassDefinitionBuilder impl = null; if (generateInstantiator) { if (clz.getContainer() instanceof Interface) { decl = classBuilder.getContainingClassBuilder(); impl = classBuilder.getContainingClassBuilder().getCompanionBuilder((Interface)clz.getContainer()); } else { decl = classBuilder.getContainingClassBuilder(); impl = classBuilder.getContainingClassBuilder(); } generateInstantiators(classBuilder, clz, ctor, decl, impl, that, parameterList); } ctorDb.userAnnotations(expressionGen().transformAnnotations(OutputElement.CONSTRUCTOR, that)); if (atIgnoreCtor) { ctorDb.modelAnnotations(makeAtIgnore()); } else if (!Decl.isDefaultConstructor(ctor)) { ctorDb.modelAnnotations(makeAtName(ctor.getName())); } if (Decl.isEnumeratedConstructor(ctor)) { ctorDb.modelAnnotations(makeAtEnumerated()); } ctorDb.modifiers(mods); for (TypeParameter tp : clz.getTypeParameters()) { ctorDb.reifiedTypeParameter(tp); } if (ctorName != null ) { // generate a constructor name class (and constant) transformConstructorName(classBuilder, result, ctor, clz, mods, ctorName, declFlags); // Add the name paramter ctorDb.parameter(makeConstructorNameParameter(ctor, declFlags)); } // Add the rest of the parameters (this worries about aliasing) if (parameterList != null) { transformClassOrCtorParameters(null, (Class)ctor.getContainer(), ctor, that, parameterList, contains(declFlags, DeclNameFlag.DELEGATION), classBuilder, ctorDb, generateInstantiator, decl, impl); } // Transformation of body has to happen after transformation of parameter so we know about parameter aliasing. at(that); ctorDb.block(make().Block(0, ctorBody)); result.add(ctorDb.build()); return result.toList(); } > boolean contains(E[] values, E value) { for (E v : values) { if (v == value) { return true; } } return false; } protected void transformConstructorName( ClassDefinitionBuilder classBuilder, ListBuffer result, Constructor ctor, Class clz, int classMods, String ctorName, DeclNameFlag...declFlags) { ClassDefinitionBuilder constructorNameClass = ClassDefinitionBuilder.klass(this, ctorName, null, true); JCVariableDecl constructorNameConst; if (Decl.isEnumeratedConstructor(ctor)) { if (clz.isToplevel()) { classMods &= ~(PRIVATE | PROTECTED | PUBLIC); classMods |= PRIVATE| STATIC | FINAL ; } else if (clz.isMember() && Decl.isToplevel((Declaration)clz.getContainer())) { classMods &= ~(PRIVATE | PROTECTED | PUBLIC); classMods |= FINAL ; if (!Decl.isAncestorLocal(ctor)) { classMods |= STATIC; } if (!ctor.isShared()) { classMods |= PRIVATE; } } else { classMods &= ~(PRIVATE | PROTECTED | PUBLIC); classMods |= FINAL ; } constructorNameConst = null; } else { if (clz.isToplevel() || (clz.isMember() && Decl.isToplevel((Declaration)clz.getContainer()))) { classMods |= STATIC | FINAL; constructorNameConst = make().VarDef(make().Modifiers(classMods, makeAtIgnore()), names().fromString(ctorName), naming.makeTypeDeclarationExpression(null, ctor, declFlags), makeNull()); } else { classMods &= ~(PRIVATE | PROTECTED | PUBLIC); constructorNameConst = null; } } constructorNameClass.modifiers(classMods); constructorNameClass.annotations(makeAtIgnore()); constructorNameClass.annotations(makeAtConstructorName(ctor.getName(), contains(declFlags, DeclNameFlag.DELEGATION))); constructorNameClass.getInitBuilder().modifiers(PRIVATE); List ctorNameClassDecl = constructorNameClass.build(); if (clz.isToplevel()) { result.addAll(ctorNameClassDecl); classBuilder.defs(constructorNameConst); } else if (clz.isClassMember()){ classBuilder.getContainingClassBuilder().defs(ctorNameClassDecl); classBuilder.getContainingClassBuilder().defs(constructorNameConst); } else if (clz.isInterfaceMember()){ classBuilder.getContainingClassBuilder().getCompanionBuilder(clz).defs(ctorNameClassDecl); classBuilder.getContainingClassBuilder().getCompanionBuilder(clz).defs(constructorNameConst); } else { classBuilder.defs(ctorNameClassDecl); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy