From f3e7fc0dc3278285e1c45d1a690d47aa2cd31858 Mon Sep 17 00:00:00 2001 From: Vic Nightfall Date: Wed, 4 Jan 2023 15:46:25 +0100 Subject: [PATCH] Test generics --- .../de/dhbwstuttgart/core/JavaTXCompiler.java | 5 +- .../syntaxtree/type/TypePlaceholder.java | 5 + .../target/generate/ASTToTargetAST.java | 138 ++----- .../dhbwstuttgart/target/generate/Bound.java | 24 ++ .../target/generate/GenericsResult.java | 68 ++++ src/test/java/targetast/TestGenerics.java | 344 +++++++++++++++++- 6 files changed, 461 insertions(+), 123 deletions(-) create mode 100644 src/main/java/de/dhbwstuttgart/target/generate/Bound.java create mode 100644 src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java diff --git a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java index 2a554e1c6..09a9f6d43 100644 --- a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java +++ b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java @@ -31,6 +31,7 @@ import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.syntaxtree.type.TypeVisitor; import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter; import de.dhbwstuttgart.target.generate.ASTToTargetAST; +import de.dhbwstuttgart.target.generate.GenericsResult; import de.dhbwstuttgart.typeinference.constraints.Constraint; import de.dhbwstuttgart.typeinference.constraints.ConstraintSet; import de.dhbwstuttgart.typeinference.constraints.Pair; @@ -852,10 +853,10 @@ public class JavaTXCompiler { generateBytecode(path, typeinferenceResult); } - private Map> generatedGenerics = new HashMap<>(); + private Map> generatedGenerics = new HashMap<>(); // TODO This is a temporary solution, we should integrate with the old API for getting Generics - public Map> getGeneratedGenerics() { + public Map> getGeneratedGenerics() { return generatedGenerics; } diff --git a/src/main/java/de/dhbwstuttgart/syntaxtree/type/TypePlaceholder.java b/src/main/java/de/dhbwstuttgart/syntaxtree/type/TypePlaceholder.java index 38932247a..2aaa80681 100644 --- a/src/main/java/de/dhbwstuttgart/syntaxtree/type/TypePlaceholder.java +++ b/src/main/java/de/dhbwstuttgart/syntaxtree/type/TypePlaceholder.java @@ -1,6 +1,7 @@ package de.dhbwstuttgart.syntaxtree.type; import java.util.Hashtable; +import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.syntaxtree.ASTVisitor; import de.dhbwstuttgart.syntaxtree.SyntaxTreeNode; import de.dhbwstuttgart.syntaxtree.factory.NameGenerator; @@ -47,6 +48,10 @@ public class TypePlaceholder extends RefTypeOrTPHOrWildcardOrGeneric return new TypePlaceholder(NameGenerator.makeNewName(), position); } + public static RefTypeOrTPHOrWildcardOrGeneric of(String name) { + return new TypePlaceholder(name, new NullToken()); + } + /** * Author: J�rg B�uerle
diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 551fb020f..b2e9048fc 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -20,122 +20,17 @@ import java.util.stream.Stream; public class ASTToTargetAST { - static RefType OBJECT = ASTFactory.createObjectType(); // TODO It would be better if I could call this directly but the hashcode seems to change + public static RefType OBJECT = ASTFactory.createObjectType(); // TODO It would be better if I could call this directly but the hashcode seems to change protected List all; protected Sigma sigma; protected ClassOrInterface currentClass; // TODO This is only needed because of SuperCall, maybe there's - private record Bound(boolean isOnMethod, RefTypeOrTPHOrWildcardOrGeneric bound) {} - - private static boolean boundChainEquals(List left, List right) { - if (left.size() != right.size()) return false; - for (var i = 0; i < left.size(); i++) { - var l = left.get(i); - var r = right.get(i); - if (l.isOnMethod != r.isOnMethod) return false; - if (i == left.size() - 1) { - if (!(l.bound instanceof TypePlaceholder)) { - if (!(l.bound.equals(r.bound))) return false; - } - } - } - return true; - } - - public static class GenericsResult { - - final Map>> computedGenericsOfClasses; - final Map>> computedGenericsOfMethods; - - final Sigma sigma; - - public GenericsResult( - Map>> computedGenericsOfClasses, - Map>> computedGenericsOfMethods) { - this.computedGenericsOfMethods = computedGenericsOfMethods; - this.computedGenericsOfClasses = computedGenericsOfClasses; - this.sigma = null; - } - - private GenericsResult(Sigma sigma) { - this.computedGenericsOfMethods = sigma.computedGenericsOfMethods; - this.computedGenericsOfClasses = sigma.computedGenericsOfClasses; - this.sigma = sigma; - } - - public Set> get(ClassOrInterface clazz) { - return computedGenericsOfClasses.get(clazz); - } - - public Set> get(Method method) { - return computedGenericsOfMethods.get(method); - } - - private TypePlaceholder equality(TypePlaceholder tph) { - if (this.sigma == null) return tph; - else return this.sigma.equality.getOrDefault(tph, tph); - } - - private List boundChain(ClassOrInterface clazz, Method method, TypePlaceholder tph) { - var result = new ArrayList(); - - RefTypeOrTPHOrWildcardOrGeneric bound = equality(tph); - var constraintsForMethod = get(method); - var constraintsForClass = get(clazz); - Optional> newBound; - do { - RefTypeOrTPHOrWildcardOrGeneric finalBound1 = bound; - newBound = constraintsForMethod.stream().filter(pair -> pair.getLeft().equals(finalBound1)).findFirst(); - if (newBound.isPresent()) { - bound = newBound.get().getRight(); - result.add(new Bound(true, bound)); - } else { - RefTypeOrTPHOrWildcardOrGeneric finalBound = bound; - newBound = constraintsForClass.stream().filter(pair -> pair.getLeft().equals(finalBound)).findFirst(); - if (newBound.isPresent()) { - bound = newBound.get().getRight(); - result.add(new Bound(false, bound)); - } - } - } while (newBound.isPresent()); - - return result; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof GenericsResult other)) return false; - if (this.computedGenericsOfClasses.size() != other.computedGenericsOfClasses.size()) - return false; - - for (var clazz : this.computedGenericsOfClasses.keySet()) { - if (!other.computedGenericsOfClasses.containsKey(clazz)) return false; - - for (var method : clazz.getMethods()) { - for (var param : method.getParameterList()) { - if (param.getType() instanceof TypePlaceholder tph) { - var leftBound = boundChain(clazz, method, tph); - var rightBound = other.boundChain(clazz, method, tph); - if (!boundChainEquals(leftBound, rightBound)) return false; - } - } - if (method.getReturnType() instanceof TypePlaceholder tph) { - var leftBound = boundChain(clazz, method, tph); - var rightBound = other.boundChain(clazz, method, tph); - if (!boundChainEquals(leftBound, rightBound)) return false; - } - } - } - return true; - } - } - public List computedGenerics() { return all.stream().map(GenericsResult::new).toList(); } - private class Sigma { + class Sigma { Map>> computedGenericsOfMethods = new HashMap<>(); Map> usedTPHsOfMethods = new HashMap<>(); Map>> computedGenericsOfClasses = new HashMap<>(); @@ -366,8 +261,9 @@ public class ASTToTargetAST { if (pair.right.equals(pair2.left)) continue outer; } - if (!hasBound(pair.right, genericsOfClass) && typeVariables.contains(pair.right)) + if (!hasBound(pair.right, genericsOfClass) && typeVariables.contains(pair.right)) { addToPairs(result, new PairTPHequalRefTypeOrWildcardType(pair.right, OBJECT)); + } } eliminateCyclesAndInfima(result); @@ -489,8 +385,8 @@ public class ASTToTargetAST { } } - void eliminateCyclesAndInfima(Set> input) { - // Eliminate cycles + + void eliminateCycles(Set> input) { var cycles = findCycles(input); for (var cycle : cycles) { var newTph = TypePlaceholder.fresh(new NullToken()); @@ -504,7 +400,9 @@ public class ASTToTargetAST { equality.put(left, newTph); } } - // Eliminate infima + } + + void eliminateInfima(Set> input) { var foundInfima = false; do { foundInfima = false; @@ -546,9 +444,21 @@ public class ASTToTargetAST { } while (foundInfima); } - TargetType get(TypePlaceholder tph) { + void eliminateCyclesAndInfima(Set> input) { + eliminateCycles(input); + eliminateInfima(input); + } + + RefTypeOrTPHOrWildcardOrGeneric getType(TypePlaceholder tph) { if (equality.containsKey(tph)) { - return get(equality.get(tph)); + return getType(equality.get(tph)); + } + return concreteTypes.getOrDefault(tph, tph); + } + + TargetType getTargetType(TypePlaceholder tph) { + if (equality.containsKey(tph)) { + return getTargetType(equality.get(tph)); } var type = concreteTypes.get(tph); if (type == null) return new TargetGenericType(tph.getName()); @@ -818,7 +728,7 @@ public class ASTToTargetAST { @Override public TargetType visit(TypePlaceholder typePlaceholder) { - return sigma.get(typePlaceholder); + return sigma.getTargetType(typePlaceholder); } @Override diff --git a/src/main/java/de/dhbwstuttgart/target/generate/Bound.java b/src/main/java/de/dhbwstuttgart/target/generate/Bound.java new file mode 100644 index 000000000..3b53e864e --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/Bound.java @@ -0,0 +1,24 @@ +package de.dhbwstuttgart.target.generate; + +import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; +import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; + +import java.util.List; + +public record Bound(boolean isOnMethod, RefTypeOrTPHOrWildcardOrGeneric bound) { + + public static boolean chainEquals(List left, List right) { + if (left.size() != right.size()) return false; + for (var i = 0; i < left.size(); i++) { + var l = left.get(i); + var r = right.get(i); + if (l.isOnMethod != r.isOnMethod) return false; + if (i == left.size() - 1) { + if (!(l.bound instanceof TypePlaceholder)) { + if (!(l.bound.equals(r.bound))) return false; + } + } + } + return true; + } +} diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java new file mode 100644 index 000000000..29922d852 --- /dev/null +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java @@ -0,0 +1,68 @@ +package de.dhbwstuttgart.target.generate; + +import de.dhbwstuttgart.syntaxtree.ClassOrInterface; +import de.dhbwstuttgart.syntaxtree.Method; +import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; +import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; +import de.dhbwstuttgart.typeinference.result.ResultPair; + +import java.util.*; + +public class GenericsResult { + final ASTToTargetAST.Sigma sigma; + + public GenericsResult(ASTToTargetAST.Sigma sigma) { + this.sigma = sigma; + } + + public Set> get(ClassOrInterface clazz) { + return this.sigma.computedGenericsOfClasses.getOrDefault(clazz, Set.of()); + } + + public Set> get(Method method) { + return this.sigma.computedGenericsOfMethods.getOrDefault(method, Set.of()); + } + + public List getBounds(RefTypeOrTPHOrWildcardOrGeneric type, ClassOrInterface clazz) { + return getBounds(type, clazz, null); + } + + public List getBounds(RefTypeOrTPHOrWildcardOrGeneric type, ClassOrInterface clazz, Method method) { + type = resolve(type); + if (type instanceof TypePlaceholder) { + var methodGenerics = get(method); + var classGenerics = get(clazz); + List result = new ArrayList<>(); + + Optional bound = Optional.empty(); + do { + bound = Optional.empty(); + for (var pair : methodGenerics) { + if (pair.getLeft().equals(type)) { + type = resolve(pair.getRight()); + bound = Optional.of(new Bound(true, type)); + break; + } + } + if (bound.isEmpty()) { + for (var pair : classGenerics) { + if (pair.getLeft().equals(type)) { + type = resolve(pair.getRight()); + bound = Optional.of(new Bound(false, type)); + break; + } + } + } + bound.ifPresent(result::add); + } while (bound.isPresent()); + return result; + } + return List.of(); + } + + public RefTypeOrTPHOrWildcardOrGeneric resolve(RefTypeOrTPHOrWildcardOrGeneric type) { + if (type instanceof TypePlaceholder tph) + return this.sigma.getType(tph); + return type; + } +} diff --git a/src/test/java/targetast/TestGenerics.java b/src/test/java/targetast/TestGenerics.java index ce4aa53ad..ea91eb12a 100644 --- a/src/test/java/targetast/TestGenerics.java +++ b/src/test/java/targetast/TestGenerics.java @@ -2,33 +2,363 @@ package targetast; import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.syntaxtree.ClassOrInterface; -import de.dhbwstuttgart.target.generate.ASTToTargetAST.GenericsResult; +import de.dhbwstuttgart.syntaxtree.Field; +import de.dhbwstuttgart.syntaxtree.Method; +import de.dhbwstuttgart.syntaxtree.type.RefType; +import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; +import de.dhbwstuttgart.target.generate.ASTToTargetAST; +import de.dhbwstuttgart.target.generate.Bound; +import de.dhbwstuttgart.target.generate.GenericsResult; +import org.junit.Ignore; import org.junit.Test; +import static org.junit.Assert.*; +import java.io.File; import java.io.IOException; +import java.lang.reflect.Type; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; +import java.util.logging.SocketHandler; + public class TestGenerics { private static final String rootDirectory = System.getProperty("user.dir") + "/src/test/resources/insertGenericsJav/"; + private static final String bytecodeDirectory = System.getProperty("user.dir") + "/src/test/resources/testBytecode/generatedBC/"; - private record Result(List genericsResults, ClassOrInterface clazz) {} + private record Result(List genericsResults, ClassOrInterface clazz) { + Method findMethod(String name) { + return clazz.getMethods().stream().filter(m -> m.getName().equals(name)).findFirst().orElse(null); + } + + Field findField(String name) { + return clazz.getFieldDecl().stream().filter(field -> field.getName().equals(name)).findFirst().orElse(null); + } + } private static Result computeGenerics(String filename) throws IOException, ClassNotFoundException { var file = Path.of(rootDirectory + filename).toFile(); var compiler = new JavaTXCompiler(List.of(file), List.of(file.getParentFile())); var inference = compiler.typeInference(); - compiler.generateBytecode(null, inference); + compiler.generateBytecode(new File(bytecodeDirectory), inference); var clazz = compiler.sourceFiles.get(file).getClasses().get(0); return new Result(compiler.getGeneratedGenerics().get(file), clazz); } @Test public void testAny() throws Exception { - var generics = computeGenerics("TestAny.jav"); - for (var result : generics.genericsResults) { - System.out.println(result.get(generics.clazz)); - } + var result = computeGenerics("TestAny.jav"); + var anyMethod = result.findMethod("anyMethod"); + var otherMethod = result.findMethod("otherMethod"); + var a = result.findField("a"); + + var generics = result.genericsResults.get(0); + assertEquals(1, generics.get(anyMethod).size()); + assertEquals(2, generics.get(result.clazz).size()); + + var ECK1 = generics.getBounds(otherMethod.getParameterList().getParameterAt(0).getType(), result.clazz, anyMethod); + var ECK2 = generics.getBounds(otherMethod.getReturnType(), result.clazz, anyMethod); + var ECKChain = List.of(new Bound(false, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(ECK1, ECK2) && Bound.chainEquals(ECK2, ECKChain)); + + var M = generics.getBounds(a.getType(), result.clazz); + var MChain = List.of(new Bound(false, TypePlaceholder.of("ECK")), new Bound(false, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(M, MChain)); + } + + @Test + public void testClassField() throws Exception { + var result = computeGenerics("TestClassField.jav"); + var fReturn = result.findMethod("fReturn"); + + var generics = result.genericsResults.get(0); + assertEquals(1, generics.get(result.clazz).size()); + assertEquals(0, generics.get(fReturn).size()); + + var N = generics.getBounds(fReturn.getReturnType(), result.clazz); + var NChain = List.of(new Bound(false, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(N, NChain)); + } + + @Test + public void testContraVariant() throws Exception { + var result = computeGenerics("TestContraVariant.jav"); + var m = result.findMethod("m"); + var main = result.findMethod("main"); + + var generics = result.genericsResults.get(0); + assertEquals(0, generics.get(result.clazz).size()); + assertEquals(2, generics.get(m).size()); + assertEquals(2, generics.get(main).size()); + + var N = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m); + var NChain = List.of(new Bound(true, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(N, NChain)); + + var Q = generics.getBounds(m.getReturnType(), result.clazz, m); + var QChain = List.of(new Bound(true, TypePlaceholder.of("N")), new Bound(true, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(Q, QChain)); + + var R = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main); + assertTrue(Bound.chainEquals(R, NChain)); + + var Q2 = generics.getBounds(main.getReturnType(), result.clazz, main); + assertTrue(Bound.chainEquals(Q2, NChain)); + } + + @Test + public void testGGFinder() throws Exception { + var result = computeGenerics("TestGGFinder.jav"); + var id = result.findMethod("id"); + var setA = result.findMethod("setA"); + var m = result.findMethod("m"); + var a = result.findField("a"); + + var generics = result.genericsResults.get(0); + assertEquals(1, generics.get(result.clazz).size()); + assertEquals(2, generics.get(id).size()); + assertEquals(1, generics.get(setA).size()); + assertEquals(2, generics.get(m).size()); + + var R = generics.getBounds(a.getType(), result.clazz); + var RChain = List.of(new Bound(false, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(R, RChain)); + + var O = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id); + var OChain = List.of(new Bound(true, TypePlaceholder.of("AB")), new Bound(true, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(O, OChain)); + var AB = generics.getBounds(id.getReturnType(), result.clazz, id); + assertTrue(Bound.chainEquals(AB, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + + var S = generics.getBounds(setA.getParameterList().getParameterAt(0).getType(), result.clazz, setA); + var SChain = List.of(new Bound(true, TypePlaceholder.of("R")), new Bound(false, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(S, SChain)); + assertTrue(Bound.chainEquals(generics.getBounds(setA.getReturnType(), result.clazz, setA), RChain)); + + 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 = List.of(new Bound(true, ASTToTargetAST.OBJECT)); + var YChain = List.of(new Bound(true, TypePlaceholder.of("Y")), new Bound(true, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(X, XChain)); + assertTrue(Bound.chainEquals(Y, YChain)); + } + + @Test + @Ignore("Not implemented") + public void testLocalVarLambda() throws Exception { + var result = computeGenerics("TestLocalVarLambda.jav"); + // TODO Generics of lambdas + } + + @Test + public void testMutualRecursion() throws Exception { + var result = computeGenerics("TestMutualRecursion.jav"); + var id = result.findMethod("id"); + var m = result.findMethod("m"); + var main = result.findMethod("main"); + var a = result.findField("a"); + + var generics = result.genericsResults.get(0); + assertEquals(1, generics.get(result.clazz).size()); + assertEquals(3, generics.get(id).size()); + assertEquals(2, generics.get(m).size()); + assertEquals(3, generics.get(main).size()); + + var M = generics.getBounds(a.getType(), result.clazz); + assertTrue(Bound.chainEquals(M, List.of(new Bound(false, ASTToTargetAST.OBJECT)))); + + var O = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id); + var OChain = List.of(new Bound(true, TypePlaceholder.of("P")), new Bound(true, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(O, OChain)); + var AK = generics.getBounds(id.getReturnType(), result.clazz, id); + var AKChain = List.of(new Bound(true, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(AK, AKChain)); + + // TODO Shouldn't AK and AK2 be the same and on the class? + var AK2 = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m); + var Y = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m); + assertTrue(Bound.chainEquals(AK2, AKChain)); + assertTrue(Bound.chainEquals(Y, AKChain)); + + var AF = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main); + var AG= generics.getBounds(main.getParameterList().getParameterAt(1).getType(), result.clazz, main); + var AK3 = generics.getBounds(main.getReturnType(), result.clazz, main); + var AFChain = List.of(new Bound(true, TypePlaceholder.of("AG")), new Bound(true, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(AF, AFChain)); + assertTrue(Bound.chainEquals(AG, AKChain)); + assertTrue(Bound.chainEquals(AK3, AKChain)); + } + + @Test + public void testReturnVar() throws Exception { + var result = computeGenerics("TestReturnVar.jav"); + var anyMethod = result.findMethod("anyMethod"); + + var generics = result.genericsResults.get(0); + assertEquals(1, generics.get(anyMethod).size()); + + var M = generics.getBounds(anyMethod.getReturnType(), result.clazz, anyMethod); + assertTrue(Bound.chainEquals(M, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + } + + @Test + public void testSecondLineOfClassConstraints() throws Exception { + var result = computeGenerics("TestSecondLineOfClassConstraints.jav"); + var a = result.findField("a"); + var b = result.findField("b"); + var anyMethod = result.findMethod("anyMethod"); + var otherMethod = result.findMethod("otherMethod"); + + var generics = result.genericsResults.get(0); + var M = generics.getBounds(a.getType(), result.clazz); + var DYX = generics.getBounds(b.getType(), result.clazz); + var MChain = List.of(new Bound(false, TypePlaceholder.of("DYX")), new Bound(false, ASTToTargetAST.OBJECT)); + var DYXChain = List.of(new Bound(false, ASTToTargetAST.OBJECT)); + assertTrue(Bound.chainEquals(M, MChain)); + + var Q = generics.getBounds(anyMethod.getReturnType(), result.clazz, anyMethod); + assertTrue(Bound.chainEquals(Q, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + + var DYX2 = generics.getBounds(otherMethod.getReturnType(), result.clazz, otherMethod); + assertTrue(Bound.chainEquals(DYX, DYX2) && Bound.chainEquals(DYX2, DYXChain)); + } + + @Test + @Ignore("This doesn't work properly") + public void testThreeArgs() throws Exception { + var result = computeGenerics("TestThreeArgs.jav"); + + } + + @Test + public void testTPHsAndGenerics() throws Exception { + var result = computeGenerics("TestTPHsAndGenerics.jav"); + var id2 = result.findMethod("id2"); + var m = result.findMethod("m"); + var m2 = result.findMethod("m2"); + + 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); + assertTrue(Bound.chainEquals(U, List.of(new Bound(true, TypePlaceholder.of("FPT")), new Bound(false, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(FPT, List.of(new Bound(false, ASTToTargetAST.OBJECT)))); + + var AA = generics.getBounds(m.getReturnType(), result.clazz, m); + var AC = generics.getBounds(m.getParameterList().getParameterAt(1).getType(), result.clazz, m); + assertTrue(Bound.chainEquals(AA, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(AC, List.of(new Bound(true, TypePlaceholder.of("AD")), new Bound(true, ASTToTargetAST.OBJECT)))); + + var AH = generics.getBounds(m2.getReturnType(), result.clazz, m2); + var AL = generics.getBounds(m2.getParameterList().getParameterAt(0).getType(), result.clazz, m2); + assertTrue(Bound.chainEquals(AH, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(AH, AL)); + } + + @Test + public void testTwoArgs() throws Exception { + var result = computeGenerics("TestTwoArgs.jav"); + + var a = result.findField("a"); + var id = result.findMethod("id"); + var setA = result.findMethod("setA"); + var m = result.findMethod("m"); + var main = result.findMethod("main"); + + var generics = result.genericsResults.get(0); + var AO = generics.getBounds(a.getType(), result.clazz); + var AOBound = List.of( + new Bound(false, TypePlaceholder.of("Y")), + new Bound(false, TypePlaceholder.of("AK")), + new Bound(false, TypePlaceholder.of("AE")), + new Bound(false, ASTToTargetAST.OBJECT) + ); + assertTrue(Bound.chainEquals(AO, AOBound)); + + var S = generics.getBounds(setA.getParameterList().getParameterAt(0).getType(), result.clazz, setA); + var SChain = new ArrayList(); + SChain.add(new Bound(true, TypePlaceholder.of("AO"))); + SChain.addAll(AOBound); + assertTrue(Bound.chainEquals(S, SChain)); + + var Y = generics.getBounds(m.getParameterList().getParameterAt(1).getType(), result.clazz, m); + var YChain = List.of(new Bound(true, TypePlaceholder.of("AE")), new Bound(false, ASTToTargetAST.OBJECT)); + var AE = generics.getBounds(m.getReturnType(), result.clazz, m); + assertTrue(Bound.chainEquals(Y, YChain)); + assertTrue(Bound.chainEquals(AE, List.of(new Bound(false, ASTToTargetAST.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); + assertTrue(Bound.chainEquals(AE, AE2)); + assertTrue(Bound.chainEquals(AF, List.of(new Bound(true, TypePlaceholder.of("AK")), new Bound(true, TypePlaceholder.of("AE")), new Bound(false, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(AG, SChain));*/ + } + + @Test + @Ignore("main changes in between runs") + public void testTwoArgs2() throws Exception { + var result = computeGenerics("TestTwoArgs2.jav"); + } + + @Test + public void testTwoCalls() throws Exception { + var result = computeGenerics("TestTwoCalls.jav"); + var id = result.findMethod("id"); + var main = result.findMethod("main"); + + var generics = result.genericsResults.get(0); + var Q = generics.getBounds(id.getReturnType(), result.clazz, id); + var N = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id); + assertTrue(Bound.chainEquals(Q, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(N, List.of(new Bound(true, TypePlaceholder.of("Q")), new Bound(true, ASTToTargetAST.OBJECT)))); + + var Q2 = generics.getBounds(main.getReturnType(), result.clazz, main); + var R = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main); + var S = generics.getBounds(main.getParameterList().getParameterAt(1).getType(), result.clazz, main); + assertTrue(Bound.chainEquals(Q2, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(R, List.of(new Bound(true, TypePlaceholder.of("Q")), new Bound(true, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(S, List.of(new Bound(true, TypePlaceholder.of("Q")), new Bound(true, ASTToTargetAST.OBJECT)))); + } + + @Test + public void testVector() throws Exception { + var result = computeGenerics("TestVector.jav"); + var m = result.findMethod("m"); + + var generics = result.genericsResults.get(0); + var par1 = generics.resolve(m.getParameterList().getParameterAt(0).getType()); + var par2 = generics.resolve(m.getParameterList().getParameterAt(1).getType()); + + var S = generics.getBounds(((RefType) par1).getParaList().get(0), result.clazz, m); + var ACM = generics.getBounds(((RefType) par2).getParaList().get(0), result.clazz, m); + assertTrue(Bound.chainEquals(S, List.of(new Bound(true, TypePlaceholder.of("V")), new Bound(true, TypePlaceholder.of("ACM")), new Bound(true, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(ACM, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + } + + @Test + public void testVectorArg() throws Exception { + var result = computeGenerics("TestVectorArg.jav"); + var add = result.findMethod("add"); + var main = result.findMethod("main"); + + var generics = result.genericsResults.get(0); + 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); + assertTrue(Bound.chainEquals(ACK, List.of(new Bound(true, ASTToTargetAST.OBJECT)))); + assertTrue(Bound.chainEquals(O, List.of(new Bound(true, TypePlaceholder.of("ACK")), new Bound(true, ASTToTargetAST.OBJECT)))); + + var par2 = generics.resolve(main.getParameterList().getParameterAt(0).getType()); + var ACK2 = generics.getBounds(((RefType) par2).getParaList().get(0), result.clazz, add); + var V = generics.getBounds(add.getParameterList().getParameterAt(1).getType(), result.clazz, add); + assertTrue(Bound.chainEquals(ACK2, ACK)); + assertTrue(Bound.chainEquals(V, O)); + } + + @Test + public void testVoidMeth() throws Exception { + var result = computeGenerics("TestVoidMeth.jav"); } }