forked from JavaTX/JavaCompilerCore
New test case
This commit is contained in:
parent
b0f7a264c2
commit
f46c2ad0f7
17
resources/bytecode/javFiles/Switch.jav
Normal file
17
resources/bytecode/javFiles/Switch.jav
Normal 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; }
|
||||
};
|
||||
}
|
||||
}
|
@ -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<TargetLambdaExpression, TargetMethod> 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);
|
||||
|
@ -681,7 +681,7 @@ public class JavaTXCompiler {
|
||||
var converter = new ASTToTargetAST(typeInferenceResult, sf, classLoader);
|
||||
var generatedClasses = new HashMap<JavaClassName, byte[]>();
|
||||
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) -> {
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user