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;
|
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);
|
||||||
|
@ -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) -> {
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user