Compare commits

..
1 Commits
Author SHA1 Message Date
julian 3bd19da742 prototype implementation of scc unification
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 43m51s
2026-09-01 10:44:37 +02:00
60 changed files with 1145 additions and 1105 deletions

No files matched your search

+1 -5
View File
@@ -2,7 +2,7 @@ import java.lang.String;
import java.lang.Object;
public class Bug365{
swap(Fun1$$<String, Fun1$$<String, Object>> f){
swap(f){
return x -> y -> f.apply(y).apply(x);
}
@@ -18,8 +18,4 @@ public class Bug365{
var func = x -> y -> z -> x + y + z;
return swap(func).apply("A").apply("B").apply("C");
}
public ex3() {
var func = x -> y -> x + y;
return swap(func).apply("A").apply("B");
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
sealed interface List<T> permits Cons, Empty {}
record Cons<T>(T a, List<T> l ) implements List <T> {}
record Cons<T>(T a , List<T> l ) implements List <T> {}
record Empty<T>() implements List <T> {}
public class Bug380 {
public <T> List<T> append(List<T> l1, List<T> l2) {
public <T> List<T> append(l1, List<T> l2) {
return switch ( l1 ) {
case Cons(e, rest) -> new Cons<>(e, append(rest, l2)); //::Typ TPH A
case Empty() -> l2;//::TPH B
+6 -4
View File
@@ -1,8 +1,10 @@
import java.lang.String;
public class Bug389 {
//public swap(f) {
// return x -> y -> f.apply(y).apply(x);
//}
public swap(f) {
return x -> y -> f.apply(y).apply(x);
}
public swap(f) {
return x -> y -> z -> f.apply(z).apply(x).apply(y);
}
}
}
+2 -1
View File
@@ -1,10 +1,11 @@
import Bug389;
import java.util.List;
import java.lang.String;
import java.lang.Integer;
public class Bug389Main {
public static main(args) {
var func = (Integer x) -> y -> z -> x + y + z;
var func = x -> y -> z -> x + y + z;
var swap = new Bug389();
swap.swap(func).apply(1).apply(2).apply(3);
}
-13
View File
@@ -1,13 +0,0 @@
import java.lang.Integer;
public class Bug395 {
dup(x) {
//Hier beliebiger Seiteneffekt
return x + x;
}
dup(s) {
return s + s;
}
}
-39
View File
@@ -1,39 +0,0 @@
//package de.dhbwstuttgart;
import java.lang.Integer;
import java.lang.IO;
public sealed interface IntList permits Empty, Cons{}
public record Empty() implements IntList {}
public record Cons(Integer a, IntList rest) implements IntList {}
public class Bug406 {
public head(Empty()) {
return -1;
}
public head(Cons(e,r)) {
return e;
}
public append(Empty(), ys) {
return ys;
}
public append(Cons(x, xs), IntList ys) {
return new Cons(x, append(xs, ys));
}
public add(x, y) {
return x + y;
}
public static main(args) {
var list1 = new Cons(1, new Cons(2, new Cons(3, new Empty())));
var list2 = new Cons(4, new Cons(5, new Cons(6, new Empty())));
IO.println(new Bug406().add(1, 2));
IO.println(new Bug406().append(list1, list2));
}
}
-13
View File
@@ -1,13 +0,0 @@
import java.lang.Integer;
public class Bug409 {
public static m() {
var n = 0;
for (var i = 0; i < 5; i++) {
n = n + 1;
if (n > 100) { return -1; }
if (i == 2) { continue; }
}
return n;
}
}
-7
View File
@@ -1,7 +0,0 @@
import java.lang.Integer;
public interface Bug411 {
default f() {
return 1;
}
}
-12
View File
@@ -1,12 +0,0 @@
import java.lang.Integer;
public class Bug412 {
public m(Integer i) {
var r = 0;
switch (i) {
case 1: r = 10; break;
default: r = 20;
}
return r;
}
}
-9
View File
@@ -1,9 +0,0 @@
import java.lang.Integer;
sealed interface L<T> permits Cons, Nil {}
public record Cons<T>(T head, L<T> tail) implements L<T> {}
public record Nil<T>() implements L<T> {}
public class Bug416 {
public static <T> len(Cons(h, t)) { return 1 + len(t); }
public static <T> len(Nil()) { return 0; }
}
-11
View File
@@ -1,11 +0,0 @@
import java.lang.Integer;
import java.lang.String;
public class Bug417 {
public name(Integer i) {
return switch (i) {
case 1 -> "one";
default -> "many";
};
}
}
-3
View File
@@ -1,3 +0,0 @@
public class Bug421 {
public m() { return 1 } // Missing semicolon
}
-2
View File
@@ -1,2 +0,0 @@
public class Bug422 {}
public class Bug422 {}
-5
View File
@@ -1,5 +0,0 @@
import java.lang.Integer;
interface Bug423I { Integer f(); }
public class Bug423 implements Bug423I { }
+1 -1
View File
@@ -4,7 +4,7 @@ import java.lang.String;
public class Fac {
getFac(n) {
Double res = 1;
var res = 1;
var i = 1;
while (i <= n) {
res = res * i;
@@ -1,9 +1,11 @@
import java.lang.String;
import java.lang.Integer;
sealed interface List<T> permits LinkedElem, Elem {}
public record LinkedElem<T>(T a, List<T> l) implements List<T>{}
public record Elem<T>(T c) implements List<T>{}
sealed interface List permits LinkedElem, Elem {}
public record LinkedElem<T>(T a,List l) implements List{}
public record Elem<T>(T c) implements List{}
public class GenericRecordSwitchCase {
public main(o) {
+1 -3
View File
@@ -5,8 +5,6 @@ import java.lang.Object;
public class HelloWorld {
public static hello() {
System.out.println("Hello World!");
System.out.println("Bye World!");
System.out.println("The end!");
System.out.println((Object)"Hello World!");
}
}
@@ -15,7 +15,6 @@ interface C {
}
class ClassX implements A {
void method1() {}
}
record ClassY(Integer myInt) implements C {}
-56
View File
@@ -1,56 +0,0 @@
//Grundsaetzlich wird Lazy-Evaluation so realisiert, dass immer beim
//Methoden/Konstruktor-Aufruf das Argument in einen Lambda-Ausdruck (Supplier)
//eingepackt wird (siehe Aufruf von Cons und Empty in Count und main) und
//ein Lazy-Argument mit get ausgerollt wird (siehe Methode rest)
import java.lang.Integer;
import java.lang.Boolean;
import java.lang.String;
import java.lang.System;
import java.io.PrintStream;
public sealed interface LazyList permits Empty, Cons {
public Integer fst();
public LazyList rest();
}
public class Filter {
/*public LazyList filter(Fun1$$<Integer, Boolean> p, LazyList l) {
return switch(l) {
case Empty e -> e;
case Cons(Integer x, Supplier<LazyList> l1) ->
p.apply(x) ? new Cons(x, () -> filter(p, l1.get())) : filter(p, l1.get());
};
}*/
public LazyList filter(Fun1$$<Integer, Boolean> p, Empty()@l) {
return l;
}
public LazyList filter(Fun1$$<Integer, Boolean> p, Cons(Integer x, l1)) {
return p.apply(x) ? new Cons(x, () -> filter(p, l1.apply())) : filter(p, l1.apply());
}
}
//Der Konstruktor Cons muss lazy sein, deshalb hier Supplier<...>
record Cons(Integer x, Fun0$$<LazyList> l) implements LazyList {
public Integer fst() { return this.x; }
public LazyList rest() { return this.l.apply(); }
public String toString() {
return "Cons(" + this.x.toString() + ", " + this.l.apply().toString() + ")";
}
}
record Empty() implements LazyList {
public Integer fst() { return -1; }
public LazyList rest() { return null; }
}
class Main {
static LazyList Count(int i) { return new Cons(i, () -> Count(i+1)); }
public static void main(args) {
System.out.println(new Cons(1, () -> new Cons(2, () -> new Empty())).fst());
System.out.println(Count(1).rest().fst());
}
}
-4
View File
@@ -5,8 +5,4 @@ public class Literal {
public m2() { return 'C'; }
public m3() { return 10L; }
public m4() { return 10.5F; }
public m5() { return 10.5D; }
public m6() { return 0xFF; }
public m7() { return 0b101010; }
public m8() { return 010; }
}
-8
View File
@@ -1,8 +0,0 @@
sealed interface Interface permits A, B {}
public record A() implements Interface {}
public record B() implements Interface {}
public interface Matcher {
default match(A()@a) { return 0; }
default match(B()@b) { return 1; }
}
@@ -5,9 +5,21 @@ public record Empty<T>() implements List<T> {}
public record Pair<T1, T2>(T1 a, T2 b) {}
public class PatternMatching {
// Anmerkung: Typ muss angegeben werden
public <A, B> Cons<Pair<A, B>> zip(Cons(x, xs), Cons(y, ys)) {
public zip(Cons(x, xs), Cons(y, ys)) {
return new Cons<>(new Pair<>(x, y), zip(xs, ys));
}
public zip(Empty(), Empty()) { return new Empty<>(); }
/*public zip(Empty x, Cons y) { return new Empty(); }
public zip(Cons x, Empty y) { return new Empty(); }
public zip(Empty x, Empty y) { return new Empty(); }
*/
/*
Generiert:
Cons zip<T>(Cons(T x, Cons xs), Cons(T y, Cons ys))
Cons zip<T>(Cons(T x, Cons xs), Cons(T y, Empty ys))
Cons zip<T>(Cons(T x, Empty xs), Cons(T y, Cons ys))
Cons zip<T>(Cons(T x, Empty xs), Cons(T y, Empty ys))
*/
}
@@ -1,12 +0,0 @@
import java.lang.Integer;
import Matcher;
import Interface;
public class M implements Matcher {}
public class PatternMatchingInterface {
public static match(matcher, v) {
return matcher.match(v);
}
}
@@ -1,55 +0,0 @@
import java.lang.Integer;
import java.lang.String;
import java.lang.System;
import java.lang.Boolean;
import java.io.PrintStream;
import LazyList;
import Cons;
import Empty;
import Filter;
public class Primzahlen {
static LazyList from(Integer i) { return new Cons(i, () -> from(i+1)); }
/*LazyList filter(Fun1$$<Integer, Boolean> p, LazyList l) {
return switch(l) {
case Empty e -> e;
case Cons(Integer x, Fun0$$<LazyList> l1) ->
p.apply(x) ? new Cons(x, () -> filter(p, l1.apply())) : filter(p, l1.apply());
};
}*/
LazyList dropMul(Integer x, LazyList xs) {
return new Filter().filter(y -> (y % x != 0), xs);
}
LazyList del(LazyList xs) {
return new Cons(xs.fst(), () -> del(dropMul(xs.fst(), xs.rest())));
}
LazyList primes() {
return del(from(2));
}
LazyList take(Integer n, LazyList l) {
if (n == 0) return new Empty();
else return switch (l) {
case Empty() -> l;
case Cons(Integer x, l1) ->
new Cons(x, () -> take(n-1, l1.apply()));
};
};
public static void main(args) {
System.out.println(new Cons(1, () -> new Cons(2, () -> new Empty())).fst());
var l = new Cons(1, () -> new Cons(2, () -> new Cons(2, () -> new Empty())));
System.out.println(l);
var filtered = new Filter().filter(x -> x == 2, l);
System.out.println(filtered);
//System.out.println(from(1).rest().fst());
//Primzahlen pz = new Primzahlen();
//System.out.println(pz.take(10, pz.from(2)));
//System.out.println(pz.take(14, pz.primes()));
}
}
@@ -1,6 +0,0 @@
import java.lang.Integer;
public record RecordTestInstanceVariables(Integer a, Integer b) {
public Integer getA() { return a; }
public Integer getB() { return b; }
}
+17 -19
View File
@@ -1,25 +1,23 @@
import java.util.List;
import java.util.ArrayList;
import java.lang.Integer;
import java.lang.String;
public class Sorting {
List<Integer> merge(List<Integer> a, List<Integer> b) {
var r = new ArrayList<>();
for (var i = 0, j = 0; i < a.size() || j < b.size();)
if (j == b.size() || (i < a.size() && a.get(i) <= b.get(j)))
r.add(a.get(i++));
else r.add(b.get(j++));
return r;
public class Sorting{
merge(a, b){
a.addAll(b);
return a;
}
split(list) {
var mid = list.size() / 2;
return List.of(list.subList(0, mid), list.subList(mid, list.size()));
}
public sort(in) {
if (in.size() <= 1) return in;
var halves = split(in);
return merge(sort(halves.get(0)), sort(halves.get(1)));
}
sort(in){
var firstHalf = in;
var secondHalf = in;
return merge(sort(firstHalf), sort(secondHalf));
}
/*
void sort(a){
a = merge(a,a);
}
*/
}
@@ -1,7 +0,0 @@
public class StaticFail {
void nonStaticM() {}
static void staticM() {
nonStaticM();
}
}
@@ -41,15 +41,6 @@ public class Codegen {
protected ClassLoader getClassLoader() {
return compiler.getClassLoader();
}
@Override
protected String getCommonSuperClass(String type1, String type2) {
var t1 = new TargetRefType(type1.replaceAll("/", "."));
var t2 = new TargetRefType(type2.replaceAll("/", "."));
var common = ASTToTargetAST.getCommonSuperType(t1, t2, compiler);
return common.getClassName().toString().replaceAll("\\.", "/");
//return super.getCommonSuperClass(type1, type2);
}
}
public Codegen(TargetStructure clazz, JavaTXCompiler compiler, ASTToTargetAST converter) {
@@ -253,16 +244,10 @@ public class Codegen {
if (source.equals(dest))
return;
if (/*isFunctionalInterface(source) &&*/ isFunctionalInterface(dest) && !source.equals(dest)) {
if (source instanceof TargetFunNType funs && dest instanceof TargetFunNType fund) {
if (funs.funNParams().size() == fund.funNParams().size() && !fund.isInterface()) {
boxFunctionalInterface(state, source, dest);
return;
}
} else {
boxFunctionalInterface(state, source, dest);
return;
}
if (isFunctionalInterface(source) && isFunctionalInterface(dest) &&
!(source instanceof TargetFunNType && dest instanceof TargetFunNType)) {
boxFunctionalInterface(state, source, dest);
return;
}
var mv = state.mv;
@@ -351,7 +336,7 @@ public class Codegen {
mv.visitTypeInsn(NEW, className);
mv.visitInsn(DUP_X1);
mv.visitInsn(SWAP);
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Ljava/lang/Object;)V", false);
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(" + source.toDescriptor() + ")V", false);
}
private boolean isFunctionalInterface(TargetType type) {
@@ -365,8 +350,6 @@ public class Codegen {
}
private TargetType largerType(TargetType left, TargetType right) {
if (left instanceof TargetExtendsWildcard wc) left = wc.innerType();
if (right instanceof TargetExtendsWildcard wc) right = wc.innerType();
if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
return TargetType.String;
} else if (left.equals(TargetType.Double) || right.equals(TargetType.Double)) {
@@ -830,9 +813,7 @@ public class Codegen {
private static TargetType removeGenerics(TargetType param) {
return switch (param) {
case null -> null;
case TargetFunNType funNType -> new TargetFunNType(funNType.name(),
funNType.funNParams().stream().map(Codegen::removeGenerics).toList(), List.of(),
funNType.returnArguments(), funNType.isInterface());
case TargetFunNType funNType -> new TargetFunNType(funNType.name(), funNType.funNParams().stream().map(Codegen::removeGenerics).toList(), List.of(), funNType.returnArguments());
case TargetRefType refType -> new TargetRefType(refType.name());
case TargetGenericType targetGenericType -> TargetType.Object;
default -> param;
@@ -880,8 +861,7 @@ public class Codegen {
var pattern = (TargetTypePattern) capture.pattern();
var variable = state.scope.get(pattern.name());
mv.visitVarInsn(ALOAD, variable.index);
if (!(capture.pattern().type() instanceof TargetGenericType))
mv.visitTypeInsn(CHECKCAST, capture.pattern().type().getInternalName());
mv.visitTypeInsn(CHECKCAST, capture.pattern().type().getInternalName());
}
var descriptor = TargetMethod.getDescriptor(lambda.type(), params.toArray(TargetType[]::new));
@@ -1056,7 +1036,6 @@ public class Codegen {
}
Label start = new Label();
Label skip = new Label();
Label end = new Label();
mv.visitLabel(start);
if (_for.termination() != null) {
@@ -1069,14 +1048,13 @@ public class Codegen {
}
var env = new BreakEnv();
env.startLabel = skip;
env.startLabel = start;
env.endLabel = end;
state.breakStack.push(env);
generate(state, _for.body());
state.breakStack.pop();
mv.visitLabel(skip);
if (_for.increment() != null) {
_for.increment().forEach(e -> {
generate(state, e);
@@ -1207,12 +1185,6 @@ public class Codegen {
}
case TargetMethodCall call: {
if (!call.isStatic()) {
if (call.expr() instanceof TargetThis && state.isStatic) {
throw new CodeGenException(
"Attempted to call instance method " + call.name() +
" with descriptor " + call.getDescriptor() + " from a static context"
);
}
generate(state, call.expr());
boxPrimitive(state, call.expr().type());
}
@@ -1223,14 +1195,13 @@ public class Codegen {
convertTo(state, e.type(), arg);
}
var descriptor = call.getDescriptor();
TargetType casted = call.type();
if (call.owner() instanceof TargetFunNType owner) {
// Decay FunN
descriptor = TargetMethod.getDescriptor(
(owner.returnArguments() == 0 ? null : TargetType.Object),
call.parameterTypes().stream().map(x -> TargetType.Object).toArray(TargetType[]::new));
if (owner.returnArguments() != 0)
casted = FunNGenerator.getReturnType(owner.funNParams());
}
int insn = INVOKEVIRTUAL;
if (call.isStatic()) insn = INVOKESTATIC;
else if (call.isInterface()) insn = INVOKEINTERFACE;
@@ -1244,7 +1215,7 @@ public class Codegen {
unboxPrimitive(state, call.type());
}*/
if (call.type() != null)
convertTo(state, call.returnType(), casted);
convertTo(state, call.returnType(), call.type());
break;
}
case TargetLambdaExpression lambda:
@@ -1437,16 +1408,13 @@ public class Codegen {
if (label instanceof TargetTypePattern || label instanceof TargetComplexPattern) {
if (label.type() instanceof TargetGenericType) types.add(Type.getType(Object.class));
else types.add(Type.getObjectType(label.type().getInternalName()));
} else if (label instanceof TargetExpressionPattern expr) {
if (expr.expression() instanceof TargetLiteral literal) {
types.add(Type.getObjectType(TargetType.toWrapper(literal.type()).getInternalName()));
} else {
throw new NotImplementedException();
}
} else if (label instanceof TargetLiteral lit) {
types.add(lit.value());
} else if (label instanceof TargetGuard guard) {
types.add(Type.getObjectType(guard.inner().type().getInternalName()));
// TODO Same here we need to evaluate constant;
} else {
logger.info(label);
throw new NotImplementedException();
}
}
@@ -1472,7 +1440,6 @@ public class Codegen {
for (var i = 0; i < aSwitch.cases().size(); i++) {
mv.visitLabel(caseLabels[i]);
var cse = aSwitch.cases().get(i);
state.enterScope();
if (cse.labels().size() == 1) {
var label = cse.labels().get(0);
@@ -1501,8 +1468,6 @@ public class Codegen {
if (cse.isSingleExpression() && aSwitch.isExpression())
yieldValue(state, cse.body().statements().get(0).type());
if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end);
state.exitScope();
}
mv.visitLabel(defaultLabel);
@@ -1514,9 +1479,6 @@ public class Codegen {
// throw illegal argument exception on missing default case
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
mv.visitInsn(DUP);
//mv.visitVarInsn(ALOAD, tmp);
//mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
//mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
mv.visitLdcInsn("Unhandled case value");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", false);
mv.visitInsn(ATHROW);
@@ -1551,28 +1513,9 @@ public class Codegen {
//if (pat.type() instanceof TargetPrimitiveType)
// boxPrimitive(state, pat.type());
if (pat.type() instanceof TargetSpecializedType) {
var name = pat.type().getInternalName();
//if (pat.type() instanceof TargetFunNType funNType) {
// name = FunNGenerator.getSuperClassName(funNType.funNParams().size() - 1, funNType.returnArguments());
//}
if (pat.type() instanceof TargetRefType) {
state.mv.visitInsn(DUP);
/*state.mv.visitInsn(DUP);
state.mv.visitTypeInsn(INSTANCEOF, "Fun0$$LLazyList$_$");
state.mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
state.mv.visitInsn(SWAP);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Z)V", false);
state.mv.visitInsn(DUP);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
state.mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
state.mv.visitInsn(SWAP);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
*/
state.mv.visitTypeInsn(INSTANCEOF, name);
state.mv.visitTypeInsn(INSTANCEOF, pat.type().getInternalName());
var cont = new Label();
state.mv.visitJumpInsn(IFNE, cont);
@@ -1585,7 +1528,7 @@ public class Codegen {
state.mv.visitJumpInsn(GOTO, start);
state.mv.visitLabel(cont);
state.mv.visitTypeInsn(CHECKCAST, name);
state.mv.visitTypeInsn(CHECKCAST, pat.type().getInternalName());
}
if (pat instanceof TargetExpressionPattern ep) {
@@ -1675,7 +1618,7 @@ public class Codegen {
}
private void generateStaticConstructor(TargetMethod constructor) {
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
var state = new State(null, mv, 0, true);
@@ -1780,7 +1723,7 @@ public class Codegen {
if (parent != null) {
state.scope.parent = parent.scope;
}
var offset = method.isStatic() ? 0 : 1;
var offset = 1;
for (var param : method.signature().parameters()) {
state.createVariable(param.pattern().name(), param.pattern().type());
}
@@ -1794,7 +1737,7 @@ public class Codegen {
generate(state, method.block());
if (method.signature().returnType() == null)
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitMaxs(0, 0);
}
mv.visitEnd();
}
@@ -1851,35 +1794,6 @@ public class Codegen {
clazz.constructors().forEach(this::generateConstructor);
clazz.methods().forEach(this::generateMethod);
// Check if non abstract class implements all interface methods
if ((access & ACC_ABSTRACT) == 0) {
for (var intf : clazz.implementingInterfaces()) {
var intfClass = compiler.getClass(new JavaClassName(intf.name()));
for (var method : intfClass.getMethods()) {
if ((method.modifier & ACC_ABSTRACT) != 0) {
// Check if the method is implemented in the current class
// TODO This is duplicate code, we should probably refactor instances of this pattern
var foundMethods = clazz.methods().stream().filter(m -> {
if (!m.name().equals(method.name)) return false;
if (m.signature().parameters().size() != method.getParameterList().getFormalparalist().size())
return false;
for (var i = 0; i < m.signature().parameters().size(); i++) {
var param1 = m.signature().parameters().get(i).pattern().type();
var param2 = converter.convert(
method.getParameterList().getParameterAt(i).getType(),
ASTToTargetAST.Generics.nullGenerics(compiler).javaGenerics());
if (!param1.equals(param2)) return false;
}
return true;
}).toList();
if (foundMethods.size() != 1) {
throw new CodeGenException("Class " + clazz.qualifiedName() + " does not implement method " + method.name + " from interface " + intfClass.getClassName());
}
}
}
}
}
if (clazz.staticConstructor() != null)
generateStaticConstructor(clazz.staticConstructor());
@@ -1889,82 +1803,54 @@ public class Codegen {
// Generate wrapper classes for function types
for (var pair : funWrapperClasses.keySet()) {
var className = funWrapperClasses.get(pair);
ClassWriter cw2 = new CustomClassWriter();
cw2.visit(V1_8, ACC_PUBLIC, className, null, "java/lang/Object", new String[] { pair.to.getInternalName() });
cw2.visitField(ACC_PRIVATE, "wrapped", pair.from.toDescriptor(), null, null).visitEnd();
var toName = "apply";
TargetType toReturn;
TargetType[] toParams;
if (!(pair.to instanceof TargetFunNType to)) {
var toClass = compiler.getClass(new JavaClassName(pair.to.name()));
var toMethod = toClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
toReturn = converter.convert(toMethod.getReturnType());
toParams = converter.convert(toMethod.getParameterList()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
toName = toMethod.name;
} else if (pair.from instanceof TargetFunNType from) {
toReturn = from.returnArguments() == 0 ? null : TargetType.Object;
toParams = from.funNParams().subList(0, from.funNParams().size() - 1).stream().map(x -> TargetType.Object).toArray(TargetType[]::new);
} else {
toReturn = to.returnArguments() == 0 ? null : TargetType.Object;
toParams = to.funNParams().subList(0, to.funNParams().size() - 1).stream().map(x -> TargetType.Object).toArray(TargetType[]::new);
}
var toDescriptor = TargetMethod.getDescriptor(toReturn, toParams);
var fieldDescriptor = pair.from.toDescriptor();
// Generate constructor
var ctor = cw2.visitMethod(ACC_PUBLIC, "<init>", "(" + pair.from.toDescriptor() + ")V", null, null);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitVarInsn(ALOAD, 1);
ctor.visitFieldInsn(PUTFIELD, className, "wrapped", pair.from.toDescriptor());
ctor.visitInsn(RETURN);
ctor.visitMaxs(0, 0);
ctor.visitEnd();
String methodName = "apply";
String fromDescriptor = null;
TargetType fromReturn = null;
String fromClass = pair.from.getInternalName();
if (isFunctionalInterface(pair.from) && !(pair.from instanceof TargetFunNType)) {
var clazz = compiler.getClass(new JavaClassName(pair.from.name()));
var fromMethod = clazz.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
if (!(pair.from instanceof TargetFunNType funNType)) {
var fromClass = compiler.getClass(new JavaClassName(pair.from.name()));
var fromMethod = fromClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
methodName = fromMethod.name;
fromReturn = converter.convert(fromMethod.getReturnType());
var fromParams = converter.convert(fromMethod.getParameterList()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
fromDescriptor = TargetMethod.getDescriptor(fromReturn, fromParams);
} else if (pair.from instanceof TargetFunNType funNType) {
} else {
fromReturn = funNType.returnArguments() > 0 ? TargetType.Object : null;
fromDescriptor = funNType.toMethodDescriptor();
} else {
fromDescriptor = toDescriptor;
fromReturn = toReturn;
var to = (TargetFunNType) pair.to;
fromClass = FunNGenerator.getSuperClassName(to.funNParams().size() - 1, to.returnArguments());
fieldDescriptor = "L" + fromClass + ";";
}
var superClasses = new ArrayList<>();
superClasses.add(pair.to.getInternalName());
if (isFunctionalInterface(pair.from))
superClasses.add(pair.from.getInternalName());
ClassWriter cw2 = new CustomClassWriter();
cw2.visit(V1_8, ACC_PUBLIC, className, null, "java/lang/Object", superClasses.toArray(String[]::new));
cw2.visitField(ACC_PRIVATE, "wrapped", fieldDescriptor, null, null).visitEnd();
// Generate constructor
var ctor = cw2.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitVarInsn(ALOAD, 1);
ctor.visitFieldInsn(PUTFIELD, className, "wrapped", fieldDescriptor);
ctor.visitInsn(RETURN);
ctor.visitMaxs(0, 0);
ctor.visitEnd();
var toClass = compiler.getClass(new JavaClassName(pair.to.name()));
var toMethod = toClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
var toReturn = converter.convert(toMethod.getReturnType());
var toParams = converter.convert(toMethod.getParameterList()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
var toDescriptor = TargetMethod.getDescriptor(toReturn, toParams);
// Generate wrapper method
var mv = cw2.visitMethod(ACC_PUBLIC, toName, toDescriptor, null, null);
var mv = cw2.visitMethod(ACC_PUBLIC, toMethod.name, toDescriptor, null, null);
var state = new State(null, mv, 0, false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "wrapped", fieldDescriptor);
mv.visitFieldInsn(GETFIELD, className, "wrapped", pair.from.toDescriptor());
for (var i = 0; i < toParams.length; i++) {
var arg = toParams[i];
mv.visitVarInsn(findLoadCode(arg), i + 1);
}
mv.visitMethodInsn(INVOKEINTERFACE, fromClass, methodName, fromDescriptor, true);
mv.visitMethodInsn(INVOKEINTERFACE, pair.from.getInternalName(), methodName, fromDescriptor, true);
if (fromReturn != null) {
if (toReturn instanceof TargetPrimitiveType) {
convertTo(state, fromReturn, TargetType.toWrapper(toReturn));
@@ -2025,7 +1911,7 @@ public class Codegen {
bootstrapArgs[i + 2] = fieldRef;
}
if (clazz.methods().stream().filter(m -> m.getDescriptor().equals("()I") && m.name().equals("hashCode")).findFirst().isEmpty()) { // hashCode
{ // hashCode
var mv = cw.visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
@@ -2034,7 +1920,7 @@ public class Codegen {
mv.visitMaxs(0, 0);
mv.visitEnd();
}
if (clazz.methods().stream().filter(m -> m.getDescriptor().equals("(Ljava/lang/Object;)Z") && m.name().equals("equals")).findFirst().isEmpty()) { // equals
{ // equals
var mv = cw.visitMethod(ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
@@ -2044,7 +1930,7 @@ public class Codegen {
mv.visitMaxs(0, 0);
mv.visitEnd();
}
if (clazz.methods().stream().filter(m -> m.getDescriptor().equals("()Ljava/lang/String;") && m.name().equals("toString")).findFirst().isEmpty()) { // toString
{ // toString
var mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
@@ -5,7 +5,6 @@ import de.dhbwstuttgart.bytecode.Codegen;
import de.dhbwstuttgart.bytecode.FunNGenerator;
import de.dhbwstuttgart.environment.CompilationEnvironment;
import de.dhbwstuttgart.environment.DirectoryClassLoader;
import de.dhbwstuttgart.exceptions.CompilerException;
import de.dhbwstuttgart.exceptions.CompilerWarning;
import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.WarningsException;
@@ -172,8 +171,6 @@ public class JavaTXCompiler {
var numberOfGenerics = decl == null ? 0 : decl.genericTypeVar().size();
var className = pkgName + (!pkgName.isEmpty() ? "." : "") + name;
if (classRegistry.contains(className))
throw new CompilerException("Duplicate class definition: " + className);
classRegistry.addName(className, numberOfGenerics);
input.add(classRegistry.getName(className));
}
@@ -717,9 +714,6 @@ public class JavaTXCompiler {
new SuperWildcardType(toRefType(targetSuperWildcard.innerType()), new NullToken());
case TargetGenericType targetGenericType -> new GenericRefType(targetGenericType.name(), new NullToken());
case TargetPrimitiveType targetPrimitiveType -> toRefType(TargetType.toWrapper(targetPrimitiveType));
case TargetFunNType targetFunNType ->
new RefType(new JavaClassName(FunNGenerator.getSuperClassName(targetFunNType.funNParams().size() - 1, targetFunNType.returnArguments())),
targetFunNType.funNParams().stream().map(JavaTXCompiler::toRefType).toList(), new NullToken());
case TargetSpecializedType targetSpecializedType ->
new RefType(new JavaClassName(targetSpecializedType.name()),
targetSpecializedType.params().stream().map(JavaTXCompiler::toRefType).toList(), new NullToken()
@@ -768,9 +762,9 @@ public class JavaTXCompiler {
);
}
var isInterface = target instanceof TargetInterface;
var generics = new GenericDeclarationList(toGenerics(target.generics()), new NullToken());
var superClass = isInterface ? ASTFactory.createObjectType() : (RefType) toRefType(target.superType());
var superClass = (RefType) toRefType(target.superType());
var isInterface = target instanceof TargetInterface;
var isFunctionalInterface = false; // TODO We might actually want to generate those
var implementedInterfaces = target.implementingInterfaces().stream()
.map(t -> (RefType) toRefType(t)).toList();
@@ -1,7 +0,0 @@
package de.dhbwstuttgart.exceptions;
public class CompilerException extends RuntimeException {
public CompilerException(String message) {
super(message);
}
}
@@ -3,6 +3,6 @@ package de.dhbwstuttgart.exceptions;
public class DebugException extends RuntimeException {
public DebugException(String message) {
super(message);
System.err.print(message);
}
}
@@ -2,8 +2,9 @@ package de.dhbwstuttgart.exceptions;
import org.antlr.v4.runtime.Token;
public class ParserError extends RuntimeException{
public ParserError(String message) {
super(message);
public class ParserError extends TypeinferenceException{
public ParserError(Token offset){
super("Fehler beim Parsen", offset);
}
}
@@ -1,7 +1,6 @@
package de.dhbwstuttgart.parser;
import de.dhbwstuttgart.environment.CompilationEnvironment;
import de.dhbwstuttgart.exceptions.ParserError;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.SyntaxTreeGenerator;
import de.dhbwstuttgart.parser.antlr.Java17Lexer;
import de.dhbwstuttgart.parser.antlr.Java17Parser;
@@ -9,7 +8,9 @@ import de.dhbwstuttgart.parser.scope.JavaClassRegistry;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.util.Logger;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import java.io.*;
import java.nio.charset.StandardCharsets;
@@ -20,25 +21,13 @@ public class JavaTXParser {
public static Logger logger = new Logger("Parser");
public static class ThrowingErrorListener extends BaseErrorListener {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
int line, int charPositionInLine, String msg, RecognitionException e) {
throw new ParserError("Syntax error at line " + line + ":" + charPositionInLine + " - " + msg);
}
}
public static Java17Parser.SourceFileContext parse(File source) throws IOException, java.lang.ClassNotFoundException {
InputStream stream = new FileInputStream(source);
// DEPRECATED: ANTLRInputStream input = new ANTLRInputStream(stream);
var errorListener = new ThrowingErrorListener();
CharStream input = CharStreams.fromStream(stream);
Java17Lexer lexer = new Java17Lexer(input);
lexer.addErrorListener(errorListener);
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java17Parser parser = new Java17Parser(tokens);
parser.addErrorListener(errorListener);
return parser.sourceFile();
/*
* SyntaxTreeGenerator generator = new SyntaxTreeGenerator(environment.getRegistry(source)); return generator.convert(tree);
@@ -15,7 +15,6 @@ import de.dhbwstuttgart.typeinference.constraints.Pair;
import de.dhbwstuttgart.typeinference.unify.PlaceholderRegistry;
import de.dhbwstuttgart.typeinference.unify.model.*;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
@@ -55,8 +54,7 @@ public class FCGenerator {
private static Boolean isFunctionalInterface(ClassOrInterface cly) {
// TODO isFunctionalInterface should be set properly, we shouldn't need to check it again
return (cly.isInterface() && (cly.isFunctionalInterface() || cly.getMethods().size() == 1 && Modifier.isAbstract(cly.getMethods().getFirst().modifier)));
return (cly.isInterface() && (cly.isFunctionalInterface() || cly.getMethods().size() == 1));
}
private static Pair genImplFunType(ClassOrInterface cly, RefTypeOrTPHOrWildcardOrGeneric fIType, HashMap<String, RefTypeOrTPHOrWildcardOrGeneric> gtvs) {
@@ -124,7 +122,7 @@ public class FCGenerator {
List<RefType> superClasses = new ArrayList<>();
superClasses.add(forType.getSuperClass());
superClasses.addAll(forType.getSuperInterfaces());
List<Pair> retList = new ArrayList<>();
for(RefType superType : superClasses){
Optional<ClassOrInterface> hasSuperclass = availableClasses.stream().filter(cl -> superType.getName().equals(cl.getClassName())).findAny();
@@ -405,16 +405,14 @@ public class StatementGenerator {
private SwitchBlock convert(Java17Parser.SwitchBlockStatementGroupContext stmt) {
List<SwitchLabel> labels = new ArrayList<>();
var isDefault = false;
for (var label : stmt.switchLabel()) {
if (label instanceof SwitchLabelDefaultContext) isDefault = true;
stmt.switchLabel().forEach((label) -> {
labels.add(convert(label));
}
});
List<Statement> block = new ArrayList<>();
stmt.blockStatement().stream().forEach((blockStmt) -> {
block.addAll(convert(blockStmt));
});
return new SwitchBlock(labels, new Block(block, stmt.blockStatement(0).getStart()), isDefault,false, stmt.getStart());
return new SwitchBlock(labels, new Block(block, stmt.blockStatement(0).getStart()), false, stmt.getStart());
}
private SwitchLabel convert(SwitchLabelContext switchLabel) {
@@ -484,8 +482,8 @@ public class StatementGenerator {
IdentifierContext identifierCtx = recordPatternCtx.identifier();
var text = (identifierCtx != null) ? identifierCtx.getText() : null;
//Hier evtl. Typ anpassen -> wenn kein Typ bekannt ist push neuen Typ auf Hashtable
var type = recordPatternCtx.type == null ? TypePlaceholder.fresh(recordPatternCtx.getStart()) : TypeGenerator.convert(recordPatternCtx.type, reg, generics);
var ctor = TypeGenerator.convert(recordPatternCtx.ctor, reg, generics);
var type = recordPatternCtx.type == null ? ctor : TypeGenerator.convert(recordPatternCtx.type, reg, generics);
if (text != null) localVars.put(text, type);
var ret = new RecordPattern(subPattern, text, type, (RefType)ctor, recordPatternCtx.getStart());
return ret;
@@ -984,11 +982,8 @@ public class StatementGenerator {
} else if (op.getText().equals("!")) {
ret = new UnaryExpr(UnaryExpr.Operation.NOT, expr, TypePlaceholder.fresh(op), op);
return ret;
} else if (op.getText().equals("-")) {
ret = new UnaryExpr(UnaryExpr.Operation.MINUS, expr, TypePlaceholder.fresh(op), op);
return ret;
} else {
throw new NotImplementedException(op.getText());
throw new NotImplementedException();
}
}
@@ -1035,22 +1030,10 @@ public class StatementGenerator {
private Expression convert(Java17Parser.LiteralContext literal) {
switch (literal) {
case IntLiteralContext intliteral:
var text = intliteral.getText();
var radix = 10;
if (text.startsWith("0x") || text.startsWith("0X")) {
radix = 16;
text = text.substring(2);
} else if (text.startsWith("0b") || text.startsWith("0B")) {
radix = 2;
text = text.substring(2);
} else if (text.startsWith("0") && text.length() > 1) {
radix = 8;
text = text.substring(1);
}
Number value;
if (text.endsWith("l") || text.endsWith("L"))
value = Long.parseLong(text.substring(0, text.length() - 1), radix);
else value = Integer.parseInt(text, radix);
if (intliteral.getText().endsWith("l") || intliteral.getText().endsWith("L"))
value = Long.parseLong(intliteral.getText().substring(0, intliteral.getText().length() - 1));
else value = Integer.parseInt(intliteral.getText());
return new Literal(TypePlaceholder.fresh(literal.getStart()), value, intliteral.getStart());
case FltLiteralContext floatliteral:
if (floatliteral.getText().endsWith("f") || floatliteral.getText().endsWith("F"))
@@ -80,7 +80,7 @@ public class SyntaxTreeGenerator {
HashMap<String, Integer> allmodifiers = new HashMap<>();
// PL 2018-11-01 fields eingefuegt, damit die fields immer die gleiche TPH
// bekommen
//private final Map<String, FieldEntry> fields = new HashMap<>();
private final Map<String, FieldEntry> fields = new HashMap<>();
// PL 2019-10-23: Muss für jede Klasse neu initilisiert werden
List<Statement> fieldInitializations = new ArrayList<>();
List<Statement> staticFieldInitializations = new ArrayList<>();
@@ -178,7 +178,6 @@ public class SyntaxTreeGenerator {
}
private ClassOrInterface convertClass(Java17Parser.ClassDeclarationContext ctx, int modifiers) {
var fieldDecls = new HashMap<String, FieldEntry>();
String className = this.pkgName + (this.pkgName.length() > 0 ? "." : "") + ctx.identifier().getText();
JavaClassName name = reg.getName(className); // Holt den Package Namen mit dazu
if (!name.toString().equals(className)) { // Kommt die Klasse schon in einem anderen Package vor?
@@ -207,7 +206,7 @@ public class SyntaxTreeGenerator {
List<RefType> implementedInterfaces = new ArrayList<>();
List<RefType> permittedSubtypes = null;
for (ClassBodyDeclarationContext clsbodydecl : ctx.classBody().classBodyDeclaration()) {
convert(clsbodydecl, fielddecl, constructors, methods, name, superClass, generics, fieldDecls);
convert(clsbodydecl, fielddecl, constructors, methods, name, superClass, generics);
}
if (constructors.isEmpty()) {
constructors.add(generateStandardConstructor(ctx.identifier().getText(), name, superClass, genericClassParameters, offset));
@@ -256,8 +255,6 @@ public class SyntaxTreeGenerator {
List<Pattern> constructorParameters = new ArrayList<>();
List<Statement> constructorStatements = new ArrayList<>();
var fieldDecl = new HashMap<String, FieldEntry>();
List<Java17Parser.RecordComponentContext> components = recordDeclaration.recordHeader().recordComponentList() != null ?
recordDeclaration.recordHeader().recordComponentList().recordComponent(): List.of();
for (RecordComponentContext component : components) {
@@ -273,7 +270,6 @@ public class SyntaxTreeGenerator {
fielddecl.add(new Field(fieldname, fieldtype, fieldmodifiers, fieldoffset));
constructorParameters.add(new FormalParameter(fieldname, fieldtype, fieldoffset));
FieldVar fieldvar = new FieldVar(new This(offset), fieldname, fieldtype, fieldoffset);
fieldDecl.put(fieldname, new FieldEntry(fieldname, fieldtype, Modifier.PRIVATE));
constructorStatements.add(new Assign(new AssignToField(fieldvar), new LocalVar(fieldname, fieldtype, fieldoffset), offset));
Statement returnStatement = new Return(fieldvar, offset);
methods.add(new Method(allmodifiers.get("public"), fieldname, fieldtype, new ParameterList(new ArrayList<>(), offset), new Block(Arrays.asList(returnStatement), offset), new GenericDeclarationList(new ArrayList<>(), offset), offset));
@@ -283,7 +279,7 @@ public class SyntaxTreeGenerator {
//Optional<Constructor> initializations = Optional.of(implicitConstructor);
constructors.add(implicitConstructor);
for (ClassBodyDeclarationContext bodyDeclaration : recordDeclaration.recordBody().classBodyDeclaration()) {
convert(bodyDeclaration, fielddecl, constructors, methods, name, superClass, generics, fieldDecl);
convert(bodyDeclaration, fielddecl, constructors, methods, name, superClass, generics);
}
if (!Objects.isNull(recordDeclaration.IMPLEMENTS())) {
implementedInterfaces.addAll(convert(recordDeclaration.typeList(), generics));
@@ -292,7 +288,7 @@ public class SyntaxTreeGenerator {
return new Record(modifiers, name, fielddecl, Optional.empty(), staticCtor, methods, constructors, genericClassParameters, superClass, isInterface, implementedInterfaces, offset, fileName);
}
private void convert(ClassBodyDeclarationContext classBody, List<Field> fields, List<Constructor> constructors, List<Method> methods, JavaClassName name, RefType superClass, GenericsRegistry generics, HashMap<String, FieldEntry> fieldDecls) {
private void convert(ClassBodyDeclarationContext classBody, List<Field> fields, List<Constructor> constructors, List<Method> methods, JavaClassName name, RefType superClass, GenericsRegistry generics) {
MemberdeclContext member;
if (classBody instanceof MemberdeclContext) {
member = (MemberdeclContext) classBody;
@@ -307,11 +303,11 @@ public class SyntaxTreeGenerator {
break;
}
case MemberfieldContext memberfield: {
fields.addAll(convert(memberfield.fieldDeclaration(), membermodifiers, generics, fieldDecls));
fields.addAll(convert(memberfield.fieldDeclaration(), membermodifiers, generics));
break;
}
case MembermethodContext membermethod: {
Method convertedMethod = convert(membermodifiers, membermethod.method(), name, superClass, generics, fieldDecls);
Method convertedMethod = convert(membermodifiers, membermethod.method(), name, superClass, generics);
if (convertedMethod instanceof Constructor constructor) {
constructors.add(constructor);
} else {
@@ -320,7 +316,7 @@ public class SyntaxTreeGenerator {
break;
}
case MemberconstructorContext memberconstructor: {
constructors.add(convert(membermodifiers, memberconstructor.constructor(), name, superClass, generics, fieldDecls));
constructors.add(convert(membermodifiers, memberconstructor.constructor(), name, superClass, generics));
break;
}
default:
@@ -328,7 +324,7 @@ public class SyntaxTreeGenerator {
}
} else if (classBody instanceof Java17Parser.ClassblockContext ctx && ctx.STATIC() != null) {
// Static blocks
var stmtgen = new StatementGenerator(superClass, compiler, reg, generics, fieldDecls, new HashMap<>());
var stmtgen = new StatementGenerator(superClass, compiler, reg, generics, this.fields, new HashMap<>());
var block = stmtgen.convert(((Java17Parser.ClassblockContext) classBody).block(), false);
staticFieldInitializations.addAll(block.statements);
}
@@ -409,9 +405,8 @@ public class SyntaxTreeGenerator {
}
}
boolean isFunctonalInterfce = methods.size() == 1 && Modifier.isAbstract(methods.getFirst().modifier);
var staticCtor = Optional.of(this.generateStaticConstructor(ctx.identifier().getText(), staticFieldInitializations, genericParams, ctx.getStart()));
return new ClassOrInterface(modifiers, name, fields, Optional.empty(), staticCtor, methods, new ArrayList<>(), genericParams, superClass, true, isFunctonalInterfce, extendedInterfaces, permittedSubtypes, ctx.getStart(), fileName);
return new ClassOrInterface(modifiers, name, fields, Optional.empty(), staticCtor, methods, new ArrayList<>(), genericParams, superClass, true, methods.size() == 1 ? true : false, extendedInterfaces, permittedSubtypes, ctx.getStart(), fileName);
}
private GenericDeclarationList createEmptyGenericDeclarationList(Token classNameIdentifier) {
@@ -439,7 +434,7 @@ public class SyntaxTreeGenerator {
retType = new Void(bodydeclaration.refType().getStart());
}
}
StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, generics, new HashMap<>(), new HashMap<>());
StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, generics, fields, new HashMap<>());
ParameterList paramlist = stmtgen.convert(bodydeclaration.formalParameters().formalParameterList(), true);
MethodBodyContext body = bodydeclaration.methodBody();
Block block = null;
@@ -512,7 +507,7 @@ public class SyntaxTreeGenerator {
return ret;
}
public Method convert(int modifiers, Java17Parser.MethodContext methodContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics, Map<String, FieldEntry> fieldDecls) {
public Method convert(int modifiers, Java17Parser.MethodContext methodContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics) {
GenericsRegistry localgenerics = generics;
MethodDeclarationContext methoddeclaration;
GenericDeclarationListContext genericdeclarations;
@@ -545,7 +540,7 @@ public class SyntaxTreeGenerator {
retType = new Void(header.refType().getStart());
}
}
StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, localgenerics, fieldDecls, new HashMap<>());
StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, localgenerics, fields, new HashMap<>());
ParameterList paramlist = stmtgen.convert(header.formalParameters().formalParameterList(), true);
MethodBodyContext body = methoddeclaration.methodBody();
Block block = null;
@@ -564,7 +559,7 @@ public class SyntaxTreeGenerator {
}
}
public Constructor convert(int modifiers, Java17Parser.ConstructorContext constructorContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics, Map<String, FieldEntry> fields) {
public Constructor convert(int modifiers, Java17Parser.ConstructorContext constructorContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics) {
GenericsRegistry localgenerics = generics;
GenericDeclarationListContext genericdeclarations;
GenericDeclarationList gtvDeclarations;
@@ -591,7 +586,7 @@ public class SyntaxTreeGenerator {
return new Constructor(modifiers, name, retType, paramlist, block, gtvDeclarations, constructordeclaration.getStart());
}
List<? extends Field> convert(Java17Parser.FieldDeclarationContext fieldDeclContext, int modifiers, GenericsRegistry generics, HashMap<String, FieldEntry> fields) {
List<? extends Field> convert(Java17Parser.FieldDeclarationContext fieldDeclContext, int modifiers, GenericsRegistry generics) {
List<Field> ret = new ArrayList<>();
RefTypeOrTPHOrWildcardOrGeneric fieldType;
if (fieldDeclContext.typeType() != null) {
@@ -603,9 +598,9 @@ public class SyntaxTreeGenerator {
}
for (Java17Parser.VariableDeclaratorContext varDecl : fieldDeclContext.variableDeclarators().variableDeclarator()) {
String fieldName = varDecl.variableDeclaratorId().getText();
fields.put(fieldName, new FieldEntry(fieldName, fieldType, modifiers));
this.fields.put(fieldName, new FieldEntry(fieldName, fieldType, modifiers));
if (varDecl.variableInitializer() != null) {
initializeField(varDecl, Modifier.isStatic(modifiers), fieldType, generics, fields);
initializeField(varDecl, Modifier.isStatic(modifiers), fieldType, generics);
}
ret.add(new Field(fieldName, fieldType, modifiers, varDecl.getStart()));
}
@@ -617,7 +612,7 @@ public class SyntaxTreeGenerator {
}
// Initialize a field by creating implicit constructor.
private void initializeField(Java17Parser.VariableDeclaratorContext ctx, boolean isStatic, RefTypeOrTPHOrWildcardOrGeneric typeOfField, GenericsRegistry generics, Map<String, FieldEntry> fields) {
private void initializeField(Java17Parser.VariableDeclaratorContext ctx, boolean isStatic, RefTypeOrTPHOrWildcardOrGeneric typeOfField, GenericsRegistry generics) {
StatementGenerator statementGenerator = new StatementGenerator(superClass, compiler, reg, generics, fields, new HashMap<>());
var assignment = statementGenerator.generateFieldAssignment(ctx, typeOfField);
if (isStatic) {
@@ -45,10 +45,6 @@ public class MethodCall extends Statement
return signature.subList(0, signature.size() - 1);
}
public TypePlaceholder returnType() {
return signature.getLast();
}
@Override
public void accept(StatementVisitor visitor) {
@@ -1,8 +1,6 @@
package de.dhbwstuttgart.target.generate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import de.dhbwstuttgart.bytecode.CodeGenException;
import de.dhbwstuttgart.bytecode.FunNGenerator;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
@@ -23,13 +21,8 @@ import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*;
import de.dhbwstuttgart.typeinference.unify.MartelliMontanariUnify;
import de.dhbwstuttgart.typeinference.unify.model.*;
import de.dhbwstuttgart.util.Logger;
import org.antlr.v4.runtime.Token;
import org.objectweb.asm.Opcodes;
import java.lang.classfile.Opcode;
import java.lang.reflect.AccessFlag;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -58,10 +51,6 @@ public class ASTToTargetAST {
private Method currentMethod;
public Method getCurrentMethod() {
return currentMethod;
}
public final JavaTXCompiler compiler;
public List<RefTypeOrTPHOrWildcardOrGeneric> findAllVariants(RefTypeOrTPHOrWildcardOrGeneric type) {
@@ -87,8 +76,8 @@ public class ASTToTargetAST {
this(new JavaGenerics(compiler, set), new TxGenerics(compiler, set));
}
public static Generics nullGenerics(JavaTXCompiler compiler) {
return new Generics(compiler, new ResultSet(Set.of()));
public static Generics nullGenerics() {
return new Generics(null, new ResultSet(Set.of()));
}
}
@@ -120,38 +109,14 @@ public class ASTToTargetAST {
}
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, JavaTXCompiler compiler) {
return findMethod(owner, name, argumentList, Generics.nullGenerics(compiler).javaGenerics(), compiler);
return findMethod(owner, name, argumentList, Generics.nullGenerics().javaGenerics(), compiler);
}
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) {
Optional<Method> method = Optional.empty();
while (method.isEmpty()) {
var methods = owner.getMethods().stream().filter(m -> {
if (!m.name.equals(name)) return false;
var plist = m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList();
return parameterEquals(plist, argumentList, compiler);
}).toList();
if (!methods.isEmpty()) {
// Find the most specific method
var resultMethods = new ArrayList<Method>();
outer: for (var ma : methods) {
for (Method mb : methods) {
for (var k = 0; k < ma.getParameterList().getFormalparalist().size(); k++) {
var parama = generics.getTargetType(ma.getParameterList().getParameterAt(k).getType());
var paramb = generics.getTargetType(mb.getParameterList().getParameterAt(k).getType());
if (!Objects.equals(parama, paramb) && isFunOrSubtype(paramb, parama, compiler)) {
continue outer;
}
}
}
resultMethods.add(ma);
}
if (resultMethods.isEmpty()) method = Optional.of(methods.getFirst());
else method = Optional.of(resultMethods.getFirst());
}
method = owner.getMethods().stream().filter(m -> m.name.equals(name) &&
parameterEquals(m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
if (owner.getClassName().toString().equals("java.lang.Object")) break;
owner = compiler.getClass(owner.getSuperClass().getName());
}
@@ -160,55 +125,27 @@ public class ASTToTargetAST {
Optional<Constructor> findConstructor(ClassOrInterface owner, List<TargetType> argumentList, IGenerics generics) {
return owner.getConstructors().stream().filter(c ->
parameterEquals(c.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList, compiler)).findFirst();
parameterEquals(c.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
}
static boolean parameterEquals(List<TargetType> pars, List<TargetType> arguments, JavaTXCompiler compiler) {
static boolean parameterEquals(List<TargetType> pars, List<TargetType> arguments) {
if (pars.size() != arguments.size())
return false;
for (var i = 0; i < pars.size(); i++) {
var type1 = pars.get(i);
var type2 = arguments.get(i);
if (type1 instanceof TargetGenericType || type2 instanceof TargetGenericType)
continue;
if (type1 instanceof TargetGenericType)
return true;
if (TargetType.toPrimitive(type2).equals(type1))
continue;
if (type1 instanceof TargetFunNType tfun1 && type2 instanceof TargetFunNType tfun2) {
if (!funNIsSubtype(tfun2, tfun1, compiler)) return false;
} else if (!type1.equals(type2)) return false;
return true;
if (!type1.equals(type2))
return false;
}
return true;
}
private static boolean isFunOrSubtype(TargetType from, TargetType to, JavaTXCompiler compiler) {
if (to instanceof TargetGenericType) return true;
if (from instanceof TargetFunNType ffun && to instanceof TargetFunNType tfun)
return funNIsSubtype(ffun, tfun, compiler);
return isSubtype(from, to, compiler);
}
private static boolean funNIsSubtype(TargetFunNType from, TargetFunNType to, JavaTXCompiler compiler) {
if (!from.isInterface() && !to.isInterface()) {
if (from.funNParams().size() != to.funNParams().size()) return false;
for (var i = 0; i < from.funNParams().size() - from.returnArguments(); i++) {
var left = from.funNParams().get(i);
var right = to.funNParams().get(i);
if (!Objects.equals(left, right) && isFunOrSubtype(right, left, compiler))
return false;
}
if (from.returnArguments() != 0) {
var ret1 = from.funNParams().getLast();
var ret2 = to.funNParams().getLast();
if (Objects.equals(ret1, ret2)) return true;
return isFunOrSubtype(ret1, ret2, compiler);
}
return true;
}
return from.name().equals(to.name());
}
Set<TargetGeneric> convert(Set<GenerateGenerics.Pair> result, IGenerics generics) {
return result.stream().map(p -> {
if (p instanceof GenerateGenerics.PairLT pair) {
@@ -235,7 +172,7 @@ public class ASTToTargetAST {
case TargetSuperWildcard targetSuperWildcard -> new SuperType(toUnifyType(targetSuperWildcard.innerType()));
case TargetGenericType targetGenericType -> new PlaceholderType(targetGenericType.name(), JavaTXCompiler.defaultClientPlaceholderRegistry);
case TargetPrimitiveType _ -> throw new NotImplementedException();
case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.funNParams().stream().map(ASTToTargetAST::toUnifyType).toList()));
case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.params().stream().map(ASTToTargetAST::toUnifyType).toList()));
case TargetRefType targetRefType -> new ReferenceType(targetRefType.name(), new TypeParams(targetRefType.params().stream().map(ASTToTargetAST::toUnifyType).toList()));
};
}
@@ -278,12 +215,9 @@ public class ASTToTargetAST {
private static Optional<TargetType> unify(TargetType a, TargetType b) {
if (typesStrictlyEqual(a, b)) return Optional.ofNullable(a);
if (a instanceof TargetFunNType || b instanceof TargetFunNType) return Optional.empty();
var unify = new MartelliMontanariUnify();
var ua = toUnifyType(a);
var ub = toUnifyType(b);
if (Objects.equals(ua, ub)) return Optional.of(a);
var unifier = unify.unify(Set.of(ua, ub));
var unifier = unify.unify(Set.of(ua, toUnifyType(b)));
if (unifier.isEmpty()) return Optional.empty();
return Optional.of(toTargetType(unifier.get().apply(ua)));
}
@@ -297,6 +231,7 @@ public class ASTToTargetAST {
// Strip off patterns, we don't need them for merged methods, they do a switch case
result.add(new MethodParameter(u.get(), a.get(i).pattern().name()));
}
return Optional.of(result);
}
@@ -396,16 +331,15 @@ public class ASTToTargetAST {
var u_opt = unify(m, m1);
if (u_opt.isPresent()) {
var u = u_opt.get();
//System.out.println("Unified " + m + " AND " + m1 + "\n\t" + u);
//Target.logger.info("Unified " + m + " AND " + m1 + "\n\t" + u);
i.remove(m1);
R.remove(m);
R.remove(m1);
R.add(u);
a.add(u);
m = u;
} else {
//System.out.println("Couldn't unify " + m + " AND " + m1);
}
} /*else {
Target.logger.info("Couldn't unify " + m + " AND " + m1);
}*/
}
}
}
@@ -418,8 +352,6 @@ public class ASTToTargetAST {
return R;
}
private record CtorWithGenerics(TargetConstructor ctor, IGenerics generics) {}
public TargetStructure convert(ClassOrInterface input) {
var generics = all.getFirst();
Set<TargetGeneric> javaGenerics = new HashSet<>();
@@ -449,33 +381,8 @@ public class ASTToTargetAST {
TargetBlock finalFieldInitializer = fieldInitializer;
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).toList();
var constructors = new ArrayList<TargetConstructor>();
for (var ctor : input.getConstructors()) {
var generated = new ArrayList<CtorWithGenerics>();
for (var g : all) {
try {
generated.add(new CtorWithGenerics(this.convert(input, ctor, finalFieldInitializer, g), g.javaGenerics));
} catch (DiscardResultSet ignored) {}
}
constructors.add(generated.getFirst().ctor);
if (generated.size() > 1) {
var first = generated.getFirst();
for (var next : generated.subList(1, generated.size())) {
if (typesAreDifferent(ctor, first.generics, next.generics)) {
compiler.warn(new CompilerWarning(ctor.block.getOffset(), "Duplicate Constructor definition"));
}
}
}
}
var fields = input.getFieldDecl().stream().map(f -> {
var possibleTypes = new HashSet<TargetType>();
for (var g : all) possibleTypes.add(convert(f.getType(), g.javaGenerics(), compiler));
if (possibleTypes.size() > 1)
compiler.warn(new CompilerWarning(f.getOffset(), "Multiple possible types for field " + f.getName() + ": " + possibleTypes + " please select one"));
return convert(f, generics.javaGenerics);
}).toList();
var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer, generics)).flatMap(List::stream).toList();
var fields = input.getFieldDecl().stream().map(f -> convert(f, generics.javaGenerics)).toList();
var m0 = groupMethods(input, input.getMethods());
var m1 = new ArrayList<TargetMethod>();
@@ -539,7 +446,7 @@ public class ASTToTargetAST {
var res = new ArrayList<MethodParameter>();
for (var i = 0; i < input.getFormalparalist().size(); i++) {
var param = input.getFormalparalist().get(i);
var pattern = (TargetPattern) convert(param, Generics.nullGenerics(compiler).javaGenerics);
var pattern = (TargetPattern) convert(param, Generics.nullGenerics().javaGenerics);
if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i);
res.add(new MethodParameter(pattern));
}
@@ -580,19 +487,29 @@ public class ASTToTargetAST {
return convertedGenerics;
}
private TargetConstructor convert(ClassOrInterface currentClass, Constructor input, TargetBlock fieldInitializer, Generics generics) {
private List<TargetConstructor> convert(ClassOrInterface currentClass, Constructor input, TargetBlock fieldInitializer, Generics generics) {
generics = all.get(0);
List<TargetConstructor> result = new ArrayList<>();
Set<List<MethodParameter>> parameterSet = new HashSet<>();
this.currentMethod = input;
this.usedTPHsOfMethods.put(input, new HashSet<>());
var javaGenerics = generics.javaGenerics.generics(currentClass, input);
var txGenerics = generics.txGenerics.generics(currentClass, input);
List<MethodParameter> params = convert(input.getParameterList(), generics.javaGenerics);
List<MethodParameter> txParams = convert(input.getParameterList(), generics.txGenerics);
var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, input);
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, input);
for (var s : all) {
generics = s;
var javaGenerics = generics.javaGenerics.generics(currentClass, input);
var txGenerics = generics.txGenerics.generics(currentClass, input);
List<MethodParameter> params = convert(input.getParameterList(), generics.javaGenerics);
if (parameterSet.stream().noneMatch(p -> p.equals(params))) {
List<MethodParameter> txParams = convert(input.getParameterList(), generics.txGenerics);
var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, input);
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, input);
this.currentMethod = null;
return new TargetConstructor(input.modifier, javaMethodGenerics, txMethodGenerics, params, txParams, convert(input.block, generics.javaGenerics), fieldInitializer);
result.add(new TargetConstructor(input.modifier, javaMethodGenerics, txMethodGenerics, params, txParams, convert(input.block, generics.javaGenerics), fieldInitializer));
parameterSet.add(params);
}
}
return result;
}
private static int counter = 0;
@@ -631,7 +548,7 @@ public class ASTToTargetAST {
mParams,
classType,
method.name(),
method.isStatic(), false, method.isPrivate()
false, false, method.isPrivate()
);
if (method.signature().returnType() != null) {
caseBody = new TargetReturn(caseBody);
@@ -639,7 +556,7 @@ public class ASTToTargetAST {
return caseBody;
}
private TargetExpression generatePatternOverloadsRec(int offset, TargetExpression switchExpr, List<TargetLocalVar> params, List<TargetPattern> patterns, List<TargetMethod> methods, TargetType classType) {
private static TargetExpression generatePatternOverloadsRec(int offset, TargetExpression switchExpr, List<TargetLocalVar> params, List<TargetPattern> patterns, List<TargetMethod> methods, TargetType classType) {
if (methods.isEmpty()) throw new DebugException("Couldn't find a candidate for switch overloading");
if (methods.size() == 1) {
var method = methods.getFirst();
@@ -649,17 +566,6 @@ public class ASTToTargetAST {
var cases = new ArrayList<TargetSwitch.Case>();
var usedPatterns = new HashSet<TargetPattern>();
for (int i = 0; i < methods.size(); i++) {
for (int j = i + 1; j < methods.size(); j++) {
var m1 = methods.get(i);
var m2 = methods.get(j);
if (m1.signature().equals(m2.signature()) && m1.base() != m2.base()) {
compiler.warn(new CompilerWarning(m1.base().getOffset(), "Duplicate Method definition " + m1.getSimpleName() +
" found, signature " + m1.signature().getDescriptor() + " clashes with signature " + m2.signature().getDescriptor()));
}
}
}
for (var method : methods) {
var patternsRec = new ArrayList<>(patterns);
@@ -749,9 +655,7 @@ public class ASTToTargetAST {
private Optional<TargetMethod> generateBridgeMethod(ClassOrInterface clazz, List<TargetMethod> methods) {
// If there's only one method we don't need a bridge
//if (clazz.isInterface()) return Optional.empty();
if (methods.size() <= 1) return Optional.empty();
var firstMethod = methods.getFirst();
var ra = firstMethod.signature().returnType();
@@ -787,14 +691,14 @@ public class ASTToTargetAST {
var parameters = resParams.stream().map( p -> new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList();
var classType = new TargetRefType(clazz.getClassName().toString());
var classType = new TargetRefType(clazz.getClassName().getClassName());
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(resParams.getFirst().pattern().type(), resParams.getFirst().pattern().name()), parameters, List.of(), methods, classType);
var block = new TargetBlock(List.of(stmt));
var generics = new HashSet<>(firstMethod.signature().generics());
var resSig = new TargetMethod.Signature(generics, resParams, resRet);
correctGenerics(resSig, generics.stream().toList());
var bridgeMethod = new TargetMethod(firstMethod.access() & ~Opcodes.ACC_ABSTRACT, firstMethod.name(), block, resSig, resSig);
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, resSig, resSig);
return Optional.of(bridgeMethod);
}
@@ -811,8 +715,8 @@ public class ASTToTargetAST {
return 0;
}
if (isSubtype(a.type(), b.type(), compiler)) return -1;
if (isSubtype(b.type(), a.type(), compiler)) return 1;
if (isSubtype(a.type(), b.type())) return -1;
if (isSubtype(b.type(), a.type())) return 1;
return 0;
}
@@ -856,12 +760,7 @@ public class ASTToTargetAST {
generics.addOverlay(tph, signatureParams.get(i).pattern().type());
}
}
TargetMethod tMethod;
try {
tMethod = convert(method, generics);
} catch (DiscardResultSet ignored) {
continue;
}
var tMethod = convert(method, generics);
res.add(new TargetMethod(tMethod.access(), name, tMethod.block(), tMethod.signature(), tMethod.txSignature(), tMethod.base(), tMethod.generics()));
}
@@ -870,7 +769,7 @@ public class ASTToTargetAST {
// .filter(p -> p.pattern() instanceof TargetComplexPattern).findFirst().orElseThrow().pattern());
// Generate dispatch method
var classType = new TargetRefType(clazz.getClassName().toString());
var classType = new TargetRefType(clazz.getClassName().getClassName());
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(signatureParams.getFirst().pattern().type(), signatureParams.getFirst().pattern().name()), parameters, List.of(), res, classType);
var block = new TargetBlock(List.of(stmt));
@@ -928,9 +827,7 @@ public class ASTToTargetAST {
private TargetMethod convert(MethodWithTphs mtph, IGenerics generics) {
this.currentMethod = mtph.method;
var res = new TargetMethod(mtph.method.modifier, mtph.method.name, convert(mtph.method.block, generics), mtph.signature.java(), mtph.signature.tx(), mtph.method, generics);
this.currentMethod = null;
return res;
return new TargetMethod(mtph.method.modifier, mtph.method.name, convert(mtph.method.block, generics), mtph.signature.java(), mtph.signature.tx(), mtph.method, generics);
}
record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {
@@ -954,13 +851,7 @@ public class ASTToTargetAST {
for (var tph : tphs) {
var left = a.getTargetType(tph);
var right = b.getTargetType(tph);
if (left instanceof TargetExtendsWildcard wc) left = wc.innerType();
if (right instanceof TargetExtendsWildcard wc) right = wc.innerType();
if (!Objects.equals(left, right)) {
Target.logger.error(m.name + " Discarded " + tph + " was " + left.name() + " and " + right.name());
return true;
}
if (!Objects.equals(left, right)) return true;
}
return false;
}
@@ -998,13 +889,10 @@ public class ASTToTargetAST {
signatures.add(new Signature(javaSignature, txSignature, generics));
}
for (var signature : new ArrayList<>(signatures)) {
try {
convert(new MethodWithTphs(method, signature.generics, signature));
} catch (DiscardResultSet discard) {
// If a result set is discarded we skip it from now on
signatures.removeIf(s -> s == signature);
}
if (!signatures.isEmpty()) {
var signature = signatures.getFirst();
// We need to convert once to find out what TPHs are existing in the given method
convert(new MethodWithTphs(method, signature.generics, signature));
}
for (var signature : signatures) {
@@ -1013,14 +901,13 @@ public class ASTToTargetAST {
.filter(m -> typesAreDifferent(method, m.generics.javaGenerics, signature.generics.javaGenerics)).findFirst();
if (duplicate.isPresent()) {
var d = duplicate.get();
compiler.warn(new CompilerWarning(method.block.getOffset(), "Duplicate Method definition " + method.getName() +
compiler.warn(new CompilerWarning(method.block.getOffset(), "Duplicate Method definition " + method.name +
" found, signature " + d.signature.java.getDescriptor() + " clashes with signature " + signature.java.getDescriptor()));
break;
}
result.add(mtph);
}
this.currentMethod = null;
return result;
}
@@ -1093,43 +980,15 @@ public class ASTToTargetAST {
return null;
}
public static ClassOrInterface getCommonSuperType(TargetType test, TargetType other, JavaTXCompiler compiler) {
var testClass = compiler.getClass(new JavaClassName(test.name()));
var otherClass = compiler.getClass(new JavaClassName(other.name()));
if (testClass == null) return null;
while (testClass != null) {
if (otherClass.isInterface()) {
for (var superInterface : testClass.getSuperInterfaces()) {
if (superInterface.getName().equals(otherClass.getClassName())) return otherClass;
var c = getCommonSuperType(new TargetRefType(superInterface.getName().toString()), other, compiler);
if (c != null) return c;
}
}
if (testClass.equals(otherClass)) return testClass;
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) return testClass;
testClass = compiler.getClass(testClass.getSuperClass().getName());
}
return null;
}
public static boolean isSubtype(TargetType test, TargetType other, JavaTXCompiler compiler) {
if (other == null) return false;
private boolean isSubtype(TargetType test, TargetType other) {
if (other.equals(TargetType.Object)) return true;
if (test instanceof TargetGenericType || other instanceof TargetGenericType) return false;
if (test instanceof TargetFunNType tfun && other instanceof TargetFunNType ofun)
return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun), compiler);
if (other instanceof TargetFunNType) return false;
return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun));
var testClass = compiler.getClass(new JavaClassName(test.name()));
var otherClass = compiler.getClass(new JavaClassName(other.name()));
if (testClass == null) return false;
while (testClass != null) {
if (otherClass.isInterface()) {
for (var superInterface : testClass.getSuperInterfaces()) {
if (superInterface.getName().equals(otherClass.getClassName())) return true;
if (isSubtype(new TargetRefType(superInterface.getName().toString()), other, compiler)) return true;
}
}
if (testClass.equals(otherClass)) return true;
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break;
testClass = compiler.getClass(testClass.getSuperClass().getName());
@@ -1137,17 +996,17 @@ public class ASTToTargetAST {
return false;
}
private static boolean isSupertype(TargetType test, TargetType other, JavaTXCompiler compiler) {
return isSubtype(other, test, compiler);
private boolean isSupertype(TargetType test, TargetType other) {
return isSubtype(other, test);
}
private static boolean isSubtype(FunNGenerator.GenericParameters test, FunNGenerator.GenericParameters other, JavaTXCompiler compiler) {
private boolean isSubtype(FunNGenerator.GenericParameters test, FunNGenerator.GenericParameters other) {
if (test.getArguments().size() != other.getArguments().size()) return false;
if (!isSubtype(test.getReturnType(), other.getReturnType(), compiler)) return false;
if (!isSubtype(test.getReturnType(), other.getReturnType())) return false;
for (int i = 0; i < test.getArguments().size(); i++) {
var arg1 = test.getArguments().get(i);
var arg2 = other.getArguments().get(i);
if (!isSupertype(arg1, arg2, compiler)) return false;
if (!isSupertype(arg1, arg2)) return false;
}
return true;
}
@@ -1157,7 +1016,7 @@ public class ASTToTargetAST {
var gep = entry.getValue();
var superInterfaces = compiler.usedFunN.values().stream()
.filter(g -> !g.equals(gep))
.filter(genericParameters -> isSubtype(gep, genericParameters, compiler))
.filter(genericParameters -> isSubtype(gep, genericParameters))
.map(FunNGenerator::getSpecializedClassName)
.toList();
@@ -1216,8 +1075,6 @@ public class ASTToTargetAST {
gep = compiler.usedFunN.get(className);
}
return flattenFunNType(params, gep);
} else if (name.matches("Fun\\d+\\$\\$.*")) {
return new TargetFunNType(name, List.of(), List.of(), 0, false);
}
return new TargetRefType(name, params);
}
@@ -1,9 +0,0 @@
package de.dhbwstuttgart.target.generate;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import java.util.List;
public class DiscardResultSet extends RuntimeException {
public DiscardResultSet() {}
}
@@ -142,17 +142,18 @@ public abstract class GenerateGenerics implements IGenerics {
this.constraints = constraints;
for (var constraint : constraints.results) {
if (constraint instanceof PairTPHsmallerTPH p) {
Target.logger.debug(p.left + " " + p.left.getVariance());
Target.logger.info(p.left + " " + p.left.getVariance());
simplifiedConstraints.add(new PairLT(new TPH(p.left), new TPH(p.right)));
} else if (constraint instanceof PairTPHEqualTPH p) {
equality.put(p.getLeft(), p.getRight());
} else if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
Target.logger.debug(p.left + " = " + p.right);
Target.logger.info(p.left + " = " + p.right);
concreteTypes.put(new TPH(p.left), p.right);
}
}
Target.logger.info("Simplified constraints: " + simplifiedConstraints);
}
public record GenericsState(Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes, Map<TypePlaceholder, TypePlaceholder> equality) {}
@@ -242,7 +243,7 @@ public abstract class GenerateGenerics implements IGenerics {
equality.put(entry.getKey(), to);
}
}
Target.logger.debug(from + " -> " + to + " " + from.getVariance());
Target.logger.info(from + " -> " + to + " " + from.getVariance());
//from.setVariance(to.getVariance());
equality.put(from, to);
referenced.remove(new TPH(from));
@@ -311,7 +312,7 @@ public abstract class GenerateGenerics implements IGenerics {
Set<TPH> T2s = new HashSet<>();
findTphs(superType, T2s);
Target.logger.debug("T1s: " + T1s + " T2s: " + T2s);
Target.logger.info("T1s: " + T1s + " T2s: " + T2s);
//Ende
superType = methodCall.receiverType;
@@ -326,7 +327,7 @@ public abstract class GenerateGenerics implements IGenerics {
var optMethod = ASTToTargetAST.findMethod(owner, methodCall.name, methodCall.signatureArguments().stream().map(x -> getTargetType(x)).toList(), GenerateGenerics.this, compiler);
if (optMethod.isEmpty()) return;
var method2 = optMethod.get();
Target.logger.debug("In: " + method.getName() + " Method: " + method2.getName());
Target.logger.info("In: " + method.getName() + " Method: " + method2.getName());
var generics = family(owner, method2);
// transitive and
@@ -359,7 +360,7 @@ public abstract class GenerateGenerics implements IGenerics {
if (!T1s.contains(R1) || !T2s.contains(R2)) continue;
var newPair = new PairLT(R1, R2);
Target.logger.debug("New pair: " + newPair);
Target.logger.info("New pair: " + newPair);
newPairs.add(newPair);
if (!containsRelation(result, newPair))
@@ -560,7 +561,7 @@ public abstract class GenerateGenerics implements IGenerics {
public Set<Pair> generics(ClassOrInterface owner, Method method) {
if (computedGenericsOfMethods.containsKey(method)) {
var cached = computedGenericsOfMethods.get(method);
Target.logger.debug("Cached " + method.getName() + ": " + cached);
Target.logger.info("Cached " + method.getName() + ": " + cached);
return cached;
}
@@ -589,7 +590,7 @@ public abstract class GenerateGenerics implements IGenerics {
normalize(result, classGenerics, usedTphs);
Target.logger.debug(this.getClass().getSimpleName() + " " + method.name + ": " + result);
Target.logger.info(this.getClass().getSimpleName() + " " + method.name + ": " + result);
return result;
}
@@ -721,7 +722,7 @@ public abstract class GenerateGenerics implements IGenerics {
if (!added) break;
}
Target.logger.debug(chain + " " + chain.stream().map(e -> e.resolve().getVariance()).toList());
Target.logger.info(chain + " " + chain.stream().map(e -> e.resolve().getVariance()).toList());
var variance = chain.get(0).resolve().getVariance();
if (variance != 1) continue;
var index = 0;
@@ -916,7 +917,7 @@ public abstract class GenerateGenerics implements IGenerics {
for (var pair : infima) {
var returnTypes = findTypeVariables(method.getReturnType());
var chain = findConnectionToReturnType(returnTypes, input, new HashSet<>(), pair.left);
Target.logger.debug("Find: " + pair.left + " " + chain);
Target.logger.info("Find: " + pair.left + " " + chain);
chain.remove(pair.left);
if (chain.size() > 0) {
for (var tph : chain)
@@ -954,8 +955,8 @@ public abstract class GenerateGenerics implements IGenerics {
}
}
newTph.setVariance(variance);
Target.logger.debug(infima + " " + infima.stream().map(i -> i.right.resolve().getVariance()).toList());
Target.logger.debug("Infima new TPH " + newTph + " variance " + variance);
Target.logger.info(infima + " " + infima.stream().map(i -> i.right.resolve().getVariance()).toList());
Target.logger.info("Infima new TPH " + newTph + " variance " + variance);
//referenced.add(newTph);
addToPairs(input, new PairLT(left, new TPH(newTph)));
@@ -1008,9 +1009,7 @@ public abstract class GenerateGenerics implements IGenerics {
public TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
if (in instanceof TypePlaceholder tph) {
if (equality.containsKey(tph)) {
var tph2 = equality.get(tph);
// Sanity check, they should not be equal!
if (!Objects.equals(in, tph2)) return getTargetType(tph2);
return getTargetType(equality.get(tph));
}
var type = concreteTypes.get(new TPH(tph));
if (type == null) return new TargetGenericType(tph.getName());
@@ -1,6 +1,5 @@
package de.dhbwstuttgart.target.generate;
import com.google.common.collect.Iterables;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.NotImplementedException;
@@ -228,9 +227,8 @@ public class StatementToTargetExpression implements ASTVisitor {
var returnType = isFunNType ? TargetType.Object : converter.convert(methodCall.signature.getLast(), generics);
var receiverName = new JavaClassName(converter.convert(methodCall.receiver.getType(), generics).name());
var argList = methodCall.signatureArguments().stream().map(sig -> converter.convert(sig, generics)).toList();
var receiverClass = converter.compiler.getClass(receiverName);
var argList = methodCall.signature.stream().map(sig -> converter.convert(sig, generics)).toList();
argList = argList.subList(0, argList.size() - 1);
Method foundMethod = null;
var isStatic = false;
@@ -243,75 +241,29 @@ public class StatementToTargetExpression implements ASTVisitor {
converter.addSignaturePair(methodCall.signatureArguments().get(i), methodCall.arglist.getArguments().get(i).getType());
}
var receiverClass = converter.compiler.getClass(receiverName);
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) {
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
var thisMethod = ASTToTargetAST.findMethod(receiverClass, methodCall.name, signature, generics, converter.compiler);
ClassOrInterface finalReceiverClass = receiverClass;
var finalSignature = signature;
foundMethod = thisMethod.orElseGet(() -> findMethod(finalReceiverClass.getSuperClass().getName(), methodCall.name, finalSignature, generics, converter.compiler).orElseThrow());
foundMethod = thisMethod.orElseGet(() -> findMethod(finalReceiverClass.getSuperClass().getName(), methodCall.name, signature, generics, converter.compiler).orElseThrow());
} else if (!isFunNType) {
receiverClass = converter.compiler.getClass(receiverName);
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
var finalSignature = signature;
foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow(
() -> new DebugException("Method " + methodCall.name + " not found (" + finalSignature + ") on class " + receiverName)
);
//if (foundMethod != null) {
// signature = foundMethod.getParameterList().getFormalparalist().stream().map(pattern -> converter.convert(pattern.getType(), generics)).toList();
//}
foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow();
}
if (!isFunNType) {
// NOTE Not using the direct conversion method on converter to bypass adding the TPH to the used TPH list
returnType = ASTToTargetAST.convert(foundMethod.getReturnType(), generics, converter.compiler);
signature = foundMethod.getParameterList().getFormalparalist().stream().map(e -> ASTToTargetAST.convert(e.getType(), generics, converter.compiler)).toList();
returnType = converter.convert(foundMethod.getReturnType(), generics);
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> converter.convert(e.getType(), generics)).toList();
isStatic = Modifier.isStatic(foundMethod.modifier);
isPrivate = Modifier.isPrivate(foundMethod.modifier);
isInterface = receiverClass.isInterface();
}
// Filter out every case that has a different method signature for the current method
// We only want to throw out cases when the method signature matches
var conflicitGenerics = new ArrayList<ASTToTargetAST.Generics>();
if (converter.getCurrentMethod() != null) {
var params = converter.convert(converter.getCurrentMethod().getParameterList(), generics);
for (var g2 : converter.all) if (g2.javaGenerics() != generics) {
var newParams = converter.convert(converter.getCurrentMethod().getParameterList(), g2.javaGenerics());
if (Objects.equals(params, newParams)) conflicitGenerics.add(g2);
}
}
// If one of the receiver types is a super type of this one AND there is a method defined that matches the parameters, we discard this result
if (!isStatic && receiverType instanceof TargetRefType) {
for (var g2 : conflicitGenerics) {
var otherReceiver = converter.convert(methodCall.receiver.getType(), g2.javaGenerics());
if (!Objects.equals(receiverType, otherReceiver) && ASTToTargetAST.isSubtype(receiverType, otherReceiver, converter.compiler)) {
ClassOrInterface clazz = converter.compiler.getClass(new JavaClassName(otherReceiver.name()));
if (clazz != null) {
var optMethod = ASTToTargetAST.findMethod(clazz, methodCall.name, argList, converter.compiler);
if (optMethod.isPresent()) {
throw new DiscardResultSet();
}
}
}
}
}
// Look at the signature in all result sets and check if a more specific insertion exists.
// If so, discard this result set by throwing an exception
for (var g2 : conflicitGenerics) {
for (var tph : Iterables.concat(methodCall.signatureArguments())) {
var currentType = converter.convert(tph, generics);
var type = converter.convert(tph, g2.javaGenerics());
if (!Objects.equals(type, currentType) && ASTToTargetAST.isSubtype(type, currentType, converter.compiler)) {
throw new DiscardResultSet();
}
}
}
//System.out.println(argList);
result = new TargetMethodCall(
converter.convert(methodCall.getType(), generics), returnType, signature,
converter.convert(methodCall.getType(), generics), returnType, argList,
converter.convert(methodCall.receiver, generics),
methodCall.getArgumentList().getArguments().stream().map(arg -> converter.convert(arg, generics)).toList(),
receiverType, methodCall.name, isStatic, isInterface, isPrivate
@@ -476,7 +428,7 @@ public class StatementToTargetExpression implements ASTVisitor {
.map(this::extractAllPatterns)
.flatMap(List::stream).toList();
case FormalParameter param -> List.of(new TypeVariants(param.getType(), converter.findAllVariants(param.getType())));
case null, default -> List.of();
default -> List.of();
};
}
@@ -3,7 +3,6 @@ package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.target.generate.IGenerics;
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.expression.TargetComplexPattern;
import de.dhbwstuttgart.target.tree.expression.TargetPattern;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.Opcodes;
@@ -17,13 +16,6 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature
this(access, name, block, signature, txSignature, null, null);
}
// If this was a generated method it has an unwieldy name, this returns the original name
// like for instance dup$0$Ljava$lang$Integer$_$ -> dup
public String getSimpleName() {
if (name.contains("$")) return name.split("\\$")[0];
return name;
}
public record Signature(Set<TargetGeneric> generics, List<MethodParameter> parameters, TargetType returnType) {
public String getSignature() {
return TargetMethod.getSignature(generics, parameters, returnType);
@@ -38,13 +30,7 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Signature signature = (Signature) o;
if (this.parameters.size() != signature.parameters.size()) return false;
for (var i = 0; i < this.parameters.size(); i++) {
var p1 = this.parameters.get(i);
var p2 = signature.parameters.get(i);
if (!p1.pattern().fuzzyEquals(p2.pattern())) return false;
}
return true;
return Objects.equals(parameters, signature.parameters);
}
@Override
@@ -3,7 +3,6 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List;
import java.util.Objects;
public record TargetComplexPattern(TargetType ctor, TargetType type, String name, List<TargetPattern> subPatterns) implements TargetPattern {
@Override
@@ -20,10 +19,4 @@ public record TargetComplexPattern(TargetType ctor, TargetType type, String name
public String toString() {
return type + "(" + String.join(", ", subPatterns.stream().map(Object::toString).toList()) + ") " + name;
}
@Override
public boolean fuzzyEquals(TargetPattern other) {
if (!(other instanceof TargetComplexPattern op)) return false;
return Objects.equals(this.type, op.type) && Objects.equals(this.subPatterns, op.subPatterns);
}
}
@@ -2,8 +2,6 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.Objects;
public sealed interface TargetPattern extends TargetExpression permits TargetComplexPattern, TargetExpressionPattern, TargetGuard, TargetTypePattern {
default String name() {
return null;
@@ -14,8 +12,4 @@ public sealed interface TargetPattern extends TargetExpression permits TargetCom
TargetType type();
TargetPattern withName(String name);
default boolean fuzzyEquals(TargetPattern other) {
return Objects.equals(this.type(), other.type());
}
}
@@ -3,9 +3,8 @@ package de.dhbwstuttgart.target.tree.type;
import de.dhbwstuttgart.bytecode.FunNGenerator;
import java.util.List;
import java.util.Objects;
public record TargetFunNType(String name, List<TargetType> funNParams, List<TargetType> params, int returnArguments, boolean isInterface) implements TargetSpecializedType {
public record TargetFunNType(String name, List<TargetType> funNParams, List<TargetType> params, int returnArguments) implements TargetSpecializedType {
public static TargetFunNType fromParams(List<TargetType> params, int returnArguments) {
return fromParams(params, params, returnArguments);
@@ -13,7 +12,7 @@ public record TargetFunNType(String name, List<TargetType> funNParams, List<Targ
public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams, int returnArguments) {
var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params));
return new TargetFunNType(name, params, realParams, returnArguments, false);
return new TargetFunNType(name, params, realParams, returnArguments);
}
public String toMethodDescriptor() {
@@ -40,21 +39,4 @@ public record TargetFunNType(String name, List<TargetType> funNParams, List<Targ
var args = FunNGenerator.getArguments(funNParams);
return "LFun" + args.size() + "$$" + TargetSpecializedType.signatureParameters(funNParams) + ";";
}
public boolean equals(Object other) {
if (!(other instanceof TargetFunNType otfn)) return false;
if (!isInterface && !otfn.isInterface) {
if (!Objects.equals(name, otfn.name)) return false;
if (funNParams.size() != otfn.funNParams.size()) return false;
for (var i = 0; i < funNParams.size(); i++) {
var p1 = funNParams.get(i);
var p2 = otfn.funNParams.get(i);
if (p1 instanceof TargetGenericType || p2 instanceof TargetGenericType) continue;
if (!Objects.equals(p1, p2)) return false;
}
return true;
}
return Objects.equals(name, otfn.name);
}
}
@@ -0,0 +1,311 @@
package de.dhbwstuttgart.typeinference.dependency;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.AbstractASTWalker;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Constructor;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.statement.BinaryExpr;
import de.dhbwstuttgart.syntaxtree.statement.BoolExpression;
import de.dhbwstuttgart.syntaxtree.statement.CastExpr;
import de.dhbwstuttgart.syntaxtree.statement.DoStmt;
import de.dhbwstuttgart.syntaxtree.statement.Expression;
import de.dhbwstuttgart.syntaxtree.statement.ExpressionReceiver;
import de.dhbwstuttgart.syntaxtree.statement.ForEachStmt;
import de.dhbwstuttgart.syntaxtree.statement.ForStmt;
import de.dhbwstuttgart.syntaxtree.statement.IfStmt;
import de.dhbwstuttgart.syntaxtree.statement.InstanceOf;
import de.dhbwstuttgart.syntaxtree.statement.MethodCall;
import de.dhbwstuttgart.syntaxtree.statement.NewClass;
import de.dhbwstuttgart.syntaxtree.statement.Receiver;
import de.dhbwstuttgart.syntaxtree.statement.Statement;
import de.dhbwstuttgart.syntaxtree.statement.StaticClassName;
import de.dhbwstuttgart.syntaxtree.statement.Super;
import de.dhbwstuttgart.syntaxtree.statement.SuperCall;
import de.dhbwstuttgart.syntaxtree.statement.This;
import de.dhbwstuttgart.syntaxtree.statement.ThisCall;
import de.dhbwstuttgart.syntaxtree.statement.Throw;
import de.dhbwstuttgart.syntaxtree.statement.WhileStmt;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* --- Comment AI generated ---
* Builds the method/constructor call graph for the classes currently being compiled together --
* the input to splitting whole-program type inference into per-strongly-connected-component
* batches, so that only mutually recursive methods have to be inferred jointly.
*
* Scope, deliberately:
* <ul>
* <li>Nodes are methods and constructors with a body, declared directly in one of
* {@code definedClasses}. Field initializers are NOT modeled as graph nodes here: a field is
* never generalized (it has exactly one type, never a per-use-site instantiated scheme like a
* method does), so it never needs SCC/mutual-recursion treatment -- only a simple topological
* position relative to whatever it calls, which a separate, much simpler pass can handle.</li>
* <li>Inherited method copies ({@code Method.isInherited}, produced by
* {@code JavaTXCompiler.addMethods}) are skipped. This builder is meant to run right after
* parsing and before {@code addMethods} has run, so in practice those copies should not exist
* yet; the check is kept as a defensive no-op in case that ordering assumption changes.</li>
* <li>Call targets are resolved using ordinary Java scoping where the receiver's type is known
* syntactically without needing inference: member lookup through the current class's
* hierarchy for unqualified/{@code this}-qualified calls (a member always shadows a
* same-named import per JLS 15.12.1), direct resolution for {@code TypeName.foo()} calls,
* {@code new Foo()}, and receivers with an explicit declared type. Only when the receiver's
* own type is itself not yet known (an omitted/inferred TPH -- e.g. the result of a chained
* call) does resolution fall back to a conservative name+arity match across all classes being
* compiled. That fallback can only ever add edges that don't exist at runtime
* (over-approximation, which just merges SCCs unnecessarily); it can never omit a real edge,
* which is the property that actually matters for soundness.</li>
* </ul>
*/
public class CallGraphBuilder {
private final JavaTXCompiler compiler;
private final Set<ClassOrInterface> definedClasses;
private final IdentityHashMap<Method, DependencyNode> nodes = new IdentityHashMap<>();
public CallGraphBuilder(JavaTXCompiler compiler, Set<ClassOrInterface> definedClasses) {
this.compiler = compiler;
this.definedClasses = definedClasses;
}
public DependencyGraph build() {
for (ClassOrInterface cl : definedClasses) {
for (Method m : cl.getMethods()) {
if (!m.isInherited && m.block != null) nodeFor(cl, m);
}
for (Constructor c : cl.getConstructors()) {
if (c.block != null) nodeFor(cl, c);
}
}
Map<DependencyNode, Set<DependencyNode>> edges = new LinkedHashMap<>();
for (Map.Entry<Method, DependencyNode> entry : nodes.entrySet()) {
DependencyNode from = entry.getValue();
EdgeCollector collector = new EdgeCollector(from.getOwner());
entry.getKey().block.accept(collector);
edges.put(from, collector.targets);
}
return new DependencyGraph(new LinkedHashSet<>(nodes.values()), edges);
}
private DependencyNode nodeFor(ClassOrInterface owner, Method m) {
return nodes.computeIfAbsent(m, mm -> new DependencyNode(owner, mm));
}
private ClassOrInterface resolve(JavaClassName name) {
return compiler.getClass(name);
}
private ClassOrInterface typeToClass(RefTypeOrTPHOrWildcardOrGeneric type) {
return type instanceof RefType rt ? resolve(rt.getName()) : null;
}
private ClassOrInterface superClassOf(ClassOrInterface cl) {
return cl.getSuperClass() == null ? null : resolve(cl.getSuperClass().getName());
}
private List<ClassOrInterface> hierarchyOf(ClassOrInterface start) {
List<ClassOrInterface> result = new ArrayList<>();
Set<JavaClassName> visited = new HashSet<>();
Deque<ClassOrInterface> queue = new ArrayDeque<>();
queue.add(start);
while (!queue.isEmpty()) {
ClassOrInterface cl = queue.poll();
if (cl == null || !visited.add(cl.getClassName())) continue;
result.add(cl);
if (cl.getSuperClass() != null) queue.add(resolve(cl.getSuperClass().getName()));
for (RefType parent : cl.getSuperInterfaces()) {
queue.add(resolve(parent.getName()));
}
}
return result;
}
private boolean matches(Method m, String name, int arity) {
return m.getName().equals(name) && m.getParameterList().getFormalparalist().size() == arity;
}
private boolean hierarchyDeclaresName(ClassOrInterface start, String name) {
for (ClassOrInterface cl : hierarchyOf(start)) {
for (Method m : cl.getMethods()) {
if (!m.isInherited && m.getName().equals(name)) return true;
}
}
return false;
}
private Set<DependencyNode> resolveInHierarchy(ClassOrInterface start, String name, int arity) {
Set<DependencyNode> found = new LinkedHashSet<>();
for (ClassOrInterface cl : hierarchyOf(start)) {
if (!definedClasses.contains(cl)) continue;
for (Method m : cl.getMethods()) {
if (!m.isInherited && m.block != null && matches(m, name, arity)) found.add(nodeFor(cl, m));
}
}
return found;
}
private Set<DependencyNode> resolveConservatively(String name, int arity) {
Set<DependencyNode> found = new LinkedHashSet<>();
for (ClassOrInterface cl : definedClasses) {
for (Method m : cl.getMethods()) {
if (!m.isInherited && m.block != null && matches(m, name, arity)) found.add(nodeFor(cl, m));
}
}
return found;
}
private ClassOrInterface receiverClass(Receiver receiver, ClassOrInterface owner) {
if (receiver instanceof StaticClassName scn) {
return typeToClass(scn.getType());
}
if (receiver instanceof ExpressionReceiver er) {
Expression inner = er.expr;
if (inner instanceof This) return owner;
if (inner instanceof Super) return superClassOf(owner);
if (inner instanceof NewClass nc) return typeToClass(nc.getType());
// Any other expression (local var, field, parameter, ...): only resolvable when its
// own declared type is an explicit RefType rather than an omitted/inferred TPH.
if (!(inner.getType() instanceof TypePlaceholder)) return typeToClass(inner.getType());
}
return null;
}
private class EdgeCollector extends AbstractASTWalker {
private final ClassOrInterface owner;
final Set<DependencyNode> targets = new LinkedHashSet<>();
EdgeCollector(ClassOrInterface owner) {
this.owner = owner;
}
@Override
public void visit(MethodCall methodCall) {
super.visit(methodCall);
int arity = methodCall.getArgumentList().getArguments().size();
targets.addAll(resolveCall(methodCall.receiver, methodCall.name, arity));
}
@Override
public void visit(BinaryExpr binary) {
binary.lexpr.accept(this);
binary.rexpr.accept(this);
}
@Override
public void visit(BoolExpression logical) {
logical.lexpr.accept(this);
logical.rexpr.accept(this);
}
@Override
public void visit(CastExpr castExpr) {
castExpr.expr.accept(this);
}
@Override
public void visit(InstanceOf instanceOf) {
instanceOf.getExpression().accept(this);
}
@Override
public void visit(Throw aThrow) {
aThrow.expr.accept(this);
}
@Override
public void visit(IfStmt ifStmt) {
ifStmt.expr.accept(this);
super.visit(ifStmt);
}
@Override
public void visit(WhileStmt whileStmt) {
whileStmt.expr.accept(this);
super.visit(whileStmt);
}
@Override
public void visit(DoStmt doStmt) {
doStmt.expr.accept(this);
super.visit(doStmt);
}
@Override
public void visit(ForStmt forStmt) {
for (Statement s : forStmt.initializer) s.accept(this);
if (forStmt.condition != null) forStmt.condition.accept(this);
for (Expression e : forStmt.loopExpr) e.accept(this);
super.visit(forStmt);
}
@Override
public void visit(ForEachStmt forEachStmt) {
forEachStmt.expression.accept(this);
super.visit(forEachStmt);
}
@Override
public void visit(SuperCall superCall) {
superCall.getArgumentList().accept(this);
ClassOrInterface parent = superClassOf(owner);
if (parent != null && definedClasses.contains(parent)) {
int arity = superCall.getArgumentList().getArguments().size();
for (Constructor c : parent.getConstructors()) {
if (c.getParameterList().getFormalparalist().size() == arity) targets.add(nodeFor(parent, c));
}
}
}
@Override
public void visit(ThisCall thisCall) {
thisCall.getArgumentList().accept(this);
int arity = thisCall.getArgumentList().getArguments().size();
for (Constructor c : owner.getConstructors()) {
if (c.getParameterList().getFormalparalist().size() == arity) targets.add(nodeFor(owner, c));
}
}
@Override
public void visit(NewClass newClass) {
super.visit(newClass);
ClassOrInterface target = typeToClass(newClass.getType());
if (target != null && definedClasses.contains(target)) {
int arity = newClass.getArgumentList().getArguments().size();
for (Constructor c : target.getConstructors()) {
if (c.getParameterList().getFormalparalist().size() == arity) targets.add(nodeFor(target, c));
}
}
}
private Set<DependencyNode> resolveCall(Receiver receiver, String name, int arity) {
// Unqualified / this-qualified calls follow JLS 15.12.1: a member always shadows a
// same-named import, so search the current class's hierarchy first -- but unlike an
// explicitly typed receiver (below), finding nothing there is not a resolved "no
// edges", it means falling through to import resolution, approximated conservatively.
if (receiver instanceof ExpressionReceiver er && er.expr instanceof This) {
if (hierarchyDeclaresName(owner, name)) return resolveInHierarchy(owner, name, arity);
return resolveConservatively(name, arity);
}
ClassOrInterface known = receiverClass(receiver, owner);
if (known != null) return resolveInHierarchy(known, name, arity);
return resolveConservatively(name, arity);
}
}
}
@@ -0,0 +1,35 @@
package de.dhbwstuttgart.typeinference.dependency;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* A directed graph over DependencyNodes: an edge from A to B means A's body contains a
* call that may resolve to B
*/
public final class DependencyGraph {
private final Set<DependencyNode> nodes;
private final Map<DependencyNode, Set<DependencyNode>> edges;
public DependencyGraph(Set<DependencyNode> nodes, Map<DependencyNode, Set<DependencyNode>> edges) {
this.nodes = Collections.unmodifiableSet(new LinkedHashSet<>(nodes));
Map<DependencyNode, Set<DependencyNode>> copy = new LinkedHashMap<>();
for (DependencyNode n : this.nodes) {
copy.put(n, Collections.unmodifiableSet(new LinkedHashSet<>(edges.getOrDefault(n, Set.of()))));
}
this.edges = Collections.unmodifiableMap(copy);
}
public Set<DependencyNode> nodes() {
return nodes;
}
/** The nodes {@code node}'s body may call into. */
public Set<DependencyNode> callees(DependencyNode node) {
return edges.getOrDefault(node, Set.of());
}
}
@@ -0,0 +1,31 @@
package de.dhbwstuttgart.typeinference.dependency;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Method;
import java.util.Objects;
public final class DependencyNode {
private final ClassOrInterface owner;
private final Method method;
DependencyNode(ClassOrInterface owner, Method method) {
this.owner = Objects.requireNonNull(owner);
this.method = Objects.requireNonNull(method);
}
public ClassOrInterface getOwner() {
return owner;
}
public Method getMethod() {
return method;
}
@Override
public String toString() {
return owner.getClassName().getClassName() + "." + method.getName() + "/"
+ method.getParameterList().getFormalparalist().size();
}
}
@@ -0,0 +1,125 @@
package de.dhbwstuttgart.typeinference.dependency;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter;
import de.dhbwstuttgart.typeinference.result.ResultPair;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import de.dhbwstuttgart.typeinference.typeAlgo.TYPE;
import de.dhbwstuttgart.typeinference.unify.PlaceholderRegistry;
import de.dhbwstuttgart.typeinference.unify.TypeUnify;
import de.dhbwstuttgart.typeinference.unify.UnifyContext;
import de.dhbwstuttgart.typeinference.unify.UnifyResultListenerImpl;
import de.dhbwstuttgart.typeinference.unify.UnifyResultModel;
import de.dhbwstuttgart.typeinference.unify.UnifyTaskModel;
import de.dhbwstuttgart.typeinference.unify.model.FiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;
import de.dhbwstuttgart.util.Logger;
import java.io.File;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
public class GeneralizingBytecodeDemo {
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.out.println("usage: GeneralizingBytecodeDemo <file1.jav> [file2.jav ...]");
return;
}
List<File> files = Arrays.stream(args).map(File::new).toList();
JavaTXCompiler compiler = new JavaTXCompiler(files);
compiler.parseAll();
Set<ClassOrInterface> definedClasses = new HashSet<>();
Set<ClassOrInterface> allClasses = new HashSet<>();
for (File f : files) {
SourceFile sf = compiler.sourceFiles.get(f);
definedClasses.addAll(sf.KlassenVektor);
allClasses.addAll(compiler.getAvailableClasses(f));
allClasses.addAll(sf.availableClasses);
}
allClasses.removeAll(definedClasses);
allClasses.addAll(definedClasses);
DependencyGraph graph = new CallGraphBuilder(compiler, definedClasses).build();
List<Set<DependencyNode>> sccs = StronglyConnectedComponents.compute(graph);
TYPE ty = new TYPE(definedClasses, allClasses);
Logger logger = new Logger("GeneralizingBytecodeDemo");
PlaceholderRegistry placeholderRegistry = new PlaceholderRegistry();
FiniteClosure finiteClosure = UnifyTypeFactory.generateFC(
allClasses.stream().toList(), logger, compiler.getClassLoader(), compiler, placeholderRegistry);
UnifyTaskModel usedTasks = new UnifyTaskModel();
Function<UnifyPair, UnifyPair> distributeInnerVars = x -> {
var lhs = x.getLhsType();
var rhs = x.getRhsType();
if (lhs instanceof PlaceholderType lp && rhs instanceof PlaceholderType rp && (lp.isInnerType() || rp.isInnerType())) {
lp.setInnerType(true);
rp.setInnerType(true);
}
return x;
};
Set<ResultPair> combined = new LinkedHashSet<>();
System.out.println("Solving " + sccs.size() + " SCCs independently, materializing each into the AST before the next SCC runs:");
for (int i = 0; i < sccs.size(); i++) {
Set<DependencyNode> component = sccs.get(i);
List<Map.Entry<ClassOrInterface, Method>> group = component.stream()
.<Map.Entry<ClassOrInterface, Method>>map(n -> new AbstractMap.SimpleEntry<>(n.getOwner(), n.getMethod()))
.toList();
var cons = ty.getConstraintsFor(group);
var unifyCons = UnifyTypeFactory.convert(compiler, cons, placeholderRegistry).map(distributeInnerVars);
UnifyResultModel urm = new UnifyResultModel(cons, finiteClosure);
UnifyResultListenerImpl listener = new UnifyResultListenerImpl();
urm.addUnifyResultListener(listener);
UnifyContext context = new UnifyContext(logger, true, urm, usedTasks, placeholderRegistry);
TypeUnify.unifyParallel(unifyCons.getUndConstraints(), unifyCons.getOderConstraints(), finiteClosure, context);
System.out.println("[" + i + "] " + component);
List<ResultSet> results = listener.getResults();
if (results.isEmpty()) {
System.out.println(" (no results)");
continue;
}
ResultSet sccResult = results.getFirst();
System.out.println(" isolated result: " + sccResult.getSortedResults());
combined.addAll(sccResult.results);
for (Map.Entry<ClassOrInterface, Method> entry : group) {
GeneralizingSccDemo.materialize(entry.getKey(), entry.getValue(), sccResult);
}
}
ResultSet whole = new ResultSet(combined);
System.out.println();
System.out.println("Source after materialization:");
for (File f : files) {
System.out.println(ASTTypePrinter.print(compiler.sourceFiles.get(f)));
}
System.out.println("Generating bytecode from the union of all SCCs' isolated ResultSets ...");
for (File f : files) {
SourceFile sf = compiler.sourceFiles.get(f);
var classes = compiler.generateBytecode(sf, List.of(whole));
compiler.writeClassFile(classes, f);
System.out.println("wrote " + classes.keySet());
}
}
}
@@ -0,0 +1,252 @@
package de.dhbwstuttgart.typeinference.dependency;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Constructor;
import de.dhbwstuttgart.syntaxtree.GenericDeclarationList;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.ParameterList;
import de.dhbwstuttgart.syntaxtree.Pattern;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType;
import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter;
import de.dhbwstuttgart.typeinference.result.ResultPair;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import de.dhbwstuttgart.typeinference.typeAlgo.TYPE;
import de.dhbwstuttgart.typeinference.unify.PlaceholderRegistry;
import de.dhbwstuttgart.typeinference.unify.TypeUnify;
import de.dhbwstuttgart.typeinference.unify.UnifyContext;
import de.dhbwstuttgart.typeinference.unify.UnifyResultListenerImpl;
import de.dhbwstuttgart.typeinference.unify.UnifyResultModel;
import de.dhbwstuttgart.typeinference.unify.UnifyTaskModel;
import de.dhbwstuttgart.typeinference.unify.model.FiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;
import de.dhbwstuttgart.util.Logger;
import java.io.File;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
public class GeneralizingSccDemo {
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.out.println("usage: GeneralizingSccDemo <file1.jav> [file2.jav ...]");
return;
}
List<File> files = Arrays.stream(args).map(File::new).toList();
JavaTXCompiler compiler = new JavaTXCompiler(files);
compiler.parseAll();
Set<ClassOrInterface> definedClasses = new HashSet<>();
Set<ClassOrInterface> allClasses = new HashSet<>();
for (File f : files) {
SourceFile sf = compiler.sourceFiles.get(f);
definedClasses.addAll(sf.KlassenVektor);
allClasses.addAll(compiler.getAvailableClasses(f));
allClasses.addAll(sf.availableClasses);
}
allClasses.removeAll(definedClasses);
allClasses.addAll(definedClasses);
System.out.println("Source with TPH names (unresolved type slots only):");
for (File f : files) {
System.out.println(ASTTypePrinter.print(compiler.sourceFiles.get(f)));
}
DependencyGraph graph = new CallGraphBuilder(compiler, definedClasses).build();
List<Set<DependencyNode>> sccs = StronglyConnectedComponents.compute(graph);
TYPE ty = new TYPE(definedClasses, allClasses);
Logger logger = new Logger("GeneralizingSccDemo");
PlaceholderRegistry placeholderRegistry = new PlaceholderRegistry();
FiniteClosure finiteClosure = UnifyTypeFactory.generateFC(
allClasses.stream().toList(), logger, compiler.getClassLoader(), compiler, placeholderRegistry);
UnifyTaskModel usedTasks = new UnifyTaskModel();
Function<UnifyPair, UnifyPair> distributeInnerVars = x -> {
var lhs = x.getLhsType();
var rhs = x.getRhsType();
if (lhs instanceof PlaceholderType lp && rhs instanceof PlaceholderType rp && (lp.isInnerType() || rp.isInnerType())) {
lp.setInnerType(true);
rp.setInnerType(true);
}
return x;
};
System.out.println("Solving " + sccs.size() + " SCCs independently, materializing each into the AST before the next SCC runs:");
for (int i = 0; i < sccs.size(); i++) {
Set<DependencyNode> component = sccs.get(i);
List<Map.Entry<ClassOrInterface, Method>> group = component.stream()
.<Map.Entry<ClassOrInterface, Method>>map(n -> new AbstractMap.SimpleEntry<>(n.getOwner(), n.getMethod()))
.toList();
var cons = ty.getConstraintsFor(group);
var unifyCons = UnifyTypeFactory.convert(compiler, cons, placeholderRegistry).map(distributeInnerVars);
UnifyResultModel urm = new UnifyResultModel(cons, finiteClosure);
UnifyResultListenerImpl listener = new UnifyResultListenerImpl();
urm.addUnifyResultListener(listener);
UnifyContext context = new UnifyContext(logger, true, urm, usedTasks, placeholderRegistry);
TypeUnify.unifyParallel(unifyCons.getUndConstraints(), unifyCons.getOderConstraints(), finiteClosure, context);
System.out.println("[" + i + "] " + component + (component.size() > 1 ? " <- mutually recursive, solved jointly" : ""));
System.out.println(" raw constraints (pre-unify, AST-level types): " + cons);
System.out.println(" unify constraints (UnifyPair, and-constraints only): " + unifyCons.getUndConstraints());
if (!unifyCons.getOderConstraints().isEmpty()) {
System.out.println(" unify constraints (or-constraints): " + unifyCons.getOderConstraints());
}
List<ResultSet> results = listener.getResults();
if (results.isEmpty()) {
System.out.println(" (no results)");
continue;
}
ResultSet sccResult = results.getFirst();
System.out.println(" isolated result: " + sccResult.getSortedResults());
for (Map.Entry<ClassOrInterface, Method> entry : group) {
Method materialized = materialize(entry.getKey(), entry.getValue(), sccResult);
System.out.println(" materialized " + entry.getKey().getClassName().getClassName() + "." + materialized.getName()
+ ": generics=" + describeGenerics(materialized) + " params=" + describeParams(materialized)
+ " returns=" + describe(materialized.getReturnType()));
}
}
System.out.println();
System.out.println("Source after materialization (still prints TPH names for anything left, e.g. method bodies -- only signatures were rewritten):");
for (File f : files) {
System.out.println(ASTTypePrinter.print(compiler.sourceFiles.get(f)));
}
}
static Method materialize(ClassOrInterface owner, Method method, ResultSet sccResult) {
Set<TypePlaceholder> used = new LinkedHashSet<>();
for (Pattern p : method.getParameterList().getFormalparalist()) collectTPHs(p.getType(), used);
collectTPHs(method.getReturnType(), used);
if (used.isEmpty()) return method;
Set<TypePlaceholder> free = new LinkedHashSet<>();
for (TypePlaceholder tph : used) {
if (sccResult.resolveType(tph).resolvedType instanceof TypePlaceholder) free.add(tph);
}
UnionFind uf = new UnionFind();
for (ResultPair rp : sccResult.results) {
if (rp.getLeft() instanceof TypePlaceholder l && rp.getRight() instanceof TypePlaceholder r
&& free.contains(l) && free.contains(r)) {
uf.union(l, r);
}
}
Map<TypePlaceholder, List<TypePlaceholder>> groups = new LinkedHashMap<>();
for (TypePlaceholder tph : free) groups.computeIfAbsent(uf.find(tph), k -> new ArrayList<>()).add(tph);
Map<TypePlaceholder, RefTypeOrTPHOrWildcardOrGeneric> substitution = new HashMap<>();
List<GenericTypeVar> newGenerics = new ArrayList<>();
RefType objectBound = new RefType(new JavaClassName("java.lang.Object"), new NullToken());
for (List<TypePlaceholder> members : groups.values()) {
String genName = members.get(0).getName();
newGenerics.add(new GenericTypeVar(genName, List.of(objectBound), new NullToken(), new NullToken()));
for (TypePlaceholder member : members) substitution.put(member, new GenericRefType(genName, new NullToken()));
}
for (TypePlaceholder tph : used) {
if (!free.contains(tph)) substitution.put(tph, sccResult.resolveType(tph).resolvedType);
}
if (substitution.isEmpty()) return method;
List<Pattern> newParams = new ArrayList<>();
for (Pattern p : method.getParameterList().getFormalparalist()) {
newParams.add(p.withType(substitute(p.getType(), substitution)));
}
ParameterList newParamList = new ParameterList(newParams, method.getParameterList().getOffset());
RefTypeOrTPHOrWildcardOrGeneric newReturnType = substitute(method.getReturnType(), substitution);
GenericDeclarationList newGenericDecl = new GenericDeclarationList(newGenerics, new NullToken());
Method rewritten = method instanceof Constructor
? new Constructor(method.modifier, method.name, newReturnType, newParamList, method.block, newGenericDecl, method.getOffset())
: new Method(method.modifier, method.name, newReturnType, newParamList, method.block, newGenericDecl, method.getOffset());
replaceMethod(owner, method, rewritten);
return rewritten;
}
private static void collectTPHs(RefTypeOrTPHOrWildcardOrGeneric type, Set<TypePlaceholder> out) {
if (type instanceof TypePlaceholder tph) out.add(tph);
else if (type instanceof RefType rt) for (var p : rt.getParaList()) collectTPHs(p, out);
else if (type instanceof ExtendsWildcardType w) collectTPHs(w.getInnerType(), out);
else if (type instanceof SuperWildcardType w) collectTPHs(w.getInnerType(), out);
}
private static RefTypeOrTPHOrWildcardOrGeneric substitute(RefTypeOrTPHOrWildcardOrGeneric type, Map<TypePlaceholder, RefTypeOrTPHOrWildcardOrGeneric> sub) {
if (type instanceof TypePlaceholder tph) return sub.getOrDefault(tph, tph);
if (type instanceof RefType rt) return new RefType(rt.getName(), rt.getParaList().stream().map(p -> substitute(p, sub)).toList(), rt.getOffset());
if (type instanceof ExtendsWildcardType w) return new ExtendsWildcardType(substitute(w.getInnerType(), sub), w.getOffset());
if (type instanceof SuperWildcardType w) return new SuperWildcardType(substitute(w.getInnerType(), sub), w.getOffset());
return type;
}
private static void replaceMethod(ClassOrInterface owner, Method original, Method replacement) {
if (original instanceof Constructor) {
var list = owner.getConstructors();
for (int i = 0; i < list.size(); i++) if (list.get(i) == original) { list.set(i, (Constructor) replacement); return; }
} else {
var list = owner.getMethods();
for (int i = 0; i < list.size(); i++) if (list.get(i) == original) { list.set(i, replacement); return; }
}
throw new IllegalStateException("method not found in owner's own list: " + original);
}
private static String describeGenerics(Method m) {
List<String> names = new ArrayList<>();
m.getGenerics().forEach(gtv -> names.add(gtv.getName()));
return names.toString();
}
private static String describeParams(Method m) {
List<String> params = new ArrayList<>();
m.getParameterList().getFormalparalist().forEach(p -> params.add(describe(p.getType())));
return params.toString();
}
private static String describe(RefTypeOrTPHOrWildcardOrGeneric type) {
if (type instanceof GenericRefType g) return g.getParsedName().toString();
if (type instanceof TypePlaceholder tph) return "TPH " + tph.getName();
return type.toString();
}
private static final class UnionFind {
private final Map<TypePlaceholder, TypePlaceholder> parent = new HashMap<>();
TypePlaceholder find(TypePlaceholder x) {
parent.putIfAbsent(x, x);
TypePlaceholder p = parent.get(x);
if (p != x) { p = find(p); parent.put(x, p); }
return p;
}
void union(TypePlaceholder a, TypePlaceholder b) {
TypePlaceholder ra = find(a), rb = find(b);
if (ra != rb) parent.put(ra, rb);
}
}
}
@@ -0,0 +1,40 @@
package de.dhbwstuttgart.typeinference.dependency;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.syntaxtree.visual.OutputGenerator;
import de.dhbwstuttgart.typeinference.result.ResultSet;
/**
* Prints a SourceFile the same way ASTTypePrinter/OutputGenerator do, except every TypePlaceholder
* the given ResultSet resolves is printed as its inferred type instead of "TPH <name>". Falls back
* to "TPH <name>" for any TPH the ResultSet doesn't cover (e.g. one that belongs to an SCC whose
* result wasn't merged in -- see SccUnifyDemo).
*/
public class ResolvedSourcePrinter extends OutputGenerator {
private final ResultSet resultSet;
private ResolvedSourcePrinter(StringBuilder out, ResultSet resultSet) {
super(out);
this.resultSet = resultSet;
}
public static String print(SourceFile sourceFile, ResultSet resultSet) {
StringBuilder out = new StringBuilder();
new ResolvedSourcePrinter(out, resultSet).visit(sourceFile);
return out.toString();
}
@Override
public void visit(TypePlaceholder typePlaceholder) {
var resolved = resultSet.resolveType(typePlaceholder).resolvedType;
if (resolved instanceof TypePlaceholder) {
out.append("TPH ").append(typePlaceholder.getName());
} else {
// resolved type may itself contain nested TPHs (e.g. a generic parameter) --
// recurse through the same visitor so those get resolved too, not just the outer type.
resolved.accept(this);
}
}
}
@@ -0,0 +1,82 @@
package de.dhbwstuttgart.typeinference.dependency;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* --- Comment AI generated ---
* Computes the strongly connected components of a {@link DependencyGraph} via Tarjan's
* algorithm, returning them in the order the SCC-scoped inference driver needs to process them:
* for every edge u -> v (u calls v) with u and v in different components, the component
* containing v appears BEFORE the component containing u -- i.e. a method's callees are always
* fully processed (and, in the inference driver, generalized) before the method itself.
*
* This is not a separate reversal step: it falls directly out of Tarjan's algorithm, since a
* component is only popped off the stack (and appended to the result) once the DFS has finished
* exploring all of its outgoing edges, which means every component it points to has already been
* popped.
*
* Implementation is recursive, so its stack depth is bounded by the depth of the call graph
* among the classes being compiled together, not by the size of the program. That is fine for
* realistic programs; a pathologically deep call chain could still overflow the JVM stack, same
* tradeoff most textbook Tarjan implementations make.
*/
public final class StronglyConnectedComponents {
private final DependencyGraph graph;
private final Map<DependencyNode, Integer> index = new HashMap<>();
private final Map<DependencyNode, Integer> lowlink = new HashMap<>();
private final Set<DependencyNode> onStack = new HashSet<>();
private final Deque<DependencyNode> stack = new ArrayDeque<>();
private final List<Set<DependencyNode>> result = new ArrayList<>();
private int counter = 0;
private StronglyConnectedComponents(DependencyGraph graph) {
this.graph = graph;
}
public static List<Set<DependencyNode>> compute(DependencyGraph graph) {
StronglyConnectedComponents scc = new StronglyConnectedComponents(graph);
for (DependencyNode node : graph.nodes()) {
if (!scc.index.containsKey(node)) {
scc.strongConnect(node);
}
}
return scc.result;
}
private void strongConnect(DependencyNode v) {
index.put(v, counter);
lowlink.put(v, counter);
counter++;
stack.push(v);
onStack.add(v);
for (DependencyNode w : graph.callees(v)) {
if (!index.containsKey(w)) {
strongConnect(w);
lowlink.put(v, Math.min(lowlink.get(v), lowlink.get(w)));
} else if (onStack.contains(w)) {
lowlink.put(v, Math.min(lowlink.get(v), index.get(w)));
}
}
if (lowlink.get(v).equals(index.get(v))) {
Set<DependencyNode> component = new LinkedHashSet<>();
DependencyNode w;
do {
w = stack.pop();
onStack.remove(w);
component.add(w);
} while (w != v);
result.add(component);
}
}
}
@@ -45,6 +45,26 @@ public class TYPE {
return ret;
}
/**
* Generates constraints for an explicit subset of methods/constructors (e.g. one strongly
* connected component of the call graph) instead of every method of every defined class.
* Uses the same TypeInferenceInformation (the whole program's classes, same as
* {@link #getConstraints()}) -- this does not change what a method's constraints can refer
* to, only which methods' constraints get generated and later unified together.
*/
public ConstraintSet<Pair> getConstraintsFor(Collection<Map.Entry<ClassOrInterface, Method>> group) {
ConstraintSet<Pair> ret = new ConstraintSet<>();
Set<ClassOrInterface> allClasses = TypeUnifyTaskHelper.getPresizedHashSet(allAvailableClasses.size());
allClasses.addAll(allAvailableClasses);
TypeInferenceInformation info = new TypeInferenceInformation(allClasses);
for (Map.Entry<ClassOrInterface, Method> entry : group) {
ClassOrInterface cl = entry.getKey();
Method m = entry.getValue();
ret.addAll(m instanceof Constructor c ? getConstraintsConstructor(c, info, cl) : getConstraintsMethod(m, info, cl));
}
return ret;
}
private ConstraintSet getConstraintsClass(ClassOrInterface cl, TypeInferenceInformation info) {
ConstraintSet ret = new ConstraintSet();
ConstraintSet methConstrains;
@@ -254,9 +254,6 @@ public class TYPEStmt implements StatementVisitor {
} else if (unaryExpr.operation == UnaryExpr.Operation.NOT) {
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), unaryExpr.getType(), PairOperator.EQUALSDOT, loc(unaryExpr.getOffset())));
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), new RefType(ASTFactory.createClass(java.lang.Boolean.class).getClassName(), new NullToken()), PairOperator.EQUALSDOT, loc(unaryExpr.getOffset())));
} else if (unaryExpr.operation == UnaryExpr.Operation.MINUS) {
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), number, PairOperator.SMALLERDOT, loc(unaryExpr.getOffset())));
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), unaryExpr.getType(), PairOperator.EQUALSDOT, loc(unaryExpr.getOffset())));
} else {
throw new NotImplementedException();
}
@@ -395,9 +392,8 @@ public class TYPEStmt implements StatementVisitor {
@Override
public void visit(BoolExpression expr) {
expr.lexpr.accept(this);
expr.rexpr.accept(this);
expr.lexpr.accept(this);
constraintsSet.addUndConstraint(new Pair(bool, expr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
constraintsSet.addUndConstraint(new Pair(bool, expr.lexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
constraintsSet.addUndConstraint(new Pair(bool, expr.rexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
@@ -875,8 +871,8 @@ public class TYPEStmt implements StatementVisitor {
if (caseExpressionType == null) {
for (var child : switchStmt.getBlocks()) {
for (var label : child.getLabels()) {
if (label.getPattern() == null && switchStmt.getSwitch().getType() instanceof TypePlaceholder) {
constraintsSet.addUndConstraint(new Pair(new RefType(new JavaClassName("java.lang.Object"), new NullToken()), switchStmt.getSwitch().getType(), PairOperator.EQUALSDOT, loc(label.getOffset())));
if (label.getPattern() == null) {
constraintsSet.addUndConstraint(new Pair(new RefType(new JavaClassName("java.lang.Object"), new NullToken()), switchStmt.getSwitch().getType(), PairOperator.SMALLERDOT, loc(label.getOffset())));
}
if (label.getPattern() instanceof RecordPattern) {
RecordPattern recordPattern = (RecordPattern) label.getPattern();
@@ -33,11 +33,13 @@ public class Logger {
private static void initLogger() {
if (defaultWriter != null) return;
if (!ConsoleInterface.writeLogFiles) return;
try {
Files.createDirectories(logFolder.toPath());
} catch (Exception e) {
throw new RuntimeException("Could not create directory for log files: " + logFolder, e);
}
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
var currentTimestamp = LocalDateTime.now().format(formatter);
try {
+8 -182
View File
@@ -1,19 +1,11 @@
import de.dhbwstuttgart.bytecode.CodeGenException;
import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.exceptions.CompilerException;
import de.dhbwstuttgart.exceptions.ParserError;
import de.dhbwstuttgart.exceptions.WarningsException;
import de.dhbwstuttgart.util.Logger;
import de.dhbwstuttgart.util.Logger.LogLevel;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;
@@ -27,14 +19,8 @@ import static org.junit.jupiter.api.Assertions.*;
import static targetast.TestCodegen.createClassLoader;
import static targetast.TestCodegen.generateClassFiles;
@Execution(ExecutionMode.CONCURRENT)
public class TestComplete {
@BeforeEach
public void setup() throws IOException {
TestCodegen.outputPath.toFile().mkdirs();
FileUtils.cleanDirectory(TestCodegen.outputPath.toFile());
}
@Test
public void applyLambdaTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "applyLambda.jav");
@@ -334,17 +320,9 @@ public class TestComplete {
}
@Test
public void sortingTest() throws Exception {
public void overloadingSortingTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Sorting.jav");
var Sorting = classFiles.get("Sorting");
var instance = Sorting.getDeclaredConstructor().newInstance();
var unsorted = List.of(10, 2, 1, 20, -1, 5);
var sorted = List.of(-1, 1, 2, 5, 10, 20);
var sort = Sorting.getDeclaredMethod("sort", List.class);
var result = sort.invoke(instance, unsorted);
assertEquals(sorted, result);
var instance = classFiles.get("Sorting").getDeclaredConstructor().newInstance();
}
@Test
@@ -679,15 +657,6 @@ public class TestComplete {
System.out.println(clazz.getDeclaredMethod("toString").invoke(instance));
}
@Test
public void recordTestInstanceVariables() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "RecordTestInstanceVariables.jav");
var clazz = classFiles.get("RecordTestInstanceVariables");
var instance = clazz.getDeclaredConstructor(Integer.class, Integer.class).newInstance(10, 20);
assertEquals(10, clazz.getDeclaredMethod("getA").invoke(instance));
assertEquals(20, clazz.getDeclaredMethod("getB").invoke(instance));
}
@Test
public void genericRecordTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "GenericRecord.jav");
@@ -1064,7 +1033,6 @@ public class TestComplete {
var instance = clazz.getDeclaredConstructor().newInstance();
}
@Disabled("Feature nicht implementiert")
@Test
public void testOverloadSwitch() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "SwitchOverload.jav");
@@ -1083,30 +1051,6 @@ public class TestComplete {
assertEquals(40.0, m.invoke(instance, d, 0));
}
@Disabled("Waiting for #407, skipping for now")
@Test
public void testPatternMatchingInterface() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), false, "Matcher.jav", "PatternMatchingInterface.jav");
var Intf = classFiles.get("Interface");
var clazz = classFiles.get("PatternMatchingInterface");
var M = classFiles.get("M");
var Matcher = classFiles.get("Matcher");
var a = classFiles.get("A").getDeclaredConstructor().newInstance();
var b = classFiles.get("B").getDeclaredConstructor().newInstance();
//var match = clazz.getDeclaredMethod("match", Intf);
//assertEquals(0, match.invoke(null, a));
//assertEquals(1, match.invoke(null, b));
var matcher = M.getDeclaredConstructor().newInstance();
var match2 = clazz.getDeclaredMethod("match", Matcher, Intf);
assertEquals(0, match2.invoke(null, matcher, a));
assertEquals(1, match2.invoke(null, matcher, b));
}
@Test
public void testInterfaces() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Interfaces.jav");
@@ -1122,16 +1066,6 @@ public class TestComplete {
assertEquals(50, m.invoke(null));
}
@Test
public void testStaticFail() throws Exception {
try {
generateClassFiles(createClassLoader(), "StaticFail.jav");
fail("No exception thrown!");
} catch (CodeGenException e) {
assertEquals("Attempted to call instance method nonStaticM with descriptor ()V from a static context", e.getMessage());
}
}
@Test
public void testFor() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "For.jav");
@@ -1230,10 +1164,6 @@ public class TestComplete {
assertEquals('C', clazz.getDeclaredMethod("m2").invoke(instance));
assertEquals(10L, clazz.getDeclaredMethod("m3").invoke(instance));
assertEquals(10.5F, clazz.getDeclaredMethod("m4").invoke(instance));
assertEquals(10.5D, clazz.getDeclaredMethod("m5").invoke(instance));
assertEquals(0xFF, clazz.getDeclaredMethod("m6").invoke(instance));
assertEquals(0b101010, clazz.getDeclaredMethod("m7").invoke(instance));
assertEquals(010, clazz.getDeclaredMethod("m8").invoke(instance));
}
@Test
@@ -1356,15 +1286,6 @@ public class TestComplete {
m.invoke(null, List.of("foo", "bar", "baz"));
}
@Disabled("See #405")
@Test
public void testLazy() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), false, "LazyList.jav", "Primzahlen.jav");
var clazz = classFiles.get("Primzahlen");
var main = clazz.getDeclaredMethod("main", List.class);
main.invoke(null, List.of());
}
@Test
public void testBug122() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug122.jav");
@@ -1528,6 +1449,7 @@ public class TestComplete {
}
@Test
@Disabled("too slow")
public void testBug325() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug325.jav");
var clazz = classFiles.get("Bug325");
@@ -1612,9 +1534,7 @@ public class TestComplete {
var m = clazz.getDeclaredMethod("ex1");
assertEquals("ABC", m.invoke(instance));
var ex2 = clazz.getDeclaredMethod("ex2");
assertEquals("CBA", ex2.invoke(instance));
var ex3 = clazz.getDeclaredMethod("ex3");
assertEquals("BA", ex3.invoke(instance));
assertEquals("BAC", ex2.invoke(instance));
}
@Test
public void testBug366() throws Exception {
@@ -1693,103 +1613,9 @@ public class TestComplete {
}
@Test
public void testBug395() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug395.jav");
fail("Shouldn't compile!");
} catch (WarningsException e) {
assertEquals(1, e.compilerWarnings.size());
var warning = e.compilerWarnings.get(0);
assertEquals("Duplicate Method definition dup found, signature (Ljava/lang/Integer;)Ljava/lang/Integer; clashes with signature (Ljava/lang/Integer;)Ljava/lang/Integer;", warning.getMessage());
}
}
@Test
public void testBug406() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug406.jav");
var clazz = classFiles.get("Bug406");
public void testBug392() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug392.jav");
var clazz = classFiles.get("Bug392");
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
}
@Test
public void testBug423() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug423.jav");
fail("No exception thrown!");
} catch (CodeGenException e) {
assertEquals("Class Bug423 does not implement method f from interface Bug423I", e.getMessage());
}
}
@Test
public void testBug422() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug422.jav");
fail("Shouldn't compile!");
} catch (CompilerException e) {
assertEquals("Duplicate class definition: Bug422", e.getMessage());
}
}
@Test
public void testBug421() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug421.jav");
fail("Shouldn't compile!");
} catch (ParserError e) {
assertEquals("Syntax error at line 2:26 - no viable alternative at input 'm() { return 1 }'", e.getMessage());
}
}
@Test
public void testBug409() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug409.jav");
var clazz = classFiles.get("Bug409");
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("m");
assertEquals(5, m.invoke(instance));
}
@Test
public void testBug417() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug417.jav");
var clazz = classFiles.get("Bug417");
var instance = clazz.getDeclaredConstructor().newInstance();
var name = clazz.getDeclaredMethod("name", Integer.class);
assertEquals("one", name.invoke(instance, 1));
assertEquals("many", name.invoke(instance, 2));
}
@Test
public void testBug411() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug411.jav");
var clazz = classFiles.get("Bug411");
}
@Test
public void testBug412() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug412.jav");
var clazz = classFiles.get("Bug412");
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("m", Integer.class);
assertEquals(10, m.invoke(instance, 1));
assertEquals(20, m.invoke(instance, 2));
}
@Test
public void testBug416() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug416.jav");
var clazz = classFiles.get("Bug416");
var L = classFiles.get("L");
var Cons = classFiles.get("Cons");
var Nil = classFiles.get("Nil");
var consCtor = Cons.getDeclaredConstructor(Object.class, L);
var nilCtor = Nil.getDeclaredConstructor();
var list = consCtor.newInstance(1, consCtor.newInstance(2, consCtor.newInstance(3, nilCtor.newInstance())));
var len = clazz.getDeclaredMethod("len", L);
assertEquals(3, len.invoke(null, list));
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ public class TestTypeDeployment {
var path = Path.of(System.getProperty("user.dir"), "/resources/bytecode/javFiles/Cycle.jav");
var file = path.toFile();
var compiler = new JavaTXCompiler(file);
compiler.generateBytecode();
compiler.parseAll();
var parsedSource = compiler.sourceFiles.get(file);
var tiResults = compiler.typeInference(file);
Set<TypeInsert> tips = new HashSet<>();
+3 -3
View File
@@ -18,7 +18,7 @@ public class InheritTest {
public static void setUpBeforeClass() throws Exception {
var classLoader = TestCodegen.createClassLoader();
var classes = TestCodegen.generateClassFiles(classLoader, false, "AA.jav", "BB.jav", "CC.jav", "DD.jav", "Inherit.jav");
var classes = TestCodegen.generateClassFiles(classLoader, "Inherit.jav", "AA.jav", "BB.jav", "CC.jav", "DD.jav");
classToTest = classes.get("Inherit");
classToTestAA = classes.get("AA");
classToTestBB = classes.get("BB");
@@ -52,7 +52,7 @@ public class InheritTest {
public void testmainCC() throws Exception {
Method m = classToTestCC.getDeclaredMethod("m", Integer.class);
assertEquals("CC", m.invoke(instanceOfClassCC, 5));
Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class);
Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class);
assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassCC, 5));
}
@@ -60,7 +60,7 @@ public class InheritTest {
public void testmainDD() throws Exception {
Method m = classToTestCC.getDeclaredMethod("m", Integer.class);
assertEquals("CC", m.invoke(instanceOfClassDD, 5));
Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class);
Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class);
assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassDD, 5));
}
+2 -2
View File
@@ -35,7 +35,7 @@ public class PutTest {
Method m = classToTest.getDeclaredMethod("putElement", Object.class, Stack.class);
Stack<Integer> s_invoke = new Stack<>();
m.invoke(instanceOfClass, 5, s_invoke);
assertEquals(5, s_invoke.pop());
assertEquals(new Integer(5), s_invoke.pop());
}
@Test
@@ -53,6 +53,6 @@ public class PutTest {
Method m = classToTest.getDeclaredMethod("main", Object.class, Stack.class);
Stack<Integer> s_invoke = new Stack<>();
m.invoke(instanceOfClass, 6, s_invoke);
assertEquals(6, s_invoke.pop());
assertEquals(new Integer(6), s_invoke.pop());
}
}
+5 -20
View File
@@ -5,7 +5,6 @@ import de.dhbwstuttgart.bytecode.Codegen;
import de.dhbwstuttgart.environment.DirectoryClassLoader;
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter;
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.tree.MethodParameter;
import de.dhbwstuttgart.target.tree.TargetClass;
@@ -15,7 +14,6 @@ import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.TargetFunNType;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -32,7 +30,7 @@ import java.util.function.Function;
import java.util.stream.Collectors;
public class TestCodegen {
public static final Path outputPath = Path.of(System.getProperty("user.dir"), "/targetTest");
static final Path outputPath = Path.of(System.getProperty("user.dir"), "/targetTest");
private static void writeClassFile(String name, byte[] code) throws IOException {
Files.createDirectories(outputPath);
@@ -58,8 +56,7 @@ public class TestCodegen {
try(var newClassLoader = new DirectoryClassLoader(List.of(outputPath.toFile()), (ClassLoader)classLoader)) {
var result = new HashMap<String, Class<?>>();
for (var file : filenames) {
var sf = compiler.sourceFiles.get(file);
var classes = sf.getClasses();
var classes = compiler.sourceFiles.get(file).getClasses();
result.putAll(classes.stream().map(cli -> {
try {
@@ -74,8 +71,7 @@ public class TestCodegen {
}
public static Class<?> generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException, ClassNotFoundException {
ASTToTargetAST converter = new ASTToTargetAST(List.of(new ResultSet(Set.of())), classLoader);
Codegen codegen = new Codegen(clazz, new JavaTXCompiler(List.of()), converter);
Codegen codegen = new Codegen(clazz, new JavaTXCompiler(List.of()), null);
var code = codegen.generate();
writeClassFile(clazz.qualifiedName().getClassName(), code);
return classLoader.loadClass(code);
@@ -202,20 +198,9 @@ public class TestCodegen {
@Test
public void testFor() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("For"));
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer,
new TargetBlock(List.of(new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)),
new TargetFor(List.of(new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0))),
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"),
new TargetLiteral.IntLiteral(10)), List.of(new TargetAssign(TargetType.Integer,
new TargetLocalVar(TargetType.Integer, "i"),
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1)))),
new TargetBlock(List.of(
new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"),
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"),
new TargetLocalVar(TargetType.Integer, "i")))))),
new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum")))));
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer, new TargetBlock(List.of(new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)), new TargetFor(List.of(new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0))), new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)), List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1)))), new TargetBlock(List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetLocalVar(TargetType.Integer, "i")))))), new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum")))));
var clazz = generateClass(targetClass, createClassLoader());
assertEquals(45, clazz.getDeclaredMethod("forLoop").invoke(null));
assertEquals(clazz.getDeclaredMethod("forLoop").invoke(null), 45);
}
@Test