Work on the generics tests
This commit is contained in:
parent
b63d1bcf73
commit
9f27d0d0fa
@ -852,6 +852,13 @@ public class JavaTXCompiler {
|
|||||||
generateBytecode(path, typeinferenceResult);
|
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 outputPath - can be null, then class file output is in the same directory as the parsed source files
|
||||||
* @param typeinferenceResult
|
* @param typeinferenceResult
|
||||||
@ -878,6 +885,7 @@ public class JavaTXCompiler {
|
|||||||
generatedClasses.put(new JavaClassName(name), source);
|
generatedClasses.put(new JavaClassName(name), source);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
generatedGenerics.put(f, converter.computedGenerics());
|
||||||
writeClassFile(generatedClasses, path);
|
writeClassFile(generatedClasses, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,116 @@ public class ASTToTargetAST {
|
|||||||
|
|
||||||
protected List<Sigma> all;
|
protected List<Sigma> all;
|
||||||
protected Sigma sigma;
|
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 {
|
private class Sigma {
|
||||||
Map<Method, Set<ResultPair<?, ?>>> computedGenericsOfMethods = new HashMap<>();
|
Map<Method, Set<ResultPair<?, ?>>> computedGenericsOfMethods = new HashMap<>();
|
||||||
@ -66,7 +175,8 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean hasBound(TypePlaceholder name, Set<ResultPair<?, ?>> generics) {
|
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) {
|
boolean containsRelation(Set<ResultPair<?, ?>> result, PairTPHsmallerTPH pair) {
|
||||||
@ -141,6 +251,7 @@ public class ASTToTargetAST {
|
|||||||
public void visit(Assign assign) {}
|
public void visit(Assign assign) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Type variables with bounds that are also type variables of the method
|
// Type variables with bounds that are also type variables of the method
|
||||||
for (var typeVariable : new HashSet<>(typeVariables)) {
|
for (var typeVariable : new HashSet<>(typeVariables)) {
|
||||||
for (var pair : simplifiedConstraints) {
|
for (var pair : simplifiedConstraints) {
|
||||||
@ -151,6 +262,7 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var visitedMethods = new HashSet<Method>();
|
var visitedMethods = new HashSet<Method>();
|
||||||
method.block.accept(new TracingStatementVisitor() {
|
method.block.accept(new TracingStatementVisitor() {
|
||||||
@Override
|
@Override
|
||||||
@ -188,8 +300,10 @@ public class ASTToTargetAST {
|
|||||||
toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft()));
|
toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft()));
|
||||||
}
|
}
|
||||||
all.addAll(toAdd);
|
all.addAll(toAdd);
|
||||||
|
|
||||||
HashSet<PairTPHsmallerTPH> newPairs = new HashSet<>();
|
HashSet<PairTPHsmallerTPH> newPairs = new HashSet<>();
|
||||||
|
|
||||||
|
System.out.println("Result: " + result);
|
||||||
|
|
||||||
// Loop from hell
|
// Loop from hell
|
||||||
outer:
|
outer:
|
||||||
for (var tph : typeVariables) {
|
for (var tph : typeVariables) {
|
||||||
@ -234,7 +348,7 @@ public class ASTToTargetAST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All unbounded type variables
|
// All unbounded type variables (bounds not in method)
|
||||||
outer:
|
outer:
|
||||||
for (var typeVariable : typeVariables) {
|
for (var typeVariable : typeVariables) {
|
||||||
for (var pair : simplifiedConstraints) {
|
for (var pair : simplifiedConstraints) {
|
||||||
@ -277,6 +391,7 @@ public class ASTToTargetAST {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var found = false;
|
||||||
for (var rsp : simplifiedConstraints) {
|
for (var rsp : simplifiedConstraints) {
|
||||||
var left = equality.getOrDefault(rsp.left, rsp.left);
|
var left = equality.getOrDefault(rsp.left, rsp.left);
|
||||||
var right = equality.getOrDefault(rsp.right, rsp.right);
|
var right = equality.getOrDefault(rsp.right, rsp.right);
|
||||||
@ -285,10 +400,11 @@ public class ASTToTargetAST {
|
|||||||
if (!generics.contains(pair)) {
|
if (!generics.contains(pair)) {
|
||||||
generics.add(pair);
|
generics.add(pair);
|
||||||
findAllBounds(right, generics);
|
findAllBounds(right, generics);
|
||||||
}
|
found = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
generics.add(new PairTPHequalRefTypeOrWildcardType(tph, OBJECT));
|
generics.add(new PairTPHequalRefTypeOrWildcardType(tph, OBJECT));
|
||||||
} else if (type instanceof RefType refType) {
|
} else if (type instanceof RefType refType) {
|
||||||
refType.getParaList().forEach(t -> findAllBounds(t, generics));
|
refType.getParaList().forEach(t -> findAllBounds(t, generics));
|
||||||
@ -308,6 +424,7 @@ public class ASTToTargetAST {
|
|||||||
eliminateInnerTypeVariables(classOrInterface, result);
|
eliminateInnerTypeVariables(classOrInterface, result);
|
||||||
equalizeTypeVariables(result);
|
equalizeTypeVariables(result);
|
||||||
|
|
||||||
|
|
||||||
System.out.println("Class " + classOrInterface.getClassName().getClassName() + ": " + result);
|
System.out.println("Class " + classOrInterface.getClassName().getClassName() + ": " + result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -410,6 +527,7 @@ public class ASTToTargetAST {
|
|||||||
if (pair.getLeft().equals(infimum.right)) {
|
if (pair.getLeft().equals(infimum.right)) {
|
||||||
input.remove(pair);
|
input.remove(pair);
|
||||||
if (pair instanceof PairTPHsmallerTPH stph) {
|
if (pair instanceof PairTPHsmallerTPH stph) {
|
||||||
|
if (!newTph.equals(stph.right))
|
||||||
addToPairs(input, new PairTPHsmallerTPH(newTph, stph.right));
|
addToPairs(input, new PairTPHsmallerTPH(newTph, stph.right));
|
||||||
} else if (pair instanceof PairTPHequalRefTypeOrWildcardType rtph) {
|
} else if (pair instanceof PairTPHequalRefTypeOrWildcardType rtph) {
|
||||||
addToPairs(input, new PairTPHequalRefTypeOrWildcardType(newTph, rtph.getRight()));
|
addToPairs(input, new PairTPHequalRefTypeOrWildcardType(newTph, rtph.getRight()));
|
||||||
@ -417,6 +535,7 @@ public class ASTToTargetAST {
|
|||||||
} else if (pair.getRight().equals(infimum.right)) {
|
} else if (pair.getRight().equals(infimum.right)) {
|
||||||
input.remove(pair);
|
input.remove(pair);
|
||||||
if (pair instanceof PairTPHsmallerTPH stph) {
|
if (pair instanceof PairTPHsmallerTPH stph) {
|
||||||
|
if (!newTph.equals(stph.left))
|
||||||
addToPairs(input, new PairTPHsmallerTPH(stph.left, newTph));
|
addToPairs(input, new PairTPHsmallerTPH(stph.left, newTph));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
src/test/java/targetast/TestGenerics.java
Normal file
34
src/test/java/targetast/TestGenerics.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user