Compare commits

...
19 Commits
Author SHA1 Message Date
dholle 78a8f82325 Work on #389
SonarQube Scan / SonarQube Trigger (push) Successful in 2m54s
2026-09-15 16:00:10 +02:00
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
35 changed files with 689 additions and 209 deletions
+5 -1
View File
@@ -2,7 +2,7 @@ import java.lang.String;
import java.lang.Object;
public class Bug365{
swap(f){
swap(Fun1$$<String, Fun1$$<String, Object>> f){
return x -> y -> f.apply(y).apply(x);
}
@@ -18,4 +18,8 @@ public class Bug365{
var func = x -> y -> z -> x + y + z;
return swap(func).apply("A").apply("B").apply("C");
}
public ex3() {
var func = x -> y -> x + y;
return swap(func).apply("A").apply("B");
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
sealed interface List<T> permits Cons, Empty {}
record Cons<T>(T a , List<T> l ) implements List <T> {}
record Cons<T>(T a, List<T> l ) implements List <T> {}
record Empty<T>() implements List <T> {}
public class Bug380 {
public <T> List<T> append(l1, List<T> l2) {
public <T> List<T> append(List<T> l1, List<T> l2) {
return switch ( l1 ) {
case Cons(e, rest) -> new Cons<>(e, append(rest, l2)); //::Typ TPH A
case Empty() -> l2;//::TPH B
+4 -6
View File
@@ -1,10 +1,8 @@
import java.lang.String;
public class Bug389 {
public swap(f) {
return x -> y -> f.apply(y).apply(x);
}
//public swap(f) {
// return x -> y -> f.apply(y).apply(x);
//}
public swap(f) {
return x -> y -> z -> f.apply(z).apply(x).apply(y);
}
}
}
+1 -2
View File
@@ -1,11 +1,10 @@
import Bug389;
import java.util.List;
import java.lang.String;
import java.lang.Integer;
public class Bug389Main {
public static main(args) {
var func = x -> y -> z -> x + y + z;
var func = (Integer x) -> y -> z -> x + y + z;
var swap = new Bug389();
swap.swap(func).apply(1).apply(2).apply(3);
}
+13
View File
@@ -0,0 +1,13 @@
import java.lang.Integer;
public class Bug395 {
dup(x) {
//Hier beliebiger Seiteneffekt
return x + x;
}
dup(s) {
return s + s;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ import java.lang.String;
public class Fac {
getFac(n) {
var res = 1;
Double res = 1;
var i = 1;
while (i <= n) {
res = res * i;
@@ -1,11 +1,9 @@
import java.lang.String;
import java.lang.Integer;
sealed interface List permits LinkedElem, Elem {}
public record LinkedElem<T>(T a,List l) implements List{}
public record Elem<T>(T c) implements List{}
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 class GenericRecordSwitchCase {
public main(o) {
+3 -1
View File
@@ -5,6 +5,8 @@ import java.lang.Object;
public class HelloWorld {
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 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));
}
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.ArrayList;
import java.lang.String;
import java.lang.Integer;
public class Sorting{
merge(a, b){
a.addAll(b);
return a;
public class Sorting {
List<Integer> merge(List<Integer> a, List<Integer> b) {
var r = new ArrayList<>();
for (var i = 0, j = 0; i < a.size() || j < b.size();)
if (j == b.size() || (i < a.size() && a.get(i) <= b.get(j)))
r.add(a.get(i++));
else r.add(b.get(j++));
return r;
}
sort(in){
var firstHalf = in;
var secondHalf = in;
return merge(sort(firstHalf), sort(secondHalf));
}
split(list) {
var mid = list.size() / 2;
return List.of(list.subList(0, mid), list.subList(mid, list.size()));
}
/*
void sort(a){
a = merge(a,a);
}
*/
public sort(in) {
if (in.size() <= 1) return in;
var halves = split(in);
return merge(sort(halves.get(0)), sort(halves.get(1)));
}
}
@@ -0,0 +1,7 @@
public class StaticFail {
void nonStaticM() {}
static void staticM() {
nonStaticM();
}
}
@@ -41,6 +41,15 @@ public class Codegen {
protected ClassLoader getClassLoader() {
return compiler.getClassLoader();
}
@Override
protected String getCommonSuperClass(String type1, String type2) {
var t1 = new TargetRefType(type1.replaceAll("/", "."));
var t2 = new TargetRefType(type2.replaceAll("/", "."));
var common = ASTToTargetAST.getCommonSuperType(t1, t2, compiler);
return common.getClassName().toString().replaceAll("\\.", "/");
//return super.getCommonSuperClass(type1, type2);
}
}
public Codegen(TargetStructure clazz, JavaTXCompiler compiler, ASTToTargetAST converter) {
@@ -244,10 +253,16 @@ public class Codegen {
if (source.equals(dest))
return;
if (isFunctionalInterface(source) && isFunctionalInterface(dest) &&
!(source instanceof TargetFunNType && dest instanceof TargetFunNType)) {
boxFunctionalInterface(state, source, dest);
return;
if (/*isFunctionalInterface(source) &&*/ isFunctionalInterface(dest) && !source.equals(dest)) {
if (source instanceof TargetFunNType funs && dest instanceof TargetFunNType fund) {
if (funs.funNParams().size() == fund.funNParams().size() && !fund.isInterface()) {
boxFunctionalInterface(state, source, dest);
return;
}
} else {
boxFunctionalInterface(state, source, dest);
return;
}
}
var mv = state.mv;
@@ -336,7 +351,7 @@ public class Codegen {
mv.visitTypeInsn(NEW, className);
mv.visitInsn(DUP_X1);
mv.visitInsn(SWAP);
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(" + source.toDescriptor() + ")V", false);
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(Ljava/lang/Object;)V", false);
}
private boolean isFunctionalInterface(TargetType type) {
@@ -350,6 +365,8 @@ public class Codegen {
}
private TargetType largerType(TargetType left, TargetType right) {
if (left instanceof TargetExtendsWildcard wc) left = wc.innerType();
if (right instanceof TargetExtendsWildcard wc) right = wc.innerType();
if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
return TargetType.String;
} else if (left.equals(TargetType.Double) || right.equals(TargetType.Double)) {
@@ -813,7 +830,9 @@ public class Codegen {
private static TargetType removeGenerics(TargetType param) {
return switch (param) {
case null -> null;
case TargetFunNType funNType -> new TargetFunNType(funNType.name(), funNType.funNParams().stream().map(Codegen::removeGenerics).toList(), List.of(), funNType.returnArguments());
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 TargetGenericType targetGenericType -> TargetType.Object;
default -> param;
@@ -861,7 +880,8 @@ public class Codegen {
var pattern = (TargetTypePattern) capture.pattern();
var variable = state.scope.get(pattern.name());
mv.visitVarInsn(ALOAD, variable.index);
mv.visitTypeInsn(CHECKCAST, capture.pattern().type().getInternalName());
if (!(capture.pattern().type() instanceof TargetGenericType))
mv.visitTypeInsn(CHECKCAST, capture.pattern().type().getInternalName());
}
var descriptor = TargetMethod.getDescriptor(lambda.type(), params.toArray(TargetType[]::new));
@@ -1185,6 +1205,12 @@ public class Codegen {
}
case TargetMethodCall call: {
if (!call.isStatic()) {
if (call.expr() instanceof TargetThis && state.isStatic) {
throw new CodeGenException(
"Attempted to call instance method " + call.name() +
" with descriptor " + call.getDescriptor() + " from a static context"
);
}
generate(state, call.expr());
boxPrimitive(state, call.expr().type());
}
@@ -1195,11 +1221,13 @@ public class Codegen {
convertTo(state, e.type(), arg);
}
var descriptor = call.getDescriptor();
TargetType casted = call.type();
if (call.owner() instanceof TargetFunNType owner) {
// Decay FunN
descriptor = TargetMethod.getDescriptor(
(owner.returnArguments() == 0 ? null : TargetType.Object),
call.parameterTypes().stream().map(x -> TargetType.Object).toArray(TargetType[]::new));
if (owner.returnArguments() != 0)
casted = FunNGenerator.getReturnType(owner.funNParams());
}
int insn = INVOKEVIRTUAL;
@@ -1215,7 +1243,7 @@ public class Codegen {
unboxPrimitive(state, call.type());
}*/
if (call.type() != null)
convertTo(state, call.returnType(), call.type());
convertTo(state, call.returnType(), casted);
break;
}
case TargetLambdaExpression lambda:
@@ -1440,6 +1468,7 @@ public class Codegen {
for (var i = 0; i < aSwitch.cases().size(); i++) {
mv.visitLabel(caseLabels[i]);
var cse = aSwitch.cases().get(i);
state.enterScope();
if (cse.labels().size() == 1) {
var label = cse.labels().get(0);
@@ -1468,6 +1497,8 @@ public class Codegen {
if (cse.isSingleExpression() && aSwitch.isExpression())
yieldValue(state, cse.body().statements().get(0).type());
if (aSwitch.isExpression()) mv.visitJumpInsn(GOTO, end);
state.exitScope();
}
mv.visitLabel(defaultLabel);
@@ -1737,7 +1768,7 @@ public class Codegen {
generate(state, method.block());
if (method.signature().returnType() == null)
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitMaxs(0, 0);
}
mv.visitEnd();
}
@@ -1803,54 +1834,77 @@ public class Codegen {
// Generate wrapper classes for function types
for (var pair : funWrapperClasses.keySet()) {
var className = funWrapperClasses.get(pair);
ClassWriter cw2 = new CustomClassWriter();
cw2.visit(V1_8, ACC_PUBLIC, className, null, "java/lang/Object", new String[] { pair.to.getInternalName() });
cw2.visitField(ACC_PRIVATE, "wrapped", pair.from.toDescriptor(), null, null).visitEnd();
// Generate constructor
var ctor = cw2.visitMethod(ACC_PUBLIC, "<init>", "(" + pair.from.toDescriptor() + ")V", null, null);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitVarInsn(ALOAD, 1);
ctor.visitFieldInsn(PUTFIELD, className, "wrapped", pair.from.toDescriptor());
ctor.visitInsn(RETURN);
ctor.visitMaxs(0, 0);
ctor.visitEnd();
var toName = "apply";
TargetType toReturn;
TargetType[] toParams;
if (!(pair.to instanceof TargetFunNType to)) {
var toClass = compiler.getClass(new JavaClassName(pair.to.name()));
var toMethod = toClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
toReturn = converter.convert(toMethod.getReturnType());
toParams = converter.convert(toMethod.getParameterList()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
toName = toMethod.name;
} else if (pair.from instanceof TargetFunNType from) {
toReturn = from.returnArguments() == 0 ? null : TargetType.Object;
toParams = from.funNParams().subList(0, from.funNParams().size() - 1).stream().map(x -> TargetType.Object).toArray(TargetType[]::new);
} else {
toReturn = to.returnArguments() == 0 ? null : TargetType.Object;
toParams = to.funNParams().subList(0, to.funNParams().size() - 1).stream().map(x -> TargetType.Object).toArray(TargetType[]::new);
}
var toDescriptor = TargetMethod.getDescriptor(toReturn, toParams);
var fieldDescriptor = pair.from.toDescriptor();
String methodName = "apply";
String fromDescriptor = null;
TargetType fromReturn = null;
if (!(pair.from instanceof TargetFunNType funNType)) {
var fromClass = compiler.getClass(new JavaClassName(pair.from.name()));
var fromMethod = fromClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
String fromClass = pair.from.getInternalName();
if (isFunctionalInterface(pair.from) && !(pair.from instanceof TargetFunNType)) {
var clazz = compiler.getClass(new JavaClassName(pair.from.name()));
var fromMethod = clazz.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
methodName = fromMethod.name;
fromReturn = converter.convert(fromMethod.getReturnType());
var fromParams = converter.convert(fromMethod.getParameterList()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
fromDescriptor = TargetMethod.getDescriptor(fromReturn, fromParams);
} else {
} else if (pair.from instanceof TargetFunNType funNType) {
fromReturn = funNType.returnArguments() > 0 ? TargetType.Object : null;
fromDescriptor = funNType.toMethodDescriptor();
} else {
fromDescriptor = toDescriptor;
fromReturn = toReturn;
var to = (TargetFunNType) pair.to;
fromClass = FunNGenerator.getSuperClassName(to.funNParams().size() - 1, to.returnArguments());
fieldDescriptor = "L" + fromClass + ";";
}
var toClass = compiler.getClass(new JavaClassName(pair.to.name()));
var toMethod = toClass.getMethods().stream().filter(m -> (m.modifier & ACC_ABSTRACT) != 0).findFirst().orElseThrow();
var toReturn = converter.convert(toMethod.getReturnType());
var toParams = converter.convert(toMethod.getParameterList()).stream().map(m -> m.pattern().type()).toArray(TargetType[]::new);
var toDescriptor = TargetMethod.getDescriptor(toReturn, toParams);
ClassWriter cw2 = new CustomClassWriter();
cw2.visit(V1_8, ACC_PUBLIC, className, null, "java/lang/Object", new String[] { pair.to.getInternalName() });
cw2.visitField(ACC_PRIVATE, "wrapped", fieldDescriptor, null, null).visitEnd();
// Generate constructor
var ctor = cw2.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitVarInsn(ALOAD, 1);
ctor.visitFieldInsn(PUTFIELD, className, "wrapped", fieldDescriptor);
ctor.visitInsn(RETURN);
ctor.visitMaxs(0, 0);
ctor.visitEnd();
// Generate wrapper method
var mv = cw2.visitMethod(ACC_PUBLIC, toMethod.name, toDescriptor, null, null);
var mv = cw2.visitMethod(ACC_PUBLIC, toName, toDescriptor, null, null);
var state = new State(null, mv, 0, false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "wrapped", pair.from.toDescriptor());
mv.visitFieldInsn(GETFIELD, className, "wrapped", fieldDescriptor);
for (var i = 0; i < toParams.length; i++) {
var arg = toParams[i];
mv.visitVarInsn(findLoadCode(arg), i + 1);
}
mv.visitMethodInsn(INVOKEINTERFACE, pair.from.getInternalName(), methodName, fromDescriptor, true);
mv.visitMethodInsn(INVOKEINTERFACE, fromClass, methodName, fromDescriptor, true);
if (fromReturn != null) {
if (toReturn instanceof TargetPrimitiveType) {
convertTo(state, fromReturn, TargetType.toWrapper(toReturn));
@@ -1911,7 +1965,7 @@ public class Codegen {
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);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
@@ -1920,7 +1974,7 @@ public class Codegen {
mv.visitMaxs(0, 0);
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);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
@@ -1930,7 +1984,7 @@ public class Codegen {
mv.visitMaxs(0, 0);
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);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
@@ -714,6 +714,9 @@ public class JavaTXCompiler {
new SuperWildcardType(toRefType(targetSuperWildcard.innerType()), new NullToken());
case TargetGenericType targetGenericType -> new GenericRefType(targetGenericType.name(), new NullToken());
case TargetPrimitiveType targetPrimitiveType -> toRefType(TargetType.toWrapper(targetPrimitiveType));
case TargetFunNType targetFunNType ->
new RefType(new JavaClassName(FunNGenerator.getSuperClassName(targetFunNType.funNParams().size() - 1, targetFunNType.returnArguments())),
targetFunNType.funNParams().stream().map(JavaTXCompiler::toRefType).toList(), new NullToken());
case TargetSpecializedType targetSpecializedType ->
new RefType(new JavaClassName(targetSpecializedType.name()),
targetSpecializedType.params().stream().map(JavaTXCompiler::toRefType).toList(), new NullToken()
@@ -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 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 implementedInterfaces = target.implementingInterfaces().stream()
.map(t -> (RefType) toRefType(t)).toList();
@@ -3,6 +3,6 @@ package de.dhbwstuttgart.exceptions;
public class DebugException extends RuntimeException {
public DebugException(String message) {
System.err.print(message);
super(message);
}
}
@@ -122,7 +122,7 @@ public class FCGenerator {
List<RefType> superClasses = new ArrayList<>();
superClasses.add(forType.getSuperClass());
superClasses.addAll(forType.getSuperInterfaces());
List<Pair> retList = new ArrayList<>();
for(RefType superType : superClasses){
Optional<ClassOrInterface> hasSuperclass = availableClasses.stream().filter(cl -> superType.getName().equals(cl.getClassName())).findAny();
@@ -482,8 +482,8 @@ public class StatementGenerator {
IdentifierContext identifierCtx = recordPatternCtx.identifier();
var text = (identifierCtx != null) ? identifierCtx.getText() : null;
//Hier evtl. Typ anpassen -> wenn kein Typ bekannt ist push neuen Typ auf Hashtable
var type = recordPatternCtx.type == null ? TypePlaceholder.fresh(recordPatternCtx.getStart()) : TypeGenerator.convert(recordPatternCtx.type, reg, generics);
var ctor = TypeGenerator.convert(recordPatternCtx.ctor, reg, generics);
var type = recordPatternCtx.type == null ? ctor : TypeGenerator.convert(recordPatternCtx.type, reg, generics);
if (text != null) localVars.put(text, type);
var ret = new RecordPattern(subPattern, text, type, (RefType)ctor, recordPatternCtx.getStart());
return ret;
@@ -982,8 +982,11 @@ public class StatementGenerator {
} else if (op.getText().equals("!")) {
ret = new UnaryExpr(UnaryExpr.Operation.NOT, expr, TypePlaceholder.fresh(op), op);
return ret;
} else if (op.getText().equals("-")) {
ret = new UnaryExpr(UnaryExpr.Operation.MINUS, expr, TypePlaceholder.fresh(op), op);
return ret;
} else {
throw new NotImplementedException();
throw new NotImplementedException(op.getText());
}
}
@@ -80,7 +80,7 @@ public class SyntaxTreeGenerator {
HashMap<String, Integer> allmodifiers = new HashMap<>();
// PL 2018-11-01 fields eingefuegt, damit die fields immer die gleiche TPH
// bekommen
private final Map<String, FieldEntry> fields = new HashMap<>();
//private final Map<String, FieldEntry> fields = new HashMap<>();
// PL 2019-10-23: Muss für jede Klasse neu initilisiert werden
List<Statement> fieldInitializations = new ArrayList<>();
List<Statement> staticFieldInitializations = new ArrayList<>();
@@ -178,6 +178,7 @@ public class SyntaxTreeGenerator {
}
private ClassOrInterface convertClass(Java17Parser.ClassDeclarationContext ctx, int modifiers) {
var fieldDecls = new HashMap<String, FieldEntry>();
String className = this.pkgName + (this.pkgName.length() > 0 ? "." : "") + ctx.identifier().getText();
JavaClassName name = reg.getName(className); // Holt den Package Namen mit dazu
if (!name.toString().equals(className)) { // Kommt die Klasse schon in einem anderen Package vor?
@@ -206,7 +207,7 @@ public class SyntaxTreeGenerator {
List<RefType> implementedInterfaces = new ArrayList<>();
List<RefType> permittedSubtypes = null;
for (ClassBodyDeclarationContext clsbodydecl : ctx.classBody().classBodyDeclaration()) {
convert(clsbodydecl, fielddecl, constructors, methods, name, superClass, generics);
convert(clsbodydecl, fielddecl, constructors, methods, name, superClass, generics, fieldDecls);
}
if (constructors.isEmpty()) {
constructors.add(generateStandardConstructor(ctx.identifier().getText(), name, superClass, genericClassParameters, offset));
@@ -255,6 +256,8 @@ public class SyntaxTreeGenerator {
List<Pattern> constructorParameters = new ArrayList<>();
List<Statement> constructorStatements = new ArrayList<>();
var fieldDecl = new HashMap<String, FieldEntry>();
List<Java17Parser.RecordComponentContext> components = recordDeclaration.recordHeader().recordComponentList() != null ?
recordDeclaration.recordHeader().recordComponentList().recordComponent(): List.of();
for (RecordComponentContext component : components) {
@@ -270,6 +273,7 @@ public class SyntaxTreeGenerator {
fielddecl.add(new Field(fieldname, fieldtype, fieldmodifiers, fieldoffset));
constructorParameters.add(new FormalParameter(fieldname, fieldtype, fieldoffset));
FieldVar fieldvar = new FieldVar(new This(offset), fieldname, fieldtype, fieldoffset);
fieldDecl.put(fieldname, new FieldEntry(fieldname, fieldtype, Modifier.PRIVATE));
constructorStatements.add(new Assign(new AssignToField(fieldvar), new LocalVar(fieldname, fieldtype, fieldoffset), offset));
Statement returnStatement = new Return(fieldvar, offset);
methods.add(new Method(allmodifiers.get("public"), fieldname, fieldtype, new ParameterList(new ArrayList<>(), offset), new Block(Arrays.asList(returnStatement), offset), new GenericDeclarationList(new ArrayList<>(), offset), offset));
@@ -279,7 +283,7 @@ public class SyntaxTreeGenerator {
//Optional<Constructor> initializations = Optional.of(implicitConstructor);
constructors.add(implicitConstructor);
for (ClassBodyDeclarationContext bodyDeclaration : recordDeclaration.recordBody().classBodyDeclaration()) {
convert(bodyDeclaration, fielddecl, constructors, methods, name, superClass, generics);
convert(bodyDeclaration, fielddecl, constructors, methods, name, superClass, generics, fieldDecl);
}
if (!Objects.isNull(recordDeclaration.IMPLEMENTS())) {
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);
}
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;
if (classBody instanceof MemberdeclContext) {
member = (MemberdeclContext) classBody;
@@ -303,11 +307,11 @@ public class SyntaxTreeGenerator {
break;
}
case MemberfieldContext memberfield: {
fields.addAll(convert(memberfield.fieldDeclaration(), membermodifiers, generics));
fields.addAll(convert(memberfield.fieldDeclaration(), membermodifiers, generics, fieldDecls));
break;
}
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) {
constructors.add(constructor);
} else {
@@ -316,7 +320,7 @@ public class SyntaxTreeGenerator {
break;
}
case MemberconstructorContext memberconstructor: {
constructors.add(convert(membermodifiers, memberconstructor.constructor(), name, superClass, generics));
constructors.add(convert(membermodifiers, memberconstructor.constructor(), name, superClass, generics, fieldDecls));
break;
}
default:
@@ -324,7 +328,7 @@ public class SyntaxTreeGenerator {
}
} else if (classBody instanceof Java17Parser.ClassblockContext ctx && ctx.STATIC() != null) {
// 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);
staticFieldInitializations.addAll(block.statements);
}
@@ -434,7 +438,7 @@ public class SyntaxTreeGenerator {
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);
MethodBodyContext body = bodydeclaration.methodBody();
Block block = null;
@@ -507,7 +511,7 @@ public class SyntaxTreeGenerator {
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;
MethodDeclarationContext methoddeclaration;
GenericDeclarationListContext genericdeclarations;
@@ -540,7 +544,7 @@ public class SyntaxTreeGenerator {
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);
MethodBodyContext body = methoddeclaration.methodBody();
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;
GenericDeclarationListContext genericdeclarations;
GenericDeclarationList gtvDeclarations;
@@ -586,7 +590,7 @@ public class SyntaxTreeGenerator {
return new Constructor(modifiers, name, retType, paramlist, block, gtvDeclarations, constructordeclaration.getStart());
}
List<? extends Field> convert(Java17Parser.FieldDeclarationContext fieldDeclContext, int modifiers, GenericsRegistry generics) {
List<? extends Field> convert(Java17Parser.FieldDeclarationContext fieldDeclContext, int modifiers, GenericsRegistry generics, HashMap<String, FieldEntry> fields) {
List<Field> ret = new ArrayList<>();
RefTypeOrTPHOrWildcardOrGeneric fieldType;
if (fieldDeclContext.typeType() != null) {
@@ -598,9 +602,9 @@ public class SyntaxTreeGenerator {
}
for (Java17Parser.VariableDeclaratorContext varDecl : fieldDeclContext.variableDeclarators().variableDeclarator()) {
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) {
initializeField(varDecl, Modifier.isStatic(modifiers), fieldType, generics);
initializeField(varDecl, Modifier.isStatic(modifiers), fieldType, generics, fields);
}
ret.add(new Field(fieldName, fieldType, modifiers, varDecl.getStart()));
}
@@ -612,7 +616,7 @@ public class SyntaxTreeGenerator {
}
// 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<>());
var assignment = statementGenerator.generateFieldAssignment(ctx, typeOfField);
if (isStatic) {
@@ -1,6 +1,8 @@
package de.dhbwstuttgart.target.generate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import de.dhbwstuttgart.bytecode.CodeGenException;
import de.dhbwstuttgart.bytecode.FunNGenerator;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
@@ -21,6 +23,7 @@ import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*;
import de.dhbwstuttgart.typeinference.unify.MartelliMontanariUnify;
import de.dhbwstuttgart.typeinference.unify.model.*;
import de.dhbwstuttgart.util.Logger;
import org.antlr.v4.runtime.Token;
import java.util.*;
@@ -51,6 +54,10 @@ public class ASTToTargetAST {
private Method currentMethod;
public Method getCurrentMethod() {
return currentMethod;
}
public final JavaTXCompiler compiler;
public List<RefTypeOrTPHOrWildcardOrGeneric> findAllVariants(RefTypeOrTPHOrWildcardOrGeneric type) {
@@ -76,8 +83,8 @@ public class ASTToTargetAST {
this(new JavaGenerics(compiler, set), new TxGenerics(compiler, set));
}
public static Generics nullGenerics() {
return new Generics(null, new ResultSet(Set.of()));
public static Generics nullGenerics(JavaTXCompiler compiler) {
return new Generics(compiler, new ResultSet(Set.of()));
}
}
@@ -109,14 +116,38 @@ public class ASTToTargetAST {
}
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) {
Optional<Method> method = Optional.empty();
while (method.isEmpty()) {
method = owner.getMethods().stream().filter(m -> m.name.equals(name) &&
parameterEquals(m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
var methods = owner.getMethods().stream().filter(m -> {
if (!m.name.equals(name)) return false;
var plist = m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList();
return parameterEquals(plist, argumentList, compiler);
}).toList();
if (!methods.isEmpty()) {
// Find the most specific method
var resultMethods = new ArrayList<Method>();
outer: for (var ma : methods) {
for (Method mb : methods) {
for (var k = 0; k < ma.getParameterList().getFormalparalist().size(); k++) {
var parama = generics.getTargetType(ma.getParameterList().getParameterAt(k).getType());
var paramb = generics.getTargetType(mb.getParameterList().getParameterAt(k).getType());
if (!Objects.equals(parama, paramb) && isFunOrSubtype(paramb, parama, compiler)) {
continue outer;
}
}
}
resultMethods.add(ma);
}
if (resultMethods.isEmpty()) method = Optional.of(methods.getFirst());
else method = Optional.of(resultMethods.getFirst());
}
if (owner.getClassName().toString().equals("java.lang.Object")) break;
owner = compiler.getClass(owner.getSuperClass().getName());
}
@@ -125,10 +156,10 @@ public class ASTToTargetAST {
Optional<Constructor> findConstructor(ClassOrInterface owner, List<TargetType> argumentList, IGenerics generics) {
return owner.getConstructors().stream().filter(c ->
parameterEquals(c.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
parameterEquals(c.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList, compiler)).findFirst();
}
static boolean parameterEquals(List<TargetType> pars, List<TargetType> arguments) {
static boolean parameterEquals(List<TargetType> pars, List<TargetType> arguments, JavaTXCompiler compiler) {
if (pars.size() != arguments.size())
return false;
@@ -139,6 +170,8 @@ public class ASTToTargetAST {
return true;
if (TargetType.toPrimitive(type2).equals(type1))
return true;
if (type1 instanceof TargetFunNType tfun1 && type2 instanceof TargetFunNType tfun2)
return funNIsSubtype(tfun2, tfun1, compiler);
if (!type1.equals(type2))
return false;
}
@@ -146,6 +179,33 @@ public class ASTToTargetAST {
return true;
}
private static boolean isFunOrSubtype(TargetType from, TargetType to, JavaTXCompiler compiler) {
if (to instanceof TargetGenericType) return true;
if (from instanceof TargetFunNType ffun && to instanceof TargetFunNType tfun)
return funNIsSubtype(ffun, tfun, compiler);
return isSubtype(from, to, compiler);
}
private static boolean funNIsSubtype(TargetFunNType from, TargetFunNType to, JavaTXCompiler compiler) {
if (!from.isInterface() && !to.isInterface()) {
if (from.funNParams().size() != to.funNParams().size()) return false;
for (var i = 0; i < from.funNParams().size() - from.returnArguments(); i++) {
var left = from.funNParams().get(i);
var right = to.funNParams().get(i);
if (!Objects.equals(left, right) && isFunOrSubtype(right, left, compiler))
return false;
}
if (from.returnArguments() != 0) {
var ret1 = from.funNParams().getLast();
var ret2 = to.funNParams().getLast();
if (Objects.equals(ret1, ret2)) return true;
return isFunOrSubtype(ret1, ret2, compiler);
}
return true;
}
return from.name().equals(to.name());
}
Set<TargetGeneric> convert(Set<GenerateGenerics.Pair> result, IGenerics generics) {
return result.stream().map(p -> {
if (p instanceof GenerateGenerics.PairLT pair) {
@@ -172,7 +232,7 @@ public class ASTToTargetAST {
case TargetSuperWildcard targetSuperWildcard -> new SuperType(toUnifyType(targetSuperWildcard.innerType()));
case TargetGenericType targetGenericType -> new PlaceholderType(targetGenericType.name(), JavaTXCompiler.defaultClientPlaceholderRegistry);
case TargetPrimitiveType _ -> throw new NotImplementedException();
case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.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()));
};
}
@@ -215,9 +275,12 @@ public class ASTToTargetAST {
private static Optional<TargetType> unify(TargetType a, TargetType b) {
if (typesStrictlyEqual(a, b)) return Optional.ofNullable(a);
if (a instanceof TargetFunNType || b instanceof TargetFunNType) return Optional.empty();
var unify = new MartelliMontanariUnify();
var ua = toUnifyType(a);
var 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();
return Optional.of(toTargetType(unifier.get().apply(ua)));
}
@@ -231,7 +294,6 @@ public class ASTToTargetAST {
// Strip off patterns, we don't need them for merged methods, they do a switch case
result.add(new MethodParameter(u.get(), a.get(i).pattern().name()));
}
return Optional.of(result);
}
@@ -331,15 +393,16 @@ public class ASTToTargetAST {
var u_opt = unify(m, m1);
if (u_opt.isPresent()) {
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);
R.remove(m);
R.remove(m1);
R.add(u);
a.add(u);
} /*else {
Target.logger.info("Couldn't unify " + m + " AND " + m1);
}*/
m = u;
} else {
//System.out.println("Couldn't unify " + m + " AND " + m1);
}
}
}
}
@@ -352,6 +415,8 @@ public class ASTToTargetAST {
return R;
}
private record CtorWithGenerics(TargetConstructor ctor, IGenerics generics) {}
public TargetStructure convert(ClassOrInterface input) {
var generics = all.getFirst();
Set<TargetGeneric> javaGenerics = new HashSet<>();
@@ -381,8 +446,33 @@ public class ASTToTargetAST {
TargetBlock finalFieldInitializer = fieldInitializer;
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 m1 = new ArrayList<TargetMethod>();
@@ -446,7 +536,7 @@ public class ASTToTargetAST {
var res = new ArrayList<MethodParameter>();
for (var i = 0; i < input.getFormalparalist().size(); i++) {
var param = input.getFormalparalist().get(i);
var pattern = (TargetPattern) convert(param, Generics.nullGenerics().javaGenerics);
var pattern = (TargetPattern) convert(param, Generics.nullGenerics(compiler).javaGenerics);
if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i);
res.add(new MethodParameter(pattern));
}
@@ -487,29 +577,19 @@ public class ASTToTargetAST {
return convertedGenerics;
}
private List<TargetConstructor> convert(ClassOrInterface currentClass, Constructor input, TargetBlock fieldInitializer, Generics generics) {
generics = all.get(0);
List<TargetConstructor> result = new ArrayList<>();
Set<List<MethodParameter>> parameterSet = new HashSet<>();
private TargetConstructor convert(ClassOrInterface currentClass, Constructor input, TargetBlock fieldInitializer, Generics generics) {
this.currentMethod = input;
this.usedTPHsOfMethods.put(input, new HashSet<>());
for (var s : all) {
generics = s;
var javaGenerics = generics.javaGenerics.generics(currentClass, input);
var txGenerics = generics.txGenerics.generics(currentClass, input);
List<MethodParameter> params = convert(input.getParameterList(), generics.javaGenerics);
if (parameterSet.stream().noneMatch(p -> p.equals(params))) {
List<MethodParameter> txParams = convert(input.getParameterList(), generics.txGenerics);
var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, input);
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, input);
var javaGenerics = generics.javaGenerics.generics(currentClass, input);
var txGenerics = generics.txGenerics.generics(currentClass, input);
List<MethodParameter> params = convert(input.getParameterList(), generics.javaGenerics);
List<MethodParameter> txParams = convert(input.getParameterList(), generics.txGenerics);
var javaMethodGenerics = collectMethodGenerics(currentClass, generics.javaGenerics(), javaGenerics, input);
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, input);
result.add(new TargetConstructor(input.modifier, javaMethodGenerics, txMethodGenerics, params, txParams, convert(input.block, generics.javaGenerics), fieldInitializer));
parameterSet.add(params);
}
}
return result;
this.currentMethod = null;
return new TargetConstructor(input.modifier, javaMethodGenerics, txMethodGenerics, params, txParams, convert(input.block, generics.javaGenerics), fieldInitializer);
}
private static int counter = 0;
@@ -556,7 +636,7 @@ public class ASTToTargetAST {
return caseBody;
}
private static TargetExpression generatePatternOverloadsRec(int offset, TargetExpression switchExpr, List<TargetLocalVar> params, List<TargetPattern> patterns, List<TargetMethod> methods, TargetType classType) {
private TargetExpression generatePatternOverloadsRec(int offset, TargetExpression switchExpr, List<TargetLocalVar> params, List<TargetPattern> patterns, List<TargetMethod> methods, TargetType classType) {
if (methods.isEmpty()) throw new DebugException("Couldn't find a candidate for switch overloading");
if (methods.size() == 1) {
var method = methods.getFirst();
@@ -566,6 +646,17 @@ public class ASTToTargetAST {
var cases = new ArrayList<TargetSwitch.Case>();
var usedPatterns = new HashSet<TargetPattern>();
for (int i = 0; i < methods.size(); i++) {
for (int j = i + 1; j < methods.size(); j++) {
var m1 = methods.get(i);
var m2 = methods.get(j);
if (m1.signature().equals(m2.signature()) && m1.base() != m2.base()) {
compiler.warn(new CompilerWarning(m1.base().getOffset(), "Duplicate Method definition " + m1.getSimpleName() +
" found, signature " + m1.signature().getDescriptor() + " clashes with signature " + m2.signature().getDescriptor()));
}
}
}
for (var method : methods) {
var patternsRec = new ArrayList<>(patterns);
@@ -655,7 +746,9 @@ public class ASTToTargetAST {
private Optional<TargetMethod> generateBridgeMethod(ClassOrInterface clazz, List<TargetMethod> methods) {
// If there's only one method we don't need a bridge
if (clazz.isInterface()) return Optional.empty();
if (methods.size() <= 1) return Optional.empty();
var firstMethod = methods.getFirst();
var ra = firstMethod.signature().returnType();
@@ -715,8 +808,8 @@ public class ASTToTargetAST {
return 0;
}
if (isSubtype(a.type(), b.type())) return -1;
if (isSubtype(b.type(), a.type())) return 1;
if (isSubtype(a.type(), b.type(), compiler)) return -1;
if (isSubtype(b.type(), a.type(), compiler)) return 1;
return 0;
}
@@ -760,7 +853,12 @@ public class ASTToTargetAST {
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()));
}
@@ -827,7 +925,9 @@ public class ASTToTargetAST {
private TargetMethod convert(MethodWithTphs mtph, IGenerics generics) {
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) {
@@ -851,7 +951,13 @@ public class ASTToTargetAST {
for (var tph : tphs) {
var left = a.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)) {
Target.logger.error(m.name + " Discarded " + tph + " was " + left.name() + " and " + right.name());
return true;
}
}
return false;
}
@@ -889,10 +995,13 @@ public class ASTToTargetAST {
signatures.add(new Signature(javaSignature, txSignature, generics));
}
if (!signatures.isEmpty()) {
var signature = signatures.getFirst();
// We need to convert once to find out what TPHs are existing in the given method
convert(new MethodWithTphs(method, signature.generics, signature));
for (var signature : new ArrayList<>(signatures)) {
try {
convert(new MethodWithTphs(method, signature.generics, signature));
} catch (DiscardResultSet discard) {
// If a result set is discarded we skip it from now on
signatures.removeIf(s -> s == signature);
}
}
for (var signature : signatures) {
@@ -901,13 +1010,14 @@ public class ASTToTargetAST {
.filter(m -> typesAreDifferent(method, m.generics.javaGenerics, signature.generics.javaGenerics)).findFirst();
if (duplicate.isPresent()) {
var d = duplicate.get();
compiler.warn(new CompilerWarning(method.block.getOffset(), "Duplicate Method definition " + method.name +
compiler.warn(new CompilerWarning(method.block.getOffset(), "Duplicate Method definition " + method.getName() +
" found, signature " + d.signature.java.getDescriptor() + " clashes with signature " + signature.java.getDescriptor()));
break;
}
result.add(mtph);
}
this.currentMethod = null;
return result;
}
@@ -980,15 +1090,43 @@ public class ASTToTargetAST {
return null;
}
private boolean isSubtype(TargetType test, TargetType other) {
public static ClassOrInterface getCommonSuperType(TargetType test, TargetType other, JavaTXCompiler compiler) {
var testClass = compiler.getClass(new JavaClassName(test.name()));
var otherClass = compiler.getClass(new JavaClassName(other.name()));
if (testClass == null) return null;
while (testClass != null) {
if (otherClass.isInterface()) {
for (var superInterface : testClass.getSuperInterfaces()) {
if (superInterface.getName().equals(otherClass.getClassName())) return otherClass;
var c = getCommonSuperType(new TargetRefType(superInterface.getName().toString()), other, compiler);
if (c != null) return c;
}
}
if (testClass.equals(otherClass)) return testClass;
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) return testClass;
testClass = compiler.getClass(testClass.getSuperClass().getName());
}
return null;
}
public static boolean isSubtype(TargetType test, TargetType other, JavaTXCompiler compiler) {
if (other == null) return false;
if (other.equals(TargetType.Object)) return true;
if (test instanceof TargetGenericType || other instanceof TargetGenericType) return false;
if (test instanceof TargetFunNType tfun && other instanceof TargetFunNType ofun)
return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun));
return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun), compiler);
if (other instanceof TargetFunNType) return false;
var testClass = compiler.getClass(new JavaClassName(test.name()));
var otherClass = compiler.getClass(new JavaClassName(other.name()));
if (testClass == null) return false;
while (testClass != null) {
if (otherClass.isInterface()) {
for (var superInterface : testClass.getSuperInterfaces()) {
if (superInterface.getName().equals(otherClass.getClassName())) return true;
if (isSubtype(new TargetRefType(superInterface.getName().toString()), other, compiler)) return true;
}
}
if (testClass.equals(otherClass)) return true;
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break;
testClass = compiler.getClass(testClass.getSuperClass().getName());
@@ -996,17 +1134,17 @@ public class ASTToTargetAST {
return false;
}
private boolean isSupertype(TargetType test, TargetType other) {
return isSubtype(other, test);
private static boolean isSupertype(TargetType test, TargetType other, JavaTXCompiler compiler) {
return isSubtype(other, test, compiler);
}
private boolean isSubtype(FunNGenerator.GenericParameters test, FunNGenerator.GenericParameters other) {
private static boolean isSubtype(FunNGenerator.GenericParameters test, FunNGenerator.GenericParameters other, JavaTXCompiler compiler) {
if (test.getArguments().size() != other.getArguments().size()) return false;
if (!isSubtype(test.getReturnType(), other.getReturnType())) return false;
if (!isSubtype(test.getReturnType(), other.getReturnType(), compiler)) return false;
for (int i = 0; i < test.getArguments().size(); i++) {
var arg1 = test.getArguments().get(i);
var arg2 = other.getArguments().get(i);
if (!isSupertype(arg1, arg2)) return false;
if (!isSupertype(arg1, arg2, compiler)) return false;
}
return true;
}
@@ -1016,7 +1154,7 @@ public class ASTToTargetAST {
var gep = entry.getValue();
var superInterfaces = compiler.usedFunN.values().stream()
.filter(g -> !g.equals(gep))
.filter(genericParameters -> isSubtype(gep, genericParameters))
.filter(genericParameters -> isSubtype(gep, genericParameters, compiler))
.map(FunNGenerator::getSpecializedClassName)
.toList();
@@ -1075,6 +1213,8 @@ public class ASTToTargetAST {
gep = compiler.usedFunN.get(className);
}
return flattenFunNType(params, gep);
} else if (name.matches("Fun\\d+\\$\\$.*")) {
return new TargetFunNType(name, List.of(), List.of(), 0, false);
}
return new TargetRefType(name, params);
}
@@ -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() {}
}
@@ -142,12 +142,12 @@ public abstract class GenerateGenerics implements IGenerics {
this.constraints = constraints;
for (var constraint : constraints.results) {
if (constraint instanceof PairTPHsmallerTPH p) {
Target.logger.info(p.left + " " + p.left.getVariance());
Target.logger.debug(p.left + " " + p.left.getVariance());
simplifiedConstraints.add(new PairLT(new TPH(p.left), new TPH(p.right)));
} else if (constraint instanceof PairTPHEqualTPH p) {
equality.put(p.getLeft(), p.getRight());
} else if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) {
Target.logger.info(p.left + " = " + p.right);
Target.logger.debug(p.left + " = " + p.right);
concreteTypes.put(new TPH(p.left), p.right);
}
}
@@ -242,7 +242,7 @@ public abstract class GenerateGenerics implements IGenerics {
equality.put(entry.getKey(), to);
}
}
Target.logger.info(from + " -> " + to + " " + from.getVariance());
Target.logger.debug(from + " -> " + to + " " + from.getVariance());
//from.setVariance(to.getVariance());
equality.put(from, to);
referenced.remove(new TPH(from));
@@ -311,7 +311,7 @@ public abstract class GenerateGenerics implements IGenerics {
Set<TPH> T2s = new HashSet<>();
findTphs(superType, T2s);
Target.logger.info("T1s: " + T1s + " T2s: " + T2s);
Target.logger.debug("T1s: " + T1s + " T2s: " + T2s);
//Ende
superType = methodCall.receiverType;
@@ -326,7 +326,7 @@ public abstract class GenerateGenerics implements IGenerics {
var optMethod = ASTToTargetAST.findMethod(owner, methodCall.name, methodCall.signatureArguments().stream().map(x -> getTargetType(x)).toList(), GenerateGenerics.this, compiler);
if (optMethod.isEmpty()) return;
var method2 = optMethod.get();
Target.logger.info("In: " + method.getName() + " Method: " + method2.getName());
Target.logger.debug("In: " + method.getName() + " Method: " + method2.getName());
var generics = family(owner, method2);
// transitive and
@@ -359,7 +359,7 @@ public abstract class GenerateGenerics implements IGenerics {
if (!T1s.contains(R1) || !T2s.contains(R2)) continue;
var newPair = new PairLT(R1, R2);
Target.logger.info("New pair: " + newPair);
Target.logger.debug("New pair: " + newPair);
newPairs.add(newPair);
if (!containsRelation(result, newPair))
@@ -560,7 +560,7 @@ public abstract class GenerateGenerics implements IGenerics {
public Set<Pair> generics(ClassOrInterface owner, Method method) {
if (computedGenericsOfMethods.containsKey(method)) {
var cached = computedGenericsOfMethods.get(method);
Target.logger.info("Cached " + method.getName() + ": " + cached);
Target.logger.debug("Cached " + method.getName() + ": " + cached);
return cached;
}
@@ -589,7 +589,7 @@ public abstract class GenerateGenerics implements IGenerics {
normalize(result, classGenerics, usedTphs);
Target.logger.info(this.getClass().getSimpleName() + " " + method.name + ": " + result);
Target.logger.debug(this.getClass().getSimpleName() + " " + method.name + ": " + result);
return result;
}
@@ -721,7 +721,7 @@ public abstract class GenerateGenerics implements IGenerics {
if (!added) break;
}
Target.logger.info(chain + " " + chain.stream().map(e -> e.resolve().getVariance()).toList());
Target.logger.debug(chain + " " + chain.stream().map(e -> e.resolve().getVariance()).toList());
var variance = chain.get(0).resolve().getVariance();
if (variance != 1) continue;
var index = 0;
@@ -916,7 +916,7 @@ public abstract class GenerateGenerics implements IGenerics {
for (var pair : infima) {
var returnTypes = findTypeVariables(method.getReturnType());
var chain = findConnectionToReturnType(returnTypes, input, new HashSet<>(), pair.left);
Target.logger.info("Find: " + pair.left + " " + chain);
Target.logger.debug("Find: " + pair.left + " " + chain);
chain.remove(pair.left);
if (chain.size() > 0) {
for (var tph : chain)
@@ -954,8 +954,8 @@ public abstract class GenerateGenerics implements IGenerics {
}
}
newTph.setVariance(variance);
Target.logger.info(infima + " " + infima.stream().map(i -> i.right.resolve().getVariance()).toList());
Target.logger.info("Infima new TPH " + newTph + " variance " + variance);
Target.logger.debug(infima + " " + infima.stream().map(i -> i.right.resolve().getVariance()).toList());
Target.logger.debug("Infima new TPH " + newTph + " variance " + variance);
//referenced.add(newTph);
addToPairs(input, new PairLT(left, new TPH(newTph)));
@@ -1008,7 +1008,9 @@ public abstract class GenerateGenerics implements IGenerics {
public TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
if (in instanceof TypePlaceholder 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));
if (type == null) return new TargetGenericType(tph.getName());
@@ -1,5 +1,6 @@
package de.dhbwstuttgart.target.generate;
import com.google.common.collect.Iterables;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.NotImplementedException;
@@ -230,6 +231,8 @@ public class StatementToTargetExpression implements ASTVisitor {
var argList = methodCall.signature.stream().map(sig -> converter.convert(sig, generics)).toList();
argList = argList.subList(0, argList.size() - 1);
var receiverClass = converter.compiler.getClass(receiverName);
Method foundMethod = null;
var isStatic = false;
var isInterface = true;
@@ -241,7 +244,6 @@ public class StatementToTargetExpression implements ASTVisitor {
converter.addSignaturePair(methodCall.signatureArguments().get(i), methodCall.arglist.getArguments().get(i).getType());
}
var receiverClass = converter.compiler.getClass(receiverName);
if (methodCall.receiver instanceof ExpressionReceiver expressionReceiver && expressionReceiver.expr instanceof This) {
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
var thisMethod = ASTToTargetAST.findMethod(receiverClass, methodCall.name, signature, generics, converter.compiler);
@@ -250,18 +252,60 @@ public class StatementToTargetExpression implements ASTVisitor {
} else if (!isFunNType) {
receiverClass = converter.compiler.getClass(receiverName);
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) {
returnType = converter.convert(foundMethod.getReturnType(), generics);
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> converter.convert(e.getType(), generics)).toList();
// NOTE Not using the direct conversion method on converter to bypass adding the TPH to the used TPH list
returnType = ASTToTargetAST.convert(foundMethod.getReturnType(), generics, converter.compiler);
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> ASTToTargetAST.convert(e.getType(), generics, converter.compiler)).toList();
isStatic = Modifier.isStatic(foundMethod.modifier);
isPrivate = Modifier.isPrivate(foundMethod.modifier);
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) && ASTToTargetAST.isSubtype(receiverType, otherReceiver, converter.compiler)) {
ClassOrInterface clazz = converter.compiler.getClass(new JavaClassName(otherReceiver.name()));
if (clazz != null) {
var optMethod = ASTToTargetAST.findMethod(clazz, methodCall.name, argList, converter.compiler);
if (optMethod.isPresent()) {
throw new DiscardResultSet();
}
}
}
}
}
// Look at the signature in all result sets and check if a more specific insertion exists.
// If so, discard this result set by throwing an exception
for (var g2 : conflicitGenerics) {
for (var tph : Iterables.concat(methodCall.signatureArguments())) {
var currentType = converter.convert(tph, generics);
var type = converter.convert(tph, g2.javaGenerics());
if (!Objects.equals(type, currentType) && ASTToTargetAST.isSubtype(type, currentType, converter.compiler)) {
throw new DiscardResultSet();
}
}
}
result = new TargetMethodCall(
converter.convert(methodCall.getType(), generics), returnType, argList,
converter.convert(methodCall.receiver, generics),
@@ -3,6 +3,7 @@ package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.target.generate.IGenerics;
import de.dhbwstuttgart.target.tree.expression.TargetBlock;
import de.dhbwstuttgart.target.tree.expression.TargetComplexPattern;
import de.dhbwstuttgart.target.tree.expression.TargetPattern;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.Opcodes;
@@ -16,6 +17,13 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature
this(access, name, block, signature, txSignature, null, null);
}
// If this was a generated method it has an unwieldy name, this returns the original name
// like for instance dup$0$Ljava$lang$Integer$_$ -> dup
public String getSimpleName() {
if (name.contains("$")) return name.split("\\$")[0];
return name;
}
public record Signature(Set<TargetGeneric> generics, List<MethodParameter> parameters, TargetType returnType) {
public String getSignature() {
return TargetMethod.getSignature(generics, parameters, returnType);
@@ -30,7 +38,13 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Signature signature = (Signature) o;
return Objects.equals(parameters, signature.parameters);
if (this.parameters.size() != signature.parameters.size()) return false;
for (var i = 0; i < this.parameters.size(); i++) {
var p1 = this.parameters.get(i);
var p2 = signature.parameters.get(i);
if (!p1.pattern().fuzzyEquals(p2.pattern())) return false;
}
return true;
}
@Override
@@ -3,6 +3,7 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List;
import java.util.Objects;
public record TargetComplexPattern(TargetType ctor, TargetType type, String name, List<TargetPattern> subPatterns) implements TargetPattern {
@Override
@@ -19,4 +20,10 @@ public record TargetComplexPattern(TargetType ctor, TargetType type, String name
public String toString() {
return type + "(" + String.join(", ", subPatterns.stream().map(Object::toString).toList()) + ") " + name;
}
@Override
public boolean fuzzyEquals(TargetPattern other) {
if (!(other instanceof TargetComplexPattern op)) return false;
return Objects.equals(this.type, op.type) && Objects.equals(this.subPatterns, op.subPatterns);
}
}
@@ -2,6 +2,8 @@ package de.dhbwstuttgart.target.tree.expression;
import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.Objects;
public sealed interface TargetPattern extends TargetExpression permits TargetComplexPattern, TargetExpressionPattern, TargetGuard, TargetTypePattern {
default String name() {
return null;
@@ -12,4 +14,8 @@ public sealed interface TargetPattern extends TargetExpression permits TargetCom
TargetType type();
TargetPattern withName(String name);
default boolean fuzzyEquals(TargetPattern other) {
return Objects.equals(this.type(), other.type());
}
}
@@ -3,8 +3,9 @@ package de.dhbwstuttgart.target.tree.type;
import de.dhbwstuttgart.bytecode.FunNGenerator;
import java.util.List;
import java.util.Objects;
public record TargetFunNType(String name, List<TargetType> funNParams, List<TargetType> params, int returnArguments) 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) {
return fromParams(params, params, returnArguments);
@@ -12,7 +13,7 @@ public record TargetFunNType(String name, List<TargetType> funNParams, List<Targ
public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams, int returnArguments) {
var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params));
return new TargetFunNType(name, params, realParams, returnArguments);
return new TargetFunNType(name, params, realParams, returnArguments, false);
}
public String toMethodDescriptor() {
@@ -39,4 +40,21 @@ public record TargetFunNType(String name, List<TargetType> funNParams, List<Targ
var args = FunNGenerator.getArguments(funNParams);
return "LFun" + args.size() + "$$" + TargetSpecializedType.signatureParameters(funNParams) + ";";
}
public boolean equals(Object other) {
if (!(other instanceof TargetFunNType otfn)) return false;
if (!isInterface && !otfn.isInterface) {
if (!Objects.equals(name, otfn.name)) return false;
if (funNParams.size() != otfn.funNParams.size()) return false;
for (var i = 0; i < funNParams.size(); i++) {
var p1 = funNParams.get(i);
var p2 = otfn.funNParams.get(i);
if (p1 instanceof TargetGenericType || p2 instanceof TargetGenericType) continue;
if (!Objects.equals(p1, p2)) return false;
}
return true;
}
return Objects.equals(name, otfn.name);
}
}
@@ -254,6 +254,9 @@ public class TYPEStmt implements StatementVisitor {
} else if (unaryExpr.operation == UnaryExpr.Operation.NOT) {
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), unaryExpr.getType(), PairOperator.EQUALSDOT, loc(unaryExpr.getOffset())));
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), new RefType(ASTFactory.createClass(java.lang.Boolean.class).getClassName(), new NullToken()), PairOperator.EQUALSDOT, loc(unaryExpr.getOffset())));
} else if (unaryExpr.operation == UnaryExpr.Operation.MINUS) {
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), number, PairOperator.SMALLERDOT, loc(unaryExpr.getOffset())));
constraintsSet.addUndConstraint(new Pair(unaryExpr.expr.getType(), unaryExpr.getType(), PairOperator.EQUALSDOT, loc(unaryExpr.getOffset())));
} else {
throw new NotImplementedException();
}
@@ -392,6 +395,8 @@ public class TYPEStmt implements StatementVisitor {
@Override
public void visit(BoolExpression expr) {
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.lexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
@@ -32,14 +32,12 @@ public class Logger {
private static Writer defaultWriter;
private static void initLogger() {
if (defaultWriter != null) return;
if (ConsoleInterface.writeLogFiles) {
try {
Files.createDirectories(logFolder.toPath());
} catch (Exception e) {
throw new RuntimeException("Could not create directory for log files: " + logFolder, e);
}
if (!ConsoleInterface.writeLogFiles) return;
try {
Files.createDirectories(logFolder.toPath());
} catch (Exception e) {
throw new RuntimeException("Could not create directory for log files: " + logFolder, e);
}
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
var currentTimestamp = LocalDateTime.now().format(formatter);
try {
+63 -9
View File
@@ -1,11 +1,17 @@
import de.dhbwstuttgart.bytecode.CodeGenException;
import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.exceptions.WarningsException;
import de.dhbwstuttgart.util.Logger;
import de.dhbwstuttgart.util.Logger.LogLevel;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;
@@ -19,8 +25,14 @@ import static org.junit.jupiter.api.Assertions.*;
import static targetast.TestCodegen.createClassLoader;
import static targetast.TestCodegen.generateClassFiles;
@Execution(ExecutionMode.CONCURRENT)
public class TestComplete {
@BeforeEach
public void setup() throws IOException {
TestCodegen.outputPath.toFile().mkdirs();
FileUtils.cleanDirectory(TestCodegen.outputPath.toFile());
}
@Test
public void applyLambdaTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "applyLambda.jav");
@@ -320,9 +332,17 @@ public class TestComplete {
}
@Test
public void overloadingSortingTest() throws Exception {
public void sortingTest() throws Exception {
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
@@ -657,6 +677,15 @@ public class TestComplete {
System.out.println(clazz.getDeclaredMethod("toString").invoke(instance));
}
@Test
public void recordTestInstanceVariables() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "RecordTestInstanceVariables.jav");
var clazz = classFiles.get("RecordTestInstanceVariables");
var instance = clazz.getDeclaredConstructor(Integer.class, Integer.class).newInstance(10, 20);
assertEquals(10, clazz.getDeclaredMethod("getA").invoke(instance));
assertEquals(20, clazz.getDeclaredMethod("getB").invoke(instance));
}
@Test
public void genericRecordTest() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "GenericRecord.jav");
@@ -1033,6 +1062,7 @@ public class TestComplete {
var instance = clazz.getDeclaredConstructor().newInstance();
}
@Disabled("Feature nicht implementiert")
@Test
public void testOverloadSwitch() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "SwitchOverload.jav");
@@ -1066,6 +1096,16 @@ public class TestComplete {
assertEquals(50, m.invoke(null));
}
@Test
public void testStaticFail() throws Exception {
try {
generateClassFiles(createClassLoader(), "StaticFail.jav");
fail("No exception thrown!");
} catch (CodeGenException e) {
assertEquals("Attempted to call instance method nonStaticM with descriptor ()V from a static context", e.getMessage());
}
}
@Test
public void testFor() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "For.jav");
@@ -1286,6 +1326,14 @@ public class TestComplete {
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
public void testBug122() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug122.jav");
@@ -1449,7 +1497,6 @@ public class TestComplete {
}
@Test
@Disabled("too slow")
public void testBug325() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug325.jav");
var clazz = classFiles.get("Bug325");
@@ -1534,7 +1581,9 @@ public class TestComplete {
var m = clazz.getDeclaredMethod("ex1");
assertEquals("ABC", m.invoke(instance));
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
public void testBug366() throws Exception {
@@ -1613,9 +1662,14 @@ public class TestComplete {
}
@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());
public void testBug395() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug395.jav");
fail("Shouldn't compile!");
} catch (WarningsException e) {
assertEquals(1, e.compilerWarnings.size());
var warning = e.compilerWarnings.get(0);
assertEquals("Duplicate Method definition dup found, signature (Ljava/lang/Integer;)Ljava/lang/Integer; clashes with signature (Ljava/lang/Integer;)Ljava/lang/Integer;", warning.getMessage());
}
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ public class TestTypeDeployment {
var path = Path.of(System.getProperty("user.dir"), "/resources/bytecode/javFiles/Cycle.jav");
var file = path.toFile();
var compiler = new JavaTXCompiler(file);
compiler.parseAll();
compiler.generateBytecode();
var parsedSource = compiler.sourceFiles.get(file);
var tiResults = compiler.typeInference(file);
Set<TypeInsert> tips = new HashSet<>();
+3 -3
View File
@@ -18,7 +18,7 @@ public class InheritTest {
public static void setUpBeforeClass() throws Exception {
var classLoader = TestCodegen.createClassLoader();
var classes = TestCodegen.generateClassFiles(classLoader, "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");
classToTestAA = classes.get("AA");
classToTestBB = classes.get("BB");
@@ -52,7 +52,7 @@ public class InheritTest {
public void testmainCC() throws Exception {
Method m = classToTestCC.getDeclaredMethod("m", Integer.class);
assertEquals("CC", m.invoke(instanceOfClassCC, 5));
Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class);
Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class);
assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassCC, 5));
}
@@ -60,7 +60,7 @@ public class InheritTest {
public void testmainDD() throws Exception {
Method m = classToTestCC.getDeclaredMethod("m", Integer.class);
assertEquals("CC", m.invoke(instanceOfClassDD, 5));
Method main = classToTest.getDeclaredMethod("main", classToTestCC, Integer.class);
Method main = classToTest.getDeclaredMethod("main", classToTestAA, Integer.class);
assertEquals("CC", main.invoke(instanceOfClass, instanceOfClassDD, 5));
}
+2 -2
View File
@@ -35,7 +35,7 @@ public class PutTest {
Method m = classToTest.getDeclaredMethod("putElement", Object.class, Stack.class);
Stack<Integer> s_invoke = new Stack<>();
m.invoke(instanceOfClass, 5, s_invoke);
assertEquals(new Integer(5), s_invoke.pop());
assertEquals(5, s_invoke.pop());
}
@Test
@@ -53,6 +53,6 @@ public class PutTest {
Method m = classToTest.getDeclaredMethod("main", Object.class, Stack.class);
Stack<Integer> s_invoke = new Stack<>();
m.invoke(instanceOfClass, 6, s_invoke);
assertEquals(new Integer(6), s_invoke.pop());
assertEquals(6, s_invoke.pop());
}
}
+7 -3
View File
@@ -5,6 +5,7 @@ import de.dhbwstuttgart.bytecode.Codegen;
import de.dhbwstuttgart.environment.DirectoryClassLoader;
import de.dhbwstuttgart.environment.IByteArrayClassLoader;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter;
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.tree.MethodParameter;
import de.dhbwstuttgart.target.tree.TargetClass;
@@ -14,6 +15,7 @@ import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.TargetFunNType;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -30,7 +32,7 @@ import java.util.function.Function;
import java.util.stream.Collectors;
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 {
Files.createDirectories(outputPath);
@@ -56,7 +58,8 @@ public class TestCodegen {
try(var newClassLoader = new DirectoryClassLoader(List.of(outputPath.toFile()), (ClassLoader)classLoader)) {
var result = new HashMap<String, Class<?>>();
for (var file : filenames) {
var classes = compiler.sourceFiles.get(file).getClasses();
var sf = compiler.sourceFiles.get(file);
var classes = sf.getClasses();
result.putAll(classes.stream().map(cli -> {
try {
@@ -71,7 +74,8 @@ public class TestCodegen {
}
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();
writeClassFile(clazz.qualifiedName().getClassName(), code);
return classLoader.loadClass(code);