Merge branch 'targetBytecode' into patternMatching
This commit is contained in:
commit
4f0162ba64
10
resources/AllgemeinTest/Bar.java
Normal file
10
resources/AllgemeinTest/Bar.java
Normal file
@ -0,0 +1,10 @@
|
||||
public class Bar{
|
||||
|
||||
void visit(Object o){
|
||||
System.out.println("Object");
|
||||
}
|
||||
|
||||
void visit(Bla f){
|
||||
System.out.println("Foo");
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
class Box<A> {
|
||||
public class Box<A> {
|
||||
|
||||
A a;
|
||||
|
||||
public Box() { }
|
||||
public Box(A a) {
|
||||
this.a = a;
|
||||
//this.a = a;
|
||||
}
|
||||
}
|
6
resources/AllgemeinTest/Foo.jav
Normal file
6
resources/AllgemeinTest/Foo.jav
Normal file
@ -0,0 +1,6 @@
|
||||
public class Foo{
|
||||
|
||||
public accept(Bar b){
|
||||
b.visit(this);
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
class Box<A> {
|
||||
void m(A a) { }
|
||||
}
|
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.scope.JavaClassName;
|
||||
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
||||
import de.dhbwstuttgart.syntaxtree.Method;
|
||||
import de.dhbwstuttgart.syntaxtree.Pattern;
|
||||
import de.dhbwstuttgart.syntaxtree.type.RefType;
|
||||
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
|
||||
import de.dhbwstuttgart.target.generate.StatementToTargetExpression;
|
||||
@ -31,15 +33,21 @@ public class Codegen {
|
||||
private final JavaTXCompiler compiler;
|
||||
private final ASTToTargetAST converter;
|
||||
|
||||
public Codegen(TargetStructure clazz, JavaTXCompiler compiler, ASTToTargetAST converter) {
|
||||
this.clazz = clazz;
|
||||
this.className = clazz.qualifiedName().getClassName();
|
||||
this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
|
||||
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) {
|
||||
this.clazz = clazz;
|
||||
this.className = clazz.qualifiedName().getClassName();
|
||||
this.cw = new CustomClassWriter();
|
||||
this.compiler = compiler;
|
||||
this.converter = converter;
|
||||
}
|
||||
@ -82,8 +90,6 @@ public class Codegen {
|
||||
int localCounter;
|
||||
MethodVisitor mv;
|
||||
TargetType returnType;
|
||||
// This is used to remember the type from lambda expressions
|
||||
TargetType contextType;
|
||||
|
||||
Stack<BreakEnv> breakStack = new Stack<>();
|
||||
Stack<Integer> switchResultValue = new Stack<>();
|
||||
@ -270,13 +276,43 @@ public class Codegen {
|
||||
mv.visitInsn(I2F);
|
||||
else if (dest.equals(TargetType.Double))
|
||||
mv.visitInsn(I2D);
|
||||
} else if (isFunctionalInterface(source) && isFunctionalInterface(dest) &&
|
||||
!(source instanceof TargetFunNType && dest instanceof TargetFunNType)) {
|
||||
boxFunctionalInterface(state, source, dest);
|
||||
} else if (!(dest instanceof TargetGenericType)) {
|
||||
boxPrimitive(state, source);
|
||||
//boxPrimitive(state, source);
|
||||
mv.visitTypeInsn(CHECKCAST, dest.getInternalName());
|
||||
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) {
|
||||
if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
|
||||
return TargetType.String;
|
||||
@ -727,41 +763,15 @@ public class Codegen {
|
||||
var mv = state.mv;
|
||||
|
||||
String methodName = "apply";
|
||||
TargetMethod.Signature signature = null;
|
||||
|
||||
if (!(state.contextType instanceof TargetFunNType ctx)) {
|
||||
var intf = compiler.getClass(new JavaClassName(state.contextType.name()));
|
||||
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()
|
||||
);
|
||||
TargetMethod.Signature signature = new TargetMethod.Signature(Set.of(),
|
||||
lambda.signature().parameters().stream().map(
|
||||
par -> par.withType(TargetType.Object)).toList(),
|
||||
lambda.signature().returnType() != null ? TargetType.Object : null);
|
||||
|
||||
var parameters = new ArrayList<>(lambda.captures());
|
||||
parameters.addAll(signature.parameters());
|
||||
var implSignature = new TargetMethod.Signature(Set.of(), parameters, lambda.signature().returnType());
|
||||
|
||||
// Normalize
|
||||
|
||||
TargetMethod impl;
|
||||
if (lambdas.containsKey(lambda)) {
|
||||
impl = lambdas.get(lambda);
|
||||
@ -782,7 +792,6 @@ public class Codegen {
|
||||
params.add(new TargetRefType(clazz.qualifiedName().getClassName()));
|
||||
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);
|
||||
for (var index = 0; index < lambda.captures().size(); index++) {
|
||||
var capture = lambda.captures().get(index);
|
||||
@ -792,9 +801,42 @@ public class Codegen {
|
||||
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()));
|
||||
}
|
||||
|
||||
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) {
|
||||
var mv = state.mv;
|
||||
switch (expr) {
|
||||
@ -819,10 +861,7 @@ public class Codegen {
|
||||
break;
|
||||
}
|
||||
case TargetCast cast:
|
||||
var ctx = state.contextType;
|
||||
state.contextType = cast.type();
|
||||
generate(state, cast.expr());
|
||||
state.contextType = ctx;
|
||||
convertTo(state, cast.expr().type(), cast.type());
|
||||
break;
|
||||
case TargetInstanceOf instanceOf:
|
||||
@ -867,10 +906,7 @@ public class Codegen {
|
||||
case TargetAssign assign: {
|
||||
switch (assign.left()) {
|
||||
case TargetLocalVar localVar -> {
|
||||
var ctype = state.contextType;
|
||||
state.contextType = localVar.type();
|
||||
generate(state, assign.right());
|
||||
state.contextType = ctype;
|
||||
|
||||
convertTo(state, assign.right().type(), localVar.type());
|
||||
boxPrimitive(state, localVar.type());
|
||||
@ -883,10 +919,7 @@ public class Codegen {
|
||||
if (!(dot.left() instanceof TargetThis && dot.isStatic()))
|
||||
generate(state, dot.left());
|
||||
|
||||
var ctype = state.contextType;
|
||||
state.contextType = fieldType;
|
||||
generate(state, assign.right());
|
||||
state.contextType = ctype;
|
||||
|
||||
convertTo(state, assign.right().type(), fieldType);
|
||||
boxPrimitive(state, fieldType);
|
||||
@ -1016,29 +1049,12 @@ public class Codegen {
|
||||
case TargetReturn ret: {
|
||||
if (ret.expression() != null && state.returnType != null) {
|
||||
if (state.returnType instanceof TargetPrimitiveType) {
|
||||
var ctype = state.contextType;
|
||||
state.contextType = state.returnType;
|
||||
generate(state, ret.expression());
|
||||
state.contextType = ctype;
|
||||
|
||||
unboxPrimitive(state, state.returnType);
|
||||
if (state.returnType.equals(TargetType.boolean_)
|
||||
|| 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);
|
||||
mv.visitInsn(findReturnCode(state.returnType));
|
||||
} else {
|
||||
var ctype = state.contextType;
|
||||
state.contextType = state.returnType;
|
||||
generate(state, ret.expression());
|
||||
state.contextType = ctype;
|
||||
boxPrimitive(state, ret.expression().type());
|
||||
convertTo(state, ret.expression().type(), state.returnType);
|
||||
mv.visitInsn(ARETURN);
|
||||
@ -1089,12 +1105,10 @@ public class Codegen {
|
||||
for (var i = 0; i < call.args().size(); i++) {
|
||||
var e = call.args().get(i);
|
||||
var arg = call.parameterTypes().get(i);
|
||||
var ctype = state.contextType;
|
||||
state.contextType = arg;
|
||||
generate(state, e);
|
||||
convertTo(state, e.type(), arg);
|
||||
if (!(arg instanceof TargetPrimitiveType))
|
||||
boxPrimitive(state, e.type());
|
||||
state.contextType = ctype;
|
||||
}
|
||||
var descriptor = call.getDescriptor();
|
||||
if (call.owner() instanceof TargetFunNType) // Decay FunN
|
||||
@ -1600,6 +1614,85 @@ public class Codegen {
|
||||
if (clazz instanceof TargetRecord)
|
||||
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();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package de.dhbwstuttgart.bytecode;
|
||||
|
||||
import de.dhbwstuttgart.syntaxtree.statement.Break;
|
||||
import de.dhbwstuttgart.target.tree.TargetGeneric;
|
||||
import de.dhbwstuttgart.target.tree.type.*;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.sql.Array;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@ -33,6 +35,36 @@ public class FunNGenerator {
|
||||
public static class GenericParameters {
|
||||
int start;
|
||||
public List<TargetType> parameters = new ArrayList<>();
|
||||
final String descriptor;
|
||||
public final List<TargetType> inParams;
|
||||
public final List<TargetType> realParams;
|
||||
|
||||
public GenericParameters(List<TargetType> params, int numReturns) {
|
||||
this.realParams = params;
|
||||
this.inParams = flattenTypeParams(params);
|
||||
var type = new TargetRefType(FunNGenerator.getSuperClassName(params.size() - 1, numReturns), params);
|
||||
descriptor = applyDescriptor(type, this);
|
||||
}
|
||||
|
||||
private static List<TargetType> flattenTypeParams(List<TargetType> params) {
|
||||
var res = new ArrayList<TargetType>();
|
||||
for (var param : params) {
|
||||
if (param instanceof TargetSpecializedType tspec) {
|
||||
res.addAll(flattenTypeParams(tspec.params()));
|
||||
} else {
|
||||
res.add(param);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public TargetType getReturnType() {
|
||||
return FunNGenerator.getReturnType(realParams);
|
||||
}
|
||||
|
||||
public List<TargetType> getArguments() {
|
||||
return FunNGenerator.getArguments(realParams);
|
||||
}
|
||||
}
|
||||
|
||||
private static String applyDescriptor(TargetType type, GenericParameters gep) {
|
||||
@ -69,7 +101,7 @@ public class FunNGenerator {
|
||||
return applyNameDescriptor(type).replace("/", "$").replace(";", "$_$");
|
||||
}
|
||||
|
||||
public static byte[] generateSuperBytecode(int numberArguments) {
|
||||
public static byte[] generateSuperBytecode(int numberArguments, int numReturnTypes) {
|
||||
StringBuilder superFunNClassSignature = new StringBuilder("<");
|
||||
StringBuilder superFunNMethodSignature = new StringBuilder("(");
|
||||
StringBuilder superFunNMethodDescriptor = new StringBuilder("(");
|
||||
@ -80,30 +112,34 @@ public class FunNGenerator {
|
||||
superFunNMethodDescriptor.append(objectSignature);
|
||||
}
|
||||
superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature));
|
||||
if (numReturnTypes > 0) {
|
||||
superFunNMethodSignature.append(String.format(")T%s;", returnGeneric));
|
||||
superFunNMethodDescriptor.append(String.format(")%s", objectSignature));
|
||||
} else {
|
||||
superFunNMethodSignature.append(")V");
|
||||
superFunNMethodDescriptor.append(")V");
|
||||
}
|
||||
|
||||
System.out.println(superFunNMethodSignature);
|
||||
|
||||
ClassWriter classWriter = new ClassWriter(0);
|
||||
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.visitEnd();
|
||||
classWriter.visitEnd();
|
||||
return classWriter.toByteArray();
|
||||
}
|
||||
|
||||
public static String getSuperClassName(int numberArguments) {
|
||||
return String.format("Fun%d$$", numberArguments);
|
||||
public static String getSuperClassName(int numberArguments, int returnArguments) {
|
||||
return returnArguments > 0 ? String.format("Fun%d$$", numberArguments) : String.format("FunVoid%d$$", numberArguments);
|
||||
}
|
||||
|
||||
public static byte[] generateSpecializedBytecode(List<TargetType> argumentTypes, TargetType returnType, GenericParameters gep) {
|
||||
List<TargetType> parameters = Stream
|
||||
.concat(argumentTypes.stream(), Stream.of(returnType))
|
||||
.toList();
|
||||
public static byte[] generateSpecializedBytecode(GenericParameters gep, List<String> superInterfaces) {
|
||||
var argumentTypes = gep.getArguments();
|
||||
var returnType = gep.getReturnType();
|
||||
|
||||
StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters), gep));
|
||||
StringBuilder funNClassSignature = new StringBuilder(objectSignature + gep.descriptor);
|
||||
boolean containsGeneric = false;
|
||||
|
||||
String genericSignature = "<";
|
||||
@ -114,10 +150,18 @@ public class FunNGenerator {
|
||||
|
||||
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.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();
|
||||
return classWriter.toByteArray();
|
||||
}
|
||||
@ -133,14 +177,24 @@ public class FunNGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSpecializedClassName(GenericParameters gep) {
|
||||
return getSpecializedClassName(gep.getArguments(), gep.getReturnType());
|
||||
}
|
||||
|
||||
public static String getSpecializedClassName(List<TargetType> argumentTypes, TargetType returnType) {
|
||||
return String.format("Fun%d$$%s%s",
|
||||
argumentTypes.size(),
|
||||
argumentTypes
|
||||
var arguments = argumentTypes
|
||||
.stream()
|
||||
.map(FunNGenerator::encodeType)
|
||||
.collect(Collectors.joining()),
|
||||
.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(),
|
||||
arguments);
|
||||
}
|
||||
|
||||
public static List<TargetType> getArguments(List<TargetType> list) {
|
||||
@ -151,8 +205,8 @@ public class FunNGenerator {
|
||||
}
|
||||
|
||||
public static TargetType getReturnType(List<TargetType> list) {
|
||||
if(list.size() == 0)
|
||||
if(list.isEmpty())
|
||||
throw new IndexOutOfBoundsException();
|
||||
return list.get(list.size() - 1);
|
||||
return list.getLast();
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class JavaTXCompiler {
|
||||
Boolean resultmodel = true;
|
||||
public final Map<File, SourceFile> sourceFiles = new HashMap<>();
|
||||
|
||||
Boolean log = true; //gibt an ob ein Log-File nach System.getProperty("user.dir")+""/logFiles/"" geschrieben werden soll?
|
||||
Boolean log = false; //gibt an ob ein Log-File nach System.getProperty("user.dir")+""/logFiles/"" geschrieben werden soll?
|
||||
public volatile UnifyTaskModel usedTasks = new UnifyTaskModel();
|
||||
public final DirectoryClassLoader classLoader;
|
||||
|
||||
@ -783,6 +783,7 @@ public class JavaTXCompiler {
|
||||
});
|
||||
}
|
||||
generatedGenerics.put(sf, converter.javaGenerics());
|
||||
converter.generateFunNTypes();
|
||||
return generatedClasses;
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,8 @@ public class FCGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tauscht die GTVs in einem Typ gegen die entsprechenden Typen in der übergebenen Map aus.
|
||||
* Tauscht die GTVs in einem Typ gegen die entsprechenden Typen in der übergebenen Map aus auf der direkten Argumentebene.
|
||||
* Hier sind keine Wildcards zulässig
|
||||
*/
|
||||
private static class TypeExchanger implements TypeVisitor<RefTypeOrTPHOrWildcardOrGeneric>{
|
||||
|
||||
@ -172,7 +173,7 @@ public class FCGenerator {
|
||||
public RefTypeOrTPHOrWildcardOrGeneric visit(RefType refType) {
|
||||
List<RefTypeOrTPHOrWildcardOrGeneric> params = new ArrayList<>();
|
||||
for(RefTypeOrTPHOrWildcardOrGeneric param : refType.getParaList()){
|
||||
params.add(param.acceptTV(this));
|
||||
params.add(param.acceptTV(new TypeExchangerInner(gtvs)));
|
||||
}
|
||||
RefTypeOrTPHOrWildcardOrGeneric ret = new RefType(refType.getName(), params, new NullToken());
|
||||
return ret;
|
||||
@ -201,4 +202,51 @@ public class FCGenerator {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tauscht die GTVs in einem Typ gegen die entsprechenden Typen in der übergebenen Map aus auf den Argumenten der Argumente.
|
||||
* Hier sind Wildcards zulässig
|
||||
*/
|
||||
private static class TypeExchangerInner implements TypeVisitor<RefTypeOrTPHOrWildcardOrGeneric>{
|
||||
|
||||
private final HashMap<String, RefTypeOrTPHOrWildcardOrGeneric> gtvs;
|
||||
|
||||
TypeExchangerInner(HashMap<String, RefTypeOrTPHOrWildcardOrGeneric> gtvs){
|
||||
this.gtvs = gtvs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefTypeOrTPHOrWildcardOrGeneric visit(RefType refType) {
|
||||
List<RefTypeOrTPHOrWildcardOrGeneric> params = new ArrayList<>();
|
||||
for(RefTypeOrTPHOrWildcardOrGeneric param : refType.getParaList()){
|
||||
params.add(param.acceptTV(this));
|
||||
}
|
||||
RefTypeOrTPHOrWildcardOrGeneric ret = new RefType(refType.getName(), params, new NullToken());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefTypeOrTPHOrWildcardOrGeneric visit(SuperWildcardType superWildcardType) {
|
||||
return superWildcardType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefTypeOrTPHOrWildcardOrGeneric visit(TypePlaceholder typePlaceholder) {
|
||||
throw new DebugException("Dieser Fall darf nicht auftreten");
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefTypeOrTPHOrWildcardOrGeneric visit(ExtendsWildcardType extendsWildcardType) {
|
||||
return extendsWildcardType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefTypeOrTPHOrWildcardOrGeneric visit(GenericRefType genericRefType) {
|
||||
if(! gtvs.containsKey(genericRefType.getParsedName()))
|
||||
throw new DebugException("Dieser Fall darf nicht auftreten");
|
||||
return gtvs.get(genericRefType.getParsedName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -188,9 +188,8 @@ public class ClassOrInterface extends SyntaxTreeNode implements TypeScope {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ClassOrInterface that = (ClassOrInterface) o;
|
||||
return Objects.equals(name, that.name);
|
||||
if (!(o instanceof ClassOrInterface other)) return false;
|
||||
return Objects.equals(name, other.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,6 +34,7 @@ public class ASTFactory {
|
||||
private static final HashMap<java.lang.Class, ClassOrInterface> cache = new HashMap<>();
|
||||
|
||||
public static ClassOrInterface createClass(java.lang.Class jreClass) {
|
||||
System.out.println(jreClass);
|
||||
if (cache.containsKey(jreClass))
|
||||
return cache.get(jreClass);
|
||||
|
||||
@ -173,6 +174,7 @@ public class ASTFactory {
|
||||
superClass = (RefType) createType(java.lang.Object.class);
|
||||
}
|
||||
List<RefType> implementedInterfaces = new ArrayList<>();
|
||||
System.out.println(jreClass);
|
||||
for (Type jreInterface : jreClass.getGenericInterfaces()) {
|
||||
implementedInterfaces.add((RefType) createType(jreInterface));
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import de.dhbwstuttgart.bytecode.FunNGenerator;
|
||||
import de.dhbwstuttgart.core.JavaTXCompiler;
|
||||
import de.dhbwstuttgart.environment.ByteArrayClassLoader;
|
||||
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
|
||||
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
||||
import de.dhbwstuttgart.parser.NullToken;
|
||||
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
||||
import de.dhbwstuttgart.syntaxtree.*;
|
||||
@ -18,23 +17,25 @@ import de.dhbwstuttgart.target.tree.expression.*;
|
||||
import de.dhbwstuttgart.target.tree.type.*;
|
||||
import de.dhbwstuttgart.typeinference.result.*;
|
||||
|
||||
import javax.sql.rowset.RowSetWarning;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* @author dholle
|
||||
*/
|
||||
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
|
||||
|
||||
protected List<Generics> all;
|
||||
public Generics generics;
|
||||
final Map<ClassOrInterface, Set<GenericTypeVar>> userDefinedGenerics = new HashMap<>();
|
||||
final Map<Method, Set<SignaturePair>> tphsInMethods = new HashMap<>();
|
||||
private Method currentMethod;
|
||||
|
||||
public final JavaTXCompiler compiler;
|
||||
|
||||
@ -56,7 +57,7 @@ public class ASTToTargetAST {
|
||||
}
|
||||
|
||||
|
||||
protected IByteArrayClassLoader classLoader;
|
||||
public IByteArrayClassLoader classLoader;
|
||||
protected SourceFile sourceFile;
|
||||
|
||||
public ASTToTargetAST(List<ResultSet> resultSets) {
|
||||
@ -78,6 +79,12 @@ public class ASTToTargetAST {
|
||||
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> method = Optional.empty();
|
||||
while (method.isEmpty()) {
|
||||
@ -131,6 +138,40 @@ public class ASTToTargetAST {
|
||||
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) {
|
||||
Set<TargetGeneric> javaGenerics = 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 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 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;
|
||||
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)
|
||||
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);
|
||||
List<TargetConstructor> result = new ArrayList<>();
|
||||
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
||||
this.currentMethod = input;
|
||||
|
||||
for (var s : all) {
|
||||
generics = s;
|
||||
@ -225,56 +267,6 @@ public class ASTToTargetAST {
|
||||
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) {
|
||||
var res = new StringBuilder();
|
||||
res.append(name);
|
||||
@ -290,9 +282,9 @@ public class ASTToTargetAST {
|
||||
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) {
|
||||
return convert(clazz, overloadedMethods.getFirst());
|
||||
return convert(clazz, overloadedMethods.getFirst()).stream().map(m -> m.method()).toList();
|
||||
}
|
||||
var methods = new ArrayList<Method>();
|
||||
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());
|
||||
|
||||
res.add(entryPoint); // TODO*/
|
||||
var res = new HashSet<TargetMethod>();
|
||||
var res = new ArrayList<TargetMethod>();
|
||||
for (var method : methods) {
|
||||
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());
|
||||
res.add(overload);
|
||||
}
|
||||
@ -381,10 +374,12 @@ public class ASTToTargetAST {
|
||||
}).findFirst();
|
||||
}
|
||||
|
||||
private Set<TargetMethod> convert(ClassOrInterface currentClass, Method method) {
|
||||
generics = all.get(0);
|
||||
Set<TargetMethod> result = new HashSet<>();
|
||||
Set<List<MethodParameter>> parameterSet = new HashSet<>();
|
||||
record MethodWithTphs(TargetMethod method, List<SignaturePairTarget> args) {}
|
||||
|
||||
private List<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) {
|
||||
generics = all.getFirst();
|
||||
List<MethodWithTphs> result = new ArrayList<>();
|
||||
this.currentMethod = method;
|
||||
|
||||
for (var s : all) {
|
||||
generics = s;
|
||||
@ -402,8 +397,6 @@ public class ASTToTargetAST {
|
||||
}
|
||||
}
|
||||
|
||||
List<MethodParameter> finalParams = params;
|
||||
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);
|
||||
@ -412,9 +405,10 @@ public class ASTToTargetAST {
|
||||
var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType);
|
||||
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);
|
||||
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;
|
||||
@ -486,10 +480,59 @@ public class ASTToTargetAST {
|
||||
}
|
||||
var filteredParams = new ArrayList<TargetType>();
|
||||
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));
|
||||
}
|
||||
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) {
|
||||
@ -504,17 +547,16 @@ public class ASTToTargetAST {
|
||||
}
|
||||
|
||||
var params = refType.getParaList().stream().map(type -> {
|
||||
var res = convert(type, generics);
|
||||
if (res == null) res = new TargetRefType("java.lang.Void");
|
||||
return res;
|
||||
return convert(type, generics);
|
||||
}).toList();
|
||||
|
||||
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())) {
|
||||
usedFunNSuperTypes.add(params.size());
|
||||
var code = FunNGenerator.generateSuperBytecode(params.size() - 1);
|
||||
var superClassName = FunNGenerator.getSuperClassName(params.size() - 1);
|
||||
var code = FunNGenerator.generateSuperBytecode(params.size() - 1, returnType != null ? 1 : 0);
|
||||
var superClassName = FunNGenerator.getSuperClassName(params.size() - 1, returnType != null ? 1 : 0);
|
||||
try {
|
||||
classLoader.findClass(superClassName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
@ -526,17 +568,8 @@ public class ASTToTargetAST {
|
||||
}
|
||||
FunNGenerator.GenericParameters gep = null;
|
||||
if (!usedFunN.containsKey(className)) {
|
||||
gep = new FunNGenerator.GenericParameters();
|
||||
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) {}
|
||||
}
|
||||
gep = new FunNGenerator.GenericParameters(params, returnType != null ? 1 : 0);
|
||||
usedFunN.put(className, gep);
|
||||
auxiliaries.put(className, code);
|
||||
} else {
|
||||
gep = usedFunN.get(className);
|
||||
}
|
||||
|
@ -652,14 +652,12 @@ public abstract class GenerateGenerics {
|
||||
}
|
||||
|
||||
void normalize(Set<Pair> result, Set<Pair> classGenerics, Set<TPH> usedTphs) {
|
||||
outer:
|
||||
for (var tph : usedTphs) {
|
||||
for (var p1 : new HashSet<>(result)) {
|
||||
if (p1 instanceof PairLT ptph && ptph.left.equals(ptph.right))
|
||||
if (p1 instanceof PairLT ptph && ptph.left.resolve().equals(ptph.right.resolve()))
|
||||
result.remove(p1); // TODO This is a bit strange
|
||||
if (p1.left.equals(tph)) continue outer;
|
||||
}
|
||||
|
||||
for (var tph : usedTphs) {
|
||||
if (classGenerics == null || classGenerics.stream().noneMatch((pair) -> pair.left.equals(tph)))
|
||||
addToPairs(result, new PairEQ(tph, ASTToTargetAST.OBJECT));
|
||||
}
|
||||
|
@ -208,6 +208,11 @@ public class StatementToTargetExpression implements ASTVisitor {
|
||||
var isPrivate = false;
|
||||
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);
|
||||
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) {
|
||||
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
|
||||
|
@ -4,15 +4,29 @@ import de.dhbwstuttgart.bytecode.FunNGenerator;
|
||||
|
||||
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) {
|
||||
return fromParams(params, params);
|
||||
public static TargetFunNType fromParams(List<TargetType> params, int returnArguments) {
|
||||
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));
|
||||
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
|
||||
|
@ -53,19 +53,17 @@ public sealed interface TargetType
|
||||
};
|
||||
}
|
||||
|
||||
static TargetType toTargetType(Class<?> clazz) {
|
||||
if (clazz.isPrimitive()) {
|
||||
if (clazz.equals(boolean.class)) return boolean_;
|
||||
if (clazz.equals(char.class)) return char_;
|
||||
if (clazz.equals(byte.class)) return byte_;
|
||||
if (clazz.equals(short.class)) return short_;
|
||||
if (clazz.equals(int.class)) return int_;
|
||||
if (clazz.equals(long.class)) return long_;
|
||||
if (clazz.equals(float.class)) return float_;
|
||||
if (clazz.equals(double.class)) return double_;
|
||||
}
|
||||
if (clazz.equals(void.class)) return null;
|
||||
return new TargetRefType(clazz.getName());
|
||||
static TargetType toWrapper(TargetType f) {
|
||||
if (f.equals(boolean_)) return Boolean;
|
||||
if (f.equals(char_)) return Char;
|
||||
if (f.equals(byte_)) return Byte;
|
||||
if (f.equals(short_)) return Short;
|
||||
if (f.equals(int_)) return Integer;
|
||||
if (f.equals(long_)) return Long;
|
||||
if (f.equals(float_)) return Float;
|
||||
if (f.equals(double_)) return Double;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
String toSignature();
|
||||
|
@ -66,7 +66,9 @@ public class AllgemeinTest {
|
||||
//String className = "WildcardList";
|
||||
//String className = "List";
|
||||
//String className = "Box";
|
||||
String className = "GenBox";
|
||||
//String className = "GenBox";
|
||||
//String className = "InnerInf";
|
||||
String className = "Foo";
|
||||
//PL 2019-10-24: genutzt fuer unterschiedliche Tests
|
||||
path = System.getProperty("user.dir")+"/resources/AllgemeinTest/" + className + ".jav";
|
||||
//path = System.getProperty("user.dir")+"/src/test/resources/AllgemeinTest/Overloading_Generics.jav";
|
||||
|
@ -898,13 +898,6 @@ public class TestComplete {
|
||||
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
|
||||
public void testAccess() throws Exception {
|
||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Access.jav");
|
||||
@ -1164,6 +1157,13 @@ public class TestComplete {
|
||||
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
|
||||
public void testBug333() throws Exception {
|
||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug333.jav");
|
||||
@ -1171,6 +1171,14 @@ public class TestComplete {
|
||||
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
|
||||
public void testBug338() throws Exception {
|
||||
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "Bug338.jav");
|
||||
|
@ -60,6 +60,8 @@ public class TestCodegen {
|
||||
}
|
||||
}).collect(Collectors.toMap(Class::getName, Function.identity())));
|
||||
|
||||
converter.generateFunNTypes();
|
||||
|
||||
for (var entry : converter.auxiliaries.entrySet()) {
|
||||
writeClassFile(entry.getKey(), entry.getValue());
|
||||
}
|
||||
@ -104,6 +106,8 @@ public class TestCodegen {
|
||||
}
|
||||
}).collect(Collectors.toMap(Class::getName, Function.identity()));
|
||||
|
||||
converter.generateFunNTypes();
|
||||
|
||||
for (var entry : converter.auxiliaries.entrySet()) {
|
||||
writeClassFile(entry.getKey(), entry.getValue());
|
||||
}
|
||||
@ -273,7 +277,7 @@ public class TestCodegen {
|
||||
public void testLambda() throws Exception {
|
||||
var classLoader = new ByteArrayClassLoader();
|
||||
// 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"));
|
||||
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