From fe2b3accb34b530543c5e22aecd18239443c10f7 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Tue, 13 Jun 2023 10:28:20 +0200 Subject: [PATCH 01/11] Fix not looking up the tph in equality --- .../target/generate/ASTToTargetAST.java | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 1b7ceea4..a258e5fb 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -47,8 +47,9 @@ public class ASTToTargetAST { } @Override - void generics(ClassOrInterface owner, Method method, Set> result, HashSet referenced) { + void generics(ClassOrInterface owner, Method method, Set> result, Set referenced) { eliminateInfima(result, referenced); + eliminateInnerTypeVariables(referenced, result); } @Override @@ -64,18 +65,19 @@ public class ASTToTargetAST { } @Override - void generics(ClassOrInterface owner, Method method, Set> result, HashSet referenced) { + 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); + eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); } } @@ -172,7 +174,7 @@ public class ASTToTargetAST { static Set> transitiveClosure(Set> generics) { Set> all = new HashSet<>(generics); - HashSet> toAdd = new HashSet<>(); + Set> toAdd = new HashSet<>(); int sizeBefore; do { sizeBefore = all.size(); @@ -221,7 +223,7 @@ public class ASTToTargetAST { //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 = + Set T1s = methodCall.getArgumentList() .getArguments() .stream() @@ -230,10 +232,10 @@ public class ASTToTargetAST { .stream().filter(x -> x instanceof TypePlaceholder) .map(tph -> equality.getOrDefault(tph, (TypePlaceholder) tph)) .collect(Collectors.toCollection(HashSet::new)); - RefTypeOrTPHOrWildcardOrGeneric T2 = superType; - if (T2 instanceof TypePlaceholder tph) T2 = equality.getOrDefault(tph, tph); + Set T2s = new HashSet<>(); + findTphs(superType, T2s); - System.out.println("T1s: " + T1s + " T2: " + T2); + System.out.println("T1s: " + T1s + " T2s: " + T2s); //Ende superType = methodCall.receiverType; @@ -264,6 +266,7 @@ public class ASTToTargetAST { // 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); @@ -276,13 +279,17 @@ public class ASTToTargetAST { 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; - if (!T1s.contains(R1) || !R2.equals(T2)) 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); @@ -466,7 +473,7 @@ public class ASTToTargetAST { }); } - abstract void generics(ClassOrInterface owner, Method method, Set> result, HashSet javaTypeVariablesOfClass); + abstract void generics(ClassOrInterface owner, Method method, Set> result, Set javaTypeVariablesOfClass); Set> family(ClassOrInterface owner, Method method) { if (familyOfMethods.containsKey(method)) @@ -492,8 +499,8 @@ public class ASTToTargetAST { } Set> generics(ClassOrInterface owner, Method method) { - //if (computedGenericsOfMethods.containsKey(method)) - // return computedGenericsOfMethods.get(method); + if (computedGenericsOfMethods.containsKey(method)) + return computedGenericsOfMethods.get(method); var classGenerics = generics(owner); @@ -520,7 +527,6 @@ public class ASTToTargetAST { generics(owner, method, result, referenced); usedTPHsOfMethods.put(method, usedTphs); - eliminateInnerTypeVariables(referenced, result); addMissingObjectBounds(result, classGenerics, usedTphs); System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result); From bb0ee7d517dc4748c46a67eaaabb41124d626582 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Tue, 13 Jun 2023 16:33:54 +0200 Subject: [PATCH 02/11] 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 += ", "; } From 9eb0dd1cf541bb4d2b70b3dd9fbb396cb0e86c3b Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 15 Jun 2023 14:13:15 +0200 Subject: [PATCH 03/11] Iteration --- .../target/generate/GenerateGenerics.java | 76 +++++++++++++------ .../target/generate/JavaGenerics.java | 2 +- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java index 5b8e841a..8aad3813 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java @@ -23,7 +23,7 @@ public abstract class GenerateGenerics { private final ASTToTargetAST astToTargetAST; public class TPH { - final TypePlaceholder wrap; + private final TypePlaceholder wrap; TPH(TypePlaceholder wrap) { this.wrap = wrap; @@ -212,6 +212,7 @@ public abstract class GenerateGenerics { equality.put(entry.getKey(), to); } } + to.setVariance(from.getVariance()); equality.put(from, to); referenced.remove(new TPH(from)); referenced.add(new TPH(to)); @@ -292,8 +293,10 @@ public abstract class GenerateGenerics { 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); + var method2 = optMethod.get(); + System.out.println("In: " + method.getName() + " Method: " + method2.getName()); + System.out.println(simplifiedConstraints); + var generics = family(owner, method2); // transitive and var all = transitiveClosure(generics); @@ -325,6 +328,7 @@ public abstract class GenerateGenerics { if (!T1s.contains(R1) || !T2s.contains(R2)) continue; var newPair = new PairLT(R1, R2); + System.out.println("New pair: " + newPair); newPairs.add(newPair); if (!containsRelation(result, newPair)) @@ -501,10 +505,10 @@ public abstract class GenerateGenerics { abstract void generics(ClassOrInterface owner, Method method, Set result, Set javaTypeVariablesOfClass); Set family(ClassOrInterface owner, Method method) { + Set result = new HashSet<>(); if (familyOfMethods.containsKey(method)) return familyOfMethods.get(method); - var result = new HashSet(); familyOfMethods.put(method, result); var classGenerics = generics(owner); @@ -524,19 +528,22 @@ public abstract class GenerateGenerics { } Set generics(ClassOrInterface owner, Method method) { - if (computedGenericsOfMethods.containsKey(method)) - return computedGenericsOfMethods.get(method); + if (computedGenericsOfMethods.containsKey(method)) { + var cached = computedGenericsOfMethods.get(method); + System.out.println("Cached " + method.getName() + ": " + cached); + return cached; + } + + var result = new HashSet(); + computedGenericsOfMethods.put(method, result); 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); + result.addAll(family(owner, method)); var referenced = new HashSet(); @@ -552,7 +559,7 @@ public abstract class GenerateGenerics { generics(owner, method, result, referenced); usedTPHsOfMethods.put(method, usedTphs); - addMissingObjectBounds(result, classGenerics, usedTphs); + normalize(result, classGenerics, usedTphs); System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result); return result; @@ -631,16 +638,18 @@ public abstract class GenerateGenerics { findTphs(field.getType(), referencedByClass); } - addMissingObjectBounds(javaResult, null, referencedByClass); + normalize(javaResult, null, referencedByClass); System.out.println(this.getClass().getSimpleName() + " Class " + classOrInterface.getClassName().getClassName() + ": " + javaResult); return javaResult; } - void addMissingObjectBounds(Set result, Set classGenerics, Set usedTphs) { + void normalize(Set result, Set classGenerics, Set usedTphs) { outer: for (var tph : usedTphs) { for (var p1 : new HashSet<>(result)) { + if (p1 instanceof PairLT ptph && ptph.left.equals(ptph.right)) + result.remove(p1); // TODO This is a bit strange if (p1.left.equals(tph)) continue outer; } @@ -683,12 +692,13 @@ public abstract class GenerateGenerics { } 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); + addToEquality(cur.resolve(), start.resolve(), referenced); TPH finalPrev = prev; input.removeIf(p -> p.equals(new PairLT(finalPrev, cur))); for (var pair2 : new HashSet<>(input)) { @@ -722,13 +732,32 @@ public abstract class GenerateGenerics { for (var field : classOrInterface.getFieldDecl()) { findTphs(field.getType(), referenced); } + doIterationForMethods(classOrInterface); for (var method : classOrInterface.getMethods()) { - generics(classOrInterface, method); referenced.addAll(usedTPHsOfMethods.get(method)); } eliminateInnerTypeVariables(referenced, input); } + void doIterationForMethods(ClassOrInterface classOrInterface) { + familyOfMethods.clear(); + + var oldFamily = new HashMap>(); + do { + oldFamily.clear(); + oldFamily.putAll(familyOfMethods); + familyOfMethods.clear(); + for (var method : classOrInterface.getMethods()) { + family(classOrInterface, method); + } + System.out.println(familyOfMethods); + } while(!oldFamily.equals(familyOfMethods)); + + for (var method : classOrInterface.getMethods()) { + generics(classOrInterface, method); + } + } + 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); @@ -793,7 +822,7 @@ public abstract class GenerateGenerics { var right = cycle.get(i + 1); var pair = new PairLT(left, right); input.remove(pair); - addToEquality(left.wrap, newTph, referenced); + addToEquality(left.resolve(), newTph, referenced); } } } @@ -806,9 +835,10 @@ public abstract class GenerateGenerics { 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)) + if (pair instanceof PairLT stph) { + if (pair.left.equals(constraint.left)) infima.add(stph); + } } if (infima.size() > 1) { foundInfima = true; @@ -826,19 +856,19 @@ public abstract class GenerateGenerics { addToPairs(input, new PairLT(left, new TPH(newTph))); input.removeAll(infima); for (var infimum : infima) { - addToEquality(infimum.right.wrap, newTph, referenced); + addToEquality(infimum.right.resolve(), newTph, referenced); new HashSet<>(input).forEach(pair -> { - if (pair.left.wrap.equals(infimum.right.wrap)) { + if (pair.left.equals(infimum.right)) { input.remove(pair); if (pair instanceof PairLT stph) { - if (!newTph.equals(stph.right.wrap)) + if (!newTph.equals(stph.right.resolve())) 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)) { + } else if (pair instanceof PairLT stph && stph.right.equals(infimum.right)) { input.remove(pair); - if (!newTph.equals(stph.left.wrap)) + if (!newTph.equals(stph.left.resolve())) addToPairs(input, new PairLT(stph.left, new TPH(newTph))); } }); diff --git a/src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java index f2f08542..16be617e 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/JavaGenerics.java @@ -15,7 +15,7 @@ final class JavaGenerics extends GenerateGenerics { void generics(ClassOrInterface owner, Method method, Set result, Set referenced) { eliminateCycles(result, referenced); eliminateInfima(result, referenced); - //equalizeTypeVariables(result, referenced); + equalizeTypeVariables(result, referenced); eliminateInnerTypeVariables(referenced, result); } From 4a18a81b3378f290be3e00979028bbeb85a264e8 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 15 Jun 2023 16:31:50 +0200 Subject: [PATCH 04/11] Alter tests a bit --- .../insertGenericsJav/TestContraVariant.jav | 25 +++++++++++++++++-- .../target/generate/GenerateGenerics.java | 2 -- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/resources/insertGenericsJav/TestContraVariant.jav b/resources/insertGenericsJav/TestContraVariant.jav index 2b0e245f..9c060f0d 100644 --- a/resources/insertGenericsJav/TestContraVariant.jav +++ b/resources/insertGenericsJav/TestContraVariant.jav @@ -4,7 +4,7 @@ public class TestContraVariant { var y3 = y2.snd(); var z; y3 = z; - return new Pair(x,z); + return new Pair<>(x, z); } m(x, y) { @@ -12,6 +12,27 @@ public class TestContraVariant { var x3 = x2.fst(); var z; x3 = z; - return new Pair(z, y); + return new Pair<>(z, y); + } +} + +class Pair { + public T x; + public U y; + + public Pair() { + } + + public Pair(T var1, U var2) { + x = var1; + y = var2; + } + + public T fst() { + return x; + } + + public U snd() { + return y; } } \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java index 8aad3813..a21ed10a 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java @@ -295,7 +295,6 @@ public abstract class GenerateGenerics { if (optMethod.isEmpty()) return; var method2 = optMethod.get(); System.out.println("In: " + method.getName() + " Method: " + method2.getName()); - System.out.println(simplifiedConstraints); var generics = family(owner, method2); // transitive and @@ -750,7 +749,6 @@ public abstract class GenerateGenerics { for (var method : classOrInterface.getMethods()) { family(classOrInterface, method); } - System.out.println(familyOfMethods); } while(!oldFamily.equals(familyOfMethods)); for (var method : classOrInterface.getMethods()) { From 711aa70d48f6efe2e357614223716ab2c28fe35c Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 22 Jun 2023 09:34:15 +0200 Subject: [PATCH 05/11] Update to java 20 and some refactoring --- pom.xml | 8 ++++---- resources/bytecode/javFiles/Cycle.class | Bin 477 -> 0 bytes .../javFiles}/TestAny.jav | 0 .../javFiles}/TestAssign.jav | 0 .../javFiles}/TestClassField.jav | 0 .../javFiles}/TestContraVariant.jav | 0 .../javFiles}/TestGGFinder.jav | 0 .../javFiles}/TestLocalVarLambda.jav | 0 .../javFiles}/TestMutualRecursion.jav | 0 .../javFiles}/TestMutualRecursionWithField.jav | 0 .../javFiles}/TestMutualRecursionWithField2.jav | 0 .../javFiles}/TestMutualRecursionWithField3.jav | 0 .../javFiles}/TestReturnVar.jav | 0 .../TestSecondLineOfClassConstraints.jav | 0 .../javFiles}/TestTPHsAndGenerics.jav | 0 .../javFiles}/TestTPHsAndGenerics2.jav | 1 + .../javFiles}/TestThreeArgs.jav | 0 .../javFiles}/TestTwoArgs.jav | 0 .../javFiles}/TestTwoArgs2.jav | 0 .../javFiles}/TestTwoCalls.jav | 0 .../javFiles}/TestVector.jav | 4 +++- .../javFiles}/TestVectorArg.jav | 0 .../javFiles}/TestVoidMeth.jav | 0 resources/javFiles/Lambda.jav | 1 - resources/javFiles/OL.jav | 1 - .../target/generate/GenerateGenerics.java | 1 + 26 files changed, 9 insertions(+), 7 deletions(-) delete mode 100644 resources/bytecode/javFiles/Cycle.class rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestAny.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestAssign.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestClassField.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestContraVariant.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestGGFinder.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestLocalVarLambda.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestMutualRecursion.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestMutualRecursionWithField.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestMutualRecursionWithField2.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestMutualRecursionWithField3.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestReturnVar.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestSecondLineOfClassConstraints.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestTPHsAndGenerics.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestTPHsAndGenerics2.jav (98%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestThreeArgs.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestTwoArgs.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestTwoArgs2.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestTwoCalls.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestVector.jav (57%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestVectorArg.jav (100%) rename resources/{insertGenericsJav => insertGenerics/javFiles}/TestVoidMeth.jav (100%) diff --git a/pom.xml b/pom.xml index add91d5c..ade9b85d 100644 --- a/pom.xml +++ b/pom.xml @@ -54,8 +54,8 @@ http://maven.apache.org/maven-v4_0_0.xsd"> 3.8.0 --enable-preview - 19 - 19 + 20 + 20 @@ -112,8 +112,8 @@ http://maven.apache.org/maven-v4_0_0.xsd"> - 19 - 19 + 20 + 20 de.dhbwstuttgart.core.ConsoleInterface diff --git a/resources/bytecode/javFiles/Cycle.class b/resources/bytecode/javFiles/Cycle.class deleted file mode 100644 index 6bac05ecc5903680e9b75544ec6fe9d8dfabd7bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 477 zcmZut%TB^j5IsXFeF*aKwRUBzCTv{M27~NOtBW>8mY3Qfh{ceq@v~T&xX`^HrOt&$ z6YEXxJkFUpbLW11zPk?p=@y0Rw``$J}k0v`}6CBU<8wKDzb+K$MthwFTDCebZCTHkcs%#7x$rT<;T0di-Fe(+4_z&tI%`g zXeM~HB e.resolve().getVariance()).toList()); var variance = chain.get(0).resolve().getVariance(); if (variance != 1) continue; var index = 0; From f32b1ee5b2e6f026ac06b2d9285ae0c4cd83d111 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 22 Jun 2023 14:32:41 +0200 Subject: [PATCH 06/11] Fix auxiliary files not containing parent interface --- .../java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 6508b0a5..5db79459 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -297,7 +297,7 @@ public class ASTToTargetAST { } catch (ClassNotFoundException e) { classLoader.loadClass(code); } - auxiliaries.put(className, code); + auxiliaries.put(superClassName, code); } FunNGenerator.GenericParameters gep = null; if (!usedFunN.containsKey(className)) { From 97a1bcbbe1d75cd1c629d0476b0688718bdb95dc Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 22 Jun 2023 17:13:01 +0200 Subject: [PATCH 07/11] Fix T1 & T2 refering to method signature instead of actual types --- .../target/generate/GenerateGenerics.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java index 4f264e68..53143235 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenerateGenerics.java @@ -285,7 +285,7 @@ public abstract class GenerateGenerics { superType = methodCall.receiverType; methodCall.receiver.accept(this); for (int i = 0; i < methodCall.arglist.getArguments().size(); i++) { - superType = methodCall.signature.get(i); + superType = methodCall.arglist.getArguments().get(i).getType(); methodCall.arglist.getArguments().get(i).accept(this); } @@ -345,7 +345,7 @@ public abstract class GenerateGenerics { @Override public void visit(LambdaExpression lambdaExpression) { - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + superType = new Void(new NullToken()); lambdaExpression.methodBody.accept(this); } @@ -357,9 +357,9 @@ public abstract class GenerateGenerics { @Override public void visit(BinaryExpr binary) { - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + superType = new Void(new NullToken()); binary.lexpr.accept(this); - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + superType = new Void(new NullToken()); binary.rexpr.accept(this); } @@ -373,11 +373,11 @@ public abstract class GenerateGenerics { @Override public void visit(IfStmt ifStmt) { - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + superType = new Void(new NullToken()); ifStmt.expr.accept(this); - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + superType = new Void(new NullToken()); ifStmt.then_block.accept(this); - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + superType = new Void(new NullToken()); ifStmt.else_block.accept(this); } @@ -389,7 +389,7 @@ public abstract class GenerateGenerics { @Override public void visit(WhileStmt whileStmt) { - superType = new de.dhbwstuttgart.syntaxtree.type.Void(new NullToken()); + superType = new Void(new NullToken()); whileStmt.expr.accept(this); superType = new Void(new NullToken()); whileStmt.loopBlock.accept(this); From 4bcb91ce2dbca71e34d77229a59e456162a4d984 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Thu, 22 Jun 2023 17:13:15 +0200 Subject: [PATCH 08/11] Refactor bounds to make the invocation smaller --- .../insertGenerics/javFiles/TestVector.jav | 4 +- .../dhbwstuttgart/target/generate/Bound.java | 14 ++++ src/test/java/targetast/TestGenerics.java | 77 ++++++++++--------- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/resources/insertGenerics/javFiles/TestVector.jav b/resources/insertGenerics/javFiles/TestVector.jav index 6c8f97f0..76c06ff9 100644 --- a/resources/insertGenerics/javFiles/TestVector.jav +++ b/resources/insertGenerics/javFiles/TestVector.jav @@ -3,9 +3,7 @@ import java.util.Vector; public class TestVector { m(v, w) { - var a = v.elementAt(0); - var b = id(a); - w.addElement(b); + w.addElement(id(v.elementAt(0))); } id(x) { diff --git a/src/main/java/de/dhbwstuttgart/target/generate/Bound.java b/src/main/java/de/dhbwstuttgart/target/generate/Bound.java index 1b39df6c..3e749092 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/Bound.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/Bound.java @@ -6,5 +6,19 @@ import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import java.util.List; public record Bound(boolean isOnMethod, RefTypeOrTPHOrWildcardOrGeneric bound) { + public static Bound onMethod(String tph) { + return new Bound(true, TypePlaceholder.of(tph)); + } + public static Bound onMethod(RefTypeOrTPHOrWildcardOrGeneric bound) { + return new Bound(true, bound); + } + + public static Bound onClass(String tph) { + return new Bound(false, TypePlaceholder.of(tph)); + } + + public static Bound onClass(RefTypeOrTPHOrWildcardOrGeneric bound) { + return new Bound(false, bound); + } } diff --git a/src/test/java/targetast/TestGenerics.java b/src/test/java/targetast/TestGenerics.java index 3d4ad553..5f74ae3a 100644 --- a/src/test/java/targetast/TestGenerics.java +++ b/src/test/java/targetast/TestGenerics.java @@ -7,10 +7,11 @@ import de.dhbwstuttgart.syntaxtree.Method; import de.dhbwstuttgart.syntaxtree.type.RefType; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.target.generate.ASTToTargetAST; +import static de.dhbwstuttgart.target.generate.ASTToTargetAST.OBJECT; import de.dhbwstuttgart.target.generate.Bound; +import static de.dhbwstuttgart.target.generate.Bound.*; import de.dhbwstuttgart.target.generate.BoundsList; import de.dhbwstuttgart.target.generate.GenericsResult; -import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; @@ -23,7 +24,7 @@ import java.util.List; public class TestGenerics { - private static final String rootDirectory = System.getProperty("user.dir") + "/resources/insertGenericsJav/"; + private static final String rootDirectory = System.getProperty("user.dir") + "/resources/insertGenerics/javFiles/"; private static final String bytecodeDirectory = System.getProperty("user.dir") + "/src/test/resources/testBytecode/generatedBC/"; private record Result(List genericsResults, ClassOrInterface clazz) { @@ -60,12 +61,12 @@ public class TestGenerics { var ECK1 = generics.getBounds(otherMethod.getParameterList().getParameterAt(0).getType(), result.clazz, anyMethod); var ECK2 = generics.getBounds(otherMethod.getReturnType(), result.clazz, anyMethod); - var ECKChain = new BoundsList(new Bound(false, ASTToTargetAST.OBJECT)); + var ECKChain = new BoundsList(onClass(OBJECT)); assertEquals(ECK1, ECK2); assertEquals(ECK2, generics.getBounds(b.getType(), result.clazz)); var M = generics.getBounds(a.getType(), result.clazz); - var MChain = new BoundsList(new Bound(false, TypePlaceholder.of("ECK")), new Bound(false, ASTToTargetAST.OBJECT)); + var MChain = new BoundsList(onClass("ECK"), onClass(OBJECT)); assertEquals(M, MChain); } @@ -79,7 +80,7 @@ public class TestGenerics { assertEquals(0, generics.get(fReturn).size()); var N = generics.getBounds(fReturn.getReturnType(), result.clazz); - var NChain = new BoundsList(new Bound(false, ASTToTargetAST.OBJECT)); + var NChain = new BoundsList(onClass(OBJECT)); assertEquals(N, NChain); } @@ -97,7 +98,7 @@ public class TestGenerics { var N = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m); var N2 = generics.getBounds(m.getReturnType(), result.clazz, m); - var NChain = new BoundsList(new Bound(true, ASTToTargetAST.OBJECT)); + var NChain = new BoundsList(onMethod(OBJECT)); assertEquals(N, N2); assertEquals(N2, NChain); @@ -123,13 +124,13 @@ public class TestGenerics { assertEquals(2, generics.get(m).size()); var R = generics.getBounds(a.getType(), result.clazz); - var RChain = new BoundsList(new Bound(false, ASTToTargetAST.OBJECT)); + var RChain = new BoundsList(onClass(OBJECT)); assertEquals(R, RChain); var O = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id); var AB = generics.getBounds(id.getReturnType(), result.clazz, id); assertEquals(O, AB); - assertEquals(AB, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(AB, new BoundsList(onMethod(OBJECT))); var S = generics.getBounds(setA.getParameterList().getParameterAt(0).getType(), result.clazz, setA); assertEquals(S, RChain); @@ -137,7 +138,7 @@ public class TestGenerics { var X = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m); var Y = generics.getBounds(m.getParameterList().getParameterAt(1).getType(), result.clazz, m); - var XChain = new BoundsList(new Bound(true, ASTToTargetAST.OBJECT)); + var XChain = new BoundsList(onMethod(OBJECT)); assertEquals(X, XChain); assertEquals(Y, X); } @@ -163,25 +164,25 @@ public class TestGenerics { assertEquals(3, generics.get(main).size()); var N = generics.getBounds(a.getType(), result.clazz); - assertEquals(N, new BoundsList(new Bound(false, ASTToTargetAST.OBJECT))); + assertEquals(N, new BoundsList(onClass(OBJECT))); var P = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id); var O = generics.getBounds(id.getReturnType(), result.clazz, id); assertEquals(P, O); - assertEquals(O, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(O, new BoundsList(onMethod(OBJECT))); assertEquals(generics.resolve(m.getParameterList().getParameterAt(0).getType()), generics.resolve(m.getReturnType())); var Y = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m); var AA = generics.getBounds(m.getParameterList().getParameterAt(1).getType(), result.clazz, m); assertEquals(Y, AA); - assertEquals(AA, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(AA, new BoundsList(onMethod(OBJECT))); var AI = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m); var AJ = generics.getBounds(m.getParameterList().getParameterAt(1).getType(), result.clazz, m); var AH = generics.getBounds(m.getReturnType(), result.clazz, m); assertEquals(AI, AJ); assertEquals(AJ, AH); - assertEquals(AH, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(AH, new BoundsList(onMethod(OBJECT))); } @Test @@ -193,7 +194,7 @@ public class TestGenerics { assertEquals(1, generics.get(anyMethod).size()); var M = generics.getBounds(anyMethod.getReturnType(), result.clazz, anyMethod); - assertEquals(M, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(M, new BoundsList(onMethod(OBJECT))); } @Test @@ -207,12 +208,12 @@ public class TestGenerics { var generics = result.genericsResults.get(0); var M = generics.getBounds(a.getType(), result.clazz); var DYX = generics.getBounds(b.getType(), result.clazz); - var MChain = new BoundsList(new Bound(false, TypePlaceholder.of("DYX")), new Bound(false, ASTToTargetAST.OBJECT)); - var DYXChain = new BoundsList(new Bound(false, ASTToTargetAST.OBJECT)); + var MChain = new BoundsList(onClass("DYX"), onClass(OBJECT)); + var DYXChain = new BoundsList(onClass(OBJECT)); assertEquals(M, MChain); var Q = generics.getBounds(anyMethod.getReturnType(), result.clazz, anyMethod); - assertEquals(Q, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(Q, new BoundsList(onMethod(OBJECT))); System.out.println(otherMethod.getReturnType()); var DYX2 = generics.getBounds(otherMethod.getReturnType(), result.clazz, otherMethod); @@ -235,17 +236,17 @@ public class TestGenerics { var generics = result.genericsResults.get(0); var U = generics.getBounds(id2.getParameterList().getParameterAt(0).getType(), result.clazz, id2); var FPT = generics.getBounds(id2.getReturnType(), result.clazz, id2); - assertEquals(U, new BoundsList(new Bound(true, TypePlaceholder.of("FPT")), new Bound(false, ASTToTargetAST.OBJECT))); - assertEquals(FPT, new BoundsList(new Bound(false, ASTToTargetAST.OBJECT))); + assertEquals(U, new BoundsList(onMethod("FPT"), onClass(OBJECT))); + assertEquals(FPT, new BoundsList(onClass(OBJECT))); var AA = generics.getBounds(m.getReturnType(), result.clazz, m); var AC = generics.getBounds(m.getParameterList().getParameterAt(1).getType(), result.clazz, m); - assertEquals(AA, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); - assertEquals(AC, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(AA, new BoundsList(onMethod(OBJECT))); + assertEquals(AC, new BoundsList(onMethod(OBJECT))); var AH = generics.getBounds(m2.getReturnType(), result.clazz, m2); var AL = generics.getBounds(m2.getParameterList().getParameterAt(0).getType(), result.clazz, m2); - assertEquals(AH, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(AH, new BoundsList(onMethod(OBJECT))); assertEquals(AH, AL); } @@ -262,32 +263,32 @@ public class TestGenerics { var generics = result.genericsResults.get(0); var AO = generics.getBounds(a.getType(), result.clazz); var AOBound = new BoundsList( - new Bound(false, TypePlaceholder.of("Y")), - new Bound(false, TypePlaceholder.of("AK")), - new Bound(false, TypePlaceholder.of("AE")), - new Bound(false, ASTToTargetAST.OBJECT) + onClass("Y"), + onClass("AK"), + onClass("AE"), + onClass(OBJECT) ); assertEquals(AO, AOBound); var S = generics.getBounds(setA.getParameterList().getParameterAt(0).getType(), result.clazz, setA); var c = new ArrayList(); - c.add(new Bound(true, TypePlaceholder.of("AO"))); + c.add(onMethod("AO")); c.addAll(AOBound); var SChain = new BoundsList(c); assertEquals(S, SChain); var Y = generics.getBounds(m.getParameterList().getParameterAt(1).getType(), result.clazz, m); - var YChain = new BoundsList(new Bound(true, TypePlaceholder.of("AE")), new Bound(false, ASTToTargetAST.OBJECT)); + var YChain = new BoundsList(onMethod("AE"), onClass(OBJECT)); var AE = generics.getBounds(m.getReturnType(), result.clazz, m); assertEquals(Y, YChain); - assertEquals(AE, new BoundsList(new Bound(false, ASTToTargetAST.OBJECT))); + assertEquals(AE, new BoundsList(onClass(OBJECT))); // TODO main seems to change between runs /*var AE2 = generics.getBounds(main.getReturnType(), result.clazz, main); var AF = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main); var AG = generics.getBounds(main.getParameterList().getParameterAt(1).getType(), result.clazz, main); assertEquals(AE, AE2)); - assertEquals(AF, new BoundsList(new Bound(true, TypePlaceholder.of("AK")), new Bound(true, TypePlaceholder.of("AE")), new Bound(false, ASTToTargetAST.OBJECT)))); + assertEquals(AF, new BoundsList(onMethod("AK"), onMethod("AE"), onClass(OBJECT)))); assertEquals(AG, SChain));*/ } @@ -307,15 +308,15 @@ public class TestGenerics { var O = generics.getBounds(id.getReturnType(), result.clazz, id); var O2 = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id); assertEquals(O, O2); - assertEquals(O2, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(O2, new BoundsList(onMethod(OBJECT))); // TODO Maybe test in other ways if the parameter generics equals the return generics var S = generics.getBounds(main.getReturnType(), result.clazz, main); var S2 = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main); var T = generics.getBounds(main.getParameterList().getParameterAt(1).getType(), result.clazz, main); assertEquals(S, S2); - assertEquals(S2, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); - assertEquals(T, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(S2, new BoundsList(onMethod(OBJECT))); + assertEquals(T, new BoundsList(onMethod(OBJECT))); } @Test @@ -330,11 +331,11 @@ public class TestGenerics { var S = generics.getBounds(((RefType) par1).getParaList().get(0), result.clazz, m); var ACM = generics.getBounds(((RefType) par2).getParaList().get(0), result.clazz, m); - assertEquals(S, new BoundsList(new Bound(true, TypePlaceholder.of("V")), new Bound(true, ASTToTargetAST.OBJECT))); - assertEquals(ACM, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(S, new BoundsList(onMethod("V"), onMethod(OBJECT))); + assertEquals(ACM, new BoundsList(onMethod(OBJECT))); var Y = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id); - assertEquals(Y, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(Y, new BoundsList(onMethod(OBJECT))); assertEquals(Y, generics.getBounds(id.getReturnType(), result.clazz, id)); } @@ -348,8 +349,8 @@ public class TestGenerics { var par1 = generics.resolve(add.getParameterList().getParameterAt(0).getType()); var ACK = generics.getBounds(((RefType) par1).getParaList().get(0), result.clazz, add); var O = generics.getBounds(add.getParameterList().getParameterAt(1).getType(), result.clazz, add); - assertEquals(ACK, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT))); - assertEquals(O, new BoundsList(new Bound(true, TypePlaceholder.of("ACK")), new Bound(true, ASTToTargetAST.OBJECT))); + assertEquals(ACK, new BoundsList(onMethod(OBJECT))); + assertEquals(O, new BoundsList(onMethod("ACK"), onMethod(OBJECT))); var par2 = generics.resolve(main.getParameterList().getParameterAt(0).getType()); var ACK2 = generics.getBounds(((RefType) par2).getParaList().get(0), result.clazz, add); From f5aa90bdbdfc0da51c7d27176125bb2b1276556e Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Fri, 23 Jun 2023 09:40:23 +0200 Subject: [PATCH 09/11] Add Cycle class (again) --- resources/bytecode/javFiles/Cycle.class | Bin 0 -> 454 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/bytecode/javFiles/Cycle.class diff --git a/resources/bytecode/javFiles/Cycle.class b/resources/bytecode/javFiles/Cycle.class new file mode 100644 index 0000000000000000000000000000000000000000..a7650d8b63e000e1e4cc8b1f35d7dfa6f14ac9dc GIT binary patch literal 454 zcmZWkO;5r=5Pj1E{jkV~UuRFYYQn`6ZMf8g$VWV|DR5knAc)0~s_|!WGV!2yf0Q~4 zjV5-JeY5l4%-fmIueWyq7dRs1!h?*Zw_(0T0YB>nz~5H zh0B`>!RpPXu}mIU{4TY44>9;FOlQBu6z)xm8T4vmxFd)Ft&UJn`hfsUn@#s z+xH?9`zCZ8L*otA(ZKt|V6Mbjg)`!qKYfL@RyxXjH7=-ZVwGhZJ88Mgiq5=!HthD# ghn@cVgEL-_(E)wKQmKL6)Wu0%4!CdYtEbH254V Date: Mon, 26 Jun 2023 13:14:53 +0200 Subject: [PATCH 10/11] Insert missing case --- src/main/java/de/dhbwstuttgart/bytecode/Codegen.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java index 29699d83..16837e50 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -782,6 +782,18 @@ public class Codegen { break; } break; + case TargetVarDecl varDecl: { + var local = state.createVariable(varDecl.name(), varDecl.varType()); + if (varDecl.value() != null) { + generate(state, varDecl.value()); + boxPrimitive(state, varDecl.varType()); + mv.visitVarInsn(ASTORE, local.index()); + } else { + mv.visitInsn(ACONST_NULL); + mv.visitVarInsn(ASTORE, local.index()); + } + break; + } case TargetBinaryOp op: generateBinaryOp(state, op); break; From bb01e78123f2d61287c5d72add78e897585a9f54 Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Tue, 27 Jun 2023 09:51:07 +0200 Subject: [PATCH 11/11] Undo that change --- .../target/generate/StatementToTargetExpression.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java index 62e68843..a5c4d644 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/StatementToTargetExpression.java @@ -206,7 +206,7 @@ public class StatementToTargetExpression implements StatementVisitor { var returnType = isFunNType ? TargetType.Object : converter.convert(methodCall.signature.get(methodCall.signature.size() - 1)); var receiverName = new JavaClassName(converter.convert(methodCall.receiver.getType()).name()); var argList = methodCall.signature.stream().map(converter::convert).toList(); - // argList = argList.subList(0, argList.size() - 1); + argList = argList.subList(0, argList.size() - 1); Method foundMethod = null; if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) {