|
|
|
@@ -4,6 +4,7 @@ import de.dhbwstuttgart.bytecode.FunNGenerator;
|
|
|
|
|
import de.dhbwstuttgart.core.JavaTXCompiler;
|
|
|
|
|
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
|
|
|
|
|
import de.dhbwstuttgart.exceptions.DebugException;
|
|
|
|
|
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
|
|
|
|
import de.dhbwstuttgart.parser.NullToken;
|
|
|
|
|
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
|
|
|
|
import de.dhbwstuttgart.syntaxtree.*;
|
|
|
|
@@ -11,17 +12,19 @@ import de.dhbwstuttgart.syntaxtree.Record;
|
|
|
|
|
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
|
|
|
|
|
import de.dhbwstuttgart.syntaxtree.statement.*;
|
|
|
|
|
import de.dhbwstuttgart.syntaxtree.type.*;
|
|
|
|
|
import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter;
|
|
|
|
|
import de.dhbwstuttgart.syntaxtree.visual.OutputGenerator;
|
|
|
|
|
import de.dhbwstuttgart.target.tree.*;
|
|
|
|
|
import de.dhbwstuttgart.target.tree.expression.*;
|
|
|
|
|
import de.dhbwstuttgart.target.tree.type.*;
|
|
|
|
|
import de.dhbwstuttgart.typeinference.result.*;
|
|
|
|
|
import de.dhbwstuttgart.typeinference.unify.MartelliMontanariUnify;
|
|
|
|
|
import de.dhbwstuttgart.typeinference.unify.model.*;
|
|
|
|
|
|
|
|
|
|
import java.lang.annotation.Target;
|
|
|
|
|
import javax.swing.text.html.Option;
|
|
|
|
|
import java.sql.Array;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import java.util.stream.Stream;
|
|
|
|
|
import java.util.stream.StreamSupport;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @author dholle
|
|
|
|
@@ -61,10 +64,14 @@ public class ASTToTargetAST {
|
|
|
|
|
return converter.result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public record Generics(JavaGenerics javaGenerics, TxGenerics txGenerics) {
|
|
|
|
|
public record Generics(IGenerics javaGenerics, IGenerics txGenerics) {
|
|
|
|
|
public Generics(JavaTXCompiler compiler, ResultSet set) {
|
|
|
|
|
this(new JavaGenerics(compiler, set), new TxGenerics(compiler, set));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Generics nullGenerics() {
|
|
|
|
|
return new Generics(null, new ResultSet(Set.of()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IByteArrayClassLoader classLoader;
|
|
|
|
@@ -94,10 +101,15 @@ public class ASTToTargetAST {
|
|
|
|
|
tphsInMethods.put(currentMethod, set);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, JavaTXCompiler compiler) {
|
|
|
|
|
return findMethod(owner, name, argumentList, Generics.nullGenerics().javaGenerics(), compiler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) {
|
|
|
|
|
Optional<Method> method = Optional.empty();
|
|
|
|
|
while (method.isEmpty()) {
|
|
|
|
|
method = owner.getMethods().stream().filter(m -> m.name.equals(name) && parameterEquals(m.getParameterList(), argumentList, generics)).findFirst();
|
|
|
|
|
method = owner.getMethods().stream().filter(m -> m.name.equals(name) &&
|
|
|
|
|
parameterEquals(m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
|
|
|
|
|
if (owner.getClassName().toString().equals("java.lang.Object")) break;
|
|
|
|
|
owner = compiler.getClass(owner.getSuperClass().getName());
|
|
|
|
|
}
|
|
|
|
@@ -105,16 +117,16 @@ public class ASTToTargetAST {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Optional<Constructor> findConstructor(ClassOrInterface owner, List<TargetType> argumentList, IGenerics generics) {
|
|
|
|
|
return owner.getConstructors().stream().filter(c -> parameterEquals(c.getParameterList(), argumentList, generics)).findFirst();
|
|
|
|
|
return owner.getConstructors().stream().filter(c ->
|
|
|
|
|
parameterEquals(c.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static boolean parameterEquals(ParameterList parameterList, List<TargetType> arguments, IGenerics generics) {
|
|
|
|
|
var pars = parameterList.getFormalparalist();
|
|
|
|
|
static boolean parameterEquals(List<TargetType> pars, List<TargetType> arguments) {
|
|
|
|
|
if (pars.size() != arguments.size())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < pars.size(); i++) {
|
|
|
|
|
var type1 = generics.getTargetType(pars.get(i).getType());
|
|
|
|
|
var type1 = pars.get(i);
|
|
|
|
|
var type2 = arguments.get(i);
|
|
|
|
|
if (type1 instanceof TargetGenericType)
|
|
|
|
|
return true;
|
|
|
|
@@ -147,190 +159,168 @@ public class ASTToTargetAST {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This finds a common sealed interface type to group together methods that use different records
|
|
|
|
|
// This function should do matching or unification
|
|
|
|
|
private List<ClassOrInterface> commonSuperInterfaceTypes(TargetType a, TargetType b) {
|
|
|
|
|
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()));
|
|
|
|
|
|
|
|
|
|
if (cla.equals(clb)) return List.of(cla);
|
|
|
|
|
|
|
|
|
|
while (!cla.equals(ASTFactory.createObjectClass())) {
|
|
|
|
|
var clb2 = clb;
|
|
|
|
|
while (!clb2.equals(ASTFactory.createObjectClass())) {
|
|
|
|
|
for (var intfa : cla.getSuperInterfaces()) {
|
|
|
|
|
for (var intfb : clb.getSuperInterfaces()) {
|
|
|
|
|
if (intfa.equals(intfb)) {
|
|
|
|
|
var clintf = compiler.getClass(intfa.getName());
|
|
|
|
|
if (clintf.isSealed()) {
|
|
|
|
|
res.add(clintf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
clb2 = compiler.getClass(clb2.getSuperClass().getName());
|
|
|
|
|
}
|
|
|
|
|
cla = compiler.getClass(cla.getSuperClass().getName());
|
|
|
|
|
}
|
|
|
|
|
return res.stream().toList();
|
|
|
|
|
}
|
|
|
|
|
return List.of();
|
|
|
|
|
private static UnifyType toUnifyType(TargetType a) {
|
|
|
|
|
return switch(a) {
|
|
|
|
|
case TargetExtendsWildcard targetExtendsWildcard -> new ExtendsType(toUnifyType(targetExtendsWildcard.innerType()));
|
|
|
|
|
case TargetSuperWildcard targetSuperWildcard -> new SuperType(toUnifyType(targetSuperWildcard.innerType()));
|
|
|
|
|
case TargetGenericType targetGenericType -> new PlaceholderType(targetGenericType.name(), JavaTXCompiler.defaultClientPlaceholderRegistry);
|
|
|
|
|
case TargetPrimitiveType _ -> throw new NotImplementedException();
|
|
|
|
|
case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.params().stream().map(ASTToTargetAST::toUnifyType).toList()));
|
|
|
|
|
case TargetRefType targetRefType -> new ReferenceType(targetRefType.name(), new TypeParams(targetRefType.params().stream().map(ASTToTargetAST::toUnifyType).toList()));
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean canCombine(Signature m1, Signature m2) {
|
|
|
|
|
var pl1 = m1.java.parameters();
|
|
|
|
|
var pl2 = m2.java.parameters();
|
|
|
|
|
if (pl1.size() != pl2.size()) return false;
|
|
|
|
|
if (pl1.isEmpty()) return false;
|
|
|
|
|
for (var i = 0; i < pl1.size(); i++) {
|
|
|
|
|
var p1 = pl1.get(i).pattern();
|
|
|
|
|
var p2 = pl2.get(i).pattern();
|
|
|
|
|
// TPH <> RefType sind nicht unterscheidbar
|
|
|
|
|
if (p1.type() instanceof TargetGenericType || p2.type() instanceof TargetGenericType) continue;
|
|
|
|
|
// Pattern(X) <> Pattern(Y) ist nicht unterscheidbar
|
|
|
|
|
if (p1 instanceof TargetComplexPattern pc1 && p2 instanceof TargetComplexPattern pc2 &&
|
|
|
|
|
pc1.type().equals(pc2.type())) continue;
|
|
|
|
|
if (!p1.equals(p2)) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
private static TargetType toTargetType(UnifyType a) {
|
|
|
|
|
return switch (a) {
|
|
|
|
|
case ExtendsType extendType -> new TargetExtendsWildcard(toTargetType(extendType.getExtendedType()));
|
|
|
|
|
case SuperType superType -> new TargetSuperWildcard(toTargetType(superType.getSuperedType()));
|
|
|
|
|
case PlaceholderType placeholderType -> new TargetGenericType(placeholderType.getName());
|
|
|
|
|
case FunNType funNType -> TargetFunNType.fromParams(StreamSupport.stream(funNType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList(), 1); // FIXME How does this work with Fun0??
|
|
|
|
|
case ReferenceType referenceType -> new TargetRefType(referenceType.getName(), StreamSupport.stream(referenceType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList());
|
|
|
|
|
default -> throw new NotImplementedException();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private record Combination(MethodWithTphs a, MethodWithTphs b) {
|
|
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object o) {
|
|
|
|
|
if (!(o instanceof Combination(MethodWithTphs a1, MethodWithTphs b1))) return false;
|
|
|
|
|
return this.a.equals(a1) && this.b.equals(b1) ||
|
|
|
|
|
this.a.equals(b1) && this.b.equals(a1);
|
|
|
|
|
private static Optional<TargetType> unify(TargetType a, TargetType b) {
|
|
|
|
|
if (a.equals(b)) return Optional.of(a);
|
|
|
|
|
var unify = new MartelliMontanariUnify();
|
|
|
|
|
var ua = toUnifyType(a);
|
|
|
|
|
var unifier = unify.unify(Set.of(ua, toUnifyType(b)));
|
|
|
|
|
if (unifier.isEmpty()) return Optional.empty();
|
|
|
|
|
return Optional.of(toTargetType(unifier.get().apply(ua)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int hashCode() {
|
|
|
|
|
return Objects.hashCode(a) + Objects.hashCode(b);
|
|
|
|
|
private static Optional<List<MethodParameter>> unify(List<MethodParameter> a, List<MethodParameter> b) {
|
|
|
|
|
if (a.size() != b.size()) return Optional.empty();
|
|
|
|
|
var result = new ArrayList<MethodParameter>();
|
|
|
|
|
for (var i = 0; i < a.size(); i++) {
|
|
|
|
|
var u = unify(a.get(i).pattern().type(), b.get(i).pattern().type());
|
|
|
|
|
if (u.isEmpty()) return Optional.empty();
|
|
|
|
|
// Strip off patterns, we don't need them for merged methods, they do a switch case
|
|
|
|
|
result.add(new MethodParameter(u.get(), a.get(i).pattern().name()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Optional.of(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Set<String> findUsedGenerics(TargetType type) {
|
|
|
|
|
if (type instanceof TargetSpecializedType tspec) {
|
|
|
|
|
return tspec.params().stream().map(ASTToTargetAST::findUsedGenerics).flatMap(Set::stream).collect(Collectors.toSet());
|
|
|
|
|
} else if (type instanceof TargetGenericType(String name)) {
|
|
|
|
|
var set = new HashSet<String>();
|
|
|
|
|
set.add(name);
|
|
|
|
|
return set;
|
|
|
|
|
}
|
|
|
|
|
return new HashSet<>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When unifying types we need to keep the generics that are still used in the signature
|
|
|
|
|
// This is done in an extra step after all generics have been combined into one list
|
|
|
|
|
private static void correctGenerics(TargetMethod.Signature signature, List<TargetGeneric> generics) {
|
|
|
|
|
var outGenerics = signature.generics();
|
|
|
|
|
outGenerics.addAll(generics);
|
|
|
|
|
var foundGenerics = signature.parameters().stream().map(p -> findUsedGenerics(p.pattern().type()))
|
|
|
|
|
.flatMap(Set::stream)
|
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
foundGenerics.addAll(findUsedGenerics(signature.returnType()));
|
|
|
|
|
|
|
|
|
|
for (var generic : new HashSet<>(outGenerics)) {
|
|
|
|
|
if (!foundGenerics.contains(generic.name())) outGenerics.remove(generic);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<List<MethodWithTphs>> groupOverloads(ClassOrInterface input, List<Method> methods) {
|
|
|
|
|
var mapOfTargetMethods = new HashMap<Generics, MethodWithTphs[]>();
|
|
|
|
|
for (var gen : all) {
|
|
|
|
|
mapOfTargetMethods.put(gen, new MethodWithTphs[methods.size()]);
|
|
|
|
|
private static Optional<TargetMethod.Signature> unify(TargetMethod.Signature a, TargetMethod.Signature b) {
|
|
|
|
|
if (a.equals(b)) return Optional.empty();
|
|
|
|
|
var res = unify(a.parameters(), b.parameters());
|
|
|
|
|
if (res.isEmpty()) return Optional.empty();
|
|
|
|
|
var returnType = unify(a.returnType(), b.returnType());
|
|
|
|
|
if (returnType.isEmpty()) return Optional.empty();
|
|
|
|
|
var generics = new ArrayList<>(a.generics());
|
|
|
|
|
generics.addAll(b.generics());
|
|
|
|
|
var signature = new TargetMethod.Signature(new HashSet<>(), res.get(), returnType.get());
|
|
|
|
|
correctGenerics(signature, generics);
|
|
|
|
|
return Optional.of(signature);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Optional<MethodGroup> unify(MethodGroup a, MethodGroup b) {
|
|
|
|
|
var junified = unify(a.signature.java, b.signature.java);
|
|
|
|
|
if (junified.isEmpty()) return Optional.empty();
|
|
|
|
|
var txunified = unify(a.signature.tx, b.signature.tx);
|
|
|
|
|
assert txunified.isPresent();
|
|
|
|
|
|
|
|
|
|
var unifiedSignature = new Signature(junified.get(), txunified.get(), a.signature().generics);
|
|
|
|
|
|
|
|
|
|
var concat = new HashSet<>(a.methods);
|
|
|
|
|
concat.addAll(b.methods);
|
|
|
|
|
return Optional.of(new MethodGroup(unifiedSignature, concat));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private record NameAndParameters(String name, int parameters) {}
|
|
|
|
|
|
|
|
|
|
private List<List<Method>> groupMethods(ClassOrInterface input, List<Method> methods) {
|
|
|
|
|
var groups = new HashMap<NameAndParameters, List<Method>>();
|
|
|
|
|
for (var m : methods) {
|
|
|
|
|
var nameAndParameters = new NameAndParameters(m.name, m.getParameterList().getFormalparalist().size());
|
|
|
|
|
var l = groups.getOrDefault(nameAndParameters, new ArrayList<>());
|
|
|
|
|
l.add(m);
|
|
|
|
|
groups.put(nameAndParameters, l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return groups.values().stream().toList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<MethodGroup> groupOverloads(ClassOrInterface input, List<Method> methods) {
|
|
|
|
|
var a = new HashSet<MethodGroup>();
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < methods.size(); i++) {
|
|
|
|
|
var method = methods.get(i);
|
|
|
|
|
// Convert all methods
|
|
|
|
|
var methodsWithTphs = convert(input, method);
|
|
|
|
|
for (var m : methodsWithTphs) {
|
|
|
|
|
var resultMethods = mapOfTargetMethods.get(m.generics);
|
|
|
|
|
resultMethods[i] = new MethodWithTphs(m.method, m.generics, m.signature);
|
|
|
|
|
var mtph = new MethodWithTphs(m.method, m.generics, m.signature);
|
|
|
|
|
a.add(new MethodGroup(m.signature, Set.of(mtph)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
System.out.println("============== INPUT ==============");
|
|
|
|
|
for (var m : mapOfTargetMethods.values()) {
|
|
|
|
|
for (var v : m) if (v != null) System.out.println(v.signature.java.returnType() + " " + v.method.name + " " + v.signature.java().parameters());
|
|
|
|
|
System.out.println();
|
|
|
|
|
for (var m : a) {
|
|
|
|
|
System.out.println(m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
// Algorithm
|
|
|
|
|
|
|
|
|
|
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 R = new HashSet<MethodGroup>();
|
|
|
|
|
var i = new HashSet<>(a);
|
|
|
|
|
|
|
|
|
|
var combinations = new HashSet<Combination>();
|
|
|
|
|
|
|
|
|
|
if (canCombine(m1.signature, m2.signature)) {
|
|
|
|
|
//System.out.println(" Combining " + m1.signature.java.getSignature() + " and " + m2.signature.java.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.signature.java.getSignature() + " and " + m3.signature.java.getSignature());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//System.out.println(" Not Combining " + m1.signature.java.getSignature() + " and " + m2.signature.java.getSignature());
|
|
|
|
|
}
|
|
|
|
|
if (!combinations.isEmpty()) allCombinations.add(combinations);
|
|
|
|
|
while (!i.isEmpty()) {
|
|
|
|
|
var m = i.iterator().next();
|
|
|
|
|
i.remove(m);
|
|
|
|
|
R.add(m);
|
|
|
|
|
var a1 = new HashSet<>(a);
|
|
|
|
|
for (var m1 : a1) {
|
|
|
|
|
if (m1 != m) {
|
|
|
|
|
var u_opt = unify(m, m1);
|
|
|
|
|
if (u_opt.isPresent()) {
|
|
|
|
|
var u = u_opt.get();
|
|
|
|
|
//System.out.println("Unified " + m + " AND " + m1 + "\n\t" + u);
|
|
|
|
|
i.remove(m1);
|
|
|
|
|
R.remove(m);
|
|
|
|
|
R.remove(m1);
|
|
|
|
|
R.add(u);
|
|
|
|
|
a.add(u);
|
|
|
|
|
} /*else {
|
|
|
|
|
System.out.println("Couldn't unify " + m + " AND " + m1);
|
|
|
|
|
}*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (allCombinations.isEmpty()) allCombinations.add(new HashSet<>());
|
|
|
|
|
|
|
|
|
|
// Combine back into output format
|
|
|
|
|
var r0 = new HashSet<Set<MethodWithTphs>>();
|
|
|
|
|
for (var combinations : allCombinations) {
|
|
|
|
|
var r1 = new HashSet<Set<MethodWithTphs>>();
|
|
|
|
|
// This is used to weed out duplicates
|
|
|
|
|
var uniqued = new HashSet<MethodWithTphs>();
|
|
|
|
|
// We go over all methods in the result
|
|
|
|
|
for (var g : all) for (var i = 0; i < methods.size(); i++) {
|
|
|
|
|
var r2 = new HashSet<MethodWithTphs>();
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
System.out.println("============== OUTPUT ==============");
|
|
|
|
|
for (var mg : R) {
|
|
|
|
|
System.out.println(mg.methods.size() + " " + mg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.method.name + " " + m.signature.java.getSignature());
|
|
|
|
|
// System.out.println();
|
|
|
|
|
//}
|
|
|
|
|
return result;
|
|
|
|
|
return R.stream().toList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TargetStructure convert(ClassOrInterface input) {
|
|
|
|
@@ -364,13 +354,29 @@ public class ASTToTargetAST {
|
|
|
|
|
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).toList();
|
|
|
|
|
var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer, generics)).flatMap(List::stream).toList();
|
|
|
|
|
var fields = input.getFieldDecl().stream().map(f -> convert(f, generics.javaGenerics)).toList();
|
|
|
|
|
var methods = groupOverloads(input, input.getMethods()).stream().map(m -> generatePatternOverloads(input, m)).flatMap(List::stream)
|
|
|
|
|
.collect(Collectors.toSet()).stream().toList(); // Unique generated methods
|
|
|
|
|
var m0 = groupMethods(input, input.getMethods());
|
|
|
|
|
|
|
|
|
|
var m1 = new ArrayList<TargetMethod>();
|
|
|
|
|
for (var group : m0) {
|
|
|
|
|
var overloads = groupOverloads(input, group);
|
|
|
|
|
// This contains the merged pattern overloads
|
|
|
|
|
var genMethods = new ArrayList<TargetMethod>();
|
|
|
|
|
for (var o : overloads) {
|
|
|
|
|
var pOverloads = generatePatternOverloads(input, o);
|
|
|
|
|
m1.addAll(pOverloads);
|
|
|
|
|
if (!pOverloads.isEmpty()) {
|
|
|
|
|
genMethods.add(pOverloads.getLast());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var bridge = generateBridgeMethod(input, genMethods);
|
|
|
|
|
bridge.map(m1::add);
|
|
|
|
|
}
|
|
|
|
|
var methods = new HashSet<>(m1).stream().toList();
|
|
|
|
|
|
|
|
|
|
TargetMethod staticConstructor = null;
|
|
|
|
|
if (input.getStaticInitializer().isPresent()) {
|
|
|
|
|
var init = this.convert(input, input.getStaticInitializer().get()).stream().findFirst().orElseThrow();
|
|
|
|
|
staticConstructor = this.convert(init, init.generics.javaGenerics);
|
|
|
|
|
staticConstructor = this.convert(init);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (input instanceof Record)
|
|
|
|
@@ -380,9 +386,15 @@ public class ASTToTargetAST {
|
|
|
|
|
else return new TargetClass(input.getModifiers(), input.getClassName(), convert(input.getSuperClass(), generics.javaGenerics, compiler), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Deprecated
|
|
|
|
|
public List<MethodParameter> convert(ParameterList input) {
|
|
|
|
|
return convert(input, all.getFirst().javaGenerics);
|
|
|
|
|
var res = new ArrayList<MethodParameter>();
|
|
|
|
|
for (var i = 0; i < input.getFormalparalist().size(); i++) {
|
|
|
|
|
var param = input.getFormalparalist().get(i);
|
|
|
|
|
var pattern = (TargetPattern) convert(param, Generics.nullGenerics().javaGenerics);
|
|
|
|
|
if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i);
|
|
|
|
|
res.add(new MethodParameter(pattern));
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<MethodParameter> convert(ParameterList input, IGenerics generics) {
|
|
|
|
@@ -542,38 +554,80 @@ public class ASTToTargetAST {
|
|
|
|
|
return new TargetSwitch(switchExpr, cases, null, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<TargetMethod> generatePatternOverloads(ClassOrInterface clazz, List<MethodWithTphs> overloadedMethods) {
|
|
|
|
|
// TODO What happens if a record implements two sealed interfaces?
|
|
|
|
|
private Optional<TargetType> commonSealedInterface(TargetType a, TargetType b) {
|
|
|
|
|
if (!(a instanceof TargetRefType ar && b instanceof TargetRefType ab)) return Optional.empty();
|
|
|
|
|
var cla = compiler.getClass(new JavaClassName(ar.name()));
|
|
|
|
|
var clb = compiler.getClass(new JavaClassName(ab.name()));
|
|
|
|
|
|
|
|
|
|
// TODO This only goes one layer deep, this is fine for records as they can't extend anything but fails for complex hierarchies
|
|
|
|
|
var intersection = new HashSet<>(cla.getSuperInterfaces());
|
|
|
|
|
intersection.retainAll(clb.getSuperInterfaces());
|
|
|
|
|
|
|
|
|
|
if (intersection.size() != 1) return Optional.empty();
|
|
|
|
|
return Optional.of(new TargetRefType(intersection.stream().findFirst().get().getName().toString()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Optional<TargetMethod> generateBridgeMethod(ClassOrInterface clazz, List<TargetMethod> methods) {
|
|
|
|
|
// If there's only one method we don't need a bridge
|
|
|
|
|
if (methods.size() <= 1) return Optional.empty();
|
|
|
|
|
var firstMethod = methods.getFirst();
|
|
|
|
|
|
|
|
|
|
// Check if all parameters are either the same or implement a sealed interface
|
|
|
|
|
var resParams = new ArrayList<MethodParameter>(Collections.nCopies(firstMethod.signature().parameters().size(), null));
|
|
|
|
|
TargetType resRet = null;
|
|
|
|
|
|
|
|
|
|
for (var m : methods.subList(1, methods.size())) {
|
|
|
|
|
for (var i = 0; i < m.signature().parameters().size(); i++) {
|
|
|
|
|
var pa = firstMethod.signature().parameters().get(i);
|
|
|
|
|
var pb = m.signature().parameters().get(i);
|
|
|
|
|
|
|
|
|
|
var ta = pa.pattern().type();
|
|
|
|
|
var tb = pb.pattern().type();
|
|
|
|
|
System.out.println(ta + " " + tb);
|
|
|
|
|
|
|
|
|
|
if (Objects.equals(ta, tb)) {
|
|
|
|
|
resParams.set(i, pa);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var common = commonSealedInterface(ta, tb);
|
|
|
|
|
if (common.isPresent()) {
|
|
|
|
|
resParams.set(i, pa.withType(common.get()));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Optional.empty();
|
|
|
|
|
}
|
|
|
|
|
var ra = firstMethod.signature().returnType();
|
|
|
|
|
var rb = m.signature().returnType();
|
|
|
|
|
if (Objects.equals(ra, rb)) {
|
|
|
|
|
resRet = ra;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
var common = commonSealedInterface(ra, rb);
|
|
|
|
|
if (common.isPresent()) resRet = common.get();
|
|
|
|
|
else return Optional.empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var parameters = resParams.stream().map( p -> new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList();
|
|
|
|
|
|
|
|
|
|
var classType = new TargetRefType(clazz.getClassName().getClassName());
|
|
|
|
|
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(resParams.getFirst().pattern().type(), resParams.getFirst().pattern().name()), parameters, List.of(), methods, classType);
|
|
|
|
|
var block = new TargetBlock(List.of(stmt));
|
|
|
|
|
|
|
|
|
|
var resSig = new TargetMethod.Signature(firstMethod.signature().generics(), resParams, resRet);
|
|
|
|
|
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, resSig, resSig);
|
|
|
|
|
return Optional.of(bridgeMethod);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<TargetMethod> generatePatternOverloads(ClassOrInterface clazz, MethodGroup group) {
|
|
|
|
|
var overloadedMethods = group.methods.stream().toList();
|
|
|
|
|
if (overloadedMethods.isEmpty()) return List.of();
|
|
|
|
|
// Check if we have a pattern as a parameter
|
|
|
|
|
var firstMethod = convert(overloadedMethods.getFirst(), overloadedMethods.getFirst().generics.javaGenerics);
|
|
|
|
|
var firstMethod = convert(overloadedMethods.getFirst());
|
|
|
|
|
if (overloadedMethods.size() == 1) return List.of(firstMethod);
|
|
|
|
|
var secondMethod = convert(overloadedMethods.get(1), overloadedMethods.get(1).generics.javaGenerics);
|
|
|
|
|
if (firstMethod.signature().parameters().stream().noneMatch(mp -> mp.pattern() instanceof TargetComplexPattern))
|
|
|
|
|
return overloadedMethods.stream().map(m -> convert(m, m.generics.javaGenerics)).toList();
|
|
|
|
|
|
|
|
|
|
var signatureParams = new ArrayList<MethodParameter>();
|
|
|
|
|
for (var i = 0; i < firstMethod.signature().parameters().size(); i++) {
|
|
|
|
|
var p1 = firstMethod.signature().parameters().get(i).pattern();
|
|
|
|
|
var t1 = p1.type();
|
|
|
|
|
var t2 = secondMethod.signature().parameters().get(i).pattern().type();
|
|
|
|
|
var commonSubTypes = new HashSet<>(commonSuperInterfaceTypes(t1, t2));
|
|
|
|
|
for (var m : overloadedMethods.subList(2, overloadedMethods.size())) {
|
|
|
|
|
var t3 = m.signature().java.parameters().get(i).pattern().type();
|
|
|
|
|
commonSubTypes.retainAll(commonSuperInterfaceTypes(t1, t3));
|
|
|
|
|
}
|
|
|
|
|
if (commonSubTypes.size() > 1) throw new DebugException("Invalid overload");
|
|
|
|
|
// TODO accept multiple types
|
|
|
|
|
var superType = ASTFactory.createObjectClass();
|
|
|
|
|
if (!commonSubTypes.isEmpty())
|
|
|
|
|
superType = commonSubTypes.iterator().next();
|
|
|
|
|
|
|
|
|
|
String name;
|
|
|
|
|
if (p1 instanceof TargetComplexPattern) name = "__var" + i;
|
|
|
|
|
else name = p1.name();
|
|
|
|
|
signatureParams.add(new MethodParameter(new TargetRefType(superType.getClassName().toString()), name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rename existing methods
|
|
|
|
|
var signatureParams = group.signature.java.parameters();
|
|
|
|
|
|
|
|
|
|
var res = new ArrayList<TargetMethod>();
|
|
|
|
|
for (var method : overloadedMethods) {
|
|
|
|
@@ -592,12 +646,6 @@ public class ASTToTargetAST {
|
|
|
|
|
res.add(new TargetMethod(tMethod.access(), name, tMethod.block(), tMethod.signature(), tMethod.txSignature()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var commonSubTypes = new HashSet<>(commonSuperInterfaceTypes(firstMethod.signature().returnType(), secondMethod.signature().returnType()));
|
|
|
|
|
for (var m : overloadedMethods.subList(2, overloadedMethods.size())) {
|
|
|
|
|
commonSubTypes.retainAll(commonSuperInterfaceTypes(firstMethod.signature().returnType(), m.signature().java.returnType()));
|
|
|
|
|
}
|
|
|
|
|
var returnType = commonSubTypes.isEmpty() ? TargetType.Object : new TargetRefType(commonSubTypes.iterator().next().getClassName().toString());
|
|
|
|
|
|
|
|
|
|
var parameters = signatureParams.stream().map( p -> new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList();
|
|
|
|
|
//var patterns = List.of((TargetComplexPattern) firstMethod.signature().parameters().stream()
|
|
|
|
|
// .filter(p -> p.pattern() instanceof TargetComplexPattern).findFirst().orElseThrow().pattern());
|
|
|
|
@@ -607,9 +655,7 @@ public class ASTToTargetAST {
|
|
|
|
|
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(signatureParams.getFirst().pattern().type(), signatureParams.getFirst().pattern().name()), parameters, List.of(), res, classType);
|
|
|
|
|
var block = new TargetBlock(List.of(stmt));
|
|
|
|
|
|
|
|
|
|
var signature = new TargetMethod.Signature(Set.of(), signatureParams, returnType);
|
|
|
|
|
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, signature, firstMethod.txSignature());
|
|
|
|
|
|
|
|
|
|
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, group.signature.java, group.signature.tx);
|
|
|
|
|
res.add(bridgeMethod);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
@@ -655,6 +701,15 @@ public class ASTToTargetAST {
|
|
|
|
|
}).findFirst();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record MethodGroup(Signature signature, Set<MethodWithTphs> methods) {
|
|
|
|
|
@Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
assert !methods.isEmpty();
|
|
|
|
|
var first = methods.iterator().next();
|
|
|
|
|
return signature.java.returnType() + " " + first.method.name + " " + signature.java.generics() + " " + signature.java.parameters();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record MethodWithTphs(Method method, Generics generics, Signature signature) {
|
|
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object o) {
|
|
|
|
@@ -669,14 +724,30 @@ public class ASTToTargetAST {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TargetMethod convert(MethodWithTphs mtph) {
|
|
|
|
|
return convert(mtph, mtph.generics.javaGenerics);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private TargetMethod convert(MethodWithTphs mtph, IGenerics generics) {
|
|
|
|
|
return new TargetMethod(mtph.method.modifier, mtph.method.name, convert(mtph.method.block, generics), mtph.signature.java(), mtph.signature.tx());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {}
|
|
|
|
|
record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {
|
|
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object other) {
|
|
|
|
|
if (this == other) return true;
|
|
|
|
|
if (!(other instanceof Signature that)) return false;
|
|
|
|
|
return Objects.equals(this.java, that.java) && Objects.equals(this.tx, that.tx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) {
|
|
|
|
|
List<MethodWithTphs> result = new ArrayList<>();
|
|
|
|
|
@Override
|
|
|
|
|
public int hashCode() {
|
|
|
|
|
return Objects.hash(java, tx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Set<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) {
|
|
|
|
|
Set<MethodWithTphs> result = new HashSet<>();
|
|
|
|
|
this.currentMethod = method;
|
|
|
|
|
|
|
|
|
|
List<Signature> signatures = new ArrayList<>();
|
|
|
|
@@ -701,6 +772,7 @@ public class ASTToTargetAST {
|
|
|
|
|
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, method);
|
|
|
|
|
|
|
|
|
|
var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType);
|
|
|
|
|
System.out.println(javaSignature.getDescriptor());
|
|
|
|
|
var txSignature = new TargetMethod.Signature(txMethodGenerics, txParams, convert(method.getReturnType(), generics.txGenerics, compiler));
|
|
|
|
|
|
|
|
|
|
signatures.add(new Signature(javaSignature, txSignature, generics));
|
|
|
|
@@ -754,7 +826,7 @@ public class ASTToTargetAST {
|
|
|
|
|
for (var i = 0; i < tspec.params().size(); i++) {
|
|
|
|
|
var param = tspec.params().get(i);
|
|
|
|
|
if (param instanceof TargetSpecializedType fn) {
|
|
|
|
|
collectArguments(tspec, newParams);
|
|
|
|
|
collectArguments(fn, newParams);
|
|
|
|
|
} else {
|
|
|
|
|
newParams.add(param);
|
|
|
|
|
}
|
|
|
|
|