diff --git a/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java index fe10d9d60..c8bf7fd81 100755 --- a/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java @@ -4,17 +4,17 @@ import de.dhbwstuttgart.target.tree.TargetClass; import de.dhbwstuttgart.target.tree.TargetConstructor; import de.dhbwstuttgart.target.tree.TargetField; import de.dhbwstuttgart.target.tree.TargetMethod; -import de.dhbwstuttgart.target.tree.expression.TargetBlock; -import de.dhbwstuttgart.target.tree.expression.TargetExpression; -import de.dhbwstuttgart.target.tree.expression.TargetVarDecl; +import de.dhbwstuttgart.target.tree.expression.*; +import de.dhbwstuttgart.target.tree.type.TargetRefType; import de.dhbwstuttgart.target.tree.type.TargetType; -import javassist.compiler.CodeGen; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import java.util.Map; import static org.objectweb.asm.Opcodes.*; +import static de.dhbwstuttgart.target.tree.expression.TargetBinaryOp.*; public class Codegen { private TargetClass clazz; @@ -25,9 +25,9 @@ public class Codegen { this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); } - private record LocalVar(int index, String name, TargetType type) {} + private static record LocalVar(int index, String name, TargetType type) {} - private class Scope { + private static class Scope { Scope parent; Map locals; @@ -51,7 +51,7 @@ public class Codegen { } } - private class State { + private static class State { Scope scope = new Scope(null); int localCounter = 1; MethodVisitor mv; @@ -74,22 +74,488 @@ public class Codegen { } } + private void boxPrimitive(State state, TargetType type) { + var mv = state.mv; + if (type.equals(TargetType.Boolean)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + } else if (type.equals(TargetType.Byte)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + } 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); + } else if (type.equals(TargetType.Integer)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(L)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)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (type.equals(TargetType.Char)) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Char", "valueOf", "(C)Ljava/lang/Char;", false); + } + } + + private void unboxPrimitive(State state, TargetType type) { + var mv = state.mv; + if (type.equals(TargetType.Boolean)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); + } else if (type.equals(TargetType.Byte)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); + } 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); + } else if (type.equals(TargetType.Integer)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + } else if (type.equals(TargetType.Float)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); + } else if (type.equals(TargetType.Short)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); + } else if (type.equals(TargetType.Char)) { + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Char", "charValue", "()C", false); + } + } + + private void generateRelationalOperator(State state, TargetBinaryOp op, int code) { + var mv = state.mv; + Label if_true = new Label(); + Label end = new Label(); + generate(state, op.left()); + generate(state, op.right()); + mv.visitJumpInsn(code, if_true); + mv.visitInsn(ICONST_0); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(if_true); + mv.visitInsn(ICONST_1); + mv.visitLabel(end); + } + + private void generateRelationalOperator(State state, TargetBinaryOp op, int cmp, int code) { + var mv = state.mv; + Label if_true = new Label(); + Label end = new Label(); + generate(state, op.left()); + generate(state, op.right()); + mv.visitInsn(cmp); + mv.visitInsn(code); + mv.visitJumpInsn(code, if_true); + mv.visitInsn(ICONST_0); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(if_true); + mv.visitInsn(ICONST_1); + mv.visitLabel(end); + } + + private void generateBinaryOp(State state, TargetBinaryOp op) { + var mv = state.mv; + switch (op) { + case Add add: { + generate(state, add.left()); + generate(state, add.right()); + var type = add.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IADD); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LADD); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FADD); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DADD); + } else { + throw new CodeGenException("Invalid argument to Add expression"); + } + break; + } + case Sub sub: { + generate(state, sub.left()); + generate(state, sub.right()); + var type = sub.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(ISUB); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LSUB); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FSUB); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DSUB); + } else { + throw new CodeGenException("Invalid argument to Sub expression"); + } + break; + } + case Div div: { + generate(state, div.left()); + generate(state, div.right()); + var type = div.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IDIV); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LDIV); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FDIV); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DDIV); + } else { + throw new CodeGenException("Invalid argument to Div expression"); + } + break; + } + case Mul mul: { + generate(state, mul.left()); + generate(state, mul.right()); + var type = mul.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IMUL); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LMUL); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FMUL); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DMUL); + } else { + throw new CodeGenException("Invalid argument to Mul expression"); + } + break; + } + case Rem rem: { + generate(state, rem.left()); + generate(state, rem.right()); + var type = rem.type(); + if (type.equals(TargetType.Byte) + || type.equals(TargetType.Integer) + || type.equals(TargetType.Short)) { + mv.visitInsn(IREM); + } else if (type.equals(TargetType.Long)) { + mv.visitInsn(LREM); + } else if (type.equals(TargetType.Float)) { + mv.visitInsn(FREM); + } else if (type.equals(TargetType.Double)) { + mv.visitInsn(DREM); + } else { + throw new CodeGenException("Invalid argument to Rem expression"); + } + break; + } + case Or or: { + Label or_false = new Label(); + Label or_true = new Label(); + Label end = new Label(); + generate(state, or.left()); + mv.visitJumpInsn(IFNE, or_true); + generate(state, or.right()); + mv.visitJumpInsn(IFEQ, or_false); + mv.visitLabel(or_true); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(or_false); + mv.visitInsn(ICONST_0); + mv.visitLabel(end); + break; + } + case And and: { + Label and_false = new Label(); + Label end = new Label(); + generate(state, and.left()); + mv.visitJumpInsn(IFEQ, and_false); + generate(state, and.right()); + mv.visitJumpInsn(IFEQ, and_false); + mv.visitInsn(ICONST_1); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(and_false); + mv.visitInsn(ICONST_0); + mv.visitLabel(end); + break; + } + case BAnd band: { + generate(state, band.left()); + generate(state, band.right()); + if (band.type().equals(TargetType.Long)) + mv.visitInsn(LAND); + else mv.visitInsn(IAND); + break; + } + case BOr bor: { + generate(state, bor.left()); + generate(state, bor.right()); + if (bor.type().equals(TargetType.Long)) + mv.visitInsn(LOR); + else mv.visitInsn(IOR); + break; + } + case XOr xor: { + generate(state, xor.left()); + generate(state, xor.right()); + if (xor.type().equals(TargetType.Long)) + mv.visitInsn(LXOR); + else mv.visitInsn(IXOR); + break; + } + case Shl shl: { + generate(state, shl.left()); + generate(state, shl.right()); + if (shl.type().equals(TargetType.Long)) + mv.visitInsn(LSHL); + else mv.visitInsn(ISHL); + break; + } + case Shr shr: { + generate(state, shr.left()); + generate(state, shr.right()); + if (shr.type().equals(TargetType.Long)) + mv.visitInsn(LSHR); + else mv.visitInsn(ISHR); + break; + } + case UShr ushr: { + generate(state, ushr.left()); + generate(state, ushr.right()); + if (ushr.type().equals(TargetType.Long)) + mv.visitInsn(LUSHR); + else mv.visitInsn(IUSHR); + break; + } + case Greater greater: { + var type = greater.type(); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, greater, LCMP, IFGT); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, greater, FCMPL, IFGT); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, greater, DCMPL, IFGT); + } else { + generateRelationalOperator(state, greater, IF_ICMPGT); + } + break; + } + case Less less: { + var type = less.type(); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, less, LCMP, IFLT); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, less, FCMPL, IFLT); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, less, DCMPL, IFLT); + } else { + generateRelationalOperator(state, less, IF_ICMPLT); + } + break; + } + case GreaterOrEqual greaterOrEqual: { + var type = greaterOrEqual.type(); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, greaterOrEqual, LCMP, IFGE); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, greaterOrEqual, FCMPL, IFGE); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, greaterOrEqual, DCMPL, IFGE); + } else { + generateRelationalOperator(state, greaterOrEqual, IF_ICMPGE); + } + break; + } + case LessOrEqual lessOrEqual: { + var type = lessOrEqual.type(); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, lessOrEqual, LCMP, IFLE); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, lessOrEqual, FCMPL, IFLE); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, lessOrEqual, DCMPL, IFLE); + } else { + generateRelationalOperator(state, lessOrEqual, IF_ICMPLE); + } + break; + } + case Equal equal: { + var type = equal.type(); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, equal, LCMP, IFEQ); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, equal, FCMPL, IFEQ); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, equal, DCMPL, IFEQ); + } else if (type.equals(TargetType.Char) + || type.equals(TargetType.Short) + || type.equals(TargetType.Byte) + || type.equals(TargetType.Integer)) { + generateRelationalOperator(state, equal, IF_ICMPEQ); + } else { + generateRelationalOperator(state, equal, IF_ACMPEQ); + } + break; + } + case NotEqual notEqual: { + var type = notEqual.type(); + if (type.equals(TargetType.Long)) { + generateRelationalOperator(state, notEqual, LCMP, IFNE); + } else if (type.equals(TargetType.Float)) { + generateRelationalOperator(state, notEqual, FCMPL, IFNE); + } else if (type.equals(TargetType.Double)) { + generateRelationalOperator(state, notEqual, DCMPL, IFNE); + } else if (type.equals(TargetType.Char) + || type.equals(TargetType.Short) + || type.equals(TargetType.Byte) + || type.equals(TargetType.Integer)) { + generateRelationalOperator(state, notEqual, IF_ICMPNE); + } else { + generateRelationalOperator(state, notEqual, IF_ACMPNE); + } + break; + } + } + } + + private void generateLambdaExpression(State state, TargetLambdaExpression lambda) { + } + private void generate(State state, TargetExpression expr) { + var mv = state.mv; switch (expr) { - case TargetBlock block: + case TargetBlock block: { var localCounter = state.localCounter; state.enterScope(); for (var e : block.statements()) { generate(state, e); + if (e instanceof TargetMethodCall) { + if (e.type() != null) mv.visitInsn(POP); + } else if (e instanceof TargetStatementExpression) { + mv.visitInsn(POP); + } } state.exitScope(); state.localCounter = localCounter; break; + } case TargetVarDecl varDecl: state.createVariable(varDecl.name(), varDecl.varType()); break; + case TargetBinaryOp op: + generateBinaryOp(state, op); + break; + case TargetAssign assign: { + switch (assign.left()) { + case TargetLocalVar local: { + generate(state, assign.right()); + boxPrimitive(state, assign.right().type()); + mv.visitInsn(ASTORE); + break; + } + case TargetFieldVar dot: { + generate(state, dot.left()); + TargetRefType type = (TargetRefType) dot.type(); + generate(state, assign.right()); + boxPrimitive(state, assign.right().type()); + if (dot.isStatic()) + mv.visitInsn(DUP); + else mv.visitInsn(DUP_X1); + mv.visitFieldInsn(dot.isStatic() ? PUTSTATIC : PUTFIELD, dot.right(), type.name(), type.toSignature()); + break; + } + default: + throw new CodeGenException("Invalid assignment"); + } + break; + } + case TargetLocalVar localVar: { + LocalVar local = state.scope.get(localVar.name()); + mv.visitVarInsn(ALOAD, local.index()); + unboxPrimitive(state, local.type()); + break; + } + case TargetFieldVar dot: { + 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()); + break; + } + case TargetFor _for: { + state.enterScope(); + var localCounter = state.localCounter; + if (_for.init() != null) + generate(state, _for.init()); + Label start = new Label(); + Label end = new Label(); + mv.visitLabel(start); + if (_for.termination() != null) + generate(state, _for.termination()); + mv.visitJumpInsn(IFEQ, end); + generate(state, _for.body()); + if (_for.increment() != null) { + generate(state, _for.increment()); + if (_for.increment().type() != null) { + mv.visitInsn(POP); + } + } + mv.visitJumpInsn(GOTO, start); + mv.visitLabel(end); + state.exitScope(); + state.localCounter = localCounter; + break; + } + case TargetWhile _while: { + Label start = new Label(); + Label end = new Label(); + mv.visitLabel(start); + generate(state, _while.cond()); + mv.visitJumpInsn(IFEQ, end); + generate(state, _while.body()); + mv.visitJumpInsn(GOTO, start); + mv.visitLabel(end); + break; + } + case TargetIf _if: { + generate(state, _if.cond()); + Label _else = new Label(); + Label end = new Label(); + mv.visitJumpInsn(IFEQ, _else); + generate(state, _if.if_body()); + mv.visitJumpInsn(GOTO, end); + mv.visitLabel(_else); + if (_if.else_body() != null) { + generate(state, _if.else_body()); + } + mv.visitLabel(end); + break; + } + case TargetReturn ret: { + generate(state, ret.expression()); + if (ret.expression() != null) + mv.visitInsn(ARETURN); + else mv.visitInsn(RETURN); + break; + } + case TargetThis _this: { + mv.visitVarInsn(ALOAD, 0); + break; + } + case TargetSuper _super: { + // TODO + 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); + break; + } + case TargetLambdaExpression lambda: + generateLambdaExpression(state, lambda); + break; default: - throw new IllegalStateException("Unexpected value: " + expr); + throw new CodeGenException("Unexpected value: " + expr); } } @@ -99,6 +565,7 @@ public class Codegen { private void generateConstructor(TargetConstructor constructor) { MethodVisitor mv = cw.visitMethod(constructor.access(), "", constructor.getDescriptor(), null, null); + mv.visitCode(); generate(new State(mv), constructor.block()); mv.visitMaxs(0, 0); mv.visitEnd(); @@ -106,6 +573,7 @@ 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()); mv.visitMaxs(0, 0); mv.visitEnd(); diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 63248f922..19420501f 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -30,7 +30,8 @@ public class ASTToTargetAST { private TargetMethod convert(Method input, Map sigma) { List params = input.getParameterList().getFormalparalist().stream() .map(param -> new MethodParameter(convert(param.getType(), sigma), param.getName())).collect(Collectors.toList()); - return new TargetMethod(input.name, params, convert(input.block)); + // TODO + return new TargetMethod(0, null, input.name, params, convert(input.block)); } private TargetBlock convert(Block block) { diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java index a78da9d3a..b6c88b2d6 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java @@ -1,10 +1,32 @@ 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 java.lang.annotation.Target; +import java.util.ArrayList; import java.util.List; public record TargetClass(int modifiers, String qualifiedName, TargetType superType, List implementingInterfaces, - List constructors, List fields, List methods) {} + List constructors, List fields, List methods) { + public TargetClass(int modifiers, String qualifiedName) { + this(modifiers, qualifiedName, TargetType.Object, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + public TargetClass(int modifiers, String qualifiedName, List implementingInterfaces) { + this(modifiers, qualifiedName, TargetType.Object, implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + } + + public void addMethod(int access, String name, List parameterTypes, TargetBlock block) { + this.methods.add(new TargetMethod(access, this, name, parameterTypes, block)); + } + + public void addConstructor(int access, List paramterTypes, TargetBlock block) { + this.constructors.add(new TargetConstructor(access, this, paramterTypes, block)); + } + + public void addField(int access, TargetType type, String name) { + this.fields.add(new TargetField(access, this, type, name)); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java index 594d5a207..4972fbed5 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java @@ -4,7 +4,7 @@ import de.dhbwstuttgart.target.tree.expression.TargetBlock; import java.util.List; -public record TargetConstructor(int access, List parameterTypes, TargetBlock block) { +public record TargetConstructor(int access, TargetClass owner, List parameterTypes, TargetBlock block) { public String getDescriptor() { // TODO diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetField.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetField.java index c9076736c..08b8e0d77 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetField.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetField.java @@ -3,7 +3,7 @@ package de.dhbwstuttgart.target.tree; import de.dhbwstuttgart.target.tree.type.TargetType; import org.objectweb.asm.Opcodes; -public record TargetField(int access, TargetType type, String name) { +public record TargetField(int access, TargetClass owner, TargetType type, String name) { public String getDescriptor() { // TODO return null; diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java new file mode 100644 index 000000000..f15acad49 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetInterface.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree; + +import java.util.List; + +public record TargetInterface(String name, List methods, List extendedInterfaces) { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java index 767f2689c..fefe7d2ea 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetMethod.java @@ -1,13 +1,18 @@ package de.dhbwstuttgart.target.tree; import de.dhbwstuttgart.target.tree.expression.TargetBlock; +import org.objectweb.asm.Opcodes; import java.util.List; -public record TargetMethod(int access, String name, List parameterTypes, TargetBlock block) { +public record TargetMethod(int access, TargetClass owner, String name, List parameterTypes, TargetBlock block) { public String getDescriptor() { // TODO return null; } + + public boolean isStatic() { + return (access & Opcodes.ACC_STATIC) != 0; + } } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetAssign.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetAssign.java index fd4bf51e5..609de796f 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetAssign.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetAssign.java @@ -2,5 +2,5 @@ package de.dhbwstuttgart.target.tree.expression; import de.dhbwstuttgart.target.tree.type.TargetType; -public record TargetAssign(TargetType type, TargetExpression leftSide, TargetExpression rightSide) implements TargetExpression { +public record TargetAssign(TargetType type, TargetExpression left, TargetExpression right) implements TargetStatementExpression { } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBinaryOp.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBinaryOp.java index 233157c55..fc06719bc 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBinaryOp.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetBinaryOp.java @@ -7,7 +7,7 @@ public sealed interface TargetBinaryOp extends TargetExpression { TargetExpression left(); TargetExpression right(); - // Artihmetic + // Arithmetic record Add(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record Sub(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record Div(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} @@ -34,7 +34,6 @@ public sealed interface TargetBinaryOp extends TargetExpression { record LessOrEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} record NotEqual(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} - record Assign(TargetType type, TargetExpression left, TargetExpression right) implements TargetBinaryOp {} } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetClassName.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetClassName.java new file mode 100644 index 000000000..7131acacd --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetClassName.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetClassName(TargetType type) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetDot.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetDot.java deleted file mode 100644 index 6eef98573..000000000 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetDot.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.dhbwstuttgart.target.tree.expression; - -import de.dhbwstuttgart.target.tree.type.TargetType; - -public record TargetDot(TargetType type, boolean isStatic, TargetExpression left, String right) implements TargetExpression { -} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetExpression.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetExpression.java index 7c9c85a22..64146f7e3 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetExpression.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetExpression.java @@ -3,7 +3,7 @@ package de.dhbwstuttgart.target.tree.expression; import de.dhbwstuttgart.target.tree.type.*; public sealed interface TargetExpression - permits TargetAssign, TargetBinaryOp, TargetBlock, TargetBreak, TargetCast, TargetContinue, TargetDot, TargetFieldVar, TargetFor, TargetForEach, TargetIf, TargetInstanceOf, TargetLambdaExpression, TargetLiteral, TargetLocalVar, TargetMethodCall, TargetNew, TargetReturn, TargetSuper, TargetSwitch, TargetThis, TargetUnaryOp, TargetVarDecl, TargetWhile { + permits TargetBinaryOp, TargetBlock, TargetBreak, TargetCast, TargetClassName, TargetContinue, TargetFieldVar, TargetFor, TargetForEach, TargetIf, TargetInstanceOf, TargetLambdaExpression, TargetLiteral, TargetLocalVar, TargetReturn, TargetStatementExpression, TargetSuper, TargetSwitch, TargetTernary, TargetThis, TargetUnaryOp, TargetVarDecl, TargetWhile { TargetType type(); } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFieldVar.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFieldVar.java index 9835fc9f7..5d41b1b4e 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFieldVar.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetFieldVar.java @@ -1,7 +1,6 @@ package de.dhbwstuttgart.target.tree.expression; -import de.dhbwstuttgart.target.tree.TargetField; import de.dhbwstuttgart.target.tree.type.TargetType; -public record TargetFieldVar(TargetType type, TargetField field) implements TargetExpression { +public record TargetFieldVar(TargetType type, boolean isStatic, TargetExpression left, String right) implements TargetExpression { } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetIf.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetIf.java index ef94fc0b8..78bc766b1 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetIf.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetIf.java @@ -2,7 +2,7 @@ package de.dhbwstuttgart.target.tree.expression; import de.dhbwstuttgart.target.tree.type.TargetType; -public record TargetIf(TargetExpression expr, TargetExpression if_body, TargetExpression else_body) implements TargetExpression { +public record TargetIf(TargetExpression cond, TargetExpression if_body, TargetExpression else_body) implements TargetExpression { @Override public TargetType type() { return null; diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java index 6a8126046..0e7359f80 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetMethodCall.java @@ -5,5 +5,5 @@ import de.dhbwstuttgart.target.tree.type.TargetType; import java.util.List; -public record TargetMethodCall(TargetType type, TargetExpression expr, List args, TargetMethod method) implements TargetExpression { +public record TargetMethodCall(TargetType type, TargetExpression expr, List args, TargetMethod method) implements TargetStatementExpression { } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetNew.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetNew.java index 6819a7448..30fb295c8 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetNew.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetNew.java @@ -4,5 +4,5 @@ import de.dhbwstuttgart.target.tree.type.TargetType; import java.util.List; -public record TargetNew(TargetType type, List params) implements TargetExpression { +public record TargetNew(TargetType type, List params) implements TargetStatementExpression { } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetStatementExpression.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetStatementExpression.java new file mode 100644 index 000000000..2d804d6ab --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetStatementExpression.java @@ -0,0 +1,4 @@ +package de.dhbwstuttgart.target.tree.expression; + +public sealed interface TargetStatementExpression extends TargetExpression permits TargetAssign, TargetMethodCall, TargetNew, TargetUnaryOp.PostDecrement, TargetUnaryOp.PostIncrement, TargetUnaryOp.PreDecrement, TargetUnaryOp.PreIncrement { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetTernary.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetTernary.java new file mode 100644 index 000000000..17349489f --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetTernary.java @@ -0,0 +1,6 @@ +package de.dhbwstuttgart.target.tree.expression; + +import de.dhbwstuttgart.target.tree.type.TargetType; + +public record TargetTernary(TargetType type, TargetExpression cond, TargetExpression ifTrue, TargetExpression ifFalse) implements TargetExpression { +} diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetUnaryOp.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetUnaryOp.java index eec721961..457a55add 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetUnaryOp.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetUnaryOp.java @@ -6,10 +6,10 @@ public sealed interface TargetUnaryOp extends TargetExpression { TargetExpression expr(); record Negate(TargetType type, TargetExpression expr) implements TargetUnaryOp {} - record Not(TargetType type, TargetExpression expr) implements TargetUnaryOp {} + record Not(TargetType type, TargetExpression expr) implements TargetUnaryOp {} - record PreIncrement(TargetType type, TargetExpression expr) implements TargetUnaryOp {} - record PostIncrement(TargetType type, TargetExpression expr) implements TargetUnaryOp {} - record PreDecrement(TargetType type, TargetExpression expr) implements TargetUnaryOp {} - record PostDecrement(TargetType type, TargetExpression expr) implements TargetUnaryOp {} + record PreIncrement(TargetType type, TargetExpression expr) implements TargetStatementExpression {} + record PostIncrement(TargetType type, TargetExpression expr) implements TargetStatementExpression {} + record PreDecrement(TargetType type, TargetExpression expr) implements TargetStatementExpression {} + record PostDecrement(TargetType type, TargetExpression expr) implements TargetStatementExpression {} } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetWhile.java b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetWhile.java index 5f0b8b065..62ff90dc4 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetWhile.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/expression/TargetWhile.java @@ -2,7 +2,7 @@ package de.dhbwstuttgart.target.tree.expression; import de.dhbwstuttgart.target.tree.type.TargetType; -public record TargetWhile(TargetExpression expr, TargetExpression body) implements TargetExpression { +public record TargetWhile(TargetExpression cond, TargetExpression body) implements TargetExpression { @Override public TargetType type() { diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetRefType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetRefType.java index 66a2b8acb..1e892a254 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetRefType.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetRefType.java @@ -7,6 +7,6 @@ import java.util.List; public record TargetRefType(String name, List params) implements TargetType { @Override public String toSignature() { - return "L" + this.name.replaceAll("\\.", "/"); + return "L" + this.name.replaceAll("\\.", "/") + ";"; } } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetType.java index 7f78b4315..49ed41064 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetType.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetType.java @@ -15,6 +15,7 @@ public sealed interface TargetType 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()); String toSignature(); } diff --git a/src/test/java/targetast/ByteArrayClassLoader.java b/src/test/java/targetast/ByteArrayClassLoader.java new file mode 100644 index 000000000..227b7094a --- /dev/null +++ b/src/test/java/targetast/ByteArrayClassLoader.java @@ -0,0 +1,9 @@ +package targetast; + +import java.util.Map; + +public class ByteArrayClassLoader extends ClassLoader { + public Class loadClass(byte[] code) { + return this.defineClass(null, code, 0, code.length); + } +} diff --git a/src/test/java/targetast/TestCodegen.java b/src/test/java/targetast/TestCodegen.java new file mode 100644 index 000000000..dc5f03f89 --- /dev/null +++ b/src/test/java/targetast/TestCodegen.java @@ -0,0 +1,25 @@ +package targetast; + +import de.dhbwstuttgart.target.bytecode.Codegen; +import de.dhbwstuttgart.target.tree.TargetClass; +import de.dhbwstuttgart.target.tree.TargetMethod; +import de.dhbwstuttgart.target.tree.expression.TargetBlock; +import org.junit.Test; +import org.objectweb.asm.Opcodes; + +import java.util.List; + +public class TestCodegen { + + private static Class generateClass(TargetClass clazz) { + var codegen = new Codegen(clazz); + return new ByteArrayClassLoader().loadClass(codegen.generate()); + } + + @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())); + generateClass(clazz).getDeclaredMethod("main").invoke(null); + } +}