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

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

package com.redhat.ceylon.compiler.java.codegen;

import com.redhat.ceylon.compiler.java.codegen.Naming.Suffix;
import com.redhat.ceylon.compiler.java.codegen.Naming.SyntheticName;
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.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;

/**
* 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<JCExpression> typeargs = ListBuffer.<JCExpression>lb();
    /** The transformed argument expressions and their transformed type expressions */
    private ListBuffer<ExpressionAndType> argumentsAndTypes = ListBuffer.<ExpressionAndType>lb();

    private JCExpression methodOrClass;
    private ExpressionAndType instantiateQualfier;
    private int cbOpts;
    private Naming.SyntheticName basename;
    private boolean built = false;
   
    private final ListBuffer<JCStatement> statements = ListBuffer.<JCStatement>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<JCExpression> 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<JCExpression> args) {
        for (JCExpression arg : args) {
            this.argument(arg);
        }
        return this;
    }
   
    public CallBuilder argumentsAndTypes(List<ExpressionAndType> argsAndTypes) {
        this.argumentsAndTypes.clear();
        this.argumentsAndTypes.addAll(argsAndTypes);
        return this;
    }
   
    /**
     * Determine whether a Let expression should be used to evaluate qualifier
     * and arguments <strong>prior</strong> 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<JCStatement> 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<JCExpression> 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.<JCExpression>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<JCExpression> dimensions = List.nil();
                if(arrayInstanceDimensions > 1){
                    for(int i=1;i<arrayInstanceDimensions;i++){
                        dimensions = dimensions.prepend(gen.makeInteger(0));
                    }
                }
                dimensions = dimensions.prepend(arguments.head);
                dimensions = dimensions.prepend(arrayInstanceReifiedType);
                result = gen.utilInvocation().makeArray(dimensions);
            }
            if(arguments.tail.nonEmpty()){
                // must fill it
                result = gen.utilInvocation().fillArray(List.of(result, arguments.tail.head));
            }
            break;
        case FIELD_READ:
            result = this.methodOrClass;
            break;
       
        default:
            throw BugException.unhandledEnumCase(kind);
        }
        if ((cbOpts & CB_LET) != 0) {
            if (voidMethod) {
                result = gen.make().LetExpr(statements.toList().append(gen.make().Exec(result)), gen.makeNull());
            } else if (!statements.isEmpty()) {
                result = gen.make().LetExpr(statements.toList(), result);
            }
        }
       
        return result;
    }

    private SyntheticName getArgumentName(Naming.SyntheticName basename, int argumentNum) {
        SyntheticName name = basename.suffixedBy(Suffix.$arg$, argumentNum);
        return name;
    }

    private SyntheticName getQualifierName(Naming.SyntheticName basename) {
        SyntheticName qualName = basename.suffixedBy(Suffix.$qual$);
        return qualName;
    }

    public void voidMethod(boolean voidMethod) {
        this.voidMethod = voidMethod;
    }

    public void javaArrayInstanceIsGeneric(JCExpression reifiedType, int dimensions) {
        this.arrayInstanceReifiedType = reifiedType;
        this.arrayInstanceDimensions = dimensions;
    }

    public void javaArrayInstanceNeedsCast(JCExpression requiredType) {
        this.arrayInstanceCast = requiredType;
    }

    public void javaArrayWriteNeedsCast(boolean arrayWriteNeedsCast) {
        this.arrayWriteNeedsCast = arrayWriteNeedsCast;
    }
}
TOP

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

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.