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.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();
|
||||||
|
@ -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) {
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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() + ";";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user