From f46c2ad0f7dccee495410687ce92f3aabfd0876b Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Tue, 1 Aug 2023 14:02:19 +0200 Subject: [PATCH] New test case --- resources/bytecode/javFiles/Switch.jav | 17 ++++++ .../de/dhbwstuttgart/bytecode/Codegen.java | 57 ++++++++++++++++--- .../de/dhbwstuttgart/core/JavaTXCompiler.java | 2 +- .../generate/StatementToTargetExpression.java | 6 +- src/test/java/TestComplete.java | 8 +++ src/test/java/targetast/TestCodegen.java | 18 ++++-- 6 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 resources/bytecode/javFiles/Switch.jav diff --git a/resources/bytecode/javFiles/Switch.jav b/resources/bytecode/javFiles/Switch.jav new file mode 100644 index 00000000..4166eb7e --- /dev/null +++ b/resources/bytecode/javFiles/Switch.jav @@ -0,0 +1,17 @@ +import java.lang.Integer; +import java.lang.Object; +import java.lang.Float; + +record Rec(Integer a, Object b) {} + +public class Switch { + main(Object o) { + return switch (o) { + case Rec(Integer a, Integer b) -> { yield a + b; } + case Rec(Integer a, Float b) -> { yield a * b; } + case Rec(Integer a, Rec(Integer b, Integer c)) -> { yield a + b + c; } + case Integer i -> { yield i; } + default -> { yield 0; } + }; + } +} \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java index a09531c1..7f0bb0d5 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -1,11 +1,18 @@ package de.dhbwstuttgart.bytecode; +import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.exceptions.NotImplementedException; +import de.dhbwstuttgart.parser.scope.JavaClassName; +import de.dhbwstuttgart.syntaxtree.ClassOrInterface; +import de.dhbwstuttgart.syntaxtree.SourceFile; +import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; +import de.dhbwstuttgart.syntaxtree.type.RefType; import de.dhbwstuttgart.target.tree.*; import de.dhbwstuttgart.target.tree.expression.*; import de.dhbwstuttgart.target.tree.type.*; import org.objectweb.asm.*; +import java.io.File; import java.lang.invoke.*; import java.util.*; @@ -19,11 +26,13 @@ public class Codegen { public final String className; private int lambdaCounter = 0; private final HashMap lambdas = new HashMap<>(); + private final JavaTXCompiler compiler; - public Codegen(TargetStructure clazz) { + public Codegen(TargetStructure clazz, JavaTXCompiler compiler) { this.clazz = clazz; this.className = clazz.qualifiedName(); this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + this.compiler = compiler; } private record LocalVar(int index, String name, TargetType type) { @@ -296,7 +305,7 @@ public class Codegen { } else if (type.equals(TargetType.Double)) { mv.visitInsn(DADD); } else { - throw new CodeGenException("Invalid argument to Add expression"); + throw new CodeGenException("Invalid argument to Add expression, type: " + add.type()); } } if (add.type().equals(TargetType.String)) { @@ -1110,7 +1119,7 @@ public class Codegen { types[i] = lit.value(); else if (label instanceof TargetSwitch.Guard guard) types[i] = Type.getObjectType(guard.inner().type().getInternalName()); - // TODO Same here we need to evaluate constants + // TODO Same here we need to evaluate constant; else throw new NotImplementedException(); } @@ -1144,10 +1153,10 @@ public class Codegen { var label = cse.labels().get(0); if (label instanceof TargetSwitch.Guard gd){ state.mv.visitVarInsn(ALOAD, tmp); - bindPattern(state, aSwitch.expr().type(), gd.inner()); + bindPattern(state, aSwitch.expr().type(), gd.inner(), start); } else if (label instanceof TargetSwitch.Pattern pat) { state.mv.visitVarInsn(ALOAD, tmp); - bindPattern(state, aSwitch.expr().type(), pat); + bindPattern(state, aSwitch.expr().type(), pat, start); } if (label instanceof TargetSwitch.Guard gd) { @@ -1188,13 +1197,30 @@ public class Codegen { state.exitScope(); } - private void bindPattern(State state, TargetType type, TargetSwitch.Pattern pat) { + private void bindPattern(State state, TargetType type, TargetSwitch.Pattern pat, Label start) { if (pat instanceof TargetSwitch.SimplePattern sp) { var local = state.createVariable(sp.name(), sp.type()); - convertTo(state, type, local.type); + convertTo(state, type, sp.type()); + boxPrimitive(state, sp.type()); state.mv.visitVarInsn(ASTORE, local.index); } else if (pat instanceof TargetSwitch.ComplexPattern cp) { convertTo(state, type, cp.type()); + boxPrimitive(state, cp.type()); + + var clazz = findClass(new JavaClassName(cp.type().name())); + if (clazz == null) throw new CodeGenException("Class definition for '" + cp.type().name() + "' not found"); + // TODO Check if class is a Record + + for (var i = 0; i < cp.subPatterns().size(); i++) { + var subPattern = cp.subPatterns().get(i); + if (i >= clazz.getFieldDecl().size()) + throw new CodeGenException("Couldn't find suitable field accessor for '" + cp.type().name() + "'"); + var field = clazz.getFieldDecl().get(i); + var fieldType = new TargetRefType(((RefType) field.getType()).getName().toString()); + state.mv.visitMethodInsn(INVOKEDYNAMIC, cp.type().getInternalName(), field.getName(), "()" + fieldType.toDescriptor(), false); + convertTo(state, fieldType, subPattern.type()); + bindPattern(state, subPattern.type(), subPattern, start); + } } } @@ -1300,6 +1326,23 @@ public class Codegen { return cw.toByteArray(); } + private ClassOrInterface findClass(JavaClassName className) { + try { + for (var sf : compiler.sourceFiles.values()) { + for (var clazz : compiler.getAvailableClasses(sf)) { + if (clazz.getClassName().equals(className)) + return clazz; + } + for (var clazz : sf.KlassenVektor) { + if (clazz.getClassName().equals(className)) + return clazz; + } + } + } catch (ClassNotFoundException ignored) {} + + return null; + } + private void generateRecordMethods() { var mt = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, TypeDescriptor.class, Class.class, String.class, MethodHandle[].class); var bootstrap = new Handle(H_INVOKESTATIC, "java/lang/runtime/ObjectMethods", "bootstrap", mt.toMethodDescriptorString(), false); diff --git a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java index b6eb211e..192b8b66 100644 --- a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java +++ b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java @@ -681,7 +681,7 @@ public class JavaTXCompiler { var converter = new ASTToTargetAST(typeInferenceResult, sf, classLoader); var generatedClasses = new HashMap(); for (var clazz : sf.getClasses()) { - var codegen = new Codegen(converter.convert(clazz)); + var codegen = new Codegen(converter.convert(clazz), this); var code = codegen.generate(); generatedClasses.put(clazz.getClassName(), code); converter.auxiliaries.forEach((name, source) -> { diff --git a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java index a5b0faf6..a743da23 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java @@ -358,11 +358,13 @@ public class StatementToTargetExpression implements StatementVisitor { public void visit(SwitchBlock switchBlock) {} @Override - public void visit(SwitchLabel switchLabel) {} + public void visit(SwitchLabel switchLabel) { + result = converter.convert(switchLabel.getExpression()); + } @Override public void visit(Yield aYield) { - // TODO Auto-generated method stub + result = new TargetYield(converter.convert(aYield.retexpr)); } @Override diff --git a/src/test/java/TestComplete.java b/src/test/java/TestComplete.java index fbf12853..9679efab 100644 --- a/src/test/java/TestComplete.java +++ b/src/test/java/TestComplete.java @@ -1,5 +1,6 @@ import de.dhbwstuttgart.environment.ByteArrayClassLoader; +import de.dhbwstuttgart.syntaxtree.statement.Expression; import org.junit.Ignore; import org.junit.Test; @@ -649,4 +650,11 @@ public class TestComplete { System.out.println(clazz.getDeclaredMethod("hashCode").invoke(instance)); System.out.println(clazz.getDeclaredMethod("toString").invoke(instance)); } + + @Test + public void testSwitch() throws Exception { + var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Switch.jav"); + var clazz = classFiles.get("Switch"); + var instance = clazz.getDeclaredConstructor().newInstance(); + } } diff --git a/src/test/java/targetast/TestCodegen.java b/src/test/java/targetast/TestCodegen.java index 07f1b176..2f478c2d 100644 --- a/src/test/java/targetast/TestCodegen.java +++ b/src/test/java/targetast/TestCodegen.java @@ -19,11 +19,10 @@ import static org.junit.Assert.*; import org.objectweb.asm.Opcodes; +import java.io.File; import java.io.IOException; -import java.lang.annotation.Target; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardOpenOption; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -53,7 +52,7 @@ public class TestCodegen { result.putAll(classes.stream().map(cli -> { try { - return generateClass(converter.convert(cli), classLoader); + return generateClass(converter.convert(cli), classLoader, compiler); } catch (IOException exception) { throw new RuntimeException(exception); } @@ -67,8 +66,15 @@ public class TestCodegen { return result; } - public static Class generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException { - var codegen = new Codegen(clazz); + public static Class generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException, ClassNotFoundException { + Codegen codegen = new Codegen(clazz, new JavaTXCompiler(List.of())); + var code = codegen.generate(); + writeClassFile(clazz.qualifiedName(), code); + return classLoader.loadClass(code); + } + + public static Class generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader, JavaTXCompiler compiler) throws IOException { + Codegen codegen = new Codegen(clazz, compiler); var code = codegen.generate(); writeClassFile(clazz.qualifiedName(), code); return classLoader.loadClass(code); @@ -85,7 +91,7 @@ public class TestCodegen { var result = classes.stream().map(cli -> { try { - return generateClass(converter.convert(cli), classLoader); + return generateClass(converter.convert(cli), classLoader, compiler); } catch (IOException exception) { throw new RuntimeException(exception); }