Work on Bug #332
Some checks failed
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 46s
Some checks failed
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 46s
This commit is contained in:
parent
091a6b8f1f
commit
ec92b5d5e1
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 {
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
@ -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;
|
||||||
|
@ -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!");
|
||||||
|
@ -1126,6 +1126,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");
|
||||||
|
Loading…
Reference in New Issue
Block a user