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

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

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.compiler.java.codegen;

import com.redhat.ceylon.compiler.java.codegen.Naming.SyntheticName;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.langtools.tools.javac.code.Flags;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCClassDecl;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCExpression;
import com.redhat.ceylon.langtools.tools.javac.tree.JCTree.JCStatement;
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.Suffix;

/**
 * A builder for constructing method calls and instantiations
 */
public class CallBuilder {

    private enum Kind {
        APPLY,
        NEW,
        ARRAY_READ,
        ARRAY_WRITE,
        NEW_ARRAY,
        FIELD_READ
    }
    
    public static final int CB_ALIAS_ARGS = 1<<0;
    public static final int CB_LET = 1<<1;
    
    private static final String MISSING_TYPE = "Type expression required when evaluateArgumentsFirst()";
    
    private final AbstractTransformer gen;
    private Kind kind;
    
    private ListBuffer typeargs = ListBuffer.lb();
    /** The transformed argument expressions and their transformed type expressions */
    private ListBuffer argumentsAndTypes = ListBuffer.lb();

    private JCExpression methodOrClass;
    private ExpressionAndType instantiateQualfier;
    private int cbOpts;
    private Naming.SyntheticName basename;
    private boolean built = false;
    
    private final ListBuffer statements = ListBuffer.lb();
    private boolean voidMethod;
    
    private boolean haveLocation = false;
    private Node location;
    private JCClassDecl classDefs;
    private JCExpression arrayInstanceReifiedType;
    private JCExpression arrayInstanceCast;
    private int arrayInstanceDimensions;
    private boolean arrayWriteNeedsCast;
    
    private CallBuilder(AbstractTransformer gen) {
        this.gen = gen;
    }
    
    public static CallBuilder instance(AbstractTransformer gen) {
        CallBuilder builder = new CallBuilder(gen);
        return builder;
    }

    public CallBuilder location(Node at) {
        haveLocation = true;
        this.location = at;
        return this;
    }
    
    public CallBuilder arrayRead(JCExpression expr) {
        this.methodOrClass = expr;
        this.instantiateQualfier = null;
        this.kind = Kind.ARRAY_READ;
        return this;
    }

    public CallBuilder arrayWrite(JCExpression expr) {
        this.methodOrClass = expr;
        this.instantiateQualfier = null;
        this.kind = Kind.ARRAY_WRITE;
        return this;
    }

    public CallBuilder invoke(JCExpression fn) {
        this.methodOrClass = fn;
        this.instantiateQualfier = null;
        this.kind = Kind.APPLY;
        return this;
    }

    public CallBuilder fieldRead(JCExpression expr) {
        this.methodOrClass = expr;
        this.instantiateQualfier = null;
        this.kind = Kind.FIELD_READ;
        return this;
    }

    public CallBuilder instantiate(JCExpression cls) {
        return instantiate(null, cls);
    }
    
    public CallBuilder instantiate(ExpressionAndType qualifier, JCExpression cls) {
        return instantiate(qualifier, cls, null);
    }
    
    public CallBuilder instantiate(ExpressionAndType qualifier, JCExpression cls, JCClassDecl classDefs) {
        this.methodOrClass = cls;
        this.classDefs = classDefs;
        this.instantiateQualfier = qualifier;
        this.kind = Kind.NEW;
        return this;
    }

    public CallBuilder javaArrayInstance(JCExpression type) {
        this.methodOrClass = type;
        this.instantiateQualfier = null;
        this.kind = Kind.NEW_ARRAY;
        return this;
    }

    public CallBuilder typeArgument(JCExpression expr) {
        this.typeargs.append(expr);
        return this;
    }
    
    public CallBuilder typeArguments(List typeArguments) {
        this.typeargs.clear();
        this.typeargs.addAll(typeArguments);
        return this;
    }

    public CallBuilder argument(JCExpression expr) {
        this.argumentAndType(new ExpressionAndType(expr, null));
        return this;
    }
    
    public CallBuilder argumentAndType(ExpressionAndType argumentAndType) {
        this.argumentsAndTypes.append(argumentAndType);
        return this;
    }

    public CallBuilder prependArgumentAndType(ExpressionAndType argumentAndType) {
        this.argumentsAndTypes = this.argumentsAndTypes.prepend(argumentAndType);
        return this;
    }

