forked from JavaTX/JavaCompilerCore
Refactoring
This commit is contained in:
parent
fe2b3accb3
commit
bb0ee7d517
@ -20,18 +20,24 @@ class Pair<T, U> {
|
|||||||
|
|
||||||
|
|
||||||
public class Iteration {
|
public class Iteration {
|
||||||
|
id(x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
m1(x, y) {
|
m1(x, y) {
|
||||||
var help;
|
var help;
|
||||||
help = m2(x, y);
|
help = m2(x, y);
|
||||||
var y2 = help.snd();
|
var y2 = help.snd();
|
||||||
return new Pair<>(x,y2);
|
var x2 = id(x);
|
||||||
|
return new Pair<>(x2,y2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m2(x,y) {
|
m2(x,y) {
|
||||||
var help = m1(x, y);
|
var help = m1(x, y);
|
||||||
var x2 = help.fst();
|
var x2 = help.fst();
|
||||||
return new Pair<>(x2, y);
|
var y2 = id(y);
|
||||||
|
return new Pair<>(x2, y2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,12 +3,10 @@ package de.dhbwstuttgart.target.generate;
|
|||||||
import de.dhbwstuttgart.bytecode.FunNGenerator;
|
import de.dhbwstuttgart.bytecode.FunNGenerator;
|
||||||
import de.dhbwstuttgart.environment.ByteArrayClassLoader;
|
import de.dhbwstuttgart.environment.ByteArrayClassLoader;
|
||||||
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
|
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
|
||||||
import de.dhbwstuttgart.parser.NullToken;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.*;
|
import de.dhbwstuttgart.syntaxtree.*;
|
||||||
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
|
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.*;
|
import de.dhbwstuttgart.syntaxtree.statement.*;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.*;
|
import de.dhbwstuttgart.syntaxtree.type.*;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.Void;
|
|
||||||
import de.dhbwstuttgart.target.tree.*;
|
import de.dhbwstuttgart.target.tree.*;
|
||||||
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
|
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
|
||||||
import de.dhbwstuttgart.target.tree.expression.TargetExpression;
|
import de.dhbwstuttgart.target.tree.expression.TargetExpression;
|
||||||
@ -41,814 +39,6 @@ public class ASTToTargetAST {
|
|||||||
|
|
||||||
record Generics(JavaGenerics javaGenerics, TxGenerics txGenerics) {}
|
record Generics(JavaGenerics javaGenerics, TxGenerics txGenerics) {}
|
||||||
|
|
||||||
final class TxGenerics extends GenerateGenerics {
|
|
||||||
TxGenerics(ResultSet constraints) {
|
|
||||||
super(constraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void generics(ClassOrInterface owner, Method method, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced) {
|
|
||||||
eliminateInfima(result, referenced);
|
|
||||||
eliminateInnerTypeVariables(referenced, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced) {
|
|
||||||
eliminateInfima(result, referenced);
|
|
||||||
eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class JavaGenerics extends GenerateGenerics {
|
|
||||||
JavaGenerics(ResultSet constraints) {
|
|
||||||
super(constraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void generics(ClassOrInterface owner, Method method, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced) {
|
|
||||||
eliminateCycles(result, referenced);
|
|
||||||
eliminateInfima(result, referenced);
|
|
||||||
equalizeTypeVariables(result, referenced);
|
|
||||||
eliminateInnerTypeVariables(referenced, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced) {
|
|
||||||
eliminateCycles(result, referenced);
|
|
||||||
eliminateInfima(result, referenced);
|
|
||||||
equalizeTypeVariables(result, referenced);
|
|
||||||
eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class GenerateGenerics {
|
|
||||||
final Map<Method, Set<ResultPair<?, ?>>> computedGenericsOfMethods = new HashMap<>();
|
|
||||||
final Map<ClassOrInterface, Set<ResultPair<?, ?>>> computedGenericsOfClasses = new HashMap<>();
|
|
||||||
|
|
||||||
final Map<Method, Set<TypePlaceholder>> usedTPHsOfMethods = new HashMap<>();
|
|
||||||
final Map<Method, Set<ResultPair<?, ?>>> familyOfMethods = new HashMap<>();
|
|
||||||
|
|
||||||
final Set<PairTPHsmallerTPH> simplifiedConstraints = new HashSet<>();
|
|
||||||
final Map<TypePlaceholder, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes = new HashMap<>();
|
|
||||||
final Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
|
|
||||||
|
|
||||||
GenerateGenerics(ResultSet constraints) {
|
|
||||||
for (var constraint : constraints.results) {
|
|
||||||
if (constraint instanceof PairTPHsmallerTPH p) {
|
|
||||||
simplifiedConstraints.add(p);
|
|
||||||
} else if (constraint instanceof PairTPHEqualTPH p) {
|
|
||||||
equality.put(p.getLeft(), p.getRight());
|
|
||||||
} else if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
|
|
||||||
concreteTypes.put(this.equality.getOrDefault(p.left, p.left), p.right);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Simplified constraints: " + simplifiedConstraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<TypePlaceholder> findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type) {
|
|
||||||
var result = new HashSet<TypePlaceholder>();
|
|
||||||
if (type instanceof TypePlaceholder tph) {
|
|
||||||
tph = equality.getOrDefault(tph, tph);
|
|
||||||
if (concreteTypes.containsKey(tph)) {
|
|
||||||
result.addAll(findTypeVariables(concreteTypes.get(tph)));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
result.add(tph);
|
|
||||||
} else if (type instanceof RefType refType) {
|
|
||||||
for (var t : refType.getParaList())
|
|
||||||
result.addAll(findTypeVariables(t));
|
|
||||||
} else if (type instanceof ExtendsWildcardType wildcardType) {
|
|
||||||
result.addAll(findTypeVariables(wildcardType.getInnerType()));
|
|
||||||
} else if (type instanceof SuperWildcardType wildcardType) {
|
|
||||||
result.addAll(findTypeVariables(wildcardType.getInnerType()));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean containsRelation(Set<ResultPair<?, ?>> result, PairTPHsmallerTPH pair) {
|
|
||||||
// Check if both the right and the left are already part of a relation
|
|
||||||
var containsLeft = false;
|
|
||||||
for (var pair2 : result) {
|
|
||||||
if (pair2.getLeft().equals(pair.left)) {
|
|
||||||
containsLeft = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var containsRight = false;
|
|
||||||
for (var pair2 : result) {
|
|
||||||
if (pair2.getRight().equals(pair.right)) {
|
|
||||||
containsRight = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return containsLeft && containsRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addToPairs(Set<ResultPair<?, ?>> input, ResultPair<?, ?> pair) {
|
|
||||||
if (pair instanceof PairTPHsmallerTPH) {
|
|
||||||
input.removeIf(pair2 -> {
|
|
||||||
if (pair2 instanceof PairTPHequalRefTypeOrWildcardType) {
|
|
||||||
return pair2.getLeft().equals(pair.getLeft()) && pair2.getRight().equals(OBJECT);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
} else if (input.stream().anyMatch(p -> p.getLeft().equals(pair.getLeft()))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.add(pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addToEquality(TypePlaceholder from, TypePlaceholder to, Set<TypePlaceholder> referenced) {
|
|
||||||
for (var entry : new HashSet<>(equality.entrySet())) {
|
|
||||||
if (entry.getValue().equals(from)) {
|
|
||||||
equality.remove(entry.getKey());
|
|
||||||
equality.put(entry.getKey(), to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
equality.put(from, to);
|
|
||||||
referenced.remove(from);
|
|
||||||
referenced.add(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Set<ResultPair<?, ?>> transitiveClosure(Set<ResultPair<?, ?>> generics) {
|
|
||||||
Set<ResultPair<?, ?>> all = new HashSet<>(generics);
|
|
||||||
Set<ResultPair<?, ?>> toAdd = new HashSet<>();
|
|
||||||
int sizeBefore;
|
|
||||||
do {
|
|
||||||
sizeBefore = all.size();
|
|
||||||
toAdd.clear();
|
|
||||||
for (var g1 : all) {
|
|
||||||
for (var g2 : all) {
|
|
||||||
if (g1 instanceof PairTPHsmallerTPH pair) {
|
|
||||||
if (g2.getLeft().equals(pair.getRight()) && g2.getRight() instanceof TypePlaceholder right)
|
|
||||||
toAdd.add(new PairTPHsmallerTPH(pair.left, right));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
all.addAll(toAdd);
|
|
||||||
} while (sizeBefore < all.size());
|
|
||||||
return all;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void methodFindConstraints(
|
|
||||||
ClassOrInterface owner, Method method,
|
|
||||||
Set<TypePlaceholder> typeVariables,
|
|
||||||
Set<TypePlaceholder> typeVariablesOfClass,
|
|
||||||
Set<ResultPair<?, ?>> result
|
|
||||||
) {
|
|
||||||
var userDefinedGenericsOfClass = userDefinedGenerics.get(owner);
|
|
||||||
|
|
||||||
// Type variables with bounds that are also type variables of the method
|
|
||||||
for (var typeVariable : new HashSet<>(typeVariables)) {
|
|
||||||
typeVariable = equality.getOrDefault(typeVariable, typeVariable);
|
|
||||||
if (classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, typeVariable))
|
|
||||||
continue;
|
|
||||||
for (var pair : simplifiedConstraints) {
|
|
||||||
var left = equality.getOrDefault(pair.left, pair.left);
|
|
||||||
var right = equality.getOrDefault(pair.right, pair.right);
|
|
||||||
if (left.equals(typeVariable) && typeVariables.contains(right)) {
|
|
||||||
addToPairs(result, new PairTPHsmallerTPH(left, right));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
method.block.accept(new TracingStatementVisitor() {
|
|
||||||
|
|
||||||
private RefTypeOrTPHOrWildcardOrGeneric superType = new Void(new NullToken());
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(MethodCall methodCall) {
|
|
||||||
//Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg
|
|
||||||
//betrachtet werden muessen
|
|
||||||
//Definition 7.2 (Family of generated generics). T1 <. R1 <.^∗ R2 <. T2
|
|
||||||
Set<TypePlaceholder> T1s =
|
|
||||||
methodCall.getArgumentList()
|
|
||||||
.getArguments()
|
|
||||||
.stream()
|
|
||||||
.map(TypableStatement::getType)
|
|
||||||
.collect(Collectors.toCollection(HashSet::new))
|
|
||||||
.stream().filter(x -> x instanceof TypePlaceholder)
|
|
||||||
.map(tph -> equality.getOrDefault(tph, (TypePlaceholder) tph))
|
|
||||||
.collect(Collectors.toCollection(HashSet::new));
|
|
||||||
Set<TypePlaceholder> T2s = new HashSet<>();
|
|
||||||
findTphs(superType, T2s);
|
|
||||||
|
|
||||||
System.out.println("T1s: " + T1s + " T2s: " + T2s);
|
|
||||||
//Ende
|
|
||||||
|
|
||||||
superType = methodCall.receiverType;
|
|
||||||
methodCall.receiver.accept(this);
|
|
||||||
for(int i = 0;i<methodCall.arglist.getArguments().size();i++){
|
|
||||||
superType = methodCall.signature.get(i);
|
|
||||||
methodCall.arglist.getArguments().get(i).accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
|
|
||||||
if (expressionReceiver.expr instanceof This) {
|
|
||||||
var optMethod = findMethod(owner, methodCall.name, methodCall.getArgumentList());
|
|
||||||
if (optMethod.isEmpty()) return;
|
|
||||||
var method = optMethod.get();
|
|
||||||
var generics = generics(owner, method);
|
|
||||||
|
|
||||||
// transitive and
|
|
||||||
var all = transitiveClosure(generics);
|
|
||||||
// reflexive
|
|
||||||
var toAdd = new HashSet<ResultPair<?, ?>>();
|
|
||||||
for (var generic : all) {
|
|
||||||
toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft()));
|
|
||||||
}
|
|
||||||
all.addAll(toAdd);
|
|
||||||
|
|
||||||
HashSet<PairTPHsmallerTPH> newPairs = new HashSet<>();
|
|
||||||
|
|
||||||
// Loop from hell
|
|
||||||
outer:
|
|
||||||
for (var R1 : typeVariables) {
|
|
||||||
R1 = equality.getOrDefault(R1, R1);
|
|
||||||
if (typeVariablesOfClass.contains(R1)) continue;
|
|
||||||
for (var generic : all) if (generic instanceof PairTPHsmallerTPH ptph) {
|
|
||||||
var l = equality.getOrDefault(ptph.left, ptph.left);
|
|
||||||
var r = equality.getOrDefault(ptph.right, ptph.right);
|
|
||||||
|
|
||||||
for (var pair : simplifiedConstraints) {
|
|
||||||
var l2 = equality.getOrDefault(pair.left, pair.left);
|
|
||||||
var r2 = equality.getOrDefault(pair.right, pair.right);
|
|
||||||
if (!(l2.equals(R1) && r2.equals(l)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for (var R2 : typeVariables) {
|
|
||||||
R2 = equality.getOrDefault(R2, R2);
|
|
||||||
for (var pair2 : simplifiedConstraints) {
|
|
||||||
var l3 = equality.getOrDefault(pair2.left, pair2.left);
|
|
||||||
var r3 = equality.getOrDefault(pair2.right, pair2.right);
|
|
||||||
|
|
||||||
if (!(r3.equals(R2) && l3.equals(r)))
|
|
||||||
continue;
|
|
||||||
if (R1.equals(R2)) continue;
|
|
||||||
T2s = T2s.stream().map(tph -> equality.getOrDefault(tph, tph)).collect(Collectors.toSet());
|
|
||||||
T1s = T1s.stream().map(tph -> equality.getOrDefault(tph, tph)).collect(Collectors.toSet());
|
|
||||||
if (!T1s.contains(R1) || !T2s.contains(R2)) continue;
|
|
||||||
|
|
||||||
var newPair = new PairTPHsmallerTPH(R1, R2);
|
|
||||||
newPairs.add(newPair);
|
|
||||||
|
|
||||||
if (!containsRelation(result, newPair))
|
|
||||||
addToPairs(result, newPair);
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
simplifiedConstraints.addAll(newPairs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(LambdaExpression lambdaExpression) {
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
lambdaExpression.methodBody.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(Assign assign) {
|
|
||||||
superType = assign.rightSide.getType();
|
|
||||||
assign.rightSide.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(BinaryExpr binary) {
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
binary.lexpr.accept(this);
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
binary.rexpr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(Block block) {
|
|
||||||
for (var expr : block.statements) {
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
expr.accept(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(IfStmt ifStmt) {
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
ifStmt.expr.accept(this);
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
ifStmt.then_block.accept(this);
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
ifStmt.else_block.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(Return aReturn) {
|
|
||||||
superType = aReturn.getType();
|
|
||||||
aReturn.retexpr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(WhileStmt whileStmt) {
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
whileStmt.expr.accept(this);
|
|
||||||
superType = new Void(new NullToken());
|
|
||||||
whileStmt.loopBlock.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ArgumentList arglist) {
|
|
||||||
for(int i = 0;i<arglist.getArguments().size();i++){
|
|
||||||
superType = arglist.getArguments().get(i).getType();
|
|
||||||
arglist.getArguments().get(i).accept(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var closure = transitiveClosure((Set) simplifiedConstraints);
|
|
||||||
// Type variables with bounds that are also type variables of the class
|
|
||||||
for (var typeVariable : new HashSet<>(typeVariables)) {
|
|
||||||
typeVariable = equality.getOrDefault(typeVariable, typeVariable);
|
|
||||||
if (typeVariablesOfClass.contains(typeVariable)) continue;
|
|
||||||
|
|
||||||
var pairs = new HashSet<PairTPHsmallerTPH>();
|
|
||||||
for (var pair : closure) {
|
|
||||||
if (!(pair instanceof PairTPHsmallerTPH ptph)) continue;
|
|
||||||
var left = equality.getOrDefault(ptph.left, ptph.left);
|
|
||||||
var right = equality.getOrDefault(ptph.right, ptph.right);
|
|
||||||
if (left.equals(typeVariable) && typeVariablesOfClass.contains(right)) {
|
|
||||||
pairs.add(new PairTPHsmallerTPH(left, right));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the closest pair with the minimum amount of steps
|
|
||||||
PairTPHsmallerTPH minimalPair = null;
|
|
||||||
var minSteps = Integer.MAX_VALUE;
|
|
||||||
for (var pair : pairs) {
|
|
||||||
var left = pair.left;
|
|
||||||
var visited = new HashSet<TypePlaceholder>();
|
|
||||||
var steps = 0;
|
|
||||||
while (!left.equals(pair.right)) {
|
|
||||||
visited.add(left);
|
|
||||||
var found = false;
|
|
||||||
for (var pair2 : simplifiedConstraints) {
|
|
||||||
if (left.equals(pair2.left) && !visited.contains(pair2.right)) {
|
|
||||||
left = pair2.right;
|
|
||||||
steps += 1;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) break;
|
|
||||||
}
|
|
||||||
if (steps < minSteps) {
|
|
||||||
minSteps = steps;
|
|
||||||
minimalPair = pair;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minimalPair != null)
|
|
||||||
addToPairs(result, new PairTPHsmallerTPH(
|
|
||||||
equality.getOrDefault(minimalPair.left, minimalPair.left),
|
|
||||||
equality.getOrDefault(minimalPair.right, minimalPair.right)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// All unbounded type variables (bounds not in method)
|
|
||||||
outer:
|
|
||||||
for (var typeVariable : typeVariables) {
|
|
||||||
typeVariable = equality.getOrDefault(typeVariable, typeVariable);
|
|
||||||
if (classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, typeVariable))
|
|
||||||
continue;
|
|
||||||
for (var pair : result) {
|
|
||||||
if (pair.getLeft().equals(typeVariable))
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
addToPairs(result, new PairTPHequalRefTypeOrWildcardType(typeVariable, OBJECT));
|
|
||||||
}
|
|
||||||
|
|
||||||
// All unbounded bounds
|
|
||||||
outer:
|
|
||||||
for (var pair : simplifiedConstraints) {
|
|
||||||
var r = equality.getOrDefault(pair.right, pair.right);
|
|
||||||
for (var pair2 : simplifiedConstraints) {
|
|
||||||
if (r.equals(equality.getOrDefault(pair2.left, pair2.left)))
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
if (!classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, r) && typeVariables.contains(r)) {
|
|
||||||
addToPairs(result, new PairTPHequalRefTypeOrWildcardType(r, OBJECT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean classHasGeneric(Set<GenericTypeVar> userDefinedGenericsOfClass, Set<TypePlaceholder> typeVariablesOfClass, TypePlaceholder typeVariable) {
|
|
||||||
return typeVariablesOfClass.contains(typeVariable) || userDefinedGenericsOfClass.stream().anyMatch(g -> g.getName().equals(typeVariable.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void methodFindTypeVariables(
|
|
||||||
Method method,
|
|
||||||
Set<TypePlaceholder> typeVariables
|
|
||||||
) {
|
|
||||||
|
|
||||||
if (!(method instanceof Constructor))
|
|
||||||
typeVariables.addAll(findTypeVariables(method.getReturnType()));
|
|
||||||
for (var arg : method.getParameterList().getFormalparalist()) {
|
|
||||||
typeVariables.addAll(findTypeVariables(arg.getType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
method.block.accept(new TracingStatementVisitor() {
|
|
||||||
@Override
|
|
||||||
public void visit(LocalVarDecl localVarDecl) {
|
|
||||||
typeVariables.addAll(findTypeVariables(localVarDecl.getType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(MethodCall methodCall) {
|
|
||||||
super.visit(methodCall);
|
|
||||||
typeVariables.addAll(findTypeVariables(methodCall.getType()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract void generics(ClassOrInterface owner, Method method, Set<ResultPair<?,?>> result, Set<TypePlaceholder> javaTypeVariablesOfClass);
|
|
||||||
|
|
||||||
Set<ResultPair<?, ?>> family(ClassOrInterface owner, Method method) {
|
|
||||||
if (familyOfMethods.containsKey(method))
|
|
||||||
return familyOfMethods.get(method);
|
|
||||||
|
|
||||||
var result = new HashSet<ResultPair<?, ?>>();
|
|
||||||
familyOfMethods.put(method, result);
|
|
||||||
|
|
||||||
var classGenerics = generics(owner);
|
|
||||||
HashSet<TypePlaceholder> typeVariablesOfClass = new HashSet<>();
|
|
||||||
|
|
||||||
for (var pair : classGenerics) {
|
|
||||||
typeVariablesOfClass.add((TypePlaceholder) pair.getLeft());
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSet<TypePlaceholder> javaTypeVariables = new HashSet<>();
|
|
||||||
|
|
||||||
methodFindTypeVariables(method, javaTypeVariables);
|
|
||||||
methodFindConstraints(owner, method, javaTypeVariables, typeVariablesOfClass, result);
|
|
||||||
eliminateTransitives(result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<ResultPair<?, ?>> generics(ClassOrInterface owner, Method method) {
|
|
||||||
if (computedGenericsOfMethods.containsKey(method))
|
|
||||||
return computedGenericsOfMethods.get(method);
|
|
||||||
|
|
||||||
var classGenerics = generics(owner);
|
|
||||||
|
|
||||||
HashSet<TypePlaceholder> typeVariablesOfClass = new HashSet<>();
|
|
||||||
|
|
||||||
for (var pair : classGenerics) {
|
|
||||||
typeVariablesOfClass.add((TypePlaceholder) pair.getLeft());
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new HashSet<>(family(owner, method));
|
|
||||||
computedGenericsOfMethods.put(method, result);
|
|
||||||
|
|
||||||
var referenced = new HashSet<TypePlaceholder>();
|
|
||||||
|
|
||||||
var usedTphs = new HashSet<TypePlaceholder>();
|
|
||||||
// For eliminating inner type variables we need to figure out which ones are actually used
|
|
||||||
for (var param : method.getParameterList().getFormalparalist()) {
|
|
||||||
usedTphs.addAll(findTypeVariables(param.getType()));
|
|
||||||
}
|
|
||||||
usedTphs.addAll(findTypeVariables(method.getReturnType()));
|
|
||||||
referenced.addAll(usedTphs);
|
|
||||||
referenced.addAll(typeVariablesOfClass);
|
|
||||||
|
|
||||||
generics(owner, method, result, referenced);
|
|
||||||
usedTPHsOfMethods.put(method, usedTphs);
|
|
||||||
|
|
||||||
addMissingObjectBounds(result, classGenerics, usedTphs);
|
|
||||||
|
|
||||||
System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void eliminateChain(Set<ResultPair<?, ?>> result, List<TypePlaceholder> chain) {
|
|
||||||
for (var pair : new HashSet<>(result)) {
|
|
||||||
if (pair instanceof PairTPHsmallerTPH ptph && chain.get(chain.size() - 1).equals(ptph.left)) {
|
|
||||||
if (chain.contains(ptph.right)) return;
|
|
||||||
var copy = new ArrayList<>(chain);
|
|
||||||
copy.add(ptph.right);
|
|
||||||
if (copy.size() > 2)
|
|
||||||
result.remove(new PairTPHsmallerTPH(chain.get(0), ptph.right));
|
|
||||||
eliminateChain(result, copy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void eliminateTransitives(Set<ResultPair<?,?>> result) {
|
|
||||||
for (var pair : new HashSet<>(result)) if (pair instanceof PairTPHsmallerTPH ptph) {
|
|
||||||
var first = ptph.left;
|
|
||||||
var chain = new ArrayList<TypePlaceholder>();
|
|
||||||
chain.add(ptph.left);
|
|
||||||
chain.add(ptph.right);
|
|
||||||
eliminateChain(result, chain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set<ResultPair<?, ?>> generics, Map<TypePlaceholder, TypePlaceholder> equality) {
|
|
||||||
if (type instanceof TypePlaceholder tph) {
|
|
||||||
tph = equality.getOrDefault(tph, tph);
|
|
||||||
|
|
||||||
var concreteType = concreteTypes.get(tph);
|
|
||||||
if (concreteType != null) {
|
|
||||||
findAllBounds(concreteType, generics, equality);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var found = false;
|
|
||||||
for (var rsp : simplifiedConstraints) {
|
|
||||||
var left = equality.getOrDefault(rsp.left, rsp.left);
|
|
||||||
var right = equality.getOrDefault(rsp.right, rsp.right);
|
|
||||||
if (left.equals(tph)) {
|
|
||||||
var pair = new PairTPHsmallerTPH(tph, right);
|
|
||||||
if (!generics.contains(pair)) {
|
|
||||||
addToPairs(generics, pair);
|
|
||||||
findAllBounds(right, generics, equality);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
addToPairs(generics, new PairTPHequalRefTypeOrWildcardType(tph, OBJECT));
|
|
||||||
} else if (type instanceof RefType refType) {
|
|
||||||
refType.getParaList().forEach(t -> findAllBounds(t, generics, equality));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced);
|
|
||||||
|
|
||||||
Set<ResultPair<?, ?>> generics(ClassOrInterface classOrInterface) {
|
|
||||||
if (computedGenericsOfClasses.containsKey(classOrInterface))
|
|
||||||
return computedGenericsOfClasses.get(classOrInterface);
|
|
||||||
|
|
||||||
Set<ResultPair<?, ?>> javaResult = new HashSet<>();
|
|
||||||
computedGenericsOfClasses.put(classOrInterface, javaResult);
|
|
||||||
|
|
||||||
for (var field : classOrInterface.getFieldDecl()) {
|
|
||||||
findAllBounds(field.getType(), javaResult, equality);
|
|
||||||
}
|
|
||||||
|
|
||||||
var referenced = new HashSet<TypePlaceholder>();
|
|
||||||
eliminateTransitives(javaResult);
|
|
||||||
generics(classOrInterface, javaResult, referenced);
|
|
||||||
|
|
||||||
var referencedByClass = new HashSet<TypePlaceholder>();
|
|
||||||
for (var field : classOrInterface.getFieldDecl()) {
|
|
||||||
findTphs(field.getType(), referencedByClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
addMissingObjectBounds(javaResult, null, referencedByClass);
|
|
||||||
|
|
||||||
System.out.println(this.getClass().getSimpleName() + " Class " + classOrInterface.getClassName().getClassName() + ": " + javaResult);
|
|
||||||
return javaResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMissingObjectBounds(Set<ResultPair<?,?>> result, Set<ResultPair<?, ?>> classGenerics, Set<TypePlaceholder> usedTphs) {
|
|
||||||
outer: for (var tph : usedTphs) {
|
|
||||||
tph = equality.getOrDefault(tph, tph);
|
|
||||||
for (var p1 : new HashSet<>(result)) {
|
|
||||||
if (p1.getLeft().equals(tph)) continue outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypePlaceholder finalTph = tph;
|
|
||||||
if (classGenerics == null || classGenerics.stream().noneMatch((pair) -> pair.getLeft().equals(finalTph)))
|
|
||||||
addToPairs(result, new PairTPHequalRefTypeOrWildcardType(tph, OBJECT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void equalizeTypeVariables(Set<ResultPair<?, ?>> input, Set<TypePlaceholder> referenced) {
|
|
||||||
for (var pair : new HashSet<>(input)) {
|
|
||||||
if (pair instanceof PairTPHsmallerTPH ptph && referenced.contains(ptph.left)) {
|
|
||||||
var chain = new ArrayList<TypePlaceholder>();
|
|
||||||
chain.add(ptph.left);
|
|
||||||
chain.add(ptph.right);
|
|
||||||
|
|
||||||
outer: while (true) {
|
|
||||||
var added = false;
|
|
||||||
for (var pair2 : input) {
|
|
||||||
if (pair2 instanceof PairTPHsmallerTPH ptph2 && ptph2.left.equals(chain.get(chain.size() - 1))) {
|
|
||||||
if (chain.contains(ptph2.right)) break outer;
|
|
||||||
chain.add(ptph2.right);
|
|
||||||
added = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!added) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var variance = chain.get(0).getVariance();
|
|
||||||
if (variance != 1) continue;
|
|
||||||
var index = 0;
|
|
||||||
for (var tph : chain) {
|
|
||||||
if (variance == 1 && tph.getVariance() == -1) {
|
|
||||||
variance = -1;
|
|
||||||
}
|
|
||||||
if (variance == -1 && tph.getVariance() == 1 && referenced.contains(tph)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
if (variance == 1) continue;
|
|
||||||
|
|
||||||
var start = chain.get(0);
|
|
||||||
var prev = start;
|
|
||||||
for (var i = 1; i < index; i++) {
|
|
||||||
var cur = chain.get(i);
|
|
||||||
if (!referenced.contains(cur)) continue;
|
|
||||||
addToEquality(cur, start, referenced);
|
|
||||||
input.remove(new PairTPHsmallerTPH(prev, cur));
|
|
||||||
for (var pair2 : new HashSet<>(input)) {
|
|
||||||
// TODO Maybe this would be unnecessary if we were to add the = constraints later on
|
|
||||||
if (pair2 instanceof PairTPHequalRefTypeOrWildcardType && pair2.getLeft().equals(cur)) {
|
|
||||||
input.remove(pair2);
|
|
||||||
input.add(new PairTPHequalRefTypeOrWildcardType(start, pair2.getRight()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prev = chain.get(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void findTphs(RefTypeOrTPHOrWildcardOrGeneric type, Set<TypePlaceholder> tphs) {
|
|
||||||
if (type instanceof RefType refType) {
|
|
||||||
refType.getParaList().forEach(t -> findTphs(t, tphs));
|
|
||||||
} else if (type instanceof TypePlaceholder tph) {
|
|
||||||
tph = equality.getOrDefault(tph, tph);
|
|
||||||
var concreteType = concreteTypes.get(tph);
|
|
||||||
if (concreteType != null) {
|
|
||||||
findTphs(concreteType, tphs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tphs.add(tph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void eliminateInnerTypeVariablesOfClass(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> input, Set<TypePlaceholder> referenced) {
|
|
||||||
for (var field : classOrInterface.getFieldDecl()) {
|
|
||||||
findTphs(field.getType(), referenced);
|
|
||||||
}
|
|
||||||
for (var method : classOrInterface.getMethods()) {
|
|
||||||
generics(classOrInterface, method);
|
|
||||||
referenced.addAll(usedTPHsOfMethods.get(method));
|
|
||||||
}
|
|
||||||
eliminateInnerTypeVariables(referenced, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findChain(Set<TypePlaceholder> referenced, Set<ResultPair<?, ?>> input, Set<ResultPair<?, ?>> output, TypePlaceholder start, TypePlaceholder end, Set<TypePlaceholder> chain) {
|
|
||||||
if (referenced.contains(end)) {
|
|
||||||
var pair = new PairTPHsmallerTPH(start, end);
|
|
||||||
output.add(pair);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var foundNext = false;
|
|
||||||
for (var pair : input) {
|
|
||||||
if (pair instanceof PairTPHsmallerTPH ptph && ptph.left.equals(end)) {
|
|
||||||
if (chain.contains(ptph.right)) return;
|
|
||||||
chain = new HashSet<>(chain);
|
|
||||||
chain.add(ptph.right);
|
|
||||||
findChain(referenced, input, output, start, ptph.right, chain);
|
|
||||||
foundNext = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!foundNext) {
|
|
||||||
output.add(new PairTPHequalRefTypeOrWildcardType(start, OBJECT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void eliminateInnerTypeVariables(Set<TypePlaceholder> referenced, Set<ResultPair<?, ?>> input) {
|
|
||||||
var output = new HashSet<ResultPair<?, ?>>();
|
|
||||||
for (var tph : referenced) {
|
|
||||||
for (var pair : input) {
|
|
||||||
if (pair instanceof PairTPHsmallerTPH pthp && pthp.left.equals(tph)) {
|
|
||||||
var chain = new HashSet<TypePlaceholder>();
|
|
||||||
chain.add(tph);
|
|
||||||
findChain(referenced, input, output, tph, pthp.right, chain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (var pair : input) {
|
|
||||||
if (pair instanceof PairTPHequalRefTypeOrWildcardType rtph) {
|
|
||||||
if (referenced.contains(rtph.left))
|
|
||||||
output.add(rtph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input.clear();
|
|
||||||
input.addAll(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void eliminateCycles(Set<ResultPair<?, ?>> input, Set<TypePlaceholder> referenced) {
|
|
||||||
var cycles = findCycles(input);
|
|
||||||
for (var cycle : cycles) {
|
|
||||||
var newTph = TypePlaceholder.fresh(new NullToken());
|
|
||||||
var variance = cycle.get(0).getVariance();
|
|
||||||
for (var tph : cycle) {
|
|
||||||
if (tph.getVariance() != variance) {
|
|
||||||
variance = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newTph.setVariance(variance);
|
|
||||||
|
|
||||||
referenced.add(newTph);
|
|
||||||
addToPairs(input, new PairTPHequalRefTypeOrWildcardType(newTph, OBJECT));
|
|
||||||
cycle.add(cycle.get(0)); // Make it a complete cycle
|
|
||||||
for (var i = 0; i < cycle.size() - 1; i++) {
|
|
||||||
var left = cycle.get(i);
|
|
||||||
var right = cycle.get(i + 1);
|
|
||||||
var pair = new PairTPHsmallerTPH(left, right);
|
|
||||||
input.remove(pair);
|
|
||||||
addToEquality(left, newTph, referenced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void eliminateInfima(Set<ResultPair<?, ?>> input, Set<TypePlaceholder> referenced) {
|
|
||||||
var foundInfima = false;
|
|
||||||
do {
|
|
||||||
foundInfima = false;
|
|
||||||
for (var constraint : new HashSet<>(input)) {
|
|
||||||
var left = (TypePlaceholder) constraint.getLeft();
|
|
||||||
Set<PairTPHsmallerTPH> infima = new HashSet<>();
|
|
||||||
for (var pair : input) {
|
|
||||||
if (pair instanceof PairTPHsmallerTPH stph)
|
|
||||||
if (pair.getLeft().equals(constraint.getLeft()))
|
|
||||||
infima.add(stph);
|
|
||||||
}
|
|
||||||
if (infima.size() > 1) {
|
|
||||||
foundInfima = true;
|
|
||||||
var newTph = TypePlaceholder.fresh(new NullToken());
|
|
||||||
var variance = infima.stream().findFirst().get().right.getVariance();
|
|
||||||
for (var pair : infima) {
|
|
||||||
if (pair.right.getVariance() != variance) {
|
|
||||||
variance = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newTph.setVariance(variance);
|
|
||||||
|
|
||||||
//referenced.add(newTph);
|
|
||||||
addToPairs(input, new PairTPHsmallerTPH(left, newTph));
|
|
||||||
input.removeAll(infima);
|
|
||||||
for (var infimum : infima) {
|
|
||||||
addToEquality(infimum.right, newTph, referenced);
|
|
||||||
new HashSet<>(input).forEach(pair -> {
|
|
||||||
if (pair.getLeft().equals(infimum.right)) {
|
|
||||||
input.remove(pair);
|
|
||||||
if (pair instanceof PairTPHsmallerTPH stph) {
|
|
||||||
if (!newTph.equals(stph.right))
|
|
||||||
addToPairs(input, new PairTPHsmallerTPH(newTph, stph.right));
|
|
||||||
} else if (pair instanceof PairTPHequalRefTypeOrWildcardType rtph) {
|
|
||||||
addToPairs(input, new PairTPHequalRefTypeOrWildcardType(newTph, rtph.getRight()));
|
|
||||||
}
|
|
||||||
} else if (pair.getRight().equals(infimum.right)) {
|
|
||||||
input.remove(pair);
|
|
||||||
if (pair instanceof PairTPHsmallerTPH stph) {
|
|
||||||
if (!newTph.equals(stph.left))
|
|
||||||
addToPairs(input, new PairTPHsmallerTPH(stph.left, newTph));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (foundInfima);
|
|
||||||
}
|
|
||||||
|
|
||||||
RefTypeOrTPHOrWildcardOrGeneric getType(RefTypeOrTPHOrWildcardOrGeneric type) {
|
|
||||||
if (type instanceof TypePlaceholder tph) {
|
|
||||||
if (equality.containsKey(tph)) {
|
|
||||||
return getType(equality.get(tph));
|
|
||||||
}
|
|
||||||
return concreteTypes.getOrDefault(tph, tph);
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
|
|
||||||
if (in instanceof TypePlaceholder tph) {
|
|
||||||
if (equality.containsKey(tph)) {
|
|
||||||
return getTargetType(equality.get(tph));
|
|
||||||
}
|
|
||||||
var type = concreteTypes.get(tph);
|
|
||||||
if (type == null) return new TargetGenericType(tph.getName());
|
|
||||||
return convert(type, this);
|
|
||||||
}
|
|
||||||
return convert(in, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IByteArrayClassLoader classLoader;
|
protected IByteArrayClassLoader classLoader;
|
||||||
protected SourceFile sourceFile;
|
protected SourceFile sourceFile;
|
||||||
|
|
||||||
@ -862,100 +52,11 @@ public class ASTToTargetAST {
|
|||||||
|
|
||||||
all = new ArrayList<>();
|
all = new ArrayList<>();
|
||||||
for (var set : resultSets) {
|
for (var set : resultSets) {
|
||||||
all.add(new Generics(new JavaGenerics(set), new TxGenerics(set)));
|
all.add(new Generics(new JavaGenerics(this, set), new TxGenerics(this, set)));
|
||||||
}
|
}
|
||||||
this.generics = all.get(0);
|
this.generics = all.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Set<TypePlaceholder> allNodes(Set<ResultPair<?, ?>> input) {
|
|
||||||
return input.stream()
|
|
||||||
.filter(pair -> pair instanceof PairTPHsmallerTPH)
|
|
||||||
.flatMap(pair -> Stream.of((TypePlaceholder) pair.getLeft(), (TypePlaceholder) pair.getRight())).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
static Set<TypePlaceholder> outgoingEdgesOf(TypePlaceholder tph, Set<ResultPair<?, ?>> input) {
|
|
||||||
return input.stream()
|
|
||||||
.filter(pair -> pair instanceof PairTPHsmallerTPH && pair.getLeft().equals(tph))
|
|
||||||
.map(pair -> (TypePlaceholder) pair.getRight()).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean containsEdge(TypePlaceholder a, TypePlaceholder b, Set<ResultPair<?, ?>> input) {
|
|
||||||
return input.stream().anyMatch(pair -> pair.getLeft().equals(a) && pair.getRight().equals(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tiernan simple cycles algorithm
|
|
||||||
// Adapted from https://github.com/jgrapht/jgrapht/blob/master/jgrapht-core/src/main/java/org/jgrapht/alg/cycle/TiernanSimpleCycles.java
|
|
||||||
static Set<List<TypePlaceholder>> findCycles(Set<ResultPair<?, ?>> input) {
|
|
||||||
Map<TypePlaceholder, Integer> indices = new HashMap<>();
|
|
||||||
List<TypePlaceholder> path = new ArrayList<>();
|
|
||||||
Set<TypePlaceholder> pathSet = new HashSet<>();
|
|
||||||
Map<TypePlaceholder, Set<TypePlaceholder>> blocked = new HashMap<>();
|
|
||||||
Set<List<TypePlaceholder>> cycles = new HashSet<>();
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
for (var tph : allNodes(input)) {
|
|
||||||
blocked.put(tph, new HashSet<>());
|
|
||||||
indices.put(tph, index++);
|
|
||||||
}
|
|
||||||
|
|
||||||
var vertexIterator = allNodes(input).iterator();
|
|
||||||
if (!vertexIterator.hasNext()) return cycles;
|
|
||||||
|
|
||||||
TypePlaceholder startOfPath = null;
|
|
||||||
TypePlaceholder endOfPath = vertexIterator.next();
|
|
||||||
TypePlaceholder temp = null;
|
|
||||||
int endIndex = 0;
|
|
||||||
boolean extensionFound = false;
|
|
||||||
path.add(endOfPath);
|
|
||||||
pathSet.add(endOfPath);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
do {
|
|
||||||
extensionFound = false;
|
|
||||||
for (TypePlaceholder n : outgoingEdgesOf(endOfPath, input)) {
|
|
||||||
int cmp = indices.get(n).compareTo(indices.get(path.get(0)));
|
|
||||||
if ((cmp > 0) && !pathSet.contains(n) && !blocked.get(endOfPath).contains(n)) {
|
|
||||||
path.add(n);
|
|
||||||
pathSet.add(n);
|
|
||||||
endOfPath = n;
|
|
||||||
extensionFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (extensionFound);
|
|
||||||
|
|
||||||
startOfPath = path.get(0);
|
|
||||||
if (containsEdge(endOfPath, startOfPath, input)) {
|
|
||||||
List<TypePlaceholder> cycle = new ArrayList<>(path);
|
|
||||||
cycles.add(cycle);
|
|
||||||
}
|
|
||||||
if (path.size() > 1) {
|
|
||||||
blocked.get(endOfPath).clear();
|
|
||||||
endIndex = path.size() - 1;
|
|
||||||
path.remove(endIndex);
|
|
||||||
pathSet.remove(endOfPath);
|
|
||||||
--endIndex;
|
|
||||||
temp = endOfPath;
|
|
||||||
endOfPath = path.get(endIndex);
|
|
||||||
blocked.get(endOfPath).add(temp);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (vertexIterator.hasNext()) {
|
|
||||||
path.clear();
|
|
||||||
pathSet.clear();
|
|
||||||
endOfPath = vertexIterator.next();
|
|
||||||
path.add(endOfPath);
|
|
||||||
pathSet.add(endOfPath);
|
|
||||||
for (TypePlaceholder tph : blocked.keySet()) {
|
|
||||||
blocked.get(tph).clear();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return cycles;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Method> findMethod(ClassOrInterface owner, String name, ArgumentList argumentList) {
|
Optional<Method> findMethod(ClassOrInterface owner, String name, ArgumentList argumentList) {
|
||||||
return owner.getMethods().stream().filter(
|
return owner.getMethods().stream().filter(
|
||||||
m -> m.name.equals(name) && parameterEquals(m.getParameterList(), argumentList)
|
m -> m.name.equals(name) && parameterEquals(m.getParameterList(), argumentList)
|
||||||
@ -979,12 +80,12 @@ public class ASTToTargetAST {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<TargetGeneric> convert(Set<ResultPair<?, ?>> result, GenerateGenerics generics) {
|
Set<TargetGeneric> convert(Set<GenerateGenerics.Pair> result, GenerateGenerics generics) {
|
||||||
return result.stream().map(p -> {
|
return result.stream().map(p -> {
|
||||||
if (p instanceof PairTPHsmallerTPH pair) {
|
if (p instanceof GenerateGenerics.PairLT pair) {
|
||||||
return new TargetGeneric(pair.left.getName(), convert(pair.right, generics));
|
return new TargetGeneric(pair.left.resolve().getName(), convert(pair.right.resolve(), generics));
|
||||||
} else if (p instanceof PairTPHequalRefTypeOrWildcardType pair) {
|
} else if (p instanceof GenerateGenerics.PairEQ pair) {
|
||||||
return new TargetGeneric(pair.left.getName(), convert(pair.right, generics));
|
return new TargetGeneric(pair.left.resolve().getName(), convert(pair.right, generics));
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
@ -1012,6 +113,8 @@ public class ASTToTargetAST {
|
|||||||
while (genericsIter.hasNext()) {
|
while (genericsIter.hasNext()) {
|
||||||
var next = genericsIter.next();
|
var next = genericsIter.next();
|
||||||
userDefinedGenerics.add(next);
|
userDefinedGenerics.add(next);
|
||||||
|
// TODO Support multiple bounds
|
||||||
|
javaGenerics.add(new TargetGeneric(next.getName(), convert(next.getBounds().get(0))));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.userDefinedGenerics.put(input, new HashSet<>());
|
this.userDefinedGenerics.put(input, new HashSet<>());
|
||||||
@ -1043,7 +146,7 @@ public class ASTToTargetAST {
|
|||||||
return generics.stream().anyMatch(g -> g.name().equals(type.getParsedName()));
|
return generics.stream().anyMatch(g -> g.name().equals(type.getParsedName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<TargetGeneric> collectMethodGenerics(GenerateGenerics generateGenerics, Set<ResultPair<?, ?>> generics, Method input) {
|
private Set<TargetGeneric> collectMethodGenerics(GenerateGenerics generateGenerics, Set<GenerateGenerics.Pair> generics, Method input) {
|
||||||
var convertedGenerics = new HashSet<>(convert(generics, generateGenerics));
|
var convertedGenerics = new HashSet<>(convert(generics, generateGenerics));
|
||||||
outer:
|
outer:
|
||||||
for (GenericTypeVar typeVar : input.getGenerics()) {
|
for (GenericTypeVar typeVar : input.getGenerics()) {
|
||||||
|
104
src/main/java/de/dhbwstuttgart/target/generate/CycleFinder.java
Normal file
104
src/main/java/de/dhbwstuttgart/target/generate/CycleFinder.java
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class CycleFinder {
|
||||||
|
private CycleFinder() {}
|
||||||
|
|
||||||
|
static Set<GenerateGenerics.TPH> allNodes(Set<? extends GenerateGenerics.Pair> input) {
|
||||||
|
return input.stream()
|
||||||
|
.filter(GenerateGenerics.PairLT.class::isInstance)
|
||||||
|
.map(GenerateGenerics.PairLT.class::cast)
|
||||||
|
.flatMap(pair -> Stream.of(pair.left, pair.right)).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
static Set<GenerateGenerics.TPH> outgoingEdgesOf(GenerateGenerics.TPH tph, Set<? extends GenerateGenerics.Pair> input) {
|
||||||
|
return input.stream()
|
||||||
|
.filter(GenerateGenerics.PairLT.class::isInstance)
|
||||||
|
.map(GenerateGenerics.PairLT.class::cast)
|
||||||
|
.filter(pair -> pair.left.equals(tph))
|
||||||
|
.map(pair -> pair.right).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean containsEdge(GenerateGenerics.TPH a, GenerateGenerics.TPH b, Set<? extends GenerateGenerics.Pair> input) {
|
||||||
|
return input.stream()
|
||||||
|
.filter(GenerateGenerics.PairLT.class::isInstance)
|
||||||
|
.map(GenerateGenerics.PairLT.class::cast)
|
||||||
|
.anyMatch(pair -> pair.left.equals(a) && pair.right.equals(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tiernan simple cycles algorithm
|
||||||
|
// Adapted from https://github.com/jgrapht/jgrapht/blob/master/jgrapht-core/src/main/java/org/jgrapht/alg/cycle/TiernanSimpleCycles.java
|
||||||
|
static Set<List<GenerateGenerics.TPH>> findCycles(Set<? extends GenerateGenerics.Pair> input) {
|
||||||
|
Map<GenerateGenerics.TPH, Integer> indices = new HashMap<>();
|
||||||
|
List<GenerateGenerics.TPH> path = new ArrayList<>();
|
||||||
|
Set<GenerateGenerics.TPH> pathSet = new HashSet<>();
|
||||||
|
Map<GenerateGenerics.TPH, Set<GenerateGenerics.TPH>> blocked = new HashMap<>();
|
||||||
|
Set<List<GenerateGenerics.TPH>> cycles = new HashSet<>();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
for (var tph : allNodes(input)) {
|
||||||
|
blocked.put(tph, new HashSet<>());
|
||||||
|
indices.put(tph, index++);
|
||||||
|
}
|
||||||
|
|
||||||
|
var vertexIterator = allNodes(input).iterator();
|
||||||
|
if (!vertexIterator.hasNext()) return cycles;
|
||||||
|
|
||||||
|
GenerateGenerics.TPH startOfPath = null;
|
||||||
|
GenerateGenerics.TPH endOfPath = vertexIterator.next();
|
||||||
|
GenerateGenerics.TPH temp = null;
|
||||||
|
int endIndex = 0;
|
||||||
|
boolean extensionFound = false;
|
||||||
|
path.add(endOfPath);
|
||||||
|
pathSet.add(endOfPath);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
do {
|
||||||
|
extensionFound = false;
|
||||||
|
for (GenerateGenerics.TPH n : outgoingEdgesOf(endOfPath, input)) {
|
||||||
|
int cmp = indices.get(n).compareTo(indices.get(path.get(0)));
|
||||||
|
if ((cmp > 0) && !pathSet.contains(n) && !blocked.get(endOfPath).contains(n)) {
|
||||||
|
path.add(n);
|
||||||
|
pathSet.add(n);
|
||||||
|
endOfPath = n;
|
||||||
|
extensionFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (extensionFound);
|
||||||
|
|
||||||
|
startOfPath = path.get(0);
|
||||||
|
if (containsEdge(endOfPath, startOfPath, input)) {
|
||||||
|
List<GenerateGenerics.TPH> cycle = new ArrayList<>(path);
|
||||||
|
cycles.add(cycle);
|
||||||
|
}
|
||||||
|
if (path.size() > 1) {
|
||||||
|
blocked.get(endOfPath).clear();
|
||||||
|
endIndex = path.size() - 1;
|
||||||
|
path.remove(endIndex);
|
||||||
|
pathSet.remove(endOfPath);
|
||||||
|
--endIndex;
|
||||||
|
temp = endOfPath;
|
||||||
|
endOfPath = path.get(endIndex);
|
||||||
|
blocked.get(endOfPath).add(temp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (vertexIterator.hasNext()) {
|
||||||
|
path.clear();
|
||||||
|
pathSet.clear();
|
||||||
|
endOfPath = vertexIterator.next();
|
||||||
|
path.add(endOfPath);
|
||||||
|
pathSet.add(endOfPath);
|
||||||
|
for (GenerateGenerics.TPH tph : blocked.keySet()) {
|
||||||
|
blocked.get(tph).clear();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return cycles;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,872 @@
|
|||||||
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.parser.NullToken;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.Constructor;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.Method;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.statement.*;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.type.*;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.type.Void;
|
||||||
|
import de.dhbwstuttgart.target.tree.type.TargetGenericType;
|
||||||
|
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||||
|
import de.dhbwstuttgart.typeinference.result.PairTPHEqualTPH;
|
||||||
|
import de.dhbwstuttgart.typeinference.result.PairTPHequalRefTypeOrWildcardType;
|
||||||
|
import de.dhbwstuttgart.typeinference.result.PairTPHsmallerTPH;
|
||||||
|
import de.dhbwstuttgart.typeinference.result.ResultSet;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public abstract class GenerateGenerics {
|
||||||
|
|
||||||
|
private final ASTToTargetAST astToTargetAST;
|
||||||
|
|
||||||
|
public class TPH {
|
||||||
|
final TypePlaceholder wrap;
|
||||||
|
|
||||||
|
TPH(TypePlaceholder wrap) {
|
||||||
|
this.wrap = wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypePlaceholder resolve() {
|
||||||
|
return equality.getOrDefault(wrap, wrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
TPH tph = (TPH) o;
|
||||||
|
return Objects.equals(resolve(), tph.resolve());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(resolve());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return resolve().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class Pair {
|
||||||
|
public final TPH left;
|
||||||
|
|
||||||
|
Pair(TPH left) {
|
||||||
|
this.left = left;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract RefTypeOrTPHOrWildcardOrGeneric resolveRight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PairLT extends Pair {
|
||||||
|
public final TPH right;
|
||||||
|
|
||||||
|
PairLT(TPH left, TPH right) {
|
||||||
|
super(left);
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
PairLT pairLT = (PairLT) o;
|
||||||
|
return Objects.equals(right, pairLT.right) && Objects.equals(left, pairLT.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(right, left);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RefTypeOrTPHOrWildcardOrGeneric resolveRight() {
|
||||||
|
return right.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + left + " < " + right + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PairEQ extends Pair {
|
||||||
|
public final RefType right;
|
||||||
|
|
||||||
|
PairEQ(TPH left, RefType right) {
|
||||||
|
super(left);
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
PairEQ pairLT = (PairEQ) o;
|
||||||
|
return Objects.equals(right, pairLT.right) && Objects.equals(left, pairLT.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(right, left);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RefTypeOrTPHOrWildcardOrGeneric resolveRight() {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + left + " = " + right + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<Method, Set<Pair>> computedGenericsOfMethods = new HashMap<>();
|
||||||
|
final Map<ClassOrInterface, Set<Pair>> computedGenericsOfClasses = new HashMap<>();
|
||||||
|
|
||||||
|
final Map<Method, Set<TPH>> usedTPHsOfMethods = new HashMap<>();
|
||||||
|
final Map<Method, Set<Pair>> familyOfMethods = new HashMap<>();
|
||||||
|
|
||||||
|
final Set<PairLT> simplifiedConstraints = new HashSet<>();
|
||||||
|
final Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes = new HashMap<>();
|
||||||
|
final Map<TypePlaceholder, TypePlaceholder> equality = new HashMap<>();
|
||||||
|
|
||||||
|
GenerateGenerics(ASTToTargetAST astToTargetAST, ResultSet constraints) {
|
||||||
|
this.astToTargetAST = astToTargetAST;
|
||||||
|
for (var constraint : constraints.results) {
|
||||||
|
if (constraint instanceof PairTPHsmallerTPH p) {
|
||||||
|
simplifiedConstraints.add(new PairLT(new TPH(p.left), new TPH(p.right)));
|
||||||
|
} else if (constraint instanceof PairTPHEqualTPH p) {
|
||||||
|
equality.put(p.getLeft(), p.getRight());
|
||||||
|
} else if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
|
||||||
|
concreteTypes.put(new TPH(p.left), p.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Simplified constraints: " + simplifiedConstraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<TPH> findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type) {
|
||||||
|
var result = new HashSet<TPH>();
|
||||||
|
if (type instanceof TypePlaceholder tph) {
|
||||||
|
var nTph = new TPH(tph);
|
||||||
|
if (concreteTypes.containsKey(nTph)) {
|
||||||
|
result.addAll(findTypeVariables(concreteTypes.get(nTph)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.add(nTph);
|
||||||
|
} else if (type instanceof RefType refType) {
|
||||||
|
for (var t : refType.getParaList())
|
||||||
|
result.addAll(findTypeVariables(t));
|
||||||
|
} else if (type instanceof ExtendsWildcardType wildcardType) {
|
||||||
|
result.addAll(findTypeVariables(wildcardType.getInnerType()));
|
||||||
|
} else if (type instanceof SuperWildcardType wildcardType) {
|
||||||
|
result.addAll(findTypeVariables(wildcardType.getInnerType()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean containsRelation(Set<Pair> result, PairLT pair) {
|
||||||
|
// Check if both the right and the left are already part of a relation
|
||||||
|
var containsLeft = false;
|
||||||
|
for (var pair2 : result) {
|
||||||
|
if (pair2.left.equals(pair.left)) {
|
||||||
|
containsLeft = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var containsRight = false;
|
||||||
|
for (var pair2 : result)
|
||||||
|
if (pair2 instanceof PairLT plt) {
|
||||||
|
if (plt.right.equals(pair.right)) {
|
||||||
|
containsRight = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return containsLeft && containsRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToPairs(Set<Pair> input, Pair pair) {
|
||||||
|
if (pair instanceof PairLT plt) {
|
||||||
|
input.removeIf(pair2 -> {
|
||||||
|
if (pair2 instanceof PairEQ peq) {
|
||||||
|
return peq.left.equals(plt.left) && peq.right.equals(ASTToTargetAST.OBJECT);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
} else if (input.stream().anyMatch(p -> p.left.equals(pair.left))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.add(pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addToEquality(TypePlaceholder from, TypePlaceholder to, Set<TPH> referenced) {
|
||||||
|
for (var entry : new HashSet<>(equality.entrySet())) {
|
||||||
|
if (entry.getValue().equals(from)) {
|
||||||
|
equality.remove(entry.getKey());
|
||||||
|
equality.put(entry.getKey(), to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
equality.put(from, to);
|
||||||
|
referenced.remove(new TPH(from));
|
||||||
|
referenced.add(new TPH(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Pair> transitiveClosure(Set<? extends Pair> generics) {
|
||||||
|
Set<Pair> all = new HashSet<>(generics);
|
||||||
|
Set<Pair> toAdd = new HashSet<>();
|
||||||
|
int sizeBefore;
|
||||||
|
do {
|
||||||
|
sizeBefore = all.size();
|
||||||
|
toAdd.clear();
|
||||||
|
for (var g1 : all) {
|
||||||
|
for (var g2 : all) {
|
||||||
|
if (g1 instanceof PairLT pair && g2 instanceof PairLT pair2) {
|
||||||
|
if (pair2.left.equals(pair.right))
|
||||||
|
toAdd.add(new PairLT(pair.left, pair2.right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all.addAll(toAdd);
|
||||||
|
} while (sizeBefore < all.size());
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void methodFindConstraints(
|
||||||
|
ClassOrInterface owner, Method method,
|
||||||
|
Set<TPH> typeVariables,
|
||||||
|
Set<TPH> typeVariablesOfClass,
|
||||||
|
Set<Pair> result
|
||||||
|
) {
|
||||||
|
var userDefinedGenericsOfClass = astToTargetAST.userDefinedGenerics.get(owner);
|
||||||
|
|
||||||
|
// Type variables with bounds that are also type variables of the method
|
||||||
|
for (var typeVariable : new HashSet<>(typeVariables)) {
|
||||||
|
if (classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, typeVariable))
|
||||||
|
continue;
|
||||||
|
for (var pair : simplifiedConstraints) {
|
||||||
|
if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right)) {
|
||||||
|
addToPairs(result, new PairLT(pair.left, pair.right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method.block.accept(new TracingStatementVisitor() {
|
||||||
|
|
||||||
|
private RefTypeOrTPHOrWildcardOrGeneric superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MethodCall methodCall) {
|
||||||
|
//Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg
|
||||||
|
//betrachtet werden muessen
|
||||||
|
//Definition 7.2 (Family of generated generics). T1 <. R1 <.^∗ R2 <. T2
|
||||||
|
Set<TPH> T1s =
|
||||||
|
methodCall.getArgumentList()
|
||||||
|
.getArguments()
|
||||||
|
.stream()
|
||||||
|
.map(TypableStatement::getType)
|
||||||
|
.collect(Collectors.toCollection(HashSet::new))
|
||||||
|
.stream().filter(TypePlaceholder.class::isInstance)
|
||||||
|
.map(TypePlaceholder.class::cast)
|
||||||
|
.map(TPH::new)
|
||||||
|
.collect(Collectors.toCollection(HashSet::new));
|
||||||
|
Set<TPH> T2s = new HashSet<>();
|
||||||
|
findTphs(superType, T2s);
|
||||||
|
|
||||||
|
System.out.println("T1s: " + T1s + " T2s: " + T2s);
|
||||||
|
//Ende
|
||||||
|
|
||||||
|
superType = methodCall.receiverType;
|
||||||
|
methodCall.receiver.accept(this);
|
||||||
|
for (int i = 0; i < methodCall.arglist.getArguments().size(); i++) {
|
||||||
|
superType = methodCall.signature.get(i);
|
||||||
|
methodCall.arglist.getArguments().get(i).accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver) {
|
||||||
|
if (expressionReceiver.expr instanceof This) {
|
||||||
|
var optMethod = astToTargetAST.findMethod(owner, methodCall.name, methodCall.getArgumentList());
|
||||||
|
if (optMethod.isEmpty()) return;
|
||||||
|
var method = optMethod.get();
|
||||||
|
var generics = generics(owner, method);
|
||||||
|
|
||||||
|
// transitive and
|
||||||
|
var all = transitiveClosure(generics);
|
||||||
|
// reflexive
|
||||||
|
var toAdd = new HashSet<Pair>();
|
||||||
|
for (var generic : all) {
|
||||||
|
toAdd.add(new PairLT(generic.left, generic.left));
|
||||||
|
}
|
||||||
|
all.addAll(toAdd);
|
||||||
|
|
||||||
|
HashSet<PairLT> newPairs = new HashSet<>();
|
||||||
|
|
||||||
|
// Loop from hell
|
||||||
|
outer:
|
||||||
|
for (var R1 : typeVariables) {
|
||||||
|
if (typeVariablesOfClass.contains(R1)) continue;
|
||||||
|
for (var generic : all)
|
||||||
|
if (generic instanceof PairLT ptph) {
|
||||||
|
for (var pair : simplifiedConstraints) {
|
||||||
|
if (!(pair.left.equals(R1) && pair.right.equals(ptph.left)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (var R2 : typeVariables) {
|
||||||
|
for (var pair2 : simplifiedConstraints) {
|
||||||
|
|
||||||
|
if (!(pair2.right.equals(R2) && pair2.left.equals(ptph.right)))
|
||||||
|
continue;
|
||||||
|
if (R1.equals(R2)) continue;
|
||||||
|
if (!T1s.contains(R1) || !T2s.contains(R2)) continue;
|
||||||
|
|
||||||
|
var newPair = new PairLT(R1, R2);
|
||||||
|
newPairs.add(newPair);
|
||||||
|
|
||||||
|
if (!containsRelation(result, newPair))
|
||||||
|
addToPairs(result, newPair);
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simplifiedConstraints.addAll(newPairs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(LambdaExpression lambdaExpression) {
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
lambdaExpression.methodBody.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Assign assign) {
|
||||||
|
superType = assign.rightSide.getType();
|
||||||
|
assign.rightSide.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(BinaryExpr binary) {
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
binary.lexpr.accept(this);
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
binary.rexpr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Block block) {
|
||||||
|
for (var expr : block.statements) {
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
expr.accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(IfStmt ifStmt) {
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
ifStmt.expr.accept(this);
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
ifStmt.then_block.accept(this);
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
ifStmt.else_block.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(Return aReturn) {
|
||||||
|
superType = aReturn.getType();
|
||||||
|
aReturn.retexpr.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(WhileStmt whileStmt) {
|
||||||
|
superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken());
|
||||||
|
whileStmt.expr.accept(this);
|
||||||
|
superType = new Void(new NullToken());
|
||||||
|
whileStmt.loopBlock.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ArgumentList arglist) {
|
||||||
|
for (int i = 0; i < arglist.getArguments().size(); i++) {
|
||||||
|
superType = arglist.getArguments().get(i).getType();
|
||||||
|
arglist.getArguments().get(i).accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var closure = transitiveClosure(simplifiedConstraints);
|
||||||
|
// Type variables with bounds that are also type variables of the class
|
||||||
|
for (var typeVariable : new HashSet<>(typeVariables)) {
|
||||||
|
if (typeVariablesOfClass.contains(typeVariable)) continue;
|
||||||
|
|
||||||
|
var pairs = new HashSet<PairLT>();
|
||||||
|
for (var pair : closure) {
|
||||||
|
if (!(pair instanceof PairLT ptph)) continue;
|
||||||
|
if (ptph.left.equals(typeVariable) && typeVariablesOfClass.contains(ptph.right)) {
|
||||||
|
pairs.add(new PairLT(ptph.left, ptph.right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the closest pair with the minimum amount of steps
|
||||||
|
PairLT minimalPair = null;
|
||||||
|
var minSteps = Integer.MAX_VALUE;
|
||||||
|
for (var pair : pairs) {
|
||||||
|
var left = pair.left;
|
||||||
|
var visited = new HashSet<TPH>();
|
||||||
|
var steps = 0;
|
||||||
|
while (!left.equals(pair.right)) {
|
||||||
|
visited.add(left);
|
||||||
|
var found = false;
|
||||||
|
for (var pair2 : simplifiedConstraints) {
|
||||||
|
if (left.equals(pair2.left) && !visited.contains(pair2.right)) {
|
||||||
|
left = pair2.right;
|
||||||
|
steps += 1;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) break;
|
||||||
|
}
|
||||||
|
if (steps < minSteps) {
|
||||||
|
minSteps = steps;
|
||||||
|
minimalPair = pair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minimalPair != null)
|
||||||
|
addToPairs(result, minimalPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All unbounded type variables (bounds not in method)
|
||||||
|
outer:
|
||||||
|
for (var typeVariable : typeVariables) {
|
||||||
|
if (classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, typeVariable))
|
||||||
|
continue;
|
||||||
|
for (var pair : result) {
|
||||||
|
if (pair.left.equals(typeVariable))
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
addToPairs(result, new PairEQ(typeVariable, ASTToTargetAST.OBJECT));
|
||||||
|
}
|
||||||
|
|
||||||
|
// All unbounded bounds
|
||||||
|
outer:
|
||||||
|
for (var pair : simplifiedConstraints) {
|
||||||
|
for (var pair2 : simplifiedConstraints) {
|
||||||
|
if (pair.right.equals(pair2.left))
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
if (!classHasGeneric(userDefinedGenericsOfClass, typeVariablesOfClass, pair.right) && typeVariables.contains(pair.right)) {
|
||||||
|
addToPairs(result, new PairEQ(pair.right, ASTToTargetAST.OBJECT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean classHasGeneric(Set<GenericTypeVar> userDefinedGenericsOfClass, Set<TPH> typeVariablesOfClass, TPH typeVariable) {
|
||||||
|
return typeVariablesOfClass.contains(typeVariable) || userDefinedGenericsOfClass.stream().anyMatch(g -> g.getName().equals(typeVariable.resolve().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void methodFindTypeVariables(
|
||||||
|
Method method,
|
||||||
|
Set<TPH> typeVariables
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (!(method instanceof Constructor))
|
||||||
|
typeVariables.addAll(findTypeVariables(method.getReturnType()));
|
||||||
|
for (var arg : method.getParameterList().getFormalparalist()) {
|
||||||
|
typeVariables.addAll(findTypeVariables(arg.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
method.block.accept(new TracingStatementVisitor() {
|
||||||
|
@Override
|
||||||
|
public void visit(LocalVarDecl localVarDecl) {
|
||||||
|
typeVariables.addAll(findTypeVariables(localVarDecl.getType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MethodCall methodCall) {
|
||||||
|
super.visit(methodCall);
|
||||||
|
typeVariables.addAll(findTypeVariables(methodCall.getType()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void generics(ClassOrInterface owner, Method method, Set<Pair> result, Set<TPH> javaTypeVariablesOfClass);
|
||||||
|
|
||||||
|
Set<Pair> family(ClassOrInterface owner, Method method) {
|
||||||
|
if (familyOfMethods.containsKey(method))
|
||||||
|
return familyOfMethods.get(method);
|
||||||
|
|
||||||
|
var result = new HashSet<Pair>();
|
||||||
|
familyOfMethods.put(method, result);
|
||||||
|
|
||||||
|
var classGenerics = generics(owner);
|
||||||
|
HashSet<TPH> typeVariablesOfClass = new HashSet<>();
|
||||||
|
|
||||||
|
for (var pair : classGenerics) {
|
||||||
|
typeVariablesOfClass.add(pair.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<TPH> javaTypeVariables = new HashSet<>();
|
||||||
|
|
||||||
|
methodFindTypeVariables(method, javaTypeVariables);
|
||||||
|
methodFindConstraints(owner, method, javaTypeVariables, typeVariablesOfClass, result);
|
||||||
|
eliminateTransitives(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Pair> generics(ClassOrInterface owner, Method method) {
|
||||||
|
if (computedGenericsOfMethods.containsKey(method))
|
||||||
|
return computedGenericsOfMethods.get(method);
|
||||||
|
|
||||||
|
var classGenerics = generics(owner);
|
||||||
|
|
||||||
|
HashSet<TPH> typeVariablesOfClass = new HashSet<>();
|
||||||
|
|
||||||
|
for (var pair : classGenerics) {
|
||||||
|
typeVariablesOfClass.add(pair.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new HashSet<>(family(owner, method));
|
||||||
|
computedGenericsOfMethods.put(method, result);
|
||||||
|
|
||||||
|
var referenced = new HashSet<TPH>();
|
||||||
|
|
||||||
|
var usedTphs = new HashSet<TPH>();
|
||||||
|
// For eliminating inner type variables we need to figure out which ones are actually used
|
||||||
|
for (var param : method.getParameterList().getFormalparalist()) {
|
||||||
|
usedTphs.addAll(findTypeVariables(param.getType()));
|
||||||
|
}
|
||||||
|
usedTphs.addAll(findTypeVariables(method.getReturnType()));
|
||||||
|
referenced.addAll(usedTphs);
|
||||||
|
referenced.addAll(typeVariablesOfClass);
|
||||||
|
|
||||||
|
generics(owner, method, result, referenced);
|
||||||
|
usedTPHsOfMethods.put(method, usedTphs);
|
||||||
|
|
||||||
|
addMissingObjectBounds(result, classGenerics, usedTphs);
|
||||||
|
|
||||||
|
System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void eliminateChain(Set<Pair> result, List<TPH> chain) {
|
||||||
|
for (var pair : new HashSet<>(result)) {
|
||||||
|
if (pair instanceof PairLT ptph && chain.get(chain.size() - 1).equals(ptph.left)) {
|
||||||
|
if (chain.contains(ptph.right)) return;
|
||||||
|
var copy = new ArrayList<>(chain);
|
||||||
|
copy.add(ptph.right);
|
||||||
|
if (copy.size() > 2)
|
||||||
|
result.remove(new PairLT(chain.get(0), ptph.right));
|
||||||
|
eliminateChain(result, copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void eliminateTransitives(Set<Pair> result) {
|
||||||
|
for (var pair : new HashSet<>(result))
|
||||||
|
if (pair instanceof PairLT ptph) {
|
||||||
|
var first = ptph.left;
|
||||||
|
var chain = new ArrayList<TPH>();
|
||||||
|
chain.add(ptph.left);
|
||||||
|
chain.add(ptph.right);
|
||||||
|
eliminateChain(result, chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set<Pair> generics) {
|
||||||
|
if (type instanceof TypePlaceholder tph) {
|
||||||
|
var nTph = new TPH(tph);
|
||||||
|
var concreteType = concreteTypes.get(nTph);
|
||||||
|
if (concreteType != null) {
|
||||||
|
findAllBounds(concreteType, generics);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var found = false;
|
||||||
|
for (var rsp : simplifiedConstraints) {
|
||||||
|
if (rsp.left.equals(nTph)) {
|
||||||
|
var pair = new PairLT(new TPH(tph), rsp.right);
|
||||||
|
if (!generics.contains(pair)) {
|
||||||
|
addToPairs(generics, pair);
|
||||||
|
findAllBounds(rsp.right.resolve(), generics);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
addToPairs(generics, new PairEQ(nTph, ASTToTargetAST.OBJECT));
|
||||||
|
} else if (type instanceof RefType refType) {
|
||||||
|
refType.getParaList().forEach(t -> findAllBounds(t, generics));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void generics(ClassOrInterface classOrInterface, Set<Pair> result, Set<TPH> referenced);
|
||||||
|
|
||||||
|
Set<Pair> generics(ClassOrInterface classOrInterface) {
|
||||||
|
if (computedGenericsOfClasses.containsKey(classOrInterface))
|
||||||
|
return computedGenericsOfClasses.get(classOrInterface);
|
||||||
|
|
||||||
|
Set<Pair> javaResult = new HashSet<>();
|
||||||
|
computedGenericsOfClasses.put(classOrInterface, javaResult);
|
||||||
|
|
||||||
|
for (var field : classOrInterface.getFieldDecl()) {
|
||||||
|
findAllBounds(field.getType(), javaResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
var referenced = new HashSet<TPH>();
|
||||||
|
eliminateTransitives(javaResult);
|
||||||
|
generics(classOrInterface, javaResult, referenced);
|
||||||
|
|
||||||
|
var referencedByClass = new HashSet<TPH>();
|
||||||
|
for (var field : classOrInterface.getFieldDecl()) {
|
||||||
|
findTphs(field.getType(), referencedByClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
addMissingObjectBounds(javaResult, null, referencedByClass);
|
||||||
|
|
||||||
|
System.out.println(this.getClass().getSimpleName() + " Class " + classOrInterface.getClassName().getClassName() + ": " + javaResult);
|
||||||
|
return javaResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMissingObjectBounds(Set<Pair> result, Set<Pair> classGenerics, Set<TPH> usedTphs) {
|
||||||
|
outer:
|
||||||
|
for (var tph : usedTphs) {
|
||||||
|
for (var p1 : new HashSet<>(result)) {
|
||||||
|
if (p1.left.equals(tph)) continue outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classGenerics == null || classGenerics.stream().noneMatch((pair) -> pair.left.equals(tph)))
|
||||||
|
addToPairs(result, new PairEQ(tph, ASTToTargetAST.OBJECT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void equalizeTypeVariables(Set<Pair> input, Set<TPH> referenced) {
|
||||||
|
for (var pair : new HashSet<>(input)) {
|
||||||
|
if (pair instanceof PairLT ptph && referenced.contains(ptph.left)) {
|
||||||
|
var chain = new ArrayList<TPH>();
|
||||||
|
chain.add(ptph.left);
|
||||||
|
chain.add(ptph.right);
|
||||||
|
|
||||||
|
outer:
|
||||||
|
while (true) {
|
||||||
|
var added = false;
|
||||||
|
for (var pair2 : input) {
|
||||||
|
if (pair2 instanceof PairLT ptph2 && ptph2.left.equals(chain.get(chain.size() - 1))) {
|
||||||
|
if (chain.contains(ptph2.right)) break outer;
|
||||||
|
chain.add(ptph2.right);
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!added) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var variance = chain.get(0).resolve().getVariance();
|
||||||
|
if (variance != 1) continue;
|
||||||
|
var index = 0;
|
||||||
|
for (var tph : chain) {
|
||||||
|
if (variance == 1 && tph.resolve().getVariance() == -1) {
|
||||||
|
variance = -1;
|
||||||
|
}
|
||||||
|
if (variance == -1 && tph.resolve().getVariance() == 1 && referenced.contains(tph)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (variance == 1) continue;
|
||||||
|
|
||||||
|
var start = chain.get(0);
|
||||||
|
var prev = start;
|
||||||
|
for (var i = 1; i < index; i++) {
|
||||||
|
var cur = chain.get(i);
|
||||||
|
if (!referenced.contains(cur)) continue;
|
||||||
|
addToEquality(cur.wrap, start.resolve(), referenced);
|
||||||
|
TPH finalPrev = prev;
|
||||||
|
input.removeIf(p -> p.equals(new PairLT(finalPrev, cur)));
|
||||||
|
for (var pair2 : new HashSet<>(input)) {
|
||||||
|
// TODO Maybe this would be unnecessary if we were to add the = constraints later on
|
||||||
|
if (pair2 instanceof PairEQ peq && pair.left.equals(cur)) {
|
||||||
|
input.remove(pair2);
|
||||||
|
input.add(new PairEQ(start, peq.right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev = chain.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void findTphs(RefTypeOrTPHOrWildcardOrGeneric type, Set<TPH> tphs) {
|
||||||
|
if (type instanceof RefType refType) {
|
||||||
|
refType.getParaList().forEach(t -> findTphs(t, tphs));
|
||||||
|
} else if (type instanceof TypePlaceholder tph) {
|
||||||
|
tph = equality.getOrDefault(tph, tph);
|
||||||
|
var concreteType = concreteTypes.get(new TPH(tph));
|
||||||
|
if (concreteType != null) {
|
||||||
|
findTphs(concreteType, tphs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tphs.add(new TPH(tph));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void eliminateInnerTypeVariablesOfClass(ClassOrInterface classOrInterface, Set<Pair> input, Set<TPH> referenced) {
|
||||||
|
for (var field : classOrInterface.getFieldDecl()) {
|
||||||
|
findTphs(field.getType(), referenced);
|
||||||
|
}
|
||||||
|
for (var method : classOrInterface.getMethods()) {
|
||||||
|
generics(classOrInterface, method);
|
||||||
|
referenced.addAll(usedTPHsOfMethods.get(method));
|
||||||
|
}
|
||||||
|
eliminateInnerTypeVariables(referenced, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findChain(Set<TPH> referenced, Set<Pair> input, Set<Pair> output, TPH start, TPH end, Set<TPH> chain) {
|
||||||
|
if (referenced.contains(end)) {
|
||||||
|
var pair = new PairLT(start, end);
|
||||||
|
output.add(pair);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var foundNext = false;
|
||||||
|
for (var pair : input) {
|
||||||
|
if (pair instanceof PairLT ptph && ptph.left.equals(end)) {
|
||||||
|
if (chain.contains(ptph.right)) return;
|
||||||
|
chain = new HashSet<>(chain);
|
||||||
|
chain.add(ptph.right);
|
||||||
|
findChain(referenced, input, output, start, ptph.right, chain);
|
||||||
|
foundNext = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundNext) {
|
||||||
|
output.add(new PairEQ(start, ASTToTargetAST.OBJECT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void eliminateInnerTypeVariables(Set<TPH> referenced, Set<Pair> input) {
|
||||||
|
var output = new HashSet<Pair>();
|
||||||
|
for (var tph : referenced) {
|
||||||
|
for (var pair : input) {
|
||||||
|
if (pair instanceof PairLT pthp && pthp.left.equals(tph)) {
|
||||||
|
var chain = new HashSet<TPH>();
|
||||||
|
chain.add(tph);
|
||||||
|
findChain(referenced, input, output, tph, pthp.right, chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var pair : input) {
|
||||||
|
if (pair instanceof PairEQ rtph) {
|
||||||
|
if (referenced.contains(rtph.left))
|
||||||
|
output.add(rtph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.clear();
|
||||||
|
input.addAll(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void eliminateCycles(Set<Pair> input, Set<TPH> referenced) {
|
||||||
|
var cycles = CycleFinder.findCycles(input);
|
||||||
|
for (var cycle : cycles) {
|
||||||
|
var newTph = TypePlaceholder.fresh(new NullToken());
|
||||||
|
var variance = cycle.get(0).resolve().getVariance();
|
||||||
|
for (var tph : cycle) {
|
||||||
|
if (tph.resolve().getVariance() != variance) {
|
||||||
|
variance = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newTph.setVariance(variance);
|
||||||
|
|
||||||
|
referenced.add(new TPH(newTph));
|
||||||
|
addToPairs(input, new PairEQ(new TPH(newTph), ASTToTargetAST.OBJECT));
|
||||||
|
cycle.add(cycle.get(0)); // Make it a complete cycle
|
||||||
|
for (var i = 0; i < cycle.size() - 1; i++) {
|
||||||
|
var left = cycle.get(i);
|
||||||
|
var right = cycle.get(i + 1);
|
||||||
|
var pair = new PairLT(left, right);
|
||||||
|
input.remove(pair);
|
||||||
|
addToEquality(left.wrap, newTph, referenced);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void eliminateInfima(Set<Pair> input, Set<TPH> referenced) {
|
||||||
|
var foundInfima = false;
|
||||||
|
do {
|
||||||
|
foundInfima = false;
|
||||||
|
for (var constraint : new HashSet<>(input)) {
|
||||||
|
var left = constraint.left;
|
||||||
|
Set<PairLT> infima = new HashSet<>();
|
||||||
|
for (var pair : input) {
|
||||||
|
if (pair instanceof PairLT stph)
|
||||||
|
if (pair.left.wrap.equals(constraint.left.wrap))
|
||||||
|
infima.add(stph);
|
||||||
|
}
|
||||||
|
if (infima.size() > 1) {
|
||||||
|
foundInfima = true;
|
||||||
|
var newTph = TypePlaceholder.fresh(new NullToken());
|
||||||
|
var variance = infima.stream().findFirst().get().right.resolve().getVariance();
|
||||||
|
for (var pair : infima) {
|
||||||
|
if (pair.right.resolve().getVariance() != variance) {
|
||||||
|
variance = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newTph.setVariance(variance);
|
||||||
|
|
||||||
|
//referenced.add(newTph);
|
||||||
|
addToPairs(input, new PairLT(left, new TPH(newTph)));
|
||||||
|
input.removeAll(infima);
|
||||||
|
for (var infimum : infima) {
|
||||||
|
addToEquality(infimum.right.wrap, newTph, referenced);
|
||||||
|
new HashSet<>(input).forEach(pair -> {
|
||||||
|
if (pair.left.wrap.equals(infimum.right.wrap)) {
|
||||||
|
input.remove(pair);
|
||||||
|
if (pair instanceof PairLT stph) {
|
||||||
|
if (!newTph.equals(stph.right.wrap))
|
||||||
|
addToPairs(input, new PairLT(new TPH(newTph), stph.right));
|
||||||
|
} else if (pair instanceof PairEQ rtph) {
|
||||||
|
addToPairs(input, new PairEQ(new TPH(newTph), rtph.right));
|
||||||
|
}
|
||||||
|
} else if (pair instanceof PairLT stph && stph.right.wrap.equals(infimum.right.wrap)) {
|
||||||
|
input.remove(pair);
|
||||||
|
if (!newTph.equals(stph.left.wrap))
|
||||||
|
addToPairs(input, new PairLT(stph.left, new TPH(newTph)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (foundInfima);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefTypeOrTPHOrWildcardOrGeneric getType(RefTypeOrTPHOrWildcardOrGeneric type) {
|
||||||
|
if (type instanceof TypePlaceholder tph) {
|
||||||
|
if (equality.containsKey(tph)) {
|
||||||
|
return getType(equality.get(tph));
|
||||||
|
}
|
||||||
|
return concreteTypes.getOrDefault(new TPH(tph), tph);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
|
||||||
|
if (in instanceof TypePlaceholder tph) {
|
||||||
|
if (equality.containsKey(tph)) {
|
||||||
|
return getTargetType(equality.get(tph));
|
||||||
|
}
|
||||||
|
var type = concreteTypes.get(new TPH(tph));
|
||||||
|
if (type == null) return new TargetGenericType(tph.getName());
|
||||||
|
return astToTargetAST.convert(type, this);
|
||||||
|
}
|
||||||
|
return astToTargetAST.convert(in, this);
|
||||||
|
}
|
||||||
|
}
|
@ -5,14 +5,13 @@ import de.dhbwstuttgart.syntaxtree.Method;
|
|||||||
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
||||||
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
||||||
import de.dhbwstuttgart.target.tree.type.TargetType;
|
import de.dhbwstuttgart.target.tree.type.TargetType;
|
||||||
import de.dhbwstuttgart.typeinference.result.ResultPair;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class GenericsResult {
|
public class GenericsResult {
|
||||||
private final ASTToTargetAST.GenerateGenerics generics;
|
private final GenerateGenerics generics;
|
||||||
|
|
||||||
GenericsResult(ASTToTargetAST.GenerateGenerics generics) {
|
GenericsResult(GenerateGenerics generics) {
|
||||||
this.generics = generics;
|
this.generics = generics;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,16 +41,16 @@ public class GenericsResult {
|
|||||||
do {
|
do {
|
||||||
bound = Optional.empty();
|
bound = Optional.empty();
|
||||||
for (var pair : methodGenerics) {
|
for (var pair : methodGenerics) {
|
||||||
if (pair.getLeft().equals(type)) {
|
if (pair.left.resolve().equals(type)) {
|
||||||
type = resolve(pair.getRight());
|
type = pair.resolveRight();
|
||||||
bound = Optional.of(new Bound(true, type));
|
bound = Optional.of(new Bound(true, type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (bound.isEmpty()) {
|
if (bound.isEmpty()) {
|
||||||
for (var pair : classGenerics) {
|
for (var pair : classGenerics) {
|
||||||
if (pair.getLeft().equals(type)) {
|
if (pair.left.resolve().equals(type)) {
|
||||||
type = resolve(pair.getRight());
|
type = pair.resolveRight();
|
||||||
bound = Optional.of(new Bound(false, type));
|
bound = Optional.of(new Bound(false, type));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
package de.dhbwstuttgart.target.generate;
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
||||||
import de.dhbwstuttgart.typeinference.result.PairTPHEqualTPH;
|
import de.dhbwstuttgart.typeinference.result.PairTPHequalRefTypeOrWildcardType;
|
||||||
|
import de.dhbwstuttgart.typeinference.result.PairTPHsmallerTPH;
|
||||||
import de.dhbwstuttgart.typeinference.result.ResultPair;
|
import de.dhbwstuttgart.typeinference.result.ResultPair;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class GenericsResultSet extends AbstractSet<ResultPair<?, ?>> {
|
public class GenericsResultSet extends AbstractSet<GenerateGenerics.Pair> {
|
||||||
|
|
||||||
final Set<ResultPair<?, ?>> backing;
|
final Set<GenerateGenerics.Pair> backing;
|
||||||
final Map<TypePlaceholder, TypePlaceholder> equality;
|
final Map<TypePlaceholder, TypePlaceholder> equality;
|
||||||
|
|
||||||
public GenericsResultSet(Set<ResultPair<?, ?>> backing, Map<TypePlaceholder, TypePlaceholder> equality) {
|
public GenericsResultSet(Set<GenerateGenerics.Pair> backing, Map<TypePlaceholder, TypePlaceholder> equality) {
|
||||||
this.backing = backing == null ? new HashSet<>() : new HashSet<>(backing);
|
this.backing = backing == null ? new HashSet<>() : new HashSet<>(backing);
|
||||||
this.equality = equality;
|
this.equality = equality;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<ResultPair<?, ?>> iterator() {
|
public Iterator<GenerateGenerics.Pair> iterator() {
|
||||||
return backing.iterator();
|
return backing.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +30,14 @@ public class GenericsResultSet extends AbstractSet<ResultPair<?, ?>> {
|
|||||||
public Optional<ResultPair<?, ?>> getResultPairFor(TypePlaceholder tph) {
|
public Optional<ResultPair<?, ?>> getResultPairFor(TypePlaceholder tph) {
|
||||||
var tph2 = equality.getOrDefault(tph, tph);
|
var tph2 = equality.getOrDefault(tph, tph);
|
||||||
return this.stream().filter(pair -> {
|
return this.stream().filter(pair -> {
|
||||||
return pair.getLeft().equals(tph2);
|
return pair.left.resolve().equals(tph2);
|
||||||
}).findFirst();
|
}).findFirst().map(pair -> {
|
||||||
|
ResultPair<?, ?> res = null;
|
||||||
|
if (pair instanceof GenerateGenerics.PairLT lt)
|
||||||
|
res = new PairTPHsmallerTPH(lt.left.resolve(), lt.right.resolve());
|
||||||
|
else if (pair instanceof GenerateGenerics.PairEQ eq)
|
||||||
|
res = new PairTPHequalRefTypeOrWildcardType(eq.left.resolve(), eq.right);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.Method;
|
||||||
|
import de.dhbwstuttgart.typeinference.result.ResultSet;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
final class JavaGenerics extends GenerateGenerics {
|
||||||
|
JavaGenerics(ASTToTargetAST astToTargetAST, ResultSet constraints) {
|
||||||
|
super(astToTargetAST, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generics(ClassOrInterface owner, Method method, Set<Pair> result, Set<TPH> referenced) {
|
||||||
|
eliminateCycles(result, referenced);
|
||||||
|
eliminateInfima(result, referenced);
|
||||||
|
//equalizeTypeVariables(result, referenced);
|
||||||
|
eliminateInnerTypeVariables(referenced, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generics(ClassOrInterface classOrInterface, Set<Pair> result, Set<TPH> referenced) {
|
||||||
|
eliminateCycles(result, referenced);
|
||||||
|
eliminateInfima(result, referenced);
|
||||||
|
eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced);
|
||||||
|
equalizeTypeVariables(result, referenced);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
||||||
|
import de.dhbwstuttgart.syntaxtree.Method;
|
||||||
|
import de.dhbwstuttgart.typeinference.result.ResultSet;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
final class TxGenerics extends GenerateGenerics {
|
||||||
|
TxGenerics(ASTToTargetAST astToTargetAST, ResultSet constraints) {
|
||||||
|
super(astToTargetAST, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generics(ClassOrInterface owner, Method method, Set<Pair> result, Set<TPH> referenced) {
|
||||||
|
eliminateInfima(result, referenced);
|
||||||
|
eliminateInnerTypeVariables(referenced, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void generics(ClassOrInterface classOrInterface, Set<Pair> result, Set<TPH> referenced) {
|
||||||
|
eliminateInfima(result, referenced);
|
||||||
|
eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced);
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import java.util.HashSet;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.target.generate.GenerateGenerics;
|
||||||
import de.dhbwstuttgart.target.generate.GenericsResult;
|
import de.dhbwstuttgart.target.generate.GenericsResult;
|
||||||
import de.dhbwstuttgart.target.generate.GenericsResultSet;
|
import de.dhbwstuttgart.target.generate.GenericsResultSet;
|
||||||
import de.dhbwstuttgart.typeinference.result.*;
|
import de.dhbwstuttgart.typeinference.result.*;
|
||||||
@ -89,10 +90,10 @@ public class TypeInsertFactory {
|
|||||||
String insert = " <";
|
String insert = " <";
|
||||||
|
|
||||||
for (var genericInsertConstraint : constraints) {
|
for (var genericInsertConstraint : constraints) {
|
||||||
if (genericInsertConstraint instanceof PairTPHequalRefTypeOrWildcardType peq) {
|
if (genericInsertConstraint instanceof GenerateGenerics.PairEQ peq) {
|
||||||
insert += peq.left.getName();
|
insert += peq.left.resolve().getName();
|
||||||
} else if (genericInsertConstraint instanceof PairTPHsmallerTPH psm) {
|
} else if (genericInsertConstraint instanceof GenerateGenerics.PairLT psm) {
|
||||||
insert += psm.left.getName() + " extends " + psm.right.getName();
|
insert += psm.left.resolve().getName() + " extends " + psm.right.resolve().getName();
|
||||||
}
|
}
|
||||||
insert += ", ";
|
insert += ", ";
|
||||||
}
|
}
|
||||||
@ -115,10 +116,10 @@ public class TypeInsertFactory {
|
|||||||
String insert = " <";
|
String insert = " <";
|
||||||
|
|
||||||
for (var genericInsertConstraint : classConstraints) {
|
for (var genericInsertConstraint : classConstraints) {
|
||||||
if (genericInsertConstraint instanceof PairTPHequalRefTypeOrWildcardType peq) {
|
if (genericInsertConstraint instanceof GenerateGenerics.PairEQ peq) {
|
||||||
insert += peq.left.getName();
|
insert += peq.left.resolve().getName();
|
||||||
} else if (genericInsertConstraint instanceof PairTPHsmallerTPH psm) {
|
} else if (genericInsertConstraint instanceof GenerateGenerics.PairLT psm) {
|
||||||
insert += psm.left.getName() + " extends " + psm.right.getName();
|
insert += psm.left.resolve().getName() + " extends " + psm.right.resolve().getName();
|
||||||
}
|
}
|
||||||
insert += ", ";
|
insert += ", ";
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user