diff --git a/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java index 13514baa0..8b540e69b 100755 --- a/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/target/bytecode/Codegen.java @@ -717,9 +717,8 @@ public class Codegen { break; } case TargetReturn ret: { - generate(state, ret.expression()); - if (ret.expression() != null) { + generate(state, ret.expression()); boxPrimitive(state, ret.expression().type()); mv.visitInsn(ARETURN); } else mv.visitInsn(RETURN); diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index e9d1805ed..39ad380d2 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -1,5 +1,6 @@ package de.dhbwstuttgart.target.generate; +import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.syntaxtree.*; import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; import de.dhbwstuttgart.syntaxtree.statement.*; @@ -226,6 +227,7 @@ public class ASTToTargetAST { result.add(new PairTPHequalRefTypeOrWildcardType(pair.right, OBJECT)); } + eliminateCyclesAndInfima(result); System.out.println(method.name + ": " + result); return result; @@ -265,9 +267,53 @@ public class ASTToTargetAST { findAllBounds(field.getType(), result); } computedGenericsOfClasses.put(classOrInterface, result); + eliminateCyclesAndInfima(result); + System.out.println("Class " + classOrInterface.getClassName().getClassName() + ": " + result); return result; } + void eliminateCyclesAndInfima(Set> input) { + // Eliminate cycles + var cycles = findCycles(input); + for (var cycle : cycles) { + var newTph = TypePlaceholder.fresh(new NullToken()); + input.add(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); + equality.put(left, newTph); + } + } + // Eliminate infima + 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()); + input.add(new PairTPHsmallerTPH(left, newTph)); + input.add(new PairTPHequalRefTypeOrWildcardType(newTph, OBJECT)); + input.removeAll(infima); + for (var infimum : infima) { + equality.put(infimum.right, newTph); + input.removeIf(pair -> pair.getLeft().equals(infimum.right)); + } + } + } + } while (foundInfima); + } + TargetType get(TypePlaceholder tph) { if (equality.containsKey(tph)) { return get(equality.get(tph)); @@ -286,6 +332,92 @@ public class ASTToTargetAST { this.sigma = all.get(0); } + static Set allNodes(Set> input) { + return input.stream().map(pair -> (TypePlaceholder) pair.getLeft()).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 + 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; + } + Set convert(Set> result) { return result.stream().map(p -> { if (p instanceof PairTPHsmallerTPH pair) { @@ -321,9 +453,10 @@ public class ASTToTargetAST { for (var s : all) { sigma = s; + var generics = sigma.generics(owner, input); List params = convert(input.getParameterList()); if (parameterSet.stream().noneMatch(p -> p.equals(params))) { - result.add(new TargetConstructor(input.modifier, convert(sigma.generics(owner, input)), params, convert(input.block))); + result.add(new TargetConstructor(input.modifier, convert(generics), params, convert(input.block))); parameterSet.add(params); } } @@ -339,11 +472,12 @@ public class ASTToTargetAST { for (var s : all) { sigma = s; + var generics = sigma.generics(owner, input); List params = convert(input.getParameterList()); if (parameterSet.stream().noneMatch(p -> p.equals(params))) { result.add(new TargetMethod( input.modifier, - input.name, convert(sigma.generics(owner, input)), params, + input.name, convert(generics), params, convert(input.getReturnType()), convert(input.block) )); diff --git a/src/test/java/targetast/ASTToTypedTargetAST.java b/src/test/java/targetast/ASTToTypedTargetAST.java index 881e0cb7c..6f914432f 100644 --- a/src/test/java/targetast/ASTToTypedTargetAST.java +++ b/src/test/java/targetast/ASTToTypedTargetAST.java @@ -62,4 +62,25 @@ public class ASTToTypedTargetAST { var tphAndGenerics = TestCodegen.generateClass(converter.convert(classes.get(0))); } + @Test + public void cycles() throws Exception { + var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/Cycle.jav").toFile(); + var compiler = new JavaTXCompiler(file); + var resultSet = compiler.typeInference(); + var converter = new ASTToTargetAST(resultSet); + var classes = compiler.sourceFiles.get(file).getClasses(); + + var cycle = TestCodegen.generateClass(converter.convert(classes.get(0))); + } + + @Test + public void infimum() throws Exception { + var file = Path.of(System.getProperty("user.dir"), "/src/test/resources/bytecode/javFiles/Infimum.jav").toFile(); + var compiler = new JavaTXCompiler(file); + var resultSet = compiler.typeInference(); + var converter = new ASTToTargetAST(resultSet); + var classes = compiler.sourceFiles.get(file).getClasses(); + + var infimum = TestCodegen.generateClass(converter.convert(classes.get(0))); + } } diff --git a/src/test/resources/bytecode/javFiles/Cycle.jav b/src/test/resources/bytecode/javFiles/Cycle.jav new file mode 100644 index 000000000..85f2ba9ad --- /dev/null +++ b/src/test/resources/bytecode/javFiles/Cycle.jav @@ -0,0 +1,6 @@ +class Cycle { + m(x, y) { + y = x; + x = y; + } +} \ No newline at end of file diff --git a/src/test/resources/bytecode/javFiles/Infimum.jav b/src/test/resources/bytecode/javFiles/Infimum.jav new file mode 100644 index 000000000..7c475394d --- /dev/null +++ b/src/test/resources/bytecode/javFiles/Infimum.jav @@ -0,0 +1,6 @@ +class Infimum { + m(x, y, z) { + y = x; + z = x; + } +} \ No newline at end of file