forked from JavaTX/JavaCompilerCore
Work on the generics tests
This commit is contained in:
parent
b63d1bcf73
commit
9f27d0d0fa
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
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