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

View File

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

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<>());
}
public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetBlock block) {
this.methods.add(new TargetMethod(access, this, name, parameterTypes, block));
public void addMethod(int access, String name, List<MethodParameter> parameterTypes, TargetType returnType, TargetBlock block) {
this.methods.add(new TargetMethod(access, new TargetRefType(this.qualifiedName, List.of()), name, parameterTypes, returnType, block));
}
public void addConstructor(int access, List<MethodParameter> paramterTypes, TargetBlock block) {
this.constructors.add(new TargetConstructor(access, this, paramterTypes, block));
this.constructors.add(new TargetConstructor(access, new TargetRefType(this.qualifiedName, List.of()), paramterTypes, block));
}
public void addField(int access, TargetType type, String name) {
this.fields.add(new TargetField(access, this, type, name));
this.fields.add(new TargetField(access, new TargetRefType(this.qualifiedName, List.of()), type, name));
}
}

View File

@ -1,10 +1,11 @@
package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import java.util.List;
public record TargetConstructor(int access, TargetClass owner, List<MethodParameter> parameterTypes, TargetBlock block) {
public record TargetConstructor(int access, TargetRefType owner, List<MethodParameter> parameters, TargetBlock block) {
public String getDescriptor() {
// TODO

View File

@ -1,9 +1,10 @@
package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.Opcodes;
public record TargetField(int access, TargetClass owner, TargetType type, String name) {
public record TargetField(int access, TargetRefType owner, TargetType type, String name) {
public String getDescriptor() {
// TODO
return null;

View File

@ -1,14 +1,30 @@
package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.Opcodes;
import java.util.List;
import java.util.stream.Collectors;
public record TargetMethod(int access, TargetRefType owner, String name, List<MethodParameter> parameters, TargetType returnType, TargetBlock block) {
public static String getDescriptor(TargetType returnType, TargetType... parameters) {
String ret = "(";
for (var parameterType : parameters) {
ret += parameterType.toSignature();
}
ret += ")";
if (returnType == null) {
ret += "V";
} else {
ret += returnType.toSignature();
}
return ret;
}
public record TargetMethod(int access, TargetClass owner, String name, List<MethodParameter> parameterTypes, TargetBlock block) {
public String getDescriptor() {
// TODO
return null;
return getDescriptor(returnType, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new));
}
public boolean isStatic() {

View File

@ -26,13 +26,21 @@ public sealed interface TargetBinaryOp extends TargetExpression {
record And(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record Or(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
sealed interface TargetRelationalOp extends TargetBinaryOp {
@Override
default TargetType type() {
return TargetType.Boolean;
}
TargetType exprType();
}
// Comparison
record Equal(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record Greater(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record GreaterOrEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record Less(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record LessOrEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record NotEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {}
record Equal(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record Greater(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record GreaterOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record Less(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record LessOrEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
record NotEqual(TargetType exprType, TargetExpression left, TargetExpression right) implements TargetRelationalOp {}
}

View File

@ -5,7 +5,14 @@ import de.dhbwstuttgart.target.tree.type.TargetType;
public sealed interface TargetLiteral extends TargetExpression {
Object value();
record CharLiteral(Integer value) implements TargetLiteral {
record BooleanLiteral(Boolean value) implements TargetLiteral {
@Override
public TargetType type() {
return TargetType.Boolean;
}
}
record CharLiteral(Character value) implements TargetLiteral {
@Override
public TargetType type() {
return TargetType.Char;
@ -26,6 +33,20 @@ public sealed interface TargetLiteral extends TargetExpression {
}
}
record FloatLiteral(Float value) implements TargetLiteral {
@Override
public TargetType type() {
return TargetType.Float;
}
}
record DoubleLiteral(Double value) implements TargetLiteral {
@Override
public TargetType type() {
return TargetType.Double;
}
}
record StringLiteral(String value) implements TargetLiteral {
@Override
public TargetType type() {

View File

@ -1,9 +1,10 @@
package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.TargetMethod;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List;
public record TargetMethodCall(TargetType type, TargetExpression expr, List<TargetExpression> args, TargetMethod method) implements TargetStatementExpression {
public record TargetMethodCall(TargetType type, TargetExpression expr, List<TargetExpression> args, TargetRefType owner, String name, String descriptor, boolean isStatic) implements TargetStatementExpression {
}

View File

@ -2,7 +2,7 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetVarDecl(String name, TargetType varType) implements TargetExpression {
public record TargetVarDecl(TargetType varType, String name, TargetExpression value) implements TargetExpression {
@Override
public TargetType type() {

View File

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

View File

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

View File

@ -1,25 +1,193 @@
package targetast;
import de.dhbwstuttgart.target.bytecode.Codegen;
import de.dhbwstuttgart.target.tree.MethodParameter;
import de.dhbwstuttgart.target.tree.TargetClass;
import de.dhbwstuttgart.target.tree.TargetMethod;
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.junit.Test;
import static org.junit.Assert.*;
import org.objectweb.asm.Opcodes;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.List;
public class TestCodegen {
private static Class generateClass(TargetClass clazz) {
private static Class generateClass(TargetClass clazz) throws IOException {
var codegen = new Codegen(clazz);
return new ByteArrayClassLoader().loadClass(codegen.generate());
var bytes = codegen.generate();
var path = Path.of(System.getProperty("user.dir"), "src/test/resources/target/");
Files.createDirectories(path);
Files.write(path.resolve(clazz.qualifiedName() + ".class"), bytes, StandardOpenOption.CREATE);
return new ByteArrayClassLoader().loadClass(bytes);
}
@Test
public void testEmptyClass() throws Exception {
var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Main");
clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), new TargetBlock(List.of()));
var clazz = new TargetClass(Opcodes.ACC_PUBLIC, "Empty");
clazz.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", List.of(), null, new TargetBlock(List.of()));
generateClass(clazz).getDeclaredMethod("main").invoke(null);
}
@Test
public void testArithmetic() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "Arithmetic");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add",
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
TargetType.Integer,
new TargetBlock(List.of(new TargetReturn(
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
))
);
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "sub",
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
TargetType.Integer,
new TargetBlock(List.of(new TargetReturn(
new TargetBinaryOp.Sub(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
))
);
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "div",
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
TargetType.Integer,
new TargetBlock(List.of(new TargetReturn(
new TargetBinaryOp.Div(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
))
);
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "mul",
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
TargetType.Integer,
new TargetBlock(List.of(new TargetReturn(
new TargetBinaryOp.Mul(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
))
);
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "rem",
List.of(new MethodParameter(TargetType.Integer, "a"), new MethodParameter(TargetType.Integer, "b")),
TargetType.Integer,
new TargetBlock(List.of(new TargetReturn(
new TargetBinaryOp.Rem(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "a"), new TargetLocalVar(TargetType.Integer, "b")))
))
);
var clazz = generateClass(targetClass);
assertEquals(clazz.getDeclaredMethod("add", Integer.class, Integer.class).invoke(null, 10, 10), 20);
assertEquals(clazz.getDeclaredMethod("sub", Integer.class, Integer.class).invoke(null, 20, 10), 10);
assertEquals(clazz.getDeclaredMethod("div", Integer.class, Integer.class).invoke(null, 20, 10), 2);
assertEquals(clazz.getDeclaredMethod("mul", Integer.class, Integer.class).invoke(null, 20, 10), 200);
assertEquals(clazz.getDeclaredMethod("rem", Integer.class, Integer.class).invoke(null, 10, 3), 1);
}
// When adding two numbers and the return type is Long it needs to convert both values to Long
@Test
public void testArithmeticConvert() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "ArithmeticConvert");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "add", List.of(), TargetType.Long,
new TargetBlock(List.of(new TargetReturn(
new TargetBinaryOp.Add(TargetType.Long, new TargetLiteral.CharLiteral((char)10), new TargetLiteral.LongLiteral((long)20))
)))
);
var clazz = generateClass(targetClass);
assertEquals(clazz.getDeclaredMethod("add").invoke(null), (long)30);
}
@Test
public void testMethodCall() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "HelloWorld");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "helloWorld", List.of(), null,
new TargetBlock(List.of(new TargetMethodCall(null,
new TargetFieldVar(
new TargetRefType("java.io.PrintStream"), true,
new TargetClassName(new TargetRefType("java.lang.System")),
"out"
),
List.of(new TargetLiteral.StringLiteral("Hello World!")),
new TargetRefType("java.io.PrintStream"),
"println",
TargetMethod.getDescriptor(null, TargetType.String),
false
)))
);
var clazz = generateClass(targetClass);
clazz.getDeclaredMethod("helloWorld").invoke(null);
}
@Test
public void testIfStatement() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "IfStmt");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "ifStmt",
List.of(new MethodParameter(TargetType.Integer, "val")),
TargetType.Integer,
new TargetBlock(List.of(new TargetIf(
new TargetBinaryOp.Equal(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(10)),
new TargetReturn(new TargetLiteral.IntLiteral(1)),
new TargetIf(
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "val"), new TargetLiteral.IntLiteral(5)),
new TargetReturn(new TargetLiteral.IntLiteral(2)),
new TargetReturn(new TargetLiteral.IntLiteral(3))
)
)))
);
var clazz = generateClass(targetClass);
var ifStmt = clazz.getDeclaredMethod("ifStmt", Integer.class);
assertEquals(ifStmt.invoke(null, 10), 1);
assertEquals(ifStmt.invoke(null, 3), 2);
assertEquals(ifStmt.invoke(null, 20), 3);
}
@Test
public void testFor() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "For");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer,
new TargetBlock(List.of(
new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)),
new TargetFor(
new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)),
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)),
new TargetAssign(TargetType.Integer,
new TargetLocalVar(TargetType.Integer, "i"),
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1))),
new TargetBlock(List.of(
new TargetAssign(TargetType.Integer,
new TargetLocalVar(TargetType.Integer, "sum"),
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetLocalVar(TargetType.Integer, "i"))
)
))
),
new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum"))
))
);
var clazz = generateClass(targetClass);
assertEquals(clazz.getDeclaredMethod("forLoop").invoke(null), 45);
}
@Test
public void testWhile() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "While");
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "whileLoop", List.of(), TargetType.Integer,
new TargetBlock(List.of(
new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0)),
new TargetWhile(
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)),
new TargetBlock(List.of(
new TargetAssign(TargetType.Integer,
new TargetLocalVar(TargetType.Integer, "i"),
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1))
)
))
),
new TargetReturn(new TargetLocalVar(TargetType.Integer, "i"))
))
);
var clazz = generateClass(targetClass);
assertEquals(clazz.getDeclaredMethod("whileLoop").invoke(null), 10);
}
}