add some e2e tests and make BytecodeTestUtil usable with different constructors

This commit is contained in:
simon 2024-06-25 17:26:31 +02:00
parent 4733e77522
commit ee2553b089
14 changed files with 301 additions and 12 deletions

View File

@ -1,6 +1,6 @@
package de.maishai.typedast;
import lombok.*;
import lombok.Getter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@ -16,6 +16,7 @@ public class MethodContext {
}
private MethodVisitor mv;
private Type returnType;
private final ClassContext classContext;
private int localVarIndex = 0;
private final Map<String, LocalVariable> variableIndex = new HashMap<>();
@ -26,9 +27,10 @@ public class MethodContext {
//used to jump to the start of the loop with continue
private final Stack<Label> continueLabels = new Stack<>();
public MethodContext(ClassContext classContext, MethodVisitor mv) {
public MethodContext(ClassContext classContext, MethodVisitor mv, Type returnType) {
this.mv = mv;
this.classContext = classContext;
this.returnType = returnType;
registerVariable("this", classContext.getType());
mv.visitCode();
}
@ -78,6 +80,10 @@ public class MethodContext {
}
public void wrapUp() {
//if return type is void, add return instruction
if (returnType.getKind() == Type.Kind.VOID) {
mv.visitInsn(Opcodes.RETURN);
}
System.out.println("maxStack: " + maxStack + " localVarIndex: " + localVarIndex);
mv.visitMaxs(maxStack, localVarIndex);
mv.visitEnd();

View File

@ -90,7 +90,7 @@ public class TypedConstructor implements TypedNode {
int accessModifier = Opcodes.ACC_PUBLIC; // ist laut Andi ok
MethodVisitor mv = ctx.getCw().visitMethod(accessModifier, "<init>", CodeGenUtils.generateDescriptor(typedParameters.stream().map(TypedParameter::getType).toList(), Type.VOID), null, null);
System.out.println("Visiting method: " + "<init>" + CodeGenUtils.generateDescriptor(typedParameters.stream().map(TypedParameter::getType).toList(), Type.VOID));
MethodContext mctx = new MethodContext(ctx, mv);
MethodContext mctx = new MethodContext(ctx, mv, Type.VOID);
typedParameters.forEach(param -> mctx.registerVariable(param.getParaName(), param.getType()));
//super();
mctx.pushStack("this");

View File

@ -1,3 +1,4 @@
import e2e.BytecodeTestUtil;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@ -16,7 +17,7 @@ public class CodegeneratorTests {
assertDoesNotThrow(() -> {
BytecodeTestUtil testUtility = new BytecodeTestUtil("src/test/testFiles/JavaTestfilesFeatures/MethodCall.java", "MethodCall");
assertEquals(3, testUtility.invokeMethod("method", null));
assertEquals(0, testUtility.invokeMethod("method", null));
assertEquals(1, testUtility.invokeMethod("method1", new Class<?>[]{int.class}, 1));
assertEquals(3, testUtility.invokeMethod("method2", new Class<?>[]{int.class, int.class}, 1, 2));
});

View File

@ -1,3 +1,5 @@
package e2e;
import de.maishai.Compiler;
import java.lang.reflect.Method;
@ -6,8 +8,24 @@ import java.util.List;
public class BytecodeTestUtil {
private Class<?> clazz;
private Object instance;
public BytecodeTestUtil(String sourceFilePath, String className) throws Exception {
clazz = loadClass(sourceFilePath, className);
instance = clazz.getDeclaredConstructor().newInstance();
}
public BytecodeTestUtil(String sourceFilePath, String className, Class<?>[] parameterTypes, Object... initargs) throws Exception {
clazz = loadClass(sourceFilePath, className);
instance = clazz.getDeclaredConstructor(parameterTypes).newInstance(initargs);
}
public Object invokeMethod(String methodName, Class<?>[] parameterTypes, Object... args) throws Exception {
Method method = clazz.getMethod(methodName, parameterTypes);
return method.invoke(instance, args);
}
public static Class<?> loadClass(String sourceFilePath, String className) throws Exception {
byte[] resultBytecode = Compiler.generateByteCodeArrayFromFile(List.of(sourceFilePath)).get(0);
ClassLoader classLoader = new ClassLoader() {
@ -16,13 +34,6 @@ public class BytecodeTestUtil {
return defineClass(name, resultBytecode, 0, resultBytecode.length);
}
};
clazz = classLoader.loadClass(className);
}
public Object invokeMethod(String methodName, Class<?>[] parameterTypes, Object... args) throws Exception {
Object instance = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod(methodName, parameterTypes);
return method.invoke(instance, args);
return classLoader.loadClass(className);
}
}

View File

@ -0,0 +1,34 @@
package e2e;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class TestConstructor {
@Test
void testConstructor_noArgAndUseGetter() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/Constructor.java", "Constructor");
Assertions.assertEquals(12, util.invokeMethod("getX", null));
});
}
@Test
void testConstructor_withArgAndUseGetter() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/Constructor.java", "Constructor", new Class<?>[]{int.class}, 777);
Assertions.assertEquals(777, util.invokeMethod("getX", null));
});
}
@Test
void testField_setAndUseGetter() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/Constructor.java", "Constructor");
util.invokeMethod("setX", new Class<?>[]{int.class}, 777);
Assertions.assertEquals(777, util.invokeMethod("getX", null));
});
}
}