    public CallBuilder arguments(List args) {
        for (JCExpression arg : args) {
            this.argument(arg);
        }
        return this;
    }
    
    public CallBuilder argumentsAndTypes(List argsAndTypes) {
        this.argumentsAndTypes.clear();
        this.argumentsAndTypes.addAll(argsAndTypes);
        return this;
    }
    
    /**
     * Determine whether a Let expression should be used to evaluate qualifier 
     * and arguments prior to evaluating a
     * {@code super} invocation or instantiation. The JVM prohibits a backward 
     * branch (i.e. loop) when an uninitialized reference is on the operand 
     * stack.
     * @see "#929"
     */
    public CallBuilder argumentHandling(int cbOpts, Naming.SyntheticName basename) {
        if (built) {
            throw new BugException("already built");
        }
        this.cbOpts = cbOpts;
        this.basename = basename;
        return this;
    }
    
    public int getArgumentHandling() {
        return cbOpts;
    }
    
    public CallBuilder appendStatement(JCStatement stmt) {
        this.statements.append(stmt);
        return this;
    }

    public List getStatements() {
        if (!built) {
            throw new BugException("not yet built");
        }
        return statements.toList();
    }
    
    public JCExpression build() {
        if (built) {
            throw new BugException("already built");
        }
        built = true;
        JCExpression result;
        List arguments;
        final JCExpression newEncl;
        
        if ((cbOpts & CB_ALIAS_ARGS) != 0) {
            if (instantiateQualfier != null 
                    && instantiateQualfier.expression != null) {
                if (instantiateQualfier.type == null) {
                    throw new BugException(MISSING_TYPE);
                }
                SyntheticName qualName = getQualifierName(basename);
                appendStatement(gen.makeVar(Flags.FINAL, qualName, 
                        instantiateQualfier.type, 
                        instantiateQualfier.expression));
                newEncl = qualName.makeIdent();
            } else {
                newEncl = null;
            }
            arguments = List.nil();
            int argumentNum = 0;
            for (ExpressionAndType argumentAndType : argumentsAndTypes) {
                SyntheticName name = getArgumentName(basename, argumentNum);
                if (argumentAndType.type == null) {
                    throw new BugException(MISSING_TYPE);
                }
                if ((cbOpts & CB_ALIAS_ARGS) != 0) {
                    appendStatement(gen.makeVar(Flags.FINAL, name, 
                            argumentAndType.type, 
                            argumentAndType.expression));
                }
                arguments = arguments.append(name.makeIdent());
                argumentNum++;
            }
            
        } else {
            newEncl = this.instantiateQualfier != null ? this.instantiateQualfier.expression : null;
            arguments = ExpressionAndType.toExpressionList(this.argumentsAndTypes);
        }
        if (haveLocation) {
            gen.at(this.location);
        }
        switch (kind) {
        case APPLY:
            result = gen.make().Apply(this.typeargs.toList(), this.methodOrClass, arguments);
            break;
        case NEW:
            result = gen.make().NewClass(newEncl, null, this.methodOrClass, arguments, classDefs);
            break;
        case ARRAY_READ:
            result = gen.make().Indexed(this.methodOrClass, arguments.head);
            break;
        case ARRAY_WRITE:
            {
                JCExpression array;
                if(arrayWriteNeedsCast)
                    array = gen.make().TypeCast(gen.make().TypeArray(gen.make().Type(gen.syms().objectType)), this.methodOrClass);
                else
                    array = this.methodOrClass;
                result = gen.make().Assign(gen.make().Indexed(array, arguments.head), arguments.tail.head);
            }
            break;
        case NEW_ARRAY:
            // methodOrClass must be a ArrayType, so we get the element type out
            JCExpression elementTypeExpr = ((JCTree.JCArrayTypeTree)this.methodOrClass).elemtype;
            if(arrayInstanceReifiedType == null){
                result = gen.make().NewArray(elementTypeExpr, List.of(arguments.head), null);
                if(arrayInstanceCast != null){
                    result = gen.make().TypeCast(arrayInstanceCast, result);
                }
            }else{
                List dimensions = List.nil();
                if(arrayInstanceDimensions > 1){
                    for(int i=1;i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy