More tests

This commit is contained in:
Victorious3 2022-05-16 14:18:58 +02:00
parent 8666bfdf17
commit 6c584f92e9
13 changed files with 455 additions and 67 deletions

View File

@ -11,10 +11,12 @@ import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.Opcodes.*;
import static de.dhbwstuttgart.target.tree.expression.TargetBinaryOp.*; import static de.dhbwstuttgart.target.tree.expression.TargetBinaryOp.*;
import static de.dhbwstuttgart.target.tree.expression.TargetLiteral.*;
public class Codegen { public class Codegen {
private TargetClass clazz; private TargetClass clazz;
@ -25,11 +27,11 @@ public class Codegen {
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 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 { private static class Scope {
Scope parent; Scope parent;
Map<String, LocalVar> locals; Map<String, LocalVar> locals = new HashMap<>();
Scope(Scope parent) { Scope(Scope parent) {
this.parent = parent; this.parent = parent;
@ -41,7 +43,7 @@ public class Codegen {
LocalVar get(String name) { LocalVar get(String name) {
var local = locals.get(name); var local = locals.get(name);
if (local != null ){ if (local != null) {
return local; return local;
} }
if (parent != null) { if (parent != null) {
@ -53,11 +55,12 @@ public class Codegen {
private static class State { private static class State {
Scope scope = new Scope(null); Scope scope = new Scope(null);
int localCounter = 1; int localCounter;
MethodVisitor mv; MethodVisitor mv;
State(MethodVisitor mv) { State(MethodVisitor mv, int localCounter) {
this.mv = mv; this.mv = mv;
this.localCounter = localCounter;
} }
void enterScope() { void enterScope() {
@ -68,9 +71,11 @@ public class Codegen {
this.scope = this.scope.parent; this.scope = this.scope.parent;
} }
void createVariable(String name, TargetType type) { LocalVar createVariable(String name, TargetType type) {
scope.add(new LocalVar(localCounter, name, type)); var local = new LocalVar(localCounter, name, type);
scope.add(local);
localCounter += 1; localCounter += 1;
return local;
} }
} }
@ -83,9 +88,9 @@ public class Codegen {
} else if (type.equals(TargetType.Double)) { } else if (type.equals(TargetType.Double)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
} else if (type.equals(TargetType.Long)) { } 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)) { } 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)) { } else if (type.equals(TargetType.Float)) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
} else if (type.equals(TargetType.Short)) { } else if (type.equals(TargetType.Short)) {
@ -104,7 +109,7 @@ public class Codegen {
} else if (type.equals(TargetType.Double)) { } else if (type.equals(TargetType.Double)) {
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
} else if (type.equals(TargetType.Long)) { } 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)) { } else if (type.equals(TargetType.Integer)) {
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
} else if (type.equals(TargetType.Float)) { } 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; var mv = state.mv;
Label if_true = new Label(); Label if_true = new Label();
Label end = new Label(); Label end = new Label();
generate(state, op.left()); generate(state, op.left());
convertTo(state, op.exprType(), op.type());
generate(state, op.right()); generate(state, op.right());
convertTo(state, op.exprType(), op.type());
mv.visitJumpInsn(code, if_true); mv.visitJumpInsn(code, if_true);
mv.visitInsn(ICONST_0); mv.visitInsn(ICONST_0);
mv.visitJumpInsn(GOTO, end); mv.visitJumpInsn(GOTO, end);
@ -130,12 +137,14 @@ public class Codegen {
mv.visitLabel(end); 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; var mv = state.mv;
Label if_true = new Label(); Label if_true = new Label();
Label end = new Label(); Label end = new Label();
generate(state, op.left()); generate(state, op.left());
convertTo(state, op.left().type(), op.exprType());
generate(state, op.right()); generate(state, op.right());
convertTo(state, op.right().type(), op.exprType());
mv.visitInsn(cmp); mv.visitInsn(cmp);
mv.visitInsn(code); mv.visitInsn(code);
mv.visitJumpInsn(code, if_true); mv.visitJumpInsn(code, if_true);
@ -146,14 +155,82 @@ public class Codegen {
mv.visitLabel(end); 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) { private void generateBinaryOp(State state, TargetBinaryOp op) {
var mv = state.mv; var mv = state.mv;
switch (op) { switch (op) {
case Add add: { case Add add: {
generate(state, add.left()); generate(state, add.left());
convertTo(state, add.left().type(), op.type());
generate(state, add.right()); generate(state, add.right());
convertTo(state, add.right().type(), op.type());
var type = add.type(); var type = add.type();
if (type.equals(TargetType.Byte) if (type.equals(TargetType.Byte)
|| type.equals(TargetType.Char)
|| type.equals(TargetType.Integer) || type.equals(TargetType.Integer)
|| type.equals(TargetType.Short)) { || type.equals(TargetType.Short)) {
mv.visitInsn(IADD); mv.visitInsn(IADD);
@ -170,9 +247,12 @@ public class Codegen {
} }
case Sub sub: { case Sub sub: {
generate(state, sub.left()); generate(state, sub.left());
convertTo(state, sub.left().type(), op.type());
generate(state, sub.right()); generate(state, sub.right());
convertTo(state, sub.right().type(), op.type());
var type = sub.type(); var type = sub.type();
if (type.equals(TargetType.Byte) if (type.equals(TargetType.Byte)
|| type.equals(TargetType.Char)
|| type.equals(TargetType.Integer) || type.equals(TargetType.Integer)
|| type.equals(TargetType.Short)) { || type.equals(TargetType.Short)) {
mv.visitInsn(ISUB); mv.visitInsn(ISUB);
@ -189,9 +269,12 @@ public class Codegen {
} }
case Div div: { case Div div: {
generate(state, div.left()); generate(state, div.left());
convertTo(state, div.left().type(), op.type());
generate(state, div.right()); generate(state, div.right());
convertTo(state, div.right().type(), op.type());
var type = div.type(); var type = div.type();
if (type.equals(TargetType.Byte) if (type.equals(TargetType.Byte)
|| type.equals(TargetType.Char)
|| type.equals(TargetType.Integer) || type.equals(TargetType.Integer)
|| type.equals(TargetType.Short)) { || type.equals(TargetType.Short)) {
mv.visitInsn(IDIV); mv.visitInsn(IDIV);
@ -208,9 +291,12 @@ public class Codegen {
} }
case Mul mul: { case Mul mul: {
generate(state, mul.left()); generate(state, mul.left());
convertTo(state, mul.left().type(), op.type());
generate(state, mul.right()); generate(state, mul.right());
convertTo(state, mul.right().type(), op.type());
var type = mul.type(); var type = mul.type();
if (type.equals(TargetType.Byte) if (type.equals(TargetType.Byte)
|| type.equals(TargetType.Char)
|| type.equals(TargetType.Integer) || type.equals(TargetType.Integer)
|| type.equals(TargetType.Short)) { || type.equals(TargetType.Short)) {
mv.visitInsn(IMUL); mv.visitInsn(IMUL);
@ -227,9 +313,12 @@ public class Codegen {
} }
case Rem rem: { case Rem rem: {
generate(state, rem.left()); generate(state, rem.left());
convertTo(state, rem.left().type(), op.type());
generate(state, rem.right()); generate(state, rem.right());
convertTo(state, rem.right().type(), op.type());
var type = rem.type(); var type = rem.type();
if (type.equals(TargetType.Byte) if (type.equals(TargetType.Byte)
|| type.equals(TargetType.Char)
|| type.equals(TargetType.Integer) || type.equals(TargetType.Integer)
|| type.equals(TargetType.Short)) { || type.equals(TargetType.Short)) {
mv.visitInsn(IREM); mv.visitInsn(IREM);
@ -276,7 +365,9 @@ public class Codegen {
} }
case BAnd band: { case BAnd band: {
generate(state, band.left()); generate(state, band.left());
convertTo(state, band.left().type(), op.type());
generate(state, band.right()); generate(state, band.right());
convertTo(state, band.right().type(), op.type());
if (band.type().equals(TargetType.Long)) if (band.type().equals(TargetType.Long))
mv.visitInsn(LAND); mv.visitInsn(LAND);
else mv.visitInsn(IAND); else mv.visitInsn(IAND);
@ -284,7 +375,9 @@ public class Codegen {
} }
case BOr bor: { case BOr bor: {
generate(state, bor.left()); generate(state, bor.left());
convertTo(state, bor.left().type(), op.type());
generate(state, bor.right()); generate(state, bor.right());
convertTo(state, bor.right().type(), op.type());
if (bor.type().equals(TargetType.Long)) if (bor.type().equals(TargetType.Long))
mv.visitInsn(LOR); mv.visitInsn(LOR);
else mv.visitInsn(IOR); else mv.visitInsn(IOR);
@ -292,7 +385,9 @@ public class Codegen {
} }
case XOr xor: { case XOr xor: {
generate(state, xor.left()); generate(state, xor.left());
convertTo(state, xor.left().type(), op.type());
generate(state, xor.right()); generate(state, xor.right());
convertTo(state, xor.right().type(), op.type());
if (xor.type().equals(TargetType.Long)) if (xor.type().equals(TargetType.Long))
mv.visitInsn(LXOR); mv.visitInsn(LXOR);
else mv.visitInsn(IXOR); else mv.visitInsn(IXOR);
@ -300,7 +395,9 @@ public class Codegen {
} }
case Shl shl: { case Shl shl: {
generate(state, shl.left()); generate(state, shl.left());
convertTo(state, shl.left().type(), op.type());
generate(state, shl.right()); generate(state, shl.right());
convertTo(state, shl.right().type(), op.type());
if (shl.type().equals(TargetType.Long)) if (shl.type().equals(TargetType.Long))
mv.visitInsn(LSHL); mv.visitInsn(LSHL);
else mv.visitInsn(ISHL); else mv.visitInsn(ISHL);
@ -308,7 +405,9 @@ public class Codegen {
} }
case Shr shr: { case Shr shr: {
generate(state, shr.left()); generate(state, shr.left());
convertTo(state, shr.left().type(), op.type());
generate(state, shr.right()); generate(state, shr.right());
convertTo(state, shr.right().type(), op.type());
if (shr.type().equals(TargetType.Long)) if (shr.type().equals(TargetType.Long))
mv.visitInsn(LSHR); mv.visitInsn(LSHR);
else mv.visitInsn(ISHR); else mv.visitInsn(ISHR);
@ -316,14 +415,16 @@ public class Codegen {
} }
case UShr ushr: { case UShr ushr: {
generate(state, ushr.left()); generate(state, ushr.left());
convertTo(state, ushr.left().type(), op.type());
generate(state, ushr.right()); generate(state, ushr.right());
convertTo(state, ushr.right().type(), op.type());
if (ushr.type().equals(TargetType.Long)) if (ushr.type().equals(TargetType.Long))
mv.visitInsn(LUSHR); mv.visitInsn(LUSHR);
else mv.visitInsn(IUSHR); else mv.visitInsn(IUSHR);
break; break;
} }
case Greater greater: { case Greater greater: {
var type = greater.type(); var type = greater.exprType();
if (type.equals(TargetType.Long)) { if (type.equals(TargetType.Long)) {
generateRelationalOperator(state, greater, LCMP, IFGT); generateRelationalOperator(state, greater, LCMP, IFGT);
} else if (type.equals(TargetType.Float)) { } else if (type.equals(TargetType.Float)) {
@ -336,7 +437,7 @@ public class Codegen {
break; break;
} }
case Less less: { case Less less: {
var type = less.type(); var type = less.exprType();
if (type.equals(TargetType.Long)) { if (type.equals(TargetType.Long)) {
generateRelationalOperator(state, less, LCMP, IFLT); generateRelationalOperator(state, less, LCMP, IFLT);
} else if (type.equals(TargetType.Float)) { } else if (type.equals(TargetType.Float)) {
@ -349,7 +450,7 @@ public class Codegen {
break; break;
} }
case GreaterOrEqual greaterOrEqual: { case GreaterOrEqual greaterOrEqual: {
var type = greaterOrEqual.type(); var type = greaterOrEqual.exprType();
if (type.equals(TargetType.Long)) { if (type.equals(TargetType.Long)) {
generateRelationalOperator(state, greaterOrEqual, LCMP, IFGE); generateRelationalOperator(state, greaterOrEqual, LCMP, IFGE);
} else if (type.equals(TargetType.Float)) { } else if (type.equals(TargetType.Float)) {
@ -362,7 +463,7 @@ public class Codegen {
break; break;
} }
case LessOrEqual lessOrEqual: { case LessOrEqual lessOrEqual: {
var type = lessOrEqual.type(); var type = lessOrEqual.exprType();
if (type.equals(TargetType.Long)) { if (type.equals(TargetType.Long)) {
generateRelationalOperator(state, lessOrEqual, LCMP, IFLE); generateRelationalOperator(state, lessOrEqual, LCMP, IFLE);
} else if (type.equals(TargetType.Float)) { } else if (type.equals(TargetType.Float)) {
@ -375,7 +476,7 @@ public class Codegen {
break; break;
} }
case Equal equal: { case Equal equal: {
var type = equal.type(); var type = equal.exprType();
if (type.equals(TargetType.Long)) { if (type.equals(TargetType.Long)) {
generateRelationalOperator(state, equal, LCMP, IFEQ); generateRelationalOperator(state, equal, LCMP, IFEQ);
} else if (type.equals(TargetType.Float)) { } else if (type.equals(TargetType.Float)) {
@ -393,7 +494,7 @@ public class Codegen {
break; break;
} }
case NotEqual notEqual: { case NotEqual notEqual: {
var type = notEqual.type(); var type = notEqual.exprType();
if (type.equals(TargetType.Long)) { if (type.equals(TargetType.Long)) {
generateRelationalOperator(state, notEqual, LCMP, IFNE); generateRelationalOperator(state, notEqual, LCMP, IFNE);
} else if (type.equals(TargetType.Float)) { } else if (type.equals(TargetType.Float)) {
@ -419,6 +520,8 @@ public class Codegen {
private void generate(State state, TargetExpression expr) { private void generate(State state, TargetExpression expr) {
var mv = state.mv; var mv = state.mv;
switch (expr) { switch (expr) {
case TargetClassName className:
break; // NOP
case TargetBlock block: { case TargetBlock block: {
var localCounter = state.localCounter; var localCounter = state.localCounter;
state.enterScope(); state.enterScope();
@ -434,18 +537,60 @@ public class Codegen {
state.localCounter = localCounter; state.localCounter = localCounter;
break; break;
} }
case TargetVarDecl varDecl: case TargetCast cast:
state.createVariable(varDecl.name(), varDecl.varType()); generate(state, cast.expr());
convertTo(state, cast.expr().type(), cast.type());
break; 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: case TargetBinaryOp op:
generateBinaryOp(state, op); generateBinaryOp(state, op);
break; break;
case TargetAssign assign: { case TargetAssign assign: {
switch (assign.left()) { switch (assign.left()) {
case TargetLocalVar local: { case TargetLocalVar localVar: {
generate(state, assign.right()); generate(state, assign.right());
boxPrimitive(state, assign.right().type()); boxPrimitive(state, assign.right().type());
mv.visitInsn(ASTORE); var local = state.scope.get(localVar.name());
mv.visitInsn(DUP);
mv.visitVarInsn(ASTORE, local.index());
break; break;
} }
case TargetFieldVar dot: { case TargetFieldVar dot: {
@ -456,7 +601,7 @@ public class Codegen {
if (dot.isStatic()) if (dot.isStatic())
mv.visitInsn(DUP); mv.visitInsn(DUP);
else mv.visitInsn(DUP_X1); 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; break;
} }
default: default:
@ -474,7 +619,7 @@ public class Codegen {
if (!dot.isStatic()) if (!dot.isStatic())
generate(state, dot.left()); generate(state, dot.left());
var type = (TargetRefType) dot.left().type(); 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; break;
} }
case TargetFor _for: { case TargetFor _for: {
@ -528,9 +673,11 @@ public class Codegen {
} }
case TargetReturn ret: { case TargetReturn ret: {
generate(state, ret.expression()); generate(state, ret.expression());
if (ret.expression() != null)
if (ret.expression() != null) {
boxPrimitive(state, ret.expression().type());
mv.visitInsn(ARETURN); mv.visitInsn(ARETURN);
else mv.visitInsn(RETURN); } else mv.visitInsn(RETURN);
break; break;
} }
case TargetThis _this: { case TargetThis _this: {
@ -542,13 +689,12 @@ public class Codegen {
break; break;
} }
case TargetMethodCall call: { case TargetMethodCall call: {
var method = call.method();
generate(state, call.expr()); generate(state, call.expr());
for (TargetExpression e : call.args()) { for (TargetExpression e : call.args()) {
generate(state, e); generate(state, e);
boxPrimitive(state, e.type()); 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; break;
} }
case TargetLambdaExpression lambda: case TargetLambdaExpression lambda:
@ -566,7 +712,11 @@ public class Codegen {
private void generateConstructor(TargetConstructor constructor) { private void generateConstructor(TargetConstructor constructor) {
MethodVisitor mv = cw.visitMethod(constructor.access(), "<init>", constructor.getDescriptor(), null, null); MethodVisitor mv = cw.visitMethod(constructor.access(), "<init>", constructor.getDescriptor(), null, null);
mv.visitCode(); 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.visitMaxs(0, 0);
mv.visitEnd(); mv.visitEnd();
} }
@ -574,18 +724,32 @@ public class Codegen {
private void generateMethod(TargetMethod method) { private void generateMethod(TargetMethod method) {
MethodVisitor mv = cw.visitMethod(method.access(), method.name(), method.getDescriptor(), null, null); MethodVisitor mv = cw.visitMethod(method.access(), method.name(), method.getDescriptor(), null, null);
mv.visitCode(); 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.visitMaxs(0, 0);
mv.visitEnd(); mv.visitEnd();
} }
public byte[] generate() { public byte[] generate() {
cw.visit(V1_8, clazz.modifiers(), clazz.qualifiedName(), 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.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)
); );
clazz.fields().forEach(this::generateField); 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); clazz.methods().forEach(this::generateMethod);
cw.visitEnd(); cw.visitEnd();

View File

@ -31,7 +31,7 @@ public class ASTToTargetAST {
List<MethodParameter> params = input.getParameterList().getFormalparalist().stream() List<MethodParameter> params = input.getParameterList().getFormalparalist().stream()
.map(param -> new MethodParameter(convert(param.getType(), sigma), param.getName())).collect(Collectors.toList()); .map(param -> new MethodParameter(convert(param.getType(), sigma), param.getName())).collect(Collectors.toList());
// TODO // 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) { private TargetBlock convert(Block block) {

View File

@ -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<>()); this(modifiers, qualifiedName, TargetType.Object, implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
} }
public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetBlock block) { public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetType returnType, TargetBlock block) {
this.methods.add(new TargetMethod(access, this, name, parameterTypes, 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) { 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) { 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));
} }
} }

View File

@ -1,10 +1,11 @@
package de.dhbwstuttgart.target.tree; package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.target.tree.expression.TargetBlock; import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import java.util.List; 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() { public String getDescriptor() {
// TODO // TODO

View File

@ -1,9 +1,10 @@
package de.dhbwstuttgart.target.tree; package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.Opcodes; 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() { public String getDescriptor() {
// TODO // TODO
return null; return null;

View File

@ -1,14 +1,30 @@
package de.dhbwstuttgart.target.tree; package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.target.tree.expression.TargetBlock; 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 org.objectweb.asm.Opcodes;
import java.util.List; 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() { public String getDescriptor() {
// TODO return getDescriptor(returnType, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
return null;
} }
public boolean isStatic() { public boolean isStatic() {

View File

@ -26,13 +26,21 @@ public sealed interface TargetBinaryOp extends TargetExpression {
record And(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record And(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record Or(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 // Comparison
record Equal(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record Equal(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record Greater(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record Greater(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record GreaterOrEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record GreaterOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record Less(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record Less(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record LessOrEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record LessOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record NotEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record NotEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
} }

View File

@ -5,7 +5,14 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
public sealed interface TargetLiteral extends TargetExpression { public sealed interface TargetLiteral extends TargetExpression {
Object value(); 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 @Override
public TargetType type() { public TargetType type() {
return TargetType.Char; 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 { record StringLiteral(String value) implements TargetLiteral {
@Override @Override
public TargetType type() { public TargetType type() {

View File

@ -1,9 +1,10 @@
package de.dhbwstuttgart.target.tree.expression; package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.TargetMethod; import de.dhbwstuttgart.target.tree.TargetMethod;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List; 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 {
} }

View File

@ -2,7 +2,7 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType; 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 @Override
public TargetType type() { public TargetType type() {

View File

@ -5,8 +5,16 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List; import java.util.List;
public record TargetRefType(String name, List<TargetType> params) implements TargetType { 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 @Override
public String toSignature() { public String toSignature() {
return "L" + this.name.replaceAll("\\.", "/") + ";"; return "L" + getName() + ";";
} }
} }

View File

@ -6,16 +6,16 @@ public sealed interface TargetType
permits TargetExtendsWildcard, TargetFunNType, TargetGenericType, TargetRefType, TargetSuperWildcard { permits TargetExtendsWildcard, TargetFunNType, TargetGenericType, TargetRefType, TargetSuperWildcard {
// Builtin types // Builtin types
TargetRefType Boolean = new TargetRefType("java.lang.Boolean", List.of()); TargetRefType Boolean = new TargetRefType("java.lang.Boolean");
TargetRefType Char = new TargetRefType("java.lang.Character", List.of()); TargetRefType Char = new TargetRefType("java.lang.Character");
TargetRefType Byte = new TargetRefType("java.lang.Byte", List.of()); TargetRefType Byte = new TargetRefType("java.lang.Byte");
TargetRefType Short = new TargetRefType("java.lang.Short", List.of()); TargetRefType Short = new TargetRefType("java.lang.Short");
TargetRefType Integer = new TargetRefType("java.lang.Integer", List.of()); TargetRefType Integer = new TargetRefType("java.lang.Integer");
TargetRefType Long = new TargetRefType("java.lang.Long", List.of()); TargetRefType Long = new TargetRefType("java.lang.Long");
TargetRefType Float = new TargetRefType("java.lang.Float", List.of()); TargetRefType Float = new TargetRefType("java.lang.Float");
TargetRefType Double = new TargetRefType("java.lang.Double", List.of()); TargetRefType Double = new TargetRefType("java.lang.Double");
TargetRefType String = new TargetRefType("java.lang.String", List.of()); TargetRefType String = new TargetRefType("java.lang.String");
TargetRefType Object = new TargetRefType("java.lang.Object", List.of()); TargetRefType Object = new TargetRefType("java.lang.Object");
String toSignature(); String toSignature();
} }

View File

@ -1,25 +1,193 @@
package targetast; package targetast;
import de.dhbwstuttgart.target.bytecode.Codegen; import de.dhbwstuttgart.target.bytecode.Codegen;
import de.dhbwstuttgart.target.tree.MethodParameter;
import de.dhbwstuttgart.target.tree.TargetClass; import de.dhbwstuttgart.target.tree.TargetClass;
import de.dhbwstuttgart.target.tree.TargetMethod; 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 org.junit.Test;
import static org.junit.Assert.*;
import org.objectweb.asm.Opcodes; 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; import java.util.List;
public class TestCodegen { public class TestCodegen {
private static Class generateClass(TargetClass clazz) { private static Class generateClass(TargetClass clazz) throws IOException {
var codegen = new Codegen(clazz); 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 @Test
public void testEmptyClass() throws Exception { public void testEmptyClass() throws Exception {
var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Main"); var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Empty");
clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), new TargetBlock(List.of())); clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), null, new TargetBlock(List.of()));
generateClass(clazz).getDeclaredMethod("main").invoke(null); 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);
}
} }