Merge branch 'targetBytecode' of https://gitea.hb.dhbw-stuttgart.de/JavaTX/JavaCompilerCore into targetBytecode
This commit is contained in:
commit
bdcd5ea3cf
4
pom.xml
4
pom.xml
@ -54,8 +54,8 @@ http://maven.apache.org/maven-v4_0_0.xsd">
|
|||||||
<version>3.11.0</version>
|
<version>3.11.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<compilerArgs>--enable-preview</compilerArgs>
|
<compilerArgs>--enable-preview</compilerArgs>
|
||||||
<source>21</source>
|
<source>22</source>
|
||||||
<target>21</target>
|
<target>22</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
15
resources/bytecode/javFiles/Bug332.jav
Normal file
15
resources/bytecode/javFiles/Bug332.jav
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import java.lang.Object;
|
||||||
|
|
||||||
|
interface Visitor {
|
||||||
|
public void visit(Object obj);
|
||||||
|
public void visit(ClassA a);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassA {
|
||||||
|
void accept(Visitor v) {
|
||||||
|
v.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Bug332 {
|
||||||
|
}
|
10
resources/bytecode/javFiles/Bug337.jav
Normal file
10
resources/bytecode/javFiles/Bug337.jav
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Number;
|
||||||
|
import java.lang.Object;
|
||||||
|
|
||||||
|
public class Bug337 {
|
||||||
|
public void main() {
|
||||||
|
Fun1$$<Object, Integer> fun1 = x -> x.hashCode() + 1;
|
||||||
|
Fun1$$<Number, Number> fun2 = fun1;
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
import java.lang.Runnable;
|
|
||||||
import java.lang.System;
|
|
||||||
import java.lang.String;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
|
|
||||||
public class LamRunnable {
|
|
||||||
|
|
||||||
public LamRunnable() {
|
|
||||||
Runnable lam = () -> {
|
|
||||||
System.out.println("lambda");
|
|
||||||
};
|
|
||||||
|
|
||||||
lam.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,6 +5,8 @@ import de.dhbwstuttgart.exceptions.NotImplementedException;
|
|||||||
import de.dhbwstuttgart.parser.NullToken;
|
import de.dhbwstuttgart.parser.NullToken;
|
||||||
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
||||||
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.Method;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.Pattern;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefType;
|
import de.dhbwstuttgart.syntaxtree.type.RefType;
|
||||||
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
|
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
|
||||||
import de.dhbwstuttgart.target.generate.StatementToTargetExpression;
|
import de.dhbwstuttgart.target.generate.StatementToTargetExpression;
|
||||||
@ -31,15 +33,21 @@ public class Codegen {
|
|||||||
private final JavaTXCompiler compiler;
|
private final JavaTXCompiler compiler;
|
||||||
private final ASTToTargetAST converter;
|
private final ASTToTargetAST converter;
|
||||||
|
|
||||||
|
private class CustomClassWriter extends ClassWriter {
|
||||||
|
public CustomClassWriter() {
|
||||||
|
super(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ClassLoader getClassLoader() {
|
||||||
|
return compiler.getClassLoader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Codegen(TargetStructure clazz, JavaTXCompiler compiler, ASTToTargetAST converter) {
|
public Codegen(TargetStructure clazz, JavaTXCompiler compiler, ASTToTargetAST converter) {
|
||||||
this.clazz = clazz;
|
this.clazz = clazz;
|
||||||
this.className = clazz.qualifiedName().getClassName();
|
this.className = clazz.qualifiedName().getClassName();
|
||||||
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
|
this.cw = new CustomClassWriter();
|
||||||
@Override
|
|
||||||
protected ClassLoader getClassLoader() {
|
|
||||||
return compiler.getClassLoader();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.compiler = compiler;
|
this.compiler = compiler;
|
||||||
this.converter = converter;
|
this.converter = converter;
|
||||||
}
|
}
|
||||||
@ -82,8 +90,6 @@ public class Codegen {
|
|||||||
int localCounter;
|
int localCounter;
|
||||||
MethodVisitor mv;
|
MethodVisitor mv;
|
||||||
TargetType returnType;
|
TargetType returnType;
|
||||||
// This is used to remember the type from lambda expressions
|
|
||||||
TargetType contextType;
|
|
||||||
|
|
||||||
Stack<BreakEnv> breakStack = new Stack<>();
|
Stack<BreakEnv> breakStack = new Stack<>();
|
||||||
Stack<Integer> switchResultValue = new Stack<>();
|
Stack<Integer> switchResultValue = new Stack<>();
|
||||||
@ -270,13 +276,43 @@ public class Codegen {
|
|||||||
mv.visitInsn(I2F);
|
mv.visitInsn(I2F);
|
||||||
else if (dest.equals(TargetType.Double))
|
else if (dest.equals(TargetType.Double))
|
||||||
mv.visitInsn(I2D);
|
mv.visitInsn(I2D);
|
||||||
|
} else if (isFunctionalInterface(source) && isFunctionalInterface(dest) &&
|
||||||
|
!(source instanceof TargetFunNType && dest instanceof TargetFunNType)) {
|
||||||
|
boxFunctionalInterface(state, source, dest);
|
||||||
} else if (!(dest instanceof TargetGenericType)) {
|
} else if (!(dest instanceof TargetGenericType)) {
|
||||||
boxPrimitive(state, source);
|
//boxPrimitive(state, source);
|
||||||
mv.visitTypeInsn(CHECKCAST, dest.getInternalName());
|
mv.visitTypeInsn(CHECKCAST, dest.getInternalName());
|
||||||
unboxPrimitive(state, dest);
|
unboxPrimitive(state, dest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record TypePair(TargetType from, TargetType to) {}
|
||||||
|
private Map<TypePair, String> funWrapperClasses = new HashMap<>();
|
||||||
|
|
||||||
|
private void boxFunctionalInterface(State state, TargetType source, TargetType dest) {
|
||||||
|
var mv = state.mv;
|
||||||
|
var className = "FunWrapper$$" +
|
||||||
|
source.name().replaceAll("\\.", "\\$") +
|
||||||
|
"$_$" +
|
||||||
|
dest.name().replaceAll("\\.", "\\$");
|
||||||
|
|
||||||
|
funWrapperClasses.put(new TypePair(source, dest), className);
|
||||||
|
mv.visitTypeInsn(NEW, className);
|
||||||
|
mv.visitInsn(DUP_X1);
|
||||||
|
mv.visitInsn(SWAP);
|
||||||
|
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(" + source.toDescriptor() + ")V", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFunctionalInterface(TargetType type) {
|
||||||
|
if (type instanceof TargetFunNType) return true;
|
||||||
|
if (type instanceof TargetRefType) {
|
||||||
|
var clazz = compiler.getClass(new JavaClassName(type.name()));
|
||||||
|
return (clazz.getModifiers() & Modifier.INTERFACE) != 0 && clazz.isFunctionalInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private TargetType largerType(TargetType left, TargetType right) {
|
private TargetType largerType(TargetType left, TargetType right) {
|
||||||
if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
|
if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
|
||||||
return TargetType.String;
|
return TargetType.String;
|
||||||
@ -727,41 +763,15 @@ public class Codegen {
|
|||||||
var mv = state.mv;
|
var mv = state.mv;
|
||||||
|
|
||||||
String methodName = "apply";
|
String methodName = "apply";
|
||||||
TargetMethod.Signature signature = null;
|
TargetMethod.Signature signature = new TargetMethod.Signature(Set.of(),
|
||||||
|
lambda.signature().parameters().stream().map(
|
||||||
if (!(state.contextType instanceof TargetFunNType ctx)) {
|
par -> par.withType(TargetType.Object)).toList(),
|
||||||
var intf = compiler.getClass(new JavaClassName(state.contextType.name()));
|
lambda.signature().returnType() != null ? TargetType.Object : null);
|
||||||
if (intf != null) {
|
|
||||||
var method = intf.getMethods().stream().filter(m -> Modifier.isAbstract(m.modifier)).findFirst().orElseThrow();
|
|
||||||
methodName = method.getName();
|
|
||||||
var methodParams = new ArrayList<MethodParameter>();
|
|
||||||
for (var i = 0; i < lambda.signature().parameters().size(); i++) {
|
|
||||||
var param = lambda.signature().parameters().get(i);
|
|
||||||
var tpe = converter.convert(method.getParameterList().getParameterAt(i).getType());
|
|
||||||
methodParams.add(param.withType(tpe));
|
|
||||||
}
|
|
||||||
var retType = converter.convert(method.getReturnType());
|
|
||||||
signature = new TargetMethod.Signature(Set.of(), methodParams, retType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (signature == null) {
|
|
||||||
signature = new TargetMethod.Signature(Set.of(), lambda.signature().parameters().stream().map(par -> par.withType(TargetType.Object)).toList(), TargetType.Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
signature = new TargetMethod.Signature(
|
|
||||||
signature.generics(),
|
|
||||||
signature.parameters().stream().map(par ->
|
|
||||||
par.withType(par.pattern().type() instanceof TargetGenericType ? TargetType.Object : par.pattern().type())
|
|
||||||
).toList(),
|
|
||||||
signature.returnType() instanceof TargetGenericType ? TargetType.Object : signature.returnType()
|
|
||||||
);
|
|
||||||
|
|
||||||
var parameters = new ArrayList<>(lambda.captures());
|
var parameters = new ArrayList<>(lambda.captures());
|
||||||
parameters.addAll(signature.parameters());
|
parameters.addAll(signature.parameters());
|
||||||
var implSignature = new TargetMethod.Signature(Set.of(), parameters, lambda.signature().returnType());
|
var implSignature = new TargetMethod.Signature(Set.of(), parameters, lambda.signature().returnType());
|
||||||
|
|
||||||
// Normalize
|
|
||||||
|
|
||||||
TargetMethod impl;
|
TargetMethod impl;
|
||||||
if (lambdas.containsKey(lambda)) {
|
if (lambdas.containsKey(lambda)) {
|
||||||
impl = lambdas.get(lambda);
|
impl = lambdas.get(lambda);
|
||||||
@ -782,7 +792,6 @@ public class Codegen {
|
|||||||
params.add(new TargetRefType(clazz.qualifiedName().getClassName()));
|
params.add(new TargetRefType(clazz.qualifiedName().getClassName()));
|
||||||
params.addAll(lambda.captures().stream().map(mp -> mp.pattern().type()).toList());
|
params.addAll(lambda.captures().stream().map(mp -> mp.pattern().type()).toList());
|
||||||
|
|
||||||
var descriptor = TargetMethod.getDescriptor(state.contextType, params.toArray(TargetType[]::new));
|
|
||||||
mv.visitVarInsn(ALOAD, 0);
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
for (var index = 0; index < lambda.captures().size(); index++) {
|
for (var index = 0; index < lambda.captures().size(); index++) {
|
||||||
var capture = lambda.captures().get(index);
|
var capture = lambda.captures().get(index);
|
||||||
@ -792,9 +801,42 @@ public class Codegen {
|
|||||||
mv.visitTypeInsn(CHECKCAST, capture.pattern().type().getInternalName());
|
mv.visitTypeInsn(CHECKCAST, capture.pattern().type().getInternalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var descriptor = TargetMethod.getDescriptor(lambda.type(), params.toArray(TargetType[]::new));
|
||||||
mv.visitInvokeDynamicInsn(methodName, descriptor, bootstrap, Type.getType(signature.getSignature()), handle, Type.getType(signature.getDescriptor()));
|
mv.visitInvokeDynamicInsn(methodName, descriptor, bootstrap, Type.getType(signature.getSignature()), handle, Type.getType(signature.getDescriptor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int findReturnCode(TargetType returnType) {
|
||||||
|
if (returnType.equals(TargetType.boolean_)
|
||||||
|
|| returnType.equals(TargetType.char_)
|
||||||
|
|| returnType.equals(TargetType.int_)
|
||||||
|
|| returnType.equals(TargetType.short_)
|
||||||
|
|| returnType.equals(TargetType.byte_))
|
||||||
|
return IRETURN;
|
||||||
|
else if (returnType.equals(TargetType.long_))
|
||||||
|
return LRETURN;
|
||||||
|
else if (returnType.equals(TargetType.float_))
|
||||||
|
return FRETURN;
|
||||||
|
else if (returnType.equals(TargetType.double_))
|
||||||
|
return DRETURN;
|
||||||
|
return ARETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findLoadCode(TargetType loadType) {
|
||||||
|
if (loadType.equals(TargetType.boolean_)
|
||||||
|
|| loadType.equals(TargetType.char_)
|
||||||
|
|| loadType.equals(TargetType.int_)
|
||||||
|
|| loadType.equals(TargetType.short_)
|
||||||
|
|| loadType.equals(TargetType.byte_))
|
||||||
|
return ILOAD;
|
||||||
|
else if (loadType.equals(TargetType.long_))
|
||||||
|
return LLOAD;
|
||||||
|
else if (loadType.equals(TargetType.float_))
|
||||||
|
return FLOAD;
|
||||||
|
else if (loadType.equals(TargetType.double_))
|
||||||
|
return DLOAD;
|
||||||
|
return ALOAD;
|
||||||
|
}
|
||||||
|
|
||||||
private void generate(State state, TargetExpression expr) {
|
private void generate(State state, TargetExpression expr) {
|
||||||
var mv = state.mv;
|
var mv = state.mv;
|
||||||
switch (expr) {
|
switch (expr) {
|
||||||
@ -819,10 +861,7 @@ public class Codegen {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TargetCast cast:
|
case TargetCast cast:
|
||||||
var ctx = state.contextType;
|
|
||||||
state.contextType = cast.type();
|
|
||||||
generate(state, cast.expr());
|
generate(state, cast.expr());
|
||||||
state.contextType = ctx;
|
|
||||||
convertTo(state, cast.expr().type(), cast.type());
|
convertTo(state, cast.expr().type(), cast.type());
|
||||||
break;
|
break;
|
||||||
case TargetInstanceOf instanceOf:
|
case TargetInstanceOf instanceOf:
|
||||||
@ -867,10 +906,7 @@ public class Codegen {
|
|||||||
case TargetAssign assign: {
|
case TargetAssign assign: {
|
||||||
switch (assign.left()) {
|
switch (assign.left()) {
|
||||||
case TargetLocalVar localVar -> {
|
case TargetLocalVar localVar -> {
|
||||||
var ctype = state.contextType;
|
|
||||||
state.contextType = localVar.type();
|
|
||||||
generate(state, assign.right());
|
generate(state, assign.right());
|
||||||
state.contextType = ctype;
|
|
||||||
|
|
||||||
convertTo(state, assign.right().type(), localVar.type());
|
convertTo(state, assign.right().type(), localVar.type());
|
||||||
boxPrimitive(state, localVar.type());
|
boxPrimitive(state, localVar.type());
|
||||||
@ -883,10 +919,7 @@ public class Codegen {
|
|||||||
if (!(dot.left() instanceof TargetThis && dot.isStatic()))
|
if (!(dot.left() instanceof TargetThis && dot.isStatic()))
|
||||||
generate(state, dot.left());
|
generate(state, dot.left());
|
||||||
|
|
||||||
var ctype = state.contextType;
|
|
||||||
state.contextType = fieldType;
|
|
||||||
generate(state, assign.right());
|
generate(state, assign.right());
|
||||||
state.contextType = ctype;
|
|
||||||
|
|
||||||
convertTo(state, assign.right().type(), fieldType);
|
convertTo(state, assign.right().type(), fieldType);
|
||||||
boxPrimitive(state, fieldType);
|
boxPrimitive(state, fieldType);
|
||||||
@ -1016,29 +1049,12 @@ public class Codegen {
|
|||||||
case TargetReturn ret: {
|
case TargetReturn ret: {
|
||||||
if (ret.expression() != null && state.returnType != null) {
|
if (ret.expression() != null && state.returnType != null) {
|
||||||
if (state.returnType instanceof TargetPrimitiveType) {
|
if (state.returnType instanceof TargetPrimitiveType) {
|
||||||
var ctype = state.contextType;
|
|
||||||
state.contextType = state.returnType;
|
|
||||||
generate(state, ret.expression());
|
generate(state, ret.expression());
|
||||||
state.contextType = ctype;
|
|
||||||
|
|
||||||
unboxPrimitive(state, state.returnType);
|
unboxPrimitive(state, state.returnType);
|
||||||
if (state.returnType.equals(TargetType.boolean_)
|
mv.visitInsn(findReturnCode(state.returnType));
|
||||||
|| state.returnType.equals(TargetType.char_)
|
|
||||||
|| state.returnType.equals(TargetType.int_)
|
|
||||||
|| state.returnType.equals(TargetType.short_)
|
|
||||||
|| state.returnType.equals(TargetType.byte_))
|
|
||||||
mv.visitInsn(IRETURN);
|
|
||||||
else if (state.returnType.equals(TargetType.long_))
|
|
||||||
mv.visitInsn(LRETURN);
|
|
||||||
else if (state.returnType.equals(TargetType.float_))
|
|
||||||
mv.visitInsn(FRETURN);
|
|
||||||
else if (state.returnType.equals(TargetType.double_))
|
|
||||||
mv.visitInsn(DRETURN);
|
|
||||||
} else {
|
} else {
|
||||||
var ctype = state.contextType;
|
|
||||||
state.contextType = state.returnType;
|
|
||||||
generate(state, ret.expression());
|
generate(state, ret.expression());
|
||||||
state.contextType = ctype;
|
|
||||||
boxPrimitive(state, ret.expression().type());
|
boxPrimitive(state, ret.expression().type());
|
||||||
convertTo(state, ret.expression().type(), state.returnType);
|
convertTo(state, ret.expression().type(), state.returnType);
|
||||||
mv.visitInsn(ARETURN);
|
mv.visitInsn(ARETURN);
|
||||||
@ -1089,12 +1105,10 @@ public class Codegen {
|
|||||||
for (var i = 0; i < call.args().size(); i++) {
|
for (var i = 0; i < call.args().size(); i++) {
|
||||||
var e = call.args().get(i);
|
var e = call.args().get(i);
|
||||||
var arg = call.parameterTypes().get(i);
|
var arg = call.parameterTypes().get(i);
|
||||||
var ctype = state.contextType;
|
|
||||||
state.contextType = arg;
|
|
||||||
generate(state, e);
|
generate(state, e);
|
||||||
|
convertTo(state, e.type(), arg);
|
||||||
if (!(arg instanceof TargetPrimitiveType))
|
if (!(arg instanceof TargetPrimitiveType))
|
||||||
boxPrimitive(state, e.type());
|
boxPrimitive(state, e.type());
|
||||||
state.contextType = ctype;
|
|
||||||
}
|
}
|
||||||
var descriptor = call.getDescriptor();
|
var descriptor = call.getDescriptor();
|
||||||
if (call.owner() instanceof TargetFunNType) // Decay FunN
|
if (call.owner() instanceof TargetFunNType) // Decay FunN
|
||||||
@ -1597,6 +1611,85 @@ public class Codegen {
|
|||||||
if (clazz instanceof TargetRecord)
|
if (clazz instanceof TargetRecord)
|
||||||
generateRecordMethods();
|
generateRecordMethods();
|
||||||
|
|
||||||
|
// Generate wrapper classes for function types
|
||||||
|
for (var pair : funWrapperClasses.keySet()) {
|
||||||
|
var className = funWrapperClasses.get(pair);
|
||||||
|
ClassWriter cw2 = new CustomClassWriter();
|
||||||
|
cw2.visit(V1_8, ACC_PUBLIC, className, null, "java/lang/Object", new String[] { pair.to.getInternalName() });
|
||||||
|
cw2.visitField(ACC_PRIVATE, "wrapped", pair.from.toDescriptor(), null, null).visitEnd();
|
||||||
|
|
||||||
|
// Generate constructor
|
||||||
|
var ctor = cw2.visitMethod(ACC_PUBLIC, "<init>", "(" + pair.from.toDescriptor() + ")V", null, null);
|
||||||
|
ctor.visitVarInsn(ALOAD, 0);
|
||||||
|
ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||||
|
ctor.visitVarInsn(ALOAD, 0);
|
||||||
|
ctor.visitVarInsn(ALOAD, 1);
|
||||||
|
ctor.visitFieldInsn(PUTFIELD, className, "wrapped", pair.from.toDescriptor());
|
||||||
|
ctor.visitInsn(RETURN);
|
||||||
|
ctor.visitMaxs(0, 0);
|
||||||
|
ctor.visitEnd();
|
||||||
|
|
||||||
|
String methodName = "apply";
|
||||||
|
String fromDescriptor = null;
|
||||||
|
TargetType fromReturn = null;
|
||||||
|
if (!(pair.from instanceof TargetFunNType funNType)) {
|
||||||
|
var fromClass = compiler.getClass(new JavaClassName(pair.from.name()));
|
||||||
|
var fromMethod = fromClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
|
||||||
|
methodName = fromMethod.name;
|
||||||
|
|
||||||
|
fromReturn = converter.convert(fromMethod.getReturnType());
|
||||||
|
var fromParams = converter.convert(fromMethod.getParameterList(), converter.generics.javaGenerics()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
|
||||||
|
fromDescriptor = TargetMethod.getDescriptor(fromReturn, fromParams);
|
||||||
|
} else {
|
||||||
|
fromReturn = funNType.returnArguments() > 0 ? TargetType.Object : null;
|
||||||
|
fromDescriptor = funNType.toMethodDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
var toClass = compiler.getClass(new JavaClassName(pair.to.name()));
|
||||||
|
var toMethod = toClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
|
||||||
|
var toReturn = converter.convert(toMethod.getReturnType());
|
||||||
|
var toParams = converter.convert(toMethod.getParameterList(), converter.generics.javaGenerics()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
|
||||||
|
var toDescriptor = TargetMethod.getDescriptor(toReturn, toParams);
|
||||||
|
|
||||||
|
// Generate wrapper method
|
||||||
|
var mv = cw2.visitMethod(ACC_PUBLIC, toMethod.name, toDescriptor, null, null);
|
||||||
|
var state = new State(null, mv, 0);
|
||||||
|
|
||||||
|
mv.visitVarInsn(ALOAD, 0);
|
||||||
|
mv.visitFieldInsn(GETFIELD, className, "wrapped", pair.from.toDescriptor());
|
||||||
|
for (var i = 0; i < toParams.length; i++) {
|
||||||
|
var arg = toParams[i];
|
||||||
|
mv.visitVarInsn(findLoadCode(arg), i + 1);
|
||||||
|
}
|
||||||
|
mv.visitMethodInsn(INVOKEINTERFACE, pair.from.getInternalName(), methodName, fromDescriptor, true);
|
||||||
|
if (fromReturn != null) {
|
||||||
|
if (toReturn instanceof TargetPrimitiveType) {
|
||||||
|
convertTo(state, fromReturn, TargetType.toWrapper(toReturn));
|
||||||
|
} else convertTo(state, fromReturn, toReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toReturn != null)
|
||||||
|
mv.visitInsn(findReturnCode(toReturn));
|
||||||
|
|
||||||
|
else mv.visitInsn(RETURN);
|
||||||
|
mv.visitMaxs(0, 0);
|
||||||
|
mv.visitEnd();
|
||||||
|
|
||||||
|
cw2.visitEnd();
|
||||||
|
var bytes = cw2.toByteArray();
|
||||||
|
converter.auxiliaries.put(className, bytes);
|
||||||
|
|
||||||
|
// TODO These class loading shenanigans happen in a few places, the tests load the classes individually.
|
||||||
|
// Instead we should just look at the folder.
|
||||||
|
try {
|
||||||
|
converter.classLoader.findClass(className);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
try {
|
||||||
|
converter.classLoader.loadClass(bytes);
|
||||||
|
} catch (LinkageError ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cw.visitEnd();
|
cw.visitEnd();
|
||||||
return cw.toByteArray();
|
return cw.toByteArray();
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,22 @@ public class FunNGenerator {
|
|||||||
public static class GenericParameters {
|
public static class GenericParameters {
|
||||||
int start;
|
int start;
|
||||||
public List<TargetType> parameters = new ArrayList<>();
|
public List<TargetType> parameters = new ArrayList<>();
|
||||||
|
final String descriptor;
|
||||||
|
public final List<TargetType> inParams;
|
||||||
|
|
||||||
|
public GenericParameters(List<TargetType> params, int numReturns) {
|
||||||
|
this.inParams = params;
|
||||||
|
var type = new TargetRefType(FunNGenerator.getSuperClassName(params.size() - 1, numReturns), params);
|
||||||
|
descriptor = applyDescriptor(type, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TargetType getReturnType() {
|
||||||
|
return FunNGenerator.getReturnType(inParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TargetType> getArguments() {
|
||||||
|
return FunNGenerator.getArguments(inParams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String applyDescriptor(TargetType type, GenericParameters gep) {
|
private static String applyDescriptor(TargetType type, GenericParameters gep) {
|
||||||
@ -69,7 +85,7 @@ public class FunNGenerator {
|
|||||||
return applyNameDescriptor(type).replace("/", "$").replace(";", "$_$");
|
return applyNameDescriptor(type).replace("/", "$").replace(";", "$_$");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] generateSuperBytecode(int numberArguments) {
|
public static byte[] generateSuperBytecode(int numberArguments, int numReturnTypes) {
|
||||||
StringBuilder superFunNClassSignature = new StringBuilder("<");
|
StringBuilder superFunNClassSignature = new StringBuilder("<");
|
||||||
StringBuilder superFunNMethodSignature = new StringBuilder("(");
|
StringBuilder superFunNMethodSignature = new StringBuilder("(");
|
||||||
StringBuilder superFunNMethodDescriptor = new StringBuilder("(");
|
StringBuilder superFunNMethodDescriptor = new StringBuilder("(");
|
||||||
@ -80,30 +96,34 @@ public class FunNGenerator {
|
|||||||
superFunNMethodDescriptor.append(objectSignature);
|
superFunNMethodDescriptor.append(objectSignature);
|
||||||
}
|
}
|
||||||
superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature));
|
superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature));
|
||||||
superFunNMethodSignature.append(String.format(")T%s;", returnGeneric));
|
if (numReturnTypes > 0) {
|
||||||
superFunNMethodDescriptor.append(String.format(")%s", objectSignature));
|
superFunNMethodSignature.append(String.format(")T%s;", returnGeneric));
|
||||||
|
superFunNMethodDescriptor.append(String.format(")%s", objectSignature));
|
||||||
|
} else {
|
||||||
|
superFunNMethodSignature.append(")V");
|
||||||
|
superFunNMethodDescriptor.append(")V");
|
||||||
|
}
|
||||||
|
|
||||||
System.out.println(superFunNMethodSignature);
|
System.out.println(superFunNMethodSignature);
|
||||||
|
|
||||||
ClassWriter classWriter = new ClassWriter(0);
|
ClassWriter classWriter = new ClassWriter(0);
|
||||||
MethodVisitor methodVisitor;
|
MethodVisitor methodVisitor;
|
||||||
classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSuperClassName(numberArguments), superFunNClassSignature.toString(), objectSuperType, null);
|
classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSuperClassName(numberArguments, numReturnTypes), superFunNClassSignature.toString(), objectSuperType, null);
|
||||||
methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, methodName, superFunNMethodDescriptor.toString(), superFunNMethodSignature.toString(), null);
|
methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, methodName, superFunNMethodDescriptor.toString(), superFunNMethodSignature.toString(), null);
|
||||||
methodVisitor.visitEnd();
|
methodVisitor.visitEnd();
|
||||||
classWriter.visitEnd();
|
classWriter.visitEnd();
|
||||||
return classWriter.toByteArray();
|
return classWriter.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSuperClassName(int numberArguments) {
|
public static String getSuperClassName(int numberArguments, int returnArguments) {
|
||||||
return String.format("Fun%d$$", numberArguments);
|
return returnArguments > 0 ? String.format("Fun%d$$", numberArguments) : String.format("FunVoid%d$$", numberArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] generateSpecializedBytecode(List<TargetType> argumentTypes, TargetType returnType, GenericParameters gep) {
|
public static byte[] generateSpecializedBytecode(GenericParameters gep, List<String> superInterfaces) {
|
||||||
List<TargetType> parameters = Stream
|
var argumentTypes = gep.getArguments();
|
||||||
.concat(argumentTypes.stream(), Stream.of(returnType))
|
var returnType = gep.getReturnType();
|
||||||
.toList();
|
|
||||||
|
|
||||||
StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters), gep));
|
StringBuilder funNClassSignature = new StringBuilder(objectSignature + gep.descriptor);
|
||||||
boolean containsGeneric = false;
|
boolean containsGeneric = false;
|
||||||
|
|
||||||
String genericSignature = "<";
|
String genericSignature = "<";
|
||||||
@ -114,10 +134,18 @@ public class FunNGenerator {
|
|||||||
|
|
||||||
genericSignature += ">";
|
genericSignature += ">";
|
||||||
if (containsGeneric) funNClassSignature.insert(0, genericSignature);
|
if (containsGeneric) funNClassSignature.insert(0, genericSignature);
|
||||||
System.out.println(funNClassSignature.toString());
|
|
||||||
|
for (var superInterface : superInterfaces) {
|
||||||
|
funNClassSignature.append('L');
|
||||||
|
funNClassSignature.append(superInterface);
|
||||||
|
funNClassSignature.append(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
var interfaces = new ArrayList<>(superInterfaces);
|
||||||
|
interfaces.add(getSuperClassName(argumentTypes.size(), returnType != null ? 1 : 0));
|
||||||
|
|
||||||
ClassWriter classWriter = new ClassWriter(0);
|
ClassWriter classWriter = new ClassWriter(0);
|
||||||
classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSpecializedClassName(argumentTypes, returnType), funNClassSignature.toString(), objectSuperType, new String[]{getSuperClassName(argumentTypes.size())});
|
classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSpecializedClassName(argumentTypes, returnType), funNClassSignature.toString(), objectSuperType, interfaces.toArray(String[]::new));
|
||||||
classWriter.visitEnd();
|
classWriter.visitEnd();
|
||||||
return classWriter.toByteArray();
|
return classWriter.toByteArray();
|
||||||
}
|
}
|
||||||
@ -133,14 +161,24 @@ public class FunNGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getSpecializedClassName(GenericParameters gep) {
|
||||||
|
return getSpecializedClassName(getArguments(gep.inParams), getReturnType(gep.inParams));
|
||||||
|
}
|
||||||
|
|
||||||
public static String getSpecializedClassName(List<TargetType> argumentTypes, TargetType returnType) {
|
public static String getSpecializedClassName(List<TargetType> argumentTypes, TargetType returnType) {
|
||||||
return String.format("Fun%d$$%s%s",
|
var arguments = argumentTypes
|
||||||
|
.stream()
|
||||||
|
.map(FunNGenerator::encodeType)
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
|
||||||
|
if (returnType != null)
|
||||||
|
return String.format("Fun%d$$%s%s",
|
||||||
|
argumentTypes.size(),
|
||||||
|
arguments,
|
||||||
|
encodeType(returnType));
|
||||||
|
else return String.format("FunVoidImpl%d$$%s",
|
||||||
argumentTypes.size(),
|
argumentTypes.size(),
|
||||||
argumentTypes
|
arguments);
|
||||||
.stream()
|
|
||||||
.map(FunNGenerator::encodeType)
|
|
||||||
.collect(Collectors.joining()),
|
|
||||||
encodeType(returnType));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<TargetType> getArguments(List<TargetType> list) {
|
public static List<TargetType> getArguments(List<TargetType> list) {
|
||||||
@ -151,8 +189,8 @@ public class FunNGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static TargetType getReturnType(List<TargetType> list) {
|
public static TargetType getReturnType(List<TargetType> list) {
|
||||||
if(list.size() == 0)
|
if(list.isEmpty())
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
return list.get(list.size() - 1);
|
return list.getLast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -783,6 +783,7 @@ public class JavaTXCompiler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
generatedGenerics.put(sf, converter.javaGenerics());
|
generatedGenerics.put(sf, converter.javaGenerics());
|
||||||
|
converter.generateFunNTypes();
|
||||||
return generatedClasses;
|
return generatedClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import de.dhbwstuttgart.bytecode.FunNGenerator;
|
|||||||
import de.dhbwstuttgart.core.JavaTXCompiler;
|
import de.dhbwstuttgart.core.JavaTXCompiler;
|
||||||
import de.dhbwstuttgart.environment.ByteArrayClassLoader;
|
import de.dhbwstuttgart.environment.ByteArrayClassLoader;
|
||||||
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
|
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
|
||||||
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
|
||||||
import de.dhbwstuttgart.parser.NullToken;
|
import de.dhbwstuttgart.parser.NullToken;
|
||||||
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
||||||
import de.dhbwstuttgart.syntaxtree.*;
|
import de.dhbwstuttgart.syntaxtree.*;
|
||||||
@ -18,23 +17,25 @@ import de.dhbwstuttgart.target.tree.expression.*;
|
|||||||
import de.dhbwstuttgart.target.tree.type.*;
|
import de.dhbwstuttgart.target.tree.type.*;
|
||||||
import de.dhbwstuttgart.typeinference.result.*;
|
import de.dhbwstuttgart.typeinference.result.*;
|
||||||
|
|
||||||
import javax.sql.rowset.RowSetWarning;
|
|
||||||
import java.lang.annotation.Target;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author dholle
|
* @author dholle
|
||||||
*/
|
*/
|
||||||
public class ASTToTargetAST {
|
public class ASTToTargetAST {
|
||||||
|
|
||||||
|
record SignaturePair(TypePlaceholder signature, RefTypeOrTPHOrWildcardOrGeneric parameter) {};
|
||||||
|
record SignaturePairTarget(TargetType signature, TargetType parameter) {}
|
||||||
|
|
||||||
public static RefType OBJECT = ASTFactory.createObjectType(); // TODO It would be better if I could call this directly but the hashcode seems to change
|
public static RefType OBJECT = ASTFactory.createObjectType(); // TODO It would be better if I could call this directly but the hashcode seems to change
|
||||||
|
|
||||||
protected List<Generics> all;
|
protected List<Generics> all;
|
||||||
public Generics generics;
|
public Generics generics;
|
||||||
final Map<ClassOrInterface, Set<GenericTypeVar>> userDefinedGenerics = new HashMap<>();
|
final Map<ClassOrInterface, Set<GenericTypeVar>> userDefinedGenerics = new HashMap<>();
|
||||||
|
final Map<Method, Set<SignaturePair>> tphsInMethods = new HashMap<>();
|
||||||
|
private Method currentMethod;
|
||||||
|
|
||||||
public final JavaTXCompiler compiler;
|
public final JavaTXCompiler compiler;
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected IByteArrayClassLoader classLoader;
|
public IByteArrayClassLoader classLoader;
|
||||||
protected SourceFile sourceFile;
|
protected SourceFile sourceFile;
|
||||||
|
|
||||||
public ASTToTargetAST(List<ResultSet> resultSets) {
|
public ASTToTargetAST(List<ResultSet> resultSets) {
|
||||||
@ -78,6 +79,12 @@ public class ASTToTargetAST {
|
|||||||
this.generics = all.get(0);
|
this.generics = all.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSignaturePair(TypePlaceholder signature, RefTypeOrTPHOrWildcardOrGeneric parameter) {
|
||||||
|
var set = tphsInMethods.getOrDefault(currentMethod, new HashSet<>());
|
||||||
|
set.add(new SignaturePair(signature, parameter));
|
||||||
|
tphsInMethods.put(currentMethod, set);
|
||||||
|
}
|
||||||
|
|
||||||
Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList) {
|
Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList) {
|
||||||
Optional<Method> method = Optional.empty();
|
Optional<Method> method = Optional.empty();
|
||||||
while (method.isEmpty()) {
|
while (method.isEmpty()) {
|
||||||
@ -131,6 +138,40 @@ public class ASTToTargetAST {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<List<TargetMethod>> groupOverloads(ClassOrInterface input, List<Method> methods) {
|
||||||
|
var res = new ArrayList<List<TargetMethod>>();
|
||||||
|
for (var method : methods) {
|
||||||
|
// Convert all methods
|
||||||
|
var methodsWithTphs = convert(input, method);
|
||||||
|
// Then check for methods with the same signature
|
||||||
|
var mapOfSignatures = new HashMap<TargetMethod.Signature, List<MethodWithTphs>>();
|
||||||
|
for (var m : methodsWithTphs) {
|
||||||
|
var methodsWithSameSignature = mapOfSignatures.getOrDefault(m.method.signature(), new ArrayList<>());
|
||||||
|
methodsWithSameSignature.add(m);
|
||||||
|
mapOfSignatures.put(m.method.signature(), methodsWithSameSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
var resMethods = new HashSet<TargetMethod>();
|
||||||
|
for (var methodsWithSignature : mapOfSignatures.values()) {
|
||||||
|
outer: for (var m1 : methodsWithSignature) {
|
||||||
|
for (var m2 : methodsWithSignature) {
|
||||||
|
for (var i = 0; i < m1.args.size(); i++) {
|
||||||
|
var arg1 = m1.args.get(i);
|
||||||
|
var arg2 = m2.args.get(i);
|
||||||
|
if (arg1.parameter.equals(arg2.parameter)) {
|
||||||
|
if (isSupertype(arg1.signature, arg2.signature) &&
|
||||||
|
!arg1.signature.equals(arg2.signature)) continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resMethods.add(m1.method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.add(resMethods.stream().toList());
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
public TargetStructure convert(ClassOrInterface input) {
|
public TargetStructure convert(ClassOrInterface input) {
|
||||||
Set<TargetGeneric> javaGenerics = new HashSet<>();
|
Set<TargetGeneric> javaGenerics = new HashSet<>();
|
||||||
Set<TargetGeneric> txGenerics = new HashSet<>();
|
Set<TargetGeneric> txGenerics = new HashSet<>();
|
||||||
@ -161,11 +202,11 @@ public class ASTToTargetAST {
|
|||||||
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics)).toList();
|
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics)).toList();
|
||||||
var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer)).flatMap(List::stream).toList();
|
var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer)).flatMap(List::stream).toList();
|
||||||
var fields = input.getFieldDecl().stream().map(this::convert).toList();
|
var fields = input.getFieldDecl().stream().map(this::convert).toList();
|
||||||
var methods = groupOverloads(input.getMethods()).stream().map(m -> convert(input, m)).flatMap(Set::stream).toList();
|
var methods = groupOverloads(input, input.getMethods()).stream().flatMap(List::stream).toList();
|
||||||
|
|
||||||
TargetMethod staticConstructor = null;
|
TargetMethod staticConstructor = null;
|
||||||
if (input.getStaticInitializer().isPresent())
|
if (input.getStaticInitializer().isPresent())
|
||||||
staticConstructor = this.convert(input, input.getStaticInitializer().get()).stream().findFirst().orElseThrow();
|
staticConstructor = this.convert(input, input.getStaticInitializer().get()).stream().findFirst().orElseThrow().method;
|
||||||
|
|
||||||
if (input instanceof Record)
|
if (input instanceof Record)
|
||||||
return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods);
|
return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods);
|
||||||
@ -206,6 +247,7 @@ public class ASTToTargetAST {
|
|||||||
generics = all.get(0);
|
generics = all.get(0);
|
||||||
List<TargetConstructor> result = new ArrayList<>();
|
List<TargetConstructor> result = new ArrayList<>();
|
||||||
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
||||||
|
this.currentMethod = input;
|
||||||
|
|
||||||
for (var s : all) {
|
for (var s : all) {
|
||||||
generics = s;
|
generics = s;
|
||||||
@ -225,56 +267,6 @@ public class ASTToTargetAST {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This only considers type patterns, all other methods aren't grouped together
|
|
||||||
* @param a
|
|
||||||
* @param b
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private boolean signatureEquals(Method a, Method b) {
|
|
||||||
if (!a.name.equals(b.name)) return false;
|
|
||||||
var para = a.getParameterList().getFormalparalist();
|
|
||||||
var parb = b.getParameterList().getFormalparalist();
|
|
||||||
if (para.size() != parb.size()) return false;
|
|
||||||
|
|
||||||
for (var i = 0; i < para.size(); i++) {
|
|
||||||
var pa = para.get(i);
|
|
||||||
var pb = parb.get(i);
|
|
||||||
|
|
||||||
if (pa instanceof RecordPattern rpa) {
|
|
||||||
if (pb instanceof RecordPattern rpb) {
|
|
||||||
if (rpa.getType().equals(rpb.getType())) continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} else if (pa.getType().equals(pb.getType())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Nested patterns
|
|
||||||
private List<List<Method>> groupOverloads(List<Method> input) {
|
|
||||||
var done = new HashSet<Method>();
|
|
||||||
var res = new ArrayList<List<Method>>();
|
|
||||||
for (var method : input) {
|
|
||||||
if (done.contains(method)) continue;
|
|
||||||
var overloads = new ArrayList<Method>();
|
|
||||||
overloads.add(method);
|
|
||||||
done.add(method);
|
|
||||||
for (var method2 : input) {
|
|
||||||
if (!done.contains(method2) && signatureEquals(method, method2)) {
|
|
||||||
done.add(method2);
|
|
||||||
overloads.add(method2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.add(overloads);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String encodeName(String name, ParameterList params) {
|
private String encodeName(String name, ParameterList params) {
|
||||||
var res = new StringBuilder();
|
var res = new StringBuilder();
|
||||||
res.append(name);
|
res.append(name);
|
||||||
@ -290,9 +282,9 @@ public class ASTToTargetAST {
|
|||||||
return res.toString();
|
return res.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<TargetMethod> convert(ClassOrInterface clazz, List<Method> overloadedMethods) {
|
private List<TargetMethod> convert(ClassOrInterface clazz, List<Method> overloadedMethods) {
|
||||||
if (overloadedMethods.size() == 1) {
|
if (overloadedMethods.size() == 1) {
|
||||||
return convert(clazz, overloadedMethods.getFirst());
|
return convert(clazz, overloadedMethods.getFirst()).stream().map(m -> m.method()).toList();
|
||||||
}
|
}
|
||||||
var methods = new ArrayList<Method>();
|
var methods = new ArrayList<Method>();
|
||||||
for (var method : overloadedMethods) {
|
for (var method : overloadedMethods) {
|
||||||
@ -329,10 +321,11 @@ public class ASTToTargetAST {
|
|||||||
var entryPoint = new Method(template.modifier, template.name, template.getReturnType(), params, block, template.getGenerics(), new NullToken());
|
var entryPoint = new Method(template.modifier, template.name, template.getReturnType(), params, block, template.getGenerics(), new NullToken());
|
||||||
|
|
||||||
res.add(entryPoint); // TODO*/
|
res.add(entryPoint); // TODO*/
|
||||||
var res = new HashSet<TargetMethod>();
|
var res = new ArrayList<TargetMethod>();
|
||||||
for (var method : methods) {
|
for (var method : methods) {
|
||||||
var overloads = convert(clazz, method);
|
var overloads = convert(clazz, method);
|
||||||
for (var overload : overloads) {
|
for (var m : overloads) {
|
||||||
|
var overload = m.method;
|
||||||
if (res.contains(overload)) throw new CodeGenException("Duplicate method found: " + overload.name() + " with signature " + overload.signature().getSignature());
|
if (res.contains(overload)) throw new CodeGenException("Duplicate method found: " + overload.name() + " with signature " + overload.signature().getSignature());
|
||||||
res.add(overload);
|
res.add(overload);
|
||||||
}
|
}
|
||||||
@ -381,10 +374,12 @@ public class ASTToTargetAST {
|
|||||||
}).findFirst();
|
}).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<TargetMethod> convert(ClassOrInterface currentClass, Method method) {
|
record MethodWithTphs(TargetMethod method, List<SignaturePairTarget> args) {}
|
||||||
generics = all.get(0);
|
|
||||||
Set<TargetMethod> result = new HashSet<>();
|
private List<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) {
|
||||||
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
generics = all.getFirst();
|
||||||
|
List<MethodWithTphs> result = new ArrayList<>();
|
||||||
|
this.currentMethod = method;
|
||||||
|
|
||||||
for (var s : all) {
|
for (var s : all) {
|
||||||
generics = s;
|
generics = s;
|
||||||
@ -402,19 +397,18 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MethodParameter> finalParams = params;
|
List<MethodParameter> txParams = convert(method.getParameterList(), this.generics.txGenerics);
|
||||||
if (parameterSet.stream().noneMatch(p -> p.equals(finalParams))) {
|
|
||||||
List<MethodParameter> txParams = convert(method.getParameterList(), this.generics.txGenerics);
|
|
||||||
|
|
||||||
var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, method);
|
var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, method);
|
||||||
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, method);
|
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, method);
|
||||||
|
|
||||||
var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType);
|
var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType);
|
||||||
var txSignature = new TargetMethod.Signature(txMethodGenerics, txParams, convert(method.getReturnType(), this.generics.txGenerics));
|
var txSignature = new TargetMethod.Signature(txMethodGenerics, txParams, convert(method.getReturnType(), this.generics.txGenerics));
|
||||||
var newMethod = new TargetMethod(method.modifier, method.name, convert(method.block), javaSignature, txSignature);
|
var newMethod = new TargetMethod(method.modifier, method.name, convert(method.block), javaSignature, txSignature);
|
||||||
result.add(newMethod);
|
|
||||||
parameterSet.add(params);
|
var concreteParams = tphsInMethods.getOrDefault(method, new HashSet<>()).stream().map(sig -> new SignaturePairTarget(convert(sig.signature), convert(sig.parameter))).toList();
|
||||||
}
|
|
||||||
|
result.add(new MethodWithTphs(newMethod, concreteParams));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -486,10 +480,59 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
var filteredParams = new ArrayList<TargetType>();
|
var filteredParams = new ArrayList<TargetType>();
|
||||||
for (var i = 0; i < newParams.size(); i++) {
|
for (var i = 0; i < newParams.size(); i++) {
|
||||||
if (gep.parameters.get(i) != null)
|
if (i < gep.inParams.size() && gep.inParams.get(i) != null)
|
||||||
filteredParams.add(newParams.get(i));
|
filteredParams.add(newParams.get(i));
|
||||||
}
|
}
|
||||||
return TargetFunNType.fromParams(params, filteredParams);
|
return TargetFunNType.fromParams(params, filteredParams, gep.getReturnType() != null ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSubtype(TargetType test, TargetType other) {
|
||||||
|
var testClass = compiler.getClass(new JavaClassName(test.name()));
|
||||||
|
var otherClass = compiler.getClass(new JavaClassName(other.name()));
|
||||||
|
if (testClass == null) return false;
|
||||||
|
while (testClass != null) {
|
||||||
|
if (testClass.equals(otherClass)) return true;
|
||||||
|
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break;
|
||||||
|
testClass = compiler.getClass(testClass.getSuperClass().getName());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSupertype(TargetType test, TargetType other) {
|
||||||
|
return isSubtype(other, test);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSubtype(FunNGenerator.GenericParameters test, FunNGenerator.GenericParameters other) {
|
||||||
|
if (test.getArguments().size() != other.getArguments().size()) return false;
|
||||||
|
if (!isSubtype(test.getReturnType(), other.getReturnType())) return false;
|
||||||
|
for (int i = 0; i < test.getArguments().size(); i++) {
|
||||||
|
var arg1 = test.getArguments().get(i);
|
||||||
|
var arg2 = other.getArguments().get(i);
|
||||||
|
if (!isSupertype(arg1, arg2)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateFunNTypes() {
|
||||||
|
for (var entry : usedFunN.entrySet()) {
|
||||||
|
var gep = entry.getValue();
|
||||||
|
var superInterfaces = usedFunN.values().stream()
|
||||||
|
.filter(g -> !g.equals(gep))
|
||||||
|
.filter(genericParameters -> isSubtype(gep, genericParameters))
|
||||||
|
.map(FunNGenerator::getSpecializedClassName)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
var code = FunNGenerator.generateSpecializedBytecode(gep, superInterfaces);
|
||||||
|
|
||||||
|
try {
|
||||||
|
classLoader.findClass(entry.getKey());
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
try {
|
||||||
|
classLoader.loadClass(code);
|
||||||
|
} catch (LinkageError ignored) {}
|
||||||
|
}
|
||||||
|
auxiliaries.put(entry.getKey(), code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input, GenerateGenerics generics) {
|
protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input, GenerateGenerics generics) {
|
||||||
@ -504,17 +547,16 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var params = refType.getParaList().stream().map(type -> {
|
var params = refType.getParaList().stream().map(type -> {
|
||||||
var res = convert(type, generics);
|
return convert(type, generics);
|
||||||
if (res == null) res = new TargetRefType("java.lang.Void");
|
|
||||||
return res;
|
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
if (name.matches("Fun\\d+\\$\\$")) { // TODO This seems like a bad idea
|
if (name.matches("Fun\\d+\\$\\$")) { // TODO This seems like a bad idea
|
||||||
var className = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params));
|
var returnType = FunNGenerator.getReturnType(params);
|
||||||
|
var className = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), returnType);
|
||||||
if (!usedFunNSuperTypes.contains(params.size())) {
|
if (!usedFunNSuperTypes.contains(params.size())) {
|
||||||
usedFunNSuperTypes.add(params.size());
|
usedFunNSuperTypes.add(params.size());
|
||||||
var code = FunNGenerator.generateSuperBytecode(params.size() - 1);
|
var code = FunNGenerator.generateSuperBytecode(params.size() - 1, returnType != null ? 1 : 0);
|
||||||
var superClassName = FunNGenerator.getSuperClassName(params.size() - 1);
|
var superClassName = FunNGenerator.getSuperClassName(params.size() - 1, returnType != null ? 1 : 0);
|
||||||
try {
|
try {
|
||||||
classLoader.findClass(superClassName);
|
classLoader.findClass(superClassName);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
@ -526,17 +568,8 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
FunNGenerator.GenericParameters gep = null;
|
FunNGenerator.GenericParameters gep = null;
|
||||||
if (!usedFunN.containsKey(className)) {
|
if (!usedFunN.containsKey(className)) {
|
||||||
gep = new FunNGenerator.GenericParameters();
|
gep = new FunNGenerator.GenericParameters(params, returnType != null ? 1 : 0);
|
||||||
var code = FunNGenerator.generateSpecializedBytecode(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params), gep);
|
|
||||||
try {
|
|
||||||
classLoader.findClass(className);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
try {
|
|
||||||
classLoader.loadClass(code);
|
|
||||||
} catch (LinkageError ignored) {}
|
|
||||||
}
|
|
||||||
usedFunN.put(className, gep);
|
usedFunN.put(className, gep);
|
||||||
auxiliaries.put(className, code);
|
|
||||||
} else {
|
} else {
|
||||||
gep = usedFunN.get(className);
|
gep = usedFunN.get(className);
|
||||||
}
|
}
|
||||||
|
@ -208,6 +208,11 @@ public class StatementToTargetExpression implements ASTVisitor {
|
|||||||
var isPrivate = false;
|
var isPrivate = false;
|
||||||
var signature = methodCall.signatureArguments().stream().map(converter::convert).toList();
|
var signature = methodCall.signatureArguments().stream().map(converter::convert).toList();
|
||||||
|
|
||||||
|
// Add used TPHs to containing method
|
||||||
|
for (var i = 0; i < methodCall.signatureArguments().size(); i++) {
|
||||||
|
converter.addSignaturePair(methodCall.signatureArguments().get(i), methodCall.arglist.getArguments().get(i).getType());
|
||||||
|
}
|
||||||
|
|
||||||
var receiverClass = converter.compiler.getClass(receiverName);
|
var receiverClass = converter.compiler.getClass(receiverName);
|
||||||
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) {
|
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) {
|
||||||
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
|
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
|
||||||
|
@ -4,15 +4,29 @@ import de.dhbwstuttgart.bytecode.FunNGenerator;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public record TargetFunNType(String name, List<TargetType> params) implements TargetSpecializedType {
|
public record TargetFunNType(String name, List<TargetType> funNParams, List<TargetType> params, int returnArguments) implements TargetSpecializedType {
|
||||||
|
|
||||||
public static TargetFunNType fromParams(List<TargetType> params) {
|
public static TargetFunNType fromParams(List<TargetType> params, int returnArguments) {
|
||||||
return fromParams(params, params);
|
return fromParams(params, params, returnArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams) {
|
public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams, int returnArguments) {
|
||||||
var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params));
|
var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params));
|
||||||
return new TargetFunNType(name, realParams);
|
return new TargetFunNType(name, params, realParams, returnArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toMethodDescriptor() {
|
||||||
|
var res = "(";
|
||||||
|
for (var i = 0; i < funNParams.size() - 1; i++) {
|
||||||
|
res += "Ljava/lang/Object;";
|
||||||
|
}
|
||||||
|
res += ")";
|
||||||
|
if (returnArguments > 0) {
|
||||||
|
res += "Ljava/lang/Object;";
|
||||||
|
} else {
|
||||||
|
res += "V";
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,19 +53,17 @@ public sealed interface TargetType
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static TargetType toTargetType(Class<?> clazz) {
|
static TargetType toWrapper(TargetType f) {
|
||||||
if (clazz.isPrimitive()) {
|
if (f.equals(boolean_)) return Boolean;
|
||||||
if (clazz.equals(boolean.class)) return boolean_;
|
if (f.equals(char_)) return Char;
|
||||||
if (clazz.equals(char.class)) return char_;
|
if (f.equals(byte_)) return Byte;
|
||||||
if (clazz.equals(byte.class)) return byte_;
|
if (f.equals(short_)) return Short;
|
||||||
if (clazz.equals(short.class)) return short_;
|
if (f.equals(int_)) return Integer;
|
||||||
if (clazz.equals(int.class)) return int_;
|
if (f.equals(long_)) return Long;
|
||||||
if (clazz.equals(long.class)) return long_;
|
if (f.equals(float_)) return Float;
|
||||||
if (clazz.equals(float.class)) return float_;
|
if (f.equals(double_)) return Double;
|
||||||
if (clazz.equals(double.class)) return double_;
|
|
||||||
}
|
return f;
|
||||||
if (clazz.equals(void.class)) return null;
|
|
||||||
return new TargetRefType(clazz.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String toSignature();
|
String toSignature();
|
||||||
|
@ -860,13 +860,6 @@ public class TestComplete {
|
|||||||
var instance = clazz.getDeclaredConstructor().newInstance();
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLamRunnable() throws Exception {
|
|
||||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "LamRunnable.jav");
|
|
||||||
var clazz = classFiles.get("LamRunnable");
|
|
||||||
var instance = clazz.getDeclaredConstructor().newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAccess() throws Exception {
|
public void testAccess() throws Exception {
|
||||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Access.jav");
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Access.jav");
|
||||||
@ -1126,6 +1119,13 @@ public class TestComplete {
|
|||||||
var instance = clazz.getDeclaredConstructor().newInstance();
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBug332() throws Exception {
|
||||||
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug332.jav");
|
||||||
|
var clazz = classFiles.get("Bug332");
|
||||||
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBug333() throws Exception {
|
public void testBug333() throws Exception {
|
||||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug333.jav");
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug333.jav");
|
||||||
@ -1133,6 +1133,14 @@ public class TestComplete {
|
|||||||
var instance = clazz.getDeclaredConstructor().newInstance();
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBug337() throws Exception {
|
||||||
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug337.jav");
|
||||||
|
var clazz = classFiles.get("Bug337");
|
||||||
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
clazz.getDeclaredMethod("main").invoke(instance);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBug338() throws Exception {
|
public void testBug338() throws Exception {
|
||||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug338.jav");
|
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug338.jav");
|
||||||
|
@ -60,6 +60,8 @@ public class TestCodegen {
|
|||||||
}
|
}
|
||||||
}).collect(Collectors.toMap(Class::getName, Function.identity())));
|
}).collect(Collectors.toMap(Class::getName, Function.identity())));
|
||||||
|
|
||||||
|
converter.generateFunNTypes();
|
||||||
|
|
||||||
for (var entry : converter.auxiliaries.entrySet()) {
|
for (var entry : converter.auxiliaries.entrySet()) {
|
||||||
writeClassFile(entry.getKey(), entry.getValue());
|
writeClassFile(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
@ -104,6 +106,8 @@ public class TestCodegen {
|
|||||||
}
|
}
|
||||||
}).collect(Collectors.toMap(Class::getName, Function.identity()));
|
}).collect(Collectors.toMap(Class::getName, Function.identity()));
|
||||||
|
|
||||||
|
converter.generateFunNTypes();
|
||||||
|
|
||||||
for (var entry : converter.auxiliaries.entrySet()) {
|
for (var entry : converter.auxiliaries.entrySet()) {
|
||||||
writeClassFile(entry.getKey(), entry.getValue());
|
writeClassFile(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
@ -273,7 +277,7 @@ public class TestCodegen {
|
|||||||
public void testLambda() throws Exception {
|
public void testLambda() throws Exception {
|
||||||
var classLoader = new ByteArrayClassLoader();
|
var classLoader = new ByteArrayClassLoader();
|
||||||
// var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class"));
|
// var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class"));
|
||||||
var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer));
|
var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer), 1);
|
||||||
|
|
||||||
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("CGLambda"));
|
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("CGLambda"));
|
||||||
targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "<init>", false, false, false))));
|
targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "<init>", false, false, false))));
|
||||||
|
Loading…
Reference in New Issue
Block a user