|
|
|
@ -17,7 +17,6 @@ import de.dhbwstuttgart.target.tree.expression.*;
|
|
|
|
|
import de.dhbwstuttgart.target.tree.type.*;
|
|
|
|
|
import de.dhbwstuttgart.typeinference.result.*;
|
|
|
|
|
|
|
|
|
|
import java.lang.annotation.Target;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import java.util.stream.Stream;
|
|
|
|
@ -145,49 +144,25 @@ public class ASTToTargetAST {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is used to serve as a custom equality to signature that performs a weak check without going into record patterns.
|
|
|
|
|
// The two signatures are considered equal if all the argument types match.
|
|
|
|
|
// This also turns equal if both types implement a sealed super interface
|
|
|
|
|
class PatternSignature {
|
|
|
|
|
final TargetMethod.Signature signature;
|
|
|
|
|
final String name;
|
|
|
|
|
PatternSignature(String name, TargetMethod.Signature signature) {
|
|
|
|
|
this.signature = signature;
|
|
|
|
|
this.name = name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object o) {
|
|
|
|
|
if (!(o instanceof PatternSignature other)) return false;
|
|
|
|
|
if (!this.name.equals(other.name)) return false;
|
|
|
|
|
if (other.signature.parameters().size() != signature.parameters().size()) return false;
|
|
|
|
|
for (var i = 0; i < signature.parameters().size(); i++) {
|
|
|
|
|
var p1 = signature.parameters().get(i).pattern().type();
|
|
|
|
|
var p2 = other.signature.parameters().get(i).pattern().type();
|
|
|
|
|
if (p1 instanceof TargetGenericType && p2 instanceof TargetGenericType) continue;
|
|
|
|
|
if (!p1.equals(p2) && commonSuperInterfaceTypes(p1, p2).isEmpty()) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int hashCode() {
|
|
|
|
|
return signature.parameters().size();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This finds a common sealed interface type to group together methods that use different records
|
|
|
|
|
private List<ClassOrInterface> commonSuperInterfaceTypes(TargetType a, TargetType b) {
|
|
|
|
|
if (a instanceof TargetGenericType && b instanceof TargetGenericType) return List.of(ASTFactory.createClass(Object.class));
|
|
|
|
|
if (a instanceof TargetGenericType && b instanceof TargetGenericType) return List.of(ASTFactory.createObjectClass());
|
|
|
|
|
if (a instanceof TargetRefType ta && b instanceof TargetGenericType)
|
|
|
|
|
return List.of(compiler.getClass(new JavaClassName(ta.name())));
|
|
|
|
|
if (b instanceof TargetRefType tb && a instanceof TargetGenericType)
|
|
|
|
|
return List.of(compiler.getClass(new JavaClassName(tb.name())));
|
|
|
|
|
|
|
|
|
|
if (a instanceof TargetRefType ta && b instanceof TargetRefType tb) {
|
|
|
|
|
var res = new HashSet<ClassOrInterface>();
|
|
|
|
|
|
|
|
|
|
var cla = compiler.getClass(new JavaClassName(ta.name()));
|
|
|
|
|
var clb = compiler.getClass(new JavaClassName(tb.name()));
|
|
|
|
|
|
|
|
|
|
while (!cla.equals(ASTFactory.createClass(Object.class))) {
|
|
|
|
|
if (cla.equals(clb)) return List.of(cla);
|
|
|
|
|
|
|
|
|
|
while (!cla.equals(ASTFactory.createObjectClass())) {
|
|
|
|
|
var clb2 = clb;
|
|
|
|
|
while (!clb2.equals(ASTFactory.createClass(Object.class))) {
|
|
|
|
|
while (!clb2.equals(ASTFactory.createObjectClass())) {
|
|
|
|
|
for (var intfa : cla.getSuperInterfaces()) {
|
|
|
|
|
for (var intfb : clb.getSuperInterfaces()) {
|
|
|
|
|
if (intfa.equals(intfb)) {
|
|
|
|
@ -207,48 +182,167 @@ public class ASTToTargetAST {
|
|
|
|
|
return List.of();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO This is ugly and probably doesn't work right
|
|
|
|
|
private boolean patternStrictlyEquals(TargetComplexPattern a, TargetComplexPattern b) {
|
|
|
|
|
if (!a.name().equals(b.name())) return false;
|
|
|
|
|
if (a.subPatterns().size() != b.subPatterns().size()) return false;
|
|
|
|
|
for (var i = 0; i < a.subPatterns().size(); i++) {
|
|
|
|
|
var p1 = a.subPatterns().get(i);
|
|
|
|
|
var p2 = b.subPatterns().get(i);
|
|
|
|
|
if (p1 instanceof TargetComplexPattern pc1 && p2 instanceof TargetComplexPattern pc2 &&
|
|
|
|
|
patternStrictlyEquals(pc1, pc2)) return false;
|
|
|
|
|
if (p1 instanceof TargetTypePattern pt1 && p2 instanceof TargetTypePattern pt2) {
|
|
|
|
|
if (pt1.type() instanceof TargetGenericType && pt2.type() instanceof TargetGenericType) continue;
|
|
|
|
|
}
|
|
|
|
|
if (!p1.type().equals(p2.type()) && commonSuperInterfaceTypes(p1.type(), p2.type()).isEmpty()) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean canCombine(TargetMethod m1, TargetMethod m2) {
|
|
|
|
|
if (!m1.name().equals(m2.name())) return false;
|
|
|
|
|
var s1 = m1.signature();
|
|
|
|
|
var s2 = m2.signature();
|
|
|
|
|
if (s1.parameters().size() != s2.parameters().size()) return false;
|
|
|
|
|
if (s1.parameters().isEmpty()) return false;
|
|
|
|
|
for (var i = 0; i < s1.parameters().size(); i++) {
|
|
|
|
|
var p1 = s1.parameters().get(i).pattern();
|
|
|
|
|
var p2 = s2.parameters().get(i).pattern();
|
|
|
|
|
if (p1.type() instanceof TargetGenericType || p2.type() instanceof TargetGenericType) continue;
|
|
|
|
|
if (p1 instanceof TargetComplexPattern pc1 && p2 instanceof TargetComplexPattern pc2 &&
|
|
|
|
|
patternStrictlyEquals(pc1, pc2)) return false;
|
|
|
|
|
if (!p1.equals(p2) && commonSuperInterfaceTypes(p1.type(), p2.type()).isEmpty()) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private record Combination(TargetMethod a, TargetMethod b) {
|
|
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object o) {
|
|
|
|
|
if (!(o instanceof Combination(TargetMethod a1, TargetMethod b1))) return false;
|
|
|
|
|
return this.a.equals(a1) && this.b.equals(b1) ||
|
|
|
|
|
this.a.equals(b1) && this.b.equals(a1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int hashCode() {
|
|
|
|
|
return Objects.hashCode(a) + Objects.hashCode(b);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<List<TargetMethod>> groupOverloads(ClassOrInterface input, List<Method> methods) {
|
|
|
|
|
var mapOfSignatures = new HashMap<PatternSignature, List<TargetMethod>>();
|
|
|
|
|
for (var method : methods) {
|
|
|
|
|
var mapOfTargetMethods = new HashMap<Generics, TargetMethod[]>();
|
|
|
|
|
for (var generics : all) {
|
|
|
|
|
mapOfTargetMethods.put(generics, new TargetMethod[methods.size()]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < methods.size(); i++) {
|
|
|
|
|
var method = methods.get(i);
|
|
|
|
|
// Convert all methods
|
|
|
|
|
var methodsWithTphs = convert(input, method);
|
|
|
|
|
// Then check for methods with the same signature
|
|
|
|
|
var resMethods = new HashSet<MethodWithTphs>();
|
|
|
|
|
|
|
|
|
|
for (var m1 : methodsWithTphs) {
|
|
|
|
|
System.out.println(m1.method.name() + " -> " + m1.method.signature().parameters().stream().map(m -> m.pattern().type()).toList());
|
|
|
|
|
for (var m : methodsWithTphs) {
|
|
|
|
|
var resultMethods = mapOfTargetMethods.get(m.generics);
|
|
|
|
|
resultMethods[i] = m.method;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*System.out.println("============== INPUT ==============");
|
|
|
|
|
for (var m : mapOfTargetMethods.values()) {
|
|
|
|
|
for (var v : m) System.out.println(v.name() + " " + v.getSignature());
|
|
|
|
|
System.out.println();
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
outer:
|
|
|
|
|
for (var m1 : methodsWithTphs) {
|
|
|
|
|
for (var m2 : methodsWithTphs) {
|
|
|
|
|
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;
|
|
|
|
|
var allCombinations = new HashSet<Set<Combination>>();
|
|
|
|
|
// Combine methods based on their signature and position in the result set
|
|
|
|
|
for (var g1 : all) {
|
|
|
|
|
var resMeth1 = mapOfTargetMethods.get(g1);
|
|
|
|
|
for (var i = 0; i < methods.size(); i++) {
|
|
|
|
|
var m1 = resMeth1[i];
|
|
|
|
|
if (m1 == null) continue;
|
|
|
|
|
|
|
|
|
|
for (var g2 : all) {
|
|
|
|
|
if (g1 == g2) continue; // No need to combine the same method
|
|
|
|
|
var resMeth2 = mapOfTargetMethods.get(g2);
|
|
|
|
|
var m2 = resMeth2[i];
|
|
|
|
|
if (m2 == null) continue;
|
|
|
|
|
|
|
|
|
|
var combinations = new HashSet<Combination>();
|
|
|
|
|
|
|
|
|
|
if (canCombine(m1, m2)) {
|
|
|
|
|
//System.out.println(" Combining " + m1.getSignature() + " and " + m2.getSignature());
|
|
|
|
|
combinations.add(new Combination(m1, m2));
|
|
|
|
|
for (var j = 0; j < methods.size(); j++) {
|
|
|
|
|
if (j == i) continue;
|
|
|
|
|
var m3 = resMeth2[j];
|
|
|
|
|
if (m3 == null) continue;
|
|
|
|
|
var m4 = resMeth1[j];
|
|
|
|
|
if (m4 == null) continue;
|
|
|
|
|
combinations.add(new Combination(m4, m3));
|
|
|
|
|
//System.out.println("Also Combining " + m4.getSignature() + " and " + m3.getSignature());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//System.out.println(" Not Combining " + m1.getSignature() + " and " + m2.getSignature());
|
|
|
|
|
}
|
|
|
|
|
if (!combinations.isEmpty()) allCombinations.add(combinations);
|
|
|
|
|
}
|
|
|
|
|
resMethods.add(m1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (var m : resMethods) {
|
|
|
|
|
var signature = new PatternSignature(m.method.name(), m.method.signature());
|
|
|
|
|
var methodsWithSameSignature = mapOfSignatures.getOrDefault(signature, new ArrayList<>());
|
|
|
|
|
methodsWithSameSignature.add(m.method);
|
|
|
|
|
mapOfSignatures.put(signature, methodsWithSameSignature);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mapOfSignatures.values().forEach(e -> {
|
|
|
|
|
e.forEach(e2 -> {
|
|
|
|
|
System.out.println(e2.name() + " -> " + e2.signature().parameters().stream().map(m -> m.pattern().type()).toList());
|
|
|
|
|
});
|
|
|
|
|
if (allCombinations.isEmpty()) allCombinations.add(new HashSet<>());
|
|
|
|
|
|
|
|
|
|
// Combine back into output format
|
|
|
|
|
var r0 = new HashSet<Set<TargetMethod>>();
|
|
|
|
|
for (var combinations : allCombinations) {
|
|
|
|
|
var r1 = new HashSet<Set<TargetMethod>>();
|
|
|
|
|
// This is used to weed out duplicates
|
|
|
|
|
var uniqued = new HashSet<TargetMethod>();
|
|
|
|
|
// We go over all methods in the result
|
|
|
|
|
for (var g : all) for (var i = 0; i < methods.size(); i++) {
|
|
|
|
|
var r2 = new HashSet<TargetMethod>();
|
|
|
|
|
var m = mapOfTargetMethods.get(g)[i];
|
|
|
|
|
if (m == null) continue;
|
|
|
|
|
if (!uniqued.contains(m)) {
|
|
|
|
|
// Add the method to r2
|
|
|
|
|
r2.add(m);
|
|
|
|
|
uniqued.add(m);
|
|
|
|
|
} else continue;
|
|
|
|
|
// Find all combinations that contain the method and add them to the result
|
|
|
|
|
// if not filtered out by uniqued
|
|
|
|
|
for (var c : combinations) {
|
|
|
|
|
if (c.a.equals(m) || c.b.equals(m)) {
|
|
|
|
|
if (!uniqued.contains(c.a)) {
|
|
|
|
|
r2.add(c.a);
|
|
|
|
|
uniqued.add(c.a);
|
|
|
|
|
}
|
|
|
|
|
if (!uniqued.contains(c.b)) {
|
|
|
|
|
r2.add(c.b);
|
|
|
|
|
uniqued.add(c.b);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
r1.add(r2);
|
|
|
|
|
}
|
|
|
|
|
outer: for (var s1 : r1) {
|
|
|
|
|
for (var s2 : new HashSet<>(r0)) {
|
|
|
|
|
if (s2.containsAll(s1)) {
|
|
|
|
|
continue outer;
|
|
|
|
|
} else if (s1.containsAll(s2)) {
|
|
|
|
|
r0.remove(s2);
|
|
|
|
|
r0.add(s1);
|
|
|
|
|
continue outer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
r0.add(s1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result = r0.stream().map(l -> l.stream().toList()).toList();
|
|
|
|
|
|
|
|
|
|
System.out.println("============== OUTPUT ==============");
|
|
|
|
|
for (var l : result) {
|
|
|
|
|
for (var m : l) System.out.println(m.name() + " " + m.getSignature());
|
|
|
|
|
System.out.println();
|
|
|
|
|
});
|
|
|
|
|
return mapOfSignatures.values().stream().toList();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TargetStructure convert(ClassOrInterface input) {
|
|
|
|
@ -281,7 +375,8 @@ 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, input.getMethods()).stream().map(m -> generatePatternOverloads(input, m)).flatMap(List::stream).toList();
|
|
|
|
|
var methods = groupOverloads(input, input.getMethods()).stream().map(m -> generatePatternOverloads(input, m)).flatMap(List::stream)
|
|
|
|
|
.collect(Collectors.toSet()).stream().toList(); // Unique generated methods
|
|
|
|
|
|
|
|
|
|
TargetMethod staticConstructor = null;
|
|
|
|
|
if (input.getStaticInitializer().isPresent())
|
|
|
|
@ -403,7 +498,7 @@ public class ASTToTargetAST {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cases = new ArrayList<TargetSwitch.Case>();
|
|
|
|
|
var usedPatterns = new HashSet<TargetType>();
|
|
|
|
|
var usedPatterns = new HashSet<TargetPattern>();
|
|
|
|
|
|
|
|
|
|
for (var method : methods) {
|
|
|
|
|
var patternsRec = new ArrayList<>(patterns);
|
|
|
|
@ -423,9 +518,8 @@ public class ASTToTargetAST {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lastPattern = patternsRec.getLast();
|
|
|
|
|
var type = unwrap(lastPattern.type());
|
|
|
|
|
if (usedPatterns.contains(type)) continue;
|
|
|
|
|
usedPatterns.add(type);
|
|
|
|
|
if (usedPatterns.contains(lastPattern)) continue;
|
|
|
|
|
usedPatterns.add(lastPattern);
|
|
|
|
|
|
|
|
|
|
var candidates = methods.stream().filter(m -> {
|
|
|
|
|
var j = 0;
|
|
|
|
@ -471,9 +565,12 @@ public class ASTToTargetAST {
|
|
|
|
|
var t3 = m.signature().parameters().get(i).pattern().type();
|
|
|
|
|
commonSubTypes.retainAll(commonSuperInterfaceTypes(t1, t3));
|
|
|
|
|
}
|
|
|
|
|
if (commonSubTypes.size() != 1) throw new DebugException("Invalid overload");
|
|
|
|
|
if (commonSubTypes.size() > 1) throw new DebugException("Invalid overload");
|
|
|
|
|
// TODO accept multiple types
|
|
|
|
|
var superType = commonSubTypes.iterator().next();
|
|
|
|
|
var superType = ASTFactory.createObjectClass();
|
|
|
|
|
if (!commonSubTypes.isEmpty())
|
|
|
|
|
superType = commonSubTypes.iterator().next();
|
|
|
|
|
|
|
|
|
|
String name;
|
|
|
|
|
if (p1 instanceof TargetComplexPattern) name = "__var" + i;
|
|
|
|
|
else name = p1.name();
|
|
|
|
@ -543,7 +640,19 @@ public class ASTToTargetAST {
|
|
|
|
|
}).findFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record MethodWithTphs(TargetMethod method, List<SignaturePairTarget> args) {}
|
|
|
|
|
record MethodWithTphs(TargetMethod method, Generics generics, List<SignaturePairTarget> args) {
|
|
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object o) {
|
|
|
|
|
if (this == o) return true;
|
|
|
|
|
if (!(o instanceof MethodWithTphs that)) return false;
|
|
|
|
|
return Objects.equals(method, that.method) && Objects.equals(args, that.args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int hashCode() {
|
|
|
|
|
return Objects.hash(method, args);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {}
|
|
|
|
|
|
|
|
|
@ -592,7 +701,7 @@ public class ASTToTargetAST {
|
|
|
|
|
var newMethod = new TargetMethod(method.modifier, method.name, convert(method.block), signature.java, signature.tx);
|
|
|
|
|
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));
|
|
|
|
|
result.add(new MethodWithTphs(newMethod, generics, concreteParams));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|