New test case

This commit is contained in:
Daniel Holle 2023-08-01 14:02:19 +02:00
parent b0f7a264c2
commit f46c2ad0f7
6 changed files with 92 additions and 16 deletions

View File

@ -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; }
};
}
}

View File

@ -1,11 +1,18 @@
package de.dhbwstuttgart.bytecode; package de.dhbwstuttgart.bytecode;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.exceptions.NotImplementedException; 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.*;
import de.dhbwstuttgart.target.tree.expression.*; import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.*; import de.dhbwstuttgart.target.tree.type.*;
import org.objectweb.asm.*; import org.objectweb.asm.*;
import java.io.File;
import java.lang.invoke.*; import java.lang.invoke.*;
import java.util.*; import java.util.*;
@ -19,11 +26,13 @@ public class Codegen {
public final String className; public final String className;
private int lambdaCounter = 0; private int lambdaCounter = 0;
private final HashMap<TargetLambdaExpression, TargetMethod> lambdas = new HashMap<>(); private final HashMap<TargetLambdaExpression, TargetMethod> lambdas = new HashMap<>();
private final JavaTXCompiler compiler;
public Codegen(TargetStructure clazz) { public Codegen(TargetStructure clazz, JavaTXCompiler compiler) {
this.clazz = clazz; this.clazz = clazz;
this.className = clazz.qualifiedName(); this.className = clazz.qualifiedName();
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
this.compiler = compiler;
} }
private record LocalVar(int index, String name, TargetType type) { private record LocalVar(int index, String name, TargetType type) {
@ -296,7 +305,7 @@ public class Codegen {
} else if (type.equals(TargetType.Double)) { } else if (type.equals(TargetType.Double)) {
mv.visitInsn(DADD); mv.visitInsn(DADD);
} else { } 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)) { if (add.type().equals(TargetType.String)) {
@ -1110,7 +1119,7 @@ public class Codegen {
types[i] = lit.value(); types[i] = lit.value();
else if (label instanceof TargetSwitch.Guard guard) else if (label instanceof TargetSwitch.Guard guard)
types[i] = Type.getObjectType(guard.inner().type().getInternalName()); 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(); else throw new NotImplementedException();
} }
@ -1144,10 +1153,10 @@ public class Codegen {
var label = cse.labels().get(0); var label = cse.labels().get(0);
if (label instanceof TargetSwitch.Guard gd){ if (label instanceof TargetSwitch.Guard gd){
state.mv.visitVarInsn(ALOAD, tmp); 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) { } else if (label instanceof TargetSwitch.Pattern pat) {
state.mv.visitVarInsn(ALOAD, tmp); state.mv.visitVarInsn(ALOAD, tmp);
bindPattern(state, aSwitch.expr().type(), pat); bindPattern(state, aSwitch.expr().type(), pat, start);
} }
if (label instanceof TargetSwitch.Guard gd) { if (label instanceof TargetSwitch.Guard gd) {
@ -1188,13 +1197,30 @@ public class Codegen {
state.exitScope(); 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) { if (pat instanceof TargetSwitch.SimplePattern sp) {
var local = state.createVariable(sp.name(), sp.type()); 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); state.mv.visitVarInsn(ASTORE, local.index);
} else if (pat instanceof TargetSwitch.ComplexPattern cp) { } else if (pat instanceof TargetSwitch.ComplexPattern cp) {
convertTo(state, type, cp.type()); 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(); 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() { private void generateRecordMethods() {
var mt = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, TypeDescriptor.class, Class.class, String.class, MethodHandle[].class); 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); var bootstrap = new Handle(H_INVOKESTATIC, "java/lang/runtime/ObjectMethods", "bootstrap", mt.toMethodDescriptorString(), false);

View File

@ -681,7 +681,7 @@ public class JavaTXCompiler {
var converter = new ASTToTargetAST(typeInferenceResult, sf, classLoader); var converter = new ASTToTargetAST(typeInferenceResult, sf, classLoader);
var generatedClasses = new HashMap<JavaClassName, byte[]>(); var generatedClasses = new HashMap<JavaClassName, byte[]>();
for (var clazz : sf.getClasses()) { for (var clazz : sf.getClasses()) {
var codegen = new Codegen(converter.convert(clazz)); var codegen = new Codegen(converter.convert(clazz), this);
var code = codegen.generate(); var code = codegen.generate();
generatedClasses.put(clazz.getClassName(), code); generatedClasses.put(clazz.getClassName(), code);
converter.auxiliaries.forEach((name, source) -> { converter.auxiliaries.forEach((name, source) -> {

View File

@ -358,11 +358,13 @@ public class StatementToTargetExpression implements StatementVisitor {
public void visit(SwitchBlock switchBlock) {} public void visit(SwitchBlock switchBlock) {}
@Override @Override
public void visit(SwitchLabel switchLabel) {} public void visit(SwitchLabel switchLabel) {
result = converter.convert(switchLabel.getExpression());
}
@Override @Override
public void visit(Yield aYield) { public void visit(Yield aYield) {
// TODO Auto-generated method stub result = new TargetYield(converter.convert(aYield.retexpr));
} }
@Override @Override

View File

@ -1,5 +1,6 @@
import de.dhbwstuttgart.environment.ByteArrayClassLoader; import de.dhbwstuttgart.environment.ByteArrayClassLoader;
import de.dhbwstuttgart.syntaxtree.statement.Expression;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -649,4 +650,11 @@ public class TestComplete {
System.out.println(clazz.getDeclaredMethod("hashCode").invoke(instance)); System.out.println(clazz.getDeclaredMethod("hashCode").invoke(instance));
System.out.println(clazz.getDeclaredMethod("toString").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();
}
} }

View File

@ -19,11 +19,10 @@ import static org.junit.Assert.*;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Target;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -53,7 +52,7 @@ public class TestCodegen {
result.putAll(classes.stream().map(cli -> { result.putAll(classes.stream().map(cli -> {
try { try {
return generateClass(converter.convert(cli), classLoader); return generateClass(converter.convert(cli), classLoader, compiler);
} catch (IOException exception) { } catch (IOException exception) {
throw new RuntimeException(exception); throw new RuntimeException(exception);
} }
@ -67,8 +66,15 @@ public class TestCodegen {
return result; return result;
} }
public static Class<?> generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException { public static Class<?> generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException, ClassNotFoundException {
var codegen = new Codegen(clazz); 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(); var code = codegen.generate();
writeClassFile(clazz.qualifiedName(), code); writeClassFile(clazz.qualifiedName(), code);
return classLoader.loadClass(code); return classLoader.loadClass(code);
@ -85,7 +91,7 @@ public class TestCodegen {
var result = classes.stream().map(cli -> { var result = classes.stream().map(cli -> {
try { try {
return generateClass(converter.convert(cli), classLoader); return generateClass(converter.convert(cli), classLoader, compiler);
} catch (IOException exception) { } catch (IOException exception) {
throw new RuntimeException(exception); throw new RuntimeException(exception);
} }