Package com.redhat.ceylon.compiler.java.codegen

Source Code of com.redhat.ceylon.compiler.java.codegen.MethodDefinitionBuilder

/*
* 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.sun.tools.javac.code.Flags.ABSTRACT;
import static com.sun.tools.javac.code.Flags.FINAL;
import static com.sun.tools.javac.code.Flags.PUBLIC;
import static com.sun.tools.javac.code.Flags.STATIC;
import static com.sun.tools.javac.code.TypeTags.VOID;

import java.util.Collections;

import com.redhat.ceylon.compiler.typechecker.model.Annotation;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.Method;
import com.redhat.ceylon.compiler.typechecker.model.MethodOrValue;
import com.redhat.ceylon.compiler.typechecker.model.Package;
import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.model.ParameterList;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.model.ProducedTypedReference;
import com.redhat.ceylon.compiler.typechecker.model.Scope;
import com.redhat.ceylon.compiler.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.compiler.typechecker.model.TypeParameter;
import com.redhat.ceylon.compiler.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;

/**
* Builder for Java Methods. With special pre-definied builders
* for normal methods, constructors, getters and setters.
*
* @author Tako Schotanus
*/
public class MethodDefinitionBuilder
        implements ParameterizedBuilder<MethodDefinitionBuilder> {
    private final AbstractTransformer gen;
   
    private final String name;
   
    private long modifiers;

    private boolean isOverride;
    private boolean isAbstract;
    private boolean isTransient;
   
    private JCExpression resultTypeExpr;
    private List<JCAnnotation> resultTypeAnnos;
   
    private final ListBuffer<JCAnnotation> userAnnotations = ListBuffer.lb();
    private final ListBuffer<JCAnnotation> modelAnnotations = ListBuffer.lb();
   
    private final ListBuffer<JCTypeParameter> typeParams = ListBuffer.lb();
    private final ListBuffer<JCExpression> typeParamAnnotations = ListBuffer.lb();
   
    private final ListBuffer<ParameterDefinitionBuilder> params = ListBuffer.lb();
   
    private ListBuffer<JCStatement> body = ListBuffer.lb();

    private int annotationFlags = Annotations.MODEL_AND_USER;
   
    private boolean built = false;

    private JCExpression defaultValue;

    private boolean haveLocation = false;
    private Node location;

    public static MethodDefinitionBuilder method(AbstractTransformer gen, Method method) {
        return new MethodDefinitionBuilder(gen, false, gen.naming.selector(method));
    }
   
    public static MethodDefinitionBuilder method(AbstractTransformer gen, TypedDeclaration decl, int namingOptions) {
        return new MethodDefinitionBuilder(gen, false, Naming.selector(decl, namingOptions));
    }
   
    public static MethodDefinitionBuilder getter(AbstractTransformer gen, TypedDeclaration attr, boolean indirect) {
        return new MethodDefinitionBuilder(gen, false, Naming.getGetterName(attr, indirect));
    }
   
    public static MethodDefinitionBuilder setter(AbstractTransformer gen, TypedDeclaration attr) {
        return new MethodDefinitionBuilder(gen, false, Naming.getSetterName(attr));
    }
   
    public static MethodDefinitionBuilder callable(AbstractTransformer gen) {
        return systemMethod(gen, Naming.getCallableMethodName());
    }
   
    public static MethodDefinitionBuilder systemMethod(AbstractTransformer gen, String name) {
        MethodDefinitionBuilder builder = new MethodDefinitionBuilder(gen, true, name);
        return builder;
    }
   
    public static MethodDefinitionBuilder constructor(AbstractTransformer gen) {
        return new MethodDefinitionBuilder(gen, false, null);
    }

    public static MethodDefinitionBuilder main(AbstractTransformer gen) {
        MethodDefinitionBuilder mdb = new MethodDefinitionBuilder(gen, false, "main")
            .modifiers(PUBLIC | STATIC);
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.systemParameter(mdb.gen, "args");
        pdb.type(gen.make().TypeArray(gen.make().Type(gen.syms().stringType)), List.<JCAnnotation>nil());
        return mdb.parameter(pdb);
    }
   
    private MethodDefinitionBuilder(AbstractTransformer gen, boolean ignoreAnnotations, String name) {
        this.gen = gen;
        this.name = name;
        if (ignoreAnnotations) {
            this.annotationFlags = Annotations.ignore(this.annotationFlags);
        }
        resultTypeExpr = makeVoidType();
    }
   
    private ListBuffer<JCAnnotation> getAnnotations() {
        ListBuffer<JCAnnotation> result = ListBuffer.lb();
        if (Annotations.includeUser(this.annotationFlags)) {
            result.appendList(this.userAnnotations);
        }
        if (Annotations.includeModel(this.annotationFlags)) {
            result.appendList(this.modelAnnotations);  
        }
        if (isOverride) {
            result.appendList(gen.makeAtOverride());
        }
        if (Annotations.includeIgnore(this.annotationFlags)) {
            result.appendList(gen.makeAtIgnore());
        }
        if (Annotations.includeModel(this.annotationFlags)) {
            if (resultTypeAnnos != null) {
                result.appendList(resultTypeAnnos);
            }
            if(!typeParamAnnotations.isEmpty()) {
                result.appendList(gen.makeAtTypeParameters(typeParamAnnotations.toList()));
            }
            if (isTransient) {
                result.appendList(gen.makeAtTransient());
            }
        }
        return result;
    }
   
    public MethodDefinitionBuilder location(Node at) {
        this.haveLocation = true;
        this.location = at;
        return this;
    }
   
    public JCTree.JCMethodDecl build() {
        if (built) {
            throw new BugException("already built");
        }
        built = true;
        if (haveLocation) {
            gen.at(location);
        }
        ListBuffer<JCVariableDecl> params = ListBuffer.lb();
        for (ParameterDefinitionBuilder pdb : this.params) {
            if (!Annotations.includeModel(this.annotationFlags)) {
                pdb.noModelAnnotations();
            }
            params.append(pdb.build());
        }

        return gen.make().MethodDef(
                gen.make().Modifiers(modifiers, getAnnotations().toList()),
                makeName(name),
                resultTypeExpr,
                typeParams.toList(),
                params.toList(),
                List.<JCExpression> nil(),
                makeBody(body),
                defaultValue);
    }

    private Name makeName(String name) {
        if (name != null) {
            return gen.names().fromString(Naming.quoteMethodName(name));
        } else {
            return gen.names().init;
        }
    }

    private JCBlock makeBody(ListBuffer<JCStatement> body) {
        for (ParameterDefinitionBuilder pdb : params) {
            if (pdb.requiresBoxedVariableDecl()) {
                body.prepend(pdb.buildBoxedVariableDecl());
            }
        }
        return (!isAbstract && (body != null) && ((modifiers & ABSTRACT) == 0)) ? gen.make().Block(0, body.toList()) : null;
    }

    JCExpression makeVoidType() {
        return gen.make().TypeIdent(VOID);
    }

    JCExpression makeResultType(TypedDeclaration typedDeclaration, ProducedType type, int flags) {
        if (typedDeclaration == null
                || ((!(typedDeclaration instanceof Method) || !((Method)typedDeclaration).isParameter())
                        && AbstractTransformer.isAnything(type))) {
            if ((typedDeclaration instanceof Method)
                    && ((Method)typedDeclaration).isDeclaredVoid()
                    && !Strategy.useBoxedVoid((Method)typedDeclaration)) {
                return makeVoidType();
            } else {
                return gen.makeJavaType(typedDeclaration, gen.typeFact().getAnythingDeclaration().getType(), flags);
            }
        } else {
            return gen.makeJavaType(typedDeclaration, type, flags);
        }
    }

    /*
     * Builder methods - they transform the inner state before doing the final construction
     */
   
    public MethodDefinitionBuilder modifiers(long... modifiers) {
        long mods = 0;
        for (long mod : modifiers) {
            mods |= mod;
        }
        this.modifiers = mods;
        return this;
    }

    /**
     * {@code @Ignore} and no model annotations.
     */
    public MethodDefinitionBuilder ignoreModelAnnotations() {
        this.annotationFlags = Annotations.ignore(annotationFlags);
        return this;
    }
   
    /** No model annotations, but possibly still {@link @Ignore} and user annotations. */
    public MethodDefinitionBuilder noModelAnnotations() {
        this.annotationFlags = Annotations.noModel(annotationFlags);
        return this;
    }
   
    /** No annotations at all (including {@code @Ignore}). */
    public MethodDefinitionBuilder noAnnotations() {
        this.annotationFlags = 0;
        return this;
    }
   
    public MethodDefinitionBuilder annotationFlags(int annotationFlags) {
        this.annotationFlags = annotationFlags;
        return this;
    }
   
    public MethodDefinitionBuilder modelAnnotations(List<JCTree.JCAnnotation> modelAnnotations) {
        this.modelAnnotations.appendList(modelAnnotations);
        return this;
    }
   
    public MethodDefinitionBuilder userAnnotations(List<JCTree.JCAnnotation> annotations) {
        this.userAnnotations.appendList(annotations);
        return this;
    }

    public MethodDefinitionBuilder typeParameter(TypeParameter param, java.util.List<ProducedType> producedBounds) {
        return typeParameter(gen.makeTypeParameter(param, producedBounds), gen.makeAtTypeParameter(param));
    }
   
    public MethodDefinitionBuilder typeParameter(TypeParameter param) {
        return typeParameter(param, null);
    }
   
    private MethodDefinitionBuilder typeParameter(JCTypeParameter tp, JCAnnotation tpAnno) {
        typeParams.append(tp);
        typeParamAnnotations.append(tpAnno);
        return this;
    }

    public MethodDefinitionBuilder parameters(List<ParameterDefinitionBuilder> pdbs) {
        params.appendList(pdbs);
        return this;
    }
   
    public MethodDefinitionBuilder parameter(ParameterDefinitionBuilder pdb) {
        params.append(pdb);
        return this;
    }

    public MethodDefinitionBuilder parameter(long modifiers,
            java.util.List<Annotation> annos,
            String name,
            Parameter decl,
            TypedDeclaration nonWideningDecl,
            ProducedType nonWideningType,
            int flags, boolean canWiden) {
        return parameter(modifiers, annos, null, name, name, decl, nonWideningDecl, nonWideningType, flags, canWiden);
    }
   
    private MethodDefinitionBuilder parameter(long modifiers,
            java.util.List<Annotation> modelAnnotations, List<JCAnnotation> userAnnotations,
            String name, String aliasedName,
            Parameter decl, TypedDeclaration nonWideningDecl, ProducedType nonWideningType,
            int flags, boolean canWiden) {
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.explicitParameter(gen, decl);
        pdb.modifiers(modifiers);
        pdb.modelAnnotations(modelAnnotations);
        pdb.userAnnotations(userAnnotations);
        pdb.aliasName(aliasedName);
        pdb.sequenced(decl.isSequenced());
        pdb.defaulted(decl.isDefaulted());
        if (isParamTypeLocalToMethod(decl,
                nonWideningType)) {
            pdb.type(gen.make().Type(gen.syms().objectType), gen.makeJavaTypeAnnotations(decl.getModel()));
        } else {
            pdb.type(paramType(gen, nonWideningDecl, nonWideningType, flags, canWiden), gen.makeJavaTypeAnnotations(decl.getModel()));
        }
        return parameter(pdb);
    }

    private boolean isParamTypeLocalToMethod(Parameter parameter,
            ProducedType nonWideningType) {
        Declaration method = parameter.getDeclaration();
        TypeDeclaration paramTypeDecl = nonWideningType.getDeclaration();
        if (paramTypeDecl instanceof TypeParameter
                && Decl.equalScopeDecl(paramTypeDecl.getContainer(), method)) {
            return false;
        }
        Scope scope = paramTypeDecl.getContainer();
        while (scope != null && !(scope instanceof Package)) {
            if (Decl.equalScopeDecl(scope, method)) {
                return true;
            }
            scope = scope.getContainer();
        }
        return false;
    }

    static JCExpression paramType(AbstractTransformer gen, TypedDeclaration nonWideningDecl,
            ProducedType nonWideningType, int flags, boolean canWiden) {
        // keep in sync with gen.willEraseToBestBounds()
        if (canWiden
                && (gen.typeFact().isUnion(nonWideningType)
                        || gen.typeFact().isIntersection(nonWideningType))) {
            final TypeDeclaration refinedTypeDecl = ((TypedDeclaration)CodegenUtil.getTopmostRefinedDeclaration(nonWideningDecl)).getType().getDeclaration();
            if (refinedTypeDecl instanceof TypeParameter
                    && !refinedTypeDecl.getSatisfiedTypes().isEmpty()) {
                nonWideningType = refinedTypeDecl.getSatisfiedTypes().get(0);
                // Could be parameterized, and type param won't be in scope, so have to go raw
                flags |= AbstractTransformer.JT_RAW;
            }
        }
        JCExpression type = gen.makeJavaType(nonWideningDecl, nonWideningType, flags);
        return type;
    }
   
    public MethodDefinitionBuilder parameter(Parameter paramDecl,
            ProducedType paramType, int mods, int flags, boolean canWiden) {
        String name = paramDecl.getName();
        return parameter(mods, paramDecl.getModel().getAnnotations(),
                name, paramDecl, paramDecl.getModel(), paramType, flags, canWiden);
    }
   
    public MethodDefinitionBuilder parameter(Parameter param,
            List<JCAnnotation> userAnnotations, int flags, boolean canWiden) {
        String paramName = param.getName();
        String aliasedName = Naming.getAliasedParameterName(param);
        MethodOrValue mov = CodegenUtil.findMethodOrValueForParam(param);
        int mods = 0;
        if (!Decl.isNonTransientValue(mov) || !mov.isVariable() || mov.isCaptured()) {
            mods |= FINAL;
        }
        TypedDeclaration nonWideningDecl = null;
        ProducedType nonWideningType;
        if (Decl.isValue(mov)) {
            ProducedTypedReference typedRef = gen.getTypedReference(mov);
            ProducedTypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef);
            nonWideningType = gen.nonWideningType(typedRef, nonWideningTypedRef);
            nonWideningDecl = nonWideningTypedRef.getDeclaration();
        }else{
            nonWideningType = param.getType();
            nonWideningDecl = param.getModel();
        }
       
        // make sure we don't accidentally narrow parameters that would be erased in the topmost declaration
        if(canWiden){
            TypedDeclaration refinedParameter = (TypedDeclaration)CodegenUtil.getTopmostRefinedDeclaration(param.getModel());
            if(!Decl.equal(refinedParameter, param.getDeclaration())){
                ProducedType refinedParameterType;
                // we don't have to use produced typed references with type params applied here because we want to know the
                // erasure status of the compilation of the refined parameter, so it's OK if we end up with unbound type parameters
                // in the refined parameter type
                if(refinedParameter instanceof Method)
                    refinedParameterType = refinedParameter.getProducedTypedReference(null, Collections.<ProducedType>emptyList()).getFullType();
                else
                    refinedParameterType = refinedParameter.getType();
                // if the supertype method itself got erased to Object, we can't do better than this
                if(gen.willEraseToObject(refinedParameterType) && !gen.willEraseToBestBounds(param))
                    nonWideningType = gen.typeFact().getObjectDeclaration().getType();
            }
        }
        return parameter(mods, param.getModel().getAnnotations(), userAnnotations, paramName, aliasedName, param, nonWideningDecl, nonWideningType, flags, canWiden);
    }

    public MethodDefinitionBuilder isOverride(boolean isOverride) {
        this.isOverride = isOverride;
        return this;
    }
   
    public MethodDefinitionBuilder isAbstract(boolean isAbstract) {
        this.isAbstract = isAbstract;
        return this;
    }
   
    public MethodDefinitionBuilder isTransient(boolean trans) {
        this.isTransient = trans;
        return this;
    }

    public MethodDefinitionBuilder body(JCStatement statement) {
        if (statement != null) {
            this.body.append(statement);
        }
        return this;
    }
   
    public MethodDefinitionBuilder body(List<JCStatement> body) {
        if (body != null) {
            this.body.appendList(body);
        }
        return this;
    }

    MethodDefinitionBuilder noBody() {
        this.body = null;
        return this;
    }

    public MethodDefinitionBuilder block(JCBlock block) {
        if (block != null) {
            body.clear();
            return body(block.getStatements());
        } else {
            return noBody();
        }
    }

    public MethodDefinitionBuilder resultType(Method method, int flags) {
        if (method.isParameter()) {
            if (Decl.isUnboxedVoid(method) && !Strategy.useBoxedVoid(method)) {
                return resultType(gen.makeJavaTypeAnnotations(method, false), gen.make().Type(gen.syms().voidType));
            } else {
                Parameter parameter = method.getInitializerParameter();
                ProducedType resultType = parameter.getType();
                for (int ii = 1; ii < method.getParameterLists().size(); ii++) {
                    resultType = gen.typeFact().getCallableType(resultType);
                }
                return resultType(gen.makeJavaType(resultType, CodegenUtil.isUnBoxed(method) ? 0 : AbstractTransformer.JT_NO_PRIMITIVES), method);
            }
        }
        ProducedTypedReference typedRef = gen.getTypedReference(method);
        ProducedTypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef);
        ProducedType nonWideningType = gen.nonWideningType(typedRef, nonWideningTypedRef);
        if(method.isActual()
                && CodegenUtil.hasTypeErased(method))
            flags |= AbstractTransformer.JT_RAW;
        return resultType(makeResultType(nonWideningTypedRef.getDeclaration(), nonWideningType, flags), method);
    }
   
    public MethodDefinitionBuilder resultTypeNonWidening(ProducedType currentType, ProducedTypedReference typedRef,
            ProducedType returnType, int flags){
        ProducedTypedReference nonWideningTypedRef = gen.nonWideningTypeDecl(typedRef, currentType);
        returnType = gen.nonWideningType(typedRef, nonWideningTypedRef);
        return resultType(makeResultType(nonWideningTypedRef.getDeclaration(), returnType, flags), typedRef.getDeclaration());

    }
   
    public MethodDefinitionBuilder resultType(TypedDeclaration resultType, ProducedType type, int flags) {
        return resultType(makeResultType(resultType, type, flags), resultType);
    }

    public MethodDefinitionBuilder resultType(JCExpression resultType, TypedDeclaration typeDecl) {
        return resultType(gen.makeJavaTypeAnnotations(typeDecl), resultType);
    }
   
    public MethodDefinitionBuilder resultType(List<JCAnnotation> resultTypeAnnos, JCExpression resultType) {
        this.resultTypeAnnos = resultTypeAnnos;
        this.resultTypeExpr = resultType;
        return this;
    }

    public MethodDefinitionBuilder modelAnnotations(java.util.List<Annotation> annotations) {
        modelAnnotations(gen.makeAtAnnotations(annotations));
        return this;
    }
   
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(getAnnotations()).append(' ');
        sb.append(Flags.toString(this.modifiers)).append(' ');
        sb.append(resultTypeExpr).append(' ');
        sb.append(name).append('(');
        int i = 0;
        for (ParameterDefinitionBuilder param : params) {
            sb.append(param);
            if (i < params.count -1) {
                sb.append(',');
            }
        }
        sb.append(')');
        return sb.toString();
    }

    public MethodDefinitionBuilder reifiedTypeParameters(java.util.List<TypeParameter> typeParams) {
        for(TypeParameter typeParam : typeParams)
            reifiedTypeParameter(typeParam);
        return this;
    }

    public MethodDefinitionBuilder reifiedTypeParameter(TypeParameter param) {
        String descriptorName = gen.naming.getTypeArgumentDescriptorName(param);
        ParameterDefinitionBuilder pdb = ParameterDefinitionBuilder.implicitParameter(gen, descriptorName);
        pdb.type(gen.makeTypeDescriptorType(), List.<JCAnnotation>nil());
        pdb.modifiers(FINAL);
        if(!Annotations.includeModel(this.annotationFlags))
            pdb.noUserOrModelAnnotations();
        else
            pdb.ignored();
        parameter(pdb);

        return this;
    }

    public MethodDefinitionBuilder reifiedTypeParametersFromModel(java.util.List<TypeParameter> typeParameters) {
        for(TypeParameter typeParam : typeParameters)
            reifiedTypeParameter(typeParam);
        return this;
    }

    public void defaultValue(JCExpression defaultValue) {
        this.defaultValue = defaultValue;
    }

    public void mpl(java.util.List<ParameterList> parameterLists) {
        StringBuilder sb = new StringBuilder();
        for (int ii = 1; ii < parameterLists.size(); ii++) {
            ParameterList parameterList = parameterLists.get(ii);
            ParameterDefinitionBuilder.functionalParameters(sb, parameterList);
        }
        modelAnnotations(gen.makeAtFunctionalParameter(sb.toString()));
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.codegen.MethodDefinitionBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.