Work on the generics tests

This commit is contained in:
Vic Nightfall 2022-12-06 17:44:39 +01:00
parent b63d1bcf73
commit 9f27d0d0fa
3 changed files with 169 additions and 8 deletions

View File

@ -852,6 +852,13 @@ public class JavaTXCompiler {
generateBytecode(path, typeinferenceResult);
}
private Map<File, List<ASTToTargetAST.GenericsResult>> generatedGenerics = new HashMap<>();
// TODO This is a temporary solution, we should integrate with the old API for getting Generics
public Map<File, List<ASTToTargetAST.GenericsResult>> getGeneratedGenerics() {
return generatedGenerics;
}
/**
* @param outputPath - can be null, then class file output is in the same directory as the parsed source files
* @param typeinferenceResult
@ -878,6 +885,7 @@ public class JavaTXCompiler {
generatedClasses.put(new JavaClassName(name), source);
});
}
generatedGenerics.put(f, converter.computedGenerics());
writeClassFile(generatedClasses, path);
}
}

View File

@ -24,7 +24,116 @@ public class ASTToTargetAST {
protected List<Sigma> all;
protected Sigma sigma;
protected ClassOrInterface currentClass; // TODO This is only needed because of SuperCall, maybe there's a better way?
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<Bound> left, List<Bound> 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<ClassOrInterface, Set<ResultPair<?, ?>>> computedGenericsOfClasses;
final Map<Method, Set<ResultPair<?, ?>>> computedGenericsOfMethods;
final Sigma sigma;
public GenericsResult(
Map<ClassOrInterface, Set<ResultPair<?, ?>>> computedGenericsOfClasses,
Map<Method, Set<ResultPair<?, ?>>> 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<ResultPair<?, ?>> get(ClassOrInterface clazz) {
return computedGenericsOfClasses.get(clazz);
}
public Set<ResultPair<?, ?>> 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<Bound> boundChain(ClassOrInterface clazz, Method method, TypePlaceholder tph) {
var result = new ArrayList<Bound>();
RefTypeOrTPHOrWildcardOrGeneric bound = equality(tph);
var constraintsForMethod = get(method);
var constraintsForClass = get(clazz);
Optional<ResultPair<?, ?>> 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<GenericsResult> computedGenerics() {
return all.stream().map(GenericsResult::new).toList();
}
private class Sigma {
Map<Method, Set<ResultPair<?, ?>>> computedGenericsOfMethods = new HashMap<>();
@ -66,7 +175,8 @@ public class ASTToTargetAST {
}
boolean hasBound(TypePlaceholder name, Set<ResultPair<?, ?>> generics) {
return generics.stream().anyMatch(generic -> generic.getLeft().equals(name));
TypePlaceholder finalName = equality.getOrDefault(name, name);
return generics.stream().anyMatch(generic -> generic.getLeft().equals(finalName));
}
boolean containsRelation(Set<ResultPair<?, ?>> result, PairTPHsmallerTPH pair) {
@ -141,6 +251,7 @@ public class ASTToTargetAST {
public void visit(Assign assign) {}
});
// Type variables with bounds that are also type variables of the method
for (var typeVariable : new HashSet<>(typeVariables)) {
for (var pair : simplifiedConstraints) {
@ -151,6 +262,7 @@ public class ASTToTargetAST {
}
}
var visitedMethods = new HashSet<Method>();
method.block.accept(new TracingStatementVisitor() {
@Override
@ -188,8 +300,10 @@ public class ASTToTargetAST {
toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft()));
}
all.addAll(toAdd);
HashSet<PairTPHsmallerTPH> newPairs = new HashSet<>();
System.out.println("Result: " + result);
// Loop from hell
outer:
for (var tph : typeVariables) {
@ -234,7 +348,7 @@ public class ASTToTargetAST {
}
}
// All unbounded type variables
// All unbounded type variables (bounds not in method)
outer:
for (var typeVariable : typeVariables) {
for (var pair : simplifiedConstraints) {
@ -277,6 +391,7 @@ public class ASTToTargetAST {
return;
}
var found = false;
for (var rsp : simplifiedConstraints) {
var left = equality.getOrDefault(rsp.left, rsp.left);
var right = equality.getOrDefault(rsp.right, rsp.right);
@ -285,11 +400,12 @@ public class ASTToTargetAST {
if (!generics.contains(pair)) {
generics.add(pair);
findAllBounds(right, generics);
found = true;
}
return;
}
}
generics.add(new PairTPHequalRefTypeOrWildcardType(tph, OBJECT));
if (!found)
generics.add(new PairTPHequalRefTypeOrWildcardType(tph, OBJECT));
} else if (type instanceof RefType refType) {
refType.getParaList().forEach(t -> findAllBounds(t, generics));
}
@ -308,6 +424,7 @@ public class ASTToTargetAST {
eliminateInnerTypeVariables(classOrInterface, result);
equalizeTypeVariables(result);
System.out.println("Class " + classOrInterface.getClassName().getClassName() + ": " + result);
return result;
}
@ -410,14 +527,16 @@ public class ASTToTargetAST {
if (pair.getLeft().equals(infimum.right)) {
input.remove(pair);
if (pair instanceof PairTPHsmallerTPH stph) {
addToPairs(input, new PairTPHsmallerTPH(newTph, stph.right));
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) {
addToPairs(input, new PairTPHsmallerTPH(stph.left, newTph));
if (!newTph.equals(stph.left))
addToPairs(input, new PairTPHsmallerTPH(stph.left, newTph));
}
}
});

View File

@ -0,0 +1,34 @@
package targetast;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.target.generate.ASTToTargetAST.GenericsResult;
import org.junit.Test;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
public class TestGenerics {
private static final String rootDirectory = System.getProperty("user.dir") + "/src/test/resources/insertGenericsJav/";
private record Result(List<GenericsResult> genericsResults, ClassOrInterface clazz) {}
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);
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));
}
}
}