Compare commits

..
27 Commits
Author SHA1 Message Date
dholle 4b576da19d Fix #414 by implementing the other number formats
Build and Test with Maven / Build-and-test-with-Maven (push) Successful in 1m26s
SonarQube Scan / SonarQube Trigger (pull_request) Successful in 2m35s
2026-11-11 14:38:19 +01:00
dholle de96cda327 Fix #417 in Codegen and by changing constraint generation
Build and Test with Maven / Build-and-test-with-Maven (push) Successful in 1m23s
SonarQube Scan / SonarQube Trigger (pull_request) Successful in 2m36s
2026-11-11 14:00:22 +01:00
dholle 2710685db3 Merge pull request 'Fix #409 by moving increment to the beginning of the loop' (#427) from fix-409 into master
SonarQube Scan / SonarQube Trigger (push) Successful in 2m29s
Reviewed-on: #427
2026-11-11 11:25:41 +00:00
dholle e951a60ae6 Fix incorrect behavior
Build and Test with Maven / Build-and-test-with-Maven (push) Successful in 1m18s
SonarQube Scan / SonarQube Trigger (pull_request) Successful in 2m37s
2026-11-11 12:18:42 +01:00
dholle 825df05fac Fix #409 by moving increment to the beginning of the loop
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m17s
SonarQube Scan / SonarQube Trigger (pull_request) Failing after 1m33s
2026-11-11 12:00:59 +01:00
dholle fab3e73596 Merge branch 'fix-423'
SonarQube Scan / SonarQube Trigger (push) Canceled after 26m58s
2026-11-11 11:34:32 +01:00
dholle 392c5fa619 Merge branch 'fix-422'
SonarQube Scan / SonarQube Trigger (push) Failing after 1m39s
2026-11-11 11:31:48 +01:00
dholle e21d2d7ced Merge pull request 'Fix #421 by adding error listeners to the Parser & Lexer' (#426) from fix-421 into master
SonarQube Scan / SonarQube Trigger (push) Failing after 1m39s
Reviewed-on: #426
2026-11-11 10:28:50 +00:00
dholle 5a12545e8f Fix #421 by adding error listeners to the Parser & Lexer
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 7m30s
SonarQube Scan / SonarQube Trigger (pull_request) Failing after 1m35s
2026-11-10 15:31:41 +01:00
dholle d6c59f07f6 Add check for duplicate class definition in source files, fixes #422
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m52s
SonarQube Scan / SonarQube Trigger (pull_request) Failing after 2m24s
2026-11-10 15:04:47 +01:00
dholle eb1c5c6629 Add check for missing interface methods, fixes #423
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m22s
SonarQube Scan / SonarQube Trigger (pull_request) Failing after 2m11s
2026-11-10 14:46:41 +01:00
dholle a5c8d93e4c Failing test
SonarQube Scan / SonarQube Trigger (push) Failing after 1m28s
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m32s
2026-10-01 10:39:29 +02:00
dholle e70f071c63 Fix tests
SonarQube Scan / SonarQube Trigger (push) Failing after 1m28s
2026-09-30 19:22:21 +02:00
dholle 4c203d9ded Fix tests
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m35s
2026-09-30 19:18:09 +02:00
dholle f66499b4fe Revert "Update"
SonarQube Scan / SonarQube Trigger (push) Failing after 3m27s
This reverts commit eb05d22735.
2026-09-29 18:01:44 +02:00
dholle eb05d22735 Update
SonarQube Scan / SonarQube Trigger (push) Failing after 2m5s
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 2m44s
2026-09-29 17:54:49 +02:00
dholle dfe90bfa2d Pattern matching on interfaces and static methods
SonarQube Scan / SonarQube Trigger (push) Failing after 3m34s
2026-09-29 17:14:07 +02:00
dholle 719e1ab4a2 Merge branch 'master' of https://gitea.hb.dhbw-stuttgart.de/JavaTX/JavaCompilerCore
SonarQube Scan / SonarQube Trigger (push) Failing after 3m28s
2026-09-23 17:15:16 +02:00
dholle 1e32f69952 Remove debug code 2026-09-23 17:15:12 +02:00
julian 65d5d79078 Merge branch 'master' of ssh://gitea.hb.dhbw-stuttgart.de:2222/JavaTX/JavaCompilerCore
SonarQube Scan / SonarQube Trigger (push) Failing after 1m42s
2026-09-23 15:36:13 +02:00
julian ca81cfd587 fix clinit signature 2026-09-23 15:35:58 +02:00
dholle 0998d231fe Fix package name
SonarQube Scan / SonarQube Trigger (push) Failing after 2m6s
2026-09-23 15:34:05 +02:00
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
36 changed files with 721 additions and 174 deletions

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
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 {
+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;
}
}
+39
View File
@@ -0,0 +1,39 @@
//package de.dhbwstuttgart;
import java.lang.Integer;
import java.lang.IO;
public sealed interface IntList permits Empty, Cons{}
public record Empty() implements IntList {}
public record Cons(Integer a, IntList rest) implements IntList {}
public class Bug406 {
public head(Empty()) {
return -1;
}
public head(Cons(e,r)) {
return e;
}
public append(Empty(), ys) {
return ys;
}
public append(Cons(x, xs), IntList ys) {
return new Cons(x, append(xs, ys));
}
public add(x, y) {
return x + y;
}
public static main(args) {
var list1 = new Cons(1, new Cons(2, new Cons(3, new Empty())));
var list2 = new Cons(4, new Cons(5, new Cons(6, new Empty())));
IO.println(new Bug406().add(1, 2));
IO.println(new Bug406().append(list1, list2));
}
}
+13
View File
@@ -0,0 +1,13 @@
import java.lang.Integer;
public class Bug409 {
public static m() {
var n = 0;
for (var i = 0; i < 5; i++) {
n = n + 1;
if (n > 100) { return -1; }
if (i == 2) { continue; }
}
return n;
}
}
+11
View File
@@ -0,0 +1,11 @@
import java.lang.Integer;
import java.lang.String;
public class Bug417 {
public name(Integer i) {
return switch (i) {
case 1 -> "one";
default -> "many";
};
}
}
+3
View File
@@ -0,0 +1,3 @@
public class Bug421 {
public m() { return 1 } // Missing semicolon
}
+2
View File
@@ -0,0 +1,2 @@
public class Bug422 {}
public class Bug422 {}
+5
View File
@@ -0,0 +1,5 @@
import java.lang.Integer;
interface Bug423I { Integer f(); }
public class Bug423 implements Bug423I { }
@@ -15,6 +15,7 @@ interface C {
}
class ClassX implements A {
void method1() {}
}
record ClassY(Integer myInt) implements C {}
+22 -5
View File
@@ -4,27 +4,44 @@
//ein Lazy-Argument mit get ausgerollt wird (siehe Methode rest)
import java.lang.Integer;
import java.lang.Boolean;
import java.lang.String;
import java.lang.System;
import java.io.PrintStream;
import java.util.function.Supplier;
public sealed interface LazyList permits Empty, Cons {
public Integer fst();
public LazyList rest();
}
public class Filter {
/*public LazyList filter(Fun1$$<Integer, Boolean> p, LazyList l) {
return switch(l) {
case Empty e -> e;
case Cons(Integer x, Supplier<LazyList> l1) ->
p.apply(x) ? new Cons(x, () -> filter(p, l1.get())) : filter(p, l1.get());
};
}*/
public LazyList filter(Fun1$$<Integer, Boolean> p, Empty()@l) {
return l;
}
public LazyList filter(Fun1$$<Integer, Boolean> p, Cons(Integer x, l1)) {
return p.apply(x) ? new Cons(x, () -> filter(p, l1.apply())) : filter(p, l1.apply());
}
}
//Der Konstruktor Cons muss lazy sein, deshalb hier Supplier<...>
record Cons(Integer x, Supplier<LazyList> l) implements LazyList {
record Cons(Integer x, Fun0$$<LazyList> l) implements LazyList {
public Integer fst() { return this.x; }
public LazyList rest() { return this.l.get(); }
public LazyList rest() { return this.l.apply(); }
public String toString() {
return "Cons(" + this.x.toString() + ", " + this.l.get().toString() + ")";
return "Cons(" + this.x.toString() + ", " + this.l.apply().toString() + ")";
}
}
record Empty() implements LazyList {
public Integer fst() { return -1;}
public Integer fst() { return -1; }
public LazyList rest() { return null; }
}
+4
View File
@@ -5,4 +5,8 @@ public class Literal {
public m2() { return 'C'; }
public m3() { return 10L; }
public m4() { return 10.5F; }
public m5() { return 10.5D; }
public m6() { return 0xFF; }
public m7() { return 0b101010; }
public m8() { return 010; }
}
+8
View File
@@ -0,0 +1,8 @@
sealed interface Interface permits A, B {}
public record A() implements Interface {}
public record B() implements Interface {}
public interface Matcher {
default match(A()@a) { return 0; }
default match(B()@b) { return 1; }
}
@@ -0,0 +1,12 @@
import java.lang.Integer;
import Matcher;
import Interface;
public class M implements Matcher {}
public class PatternMatchingInterface {
public static match(matcher, v) {
return matcher.match(v);
}
}
+19 -18
View File
@@ -3,26 +3,25 @@ import java.lang.String;
import java.lang.System;
import java.lang.Boolean;
import java.io.PrintStream;
import java.util.function.Supplier;
import java.util.function.Function;
import LazyList;
import Cons;
import Empty;
import Filter;
public class Primzahlen {
static LazyList from(int i) { return new Cons(i, () -> from(i+1)); }
static LazyList from(Integer i) { return new Cons(i, () -> from(i+1)); }
LazyList filter(Function<Integer, Boolean> p, LazyList l) {
return switch (l) {
case Empty() -> l;
case Cons(Integer x, Supplier<LazyList> l1) ->
p.apply(x) ? new Cons(x, () -> filter(p, l1.get())) : filter(p, l1.get());
/*LazyList filter(Fun1$$<Integer, Boolean> p, LazyList l) {
return switch(l) {
case Empty e -> e;
case Cons(Integer x, Fun0$$<LazyList> l1) ->
p.apply(x) ? new Cons(x, () -> filter(p, l1.apply())) : filter(p, l1.apply());
};
};
}*/
LazyList dropMul(Integer x, LazyList xs) {
return filter(y -> (y % x != 0), xs);
return new Filter().filter(y -> (y % x != 0), xs);
}
LazyList del(LazyList xs) {
@@ -37,18 +36,20 @@ public class Primzahlen {
if (n == 0) return new Empty();
else return switch (l) {
case Empty() -> l;
case Cons(Integer x, Supplier<LazyList> l1) ->
new Cons(x, () -> take(n-1, l1.get()));
case Cons(Integer x, l1) ->
new Cons(x, () -> take(n-1, l1.apply()));
};
};
public static void main(args) {
System.out.println(new Cons(1, () -> new Cons(2, () -> new Empty())).fst());
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()));
var l = new Cons(1, () -> new Cons(2, () -> new Cons(2, () -> new Empty())));
System.out.println(l);
var filtered = new Filter().filter(x -> x == 2, l);
System.out.println(filtered);
//System.out.println(from(1).rest().fst());
//Primzahlen pz = new Primzahlen();
//System.out.println(pz.take(10, pz.from(2)));
//System.out.println(pz.take(14, pz.primes()));
}
}
@@ -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; }
}
@@ -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) {
@@ -865,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));
@@ -1040,6 +1056,7 @@ public class Codegen {
}
Label start = new Label();
Label skip = new Label();
Label end = new Label();
mv.visitLabel(start);
if (_for.termination() != null) {
@@ -1052,13 +1069,14 @@ public class Codegen {
}
var env = new BreakEnv();
env.startLabel = start;
env.startLabel = skip;
env.endLabel = end;
state.breakStack.push(env);
generate(state, _for.body());
state.breakStack.pop();
mv.visitLabel(skip);
if (_for.increment() != null) {
_for.increment().forEach(e -> {
generate(state, e);
@@ -1189,6 +1207,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());
}
@@ -1199,13 +1223,14 @@ public class Codegen {
convertTo(state, e.type(), arg);
}
var descriptor = call.getDescriptor();
TargetType casted = call.type();
if (call.owner() instanceof TargetFunNType owner) {
// Decay FunN
descriptor = TargetMethod.getDescriptor(
(owner.returnArguments() == 0 ? null : TargetType.Object),
call.parameterTypes().stream().map(x -> TargetType.Object).toArray(TargetType[]::new));
if (owner.returnArguments() != 0)
casted = FunNGenerator.getReturnType(owner.funNParams());
}
int insn = INVOKEVIRTUAL;
if (call.isStatic()) insn = INVOKESTATIC;
else if (call.isInterface()) insn = INVOKEINTERFACE;
@@ -1219,7 +1244,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:
@@ -1412,13 +1437,16 @@ public class Codegen {
if (label instanceof TargetTypePattern || label instanceof TargetComplexPattern) {
if (label.type() instanceof TargetGenericType) types.add(Type.getType(Object.class));
else types.add(Type.getObjectType(label.type().getInternalName()));
} else if (label instanceof TargetLiteral lit) {
types.add(lit.value());
} else if (label instanceof TargetExpressionPattern expr) {
if (expr.expression() instanceof TargetLiteral literal) {
types.add(Type.getObjectType(TargetType.toWrapper(literal.type()).getInternalName()));
} else {
throw new NotImplementedException();
}
} else if (label instanceof TargetGuard guard) {
types.add(Type.getObjectType(guard.inner().type().getInternalName()));
// TODO Same here we need to evaluate constant;
} else {
logger.info(label);
throw new NotImplementedException();
}
}
@@ -1486,6 +1514,9 @@ public class Codegen {
// throw illegal argument exception on missing default case
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
mv.visitInsn(DUP);
//mv.visitVarInsn(ALOAD, tmp);
//mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
//mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
mv.visitLdcInsn("Unhandled case value");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", false);
mv.visitInsn(ATHROW);
@@ -1520,9 +1551,28 @@ public class Codegen {
//if (pat.type() instanceof TargetPrimitiveType)
// boxPrimitive(state, pat.type());
if (pat.type() instanceof TargetRefType) {
if (pat.type() instanceof TargetSpecializedType) {
var name = pat.type().getInternalName();
//if (pat.type() instanceof TargetFunNType funNType) {
// name = FunNGenerator.getSuperClassName(funNType.funNParams().size() - 1, funNType.returnArguments());
//}
state.mv.visitInsn(DUP);
state.mv.visitTypeInsn(INSTANCEOF, pat.type().getInternalName());
/*state.mv.visitInsn(DUP);
state.mv.visitTypeInsn(INSTANCEOF, "Fun0$$LLazyList$_$");
state.mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
state.mv.visitInsn(SWAP);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Z)V", false);
state.mv.visitInsn(DUP);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
state.mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
state.mv.visitInsn(SWAP);
state.mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
*/
state.mv.visitTypeInsn(INSTANCEOF, name);
var cont = new Label();
state.mv.visitJumpInsn(IFNE, cont);
@@ -1535,7 +1585,7 @@ public class Codegen {
state.mv.visitJumpInsn(GOTO, start);
state.mv.visitLabel(cont);
state.mv.visitTypeInsn(CHECKCAST, pat.type().getInternalName());
state.mv.visitTypeInsn(CHECKCAST, name);
}
if (pat instanceof TargetExpressionPattern ep) {
@@ -1625,7 +1675,7 @@ public class Codegen {
}
private void generateStaticConstructor(TargetMethod constructor) {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", "()V", null, null);
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
var state = new State(null, mv, 0, true);
@@ -1744,7 +1794,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();
}
@@ -1801,6 +1851,35 @@ public class Codegen {
clazz.constructors().forEach(this::generateConstructor);
clazz.methods().forEach(this::generateMethod);
// Check if non abstract class implements all interface methods
if ((access & ACC_ABSTRACT) == 0) {
for (var intf : clazz.implementingInterfaces()) {
var intfClass = compiler.getClass(new JavaClassName(intf.name()));
for (var method : intfClass.getMethods()) {
if ((method.modifier & ACC_ABSTRACT) != 0) {
// Check if the method is implemented in the current class
// TODO This is duplicate code, we should probably refactor instances of this pattern
var foundMethods = clazz.methods().stream().filter(m -> {
if (!m.name().equals(method.name)) return false;
if (m.signature().parameters().size() != method.getParameterList().getFormalparalist().size())
return false;
for (var i = 0; i < m.signature().parameters().size(); i++) {
var param1 = m.signature().parameters().get(i).pattern().type();
var param2 = converter.convert(
method.getParameterList().getParameterAt(i).getType(),
ASTToTargetAST.Generics.nullGenerics(compiler).javaGenerics());
if (!param1.equals(param2)) return false;
}
return true;
}).toList();
if (foundMethods.size() != 1) {
throw new CodeGenException("Class " + clazz.qualifiedName() + " does not implement method " + method.name + " from interface " + intfClass.getClassName());
}
}
}
}
}
if (clazz.staticConstructor() != null)
generateStaticConstructor(clazz.staticConstructor());
@@ -1810,54 +1889,82 @@ 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);
var superClasses = new ArrayList<>();
superClasses.add(pair.to.getInternalName());
if (isFunctionalInterface(pair.from))
superClasses.add(pair.from.getInternalName());
ClassWriter cw2 = new CustomClassWriter();
cw2.visit(V1_8, ACC_PUBLIC, className, null, "java/lang/Object", superClasses.toArray(String[]::new));
cw2.visitField(ACC_PRIVATE, "wrapped", fieldDescriptor, null, null).visitEnd();
// Generate constructor
var ctor = cw2.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
ctor.visitVarInsn(ALOAD, 0);
ctor.visitVarInsn(ALOAD, 1);
ctor.visitFieldInsn(PUTFIELD, className, "wrapped", fieldDescriptor);
ctor.visitInsn(RETURN);
ctor.visitMaxs(0, 0);
ctor.visitEnd();
// 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));
@@ -5,6 +5,7 @@ import de.dhbwstuttgart.bytecode.Codegen;
import de.dhbwstuttgart.bytecode.FunNGenerator;
import de.dhbwstuttgart.environment.CompilationEnvironment;
import de.dhbwstuttgart.environment.DirectoryClassLoader;
import de.dhbwstuttgart.exceptions.CompilerException;
import de.dhbwstuttgart.exceptions.CompilerWarning;
import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.WarningsException;
@@ -171,6 +172,8 @@ public class JavaTXCompiler {
var numberOfGenerics = decl == null ? 0 : decl.genericTypeVar().size();
var className = pkgName + (!pkgName.isEmpty() ? "." : "") + name;
if (classRegistry.contains(className))
throw new CompilerException("Duplicate class definition: " + className);
classRegistry.addName(className, numberOfGenerics);
input.add(classRegistry.getName(className));
}
@@ -714,6 +717,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()
@@ -0,0 +1,7 @@
package de.dhbwstuttgart.exceptions;
public class CompilerException extends RuntimeException {
public CompilerException(String message) {
super(message);
}
}
@@ -2,9 +2,8 @@ package de.dhbwstuttgart.exceptions;
import org.antlr.v4.runtime.Token;
public class ParserError extends TypeinferenceException{
public ParserError(Token offset){
super("Fehler beim Parsen", offset);
public class ParserError extends RuntimeException{
public ParserError(String message) {
super(message);
}
}
@@ -1,6 +1,7 @@
package de.dhbwstuttgart.parser;
import de.dhbwstuttgart.environment.CompilationEnvironment;
import de.dhbwstuttgart.exceptions.ParserError;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.SyntaxTreeGenerator;
import de.dhbwstuttgart.parser.antlr.Java17Lexer;
import de.dhbwstuttgart.parser.antlr.Java17Parser;
@@ -8,9 +9,7 @@ import de.dhbwstuttgart.parser.scope.JavaClassRegistry;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.util.Logger;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
@@ -21,13 +20,25 @@ public class JavaTXParser {
public static Logger logger = new Logger("Parser");
public static class ThrowingErrorListener extends BaseErrorListener {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
int line, int charPositionInLine, String msg, RecognitionException e) {
throw new ParserError("Syntax error at line " + line + ":" + charPositionInLine + " - " + msg);
}
}
public static Java17Parser.SourceFileContext parse(File source) throws IOException, java.lang.ClassNotFoundException {
InputStream stream = new FileInputStream(source);
// DEPRECATED: ANTLRInputStream input = new ANTLRInputStream(stream);
var errorListener = new ThrowingErrorListener();
CharStream input = CharStreams.fromStream(stream);
Java17Lexer lexer = new Java17Lexer(input);
lexer.addErrorListener(errorListener);
CommonTokenStream tokens = new CommonTokenStream(lexer);
Java17Parser parser = new Java17Parser(tokens);
parser.addErrorListener(errorListener);
return parser.sourceFile();
/*
* SyntaxTreeGenerator generator = new SyntaxTreeGenerator(environment.getRegistry(source)); return generator.convert(tree);
@@ -1033,10 +1033,22 @@ public class StatementGenerator {
private Expression convert(Java17Parser.LiteralContext literal) {
switch (literal) {
case IntLiteralContext intliteral:
var text = intliteral.getText();
var radix = 10;
if (text.startsWith("0x") || text.startsWith("0X")) {
radix = 16;
text = text.substring(2);
} else if (text.startsWith("0b") || text.startsWith("0B")) {
radix = 2;
text = text.substring(2);
} else if (text.startsWith("0") && text.length() > 1) {
radix = 8;
text = text.substring(1);
}
Number value;
if (intliteral.getText().endsWith("l") || intliteral.getText().endsWith("L"))
value = Long.parseLong(intliteral.getText().substring(0, intliteral.getText().length() - 1));
else value = Integer.parseInt(intliteral.getText());
if (text.endsWith("l") || text.endsWith("L"))
value = Long.parseLong(text.substring(0, text.length() - 1), radix);
else value = Integer.parseInt(text, radix);
return new Literal(TypePlaceholder.fresh(literal.getStart()), value, intliteral.getStart());
case FltLiteralContext floatliteral:
if (floatliteral.getText().endsWith("f") || floatliteral.getText().endsWith("F"))
@@ -80,7 +80,7 @@ public class SyntaxTreeGenerator {
HashMap<String, Integer> allmodifiers = new HashMap<>();
// PL 2018-11-01 fields eingefuegt, damit die fields immer die gleiche TPH
// bekommen
private final Map<String, FieldEntry> fields = new HashMap<>();
//private final Map<String, FieldEntry> fields = new HashMap<>();
// PL 2019-10-23: Muss für jede Klasse neu initilisiert werden
List<Statement> fieldInitializations = new ArrayList<>();
List<Statement> staticFieldInitializations = new ArrayList<>();
@@ -178,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) {
@@ -45,6 +45,10 @@ public class MethodCall extends Statement
return signature.subList(0, signature.size() - 1);
}
public TypePlaceholder returnType() {
return signature.getLast();
}
@Override
public void accept(StatementVisitor visitor) {
@@ -1,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,8 +23,12 @@ import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*;
import de.dhbwstuttgart.typeinference.unify.MartelliMontanariUnify;
import de.dhbwstuttgart.typeinference.unify.model.*;
import de.dhbwstuttgart.util.Logger;
import org.antlr.v4.runtime.Token;
import org.objectweb.asm.Opcodes;
import java.lang.classfile.Opcode;
import java.lang.reflect.AccessFlag;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -80,8 +86,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()));
}
}
@@ -113,16 +119,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 -> {
return 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());
}
@@ -131,27 +159,55 @@ 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;
for (var i = 0; i < pars.size(); i++) {
var type1 = pars.get(i);
var type2 = arguments.get(i);
if (type1 instanceof TargetGenericType)
return true;
if (type1 instanceof TargetGenericType || type2 instanceof TargetGenericType)
continue;
if (TargetType.toPrimitive(type2).equals(type1))
return true;
if (!type1.equals(type2))
return false;
continue;
if (type1 instanceof TargetFunNType tfun1 && type2 instanceof TargetFunNType tfun2) {
if (!funNIsSubtype(tfun2, tfun1, compiler)) return false;
} else if (!type1.equals(type2)) return false;
}
return true;
}
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) {
@@ -188,7 +244,7 @@ public class ASTToTargetAST {
case ExtendsType extendType -> new TargetExtendsWildcard(toTargetType(extendType.getExtendedType()));
case SuperType superType -> new TargetSuperWildcard(toTargetType(superType.getSuperedType()));
case PlaceholderType placeholderType -> new TargetGenericType(placeholderType.getName());
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 FunNType funNType -> TargetFunNType.fromParams(StreamSupport.stream(funNType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList(), 1); // FIXME How does this work with Fun0??
case ReferenceType referenceType -> new TargetRefType(referenceType.getName(), StreamSupport.stream(referenceType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList());
default -> throw new NotImplementedException();
};
@@ -240,7 +296,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);
}
@@ -362,7 +417,7 @@ public class ASTToTargetAST {
return R;
}
private static final record CtorWithGenerics(TargetConstructor ctor, IGenerics generics) {}
private record CtorWithGenerics(TargetConstructor ctor, IGenerics generics) {}
public TargetStructure convert(ClassOrInterface input) {
var generics = all.getFirst();
@@ -483,7 +538,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));
}
@@ -583,7 +638,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();
@@ -593,6 +648,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);
@@ -682,7 +748,7 @@ public class ASTToTargetAST {
private Optional<TargetMethod> generateBridgeMethod(ClassOrInterface clazz, List<TargetMethod> methods) {
// If there's only one method we don't need a bridge
if (clazz.isInterface()) return Optional.empty();
//if (clazz.isInterface()) return Optional.empty();
if (methods.size() <= 1) return Optional.empty();
var firstMethod = methods.getFirst();
@@ -720,15 +786,14 @@ public class ASTToTargetAST {
var parameters = resParams.stream().map( p -> new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList();
var classType = new TargetRefType(clazz.getClassName().getClassName());
var classType = new TargetRefType(clazz.getClassName().toString());
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 generics = new HashSet<>(firstMethod.signature().generics());
var resSig = new TargetMethod.Signature(generics, resParams, resRet);
correctGenerics(resSig, generics.stream().toList());
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, resSig, resSig);
var bridgeMethod = new TargetMethod(firstMethod.access() & ~Opcodes.ACC_ABSTRACT, firstMethod.name(), block, resSig, resSig);
return Optional.of(bridgeMethod);
}
@@ -745,8 +810,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;
}
@@ -804,7 +869,7 @@ public class ASTToTargetAST {
// .filter(p -> p.pattern() instanceof TargetComplexPattern).findFirst().orElseThrow().pattern());
// Generate dispatch method
var classType = new TargetRefType(clazz.getClassName().getClassName());
var classType = new TargetRefType(clazz.getClassName().toString());
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(signatureParams.getFirst().pattern().type(), signatureParams.getFirst().pattern().name()), parameters, List.of(), res, classType);
var block = new TargetBlock(List.of(stmt));
@@ -892,6 +957,7 @@ public class ASTToTargetAST {
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;
}
}
@@ -946,7 +1012,7 @@ 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;
}
@@ -1005,7 +1071,7 @@ public class ASTToTargetAST {
}
}
static TargetType flattenFunNType(List<TargetType> params, FunNGenerator.GenericParameters gep, boolean isInterface) {
static TargetType flattenFunNType(List<TargetType> params, FunNGenerator.GenericParameters gep) {
var newParams = new ArrayList<TargetType>();
for (TargetType param : params) {
if (param instanceof TargetSpecializedType fn) {
@@ -1019,19 +1085,38 @@ public class ASTToTargetAST {
if (i < gep.inParams.size() && gep.inParams.get(i) != null)
filteredParams.add(newParams.get(i));
}
return TargetFunNType.fromParams(params, filteredParams, gep.getReturnType() != null ? 1 : 0, isInterface);
return TargetFunNType.fromParams(params, filteredParams, gep.getReturnType() != null ? 1 : 0);
}
private FunNGenerator.GenericParameters convertToParameters(TargetFunNType input) {
return null;
}
public 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()));
@@ -1041,7 +1126,7 @@ public class ASTToTargetAST {
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 (isSubtype(new TargetRefType(superInterface.getName().toString()), other, compiler)) return true;
}
}
if (testClass.equals(otherClass)) return true;
@@ -1051,17 +1136,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;
}
@@ -1071,7 +1156,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();
@@ -1129,7 +1214,7 @@ public class ASTToTargetAST {
} else {
gep = compiler.usedFunN.get(className);
}
return flattenFunNType(params, gep, true);
return flattenFunNType(params, gep);
} else if (name.matches("Fun\\d+\\$\\$.*")) {
return new TargetFunNType(name, List.of(), List.of(), 0, false);
}
@@ -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)));
@@ -228,8 +228,7 @@ public class StatementToTargetExpression implements ASTVisitor {
var returnType = isFunNType ? TargetType.Object : converter.convert(methodCall.signature.getLast(), generics);
var receiverName = new JavaClassName(converter.convert(methodCall.receiver.getType(), generics).name());
var argList = methodCall.signature.stream().map(sig -> converter.convert(sig, generics)).toList();
argList = argList.subList(0, argList.size() - 1);
var argList = methodCall.signatureArguments().stream().map(sig -> converter.convert(sig, generics)).toList();
var receiverClass = converter.compiler.getClass(receiverName);
@@ -248,19 +247,24 @@ public class StatementToTargetExpression implements ASTVisitor {
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
var thisMethod = ASTToTargetAST.findMethod(receiverClass, methodCall.name, signature, generics, converter.compiler);
ClassOrInterface finalReceiverClass = receiverClass;
foundMethod = thisMethod.orElseGet(() -> findMethod(finalReceiverClass.getSuperClass().getName(), methodCall.name, signature, generics, converter.compiler).orElseThrow());
var finalSignature = signature;
foundMethod = thisMethod.orElseGet(() -> findMethod(finalReceiverClass.getSuperClass().getName(), methodCall.name, finalSignature, generics, converter.compiler).orElseThrow());
} else if (!isFunNType) {
receiverClass = converter.compiler.getClass(receiverName);
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
var finalSignature = signature;
foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow(
() -> new DebugException("Method " + methodCall.name + " not found (" + signature + ") on class " + receiverName)
() -> new DebugException("Method " + methodCall.name + " not found (" + finalSignature + ") on class " + receiverName)
);
//if (foundMethod != null) {
// signature = foundMethod.getParameterList().getFormalparalist().stream().map(pattern -> converter.convert(pattern.getType(), generics)).toList();
//}
}
if (!isFunNType) {
// NOTE Not using the direct conversion method on converter to bypass adding the TPH to the used TPH list
returnType = ASTToTargetAST.convert(foundMethod.getReturnType(), generics, converter.compiler);
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> ASTToTargetAST.convert(e.getType(), generics, converter.compiler)).toList();
signature = 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();
@@ -282,7 +286,7 @@ public class StatementToTargetExpression implements ASTVisitor {
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)) {
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);
@@ -300,14 +304,14 @@ public class StatementToTargetExpression implements ASTVisitor {
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)) {
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.getType(), generics), returnType, signature,
converter.convert(methodCall.receiver, generics),
methodCall.getArgumentList().getArguments().stream().map(arg -> converter.convert(arg, generics)).toList(),
receiverType, methodCall.name, isStatic, isInterface, isPrivate
@@ -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());
}
}
@@ -7,13 +7,13 @@ import java.util.Objects;
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, boolean isInterface) {
return fromParams(params, params, returnArguments, isInterface);
public static TargetFunNType fromParams(List<TargetType> params, int returnArguments) {
return fromParams(params, params, returnArguments);
}
public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams, int returnArguments, boolean isInterface) {
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, isInterface);
return new TargetFunNType(name, params, realParams, returnArguments, false);
}
public String toMethodDescriptor() {
@@ -43,6 +43,18 @@ public record TargetFunNType(String name, List<TargetType> funNParams, List<Targ
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);
}
}
@@ -875,8 +875,8 @@ public class TYPEStmt implements StatementVisitor {
if (caseExpressionType == null) {
for (var child : switchStmt.getBlocks()) {
for (var label : child.getLabels()) {
if (label.getPattern() == null) {
constraintsSet.addUndConstraint(new Pair(new RefType(new JavaClassName("java.lang.Object"), new NullToken()), switchStmt.getSwitch().getType(), PairOperator.SMALLERDOT, loc(label.getOffset())));
if (label.getPattern() == null && switchStmt.getSwitch().getType() instanceof TypePlaceholder) {
constraintsSet.addUndConstraint(new Pair(new RefType(new JavaClassName("java.lang.Object"), new NullToken()), switchStmt.getSwitch().getType(), PairOperator.EQUALSDOT, loc(label.getOffset())));
}
if (label.getPattern() instanceof RecordPattern) {
RecordPattern recordPattern = (RecordPattern) label.getPattern();
+123 -4
View File
@@ -1,10 +1,15 @@
import de.dhbwstuttgart.bytecode.CodeGenException;
import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.exceptions.CompilerException;
import de.dhbwstuttgart.exceptions.ParserError;
import de.dhbwstuttgart.exceptions.WarningsException;
import de.dhbwstuttgart.util.Logger;
import de.dhbwstuttgart.util.Logger.LogLevel;
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -24,8 +29,8 @@ import static targetast.TestCodegen.generateClassFiles;
public class TestComplete {
@BeforeAll
public static void setup() throws IOException {
@BeforeEach
public void setup() throws IOException {
TestCodegen.outputPath.toFile().mkdirs();
FileUtils.cleanDirectory(TestCodegen.outputPath.toFile());
}
@@ -674,6 +679,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");
@@ -1069,6 +1083,30 @@ public class TestComplete {
assertEquals(40.0, m.invoke(instance, d, 0));
}
@Disabled("Waiting for #407, skipping for now")
@Test
public void testPatternMatchingInterface() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), false, "Matcher.jav", "PatternMatchingInterface.jav");
var Intf = classFiles.get("Interface");
var clazz = classFiles.get("PatternMatchingInterface");
var M = classFiles.get("M");
var Matcher = classFiles.get("Matcher");
var a = classFiles.get("A").getDeclaredConstructor().newInstance();
var b = classFiles.get("B").getDeclaredConstructor().newInstance();
//var match = clazz.getDeclaredMethod("match", Intf);
//assertEquals(0, match.invoke(null, a));
//assertEquals(1, match.invoke(null, b));
var matcher = M.getDeclaredConstructor().newInstance();
var match2 = clazz.getDeclaredMethod("match", Matcher, Intf);
assertEquals(0, match2.invoke(null, matcher, a));
assertEquals(1, match2.invoke(null, matcher, b));
}
@Test
public void testInterfaces() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Interfaces.jav");
@@ -1084,6 +1122,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");
@@ -1182,6 +1230,10 @@ public class TestComplete {
assertEquals('C', clazz.getDeclaredMethod("m2").invoke(instance));
assertEquals(10L, clazz.getDeclaredMethod("m3").invoke(instance));
assertEquals(10.5F, clazz.getDeclaredMethod("m4").invoke(instance));
assertEquals(10.5D, clazz.getDeclaredMethod("m5").invoke(instance));
assertEquals(0xFF, clazz.getDeclaredMethod("m6").invoke(instance));
assertEquals(0b101010, clazz.getDeclaredMethod("m7").invoke(instance));
assertEquals(010, clazz.getDeclaredMethod("m8").invoke(instance));
}
@Test
@@ -1304,6 +1356,7 @@ public class TestComplete {
m.invoke(null, List.of("foo", "bar", "baz"));
}
@Disabled("See #405")
@Test
public void testLazy() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), false, "LazyList.jav", "Primzahlen.jav");
@@ -1475,7 +1528,6 @@ public class TestComplete {
}
@Test
@Disabled("too slow")
public void testBug325() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug325.jav");
var clazz = classFiles.get("Bug325");
@@ -1628,7 +1680,7 @@ public class TestComplete {
@Test
public void testBug389() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), true, "Bug389.jav", "Bug389Main.jav");
var classFiles = generateClassFiles(createClassLoader(), false, "Bug389.jav", "Bug389Main.jav");
var clazz = classFiles.get("Bug389Main");
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
}
@@ -1640,4 +1692,71 @@ public class TestComplete {
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
}
@Test
public void testBug395() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug395.jav");
fail("Shouldn't compile!");
} catch (WarningsException e) {
assertEquals(1, e.compilerWarnings.size());
var warning = e.compilerWarnings.get(0);
assertEquals("Duplicate Method definition dup found, signature (Ljava/lang/Integer;)Ljava/lang/Integer; clashes with signature (Ljava/lang/Integer;)Ljava/lang/Integer;", warning.getMessage());
}
}
@Test
public void testBug406() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug406.jav");
var clazz = classFiles.get("Bug406");
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
}
@Test
public void testBug423() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug423.jav");
fail("No exception thrown!");
} catch (CodeGenException e) {
assertEquals("Class Bug423 does not implement method f from interface Bug423I", e.getMessage());
}
}
@Test
public void testBug422() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug422.jav");
fail("Shouldn't compile!");
} catch (CompilerException e) {
assertEquals("Duplicate class definition: Bug422", e.getMessage());
}
}
@Test
public void testBug421() throws Exception {
try {
var classFiles = generateClassFiles(createClassLoader(), "Bug421.jav");
fail("Shouldn't compile!");
} catch (ParserError e) {
assertEquals("Syntax error at line 2:26 - no viable alternative at input 'm() { return 1 }'", e.getMessage());
}
}
@Test
public void testBug409() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug409.jav");
var clazz = classFiles.get("Bug409");
var instance = clazz.getDeclaredConstructor().newInstance();
var m = clazz.getDeclaredMethod("m");
assertEquals(5, m.invoke(instance));
}
@Test
public void testBug417() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug417.jav");
var clazz = classFiles.get("Bug417");
var instance = clazz.getDeclaredConstructor().newInstance();
var name = clazz.getDeclaredMethod("name", Integer.class);
assertEquals("one", name.invoke(instance, 1));
assertEquals("many", name.invoke(instance, 2));
}
}
+14 -3
View File
@@ -202,9 +202,20 @@ public class TestCodegen {
@Test
public void testFor() throws Exception {
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, new JavaClassName("For"));
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer, new TargetBlock(List.of(new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)), new TargetFor(List.of(new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0))), new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(10)), List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1)))), new TargetBlock(List.of(new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"), new TargetLocalVar(TargetType.Integer, "i")))))), new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum")))));
targetClass.addMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "forLoop", List.of(), TargetType.Integer,
new TargetBlock(List.of(new TargetVarDecl(TargetType.Integer, "sum", new TargetLiteral.IntLiteral(0)),
new TargetFor(List.of(new TargetVarDecl(TargetType.Integer, "i", new TargetLiteral.IntLiteral(0))),
new TargetBinaryOp.Less(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"),
new TargetLiteral.IntLiteral(10)), List.of(new TargetAssign(TargetType.Integer,
new TargetLocalVar(TargetType.Integer, "i"),
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "i"), new TargetLiteral.IntLiteral(1)))),
new TargetBlock(List.of(
new TargetAssign(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"),
new TargetBinaryOp.Add(TargetType.Integer, new TargetLocalVar(TargetType.Integer, "sum"),
new TargetLocalVar(TargetType.Integer, "i")))))),
new TargetReturn(new TargetLocalVar(TargetType.Integer, "sum")))));
var clazz = generateClass(targetClass, createClassLoader());
assertEquals(clazz.getDeclaredMethod("forLoop").invoke(null), 45);
assertEquals(45, clazz.getDeclaredMethod("forLoop").invoke(null));
}
@Test
@@ -276,7 +287,7 @@ public class TestCodegen {
public void testLambda() throws Exception {
var classLoader = createClassLoader();
// 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, false);
var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer), 1);
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))));