Compare commits

..
18 Commits
Author SHA1 Message Date
dholle 20b974fd2a Fix instance variable handling
SonarQube Scan / SonarQube Trigger (push) Successful in 3m30s
2026-09-11 12:05:04 +02:00
dholle a504743a40 Throw error message for invalid calls to instance method from static context, fix #401
SonarQube Scan / SonarQube Trigger (push) Successful in 1h21m5s
2026-09-11 10:39:28 +02:00
dholle 70764640d6 Small fix and make use of FunN types
SonarQube Scan / SonarQube Trigger (push) Successful in 2m45s
2026-09-10 17:34:29 +02:00
dholle 090263b6ca Doesn't compile...
SonarQube Scan / SonarQube Trigger (push) Failing after 1m27s
2026-09-10 17:14:57 +02:00
dholle 2e5447c06b Add Primzahlen, fixed a few problems
SonarQube Scan / SonarQube Trigger (push) Failing after 2m17s
Build and Test with Maven / Build-and-test-with-Maven (push) Successful in 1m44s
2026-09-10 16:22:00 +02:00
dholle 1fb9dcd691 Make all tests pass again!
SonarQube Scan / SonarQube Trigger (push) Failing after 1m22s
2026-09-10 15:21:16 +02:00
dholle 2e238efbc5 Implement #402
SonarQube Scan / SonarQube Trigger (push) Failing after 1m52s
2026-09-09 18:30:58 +02:00
dholle c375235680 Fix tests
SonarQube Scan / SonarQube Trigger (push) Failing after 2m48s
2026-09-09 16:11:42 +02:00
dholle d5c0d653d2 Add test for a merge stort
SonarQube Scan / SonarQube Trigger (push) Failing after 1m18s
2026-08-28 18:25:20 +02:00
dholle f7e5a1f8a2 Fix log directory getting deleted, but logger should create it
SonarQube Scan / SonarQube Trigger (push) Failing after 1m17s
2026-08-28 13:21:32 +02:00
dholle 8f6e6e1980 Fix one of the tests 2026-08-28 13:20:58 +02:00
dholle 912f3d381e Fix pattern matching
SonarQube Scan / SonarQube Trigger (push) Failing after 59s
2026-08-28 13:02:53 +02:00
dholle 7e24bbd552 Give error on duplicate fields
SonarQube Scan / SonarQube Trigger (push) Failing after 1m0s
2026-08-27 18:34:24 +02:00
dholle 50f2572644 Also add receiver type to method call disambiguation
SonarQube Scan / SonarQube Trigger (push) Failing after 58s
2026-08-27 17:48:31 +02:00
dholle e1518c8b37 Merge branch 'master' of ssh://gitea.hb.dhbw-stuttgart.de:2222/JavaTX/JavaCompilerCore
SonarQube Scan / SonarQube Trigger (push) Failing after 1m17s
2026-08-27 11:36:23 +02:00
dholle f7a85db191 Discard method calls that are less specific to preserve Java semantics, see #402 2026-08-27 11:36:16 +02:00
dholle 41d5f661e1 Merge branch 'master' of https://gitea.hb.dhbw-stuttgart.de/JavaTX/JavaCompilerCore
SonarQube Scan / SonarQube Trigger (push) Failing after 1m37s
2026-08-25 11:14:53 +02:00
dholle b7f46c428f Add recursive call for BoolExpression 2026-08-25 11:14:27 +02:00
37 changed files with 441 additions and 1042 deletions
+5 -1
View File
@@ -2,7 +2,7 @@ import java.lang.String;
import java.lang.Object; import java.lang.Object;
public class Bug365{ public class Bug365{
swap(f){ swap(Fun1$$<String, Fun1$$<String, Object>> f){
return x -> y -> f.apply(y).apply(x); return x -> y -> f.apply(y).apply(x);
} }
@@ -18,4 +18,8 @@ public class Bug365{
var func = x -> y -> z -> x + y + z; var func = x -> y -> z -> x + y + z;
return swap(func).apply("A").apply("B").apply("C"); 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 -1
View File
@@ -3,7 +3,7 @@ record Cons<T>(T a , List<T> l ) implements List <T> {}
record Empty<T>() implements List <T> {} record Empty<T>() implements List <T> {}
public class Bug380 { public class Bug380 {
public <T> List<T> append(l1, List<T> l2) { public <T> List<T> append(List<T> l1, List<T> l2) {
return switch ( l1 ) { return switch ( l1 ) {
case Cons(e, rest) -> new Cons<>(e, append(rest, l2)); //::Typ TPH A case Cons(e, rest) -> new Cons<>(e, append(rest, l2)); //::Typ TPH A
case Empty() -> l2;//::TPH B case Empty() -> l2;//::TPH B
+1 -1
View File
@@ -4,7 +4,7 @@ import java.lang.String;
public class Fac { public class Fac {
getFac(n) { getFac(n) {
var res = 1; Double res = 1;
var i = 1; var i = 1;
while (i <= n) { while (i <= n) {
res = res * i; res = res * i;
@@ -1,11 +1,9 @@
import java.lang.String; import java.lang.String;
import java.lang.Integer; import java.lang.Integer;
sealed interface List permits LinkedElem, Elem {} 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>{}
public record LinkedElem<T>(T a,List l) implements List{}
public record Elem<T>(T c) implements List{}
public class GenericRecordSwitchCase { public class GenericRecordSwitchCase {
public main(o) { public main(o) {
+3 -1
View File
@@ -5,6 +5,8 @@ import java.lang.Object;
public class HelloWorld { public class HelloWorld {
public static hello() { public static hello() {
System.out.println((Object)"Hello World!"); System.out.println("Hello World!");
System.out.println("Bye World!");
System.out.println("The end!");
} }
} }
+38
View File
@@ -0,0 +1,38 @@
//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.String;
import java.lang.System;
import java.io.PrintStream;
public sealed interface LazyList permits Empty, Cons {
public Integer fst();
public LazyList rest();
}
//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,21 +5,9 @@ public record Empty<T>() implements List<T> {}
public record Pair<T1, T2>(T1 a, T2 b) {} public record Pair<T1, T2>(T1 a, T2 b) {}
public class PatternMatching { public class PatternMatching {
public zip(Cons(x, xs), Cons(y, ys)) { // Anmerkung: Typ muss angegeben werden
public <A, B> Cons<Pair<A, B>> zip(Cons(x, xs), Cons(y, ys)) {
return new Cons<>(new Pair<>(x, y), zip(xs, ys)); return new Cons<>(new Pair<>(x, y), zip(xs, ys));
} }
public zip(Empty(), Empty()) { return new Empty<>(); } 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))
*/
} }
@@ -0,0 +1,60 @@
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;
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 filter(Fun1$$<Integer, Boolean> p, Empty()@l) {
return l;
}
LazyList filter(Fun1$$<Integer, Boolean> p, Cons(Integer x, Fun0$$<LazyList> l1)) {
return p.apply(x) ? new Cons(x, () -> filter(p, l1.apply())) : filter(p, l1.apply());
}*/
LazyList dropMul(Integer x, LazyList xs) {
return 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, Fun0$$<LazyList> 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());
LazyList l = new Cons(1, () -> new Cons(2, () -> new Cons(2, () -> new Empty())));
System.out.println(new Primzahlen().filter(x -> x == 2, l));
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()));
}
}
@@ -0,0 +1,6 @@
import java.lang.Integer;
public record RecordTestInstanceVariables(Integer a, Integer b) {
public Integer getA() { return a; }
public Integer getB() { return b; }
}
+18 -16
View File
@@ -1,23 +1,25 @@
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.lang.String; import java.lang.Integer;
public class Sorting{ public class Sorting {
merge(a, b){ List<Integer> merge(List<Integer> a, List<Integer> b) {
a.addAll(b); var r = new ArrayList<>();
return a; 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;
} }
sort(in){ split(list) {
var firstHalf = in; var mid = list.size() / 2;
var secondHalf = in; return List.of(list.subList(0, mid), list.subList(mid, list.size()));
return merge(sort(firstHalf), sort(secondHalf)); }
}
public sort(in) {
if (in.size() <= 1) return in;
/* var halves = split(in);
void sort(a){ return merge(sort(halves.get(0)), sort(halves.get(1)));
a = merge(a,a);
} }
*/
} }
@@ -0,0 +1,7 @@
public class StaticFail {
void nonStaticM() {}
static void staticM() {
nonStaticM();
}
}
@@ -350,6 +350,8 @@ public class Codegen {
} }
private TargetType largerType(TargetType left, TargetType right) { 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)) { if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
return TargetType.String; return TargetType.String;
} else if (left.equals(TargetType.Double) || right.equals(TargetType.Double)) { } else if (left.equals(TargetType.Double) || right.equals(TargetType.Double)) {
@@ -813,7 +815,9 @@ public class Codegen {
private static TargetType removeGenerics(TargetType param) { private static TargetType removeGenerics(TargetType param) {
return switch (param) { return switch (param) {
case null -> null; case null -> null;
case TargetFunNType funNType -> new TargetFunNType(funNType.name(), funNType.funNParams().stream().map(Codegen::removeGenerics).toList(), List.of(), funNType.returnArguments()); case TargetFunNType funNType -> new TargetFunNType(funNType.name(),
funNType.funNParams().stream().map(Codegen::removeGenerics).toList(), List.of(),
funNType.returnArguments(), funNType.isInterface());
case TargetRefType refType -> new TargetRefType(refType.name()); case TargetRefType refType -> new TargetRefType(refType.name());
case TargetGenericType targetGenericType -> TargetType.Object; case TargetGenericType targetGenericType -> TargetType.Object;
default -> param; default -> param;
@@ -1185,6 +1189,12 @@ public class Codegen {
} }
case TargetMethodCall call: { case TargetMethodCall call: {
if (!call.isStatic()) { 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()); generate(state, call.expr());
boxPrimitive(state, call.expr().type()); boxPrimitive(state, call.expr().type());
} }
@@ -1440,6 +1450,7 @@ public class Codegen {
for (var i = 0; i < aSwitch.cases().size(); i++) { for (var i = 0; i < aSwitch.cases().size(); i++) {
mv.visitLabel(caseLabels[i]); mv.visitLabel(caseLabels[i]);
var cse = aSwitch.cases().get(i); var cse = aSwitch.cases().get(i);
state.enterScope();
if (cse.labels().size() == 1) { if (cse.labels().size() == 1) {
var label = cse.labels().get(0); var label = cse.labels().get(0);
@@ -1468,6 +1479,8 @@ public class Codegen {
if (cse.isSingleExpression() && aSwitch.isExpression()) if (cse.isSingleExpression() && aSwitch.isExpression())
yieldValue(state, cse.body().statements().get(0).type()); yieldValue(state, cse.body().statements().get(0).type());
if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end); if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end);
state.exitScope();
} }
mv.visitLabel(defaultLabel); mv.visitLabel(defaultLabel);
@@ -1911,7 +1924,7 @@ public class Codegen {
bootstrapArgs[i + 2] = fieldRef; bootstrapArgs[i + 2] = fieldRef;
} }
{ // hashCode if (clazz.methods().stream().filter(m -> m.getDescriptor().equals("()I") && m.name().equals("hashCode")).findFirst().isEmpty()) { // hashCode
var mv = cw.visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null); var mv = cw.visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null);
mv.visitCode(); mv.visitCode();
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
@@ -1920,7 +1933,7 @@ public class Codegen {
mv.visitMaxs(0, 0); mv.visitMaxs(0, 0);
mv.visitEnd(); mv.visitEnd();
} }
{ // equals if (clazz.methods().stream().filter(m -> m.getDescriptor().equals("(Ljava/lang/Object;)Z") && m.name().equals("equals")).findFirst().isEmpty()) { // equals
var mv = cw.visitMethod(ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null); var mv = cw.visitMethod(ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
mv.visitCode(); mv.visitCode();
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
@@ -1930,7 +1943,7 @@ public class Codegen {
mv.visitMaxs(0, 0); mv.visitMaxs(0, 0);
mv.visitEnd(); mv.visitEnd();
} }
{ // toString if (clazz.methods().stream().filter(m -> m.getDescriptor().equals("()Ljava/lang/String;") && m.name().equals("toString")).findFirst().isEmpty()) { // toString
var mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null); var mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
mv.visitCode(); mv.visitCode();
mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0);
@@ -714,6 +714,9 @@ public class JavaTXCompiler {
new SuperWildcardType(toRefType(targetSuperWildcard.innerType()), new NullToken()); new SuperWildcardType(toRefType(targetSuperWildcard.innerType()), new NullToken());
case TargetGenericType targetGenericType -> new GenericRefType(targetGenericType.name(), new NullToken()); case TargetGenericType targetGenericType -> new GenericRefType(targetGenericType.name(), new NullToken());
case TargetPrimitiveType targetPrimitiveType -> toRefType(TargetType.toWrapper(targetPrimitiveType)); 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 -> case TargetSpecializedType targetSpecializedType ->
new RefType(new JavaClassName(targetSpecializedType.name()), new RefType(new JavaClassName(targetSpecializedType.name()),
targetSpecializedType.params().stream().map(JavaTXCompiler::toRefType).toList(), new NullToken() targetSpecializedType.params().stream().map(JavaTXCompiler::toRefType).toList(), new NullToken()
@@ -762,9 +765,9 @@ public class JavaTXCompiler {
); );
} }
var generics = new GenericDeclarationList(toGenerics(target.generics()), new NullToken());
var superClass = (RefType) toRefType(target.superType());
var isInterface = target instanceof TargetInterface; var isInterface = target instanceof TargetInterface;
var generics = new GenericDeclarationList(toGenerics(target.generics()), new NullToken());
var superClass = isInterface ? ASTFactory.createObjectType() : (RefType) toRefType(target.superType());
var isFunctionalInterface = false; // TODO We might actually want to generate those var isFunctionalInterface = false; // TODO We might actually want to generate those
var implementedInterfaces = target.implementingInterfaces().stream() var implementedInterfaces = target.implementingInterfaces().stream()
.map(t -> (RefType) toRefType(t)).toList(); .map(t -> (RefType) toRefType(t)).toList();
@@ -3,6 +3,6 @@ package de.dhbwstuttgart.exceptions;
public class DebugException extends RuntimeException { public class DebugException extends RuntimeException {
public DebugException(String message) { public DebugException(String message) {
System.err.print(message); super(message);
} }
} }
@@ -482,8 +482,8 @@ public class StatementGenerator {
IdentifierContext identifierCtx = recordPatternCtx.identifier(); IdentifierContext identifierCtx = recordPatternCtx.identifier();
var text = (identifierCtx != null) ? identifierCtx.getText() : null; var text = (identifierCtx != null) ? identifierCtx.getText() : null;
//Hier evtl. Typ anpassen -> wenn kein Typ bekannt ist push neuen Typ auf Hashtable //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 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); if (text != null) localVars.put(text, type);
var ret = new RecordPattern(subPattern, text, type, (RefType)ctor, recordPatternCtx.getStart()); var ret = new RecordPattern(subPattern, text, type, (RefType)ctor, recordPatternCtx.getStart());
return ret; return ret;
@@ -982,8 +982,11 @@ public class StatementGenerator {
} else if (op.getText().equals("!")) { } else if (op.getText().equals("!")) {
ret = new UnaryExpr(UnaryExpr.Operation.NOT, expr, TypePlaceholder.fresh(op), op); ret = new UnaryExpr(UnaryExpr.Operation.NOT, expr, TypePlaceholder.fresh(op), op);
return ret; return ret;
} else if (op.getText().equals("-")) {
ret = new UnaryExpr(UnaryExpr.Operation.MINUS, expr, TypePlaceholder.fresh(op), op);
return ret;
} else { } else {
throw new NotImplementedException(); throw new NotImplementedException(op.getText());
} }
} }
@@ -80,7 +80,7 @@ public class SyntaxTreeGenerator {
HashMap<String, Integer> allmodifiers = new HashMap<>(); HashMap<String, Integer> allmodifiers = new HashMap<>();
// PL 2018-11-01 fields eingefuegt, damit die fields immer die gleiche TPH // PL 2018-11-01 fields eingefuegt, damit die fields immer die gleiche TPH
// bekommen // 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 // PL 2019-10-23: Muss für jede Klasse neu initilisiert werden
List<Statement> fieldInitializations = new ArrayList<>(); List<Statement> fieldInitializations = new ArrayList<>();
List<Statement> staticFieldInitializations = new ArrayList<>(); List<Statement> staticFieldInitializations = new ArrayList<>();
@@ -178,6 +178,7 @@ public class SyntaxTreeGenerator {
} }
private ClassOrInterface convertClass(Java17Parser.ClassDeclarationContext ctx, int modifiers) { 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(); String className = this.pkgName + (this.pkgName.length() > 0 ? "." : "") + ctx.identifier().getText();
JavaClassName name = reg.getName(className); // Holt den Package Namen mit dazu 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? if (!name.toString().equals(className)) { // Kommt die Klasse schon in einem anderen Package vor?
@@ -206,7 +207,7 @@ public class SyntaxTreeGenerator {
List<RefType> implementedInterfaces = new ArrayList<>(); List<RefType> implementedInterfaces = new ArrayList<>();
List<RefType> permittedSubtypes = null; List<RefType> permittedSubtypes = null;
for (ClassBodyDeclarationContext clsbodydecl : ctx.classBody().classBodyDeclaration()) { for (ClassBodyDeclarationContext clsbodydecl : ctx.classBody().classBodyDeclaration()) {
convert(clsbodydecl, fielddecl, constructors, methods, name, superClass, generics); convert(clsbodydecl, fielddecl, constructors, methods, name, superClass, generics, fieldDecls);
} }
if (constructors.isEmpty()) { if (constructors.isEmpty()) {
constructors.add(generateStandardConstructor(ctx.identifier().getText(), name, superClass, genericClassParameters, offset)); constructors.add(generateStandardConstructor(ctx.identifier().getText(), name, superClass, genericClassParameters, offset));
@@ -255,6 +256,8 @@ public class SyntaxTreeGenerator {
List<Pattern> constructorParameters = new ArrayList<>(); List<Pattern> constructorParameters = new ArrayList<>();
List<Statement> constructorStatements = new ArrayList<>(); List<Statement> constructorStatements = new ArrayList<>();
var fieldDecl = new HashMap<String, FieldEntry>();
List<Java17Parser.RecordComponentContext> components = recordDeclaration.recordHeader().recordComponentList() != null ? List<Java17Parser.RecordComponentContext> components = recordDeclaration.recordHeader().recordComponentList() != null ?
recordDeclaration.recordHeader().recordComponentList().recordComponent(): List.of(); recordDeclaration.recordHeader().recordComponentList().recordComponent(): List.of();
for (RecordComponentContext component : components) { for (RecordComponentContext component : components) {
@@ -270,6 +273,7 @@ public class SyntaxTreeGenerator {
fielddecl.add(new Field(fieldname, fieldtype, fieldmodifiers, fieldoffset)); fielddecl.add(new Field(fieldname, fieldtype, fieldmodifiers, fieldoffset));
constructorParameters.add(new FormalParameter(fieldname, fieldtype, fieldoffset)); constructorParameters.add(new FormalParameter(fieldname, fieldtype, fieldoffset));
FieldVar fieldvar = new FieldVar(new This(offset), 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)); constructorStatements.add(new Assign(new AssignToField(fieldvar), new LocalVar(fieldname, fieldtype, fieldoffset), offset));
Statement returnStatement = new Return(fieldvar, 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)); 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));
@@ -279,7 +283,7 @@ public class SyntaxTreeGenerator {
//Optional<Constructor> initializations = Optional.of(implicitConstructor); //Optional<Constructor> initializations = Optional.of(implicitConstructor);
constructors.add(implicitConstructor); constructors.add(implicitConstructor);
for (ClassBodyDeclarationContext bodyDeclaration : recordDeclaration.recordBody().classBodyDeclaration()) { for (ClassBodyDeclarationContext bodyDeclaration : recordDeclaration.recordBody().classBodyDeclaration()) {
convert(bodyDeclaration, fielddecl, constructors, methods, name, superClass, generics); convert(bodyDeclaration, fielddecl, constructors, methods, name, superClass, generics, fieldDecl);
} }
if (!Objects.isNull(recordDeclaration.IMPLEMENTS())) { if (!Objects.isNull(recordDeclaration.IMPLEMENTS())) {
implementedInterfaces.addAll(convert(recordDeclaration.typeList(), generics)); implementedInterfaces.addAll(convert(recordDeclaration.typeList(), generics));
@@ -288,7 +292,7 @@ public class SyntaxTreeGenerator {
return new Record(modifiers, name, fielddecl, Optional.empty(), staticCtor, methods, constructors, genericClassParameters, superClass, isInterface, implementedInterfaces, offset, fileName); 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) { private void convert(ClassBodyDeclarationContext classBody, List<Field> fields, List<Constructor> constructors, List<Method> methods, JavaClassName name, RefType superClass, GenericsRegistry generics, HashMap<String, FieldEntry> fieldDecls) {
MemberdeclContext member; MemberdeclContext member;
if (classBody instanceof MemberdeclContext) { if (classBody instanceof MemberdeclContext) {
member = (MemberdeclContext) classBody; member = (MemberdeclContext) classBody;
@@ -303,11 +307,11 @@ public class SyntaxTreeGenerator {
break; break;
} }
case MemberfieldContext memberfield: { case MemberfieldContext memberfield: {
fields.addAll(convert(memberfield.fieldDeclaration(), membermodifiers, generics)); fields.addAll(convert(memberfield.fieldDeclaration(), membermodifiers, generics, fieldDecls));
break; break;
} }
case MembermethodContext membermethod: { case MembermethodContext membermethod: {
Method convertedMethod = convert(membermodifiers, membermethod.method(), name, superClass, generics); Method convertedMethod = convert(membermodifiers, membermethod.method(), name, superClass, generics, fieldDecls);
if (convertedMethod instanceof Constructor constructor) { if (convertedMethod instanceof Constructor constructor) {
constructors.add(constructor); constructors.add(constructor);
} else { } else {
@@ -316,7 +320,7 @@ public class SyntaxTreeGenerator {
break; break;
} }
case MemberconstructorContext memberconstructor: { case MemberconstructorContext memberconstructor: {
constructors.add(convert(membermodifiers, memberconstructor.constructor(), name, superClass, generics)); constructors.add(convert(membermodifiers, memberconstructor.constructor(), name, superClass, generics, fieldDecls));
break; break;
} }
default: default:
@@ -324,7 +328,7 @@ public class SyntaxTreeGenerator {
} }
} else if (classBody instanceof Java17Parser.ClassblockContext ctx && ctx.STATIC() != null) { } else if (classBody instanceof Java17Parser.ClassblockContext ctx && ctx.STATIC() != null) {
// Static blocks // Static blocks
var stmtgen = new StatementGenerator(superClass, compiler, reg, generics, this.fields, new HashMap<>()); var stmtgen = new StatementGenerator(superClass, compiler, reg, generics, fieldDecls, new HashMap<>());
var block = stmtgen.convert(((Java17Parser.ClassblockContext) classBody).block(), false); var block = stmtgen.convert(((Java17Parser.ClassblockContext) classBody).block(), false);
staticFieldInitializations.addAll(block.statements); staticFieldInitializations.addAll(block.statements);
} }
@@ -434,7 +438,7 @@ public class SyntaxTreeGenerator {
retType = new Void(bodydeclaration.refType().getStart()); retType = new Void(bodydeclaration.refType().getStart());
} }
} }
StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, generics, fields, new HashMap<>()); StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, generics, new HashMap<>(), new HashMap<>());
ParameterList paramlist = stmtgen.convert(bodydeclaration.formalParameters().formalParameterList(), true); ParameterList paramlist = stmtgen.convert(bodydeclaration.formalParameters().formalParameterList(), true);
MethodBodyContext body = bodydeclaration.methodBody(); MethodBodyContext body = bodydeclaration.methodBody();
Block block = null; Block block = null;
@@ -507,7 +511,7 @@ public class SyntaxTreeGenerator {
return ret; return ret;
} }
public Method convert(int modifiers, Java17Parser.MethodContext methodContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics) { public Method convert(int modifiers, Java17Parser.MethodContext methodContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics, Map<String, FieldEntry> fieldDecls) {
GenericsRegistry localgenerics = generics; GenericsRegistry localgenerics = generics;
MethodDeclarationContext methoddeclaration; MethodDeclarationContext methoddeclaration;
GenericDeclarationListContext genericdeclarations; GenericDeclarationListContext genericdeclarations;
@@ -540,7 +544,7 @@ public class SyntaxTreeGenerator {
retType = new Void(header.refType().getStart()); retType = new Void(header.refType().getStart());
} }
} }
StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, localgenerics, fields, new HashMap<>()); StatementGenerator stmtgen = new StatementGenerator(superClass, compiler, reg, localgenerics, fieldDecls, new HashMap<>());
ParameterList paramlist = stmtgen.convert(header.formalParameters().formalParameterList(), true); ParameterList paramlist = stmtgen.convert(header.formalParameters().formalParameterList(), true);
MethodBodyContext body = methoddeclaration.methodBody(); MethodBodyContext body = methoddeclaration.methodBody();
Block block = null; Block block = null;
@@ -559,7 +563,7 @@ public class SyntaxTreeGenerator {
} }
} }
public Constructor convert(int modifiers, Java17Parser.ConstructorContext constructorContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics) { public Constructor convert(int modifiers, Java17Parser.ConstructorContext constructorContext, JavaClassName parentClass, RefType superClass, GenericsRegistry generics, Map<String, FieldEntry> fields) {
GenericsRegistry localgenerics = generics; GenericsRegistry localgenerics = generics;
GenericDeclarationListContext genericdeclarations; GenericDeclarationListContext genericdeclarations;
GenericDeclarationList gtvDeclarations; GenericDeclarationList gtvDeclarations;
@@ -586,7 +590,7 @@ public class SyntaxTreeGenerator {
return new Constructor(modifiers, name, retType, paramlist, block, gtvDeclarations, constructordeclaration.getStart()); return new Constructor(modifiers, name, retType, paramlist, block, gtvDeclarations, constructordeclaration.getStart());
} }
List<? extends Field> convert(Java17Parser.FieldDeclarationContext fieldDeclContext, int modifiers, GenericsRegistry generics) { List<? extends Field> convert(Java17Parser.FieldDeclarationContext fieldDeclContext, int modifiers, GenericsRegistry generics, HashMap<String, FieldEntry> fields) {
List<Field> ret = new ArrayList<>(); List<Field> ret = new ArrayList<>();
RefTypeOrTPHOrWildcardOrGeneric fieldType; RefTypeOrTPHOrWildcardOrGeneric fieldType;
if (fieldDeclContext.typeType() != null) { if (fieldDeclContext.typeType() != null) {
@@ -598,9 +602,9 @@ public class SyntaxTreeGenerator {
} }
for (Java17Parser.VariableDeclaratorContext varDecl : fieldDeclContext.variableDeclarators().variableDeclarator()) { for (Java17Parser.VariableDeclaratorContext varDecl : fieldDeclContext.variableDeclarators().variableDeclarator()) {
String fieldName = varDecl.variableDeclaratorId().getText(); String fieldName = varDecl.variableDeclaratorId().getText();
this.fields.put(fieldName, new FieldEntry(fieldName, fieldType, modifiers)); fields.put(fieldName, new FieldEntry(fieldName, fieldType, modifiers));
if (varDecl.variableInitializer() != null) { if (varDecl.variableInitializer() != null) {
initializeField(varDecl, Modifier.isStatic(modifiers), fieldType, generics); initializeField(varDecl, Modifier.isStatic(modifiers), fieldType, generics, fields);
} }
ret.add(new Field(fieldName, fieldType, modifiers, varDecl.getStart())); ret.add(new Field(fieldName, fieldType, modifiers, varDecl.getStart()));
} }
@@ -612,7 +616,7 @@ public class SyntaxTreeGenerator {
} }
// Initialize a field by creating implicit constructor. // Initialize a field by creating implicit constructor.
private void initializeField(Java17Parser.VariableDeclaratorContext ctx, boolean isStatic, RefTypeOrTPHOrWildcardOrGeneric typeOfField, GenericsRegistry generics) { private void initializeField(Java17Parser.VariableDeclaratorContext ctx, boolean isStatic, RefTypeOrTPHOrWildcardOrGeneric typeOfField, GenericsRegistry generics, Map<String, FieldEntry> fields) {
StatementGenerator statementGenerator = new StatementGenerator(superClass, compiler, reg, generics, fields, new HashMap<>()); StatementGenerator statementGenerator = new StatementGenerator(superClass, compiler, reg, generics, fields, new HashMap<>());
var assignment = statementGenerator.generateFieldAssignment(ctx, typeOfField); var assignment = statementGenerator.generateFieldAssignment(ctx, typeOfField);
if (isStatic) { if (isStatic) {
@@ -51,6 +51,10 @@ public class ASTToTargetAST {
private Method currentMethod; private Method currentMethod;
public Method getCurrentMethod() {
return currentMethod;
}
public final JavaTXCompiler compiler; public final JavaTXCompiler compiler;
public List<RefTypeOrTPHOrWildcardOrGeneric> findAllVariants(RefTypeOrTPHOrWildcardOrGeneric type) { public List<RefTypeOrTPHOrWildcardOrGeneric> findAllVariants(RefTypeOrTPHOrWildcardOrGeneric type) {
@@ -76,8 +80,8 @@ public class ASTToTargetAST {
this(new JavaGenerics(compiler, set), new TxGenerics(compiler, set)); this(new JavaGenerics(compiler, set), new TxGenerics(compiler, set));
} }
public static Generics nullGenerics() { public static Generics nullGenerics(JavaTXCompiler compiler) {
return new Generics(null, new ResultSet(Set.of())); return new Generics(compiler, new ResultSet(Set.of()));
} }
} }
@@ -109,14 +113,16 @@ public class ASTToTargetAST {
} }
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, JavaTXCompiler compiler) { public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, JavaTXCompiler compiler) {
return findMethod(owner, name, argumentList, Generics.nullGenerics().javaGenerics(), compiler); return findMethod(owner, name, argumentList, Generics.nullGenerics(compiler).javaGenerics(), compiler);
} }
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) { public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) {
Optional<Method> method = Optional.empty(); Optional<Method> method = Optional.empty();
while (method.isEmpty()) { while (method.isEmpty()) {
method = owner.getMethods().stream().filter(m -> m.name.equals(name) && method = owner.getMethods().stream().filter(m -> {
parameterEquals(m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst(); return 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; if (owner.getClassName().toString().equals("java.lang.Object")) break;
owner = compiler.getClass(owner.getSuperClass().getName()); owner = compiler.getClass(owner.getSuperClass().getName());
} }
@@ -172,7 +178,7 @@ public class ASTToTargetAST {
case TargetSuperWildcard targetSuperWildcard -> new SuperType(toUnifyType(targetSuperWildcard.innerType())); case TargetSuperWildcard targetSuperWildcard -> new SuperType(toUnifyType(targetSuperWildcard.innerType()));
case TargetGenericType targetGenericType -> new PlaceholderType(targetGenericType.name(), JavaTXCompiler.defaultClientPlaceholderRegistry); case TargetGenericType targetGenericType -> new PlaceholderType(targetGenericType.name(), JavaTXCompiler.defaultClientPlaceholderRegistry);
case TargetPrimitiveType _ -> throw new NotImplementedException(); case TargetPrimitiveType _ -> throw new NotImplementedException();
case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.params().stream().map(ASTToTargetAST::toUnifyType).toList())); case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.funNParams().stream().map(ASTToTargetAST::toUnifyType).toList()));
case TargetRefType targetRefType -> new ReferenceType(targetRefType.name(), new TypeParams(targetRefType.params().stream().map(ASTToTargetAST::toUnifyType).toList())); case TargetRefType targetRefType -> new ReferenceType(targetRefType.name(), new TypeParams(targetRefType.params().stream().map(ASTToTargetAST::toUnifyType).toList()));
}; };
} }
@@ -182,7 +188,7 @@ public class ASTToTargetAST {
case ExtendsType extendType -> new TargetExtendsWildcard(toTargetType(extendType.getExtendedType())); case ExtendsType extendType -> new TargetExtendsWildcard(toTargetType(extendType.getExtendedType()));
case SuperType superType -> new TargetSuperWildcard(toTargetType(superType.getSuperedType())); case SuperType superType -> new TargetSuperWildcard(toTargetType(superType.getSuperedType()));
case PlaceholderType placeholderType -> new TargetGenericType(placeholderType.getName()); case PlaceholderType placeholderType -> new TargetGenericType(placeholderType.getName());
case FunNType funNType -> TargetFunNType.fromParams(StreamSupport.stream(funNType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList(), 1); // FIXME How does this work with Fun0?? case FunNType funNType -> TargetFunNType.fromParams(StreamSupport.stream(funNType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList(), 1, false); // FIXME How does this work with Fun0??
case ReferenceType referenceType -> new TargetRefType(referenceType.getName(), StreamSupport.stream(referenceType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList()); case ReferenceType referenceType -> new TargetRefType(referenceType.getName(), StreamSupport.stream(referenceType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList());
default -> throw new NotImplementedException(); default -> throw new NotImplementedException();
}; };
@@ -215,9 +221,12 @@ public class ASTToTargetAST {
private static Optional<TargetType> unify(TargetType a, TargetType b) { private static Optional<TargetType> unify(TargetType a, TargetType b) {
if (typesStrictlyEqual(a, b)) return Optional.ofNullable(a); if (typesStrictlyEqual(a, b)) return Optional.ofNullable(a);
if (a instanceof TargetFunNType || b instanceof TargetFunNType) return Optional.empty();
var unify = new MartelliMontanariUnify(); var unify = new MartelliMontanariUnify();
var ua = toUnifyType(a); var ua = toUnifyType(a);
var unifier = unify.unify(Set.of(ua, toUnifyType(b))); var ub = toUnifyType(b);
if (Objects.equals(ua, ub)) return Optional.of(a);
var unifier = unify.unify(Set.of(ua, ub));
if (unifier.isEmpty()) return Optional.empty(); if (unifier.isEmpty()) return Optional.empty();
return Optional.of(toTargetType(unifier.get().apply(ua))); return Optional.of(toTargetType(unifier.get().apply(ua)));
} }
@@ -331,15 +340,16 @@ public class ASTToTargetAST {
var u_opt = unify(m, m1); var u_opt = unify(m, m1);
if (u_opt.isPresent()) { if (u_opt.isPresent()) {
var u = u_opt.get(); var u = u_opt.get();
//Target.logger.info("Unified " + m + " AND " + m1 + "\n\t" + u); //System.out.println("Unified " + m + " AND " + m1 + "\n\t" + u);
i.remove(m1); i.remove(m1);
R.remove(m); R.remove(m);
R.remove(m1); R.remove(m1);
R.add(u); R.add(u);
a.add(u); a.add(u);
} /*else { m = u;
Target.logger.info("Couldn't unify " + m + " AND " + m1); } else {
}*/ //System.out.println("Couldn't unify " + m + " AND " + m1);
}
} }
} }
} }
@@ -352,6 +362,8 @@ public class ASTToTargetAST {
return R; return R;
} }
private static final record CtorWithGenerics(TargetConstructor ctor, IGenerics generics) {}
public TargetStructure convert(ClassOrInterface input) { public TargetStructure convert(ClassOrInterface input) {
var generics = all.getFirst(); var generics = all.getFirst();
Set<TargetGeneric> javaGenerics = new HashSet<>(); Set<TargetGeneric> javaGenerics = new HashSet<>();
@@ -381,8 +393,33 @@ public class ASTToTargetAST {
TargetBlock finalFieldInitializer = fieldInitializer; TargetBlock finalFieldInitializer = fieldInitializer;
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).toList(); var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).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 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 m0 = groupMethods(input, input.getMethods()); var m0 = groupMethods(input, input.getMethods());
var m1 = new ArrayList<TargetMethod>(); var m1 = new ArrayList<TargetMethod>();
@@ -446,7 +483,7 @@ public class ASTToTargetAST {
var res = new ArrayList<MethodParameter>(); var res = new ArrayList<MethodParameter>();
for (var i = 0; i < input.getFormalparalist().size(); i++) { for (var i = 0; i < input.getFormalparalist().size(); i++) {
var param = input.getFormalparalist().get(i); var param = input.getFormalparalist().get(i);
var pattern = (TargetPattern) convert(param, Generics.nullGenerics().javaGenerics); var pattern = (TargetPattern) convert(param, Generics.nullGenerics(compiler).javaGenerics);
if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i); if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i);
res.add(new MethodParameter(pattern)); res.add(new MethodParameter(pattern));
} }
@@ -487,29 +524,19 @@ public class ASTToTargetAST {
return convertedGenerics; return convertedGenerics;
} }
private List<TargetConstructor> convert(ClassOrInterface currentClass, Constructor input, TargetBlock fieldInitializer, Generics generics) { private 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.currentMethod = input;
this.usedTPHsOfMethods.put(input, new HashSet<>()); this.usedTPHsOfMethods.put(input, new HashSet<>());
for (var s : all) {
generics = s;
var javaGenerics = generics.javaGenerics.generics(currentClass, input); var javaGenerics = generics.javaGenerics.generics(currentClass, input);
var txGenerics = generics.txGenerics.generics(currentClass, input); var txGenerics = generics.txGenerics.generics(currentClass, input);
List<MethodParameter> params = convert(input.getParameterList(), generics.javaGenerics); List<MethodParameter> params = convert(input.getParameterList(), generics.javaGenerics);
if (parameterSet.stream().noneMatch(p -> p.equals(params))) {
List<MethodParameter> txParams = convert(input.getParameterList(), generics.txGenerics); List<MethodParameter> txParams = convert(input.getParameterList(), generics.txGenerics);
var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, input); var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, input);
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, input); var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, input);
result.add(new TargetConstructor(input.modifier, javaMethodGenerics, txMethodGenerics, params, txParams, convert(input.block, generics.javaGenerics), fieldInitializer)); this.currentMethod = null;
parameterSet.add(params); return new TargetConstructor(input.modifier, javaMethodGenerics, txMethodGenerics, params, txParams, convert(input.block, generics.javaGenerics), fieldInitializer);
}
}
return result;
} }
private static int counter = 0; private static int counter = 0;
@@ -655,7 +682,9 @@ public class ASTToTargetAST {
private Optional<TargetMethod> generateBridgeMethod(ClassOrInterface clazz, List<TargetMethod> methods) { private Optional<TargetMethod> generateBridgeMethod(ClassOrInterface clazz, List<TargetMethod> methods) {
// If there's only one method we don't need a bridge // 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(); if (methods.size() <= 1) return Optional.empty();
var firstMethod = methods.getFirst(); var firstMethod = methods.getFirst();
var ra = firstMethod.signature().returnType(); var ra = firstMethod.signature().returnType();
@@ -693,6 +722,7 @@ public class ASTToTargetAST {
var classType = new TargetRefType(clazz.getClassName().getClassName()); 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 stmt = generatePatternOverloadsRec(0, new TargetLocalVar(resParams.getFirst().pattern().type(), resParams.getFirst().pattern().name()), parameters, List.of(), methods, classType);
System.out.println(stmt);
var block = new TargetBlock(List.of(stmt)); var block = new TargetBlock(List.of(stmt));
var generics = new HashSet<>(firstMethod.signature().generics()); var generics = new HashSet<>(firstMethod.signature().generics());
@@ -760,7 +790,12 @@ public class ASTToTargetAST {
generics.addOverlay(tph, signatureParams.get(i).pattern().type()); generics.addOverlay(tph, signatureParams.get(i).pattern().type());
} }
} }
var tMethod = convert(method, generics); TargetMethod tMethod;
try {
tMethod = convert(method, generics);
} catch (DiscardResultSet ignored) {
continue;
}
res.add(new TargetMethod(tMethod.access(), name, tMethod.block(), tMethod.signature(), tMethod.txSignature(), tMethod.base(), tMethod.generics())); res.add(new TargetMethod(tMethod.access(), name, tMethod.block(), tMethod.signature(), tMethod.txSignature(), tMethod.base(), tMethod.generics()));
} }
@@ -827,7 +862,9 @@ public class ASTToTargetAST {
private TargetMethod convert(MethodWithTphs mtph, IGenerics generics) { private TargetMethod convert(MethodWithTphs mtph, IGenerics generics) {
this.currentMethod = mtph.method; this.currentMethod = mtph.method;
return new TargetMethod(mtph.method.modifier, mtph.method.name, convert(mtph.method.block, generics), mtph.signature.java(), mtph.signature.tx(), mtph.method, generics); 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;
} }
record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) { record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {
@@ -851,7 +888,12 @@ public class ASTToTargetAST {
for (var tph : tphs) { for (var tph : tphs) {
var left = a.getTargetType(tph); var left = a.getTargetType(tph);
var right = b.getTargetType(tph); var right = b.getTargetType(tph);
if (!Objects.equals(left, right)) return true; if (left instanceof TargetExtendsWildcard wc) left = wc.innerType();
if (right instanceof TargetExtendsWildcard wc) right = wc.innerType();
if (!Objects.equals(left, right)) {
return true;
}
} }
return false; return false;
} }
@@ -889,10 +931,13 @@ public class ASTToTargetAST {
signatures.add(new Signature(javaSignature, txSignature, generics)); signatures.add(new Signature(javaSignature, txSignature, generics));
} }
if (!signatures.isEmpty()) { for (var signature : new ArrayList<>(signatures)) {
var signature = signatures.getFirst(); try {
// We need to convert once to find out what TPHs are existing in the given method
convert(new MethodWithTphs(method, signature.generics, signature)); 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);
}
} }
for (var signature : signatures) { for (var signature : signatures) {
@@ -908,6 +953,7 @@ public class ASTToTargetAST {
result.add(mtph); result.add(mtph);
} }
this.currentMethod = null;
return result; return result;
} }
@@ -959,7 +1005,7 @@ public class ASTToTargetAST {
} }
} }
static TargetType flattenFunNType(List<TargetType> params, FunNGenerator.GenericParameters gep) { static TargetType flattenFunNType(List<TargetType> params, FunNGenerator.GenericParameters gep, boolean isInterface) {
var newParams = new ArrayList<TargetType>(); var newParams = new ArrayList<TargetType>();
for (TargetType param : params) { for (TargetType param : params) {
if (param instanceof TargetSpecializedType fn) { if (param instanceof TargetSpecializedType fn) {
@@ -973,22 +1019,31 @@ public class ASTToTargetAST {
if (i < gep.inParams.size() && gep.inParams.get(i) != null) if (i < gep.inParams.size() && gep.inParams.get(i) != null)
filteredParams.add(newParams.get(i)); filteredParams.add(newParams.get(i));
} }
return TargetFunNType.fromParams(params, filteredParams, gep.getReturnType() != null ? 1 : 0); return TargetFunNType.fromParams(params, filteredParams, gep.getReturnType() != null ? 1 : 0, isInterface);
} }
private FunNGenerator.GenericParameters convertToParameters(TargetFunNType input) { private FunNGenerator.GenericParameters convertToParameters(TargetFunNType input) {
return null; return null;
} }
private boolean isSubtype(TargetType test, TargetType other) { public boolean isSubtype(TargetType test, TargetType other) {
if (other == null) return false;
if (other.equals(TargetType.Object)) return true; 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) if (test instanceof TargetFunNType tfun && other instanceof TargetFunNType ofun)
return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun)); return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun));
if (other instanceof TargetFunNType) return false;
var testClass = compiler.getClass(new JavaClassName(test.name())); var testClass = compiler.getClass(new JavaClassName(test.name()));
var otherClass = compiler.getClass(new JavaClassName(other.name())); var otherClass = compiler.getClass(new JavaClassName(other.name()));
if (testClass == null) return false; if (testClass == null) return false;
while (testClass != null) { 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)) return true;
}
}
if (testClass.equals(otherClass)) return true; if (testClass.equals(otherClass)) return true;
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break; if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break;
testClass = compiler.getClass(testClass.getSuperClass().getName()); testClass = compiler.getClass(testClass.getSuperClass().getName());
@@ -1074,7 +1129,9 @@ public class ASTToTargetAST {
} else { } else {
gep = compiler.usedFunN.get(className); gep = compiler.usedFunN.get(className);
} }
return flattenFunNType(params, gep); return flattenFunNType(params, gep, true);
} else if (name.matches("Fun\\d+\\$\\$.*")) {
return new TargetFunNType(name, List.of(), List.of(), 0, false);
} }
return new TargetRefType(name, params); return new TargetRefType(name, params);
} }
@@ -0,0 +1,9 @@
package de.dhbwstuttgart.target.generate;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import java.util.List;
public class DiscardResultSet extends RuntimeException {
public DiscardResultSet() {}
}
@@ -153,7 +153,6 @@ public abstract class GenerateGenerics implements IGenerics {
} }
Target.logger.info("Simplified constraints: " + simplifiedConstraints); Target.logger.info("Simplified constraints: " + simplifiedConstraints);
} }
public record GenericsState(Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes, Map<TypePlaceholder, TypePlaceholder> equality) {} public record GenericsState(Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes, Map<TypePlaceholder, TypePlaceholder> equality) {}
@@ -1009,7 +1008,9 @@ public abstract class GenerateGenerics implements IGenerics {
public TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) { public TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
if (in instanceof TypePlaceholder tph) { if (in instanceof TypePlaceholder tph) {
if (equality.containsKey(tph)) { if (equality.containsKey(tph)) {
return getTargetType(equality.get(tph)); var tph2 = equality.get(tph);
// Sanity check, they should not be equal!
if (!Objects.equals(in, tph2)) return getTargetType(tph2);
} }
var type = concreteTypes.get(new TPH(tph)); var type = concreteTypes.get(new TPH(tph));
if (type == null) return new TargetGenericType(tph.getName()); if (type == null) return new TargetGenericType(tph.getName());
@@ -1,5 +1,6 @@
package de.dhbwstuttgart.target.generate; package de.dhbwstuttgart.target.generate;
import com.google.common.collect.Iterables;
import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.exceptions.DebugException; import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.NotImplementedException; import de.dhbwstuttgart.exceptions.NotImplementedException;
@@ -230,6 +231,8 @@ public class StatementToTargetExpression implements ASTVisitor {
var argList = methodCall.signature.stream().map(sig -> converter.convert(sig, generics)).toList(); var argList = methodCall.signature.stream().map(sig -> converter.convert(sig, generics)).toList();
argList = argList.subList(0, argList.size() - 1); argList = argList.subList(0, argList.size() - 1);
var receiverClass = converter.compiler.getClass(receiverName);
Method foundMethod = null; Method foundMethod = null;
var isStatic = false; var isStatic = false;
var isInterface = true; var isInterface = true;
@@ -241,7 +244,6 @@ public class StatementToTargetExpression implements ASTVisitor {
converter.addSignaturePair(methodCall.signatureArguments().get(i), methodCall.arglist.getArguments().get(i).getType()); 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 (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) {
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!"); if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
var thisMethod = ASTToTargetAST.findMethod(receiverClass, methodCall.name, signature, generics, converter.compiler); var thisMethod = ASTToTargetAST.findMethod(receiverClass, methodCall.name, signature, generics, converter.compiler);
@@ -250,18 +252,60 @@ public class StatementToTargetExpression implements ASTVisitor {
} else if (!isFunNType) { } else if (!isFunNType) {
receiverClass = converter.compiler.getClass(receiverName); receiverClass = converter.compiler.getClass(receiverName);
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!"); if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow(); foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow(
() -> new DebugException("Method " + methodCall.name + " not found (" + signature + ") on class " + receiverName)
);
} }
if (!isFunNType) { if (!isFunNType) {
returnType = converter.convert(foundMethod.getReturnType(), generics); // NOTE Not using the direct conversion method on converter to bypass adding the TPH to the used TPH list
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> converter.convert(e.getType(), generics)).toList(); returnType = ASTToTargetAST.convert(foundMethod.getReturnType(), generics, converter.compiler);
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> ASTToTargetAST.convert(e.getType(), generics, converter.compiler)).toList();
isStatic = Modifier.isStatic(foundMethod.modifier); isStatic = Modifier.isStatic(foundMethod.modifier);
isPrivate = Modifier.isPrivate(foundMethod.modifier); isPrivate = Modifier.isPrivate(foundMethod.modifier);
isInterface = receiverClass.isInterface(); isInterface = receiverClass.isInterface();
} }
//System.out.println(argList); // 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) && converter.isSubtype(receiverType, otherReceiver)) {
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) && converter.isSubtype(type, currentType)) {
throw new DiscardResultSet();
}
}
}
result = new TargetMethodCall( result = new TargetMethodCall(
converter.convert(methodCall.getType(), generics), returnType, argList, converter.convert(methodCall.getType(), generics), returnType, argList,
converter.convert(methodCall.receiver, generics), converter.convert(methodCall.receiver, generics),
@@ -3,16 +3,17 @@ package de.dhbwstuttgart.target.tree.type;
import de.dhbwstuttgart.bytecode.FunNGenerator; import de.dhbwstuttgart.bytecode.FunNGenerator;
import java.util.List; import java.util.List;
import java.util.Objects;
public record TargetFunNType(String name, List<TargetType> funNParams, List<TargetType> params, int returnArguments) implements TargetSpecializedType { public record TargetFunNType(String name, List<TargetType> funNParams, List<TargetType> params, int returnArguments, boolean isInterface) implements TargetSpecializedType {
public static TargetFunNType fromParams(List<TargetType> params, int returnArguments) { public static TargetFunNType fromParams(List<TargetType> params, int returnArguments, boolean isInterface) {
return fromParams(params, params, returnArguments); return fromParams(params, params, returnArguments, isInterface);
} }
public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams, int returnArguments) { public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams, int returnArguments, boolean isInterface) {
var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params)); var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params));
return new TargetFunNType(name, params, realParams, returnArguments); return new TargetFunNType(name, params, realParams, returnArguments, isInterface);
} }
public String toMethodDescriptor() { public String toMethodDescriptor() {
@@ -39,4 +40,9 @@ public record TargetFunNType(String name, List<TargetType> funNParams, List<Targ
var args = FunNGenerator.getArguments(funNParams); var args = FunNGenerator.getArguments(funNParams);
return "LFun" + args.size() + "$$" + TargetSpecializedType.signatureParameters(funNParams) + ";"; return "LFun" + args.size() + "$$" + TargetSpecializedType.signatureParameters(funNParams) + ";";
} }
public boolean equals(Object other) {
if (!(other instanceof TargetFunNType otfn)) return false;
return Objects.equals(name, otfn.name);
}
} }
@@ -1,311 +0,0 @@
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);
}
}
}
@@ -1,35 +0,0 @@
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());
}
}
@@ -1,31 +0,0 @@
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();
}
}
@@ -1,125 +0,0 @@
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());
}
}
}
@@ -1,252 +0,0 @@
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);
}
}
}
@@ -1,40 +0,0 @@
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);
}
}
}
@@ -1,82 +0,0 @@
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,26 +45,6 @@ public class TYPE {
return ret; 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) { private ConstraintSet getConstraintsClass(ClassOrInterface cl, TypeInferenceInformation info) {
ConstraintSet ret = new ConstraintSet(); ConstraintSet ret = new ConstraintSet();
ConstraintSet methConstrains; ConstraintSet methConstrains;
@@ -254,6 +254,9 @@ public class TYPEStmt implements StatementVisitor {
} else if (unaryExpr.operation == UnaryExpr.Operation.NOT) { } 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(), 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()))); 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 { } else {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@@ -392,8 +395,9 @@ public class TYPEStmt implements StatementVisitor {
@Override @Override
public void visit(BoolExpression expr) { public void visit(BoolExpression expr) {
expr.rexpr.accept(this);
expr.lexpr.accept(this); expr.lexpr.accept(this);
expr.rexpr.accept(this);
constraintsSet.addUndConstraint(new Pair(bool, expr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset()))); 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.lexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
constraintsSet.addUndConstraint(new Pair(bool, expr.rexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset()))); constraintsSet.addUndConstraint(new Pair(bool, expr.rexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
@@ -33,13 +33,11 @@ public class Logger {
private static void initLogger() { private static void initLogger() {
if (defaultWriter != null) return; if (defaultWriter != null) return;
if (!ConsoleInterface.writeLogFiles) return; if (!ConsoleInterface.writeLogFiles) return;
try { try {
Files.createDirectories(logFolder.toPath()); Files.createDirectories(logFolder.toPath());
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Could not create directory for log files: " + logFolder, e); throw new RuntimeException("Could not create directory for log files: " + logFolder, e);
} }
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss"); var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
var currentTimestamp = LocalDateTime.now().format(formatter); var currentTimestamp = LocalDateTime.now().format(formatter);
try { try {
+55 -11
View File
@@ -1,11 +1,15 @@
import de.dhbwstuttgart.bytecode.CodeGenException;
import de.dhbwstuttgart.core.ConsoleInterface; import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.util.Logger; import de.dhbwstuttgart.util.Logger;
import de.dhbwstuttgart.util.Logger.LogLevel; import de.dhbwstuttgart.util.Logger.LogLevel;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -19,8 +23,14 @@ import static org.junit.jupiter.api.Assertions.*;
import static targetast.TestCodegen.createClassLoader; import static targetast.TestCodegen.createClassLoader;
import static targetast.TestCodegen.generateClassFiles; import static targetast.TestCodegen.generateClassFiles;
@Execution(ExecutionMode.CONCURRENT)
public class TestComplete { public class TestComplete {
@BeforeAll
public static void setup() throws IOException {
TestCodegen.outputPath.toFile().mkdirs();
FileUtils.cleanDirectory(TestCodegen.outputPath.toFile());
}
@Test @Test
public void applyLambdaTest() throws Exception { public void applyLambdaTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "applyLambda.jav"); var classFiles = generateClassFiles(createClassLoader(), "applyLambda.jav");
@@ -320,9 +330,17 @@ public class TestComplete {
} }
@Test @Test
public void overloadingSortingTest() throws Exception { public void sortingTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Sorting.jav"); var classFiles = generateClassFiles(createClassLoader(), "Sorting.jav");
var instance = classFiles.get("Sorting").getDeclaredConstructor().newInstance(); 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);
} }
@Test @Test
@@ -657,6 +675,15 @@ public class TestComplete {
System.out.println(clazz.getDeclaredMethod("toString").invoke(instance)); 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 @Test
public void genericRecordTest() throws Exception { public void genericRecordTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "GenericRecord.jav"); var classFiles = generateClassFiles(createClassLoader(), "GenericRecord.jav");
@@ -1033,6 +1060,7 @@ public class TestComplete {
var instance = clazz.getDeclaredConstructor().newInstance(); var instance = clazz.getDeclaredConstructor().newInstance();
} }
@Disabled("Feature nicht implementiert")
@Test @Test
public void testOverloadSwitch() throws Exception { public void testOverloadSwitch() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "SwitchOverload.jav"); var classFiles = generateClassFiles(createClassLoader(), "SwitchOverload.jav");
@@ -1066,6 +1094,18 @@ public class TestComplete {
assertEquals(50, m.invoke(null)); 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());
} catch (RuntimeException e) {
fail("Wrong exception thrown!");
}
}
@Test @Test
public void testFor() throws Exception { public void testFor() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "For.jav"); var classFiles = generateClassFiles(createClassLoader(), "For.jav");
@@ -1286,6 +1326,14 @@ public class TestComplete {
m.invoke(null, List.of("foo", "bar", "baz")); m.invoke(null, List.of("foo", "bar", "baz"));
} }
@Test
public void testLazy() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), false, "LazyList.jav", "Primzahlen.jav");
var clazz = classFiles.get("Primzahlen");
var main = clazz.getDeclaredMethod("main", List.class);
main.invoke(null, List.of());
}
@Test @Test
public void testBug122() throws Exception { public void testBug122() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug122.jav"); var classFiles = generateClassFiles(createClassLoader(), "Bug122.jav");
@@ -1534,7 +1582,9 @@ public class TestComplete {
var m = clazz.getDeclaredMethod("ex1"); var m = clazz.getDeclaredMethod("ex1");
assertEquals("ABC", m.invoke(instance)); assertEquals("ABC", m.invoke(instance));
var ex2 = clazz.getDeclaredMethod("ex2"); var ex2 = clazz.getDeclaredMethod("ex2");
assertEquals("BAC", ex2.invoke(instance)); assertEquals("CBA", ex2.invoke(instance));
var ex3 = clazz.getDeclaredMethod("ex3");
assertEquals("BA", ex3.invoke(instance));
} }
@Test @Test
public void testBug366() throws Exception { public void testBug366() throws Exception {
@@ -1600,7 +1650,7 @@ public class TestComplete {
@Test @Test
public void testBug389() throws Exception { public void testBug389() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), false, "Bug389.jav", "Bug389Main.jav"); var classFiles = generateClassFiles(createClassLoader(), true, "Bug389.jav", "Bug389Main.jav");
var clazz = classFiles.get("Bug389Main"); var clazz = classFiles.get("Bug389Main");
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of()); clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
} }
@@ -1612,10 +1662,4 @@ public class TestComplete {
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of()); clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
} }
@Test
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());
}
} }
+1 -1
View File
@@ -19,7 +19,7 @@ public class TestTypeDeployment {
var path = Path.of(System.getProperty("user.dir"), "/resources/bytecode/javFiles/Cycle.jav"); var path = Path.of(System.getProperty("user.dir"), "/resources/bytecode/javFiles/Cycle.jav");
var file = path.toFile(); var file = path.toFile();
var compiler = new JavaTXCompiler(file); var compiler = new JavaTXCompiler(file);
compiler.parseAll(); compiler.generateBytecode();
var parsedSource = compiler.sourceFiles.get(file); var parsedSource = compiler.sourceFiles.get(file);
var tiResults = compiler.typeInference(file); var tiResults = compiler.typeInference(file);
Set<TypeInsert> tips = new HashSet<>(); Set<TypeInsert> tips = new HashSet<>();
+3 -3
View File
@@ -18,7 +18,7 @@ public class InheritTest {
public static void setUpBeforeClass() throws Exception { public static void setUpBeforeClass() throws Exception {
var classLoader = TestCodegen.createClassLoader(); var classLoader = TestCodegen.createClassLoader();
var classes = TestCodegen.generateClassFiles(classLoader, "Inherit.jav", "AA.jav", "BB.jav", "CC.jav", "DD.jav"); var classes = TestCodegen.generateClassFiles(classLoader, false, "AA.jav", "BB.jav", "CC.jav", "DD.jav", "Inherit.jav");
classToTest = classes.get("Inherit"); classToTest = classes.get("Inherit");
classToTestAA = classes.get("AA"); classToTestAA = classes.get("AA");
classToTestBB = classes.get("BB"); classToTestBB = classes.get("BB");
@@ -52,7 +52,7 @@ public class InheritTest {
public void testmainCC() throws Exception { public void testmainCC() throws Exception {
Method m = classToTestCC.getDeclaredMethod("m", Integer.class); Method m = classToTestCC.getDeclaredMethod("m", Integer.class);
assertEquals("CC", m.invoke(instanceOfClassCC, 5)); assertEquals("CC", m.invoke(instanceOfClassCC, 5));
Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class); Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class);
assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassCC, 5)); assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassCC, 5));
} }
@@ -60,7 +60,7 @@ public class InheritTest {
public void testmainDD() throws Exception { public void testmainDD() throws Exception {
Method m = classToTestCC.getDeclaredMethod("m", Integer.class); Method m = classToTestCC.getDeclaredMethod("m", Integer.class);
assertEquals("CC", m.invoke(instanceOfClassDD, 5)); assertEquals("CC", m.invoke(instanceOfClassDD, 5));
Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class); Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class);
assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassDD, 5)); assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassDD, 5));
} }
+2 -2
View File
@@ -35,7 +35,7 @@ public class PutTest {
Method m = classToTest.getDeclaredMethod("putElement", Object.class, Stack.class); Method m = classToTest.getDeclaredMethod("putElement", Object.class, Stack.class);
Stack<Integer> s_invoke = new Stack<>(); Stack<Integer> s_invoke = new Stack<>();
m.invoke(instanceOfClass, 5, s_invoke); m.invoke(instanceOfClass, 5, s_invoke);
assertEquals(new Integer(5), s_invoke.pop()); assertEquals(5, s_invoke.pop());
} }
@Test @Test
@@ -53,6 +53,6 @@ public class PutTest {
Method m = classToTest.getDeclaredMethod("main", Object.class, Stack.class); Method m = classToTest.getDeclaredMethod("main", Object.class, Stack.class);
Stack<Integer> s_invoke = new Stack<>(); Stack<Integer> s_invoke = new Stack<>();
m.invoke(instanceOfClass, 6, s_invoke); m.invoke(instanceOfClass, 6, s_invoke);
assertEquals(new Integer(6), s_invoke.pop()); assertEquals(6, s_invoke.pop());
} }
} }
+8 -4
View File
@@ -5,6 +5,7 @@ import de.dhbwstuttgart.bytecode.Codegen;
import de.dhbwstuttgart.environment.DirectoryClassLoader; import de.dhbwstuttgart.environment.DirectoryClassLoader;
import de.dhbwstuttgart.environment.IByteArrayClassLoader; import de.dhbwstuttgart.environment.IByteArrayClassLoader;
import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter;
import de.dhbwstuttgart.target.generate.ASTToTargetAST; import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.tree.MethodParameter; import de.dhbwstuttgart.target.tree.MethodParameter;
import de.dhbwstuttgart.target.tree.TargetClass; import de.dhbwstuttgart.target.tree.TargetClass;
@@ -14,6 +15,7 @@ import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.TargetFunNType; import de.dhbwstuttgart.target.tree.type.TargetFunNType;
import de.dhbwstuttgart.target.tree.type.TargetRefType; import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -30,7 +32,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class TestCodegen { public class TestCodegen {
static final Path outputPath = Path.of(System.getProperty("user.dir"), "/targetTest"); public static final Path outputPath = Path.of(System.getProperty("user.dir"), "/targetTest");
private static void writeClassFile(String name, byte[] code) throws IOException { private static void writeClassFile(String name, byte[] code) throws IOException {
Files.createDirectories(outputPath); Files.createDirectories(outputPath);
@@ -56,7 +58,8 @@ public class TestCodegen {
try(var newClassLoader = new DirectoryClassLoader(List.of(outputPath.toFile()), (ClassLoader)classLoader)) { try(var newClassLoader = new DirectoryClassLoader(List.of(outputPath.toFile()), (ClassLoader)classLoader)) {
var result = new HashMap<String, Class<?>>(); var result = new HashMap<String, Class<?>>();
for (var file : filenames) { for (var file : filenames) {
var classes = compiler.sourceFiles.get(file).getClasses(); var sf = compiler.sourceFiles.get(file);
var classes = sf.getClasses();
result.putAll(classes.stream().map(cli -> { result.putAll(classes.stream().map(cli -> {
try { try {
@@ -71,7 +74,8 @@ public class TestCodegen {
} }
public static Class<?> generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException, ClassNotFoundException { public static Class<?> generateClass(TargetStructure clazz, IByteArrayClassLoader classLoader) throws IOException, ClassNotFoundException {
Codegen codegen = new Codegen(clazz, new JavaTXCompiler(List.of()), null); ASTToTargetAST converter = new ASTToTargetAST(List.of(new ResultSet(Set.of())), classLoader);
Codegen codegen = new Codegen(clazz, new JavaTXCompiler(List.of()), converter);
var code = codegen.generate(); var code = codegen.generate();
writeClassFile(clazz.qualifiedName().getClassName(), code); writeClassFile(clazz.qualifiedName().getClassName(), code);
return classLoader.loadClass(code); return classLoader.loadClass(code);
@@ -272,7 +276,7 @@ public class TestCodegen {
public void testLambda() throws Exception { public void testLambda() throws Exception {
var classLoader = createClassLoader(); var classLoader = createClassLoader();
// var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class")); // var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class"));
var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer), 1); var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer), 1, false);
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("CGLambda")); var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("CGLambda"));
targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "<init>", false, false, false)))); targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(new TargetMethodCall(null, new TargetSuper(TargetType.Object), List.of(), TargetType.Object, "<init>", false, false, false))));