View File

@ -0,0 +1,33 @@
package e2e;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
class TestForLoop {
@Test
void testFor_simple() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/ForLoop.java", "ForLoop");
Assertions.assertEquals(10, util.invokeMethod("simple", null));
});
}
@Test
void testFor_withContinue() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/ForLoop.java", "ForLoop");
Assertions.assertEquals(8, util.invokeMethod("withContinue", null));
});
}
@Test
void testFor_withBreak() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/ForLoop.java", "ForLoop");
Assertions.assertEquals(3, util.invokeMethod("withBreak", null));
});
}
}

View File

@ -0,0 +1,33 @@
package e2e;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestIf {
@Test
void testIf_simple() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/If.java", "If");
assertEquals(1, util.invokeMethod("simple", new Class<?>[]{int.class}, 1));
});
}
@Test
void testIf_simple2() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/If.java", "If");
assertEquals(0, util.invokeMethod("simple", new Class<?>[]{int.class}, 0));
});
}
@Test
void testIf_nested() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/If.java", "If");
assertEquals(0, util.invokeMethod("nested", new Class<?>[]{int.class}, 0));
});
}
}

View File

@ -0,0 +1,17 @@
package e2e;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class TestNewClass {
@Test
void testNew_withFields() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/NewClass.java", "NewClass");
Assertions.assertEquals(10, util.invokeMethod("simple", null));
});
}
}

View File

@ -0,0 +1,32 @@
package e2e;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class TestWhileLoop {
@Test
void testWhile_simple() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/WhileLoop.java", "WhileLoop");
Assertions.assertEquals(10, util.invokeMethod("simple", null));
});
}
@Test
void testWhile_withContinue() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/WhileLoop.java", "WhileLoop");
Assertions.assertEquals(8, util.invokeMethod("withContinue", null));
});
}
@Test
void testWhile_withBreak() {
assertDoesNotThrow(() -> {
BytecodeTestUtil util = new BytecodeTestUtil("src/test/testFiles/e2e/WhileLoop.java", "WhileLoop");
Assertions.assertEquals(3, util.invokeMethod("withBreak", null));
});
}
}

View File

@ -0,0 +1,18 @@
public class Constructor {
public int x;
public Constructor() {
this.x = 12;
}
public Constructor(int y) {
this.x = y;
}
public int getX() {
return this.x;
}
public void setX(int y) {
this.x = y;
}
}

View File

@ -0,0 +1,31 @@
public class ForLoop {
public int simple() {
int sum = 0;
for (int i = 0; i < 5; i+=1) {
sum += i;
}
return sum;
}
public int withContinue() {
int sum = 0;
for (int i = 0; i < 5; i+=1) {
if (i == 2) {
continue;
}
sum += i;
}
return sum;
}
public int withBreak() {
int sum = 0;
for (int i = 0; i < 5; i+=1) {
if (i == 3) {
break;
}
sum += i;
}
return sum;
}
}

View File

@ -0,0 +1,22 @@
public class If {
public int simple(int x) {
if (x > 0) {
return 1;
} else {
return 0;
}
}
public int nested(int x) {
if (x > 0) {
if (x > 1) {
return 2;
} else {
return 1;
}
} else {
return 0;
}
return -1;
}
}

View File

@ -0,0 +1,13 @@
public class NewClass {
public int x;
public NewClass() {
x = 6;
}
public int simple() {
x = 4;
NewClass y = new NewClass();
return x + y.x;
}
}

View File

@ -0,0 +1,38 @@
public class WhileLoop {
public int simple() {
int sum = 0;
int i = 0;
while (i < 5) {
sum += i;
i += 1;
}
return sum;
}
public int withContinue() {
int sum = 0;
int i = 0;
while (i < 5) {
if (i == 2) {
i += 1;
continue;
}
sum += i;
i += 1;
}
return sum;
}
public int withBreak() {
int sum = 0;
int i = 0;
while (i < 5) {
if (i == 3) {
break;
}
sum += i;
i += 1;
}
return sum;
}
}