From bb0ee7d517dc4748c46a67eaaabb41124d626582 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Tue, 13 Jun 2023 16:33:54 +0200 Subject: [PATCH] Refactoring --- resources/AllgemeinTest/Iteration.jav | 12 +- .../target/generate/ASTToTargetAST.java | 915 +----------------- .../target/generate/CycleFinder.java | 104 ++ .../target/generate/GenerateGenerics.java | 872 +++++++++++++++++ .../target/generate/GenericsResult.java | 13 +- .../target/generate/GenericsResultSet.java | 22 +- .../target/generate/JavaGenerics.java | 29 + .../target/generate/TxGenerics.java | 25 + .../typedeployment/TypeInsertFactory.java | 17 +- 9 files changed, 1078 insertions(+), 931 deletions(-) create mode 100644 src/main/java/de/dhbwstuttgart/target/generate/CycleFinder.java create mode 100644 src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java create mode 100644 src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java create mode 100644 src/main/java/de/dhbwstuttgart/target/generate/TxGenerics.java diff --git a/resources/AllgemeinTest/Iteration.jav b/resources/AllgemeinTest/Iteration.jav index 29b201f5..c0b9dd65 100644 --- a/resources/AllgemeinTest/Iteration.jav +++ b/resources/AllgemeinTest/Iteration.jav @@ -20,18 +20,24 @@ class Pair { public class Iteration { + id(x) { + return x; + } + m1(x, y) { var help; help = m2(x, y); var y2 = help.snd(); - return new Pair<>(x,y2); - + var x2 = id(x); + return new Pair<>(x2,y2); + } m2(x,y) { var help = m1(x, y); var x2 = help.fst(); - return new Pair<>(x2, y); + var y2 = id(y); + return new Pair<>(x2, y2); } } \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index a258e5fb..6508b0a5 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -3,12 +3,10 @@ package de.dhbwstuttgart.target.generate; import de.dhbwstuttgart.bytecode.FunNGenerator; import de.dhbwstuttgart.environment.ByteArrayClassLoader; import de.dhbwstuttgart.environment.IByteArrayClassLoader; -import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.syntaxtree.*; import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; import de.dhbwstuttgart.syntaxtree.statement.*; import de.dhbwstuttgart.syntaxtree.type.*; -import de.dhbwstuttgart.syntaxtree.type.Void; import de.dhbwstuttgart.target.tree.*; import de.dhbwstuttgart.target.tree.expression.TargetBlock; import de.dhbwstuttgart.target.tree.expression.TargetExpression; @@ -41,814 +39,6 @@ public class ASTToTargetAST { record Generics(JavaGenerics javaGenerics, TxGenerics txGenerics) {} - final class TxGenerics extends GenerateGenerics { - TxGenerics(ResultSet constraints) { - super(constraints); - } - - @Override - void generics(ClassOrInterface owner, Method method, Set> result, Set referenced) { - eliminateInfima(result, referenced); - eliminateInnerTypeVariables(referenced, result); - } - - @Override - void generics(ClassOrInterface classOrInterface, Set> result, Set 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> result, Set referenced) { - eliminateCycles(result, referenced); - eliminateInfima(result, referenced); - equalizeTypeVariables(result, referenced); - eliminateInnerTypeVariables(referenced, result); - } - - @Override - void generics(ClassOrInterface classOrInterface, Set> result, Set referenced) { - eliminateCycles(result, referenced); - eliminateInfima(result, referenced); - equalizeTypeVariables(result, referenced); - eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); - } - } - - abstract class GenerateGenerics { - final Map>> computedGenericsOfMethods = new HashMap<>(); - final Map>> computedGenericsOfClasses = new HashMap<>(); - - final Map> usedTPHsOfMethods = new HashMap<>(); - final Map>> familyOfMethods = new HashMap<>(); - - final Set simplifiedConstraints = new HashSet<>(); - final Map concreteTypes = new HashMap<>(); - final Map 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 findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type) { - var result = new HashSet(); - 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> 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> 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 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> transitiveClosure(Set> generics) { - Set> all = new HashSet<>(generics); - Set> 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 typeVariables, - Set typeVariablesOfClass, - Set> 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 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 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>(); - for (var generic : all) { - toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft())); - } - all.addAll(toAdd); - - HashSet 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(typeVariables)) { - typeVariable = equality.getOrDefault(typeVariable, typeVariable); - if (typeVariablesOfClass.contains(typeVariable)) continue; - - var pairs = new HashSet(); - 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(); - 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 userDefinedGenericsOfClass, Set typeVariablesOfClass, TypePlaceholder typeVariable) { - return typeVariablesOfClass.contains(typeVariable) || userDefinedGenericsOfClass.stream().anyMatch(g -> g.getName().equals(typeVariable.getName())); - } - - private void methodFindTypeVariables( - Method method, - Set 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> result, Set javaTypeVariablesOfClass); - - Set> family(ClassOrInterface owner, Method method) { - if (familyOfMethods.containsKey(method)) - return familyOfMethods.get(method); - - var result = new HashSet>(); - familyOfMethods.put(method, result); - - var classGenerics = generics(owner); - HashSet typeVariablesOfClass = new HashSet<>(); - - for (var pair : classGenerics) { - typeVariablesOfClass.add((TypePlaceholder) pair.getLeft()); - } - - HashSet javaTypeVariables = new HashSet<>(); - - methodFindTypeVariables(method, javaTypeVariables); - methodFindConstraints(owner, method, javaTypeVariables, typeVariablesOfClass, result); - eliminateTransitives(result); - - return result; - } - - Set> generics(ClassOrInterface owner, Method method) { - if (computedGenericsOfMethods.containsKey(method)) - return computedGenericsOfMethods.get(method); - - var classGenerics = generics(owner); - - HashSet 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(); - - var usedTphs = new HashSet(); - // 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> result, List 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> result) { - for (var pair : new HashSet<>(result)) if (pair instanceof PairTPHsmallerTPH ptph) { - var first = ptph.left; - var chain = new ArrayList(); - chain.add(ptph.left); - chain.add(ptph.right); - eliminateChain(result, chain); - } - } - - void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set> generics, Map 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> result, Set referenced); - - Set> generics(ClassOrInterface classOrInterface) { - if (computedGenericsOfClasses.containsKey(classOrInterface)) - return computedGenericsOfClasses.get(classOrInterface); - - Set> javaResult = new HashSet<>(); - computedGenericsOfClasses.put(classOrInterface, javaResult); - - for (var field : classOrInterface.getFieldDecl()) { - findAllBounds(field.getType(), javaResult, equality); - } - - var referenced = new HashSet(); - eliminateTransitives(javaResult); - generics(classOrInterface, javaResult, referenced); - - var referencedByClass = new HashSet(); - 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> result, Set> classGenerics, Set 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> input, Set referenced) { - for (var pair : new HashSet<>(input)) { - if (pair instanceof PairTPHsmallerTPH ptph && referenced.contains(ptph.left)) { - var chain = new ArrayList(); - 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 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> input, Set 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 referenced, Set> input, Set> output, TypePlaceholder start, TypePlaceholder end, Set 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 referenced, Set> input) { - var output = new HashSet>(); - for (var tph : referenced) { - for (var pair : input) { - if (pair instanceof PairTPHsmallerTPH pthp && pthp.left.equals(tph)) { - var chain = new HashSet(); - 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> input, Set 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> input, Set referenced) { - var foundInfima = false; - do { - foundInfima = false; - for (var constraint : new HashSet<>(input)) { - var left = (TypePlaceholder) constraint.getLeft(); - Set 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 SourceFile sourceFile; @@ -862,100 +52,11 @@ public class ASTToTargetAST { all = new ArrayList<>(); 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); } - static Set allNodes(Set> input) { - return input.stream() - .filter(pair -> pair instanceof PairTPHsmallerTPH) - .flatMap(pair -> Stream.of((TypePlaceholder) pair.getLeft(), (TypePlaceholder) pair.getRight())).collect(Collectors.toSet()); - } - - static Set outgoingEdgesOf(TypePlaceholder tph, Set> 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> 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> findCycles(Set> input) { - Map indices = new HashMap<>(); - List path = new ArrayList<>(); - Set pathSet = new HashSet<>(); - Map> blocked = new HashMap<>(); - Set> 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 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 findMethod(ClassOrInterface owner, String name, ArgumentList argumentList) { return owner.getMethods().stream().filter( m -> m.name.equals(name) && parameterEquals(m.getParameterList(), argumentList) @@ -979,12 +80,12 @@ public class ASTToTargetAST { return true; } - Set convert(Set> result, GenerateGenerics generics) { + Set convert(Set result, GenerateGenerics generics) { return result.stream().map(p -> { - if (p instanceof PairTPHsmallerTPH pair) { - return new TargetGeneric(pair.left.getName(), convert(pair.right, generics)); - } else if (p instanceof PairTPHequalRefTypeOrWildcardType pair) { - return new TargetGeneric(pair.left.getName(), convert(pair.right, generics)); + if (p instanceof GenerateGenerics.PairLT pair) { + return new TargetGeneric(pair.left.resolve().getName(), convert(pair.right.resolve(), generics)); + } else if (p instanceof GenerateGenerics.PairEQ pair) { + return new TargetGeneric(pair.left.resolve().getName(), convert(pair.right, generics)); } else { throw new IllegalArgumentException(); } @@ -1012,6 +113,8 @@ public class ASTToTargetAST { while (genericsIter.hasNext()) { var next = genericsIter.next(); userDefinedGenerics.add(next); + // TODO Support multiple bounds + javaGenerics.add(new TargetGeneric(next.getName(), convert(next.getBounds().get(0)))); } } else { this.userDefinedGenerics.put(input, new HashSet<>()); @@ -1043,7 +146,7 @@ public class ASTToTargetAST { return generics.stream().anyMatch(g -> g.name().equals(type.getParsedName())); } - private Set collectMethodGenerics(GenerateGenerics generateGenerics, Set> generics, Method input) { + private Set collectMethodGenerics(GenerateGenerics generateGenerics, Set generics, Method input) { var convertedGenerics = new HashSet<>(convert(generics, generateGenerics)); outer: for (GenericTypeVar typeVar : input.getGenerics()) { diff --git a/src/main/java/de/dhbwstuttgart/target/generate/CycleFinder.java b/src/main/java/de/dhbwstuttgart/target/generate/CycleFinder.java new file mode 100644 index 00000000..175b4221 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/CycleFinder.java @@ -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 allNodes(Set 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 outgoingEdgesOf(GenerateGenerics.TPH tph, Set 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 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> findCycles(Set input) { + Map indices = new HashMap<>(); + List path = new ArrayList<>(); + Set pathSet = new HashSet<>(); + Map> blocked = new HashMap<>(); + Set> 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 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; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java new file mode 100644 index 00000000..5b8e841a --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java @@ -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> computedGenericsOfMethods = new HashMap<>(); + final Map> computedGenericsOfClasses = new HashMap<>(); + + final Map> usedTPHsOfMethods = new HashMap<>(); + final Map> familyOfMethods = new HashMap<>(); + + final Set simplifiedConstraints = new HashSet<>(); + final Map concreteTypes = new HashMap<>(); + final Map 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 findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type) { + var result = new HashSet(); + 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 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 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 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 transitiveClosure(Set generics) { + Set all = new HashSet<>(generics); + Set 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 typeVariables, + Set typeVariablesOfClass, + Set 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 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 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(); + for (var generic : all) { + toAdd.add(new PairLT(generic.left, generic.left)); + } + all.addAll(toAdd); + + HashSet 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(); + 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(); + 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 userDefinedGenericsOfClass, Set typeVariablesOfClass, TPH typeVariable) { + return typeVariablesOfClass.contains(typeVariable) || userDefinedGenericsOfClass.stream().anyMatch(g -> g.getName().equals(typeVariable.resolve().getName())); + } + + private void methodFindTypeVariables( + Method method, + Set 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 result, Set javaTypeVariablesOfClass); + + Set family(ClassOrInterface owner, Method method) { + if (familyOfMethods.containsKey(method)) + return familyOfMethods.get(method); + + var result = new HashSet(); + familyOfMethods.put(method, result); + + var classGenerics = generics(owner); + HashSet typeVariablesOfClass = new HashSet<>(); + + for (var pair : classGenerics) { + typeVariablesOfClass.add(pair.left); + } + + HashSet javaTypeVariables = new HashSet<>(); + + methodFindTypeVariables(method, javaTypeVariables); + methodFindConstraints(owner, method, javaTypeVariables, typeVariablesOfClass, result); + eliminateTransitives(result); + + return result; + } + + Set generics(ClassOrInterface owner, Method method) { + if (computedGenericsOfMethods.containsKey(method)) + return computedGenericsOfMethods.get(method); + + var classGenerics = generics(owner); + + HashSet 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(); + + var usedTphs = new HashSet(); + // 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 result, List 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 result) { + for (var pair : new HashSet<>(result)) + if (pair instanceof PairLT ptph) { + var first = ptph.left; + var chain = new ArrayList(); + chain.add(ptph.left); + chain.add(ptph.right); + eliminateChain(result, chain); + } + } + + void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set 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 result, Set referenced); + + Set generics(ClassOrInterface classOrInterface) { + if (computedGenericsOfClasses.containsKey(classOrInterface)) + return computedGenericsOfClasses.get(classOrInterface); + + Set javaResult = new HashSet<>(); + computedGenericsOfClasses.put(classOrInterface, javaResult); + + for (var field : classOrInterface.getFieldDecl()) { + findAllBounds(field.getType(), javaResult); + } + + var referenced = new HashSet(); + eliminateTransitives(javaResult); + generics(classOrInterface, javaResult, referenced); + + var referencedByClass = new HashSet(); + 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 result, Set classGenerics, Set 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 input, Set referenced) { + for (var pair : new HashSet<>(input)) { + if (pair instanceof PairLT ptph && referenced.contains(ptph.left)) { + var chain = new ArrayList(); + 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 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 input, Set 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 referenced, Set input, Set output, TPH start, TPH end, Set 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 referenced, Set input) { + var output = new HashSet(); + for (var tph : referenced) { + for (var pair : input) { + if (pair instanceof PairLT pthp && pthp.left.equals(tph)) { + var chain = new HashSet(); + 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 input, Set 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 input, Set referenced) { + var foundInfima = false; + do { + foundInfima = false; + for (var constraint : new HashSet<>(input)) { + var left = constraint.left; + Set 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); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java index e06bca66..a30fdba9 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java @@ -5,14 +5,13 @@ import de.dhbwstuttgart.syntaxtree.Method; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.target.tree.type.TargetType; -import de.dhbwstuttgart.typeinference.result.ResultPair; import java.util.*; public class GenericsResult { - private final ASTToTargetAST.GenerateGenerics generics; + private final GenerateGenerics generics; - GenericsResult(ASTToTargetAST.GenerateGenerics generics) { + GenericsResult(GenerateGenerics generics) { this.generics = generics; } @@ -42,16 +41,16 @@ public class GenericsResult { do { bound = Optional.empty(); for (var pair : methodGenerics) { - if (pair.getLeft().equals(type)) { - type = resolve(pair.getRight()); + if (pair.left.resolve().equals(type)) { + type = pair.resolveRight(); bound = Optional.of(new Bound(true, type)); break; } } if (bound.isEmpty()) { for (var pair : classGenerics) { - if (pair.getLeft().equals(type)) { - type = resolve(pair.getRight()); + if (pair.left.resolve().equals(type)) { + type = pair.resolveRight(); bound = Optional.of(new Bound(false, type)); break; } diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java index 8092e44e..19ccaf31 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java @@ -1,23 +1,24 @@ package de.dhbwstuttgart.target.generate; 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 java.util.*; -public class GenericsResultSet extends AbstractSet> { +public class GenericsResultSet extends AbstractSet { - final Set> backing; + final Set backing; final Map equality; - public GenericsResultSet(Set> backing, Map equality) { + public GenericsResultSet(Set backing, Map equality) { this.backing = backing == null ? new HashSet<>() : new HashSet<>(backing); this.equality = equality; } @Override - public Iterator> iterator() { + public Iterator iterator() { return backing.iterator(); } @@ -29,7 +30,14 @@ public class GenericsResultSet extends AbstractSet> { public Optional> getResultPairFor(TypePlaceholder tph) { var tph2 = equality.getOrDefault(tph, tph); return this.stream().filter(pair -> { - return pair.getLeft().equals(tph2); - }).findFirst(); + return pair.left.resolve().equals(tph2); + }).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; + }); } } diff --git a/src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java new file mode 100644 index 00000000..f2f08542 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java @@ -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 result, Set referenced) { + eliminateCycles(result, referenced); + eliminateInfima(result, referenced); + //equalizeTypeVariables(result, referenced); + eliminateInnerTypeVariables(referenced, result); + } + + @Override + void generics(ClassOrInterface classOrInterface, Set result, Set referenced) { + eliminateCycles(result, referenced); + eliminateInfima(result, referenced); + eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); + equalizeTypeVariables(result, referenced); + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/generate/TxGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/TxGenerics.java new file mode 100644 index 00000000..b64babb6 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/TxGenerics.java @@ -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 result, Set referenced) { + eliminateInfima(result, referenced); + eliminateInnerTypeVariables(referenced, result); + } + + @Override + void generics(ClassOrInterface classOrInterface, Set result, Set referenced) { + eliminateInfima(result, referenced); + eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); + } +} diff --git a/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java b/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java index 660fc494..bfe4d566 100644 --- a/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java +++ b/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java @@ -4,6 +4,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import de.dhbwstuttgart.target.generate.GenerateGenerics; import de.dhbwstuttgart.target.generate.GenericsResult; import de.dhbwstuttgart.target.generate.GenericsResultSet; import de.dhbwstuttgart.typeinference.result.*; @@ -89,10 +90,10 @@ public class TypeInsertFactory { String insert = " <"; for (var genericInsertConstraint : constraints) { - if (genericInsertConstraint instanceof PairTPHequalRefTypeOrWildcardType peq) { - insert += peq.left.getName(); - } else if (genericInsertConstraint instanceof PairTPHsmallerTPH psm) { - insert += psm.left.getName() + " extends " + psm.right.getName(); + if (genericInsertConstraint instanceof GenerateGenerics.PairEQ peq) { + insert += peq.left.resolve().getName(); + } else if (genericInsertConstraint instanceof GenerateGenerics.PairLT psm) { + insert += psm.left.resolve().getName() + " extends " + psm.right.resolve().getName(); } insert += ", "; } @@ -115,10 +116,10 @@ public class TypeInsertFactory { String insert = " <"; for (var genericInsertConstraint : classConstraints) { - if (genericInsertConstraint instanceof PairTPHequalRefTypeOrWildcardType peq) { - insert += peq.left.getName(); - } else if (genericInsertConstraint instanceof PairTPHsmallerTPH psm) { - insert += psm.left.getName() + " extends " + psm.right.getName(); + if (genericInsertConstraint instanceof GenerateGenerics.PairEQ peq) { + insert += peq.left.resolve().getName(); + } else if (genericInsertConstraint instanceof GenerateGenerics.PairLT psm) { + insert += psm.left.resolve().getName() + " extends " + psm.right.resolve().getName(); } insert += ", "; }