com.redhat.ceylon.compiler.java.codegen.CallBuilder Maven / Gradle / Ivy
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