forked from JavaTX/JavaCompilerCore
More tests
This commit is contained in:
parent
8666bfdf17
commit
6c584f92e9
@ -11,10 +11,12 @@ import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.objectweb.asm.Opcodes.*;
|
||||
import static de.dhbwstuttgart.target.tree.expression.TargetBinaryOp.*;
|
||||
import static de.dhbwstuttgart.target.tree.expression.TargetLiteral.*;
|
||||
|
||||
public class Codegen {
|
||||
private TargetClass clazz;
|
||||
@ -25,11 +27,11 @@ public class Codegen {
|
||||
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
}
|
||||
|
||||
private static record LocalVar(int index, String name, TargetType type) {}
|
||||
private record LocalVar(int index, String name, TargetType type) {}
|
||||
|
||||
private static class Scope {
|
||||
Scope parent;
|
||||
Map<String, LocalVar> locals;
|
||||
Map<String, LocalVar> locals = new HashMap<>();
|
||||
|
||||
Scope(Scope parent) {
|
||||
this.parent = parent;
|
||||
@ -41,7 +43,7 @@ public class Codegen {
|
||||
|
||||
LocalVar get(String name) {
|
||||
var local = locals.get(name);
|
||||
if (local != null ){
|
||||
if (local != null) {
|
||||
return local;
|
||||
}
|
||||
if (parent != null) {
|
||||
@ -53,11 +55,12 @@ public class Codegen {
|
||||
|
||||
private static class State {
|
||||
Scope scope = new Scope(null);
|
||||
int localCounter = 1;
|
||||
int localCounter;
|
||||
MethodVisitor mv;
|
||||
|
||||
State(MethodVisitor mv) {
|
||||
State(MethodVisitor mv, int localCounter) {
|
||||
this.mv = mv;
|
||||
this.localCounter = localCounter;
|
||||
}
|
||||
|
||||
void enterScope() {
|
||||
@ -68,9 +71,11 @@ public class Codegen {
|
||||
this.scope = this.scope.parent;
|
||||
}
|
||||
|
||||
void createVariable(String name, TargetType type) {
|
||||
scope.add(new LocalVar(localCounter, name, type));
|
||||
LocalVar createVariable(String name, TargetType type) {
|
||||
var local = new LocalVar(localCounter, name, type);
|
||||
scope.add(local);
|
||||
localCounter += 1;
|
||||
return local;
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,9 +88,9 @@ public class Codegen {
|
||||
} else if (type.equals(TargetType.Double)) {
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
|
||||
} else if (type.equals(TargetType.Long)) {
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(L)Ljava/lang/Long;", false);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
|
||||
} else if (type.equals(TargetType.Integer)) {
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(L)Ljava/lang/Integer;", false);
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
|
||||
} else if (type.equals(TargetType.Short)) {
|
||||
@ -104,7 +109,7 @@ public class Codegen {
|
||||
} else if (type.equals(TargetType.Double)) {
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
|
||||
} else if (type.equals(TargetType.Long)) {
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()L", false);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
|
||||
} else if (type.equals(TargetType.Integer)) {
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
@ -116,12 +121,14 @@ public class Codegen {
|
||||
}
|
||||
}
|
||||
|
||||
private void generateRelationalOperator(State state, TargetBinaryOp op, int code) {
|
||||
private void generateRelationalOperator(State state, TargetRelationalOp op, int code) {
|
||||
var mv = state.mv;
|
||||
Label if_true = new Label();
|
||||
Label end = new Label();
|
||||
generate(state, op.left());
|
||||
convertTo(state, op.exprType(), op.type());
|
||||
generate(state, op.right());
|
||||
convertTo(state, op.exprType(), op.type());
|
||||
mv.visitJumpInsn(code, if_true);
|
||||
mv.visitInsn(ICONST_0);
|
||||
mv.visitJumpInsn(GOTO, end);
|
||||
@ -130,12 +137,14 @@ public class Codegen {
|
||||
mv.visitLabel(end);
|
||||
}
|
||||
|
||||
private void generateRelationalOperator(State state, TargetBinaryOp op, int cmp, int code) {
|
||||
private void generateRelationalOperator(State state, TargetRelationalOp op, int cmp, int code) {
|
||||
var mv = state.mv;
|
||||
Label if_true = new Label();
|
||||
Label end = new Label();
|
||||
generate(state, op.left());
|
||||
convertTo(state, op.left().type(), op.exprType());
|
||||
generate(state, op.right());
|
||||
convertTo(state, op.right().type(), op.exprType());
|
||||
mv.visitInsn(cmp);
|
||||
mv.visitInsn(code);
|
||||
mv.visitJumpInsn(code, if_true);
|
||||
@ -146,14 +155,82 @@ public class Codegen {
|
||||
mv.visitLabel(end);
|
||||
}
|
||||
|
||||
private void convertTo(State state, TargetType source, TargetType dest) {
|
||||
var mv = state.mv;
|
||||
if (source.equals(dest))
|
||||
return;
|
||||
if (source.equals(TargetType.Long)) {
|
||||
if (dest.equals(TargetType.Integer))
|
||||
mv.visitInsn(L2I);
|
||||
else if (dest.equals(TargetType.Float))
|
||||
mv.visitInsn(L2F);
|
||||
else if (dest.equals(TargetType.Double))
|
||||
mv.visitInsn(L2D);
|
||||
else if (dest.equals(TargetType.Byte)
|
||||
|| dest.equals(TargetType.Char)
|
||||
|| dest.equals(TargetType.Short)) {
|
||||
mv.visitInsn(L2I);
|
||||
source = TargetType.Integer;
|
||||
}
|
||||
} else if (source.equals(TargetType.Float)) {
|
||||
if (dest.equals(TargetType.Integer))
|
||||
mv.visitInsn(F2I);
|
||||
else if (dest.equals(TargetType.Double))
|
||||
mv.visitInsn(F2D);
|
||||
else if (dest.equals(TargetType.Long))
|
||||
mv.visitInsn(F2L);
|
||||
else if (dest.equals(TargetType.Byte)
|
||||
|| dest.equals(TargetType.Char)
|
||||
|| dest.equals(TargetType.Short)) {
|
||||
mv.visitInsn(F2I);
|
||||
source = TargetType.Integer;
|
||||
}
|
||||
} else if (source.equals(TargetType.Double)) {
|
||||
if (dest.equals(TargetType.Integer))
|
||||
mv.visitInsn(D2I);
|
||||
else if (dest.equals(TargetType.Float))
|
||||
mv.visitInsn(D2F);
|
||||
else if (dest.equals(TargetType.Long))
|
||||
mv.visitInsn(D2L);
|
||||
else if (dest.equals(TargetType.Byte)
|
||||
|| dest.equals(TargetType.Char)
|
||||
|| dest.equals(TargetType.Short)) {
|
||||
mv.visitInsn(D2I);
|
||||
source = TargetType.Integer;
|
||||
}
|
||||
}
|
||||
if (source.equals(TargetType.Byte)
|
||||
|| source.equals(TargetType.Char)
|
||||
|| source.equals(TargetType.Short)
|
||||
|| source.equals(TargetType.Integer)) {
|
||||
if (dest.equals(TargetType.Byte))
|
||||
mv.visitInsn(I2B);
|
||||
else if (dest.equals(TargetType.Char))
|
||||
mv.visitInsn(I2C);
|
||||
else if (dest.equals(TargetType.Short))
|
||||
mv.visitInsn(I2S);
|
||||
else if (dest.equals(TargetType.Long))
|
||||
mv.visitInsn(I2L);
|
||||
else if (dest.equals(TargetType.Float))
|
||||
mv.visitInsn(I2F);
|
||||
else if (dest.equals(TargetType.Double))
|
||||
mv.visitInsn(I2D);
|
||||
} else {
|
||||
mv.visitTypeInsn(CHECKCAST, ((TargetRefType) dest).getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void generateBinaryOp(State state, TargetBinaryOp op) {
|
||||
var mv = state.mv;
|
||||
switch (op) {
|
||||
case Add add: {
|
||||
generate(state, add.left());
|
||||
convertTo(state, add.left().type(), op.type());
|
||||
generate(state, add.right());
|
||||
convertTo(state, add.right().type(), op.type());
|
||||
var type = add.type();
|
||||
if (type.equals(TargetType.Byte)
|
||||
|| type.equals(TargetType.Char)
|
||||
|| type.equals(TargetType.Integer)
|
||||
|| type.equals(TargetType.Short)) {
|
||||
mv.visitInsn(IADD);
|
||||
@ -170,9 +247,12 @@ public class Codegen {
|
||||
}
|
||||
case Sub sub: {
|
||||
generate(state, sub.left());
|
||||
convertTo(state, sub.left().type(), op.type());
|
||||
generate(state, sub.right());
|
||||
convertTo(state, sub.right().type(), op.type());
|
||||
var type = sub.type();
|
||||
if (type.equals(TargetType.Byte)
|
||||
|| type.equals(TargetType.Char)
|
||||
|| type.equals(TargetType.Integer)
|
||||
|| type.equals(TargetType.Short)) {
|
||||
mv.visitInsn(ISUB);
|
||||
@ -189,9 +269,12 @@ public class Codegen {
|
||||
}
|
||||
case Div div: {
|
||||
generate(state, div.left());
|
||||
convertTo(state, div.left().type(), op.type());
|
||||
generate(state, div.right());
|
||||
convertTo(state, div.right().type(), op.type());
|
||||
var type = div.type();
|
||||
if (type.equals(TargetType.Byte)
|
||||
|| type.equals(TargetType.Char)
|
||||
|| type.equals(TargetType.Integer)
|
||||
|| type.equals(TargetType.Short)) {
|
||||
mv.visitInsn(IDIV);
|
||||
@ -208,9 +291,12 @@ public class Codegen {
|
||||
}
|
||||
case Mul mul: {
|
||||
generate(state, mul.left());
|
||||
convertTo(state, mul.left().type(), op.type());
|
||||
generate(state, mul.right());
|
||||
convertTo(state, mul.right().type(), op.type());
|
||||
var type = mul.type();
|
||||
if (type.equals(TargetType.Byte)
|
||||
|| type.equals(TargetType.Char)
|
||||
|| type.equals(TargetType.Integer)
|
||||
|| type.equals(TargetType.Short)) {
|
||||
mv.visitInsn(IMUL);
|
||||
@ -227,9 +313,12 @@ public class Codegen {
|
||||
}
|
||||
case Rem rem: {
|
||||
generate(state, rem.left());
|
||||
convertTo(state, rem.left().type(), op.type());
|
||||
generate(state, rem.right());
|
||||
convertTo(state, rem.right().type(), op.type());
|
||||
var type = rem.type();
|
||||
if (type.equals(TargetType.Byte)
|
||||
|| type.equals(TargetType.Char)
|
||||
|| type.equals(TargetType.Integer)
|
||||
|| type.equals(TargetType.Short)) {
|
||||
mv.visitInsn(IREM);
|
||||
@ -276,7 +365,9 @@ public class Codegen {
|
||||
}
|
||||
case BAnd band: {
|
||||
generate(state, band.left());
|
||||
convertTo(state, band.left().type(), op.type());
|
||||
generate(state, band.right());
|
||||
convertTo(state, band.right().type(), op.type());
|
||||
if (band.type().equals(TargetType.Long))
|
||||
mv.visitInsn(LAND);
|
||||
else mv.visitInsn(IAND);
|
||||
@ -284,7 +375,9 @@ public class Codegen {
|
||||
}
|
||||
case BOr bor: {
|
||||
generate(state, bor.left());
|
||||
convertTo(state, bor.left().type(), op.type());
|
||||
generate(state, bor.right());
|
||||
convertTo(state, bor.right().type(), op.type());
|
||||
if (bor.type().equals(TargetType.Long))
|
||||
mv.visitInsn(LOR);
|
||||
else mv.visitInsn(IOR);
|
||||
@ -292,7 +385,9 @@ public class Codegen {
|
||||
}
|
||||
case XOr xor: {
|
||||
generate(state, xor.left());
|
||||
convertTo(state, xor.left().type(), op.type());
|
||||
generate(state, xor.right());
|
||||
convertTo(state, xor.right().type(), op.type());
|
||||
if (xor.type().equals(TargetType.Long))
|
||||
mv.visitInsn(LXOR);
|
||||
else mv.visitInsn(IXOR);
|
||||
@ -300,7 +395,9 @@ public class Codegen {
|
||||
}
|
||||
case Shl shl: {
|
||||
generate(state, shl.left());
|
||||
convertTo(state, shl.left().type(), op.type());
|
||||
generate(state, shl.right());
|
||||
convertTo(state, shl.right().type(), op.type());
|
||||
if (shl.type().equals(TargetType.Long))
|
||||
mv.visitInsn(LSHL);
|
||||
else mv.visitInsn(ISHL);
|
||||
@ -308,7 +405,9 @@ public class Codegen {
|
||||
}
|
||||
case Shr shr: {
|
||||
generate(state, shr.left());
|
||||
convertTo(state, shr.left().type(), op.type());
|
||||
generate(state, shr.right());
|
||||
convertTo(state, shr.right().type(), op.type());
|
||||
if (shr.type().equals(TargetType.Long))
|
||||
mv.visitInsn(LSHR);
|
||||
else mv.visitInsn(ISHR);
|
||||
@ -316,14 +415,16 @@ public class Codegen {
|
||||
}
|
||||
case UShr ushr: {
|
||||
generate(state, ushr.left());
|
||||
convertTo(state, ushr.left().type(), op.type());
|
||||
generate(state, ushr.right());
|
||||
convertTo(state, ushr.right().type(), op.type());
|
||||
if (ushr.type().equals(TargetType.Long))
|
||||
mv.visitInsn(LUSHR);
|
||||
else mv.visitInsn(IUSHR);
|
||||
break;
|
||||
}
|
||||
case Greater greater: {
|
||||
var type = greater.type();
|
||||
var type = greater.exprType();
|
||||
if (type.equals(TargetType.Long)) {
|
||||
generateRelationalOperator(state, greater, LCMP, IFGT);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
@ -336,7 +437,7 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case Less less: {
|
||||
var type = less.type();
|
||||
var type = less.exprType();
|
||||
if (type.equals(TargetType.Long)) {
|
||||
generateRelationalOperator(state, less, LCMP, IFLT);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
@ -349,7 +450,7 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case GreaterOrEqual greaterOrEqual: {
|
||||
var type = greaterOrEqual.type();
|
||||
var type = greaterOrEqual.exprType();
|
||||
if (type.equals(TargetType.Long)) {
|
||||
generateRelationalOperator(state, greaterOrEqual, LCMP, IFGE);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
@ -362,7 +463,7 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case LessOrEqual lessOrEqual: {
|
||||
var type = lessOrEqual.type();
|
||||
var type = lessOrEqual.exprType();
|
||||
if (type.equals(TargetType.Long)) {
|
||||
generateRelationalOperator(state, lessOrEqual, LCMP, IFLE);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
@ -375,7 +476,7 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case Equal equal: {
|
||||
var type = equal.type();
|
||||
var type = equal.exprType();
|
||||
if (type.equals(TargetType.Long)) {
|
||||
generateRelationalOperator(state, equal, LCMP, IFEQ);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
@ -393,7 +494,7 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case NotEqual notEqual: {
|
||||
var type = notEqual.type();
|
||||
var type = notEqual.exprType();
|
||||
if (type.equals(TargetType.Long)) {
|
||||
generateRelationalOperator(state, notEqual, LCMP, IFNE);
|
||||
} else if (type.equals(TargetType.Float)) {
|
||||
@ -419,6 +520,8 @@ public class Codegen {
|
||||
private void generate(State state, TargetExpression expr) {
|
||||
var mv = state.mv;
|
||||
switch (expr) {
|
||||
case TargetClassName className:
|
||||
break; // NOP
|
||||
case TargetBlock block: {
|
||||
var localCounter = state.localCounter;
|
||||
state.enterScope();
|
||||
@ -434,18 +537,60 @@ public class Codegen {
|
||||
state.localCounter = localCounter;
|
||||
break;
|
||||
}
|
||||
case TargetVarDecl varDecl:
|
||||
state.createVariable(varDecl.name(), varDecl.varType());
|
||||
case TargetCast cast:
|
||||
generate(state, cast.expr());
|
||||
convertTo(state, cast.expr().type(), cast.type());
|
||||
break;
|
||||
case TargetInstanceOf instanceOf:
|
||||
mv.visitTypeInsn(INSTANCEOF, ((TargetRefType) instanceOf.right()).getName());
|
||||
break;
|
||||
case TargetLiteral literal:
|
||||
switch (literal) {
|
||||
case IntLiteral intLiteral:
|
||||
mv.visitLdcInsn(intLiteral.value());
|
||||
break;
|
||||
case FloatLiteral floatLiteral:
|
||||
mv.visitLdcInsn(floatLiteral.value());
|
||||
break;
|
||||
case LongLiteral longLiteral:
|
||||
mv.visitLdcInsn(longLiteral.value());
|
||||
break;
|
||||
case StringLiteral stringLiteral:
|
||||
mv.visitLdcInsn(stringLiteral.value());
|
||||
break;
|
||||
case CharLiteral charLiteral:
|
||||
mv.visitIntInsn(BIPUSH, charLiteral.value());
|
||||
break;
|
||||
case DoubleLiteral doubleLiteral:
|
||||
mv.visitLdcInsn(doubleLiteral.value());
|
||||
break;
|
||||
case BooleanLiteral booleanLiteral:
|
||||
if (booleanLiteral.value()) {
|
||||
mv.visitInsn(ICONST_1);
|
||||
} else {
|
||||
mv.visitInsn(ICONST_0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TargetVarDecl varDecl: {
|
||||
var local = state.createVariable(varDecl.name(), varDecl.varType());
|
||||
generate(state, varDecl.value());
|
||||
boxPrimitive(state, varDecl.varType());
|
||||
mv.visitVarInsn(ASTORE, local.index());
|
||||
break;
|
||||
}
|
||||
case TargetBinaryOp op:
|
||||
generateBinaryOp(state, op);
|
||||
break;
|
||||
case TargetAssign assign: {
|
||||
switch (assign.left()) {
|
||||
case TargetLocalVar local: {
|
||||
case TargetLocalVar localVar: {
|
||||
generate(state, assign.right());
|
||||
boxPrimitive(state, assign.right().type());
|
||||
mv.visitInsn(ASTORE);
|
||||
var local = state.scope.get(localVar.name());
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitVarInsn(ASTORE, local.index());
|
||||
break;
|
||||
}
|
||||
case TargetFieldVar dot: {
|
||||
@ -456,7 +601,7 @@ public class Codegen {
|
||||
if (dot.isStatic())
|
||||
mv.visitInsn(DUP);
|
||||
else mv.visitInsn(DUP_X1);
|
||||
mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.right(), type.name(), type.toSignature());
|
||||
mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.right(), type.getName(), type.toSignature());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -474,7 +619,7 @@ public class Codegen {
|
||||
if (!dot.isStatic())
|
||||
generate(state, dot.left());
|
||||
var type = (TargetRefType) dot.left().type();
|
||||
mv.visitFieldInsn(dot.isStatic() ? GETSTATIC : GETFIELD, type.name(), dot.right(), dot.type().toSignature());
|
||||
mv.visitFieldInsn(dot.isStatic() ? GETSTATIC : GETFIELD, type.getName(), dot.right(), dot.type().toSignature());
|
||||
break;
|
||||
}
|
||||
case TargetFor _for: {
|
||||
@ -528,9 +673,11 @@ public class Codegen {
|
||||
}
|
||||
case TargetReturn ret: {
|
||||
generate(state, ret.expression());
|
||||
if (ret.expression() != null)
|
||||
|
||||
if (ret.expression() != null) {
|
||||
boxPrimitive(state, ret.expression().type());
|
||||
mv.visitInsn(ARETURN);
|
||||
else mv.visitInsn(RETURN);
|
||||
} else mv.visitInsn(RETURN);
|
||||
break;
|
||||
}
|
||||
case TargetThis _this: {
|
||||
@ -542,13 +689,12 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case TargetMethodCall call: {
|
||||
var method = call.method();
|
||||
generate(state, call.expr());
|
||||
for (TargetExpression e : call.args()) {
|
||||
generate(state, e);
|
||||
boxPrimitive(state, e.type());
|
||||
}
|
||||
mv.visitMethodInsn(method.isStatic() ? INVOKESTATIC: INVOKEVIRTUAL, method.owner().qualifiedName(), method.name(), method.getDescriptor(), false);
|
||||
mv.visitMethodInsn(call.isStatic() ? INVOKESTATIC: INVOKEVIRTUAL, call.owner().getName(), call.name(), call.descriptor(), false);
|
||||
break;
|
||||
}
|
||||
case TargetLambdaExpression lambda:
|
||||
@ -566,7 +712,11 @@ public class Codegen {
|
||||
private void generateConstructor(TargetConstructor constructor) {
|
||||
MethodVisitor mv = cw.visitMethod(constructor.access(), "<init>", constructor.getDescriptor(), null, null);
|
||||
mv.visitCode();
|
||||
generate(new State(mv), constructor.block());
|
||||
var state = new State(mv, 1);
|
||||
for (var param: constructor.parameters())
|
||||
state.createVariable(param.name(), param.type());
|
||||
generate(state, constructor.block());
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
@ -574,18 +724,32 @@ public class Codegen {
|
||||
private void generateMethod(TargetMethod method) {
|
||||
MethodVisitor mv = cw.visitMethod(method.access(), method.name(), method.getDescriptor(), null, null);
|
||||
mv.visitCode();
|
||||
generate(new State(mv), method.block());
|
||||
var state = new State(mv, method.isStatic() ? 0 : 1);
|
||||
for (var param: method.parameters())
|
||||
state.createVariable(param.name(), param.type());
|
||||
generate(state, method.block());
|
||||
if (method.returnType() == null)
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
public byte[] generate() {
|
||||
cw.visit(V1_8, clazz.modifiers(), clazz.qualifiedName(),
|
||||
null, clazz.superType().toSignature(),
|
||||
null, ((TargetRefType) clazz.superType()).getName(),
|
||||
clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)
|
||||
);
|
||||
clazz.fields().forEach(this::generateField);
|
||||
clazz.constructors().forEach(this::generateConstructor);
|
||||
if (clazz.constructors().size() == 0) {
|
||||
var mv = cw.visitMethod(ACC_PROTECTED, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, ((TargetRefType) clazz.superType()).getName(), "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
}
|
||||
else clazz.constructors().forEach(this::generateConstructor);
|
||||
clazz.methods().forEach(this::generateMethod);
|
||||
|
||||
cw.visitEnd();
|
||||
|
@ -31,7 +31,7 @@ public class ASTToTargetAST {
|
||||
List<MethodParameter> params = input.getParameterList().getFormalparalist().stream()
|
||||
.map(param -> new MethodParameter(convert(param.getType(), sigma), param.getName())).collect(Collectors.toList());
|
||||
// TODO
|
||||
return new TargetMethod(0, null, input.name, params, convert(input.block));
|
||||
return new TargetMethod(0, null, input.name, params, null, convert(input.block));
|
||||
}
|
||||
|
||||
private TargetBlock convert(Block block) {
|
||||
|
@ -17,16 +17,16 @@ public record TargetClass(int modifiers, String qualifiedName, TargetType superT
|
||||
this(modifiers, qualifiedName, TargetType.Object, implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
|
||||
}
|
||||
|
||||
public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetBlock block) {
|
||||
this.methods.add(new TargetMethod(access, this, name, parameterTypes, block));
|
||||
public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetType returnType, TargetBlock block) {
|
||||
this.methods.add(new TargetMethod(access, new TargetRefType(this.qualifiedName, List.of()), name, parameterTypes, returnType, block));
|
||||
}
|
||||
|
||||
public void addConstructor(int access, List<MethodParameter> paramterTypes, TargetBlock block) {
|
||||
this.constructors.add(new TargetConstructor(access, this, paramterTypes, block));
|
||||
this.constructors.add(new TargetConstructor(access, new TargetRefType(this.qualifiedName, List.of()), paramterTypes, block));
|
||||
}
|
||||
|
||||
public void addField(int access, TargetType type, String name) {
|
||||
this.fields.add(new TargetField(access, this, type, name));
|
||||
this.fields.add(new TargetField(access, new TargetRefType(this.qualifiedName, List.of()), type, name));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
package de.dhbwstuttgart.target.tree;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record TargetConstructor(int access, TargetClass owner, List<MethodParameter> parameterTypes, TargetBlock block) {
|
||||
public record TargetConstructor(int access, TargetRefType owner, List<MethodParameter> parameters, TargetBlock block) {
|
||||
|
||||
public String getDescriptor() {
|
||||
// TODO
|
||||
|
@ -1,9 +1,10 @@
|
||||
package de.dhbwstuttgart.target.tree;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
public record TargetField(int access, TargetClass owner, TargetType type, String name) {
|
||||
public record TargetField(int access, TargetRefType owner, TargetType type, String name) {
|
||||
public String getDescriptor() {
|
||||
// TODO
|
||||
return null;
|
||||
|
@ -1,14 +1,30 @@
|
||||
package de.dhbwstuttgart.target.tree;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public record TargetMethod(int access, TargetRefType owner, String name, List<MethodParameter> parameters, TargetType returnType, TargetBlock block) {
|
||||
public static String getDescriptor(TargetType returnType, TargetType... parameters) {
|
||||
String ret = "(";
|
||||
for (var parameterType : parameters) {
|
||||
ret += parameterType.toSignature();
|
||||
}
|
||||
ret += ")";
|
||||
if (returnType == null) {
|
||||
ret += "V";
|
||||
} else {
|
||||
ret += returnType.toSignature();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public record TargetMethod(int access, TargetClass owner, String name, List<MethodParameter> parameterTypes, TargetBlock block) {
|
||||
public String getDescriptor() {
|
||||
// TODO
|
||||
return null;
|
||||
return getDescriptor(returnType, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
|
@ -26,13 +26,21 @@ public sealed interface TargetBinaryOp extends TargetExpression {
|
||||
record And(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
record Or(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
|
||||
sealed interface TargetRelationalOp extends TargetBinaryOp {
|
||||
@Override
|
||||
default TargetType type() {
|
||||
return TargetType.Boolean;
|
||||
}
|
||||
TargetType exprType();
|
||||
}
|
||||
|
||||
// Comparison
|
||||
record Equal(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
record Greater(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
record GreaterOrEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
record Less(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
record LessOrEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
record NotEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
|
||||
record Equal(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||
record Greater(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||
record GreaterOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||
record Less(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||
record LessOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||
record NotEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,14 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
public sealed interface TargetLiteral extends TargetExpression {
|
||||
Object value();
|
||||
|
||||
record CharLiteral(Integer value) implements TargetLiteral {
|
||||
record BooleanLiteral(Boolean value) implements TargetLiteral {
|
||||
@Override
|
||||
public TargetType type() {
|
||||
return TargetType.Boolean;
|
||||
}
|
||||
}
|
||||
|
||||
record CharLiteral(Character value) implements TargetLiteral {
|
||||
@Override
|
||||
public TargetType type() {
|
||||
return TargetType.Char;
|
||||
@ -26,6 +33,20 @@ public sealed interface TargetLiteral extends TargetExpression {
|
||||
}
|
||||
}
|
||||
|
||||
record FloatLiteral(Float value) implements TargetLiteral {
|
||||
@Override
|
||||
public TargetType type() {
|
||||
return TargetType.Float;
|
||||
}
|
||||
}
|
||||
|
||||
record DoubleLiteral(Double value) implements TargetLiteral {
|
||||
@Override
|
||||
public TargetType type() {
|
||||
return TargetType.Double;
|
||||
}
|
||||
}
|
||||
|
||||
record StringLiteral(String value) implements TargetLiteral {
|
||||
@Override
|
||||
public TargetType type() {
|
||||
|
@ -1,9 +1,10 @@
|
||||
package de.dhbwstuttgart.target.tree.expression;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record TargetMethodCall(TargetType type, TargetExpression expr, List<TargetExpression> args, TargetMethod method) implements TargetStatementExpression {
|
||||
public record TargetMethodCall(TargetType type, TargetExpression expr, List<TargetExpression> args, TargetRefType owner, String name, String descriptor, boolean isStatic) implements TargetStatementExpression {
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package de.dhbwstuttgart.target.tree.expression;
|
||||
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
|
||||
public record TargetVarDecl(String name, TargetType varType) implements TargetExpression {
|
||||
public record TargetVarDecl(TargetType varType, String name, TargetExpression value) implements TargetExpression {
|
||||
|
||||
@Override
|
||||
public TargetType type() {
|
||||
|
@ -5,8 +5,16 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
import java.util.List;
|
||||
|
||||
public record TargetRefType(String name, List<TargetType> params) implements TargetType {
|
||||
public TargetRefType(String name) {
|
||||
this(name, List.of());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name.replaceAll("\\.", "/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSignature() {
|
||||
return "L" + this.name.replaceAll("\\.", "/") + ";";
|
||||
return "L" + getName() + ";";
|
||||
}
|
||||
}
|
||||
|
@ -6,16 +6,16 @@ public sealed interface TargetType
|
||||
permits TargetExtendsWildcard, TargetFunNType, TargetGenericType, TargetRefType, TargetSuperWildcard {
|
||||
|
||||
// Builtin types
|
||||
TargetRefType Boolean = new TargetRefType("java.lang.Boolean", List.of());
|
||||
TargetRefType Char = new TargetRefType("java.lang.Character", List.of());
|
||||
TargetRefType Byte = new TargetRefType("java.lang.Byte", List.of());
|
||||
TargetRefType Short = new TargetRefType("java.lang.Short", List.of());
|
||||
TargetRefType Integer = new TargetRefType("java.lang.Integer", List.of());
|
||||
TargetRefType Long = new TargetRefType("java.lang.Long", List.of());
|
||||
TargetRefType Float = new TargetRefType("java.lang.Float", List.of());
|
||||
TargetRefType Double = new TargetRefType("java.lang.Double", List.of());
|
||||
TargetRefType String = new TargetRefType("java.lang.String", List.of());
|
||||
TargetRefType Object = new TargetRefType("java.lang.Object", List.of());
|
||||
TargetRefType Boolean = new TargetRefType("java.lang.Boolean");
|
||||
TargetRefType Char = new TargetRefType("java.lang.Character");
|
||||
TargetRefType Byte = new TargetRefType("java.lang.Byte");
|
||||
TargetRefType Short = new TargetRefType("java.lang.Short");
|
||||
TargetRefType Integer = new TargetRefType("java.lang.Integer");
|
||||
TargetRefType Long = new TargetRefType("java.lang.Long");
|
||||
TargetRefType Float = new TargetRefType("java.lang.Float");
|
||||
TargetRefType Double = new TargetRefType("java.lang.Double");
|
||||
TargetRefType String = new TargetRefType("java.lang.String");
|
||||
TargetRefType Object = new TargetRefType("java.lang.Object");
|
||||
|
||||
String toSignature();
|
||||
}
|
||||
|
@ -1,25 +1,193 @@
|
||||
package targetast;
|
||||
|
||||
import de.dhbwstuttgart.target.bytecode.Codegen;
|
||||
import de.dhbwstuttgart.target.tree.MethodParameter;
|
||||
import de.dhbwstuttgart.target.tree.TargetClass;
|
||||
import de.dhbwstuttgart.target.tree.TargetMethod;
|
||||
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
|
||||
import de.dhbwstuttgart.target.tree.expression.*;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetRefType;
|
||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.*;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class TestCodegen {
|
||||
|
||||
private static Class generateClass(TargetClass clazz) {
|
||||
private static Class generateClass(TargetClass clazz) throws IOException {
|
||||
var codegen = new Codegen(clazz);
|
||||
return new ByteArrayClassLoader().loadClass(codegen.generate());
|
||||
var bytes = codegen.generate();
|
||||
var path = Path.of(System.getProperty("user.dir"), "src/test/resources/target/");
|
||||
Files.createDirectories(path);
|
||||
Files.write(path.resolve(clazz.qualifiedName() + ".class"), bytes, StandardOpenOption.CREATE);
|
||||
return new ByteArrayClassLoader().loadClass(bytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyClass() throws Exception {
|
||||
var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Main");
|
||||
clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), new TargetBlock(List.of()));
|
||||
var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Empty");
|
||||
clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), null, new TargetBlock(List.of()));
|
||||
generateClass(clazz).getDeclaredMethod("main").invoke(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArithmetic() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Arithmetic");
|
||||
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add",
|
||||
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
|
||||
TargetType.Integer,
|
||||
new TargetBlock(List.of(new TargetReturn(
|
||||
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
|
||||
))
|
||||
);
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "sub",
|
||||
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
|
||||
TargetType.Integer,
|
||||
new TargetBlock(List.of(new TargetReturn(
|
||||
new TargetBinaryOp.Sub(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
|
||||
))
|
||||
);
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "div",
|
||||
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
|
||||
TargetType.Integer,
|
||||
new TargetBlock(List.of(new TargetReturn(
|
||||
new TargetBinaryOp.Div(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
|
||||
))
|
||||
);
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "mul",
|
||||
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
|
||||
TargetType.Integer,
|
||||
new TargetBlock(List.of(new TargetReturn(
|
||||
new TargetBinaryOp.Mul(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
|
||||
))
|
||||
);
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "rem",
|
||||
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
|
||||
TargetType.Integer,
|
||||
new TargetBlock(List.of(new TargetReturn(
|
||||
new TargetBinaryOp.Rem(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
|
||||
))
|
||||
);
|
||||
|
||||
|
||||
var clazz = generateClass(targetClass);
|
||||
assertEquals(clazz.getDeclaredMethod("add", Integer.class, Integer.class).invoke(null, 10, 10), 20);
|
||||
assertEquals(clazz.getDeclaredMethod("sub", Integer.class, Integer.class).invoke(null, 20, 10), 10);
|
||||
assertEquals(clazz.getDeclaredMethod("div", Integer.class, Integer.class).invoke(null, 20, 10), 2);
|
||||
assertEquals(clazz.getDeclaredMethod("mul", Integer.class, Integer.class).invoke(null, 20, 10), 200);
|
||||
assertEquals(clazz.getDeclaredMethod("rem", Integer.class, Integer.class).invoke(null, 10, 3), 1);
|
||||
}
|
||||
|
||||
// When adding two numbers and the return type is Long it needs to convert both values to Long
|
||||
@Test
|
||||
public void testArithmeticConvert() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "ArithmeticConvert");
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add", List.of(), TargetType.Long,
|
||||
new TargetBlock(List.of(new TargetReturn(
|
||||
new TargetBinaryOp.Add(TargetType.Long, new TargetLiteral.CharLiteral((char)10), new TargetLiteral.LongLiteral((long)20))
|
||||
)))
|
||||
);
|
||||
var clazz = generateClass(targetClass);
|
||||
assertEquals(clazz.getDeclaredMethod("add").invoke(null), (long)30);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodCall() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "HelloWorld");
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "helloWorld", List.of(), null,
|
||||
new TargetBlock(List.of(new TargetMethodCall(null,
|
||||
new TargetFieldVar(
|
||||
new TargetRefType("java.io.PrintStream"), true,
|
||||
new TargetClassName(new TargetRefType("java.lang.System")),
|
||||
"out"
|
||||
),
|
||||
List.of(new TargetLiteral.StringLiteral("Hello World!")),
|
||||
new TargetRefType("java.io.PrintStream"),
|
||||
"println",
|
||||
TargetMethod.getDescriptor(null, TargetType.String),
|
||||
false
|
||||
)))
|
||||
);
|
||||
|
||||
var clazz = generateClass(targetClass);
|
||||
clazz.getDeclaredMethod("helloWorld").invoke(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIfStatement() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "IfStmt");
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "ifStmt",
|
||||
List.of(new MethodParameter(TargetType.Integer, "val")),
|
||||
TargetType.Integer,
|
||||
new TargetBlock(List.of(new TargetIf(
|
||||
new TargetBinaryOp.Equal(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(10)),
|
||||
new TargetReturn(new TargetLiteral.IntLiteral(1)),
|
||||
new TargetIf(
|
||||
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(5)),
|
||||
new TargetReturn(new TargetLiteral.IntLiteral(2)),
|
||||
new TargetReturn(new TargetLiteral.IntLiteral(3))
|
||||
)
|
||||
)))
|
||||
);
|
||||
var clazz = generateClass(targetClass);
|
||||
var ifStmt = clazz.getDeclaredMethod("ifStmt", Integer.class);
|
||||
assertEquals(ifStmt.invoke(null, 10), 1);
|
||||
assertEquals(ifStmt.invoke(null, 3), 2);
|
||||
assertEquals(ifStmt.invoke(null, 20), 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFor() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "For");
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer,
|
||||
new TargetBlock(List.of(
|
||||
new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)),
|
||||
new TargetFor(
|
||||
new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)),
|
||||
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)),
|
||||
new TargetAssign(TargetType.Integer,
|
||||
new TargetLocalVar(TargetType.Integer, "i"),
|
||||
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1))),
|
||||
new TargetBlock(List.of(
|
||||
new TargetAssign(TargetType.Integer,
|
||||
new TargetLocalVar(TargetType.Integer, "sum"),
|
||||
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetLocalVar(TargetType.Integer, "i"))
|
||||
)
|
||||
))
|
||||
),
|
||||
new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum"))
|
||||
))
|
||||
);
|
||||
var clazz = generateClass(targetClass);
|
||||
assertEquals(clazz.getDeclaredMethod("forLoop").invoke(null), 45);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhile() throws Exception {
|
||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "While");
|
||||
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "whileLoop", List.of(), TargetType.Integer,
|
||||
new TargetBlock(List.of(
|
||||
new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)),
|
||||
new TargetWhile(
|
||||
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)),
|
||||
new TargetBlock(List.of(
|
||||
new TargetAssign(TargetType.Integer,
|
||||
new TargetLocalVar(TargetType.Integer, "i"),
|
||||
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1))
|
||||
)
|
||||
))
|
||||
),
|
||||
new TargetReturn(new TargetLocalVar(TargetType.Integer, "i"))
|
||||
))
|
||||
);
|
||||
var clazz = generateClass(targetClass);
|
||||
assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user