Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bd19da742 |
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import java.lang.Integer;
|
||||
|
||||
public class Bug395 {
|
||||
dup(x) {
|
||||
//Hier beliebiger Seiteneffekt
|
||||
return x + x;
|
||||
}
|
||||
|
||||
dup(s) {
|
||||
return s + s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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,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; }
|
||||
}
|
||||
@@ -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));
|
||||
@@ -1205,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());
|
||||
}
|
||||
@@ -1221,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;
|
||||
@@ -1242,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:
|
||||
@@ -1467,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);
|
||||
@@ -1496,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);
|
||||
@@ -1509,10 +1479,7 @@ 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.visitLdcInsn("Unhandled case value");
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", false);
|
||||
mv.visitInsn(ATHROW);
|
||||
}
|
||||
@@ -1546,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);
|
||||
@@ -1580,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) {
|
||||
@@ -1670,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);
|
||||
@@ -1789,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();
|
||||
}
|
||||
@@ -1855,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));
|
||||
@@ -1991,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);
|
||||
@@ -2000,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);
|
||||
@@ -2010,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);
|
||||
|
||||
@@ -714,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()
|
||||
@@ -765,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();
|
||||
|
||||
@@ -3,6 +3,6 @@ package de.dhbwstuttgart.exceptions;
|
||||
public class DebugException extends RuntimeException {
|
||||
|
||||
public DebugException(String message) {
|
||||
super(message);
|
||||
System.err.print(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,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();
|
||||
|
||||
@@ -482,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;
|
||||
@@ -982,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-20
@@ -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);
|
||||
}
|
||||
@@ -438,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;
|
||||
@@ -511,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;
|
||||
@@ -544,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;
|
||||
@@ -563,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;
|
||||
@@ -590,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) {
|
||||
@@ -602,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()));
|
||||
}
|
||||
@@ -616,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,7 +21,6 @@ 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 java.util.*;
|
||||
@@ -54,10 +51,6 @@ public class ASTToTargetAST {
|
||||
|
||||
private Method currentMethod;
|
||||
|
||||
public Method getCurrentMethod() {
|
||||
return currentMethod;
|
||||
}
|
||||
|
||||
public final JavaTXCompiler compiler;
|
||||
|
||||
public List<RefTypeOrTPHOrWildcardOrGeneric> findAllVariants(RefTypeOrTPHOrWildcardOrGeneric type) {
|
||||
@@ -83,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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,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());
|
||||
}
|
||||
@@ -156,10 +125,10 @@ 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;
|
||||
|
||||
@@ -167,11 +136,9 @@ public class ASTToTargetAST {
|
||||
var type1 = pars.get(i);
|
||||
var type2 = arguments.get(i);
|
||||
if (type1 instanceof TargetGenericType)
|
||||
continue;
|
||||
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;
|
||||
return true;
|
||||
if (!type1.equals(type2))
|
||||
return false;
|
||||
}
|
||||
@@ -179,33 +146,6 @@ public class ASTToTargetAST {
|
||||
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) {
|
||||
@@ -232,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()));
|
||||
};
|
||||
}
|
||||
@@ -275,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)));
|
||||
}
|
||||
@@ -294,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);
|
||||
}
|
||||
|
||||
@@ -393,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);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -415,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<>();
|
||||
@@ -446,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>();
|
||||
@@ -536,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));
|
||||
}
|
||||
@@ -577,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;
|
||||
@@ -636,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();
|
||||
@@ -646,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);
|
||||
|
||||
@@ -746,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();
|
||||
|
||||
@@ -784,7 +691,7 @@ 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));
|
||||
|
||||
@@ -808,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;
|
||||
}
|
||||
|
||||
@@ -853,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()));
|
||||
}
|
||||
|
||||
@@ -867,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));
|
||||
|
||||
@@ -925,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) {
|
||||
@@ -951,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;
|
||||
}
|
||||
@@ -995,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) {
|
||||
@@ -1010,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;
|
||||
}
|
||||
|
||||
@@ -1090,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());
|
||||
@@ -1134,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;
|
||||
}
|
||||
@@ -1154,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();
|
||||
|
||||
@@ -1213,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);
|
||||
argList = 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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
+125
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+82
@@ -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())));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
|
||||
import de.dhbwstuttgart.bytecode.CodeGenException;
|
||||
import de.dhbwstuttgart.core.ConsoleInterface;
|
||||
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;
|
||||
@@ -25,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");
|
||||
@@ -332,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
|
||||
@@ -677,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");
|
||||
@@ -1062,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");
|
||||
@@ -1096,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");
|
||||
@@ -1326,15 +1286,6 @@ public class TestComplete {
|
||||
m.invoke(null, List.of("foo", "bar", "baz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazy() throws Exception {
|
||||
ConsoleInterface.logLevel = LogLevel.INFO;
|
||||
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");
|
||||
@@ -1498,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");
|
||||
@@ -1582,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 {
|
||||
@@ -1663,22 +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 {
|
||||
ConsoleInterface.logLevel = LogLevel.INFO;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
Reference in New Issue
Block a user