
brennus.asm.ASMExpressionVisitor Maven / Gradle / Ivy
The newest version!
package brennus.asm;
import static brennus.model.ExistingType.BOOLEAN;
import static brennus.model.ExistingType.INT;
import static brennus.model.Protection.PRIVATE;
import brennus.ImmutableList;
import brennus.MethodContext;
import brennus.model.BinaryExpression;
import brennus.model.CallConstructorExpression;
import brennus.model.CallMethodExpression;
import brennus.model.CastExpression;
import brennus.model.ExistingType;
import brennus.model.Expression;
import brennus.model.ExpressionVisitor;
import brennus.model.Field;
import brennus.model.FieldAccessType;
import brennus.model.GetExpression;
import brennus.model.InstanceOfExpression;
import brennus.model.InstantiationExpression;
import brennus.model.LiteralExpression;
import brennus.model.LocalVariableAccessType;
import brennus.model.Method;
import brennus.model.NewArrayExpression;
import brennus.model.Parameter;
import brennus.model.ParameterAccessType;
import brennus.model.Type;
import brennus.model.UnaryExpression;
import brennus.model.VarAccessType;
import brennus.model.VarAccessTypeVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
class ASMExpressionVisitor implements Opcodes, ExpressionVisitor {
private final MethodContext methodContext;
private final MethodByteCodeContext methodByteCodeContext;
private Type lastExpressionType;
ASMExpressionVisitor(MethodContext methodContext, MethodByteCodeContext methodByteCodeContext) {
super();
this.methodContext = methodContext;
this.methodByteCodeContext = methodByteCodeContext;
}
@Override
public void visit(final GetExpression getExpression) {
methodByteCodeContext.incIndent("get", getExpression.getFieldName());
VarAccessType varAccessType = methodContext.getVarAccessType(getExpression.getFieldName());
varAccessType.accept(new VarAccessTypeVisitor() {
public void visit(FieldAccessType fieldAccessType) {
Field field = fieldAccessType.getField();
if (field.isStatic()) {
methodByteCodeContext.addInstruction(
new FieldInsnNode(GETSTATIC, methodContext.getType().getClassIdentifier(), getExpression.getFieldName(), field.getSignature()),
"get static field", getExpression.getFieldName());
} else {
methodByteCodeContext.loadThis("get field", getExpression.getFieldName(), "on this");
methodByteCodeContext.addInstruction(
new FieldInsnNode(GETFIELD, methodContext.getType().getClassIdentifier(), getExpression.getFieldName(), field.getSignature()),
"get field", getExpression.getFieldName());
}
lastExpressionType = field.getType();
}
public void visit(ParameterAccessType parameterAccessType) {
// TODO: type support
// System.out.println(getExpression.getFieldName()+" "+param.getIndex());
Parameter param = parameterAccessType.getParam();
// TODO: check boxing
methodByteCodeContext.load(param.getType(), methodByteCodeContext.getParamByteCodeIndex(param.getIndex()),
"get param", getExpression.getFieldName());
lastExpressionType = param.getType();
}
@Override
public void visit(LocalVariableAccessType localVariableAccessType) {
// TODO: check boxing
methodByteCodeContext.load(
localVariableAccessType.getType(),
methodByteCodeContext.getLocalVariableByteCodeIndex(localVariableAccessType.getVarIndex()),
"get local variable", getExpression.getFieldName());
lastExpressionType = localVariableAccessType.getType();
}
});
methodByteCodeContext.decIndent();
}
@Override
public void visit(CallMethodExpression callMethodExpression) {
String methodName = callMethodExpression.getMethodName();
methodByteCodeContext.incIndent("call method", methodName);
Method method;
int parameterCount = callMethodExpression.getParameters().size();
if (callMethodExpression.getCallee()==null) {
methodByteCodeContext.loadThis("calling on this", methodName);
// TODO: use parameter count/types for lookup
method = methodContext.getType().getMethod(methodName, parameterCount);
if (method == null) {
throw new RuntimeException("can't find method "+methodName+" in hierarchy of "+methodContext.getType());
}
} else {
callMethodExpression.getCallee().accept(this);
method = lastExpressionType.getMethod(methodName, parameterCount);
if (method == null) {
throw new RuntimeException("can't find method "+methodName+" with " + parameterCount + " parameters in hierarchy of "+lastExpressionType);
}
}
if (method.getFlags().isStatic()) {
throw new UnsupportedOperationException();
}
ImmutableList parameters = callMethodExpression.getParameters();
loadParameters(methodName, method, parameters);
methodByteCodeContext.addInstruction(
new MethodInsnNode(
method.isInterfaceMethod() ? INVOKEINTERFACE :
method.getFlags().getProtection() == PRIVATE || method.getFlags().isFinal() ? INVOKESPECIAL : INVOKEVIRTUAL,
method.getTypeName(),
methodName,
method.getSignature()),
"call", methodName);
lastExpressionType = method.getReturnType();
methodByteCodeContext.decIndent();
}
@Override
public void visit(CallConstructorExpression callConstructorExpression) {
methodByteCodeContext.incIndent("call super constructor");
methodByteCodeContext.loadThis();
Method constructor = methodContext.getType().getSuperConstructor(callConstructorExpression.getParameters().size());
if (constructor == null) {
throw new RuntimeException(
"can't find constructor with "
+ callConstructorExpression.getParameters().size() + " parameters in "
+ methodContext.getType().getExtending() + " parent of " + methodContext.getType());
}
loadParameters("", constructor, callConstructorExpression.getParameters());
methodByteCodeContext.addInstruction(new MethodInsnNode(INVOKESPECIAL, methodContext.getType().getExtending().getClassIdentifier(), "", constructor.getSignature()), "super(...)");
methodByteCodeContext.decIndent();
}
private void loadParameters(String methodName, Method method,
ImmutableList parameters) {
methodByteCodeContext.incIndent("pass", parameters.size(), "params to", methodName);
ImmutableList parameterValues = parameters;
ImmutableList parameterTypes = method.getParameters();
if (parameterTypes.size() != parameterValues.size()) {
throw new RuntimeException("parameters passed do not match, parameters declared in "+method);
}
for (int i = 0; i < parameterValues.size(); i++) {
methodByteCodeContext.incIndent("param", i);
Expression expression = parameterValues.get(i);
Type expected = parameterTypes.get(i).getType();
expression.accept(this);
methodByteCodeContext.handleConversion(lastExpressionType, expected, "param", i, "for", methodName);
methodByteCodeContext.decIndent();
}
methodByteCodeContext.decIndent();
}
@Override
public void visit(LiteralExpression literalExpression) {
lastExpressionType = literalExpression.getType();
// TODO: support other types
if (literalExpression.getType().getExisting().equals(Integer.TYPE)) {
int intValue = ((Integer)literalExpression.getValue()).intValue();
if (intValue >= -128 && intValue <= 127) {
// http://www.vmth.ucdavis.edu/incoming/Jasmin/ref-_bipush.html
methodByteCodeContext.push(BIPUSH, intValue, "int literal", literalExpression.getValue());
} else {
methodByteCodeContext.ldc((Integer)literalExpression.getValue(), "int literal", literalExpression.getValue());
}
} else if (literalExpression.getType().getExisting().equals(Long.TYPE)) {
methodByteCodeContext.ldc((Long)literalExpression.getValue(), "long literal", literalExpression.getValue());
} else if (literalExpression.getType().getExisting().equals(Float.TYPE)) {
methodByteCodeContext.ldc((Float)literalExpression.getValue(), "float literal", literalExpression.getValue());
} else if (literalExpression.getType().getExisting().equals(Double.TYPE)) {
methodByteCodeContext.ldc((Double)literalExpression.getValue(), "double literal", literalExpression.getValue());
} else if (literalExpression.getType().getExisting().equals(String.class)) {
methodByteCodeContext.ldc((String)literalExpression.getValue(), "String literal", literalExpression.getValue());
} else if (literalExpression.getType().getExisting().equals(Boolean.TYPE)) {
boolean b = (Boolean)literalExpression.getValue();
methodByteCodeContext.addBool(b, "bool literal");
} else {
throw new UnsupportedOperationException(literalExpression.toString());
}
}
@Override
public void visit(BinaryExpression binaryExpression) {
methodByteCodeContext.incIndent(binaryExpression.getOperator().getRepresentation());
// TODO: support other types
switch (binaryExpression.getOperator()) {
case PLUS:
methodByteCodeContext.incIndent("left +");
binaryExpression.getLeftExpression().accept(this);
methodByteCodeContext.decIndent();
methodByteCodeContext.incIndent("+ right");
binaryExpression.getRightExpression().accept(this);
methodByteCodeContext.decIndent();
lastExpressionType = ExistingType.INT;
methodByteCodeContext.addInstruction(new InsnNode(IADD), "+");
break;
case AND:
methodByteCodeContext.incIndent("left &&");
binaryExpression.getLeftExpression().accept(this);
methodByteCodeContext.decIndent();
new LiteralExpression(false).accept(this);
LabelNode falseLabel = new LabelNode();
LabelNode endLabel = new LabelNode();
methodByteCodeContext.addInstruction(new JumpInsnNode(IF_ICMPEQ, falseLabel), "AND: IF left is false => false");
methodByteCodeContext.incIndent("&& right");
binaryExpression.getRightExpression().accept(this);
methodByteCodeContext.decIndent();
new LiteralExpression(false).accept(this);
methodByteCodeContext.addInstruction(new JumpInsnNode(IF_ICMPEQ, falseLabel), "AND: IF right is false => false");
new LiteralExpression(true).accept(this);
methodByteCodeContext.addInstruction(new JumpInsnNode(GOTO, endLabel), "AND: all true => skip false");
methodByteCodeContext.addLabel(falseLabel, "AND: false");
new LiteralExpression(false).accept(this);
methodByteCodeContext.addLabel(endLabel, "AND: end");
lastExpressionType = BOOLEAN;
break;
case GETARRAYATINDEX:
methodByteCodeContext.incIndent("array");
binaryExpression.getLeftExpression().accept(this);
Type arrayType = lastExpressionType;
methodByteCodeContext.decIndent();
methodByteCodeContext.incIndent("[");
binaryExpression.getRightExpression().accept(this);
methodByteCodeContext.decIndent();
lastExpressionType = arrayType.unNestArray();
methodByteCodeContext.aload(lastExpressionType, "[]");
break;
default:
// TODO: other operators
throw new UnsupportedOperationException("op: "+binaryExpression.getOperator());
}
methodByteCodeContext.decIndent();
}
public Type getExpressionType() {
return lastExpressionType;
}
@Override
public void visit(UnaryExpression unaryExpression) {
methodByteCodeContext.incIndent(unaryExpression.getOperator().getRepresentation());
methodByteCodeContext.incIndent("unary exp");
unaryExpression.getExpression().accept(this);
methodByteCodeContext.decIndent();
switch (unaryExpression.getOperator()) {
case NOT: {
// TODO: combine with parent
methodByteCodeContext.ifCondElse(IFEQ, "NOT: IF true => false")
.addBool(false, "NOT true: result false")
.thenCase()
.addBool(true, "NOT false: result true")
.endIf();
lastExpressionType = BOOLEAN;
break;
}
case ISNULL: {
// TODO: combine with parent
methodByteCodeContext.ifCondElse(IFNONNULL, "ISNULL: IF NULL => true")
.addBool(true, "NOT NULL: result true")
.thenCase()
.addBool(false, "NULL: result false")
.endIf();
lastExpressionType = BOOLEAN;
break;
}
case ISNOTNULL: {
// TODO: combine with parent
methodByteCodeContext.ifCondElse(IFNULL, "ISNONNULL: IF NON NULL => true")
.addBool(true, "NULL: result true")
.thenCase()
.addBool(false, "NOT NULL: result false")
.endIf();
lastExpressionType = BOOLEAN;
break;
}
case ARRAYSIZE:
methodByteCodeContext.addInstruction(new InsnNode(ARRAYLENGTH), ".length");
lastExpressionType = INT;
break;
default:
// TODO: other operators
throw new UnsupportedOperationException("op: "+unaryExpression.getOperator());
}
methodByteCodeContext.decIndent();
}
@Override
public void visit(InstanceOfExpression instanceOfExpression) {
methodByteCodeContext.incIndent("instanceOF");
instanceOfExpression.getExpression().accept(this);
methodByteCodeContext.addInstruction(new TypeInsnNode(INSTANCEOF, instanceOfExpression.getType().getClassIdentifier()));
lastExpressionType = BOOLEAN;
methodByteCodeContext.decIndent();
}
@Override
public void visit(CastExpression castExpression) {
methodByteCodeContext.incIndent("cast (", castExpression.getType(), ")");
castExpression.getExpression().accept(this);
methodByteCodeContext.cast(castExpression.getType(), "cast to", castExpression.getType());
lastExpressionType = castExpression.getType();
methodByteCodeContext.decIndent();
}
@Override
public void visit(InstantiationExpression instantiationExpression) {
// new instance involves creating a new object and then calling the constructor
Type type = instantiationExpression.getType();
methodByteCodeContext.incIndent("new ", type.getName(), "()");
methodByteCodeContext.addInstruction(new TypeInsnNode(NEW, type.getClassIdentifier()), "new ", type.getName(), "()");
methodByteCodeContext.dup("for constructor call"); // so that we still have a reference to the object after we call the constructor.
Method constructor = type.getConstructor(instantiationExpression.getParameters().size());
if (constructor == null) {
throw new RuntimeException(
"can't find constructor with "
+ instantiationExpression.getParameters().size() + " parameters in "
+ type);
}
loadParameters("", constructor, instantiationExpression.getParameters());
methodByteCodeContext.addInstruction(new MethodInsnNode(INVOKESPECIAL, type.getClassIdentifier(), "", constructor.getSignature()), "new ", type.getName(), "(...)");
lastExpressionType = type;
methodByteCodeContext.decIndent();
}
@Override
public void visit(NewArrayExpression e) {
methodByteCodeContext.incIndent("new ", e.getType(), "[]");
e.getSize().accept(this);
methodByteCodeContext.newArray(e.getType(), "new ", e.getType(), "[]");
lastExpressionType = e.getType().nestArray();
methodByteCodeContext.decIndent();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy