Compare commits

..

14 Commits

Author SHA1 Message Date
dholle 2e7b536d9d Add generics to bridge method
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m20s
2026-05-21 21:17:54 +02:00
dholle 654c8d6df8 Fix last pattern match not branching
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m25s
2026-05-21 16:25:22 +02:00
dholle 30dd045a7c Fix Zip example
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m31s
2026-05-21 15:10:42 +02:00
dholle 9b762470b5 Some fixes
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m16s
2026-05-21 14:32:32 +02:00
dholle 85ee805a3f Fix some problems
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m15s
2026-05-21 13:20:50 +02:00
dholle 523452f437 Write back classes
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m30s
2026-05-20 18:33:09 +02:00
dholle 7051b4335a Test ordering
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m12s
2026-05-20 13:36:10 +02:00
dholle 7daec0a291 Create bridge method
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m15s
2026-05-19 11:34:53 +02:00
dholle 653f704265 Deduplicate methods earlier
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m7s
2026-05-15 18:21:29 +02:00
dholle b36ba5e128 Group methods
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m9s
2026-05-15 17:30:11 +02:00
dholle a43f49cadb Make append example work again
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m48s
2026-05-15 16:47:20 +02:00
dholle 69292c37fc Keep working on it
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m48s
2026-05-15 15:32:37 +02:00
dholle 218e704272 Merge branch 'master' of ssh://gitea.hb.dhbw-stuttgart.de:2222/JavaTX/JavaCompilerCore 2026-05-15 14:01:55 +02:00
dholle e83e9a041c Work on pattern matching
SonarQube Scan / SonarQube Trigger (push) Failing after 1m45s
2026-05-14 20:48:13 +02:00
28 changed files with 568 additions and 794 deletions
@@ -1,19 +0,0 @@
import java.util.stream.Stream;
import Pair;
import java.util.function.Function;
public class Kombinatoren_After {
after (fst, snd) { //System.out.println("after2");
return () -> Stream.of(
toks -> {
return fst.apply().flatMap(x ->
x.apply(toks).flatMap(p1 ->
snd.apply().flatMap(y -> y.apply(p1.snd()).map(p2 ->
new Pair<>(new Pair<>(p1.fst(), p2.fst()), p2.snd()))))); } );
}
}
@@ -1,17 +0,0 @@
import java.util.stream.Stream;
import Pair;
import java.util.function.Function;
public class Kombinatoren_After_Lazy {
afterP (fst, snd) {
return toks -> {
return fst.apply(toks).flatMap(p1 ->
snd.apply(p1.snd()).map(p2 ->
new Pair<>(new Pair<>(p1.fst(), p2.fst()), p2.snd())));
};
}
}
@@ -1,30 +0,0 @@
import java.util.stream.Stream;
import Pair;
import java.util.function.Function;
public class Kombinatoren_Or {
orP (p1, p2) {
return ()-> Stream.concat(p1.apply(), p2.apply());
}
/*
after (fst, snd) { //System.out.println("after2");
return () -> Stream.of(
toks -> {
return fst.apply().flatMap(x ->
x.apply(toks).flatMap(p1 ->
snd.apply().flatMap(y -> y.apply(p1.snd()).map(p2 ->
new Pair<>(new Pair<>(p1.fst(), p2.fst()), p2.snd()))))); } );
}
trans (p, f) {
return () -> p.apply().map(x -> (toks -> x.apply(toks).map(pr -> new Pair<>(f.apply(pr.fst()), pr.snd() ) ) ) );
}
*/
}
@@ -1,70 +0,0 @@
import java.util.stream.Stream;
//import java.util.List;
import java.util.ArrayList;
import java.io.PrintStream;
import java.lang.System;
//import java.lang.String;
import java.lang.Boolean;
import java.util.function.Function;
import Pair;
import Kombinatoren_failure;
public class Kombinatoren_Satisfy {
kb = new Kombinatoren_failure();
satisfy(cond) {
// return () -> Stream.of (newToks -> new Kombinatoren_failure().failure().apply().map(x -> x.apply(newToks)));
//}
return () -> Stream.of (toks -> {
if(toks.isEmpty()) {
return new ArrayList<>().stream();
}
else
{
var fst = toks.getFirst();
if (cond.apply(fst))
{
//var newToks = List.copyOf(toks.subList(1, toks.size()));
var newToks = new ArrayList<>(toks);
return newToks.removeFirst();
kb.getContent(
kb.succeed(fst).apply().map(x -> x.apply(newToks))
);
// ;
}
}
}
);
}
/* () -> Stream.of (toks -> {
//System.out.println(toks);
if(toks.isEmpty())
{
return new ArrayList<>().stream();
}
else {
var fst = toks.getFirst();
if (cond.apply(fst))
{
var newToks = List.copyOf(toks.subList(1, toks.size()));
//System.out.println("satisfy ok " + fst.toString());
//return get(succeed(fst).apply().map(x -> x.apply(newToks)));
}
else {
//System.out.println("satisfy failure " + fst.toString());
//return get(failure().apply()).apply(toks);
}
return new ArrayList<>().stream();
} } ) ;
}
*/
}
@@ -1,13 +0,0 @@
import java.util.stream.Stream;
import Pair;
//import java.util.function.Function;
public class Kombinatoren_Trans {
trans (p, f) {
return () -> p.apply().map(x -> (toks -> x.apply(toks).map(pr -> new Pair<>(f.apply(pr.fst()), pr.snd() ) ) ) );
}
}
@@ -1,73 +0,0 @@
import java.util.stream.Stream;
import java.util.stream.IntStream;
import java.util.List;
import java.util.ArrayList;
import java.io.PrintStream;
import java.lang.System;
import java.util.Optional;
import java.lang.String;
import java.lang.Boolean;
import java.util.function.Function;
import Pair;
public class Kombinatoren_failure {
public getContent(s) {
return s.toList().getFirst();
}
public failure() {
return () -> Stream.of(
toks -> new ArrayList<>().stream());
}
public succeed( value) {
return () -> Stream.of(toks -> {
//System.out.println("succeed");
var al = new ArrayList<>();
al.add(new Pair<>(value, toks));
//al.forEach(x -> { System.out.println(x.toString());});
return al.stream();});
}
parser(p, inp) {
return p.map(y -> y.apply(
inp.chars().mapToObj(c -> (char) c)
.collect(Collectors.toList())))
.flatMap(x -> x)
.filter(x -> x.snd().isEmpty())
.findFirst()
.get()
.fst();
}
}
/*
satisfy(cond) {
return newToks -> failure().apply().map(x -> x.apply(newToks));
/*
return () -> Stream.of (toks -> {
if(toks.isEmpty()) {
return new ArrayList<>().stream();
}
else {
var fst = toks.getFirst();
if (cond.apply(fst)) {
var newToks = List.copyOf(toks.subList(1, toks.size()));
this.getContent(
succeed(fst)
.apply().map(x -> x.apply(newToks))
)
;
}
}
}
);
*/
}
}
*/
-27
View File
@@ -1,27 +0,0 @@
import java.util.*;
public class Pair<T, U> {
T a;
U b;
public Pair() { }
public Pair(T a, U b) {
System.out.println("Pair a; " + a + " b: " + b);
this.a = a;
this.b = b;
}
public T fst () {
return a;
}
public U snd () {
System.out.println("snd b: " + b);
return b;
}
public String toString() {
return "Pair<"+a.toString()+","+b.toString()+">";
}
}
-82
View File
@@ -1,82 +0,0 @@
import java.util.stream.Stream;
//import java.util.stream.IntStream;
//import java.util.List;
import java.util.ArrayList;
import java.io.PrintStream;
import java.lang.System;
import java.util.Optional;
import java.lang.Character;
import java.lang.String;
import java.lang.Boolean;
import java.lang.Integer;
import java.util.function.Function;
import java.util.function.Predicate;
//import java.util.function.IntFunction;
import Pair;
public class Parser {
strToList(s) {
var al;
al = new ArrayList<>();
var i = 0;
while (i < s.length()) {
al.add(s.charAt(i));
i=i+1;
}
return al;
}
/*
parser(p, inp) {
return p.map(y -> y.apply(
strToList(inp)//chars().mapToObj(c -> (Character) c)
//.toList()
))
//.flatMap(x -> x)
//.filter(x -> x.snd().isEmpty())
;
/*
.findFirst()
.get()
.fst()
;
}
*/
}
/*
satisfy(cond) {
return newToks -> failure().apply().map(x -> x.apply(newToks));
/*
return () -> Stream.of (toks -> {
if(toks.isEmpty()) {
return new ArrayList<>().stream();
}
else {
var fst = toks.getFirst();
if (cond.apply(fst)) {
var newToks = List.copyOf(toks.subList(1, toks.size()));
this.getContent(
succeed(fst)
.apply().map(x -> x.apply(newToks))
)
;
}
}
}
);
*/
}
}
*/
@@ -1,13 +1,14 @@
import Cons; public sealed interface List<T> permits Cons, Empty {}
import Empty;
import List; public record Cons<T>(T a, List<T> l) implements List<T> {}
import Pair; public record Empty<T>() implements List<T> {}
public record Pair<T1, T2>(T1 a, T2 b) {}
public class PatternMatching { public class PatternMatching {
public zip(Cons(x, xs), Cons(y, ys)) { public zip(Cons(x, xs), Cons(y, ys)) {
return new Cons<>(new Pair<>(x, y), zip(xs, ys)); return new Cons<>(new Pair<>(x, y), zip(xs, ys));
} }
public zip(Empty x, Empty y) { return new Empty<>(); } public zip(Empty(), Empty()) { return new Empty<>(); }
/*public zip(Empty x, Cons y) { return new Empty(); } /*public zip(Empty x, Cons y) { return new Empty(); }
public zip(Cons x, Empty y) { return new Empty(); } public zip(Cons x, Empty y) { return new Empty(); }
@@ -1,7 +1,7 @@
import java.lang.Boolean; import java.lang.Boolean;
import java.lang.Object; import java.lang.Object;
sealed interface List<T> permits Cons, Empty {} public sealed interface List<T> permits Cons, Empty {}
public record Cons<T>(T a, List<T> l) implements List<T> {} public record Cons<T>(T a, List<T> l) implements List<T> {}
public record Empty<T>() implements List<T> {} public record Empty<T>() implements List<T> {}
@@ -0,0 +1,9 @@
import java.lang.Integer;
import java.lang.Number;
public record R(Number n) {}
public class PatternMatchingSpecificity {
public m(R(Number n)) { return 0; }
public m(R(Integer i)) { return 1; }
}
@@ -1499,12 +1499,13 @@ public class Codegen {
state.exitScope(); state.exitScope();
} }
private void extractField(State state, TargetType type, int i, ClassOrInterface clazz) { private void extractField(State state, TargetType ret, TargetType type, int i, ClassOrInterface clazz) {
if (i >= clazz.getFieldDecl().size()) if (i >= clazz.getFieldDecl().size())
throw new CodeGenException("Couldn't find suitable field accessor for '" + type.name() + "'"); throw new CodeGenException("Couldn't find suitable field accessor for '" + type.name() + "'");
var field = clazz.getFieldDecl().get(i); var field = clazz.getFieldDecl().get(i);
var fieldType = converter.convert(field.getType()); var fieldType = converter.convert(field.getType());
state.mv.visitMethodInsn(INVOKEVIRTUAL, type.getInternalName(), field.getName(), "()" + fieldType.toDescriptor(), false); state.mv.visitMethodInsn(INVOKEVIRTUAL, type.getInternalName(), field.getName(), "()" + fieldType.toDescriptor(), false);
if (ret != null) convertTo(state, fieldType, ret);
} }
private void bindPattern(State state, TargetType type, TargetPattern pat, Label start, int index, int depth) { private void bindPattern(State state, TargetType type, TargetPattern pat, Label start, int index, int depth) {
@@ -1567,7 +1568,7 @@ public class Codegen {
var subPattern = cp.subPatterns().get(i); var subPattern = cp.subPatterns().get(i);
state.mv.visitInsn(DUP); state.mv.visitInsn(DUP);
extractField(state, cp.type(), i, clazz); extractField(state, null, cp.type(), i, clazz);
if (subPattern.type() instanceof TargetRefType || subPattern.type() instanceof TargetExtendsWildcard) { if (subPattern.type() instanceof TargetRefType || subPattern.type() instanceof TargetExtendsWildcard) {
state.mv.visitInsn(DUP); state.mv.visitInsn(DUP);
@@ -1669,7 +1670,7 @@ public class Codegen {
if (i < cp.subPatterns().size() - 1) if (i < cp.subPatterns().size() - 1)
state.mv.visitInsn(DUP); state.mv.visitInsn(DUP);
extractField(state, cp.type(), i, clazz); extractField(state, subPattern.type(), cp.type(), i, clazz);
if (subPattern.type() instanceof TargetRefType) if (subPattern.type() instanceof TargetRefType)
state.mv.visitTypeInsn(CHECKCAST, subPattern.type().getInternalName()); state.mv.visitTypeInsn(CHECKCAST, subPattern.type().getInternalName());
offset = state.createVariable(subPattern.name(), subPattern.type()).index; offset = state.createVariable(subPattern.name(), subPattern.type()).index;
@@ -20,14 +20,11 @@ import de.dhbwstuttgart.server.SocketFuture;
import de.dhbwstuttgart.server.packet.SetAutoclosePacket; import de.dhbwstuttgart.server.packet.SetAutoclosePacket;
import de.dhbwstuttgart.server.packet.UnifyRequestPacket; import de.dhbwstuttgart.server.packet.UnifyRequestPacket;
import de.dhbwstuttgart.server.packet.UnifyResultPacket; import de.dhbwstuttgart.server.packet.UnifyResultPacket;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface; import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.ParameterList;
import de.dhbwstuttgart.syntaxtree.SourceFile;
import de.dhbwstuttgart.syntaxtree.GenericDeclarationList;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory; import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
import de.dhbwstuttgart.syntaxtree.statement.Block;
import de.dhbwstuttgart.syntaxtree.statement.Statement;
import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType; import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType;
import de.dhbwstuttgart.syntaxtree.type.GenericRefType; import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
import de.dhbwstuttgart.syntaxtree.type.RefType; import de.dhbwstuttgart.syntaxtree.type.RefType;
@@ -35,9 +32,12 @@ import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType; import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.syntaxtree.type.TypeVisitor; import de.dhbwstuttgart.syntaxtree.type.TypeVisitor;
import de.dhbwstuttgart.syntaxtree.type.Void;
import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter; import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter;
import de.dhbwstuttgart.target.generate.ASTToTargetAST; import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.generate.GenericsResult; import de.dhbwstuttgart.target.generate.GenericsResult;
import de.dhbwstuttgart.target.tree.*;
import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.constraints.Constraint; import de.dhbwstuttgart.typeinference.constraints.Constraint;
import de.dhbwstuttgart.typeinference.constraints.ConstraintSet; import de.dhbwstuttgart.typeinference.constraints.ConstraintSet;
import de.dhbwstuttgart.typeinference.constraints.Pair; import de.dhbwstuttgart.typeinference.constraints.Pair;
@@ -62,6 +62,7 @@ import de.dhbwstuttgart.typeinference.unify.UnifyTaskModel;
import de.dhbwstuttgart.util.Logger; import de.dhbwstuttgart.util.Logger;
import java.io.*; import java.io.*;
import java.lang.Record;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.nio.file.Path; import java.nio.file.Path;
import java.sql.Array; import java.sql.Array;
@@ -82,7 +83,9 @@ public class JavaTXCompiler {
// public static JavaTXCompiler INSTANCE; // public static JavaTXCompiler INSTANCE;
final CompilationEnvironment environment; final CompilationEnvironment environment;
Boolean resultmodel = true; Boolean resultmodel = true;
public final Map<File, SourceFile> sourceFiles = new HashMap<>();
private final List<File> sources;
public final Map<File, SourceFile> sourceFiles = new LinkedHashMap<>();
public final Set<JavaClassName> input = new HashSet<>(); public final Set<JavaClassName> input = new HashSet<>();
public volatile UnifyTaskModel usedTasks = new UnifyTaskModel(); public volatile UnifyTaskModel usedTasks = new UnifyTaskModel();
@@ -124,7 +127,11 @@ public class JavaTXCompiler {
environment = new CompilationEnvironment(sources, classLoader); environment = new CompilationEnvironment(sources, classLoader);
classPath = path; classPath = path;
this.outputPath = outputPath; this.outputPath = outputPath;
this.sources = sources;
// INSTANCE = this;
}
private List<SourceFileContext> generateSources(List<File> sources) throws IOException, ClassNotFoundException {
// TODO This should maybe be moved elsewhere // TODO This should maybe be moved elsewhere
var treeList = new ArrayList<SourceFileContext>(sources.size()); var treeList = new ArrayList<SourceFileContext>(sources.size());
for (var s : sources) { for (var s : sources) {
@@ -154,11 +161,7 @@ public class JavaTXCompiler {
input.add(classRegistry.getName(className)); input.add(classRegistry.getName(className));
} }
} }
return treeList;
for (var i = 0; i < sources.size(); i++) {
parse(treeList.get(i), sources.get(i));
}
// INSTANCE = this;
} }
private void addSourceFile(File file, SourceFile sf) { private void addSourceFile(File file, SourceFile sf) {
@@ -686,7 +689,7 @@ public class JavaTXCompiler {
public final JavaClassRegistry classRegistry = new JavaClassRegistry(); public final JavaClassRegistry classRegistry = new JavaClassRegistry();
private void parse(SourceFileContext tree, File sourceFile) throws IOException, java.lang.ClassNotFoundException { private void parse(SourceFileContext tree, File sourceFile) throws IOException, ClassNotFoundException {
SyntaxTreeGenerator generator = new SyntaxTreeGenerator(this, classRegistry, new GenericsRegistry(null), sourceFile.getName()); SyntaxTreeGenerator generator = new SyntaxTreeGenerator(this, classRegistry, new GenericsRegistry(null), sourceFile.getName());
environment.addClassesToRegistry(classRegistry, tree, sourceFile, this); environment.addClassesToRegistry(classRegistry, tree, sourceFile, this);
var classes = new ArrayList<ClassOrInterface>(); var classes = new ArrayList<ClassOrInterface>();
@@ -697,8 +700,18 @@ public class JavaTXCompiler {
sf.imports.addAll(generator.imports); sf.imports.addAll(generator.imports);
} }
public void parseAll() throws IOException, ClassNotFoundException {
var treeList = generateSources(sources);
for (var i = 0; i < sources.size(); i++) {
parse(treeList.get(i), sources.get(i));
}
}
public void generateBytecode() throws ClassNotFoundException, IOException { public void generateBytecode() throws ClassNotFoundException, IOException {
// The compiler has two modes, one is to infer all files together and the other one is to do them in sequence
if (inferTogether) { if (inferTogether) {
parseAll();
var files = sourceFiles.keySet().stream().toList(); var files = sourceFiles.keySet().stream().toList();
var typeinferenceResult = this.typeInference(files); var typeinferenceResult = this.typeInference(files);
for (var file : files) { for (var file : files) {
@@ -709,7 +722,11 @@ public class JavaTXCompiler {
writeClassFile(classes, outputPath == null ? file.getParentFile() : outputPath, outputPath == null); writeClassFile(classes, outputPath == null ? file.getParentFile() : outputPath, outputPath == null);
} }
} else { } else {
for (var file : sourceFiles.keySet()) { var treeList = generateSources(sources);
for (var i = 0; i < sources.size(); i++) {
var file = sources.get(i);
parse(treeList.get(i), file);
var sf = sourceFiles.get(file); var sf = sourceFiles.get(file);
if (sf.isGenerated()) continue; if (sf.isGenerated()) continue;
var classes = generateBytecode(file); var classes = generateBytecode(file);
@@ -719,7 +736,7 @@ public class JavaTXCompiler {
} }
} }
public Map<JavaClassName, byte[]> generateBytecode(File sourceFile) throws ClassNotFoundException, IOException { private Map<JavaClassName, byte[]> generateBytecode(File sourceFile) throws ClassNotFoundException, IOException {
var sf = sourceFiles.get(sourceFile); var sf = sourceFiles.get(sourceFile);
if (sf.isGenerated()) return null; if (sf.isGenerated()) return null;
List<ResultSet> typeinferenceResult = this.typeInference(sourceFile); List<ResultSet> typeinferenceResult = this.typeInference(sourceFile);
@@ -753,11 +770,91 @@ public class JavaTXCompiler {
} }
} }
// TODO Maybe move this elsewhere it doesn't really belong here
private static RefTypeOrTPHOrWildcardOrGeneric toRefType(TargetType type) {
return switch(type) {
case TargetExtendsWildcard targetExtendsWildcard ->
new ExtendsWildcardType(toRefType(targetExtendsWildcard.innerType()), new NullToken());
case TargetSuperWildcard targetSuperWildcard ->
new SuperWildcardType(toRefType(targetSuperWildcard.innerType()), new NullToken());
case TargetGenericType targetGenericType -> new GenericRefType(targetGenericType.name(), new NullToken());
case TargetPrimitiveType targetPrimitiveType -> toRefType(TargetType.toWrapper(targetPrimitiveType));
case TargetSpecializedType targetSpecializedType ->
new RefType(new JavaClassName(targetSpecializedType.name()),
targetSpecializedType.params().stream().map(JavaTXCompiler::toRefType).toList(), new NullToken()
);
case null -> new Void(new NullToken());
};
}
private static ParameterList toParameterList(List<MethodParameter> params) {
return new ParameterList(params.stream().map(mp
-> (Pattern) new FormalParameter(mp.pattern().name(), toRefType(mp.pattern().type()), new NullToken())).toList(),
new NullToken()
);
}
private static GenericDeclarationList toGenerics(Set<TargetGeneric> generics) {
return new GenericDeclarationList(
generics.stream().map(g
-> new GenericTypeVar(g.name(), List.of(toRefType(g.bound())), new NullToken(), new NullToken())
).toList(), new NullToken()
);
}
/**
* This writes back the compiled target structure into the loadedClasses map so that it can be used
* as an input in further processing
* @param target
*/
private void writeBackToClass(TargetStructure target) {
var fielddecl = new ArrayList<Field>();
for (var field : target.fields()) {
fielddecl.add(new Field(field.name(), toRefType(field.type()), field.access(), new NullToken()));
}
var methods = new ArrayList<Method>();
for (var method : target.methods()) {
methods.add(new Method(method.access(), method.name(), toRefType(method.signature().returnType()),
toParameterList(method.signature().parameters()), new Block(new ArrayList<>(), new NullToken()), toGenerics(method.signature().generics()), new NullToken())
);
}
var constructors = new ArrayList<Constructor>();
for (var ctor : target.constructors()) {
constructors.add(new Constructor(ctor.access(), target.getName(), new RefType(target.qualifiedName(), new NullToken()),
toParameterList(ctor.parameters()), new Block(new ArrayList<>(), new NullToken()), toGenerics(ctor.generics()), new NullToken())
);
}
var generics = new GenericDeclarationList(toGenerics(target.generics()), new NullToken());
var superClass = (RefType) toRefType(target.superType());
var isInterface = target instanceof TargetInterface;
var isFunctionalInterface = false; // TODO We might actually want to generate those
var implementedInterfaces = target.implementingInterfaces().stream()
.map(t -> (RefType) toRefType(t)).toList();
var permittedSubtypes = List.<RefType>of();
if (target instanceof TargetInterface tinf) {
permittedSubtypes = tinf.permittedSubtypes().stream().map(t -> (RefType) toRefType(t)).toList();
}
var clazz = new ClassOrInterface(
target.modifiers(), target.qualifiedName(),
fielddecl, Optional.empty(), Optional.empty(),
methods, constructors, generics, superClass,
isInterface, isFunctionalInterface, implementedInterfaces,
permittedSubtypes, new NullToken(), ""
);
loadedClasses.put(target.qualifiedName(), clazz);
}
public synchronized Map<JavaClassName, byte[]> generateBytecode(SourceFile sf, List<ResultSet> typeInferenceResult) { public synchronized Map<JavaClassName, byte[]> generateBytecode(SourceFile sf, List<ResultSet> typeInferenceResult) {
var converter = new ASTToTargetAST(this, typeInferenceResult, sf, classLoader); var converter = new ASTToTargetAST(this, typeInferenceResult, sf, classLoader);
var generatedClasses = new HashMap<JavaClassName, byte[]>(); var generatedClasses = new HashMap<JavaClassName, byte[]>();
for (var clazz : sf.getClasses()) { for (var clazz : sf.getClasses()) {
var codegen = new Codegen(converter.convert(clazz), this, converter); var target = converter.convert(clazz);
writeBackToClass(target);
var codegen = new Codegen(target, this, converter);
var code = codegen.generate(); var code = codegen.generate();
generatedClasses.put(clazz.getClassName(), code); generatedClasses.put(clazz.getClassName(), code);
} }
@@ -58,14 +58,11 @@ public class LanguageServerInterface {
var file = path.toFile(); var file = path.toFile();
Files.createDirectories(path.getParent().resolve("out")); Files.createDirectories(path.getParent().resolve("out"));
var compiler = new JavaTXCompiler(List.of(file), List.of(path.getParent().toFile()), path.getParent().resolve("out").toFile(), false); var compiler = new JavaTXCompiler(List.of(file), List.of(path.getParent().toFile()), path.getParent().resolve("out").toFile(), false);
compiler.generateBytecode();
var parsedSource = compiler.sourceFiles.get(file); var parsedSource = compiler.sourceFiles.get(file);
var tiResults = compiler.typeInference(file); var tiResults = compiler.typeInference(file);
Map<JavaClassName, byte[]> bytecode = compiler.generateBytecode(parsedSource, tiResults);
Files.createDirectories(path.getParent().resolve("out"));
compiler.writeClassFile(bytecode, path.getParent().resolve("out").toFile(), false);
return new LanguageServerTransferObject(tiResults, parsedSource, "", compiler.getGeneratedGenerics()); return new LanguageServerTransferObject(tiResults, parsedSource, "", compiler.getGeneratedGenerics());
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e.getMessage(), e); throw new RuntimeException(e.getMessage(), e);
@@ -1,9 +1,11 @@
package de.dhbwstuttgart.target.generate; package de.dhbwstuttgart.target.generate;
import com.google.common.collect.Lists;
import de.dhbwstuttgart.bytecode.FunNGenerator; import de.dhbwstuttgart.bytecode.FunNGenerator;
import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.environment.IByteArrayClassLoader; import de.dhbwstuttgart.environment.IByteArrayClassLoader;
import de.dhbwstuttgart.exceptions.DebugException; import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.*; import de.dhbwstuttgart.syntaxtree.*;
@@ -11,17 +13,20 @@ import de.dhbwstuttgart.syntaxtree.Record;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory; import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
import de.dhbwstuttgart.syntaxtree.statement.*; import de.dhbwstuttgart.syntaxtree.statement.*;
import de.dhbwstuttgart.syntaxtree.type.*; import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter; import de.dhbwstuttgart.target.Target;
import de.dhbwstuttgart.syntaxtree.visual.OutputGenerator;
import de.dhbwstuttgart.target.tree.*; import de.dhbwstuttgart.target.tree.*;
import de.dhbwstuttgart.target.tree.expression.*; import de.dhbwstuttgart.target.tree.expression.*;
import de.dhbwstuttgart.target.tree.type.*; import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*; import de.dhbwstuttgart.typeinference.result.*;
import de.dhbwstuttgart.typeinference.unify.MartelliMontanariUnify;
import de.dhbwstuttgart.typeinference.unify.model.*;
import java.lang.annotation.Target; import javax.swing.text.html.Option;
import java.sql.Array;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/** /**
* @author dholle * @author dholle
@@ -61,7 +66,7 @@ public class ASTToTargetAST {
return converter.result; return converter.result;
} }
public record Generics(JavaGenerics javaGenerics, TxGenerics txGenerics) { public record Generics(IGenerics javaGenerics, IGenerics txGenerics) {
public Generics(JavaTXCompiler compiler, ResultSet set) { public Generics(JavaTXCompiler compiler, ResultSet set) {
this(new JavaGenerics(compiler, set), new TxGenerics(compiler, set)); this(new JavaGenerics(compiler, set), new TxGenerics(compiler, set));
} }
@@ -156,190 +161,190 @@ public class ASTToTargetAST {
return ret; return ret;
} }
// This finds a common sealed interface type to group together methods that use different records private static UnifyType toUnifyType(TargetType a) {
// This function should do matching or unification return switch(a) {
private List<ClassOrInterface> commonSuperInterfaceTypes(TargetType a, TargetType b) { case TargetExtendsWildcard targetExtendsWildcard -> new ExtendsType(toUnifyType(targetExtendsWildcard.innerType()));
if (a instanceof TargetGenericType && b instanceof TargetGenericType) return List.of(ASTFactory.createObjectClass()); case TargetSuperWildcard targetSuperWildcard -> new SuperType(toUnifyType(targetSuperWildcard.innerType()));
if (a instanceof TargetRefType ta && b instanceof TargetGenericType) case TargetGenericType targetGenericType -> new PlaceholderType(targetGenericType.name(), JavaTXCompiler.defaultClientPlaceholderRegistry);
return List.of(compiler.getClass(new JavaClassName(ta.name()))); case TargetPrimitiveType _ -> throw new NotImplementedException();
if (b instanceof TargetRefType tb && a instanceof TargetGenericType) case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.params().stream().map(ASTToTargetAST::toUnifyType).toList()));
return List.of(compiler.getClass(new JavaClassName(tb.name()))); case TargetRefType targetRefType -> new ReferenceType(targetRefType.name(), new TypeParams(targetRefType.params().stream().map(ASTToTargetAST::toUnifyType).toList()));
};
if (a instanceof TargetRefType ta && b instanceof TargetRefType tb) {
var res = new HashSet<ClassOrInterface>();
var cla = compiler.getClass(new JavaClassName(ta.name()));
var clb = compiler.getClass(new JavaClassName(tb.name()));
if (cla.equals(clb)) return List.of(cla);
while (!cla.equals(ASTFactory.createObjectClass())) {
var clb2 = clb;
while (!clb2.equals(ASTFactory.createObjectClass())) {
for (var intfa : cla.getSuperInterfaces()) {
for (var intfb : clb.getSuperInterfaces()) {
if (intfa.equals(intfb)) {
var clintf = compiler.getClass(intfa.getName());
if (clintf.isSealed()) {
res.add(clintf);
}
}
}
}
clb2 = compiler.getClass(clb2.getSuperClass().getName());
}
cla = compiler.getClass(cla.getSuperClass().getName());
}
return res.stream().toList();
}
return List.of();
} }
private boolean canCombine(Signature m1, Signature m2) { private static TargetType toTargetType(UnifyType a) {
var pl1 = m1.java.parameters(); return switch (a) {
var pl2 = m2.java.parameters(); case ExtendsType extendType -> new TargetExtendsWildcard(toTargetType(extendType.getExtendedType()));
if (pl1.size() != pl2.size()) return false; case SuperType superType -> new TargetSuperWildcard(toTargetType(superType.getSuperedType()));
if (pl1.isEmpty()) return false; case PlaceholderType placeholderType -> new TargetGenericType(placeholderType.getName());
for (var i = 0; i < pl1.size(); i++) { case FunNType funNType -> TargetFunNType.fromParams(StreamSupport.stream(funNType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList(), 1); // FIXME How does this work with Fun0??
var p1 = pl1.get(i).pattern(); case ReferenceType referenceType -> new TargetRefType(referenceType.getName(), StreamSupport.stream(referenceType.getTypeParams().spliterator(), false).map(ASTToTargetAST::toTargetType).toList());
var p2 = pl2.get(i).pattern(); default -> throw new NotImplementedException();
// TPH <> RefType sind nicht unterscheidbar };
if (p1.type() instanceof TargetGenericType || p2.type() instanceof TargetGenericType) continue; }
// Pattern(X) <> Pattern(Y) ist nicht unterscheidbar
if (p1 instanceof TargetComplexPattern pc1 && p2 instanceof TargetComplexPattern pc2 && private static boolean typesStrictlyEqual(List<TargetType> a, List<TargetType> b) {
pc1.type().equals(pc2.type())) continue; if (a.size() != b.size()) return false;
if (!p1.equals(p2)) return false; for (var i = 0; i < a.size(); i++) {
if (!typesStrictlyEqual(a.get(i), b.get(i))) return false;
} }
return true; return true;
} }
private record Combination(MethodWithTphs a, MethodWithTphs b) { // TODO This should be moved elsewhere
@Override private static boolean typesStrictlyEqual(TargetType a, TargetType b) {
public boolean equals(Object o) { if (a instanceof TargetRefType ta && b instanceof TargetRefType tb) {
if (!(o instanceof Combination(MethodWithTphs a1, MethodWithTphs b1))) return false; if (!Objects.equals(ta.name(), tb.name())) return false;
return this.a.equals(a1) && this.b.equals(b1) || if (ta.params().size() != tb.params().size()) return false;
this.a.equals(b1) && this.b.equals(a1); for (var i = 0; i < ta.params().size(); i++) {
if (!typesStrictlyEqual(ta.params().get(i), tb.params().get(i)))
return false;
}
return true;
}
// Generics are considered equal
if (a instanceof TargetGenericType && b instanceof TargetGenericType)
return true;
return Objects.equals(a, b);
} }
@Override private static Optional<TargetType> unify(TargetType a, TargetType b) {
public int hashCode() { if (typesStrictlyEqual(a, b)) return Optional.of(a);
return Objects.hashCode(a) + Objects.hashCode(b); var unify = new MartelliMontanariUnify();
var ua = toUnifyType(a);
var unifier = unify.unify(Set.of(ua, toUnifyType(b)));
if (unifier.isEmpty()) return Optional.empty();
return Optional.of(toTargetType(unifier.get().apply(ua)));
}
private static Optional<List<MethodParameter>> unify(List<MethodParameter> a, List<MethodParameter> b) {
if (a.size() != b.size()) return Optional.empty();
var result = new ArrayList<MethodParameter>();
for (var i = 0; i < a.size(); i++) {
var u = unify(a.get(i).pattern().type(), b.get(i).pattern().type());
if (u.isEmpty()) return Optional.empty();
// 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);
}
private static Set<String> findUsedGenerics(TargetType type) {
if (type instanceof TargetSpecializedType tspec) {
return tspec.params().stream().map(ASTToTargetAST::findUsedGenerics).flatMap(Set::stream).collect(Collectors.toSet());
} else if (type instanceof TargetGenericType(String name)) {
var set = new HashSet<String>();
set.add(name);
return set;
}
return new HashSet<>();
}
// When unifying types we need to keep the generics that are still used in the signature
// This is done in an extra step after all generics have been combined into one list
private static void correctGenerics(TargetMethod.Signature signature, List<TargetGeneric> generics) {
var outGenerics = signature.generics();
outGenerics.addAll(generics);
var foundGenerics = signature.parameters().stream().map(p -> findUsedGenerics(p.pattern().type()))
.flatMap(Set::stream)
.collect(Collectors.toSet());
foundGenerics.addAll(findUsedGenerics(signature.returnType()));
for (var generic : new HashSet<>(outGenerics)) {
if (!foundGenerics.contains(generic.name())) outGenerics.remove(generic);
} }
} }
private List<List<MethodWithTphs>> groupOverloads(ClassOrInterface input, List<Method> methods) { private static Optional<TargetMethod.Signature> unify(TargetMethod.Signature a, TargetMethod.Signature b) {
var mapOfTargetMethods = new HashMap<Generics, MethodWithTphs[]>(); var returnType = unify(a.returnType(), b.returnType());
for (var gen : all) { if (returnType.isEmpty()) return Optional.empty();
mapOfTargetMethods.put(gen, new MethodWithTphs[methods.size()]); var res = unify(a.parameters(), b.parameters());
if (res.isEmpty()) return Optional.empty();
var generics = new ArrayList<>(a.generics());
generics.addAll(b.generics());
var signature = new TargetMethod.Signature(new HashSet<>(), res.get(), returnType.get());
correctGenerics(signature, generics);
return Optional.of(signature);
} }
for (var i = 0; i < methods.size(); i++) { private static Optional<MethodGroup> unify(MethodGroup a, MethodGroup b) {
var method = methods.get(i); var junified = unify(a.signature.java, b.signature.java);
if (junified.isEmpty()) return Optional.empty();
var txunified = unify(a.signature.tx, b.signature.tx);
var unifiedSignature = new Signature(junified.get(), txunified.orElseGet(junified::get), a.signature().generics);
var concat = new HashSet<>(a.methods);
concat.addAll(b.methods);
return Optional.of(new MethodGroup(unifiedSignature, concat));
}
private record NameAndParameters(String name, int parameters) {}
private List<List<Method>> groupMethods(ClassOrInterface input, List<Method> methods) {
var groups = new HashMap<NameAndParameters, List<Method>>();
for (var m : methods) {
var nameAndParameters = new NameAndParameters(m.name, m.getParameterList().getFormalparalist().size());
var l = groups.getOrDefault(nameAndParameters, new ArrayList<>());
l.add(m);
groups.put(nameAndParameters, l);
}
return groups.values().stream().toList();
}
private Set<MethodGroup> groupOverloads(ClassOrInterface input, List<Method> methods) {
var a = new HashSet<MethodGroup>();
for (Method method : methods) {
// Convert all methods // Convert all methods
var methodsWithTphs = convert(input, method); var methodsWithTphs = convert(input, method);
for (var m : methodsWithTphs) { for (var m : methodsWithTphs) {
var resultMethods = mapOfTargetMethods.get(m.generics); var mtph = new MethodWithTphs(m.method, m.generics, m.signature);
resultMethods[i] = new MethodWithTphs(m.method, m.generics, m.signature); a.add(new MethodGroup(m.signature, Set.of(mtph)));
} }
} }
System.out.println("============== INPUT =============="); System.out.println("============== INPUT ==============");
for (var m : mapOfTargetMethods.values()) { for (var m : a) {
for (var v : m) if (v != null) System.out.println(v.signature.java.returnType() + " " + v.method.name + " " + v.signature.java().parameters()); System.out.println(m);
System.out.println();
} }
var allCombinations = new HashSet<Set<Combination>>(); // Algorithm
// Combine methods based on their signature and position in the result set
for (var g1 : all) {
var resMeth1 = mapOfTargetMethods.get(g1);
for (var i = 0; i < methods.size(); i++) {
var m1 = resMeth1[i];
if (m1 == null) continue;
for (var g2 : all) { var R = new HashSet<MethodGroup>();
if (g1 == g2) continue; // No need to combine the same method var i = new HashSet<>(a);
var resMeth2 = mapOfTargetMethods.get(g2);
var m2 = resMeth2[i];
if (m2 == null) continue;
var combinations = new HashSet<Combination>(); while (!i.isEmpty()) {
var m = i.iterator().next();
if (canCombine(m1.signature, m2.signature)) { i.remove(m);
//System.out.println(" Combining " + m1.signature.java.getSignature() + " and " + m2.signature.java.getSignature()); R.add(m);
combinations.add(new Combination(m1, m2)); var a1 = new HashSet<>(a);
for (var j = 0; j < methods.size(); j++) { for (var m1 : a1) {
if (j == i) continue; if (m1 != m) {
var m3 = resMeth2[j]; var u_opt = unify(m, m1);
if (m3 == null) continue; if (u_opt.isPresent()) {
var m4 = resMeth1[j]; var u = u_opt.get();
if (m4 == null) continue; //System.out.println("Unified " + m + " AND " + m1 + "\n\t" + u);
combinations.add(new Combination(m4, m3)); i.remove(m1);
//System.out.println("Also Combining " + m4.signature.java.getSignature() + " and " + m3.signature.java.getSignature()); R.remove(m);
} R.remove(m1);
} else { R.add(u);
//System.out.println(" Not Combining " + m1.signature.java.getSignature() + " and " + m2.signature.java.getSignature()); a.add(u);
} } /*else {
if (!combinations.isEmpty()) allCombinations.add(combinations); System.out.println("Couldn't unify " + m + " AND " + m1);
}*/
} }
} }
} }
if (allCombinations.isEmpty()) allCombinations.add(new HashSet<>()); System.out.println("============== OUTPUT ==============");
for (var mg : R) {
// Combine back into output format System.out.println(mg.methods.size() + " " + mg);
var r0 = new HashSet<Set<MethodWithTphs>>();
for (var combinations : allCombinations) {
var r1 = new HashSet<Set<MethodWithTphs>>();
// This is used to weed out duplicates
var uniqued = new HashSet<MethodWithTphs>();
// We go over all methods in the result
for (var g : all) for (var i = 0; i < methods.size(); i++) {
var r2 = new HashSet<MethodWithTphs>();
var m = mapOfTargetMethods.get(g)[i];
if (m == null) continue;
if (!uniqued.contains(m)) {
// Add the method to r2
r2.add(m);
uniqued.add(m);
} else continue;
// Find all combinations that contain the method and add them to the result
// if not filtered out by uniqued
for (var c : combinations) {
if (c.a.equals(m) || c.b.equals(m)) {
if (!uniqued.contains(c.a)) {
r2.add(c.a);
uniqued.add(c.a);
}
if (!uniqued.contains(c.b)) {
r2.add(c.b);
uniqued.add(c.b);
}
}
}
r1.add(r2);
}
outer: for (var s1 : r1) {
for (var s2 : new HashSet<>(r0)) {
if (s2.containsAll(s1)) {
continue outer;
} else if (s1.containsAll(s2)) {
r0.remove(s2);
r0.add(s1);
continue outer;
}
}
r0.add(s1);
}
} }
var result = r0.stream().map(l -> l.stream().toList()).toList(); return R;
//System.out.println("============== OUTPUT ==============");
//for (var l : result) {
// for (var m : l) System.out.println(m.method.name + " " + m.signature.java.getSignature());
// System.out.println();
//}
return result;
} }
public TargetStructure convert(ClassOrInterface input) { public TargetStructure convert(ClassOrInterface input) {
@@ -373,19 +378,36 @@ public class ASTToTargetAST {
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).toList(); var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).toList();
var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer, generics)).flatMap(List::stream).toList(); var 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 fields = input.getFieldDecl().stream().map(f -> convert(f, generics.javaGenerics)).toList();
var methods = groupOverloads(input, input.getMethods()).stream().map(m -> generatePatternOverloads(input, m)).flatMap(List::stream) var m0 = groupMethods(input, input.getMethods());
.collect(Collectors.toSet()).stream().toList(); // Unique generated methods
var m1 = new ArrayList<TargetMethod>();
for (var group : m0) {
var overloads = groupOverloads(input, group);
// This contains the merged pattern overloads
var genMethods = new ArrayList<TargetMethod>();
for (var o : overloads) {
var pOverloads = generatePatternOverloads(input, o);
m1.addAll(pOverloads);
if (!pOverloads.isEmpty()) {
genMethods.add(pOverloads.getLast());
}
}
var bridge = generateBridgeMethod(input, genMethods);
bridge.map(m1::add);
}
var methods = new HashSet<>(m1).stream().toList();
TargetMethod staticConstructor = null; TargetMethod staticConstructor = null;
if (input.getStaticInitializer().isPresent()) { if (input.getStaticInitializer().isPresent()) {
var init = this.convert(input, input.getStaticInitializer().get()).stream().findFirst().orElseThrow(); var init = this.convert(input, input.getStaticInitializer().get()).stream().findFirst().orElseThrow();
staticConstructor = this.convert(init, init.generics.javaGenerics); staticConstructor = this.convert(init);
} }
if (input instanceof Record) if (input instanceof Record)
return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods); return new TargetRecord(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods);
else if (input.isInterface()) else if (input.isInterface())
return new TargetInterface(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, methods, superInterfaces, staticConstructor); return new TargetInterface(input.getModifiers(), input.getClassName(), javaGenerics, txGenerics, methods, superInterfaces, staticConstructor,
input.getPermittedSubtypes().stream().map(t -> (TargetRefType) convert(t)).toList());
else return new TargetClass(input.getModifiers(), input.getClassName(), convert(input.getSuperClass(), generics.javaGenerics, compiler), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods); else return new TargetClass(input.getModifiers(), input.getClassName(), convert(input.getSuperClass(), generics.javaGenerics, compiler), javaGenerics, txGenerics, superInterfaces, constructors, staticConstructor, fields, methods);
} }
@@ -481,17 +503,7 @@ public class ASTToTargetAST {
} }
} }
private TargetType unwrap(TargetType type) { private static TargetExpression generateCall(TargetMethod method, List<TargetLocalVar> params, TargetType classType) {
if (type instanceof TargetRefType ref) {
if (!ref.params().isEmpty()) return new TargetRefType(ref.name());
}
return type;
}
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();
var mParams = new ArrayList<TargetExpression>(); var mParams = new ArrayList<TargetExpression>();
for (var i = 0; i < params.size(); i++) { for (var i = 0; i < params.size(); i++) {
var tpe = method.signature().parameters().get(i).pattern().type(); var tpe = method.signature().parameters().get(i).pattern().type();
@@ -511,6 +523,13 @@ public class ASTToTargetAST {
return caseBody; return caseBody;
} }
private static TargetExpression generatePatternOverloadsRec(int offset, TargetExpression switchExpr, List<TargetLocalVar> params, List<TargetPattern> patterns, List<TargetMethod> methods, TargetType classType) {
if (methods.isEmpty()) throw new DebugException("Couldn't find a candidate for switch overloading");
if (methods.size() == 1) {
var method = methods.getFirst();
return generateCall(method, params, classType);
}
var cases = new ArrayList<TargetSwitch.Case>(); var cases = new ArrayList<TargetSwitch.Case>();
var usedPatterns = new HashSet<TargetPattern>(); var usedPatterns = new HashSet<TargetPattern>();
@@ -542,10 +561,13 @@ public class ASTToTargetAST {
if (!patternsRec.get(j).equals(param.pattern())) return false; if (!patternsRec.get(j).equals(param.pattern())) return false;
j++; j++;
} }
return true; return false;
}).toList(); }).toList();
//System.out.println(offset + " -> " + lastPattern); if (candidates.isEmpty()) {
var body = new TargetBlock(List.of(generateCall(method, params, classType)));
cases.add(new TargetSwitch.Case(List.of(lastPattern), body));
} else {
//candidates.forEach(m -> System.out.println(m.getSignature())); //candidates.forEach(m -> System.out.println(m.getSignature()));
var caseBody = generatePatternOverloadsRec(offset + 1, expr, params, patternsRec, candidates, classType); var caseBody = generatePatternOverloadsRec(offset + 1, expr, params, patternsRec, candidates, classType);
var body = new TargetBlock(List.of(caseBody)); var body = new TargetBlock(List.of(caseBody));
@@ -553,42 +575,141 @@ public class ASTToTargetAST {
cases.add(case_); cases.add(case_);
} }
}
return new TargetSwitch(switchExpr, cases, null, true); return new TargetSwitch(switchExpr, cases, null, true);
} }
private List<TargetMethod> generatePatternOverloads(ClassOrInterface clazz, List<MethodWithTphs> overloadedMethods) { private static List<TargetType> transferParameters(ClassOrInterface type, ClassOrInterface intf, List<TargetType> params) {
if (params.isEmpty()) return List.of();
var map = new HashMap<String, TargetType>();
var generics = Lists.newArrayList(type.getGenerics());
for (var i = 0; i < params.size(); i++) {
var name = generics.get(i).getName();
var gtype = params.get(i);
map.put(name, gtype);
}
var intfGenerics = Lists.newArrayList(intf.getGenerics());
var insertedGenerics = new TargetType[intfGenerics.size()];
for (var i = 0; i < intfGenerics.size(); i++) {
insertedGenerics[i] = map.get(intfGenerics.get(i).getName());
}
return Lists.newArrayList(insertedGenerics);
}
// TODO What happens if a record implements two sealed interfaces?
private Optional<TargetType> commonSealedInterface(TargetType a, TargetType b) {
if (!(a instanceof TargetRefType ar && b instanceof TargetRefType br)) return Optional.empty();
var cla = compiler.getClass(new JavaClassName(ar.name()));
var clb = compiler.getClass(new JavaClassName(br.name()));
// TODO This only goes one layer deep, this is fine for records as they can't extend anything but fails for complex hierarchies
var intersection = new HashSet<>(cla.getSuperInterfaces());
intersection.retainAll(clb.getSuperInterfaces());
var intfclass = compiler.getClass(intersection.iterator().next().getName());
// We need to map the generics
var insertedParamsA = transferParameters(cla, intfclass, ar.params());
var insertedParamsB = transferParameters(clb, intfclass, br.params());
// Make sure both insertions yield the same parameters
if (!typesStrictlyEqual(insertedParamsA, insertedParamsB)) return Optional.empty();
if (intersection.size() != 1) return Optional.empty();
return Optional.of(new TargetRefType(intfclass.getClassName().toString(), insertedParamsA));
}
private Optional<TargetMethod> generateBridgeMethod(ClassOrInterface clazz, List<TargetMethod> methods) {
// If there's only one method we don't need a bridge
if (methods.size() <= 1) return Optional.empty();
var firstMethod = methods.getFirst();
var ra = firstMethod.signature().returnType();
// Check if all parameters are either the same or implement a sealed interface
var resParams = new ArrayList<>(firstMethod.signature().parameters());
TargetType resRet = firstMethod.signature().returnType();
for (var m : methods.subList(1, methods.size())) {
for (var i = 0; i < m.signature().parameters().size(); i++) {
var pa = firstMethod.signature().parameters().get(i);
var pb = m.signature().parameters().get(i);
var ta = pa.pattern().type();
var tb = pb.pattern().type();
//System.out.println(ta + " " + tb);
if (Objects.equals(ta, tb)) continue;
var common = commonSealedInterface(ta, tb);
if (common.isPresent()) {
resParams.set(i, pa.withType(common.get()));
continue;
}
return Optional.empty();
}
var rb = m.signature().returnType();
if (Objects.equals(ra, rb)) continue;
var common = commonSealedInterface(ra, rb);
if (common.isPresent()) resRet = common.get();
else return Optional.empty();
}
var parameters = resParams.stream().map( p -> new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList();
var classType = new TargetRefType(clazz.getClassName().getClassName());
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(resParams.getFirst().pattern().type(), resParams.getFirst().pattern().name()), parameters, List.of(), methods, classType);
var block = new TargetBlock(List.of(stmt));
var resSig = new TargetMethod.Signature(firstMethod.signature().generics(), resParams, resRet);
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, resSig, resSig);
return Optional.of(bridgeMethod);
}
private int comparePattern(TargetPattern a, TargetPattern b) {
if (a instanceof TargetComplexPattern cpa
&& b instanceof TargetComplexPattern cpb
&& Objects.equals(cpa.type(), cpb.type())) {
assert cpa.subPatterns().size() == cpb.subPatterns().size();
for (var i = 0; i < cpa.subPatterns().size(); i++) {
var c = comparePattern(cpa.subPatterns().get(i), cpb.subPatterns().get(i));
if (c != 0) return c;
}
return 0;
}
if (isSubtype(a.type(), b.type())) return -1;
if (isSubtype(b.type(), a.type())) return 1;
return 0;
}
private class CompareBySpecificity implements Comparator<MethodWithTphs> {
@Override
public int compare(MethodWithTphs o1, MethodWithTphs o2) {
var p1 = o1.signature.java.parameters();
var p2 = o2.signature.java.parameters();
if (p1.size() != p2.size()) return 0;
for (var i = 0; i < p1.size(); i++) {
var c = comparePattern(p1.get(i).pattern(), p2.get(i).pattern());
if (c != 0) return c;
}
return 0;
}
}
private void sortBySpecificity(List<MethodWithTphs> methods) {
methods.sort(new CompareBySpecificity());
}
private List<TargetMethod> generatePatternOverloads(ClassOrInterface clazz, MethodGroup group) {
var overloadedMethods = new ArrayList<>(group.methods);
if (overloadedMethods.isEmpty()) return List.of(); if (overloadedMethods.isEmpty()) return List.of();
// Check if we have a pattern as a parameter // Check if we have a pattern as a parameter
var firstMethod = convert(overloadedMethods.getFirst(), overloadedMethods.getFirst().generics.javaGenerics); sortBySpecificity(overloadedMethods);
var firstMethod = convert(overloadedMethods.getFirst());
if (overloadedMethods.size() == 1) return List.of(firstMethod); if (overloadedMethods.size() == 1) return List.of(firstMethod);
var secondMethod = convert(overloadedMethods.get(1), overloadedMethods.get(1).generics.javaGenerics); var signatureParams = group.signature.java.parameters();
if (firstMethod.signature().parameters().stream().noneMatch(mp -> mp.pattern() instanceof TargetComplexPattern))
return overloadedMethods.stream().map(m -> convert(m, m.generics.javaGenerics)).toList();
var signatureParams = new ArrayList<MethodParameter>();
for (var i = 0; i < firstMethod.signature().parameters().size(); i++) {
var p1 = firstMethod.signature().parameters().get(i).pattern();
var t1 = p1.type();
var t2 = secondMethod.signature().parameters().get(i).pattern().type();
var commonSubTypes = new HashSet<>(commonSuperInterfaceTypes(t1, t2));
for (var m : overloadedMethods.subList(2, overloadedMethods.size())) {
var t3 = m.signature().java.parameters().get(i).pattern().type();
commonSubTypes.retainAll(commonSuperInterfaceTypes(t1, t3));
}
if (commonSubTypes.size() > 1) throw new DebugException("Invalid overload");
// TODO accept multiple types
var superType = ASTFactory.createObjectClass();
if (!commonSubTypes.isEmpty())
superType = commonSubTypes.iterator().next();
String name;
if (p1 instanceof TargetComplexPattern) name = "__var" + i;
else name = p1.name();
signatureParams.add(new MethodParameter(new TargetRefType(superType.getClassName().toString()), name));
}
// Rename existing methods
var res = new ArrayList<TargetMethod>(); var res = new ArrayList<TargetMethod>();
for (var method : overloadedMethods) { for (var method : overloadedMethods) {
@@ -607,12 +728,6 @@ public class ASTToTargetAST {
res.add(new TargetMethod(tMethod.access(), name, tMethod.block(), tMethod.signature(), tMethod.txSignature())); res.add(new TargetMethod(tMethod.access(), name, tMethod.block(), tMethod.signature(), tMethod.txSignature()));
} }
var commonSubTypes = new HashSet<>(commonSuperInterfaceTypes(firstMethod.signature().returnType(), secondMethod.signature().returnType()));
for (var m : overloadedMethods.subList(2, overloadedMethods.size())) {
commonSubTypes.retainAll(commonSuperInterfaceTypes(firstMethod.signature().returnType(), m.signature().java.returnType()));
}
var returnType = commonSubTypes.isEmpty() ? TargetType.Object : new TargetRefType(commonSubTypes.iterator().next().getClassName().toString());
var parameters = signatureParams.stream().map( p -> new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList(); var parameters = signatureParams.stream().map( p -> new TargetLocalVar(p.pattern().type(), p.pattern().name())).toList();
//var patterns = List.of((TargetComplexPattern) firstMethod.signature().parameters().stream() //var patterns = List.of((TargetComplexPattern) firstMethod.signature().parameters().stream()
// .filter(p -> p.pattern() instanceof TargetComplexPattern).findFirst().orElseThrow().pattern()); // .filter(p -> p.pattern() instanceof TargetComplexPattern).findFirst().orElseThrow().pattern());
@@ -622,9 +737,7 @@ public class ASTToTargetAST {
var stmt = generatePatternOverloadsRec(0, new TargetLocalVar(signatureParams.getFirst().pattern().type(), signatureParams.getFirst().pattern().name()), parameters, List.of(), res, classType); 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)); var block = new TargetBlock(List.of(stmt));
var signature = new TargetMethod.Signature(Set.of(), signatureParams, returnType); var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, group.signature.java, group.signature.tx);
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, signature, firstMethod.txSignature());
res.add(bridgeMethod); res.add(bridgeMethod);
return res; return res;
} }
@@ -670,6 +783,15 @@ public class ASTToTargetAST {
}).findFirst(); }).findFirst();
} }
record MethodGroup(Signature signature, Set<MethodWithTphs> methods) {
@Override
public String toString() {
assert !methods.isEmpty();
var first = methods.iterator().next();
return signature.java.returnType() + " " + first.method.name + " " + signature.java.generics() + " " + signature.java.parameters();
}
}
record MethodWithTphs(Method method, Generics generics, Signature signature) { record MethodWithTphs(Method method, Generics generics, Signature signature) {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
@@ -684,14 +806,31 @@ public class ASTToTargetAST {
} }
} }
private TargetMethod convert(MethodWithTphs mtph) {
return convert(mtph, mtph.generics.javaGenerics);
}
private TargetMethod convert(MethodWithTphs mtph, IGenerics generics) { private TargetMethod convert(MethodWithTphs mtph, IGenerics generics) {
return new TargetMethod(mtph.method.modifier, mtph.method.name, convert(mtph.method.block, generics), mtph.signature.java(), mtph.signature.tx()); return new TargetMethod(mtph.method.modifier, mtph.method.name, convert(mtph.method.block, generics), mtph.signature.java(), mtph.signature.tx());
} }
record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {} record Signature(TargetMethod.Signature java, TargetMethod.Signature tx, Generics generics) {
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof Signature that)) return false;
return Objects.equals(this.java, that.java) && Objects.equals(this.tx, that.tx)
&& typesStrictlyEqual(this.java.returnType(), that.java.returnType()) && typesStrictlyEqual(this.tx.returnType(), that.tx.returnType());
}
private List<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) { @Override
List<MethodWithTphs> result = new ArrayList<>(); public int hashCode() {
return Objects.hash(java, tx);
}
}
private Set<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) {
Set<MethodWithTphs> result = new HashSet<>();
this.currentMethod = method; this.currentMethod = method;
List<Signature> signatures = new ArrayList<>(); List<Signature> signatures = new ArrayList<>();
@@ -716,7 +855,7 @@ public class ASTToTargetAST {
var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, method); var txMethodGenerics = collectMethodGenerics(currentClass, generics.txGenerics(), txGenerics, method);
var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType); var javaSignature = new TargetMethod.Signature(javaMethodGenerics, params, returnType);
System.out.println(javaSignature.getDescriptor()); //System.out.println(javaSignature.getDescriptor());
var txSignature = new TargetMethod.Signature(txMethodGenerics, txParams, convert(method.getReturnType(), generics.txGenerics, compiler)); var txSignature = new TargetMethod.Signature(txMethodGenerics, txParams, convert(method.getReturnType(), generics.txGenerics, compiler));
signatures.add(new Signature(javaSignature, txSignature, generics)); signatures.add(new Signature(javaSignature, txSignature, generics));
@@ -3,4 +3,9 @@ package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
public record TargetGeneric(String name, TargetType bound) { public record TargetGeneric(String name, TargetType bound) {
@Override
public String toString() {
if (bound.equals(TargetType.Object)) return "'" + name;
else return "'" + name + " < " + bound;
}
} }
@@ -1,12 +1,13 @@
package de.dhbwstuttgart.target.tree; package de.dhbwstuttgart.target.tree;
import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetType; import de.dhbwstuttgart.target.tree.type.TargetType;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
public record TargetInterface(int modifiers, JavaClassName qualifiedName, Set<TargetGeneric> generics, Set<TargetGeneric> txGenerics, List<TargetMethod> methods, List<TargetType> implementingInterfaces, TargetMethod staticConstructor) implements TargetStructure { public record TargetInterface(int modifiers, JavaClassName qualifiedName, Set<TargetGeneric> generics, Set<TargetGeneric> txGenerics, List<TargetMethod> methods, List<TargetType> implementingInterfaces, TargetMethod staticConstructor, List<TargetRefType> permittedSubtypes) implements TargetStructure {
@Override @Override
public TargetType superType() { public TargetType superType() {
return null; return null;
@@ -736,12 +736,12 @@ public class TYPEStmt implements StatementVisitor {
for (int i = 0; i < foMethod.arglist.getArguments().size(); i++) { for (int i = 0; i < foMethod.arglist.getArguments().size(); i++) {
// Zuordnung von MethoCall.signature (Argumenttypen) zu der Argumenttypen der ausgewaehlten Methode (assumption.params) // Zuordnung von MethoCall.signature (Argumenttypen) zu der Argumenttypen der ausgewaehlten Methode (assumption.params)
ret.add(new Pair(resolver.resolve(foMethod.signature.get(i)), resolver.resolve(assumption.getArgTypes().get(i)), PairOperator.EQUALSDOT)); ret.add(new Pair(foMethod.signature.get(i), assumption.getArgTypes().get(i), PairOperator.EQUALSDOT));
} }
// Zuordnung von MethodCall.signature(ReturnType) zu dem ReturnType der ausgewaehlten Methode (assumption.returnType) // Zuordnung von MethodCall.signature(ReturnType) zu dem ReturnType der ausgewaehlten Methode (assumption.returnType)
ret.add(new Pair(resolver.resolve(foMethod.signature.getLast()), resolver.resolve(assumption.getReturnType()), PairOperator.EQUALSDOT)); ret.add(new Pair(foMethod.signature.getLast(), assumption.getReturnType(), PairOperator.EQUALSDOT));
return ret; return ret;
} }
@@ -755,7 +755,7 @@ public class RuleSet implements IRuleSet{
UnifyType lhsType = pair.getLhsType(); UnifyType lhsType = pair.getLhsType();
UnifyType rhsType = pair.getRhsType(); UnifyType rhsType = pair.getRhsType();
if((!(lhsType instanceof ReferenceType) && !(lhsType instanceof FunNType)) || !(rhsType instanceof ExtendsType)) if(!(lhsType instanceof ReferenceType) || !(rhsType instanceof ExtendsType))
return Optional.empty(); return Optional.empty();
return Optional.of(new UnifyPair(lhsType, ((ExtendsType) rhsType).getExtendedType(), PairOperator.SMALLERDOT, pair.getSubstitution(), pair.getBasePair())); return Optional.of(new UnifyPair(lhsType, ((ExtendsType) rhsType).getExtendedType(), PairOperator.SMALLERDOT, pair.getSubstitution(), pair.getBasePair()));
@@ -781,7 +781,7 @@ public class RuleSet implements IRuleSet{
UnifyType lhsType = pair.getLhsType(); UnifyType lhsType = pair.getLhsType();
UnifyType rhsType = pair.getRhsType(); UnifyType rhsType = pair.getRhsType();
if((!(lhsType instanceof ReferenceType) && !(lhsType instanceof FunNType)) || !(rhsType instanceof SuperType)) if(!(lhsType instanceof ReferenceType) || !(rhsType instanceof SuperType))
return Optional.empty(); return Optional.empty();
return Optional.of(new UnifyPair(((SuperType) rhsType).getSuperedType(), lhsType, PairOperator.SMALLERDOTWC, pair.getSubstitution(), pair.getBasePair())); return Optional.of(new UnifyPair(((SuperType) rhsType).getSuperedType(), lhsType, PairOperator.SMALLERDOTWC, pair.getSubstitution(), pair.getBasePair()));
@@ -904,7 +904,7 @@ public class RuleSet implements IRuleSet{
var fiArgs = intf.getFunctionalInterfaceTypeArguments(refType); var fiArgs = intf.getFunctionalInterfaceTypeArguments(refType);
var retType = fiArgs.getFirst(); var retType = fiArgs.getFirst();
var lhsArgs = intf.getFunctionalInterfaceTypeArguments(lhsType); //FALSCHRUM???? var lhsArgs = intf.getFunctionalInterfaceTypeArguments(lhsType);
var lhsRet = lhsArgs.getFirst(); var lhsRet = lhsArgs.getFirst();
Set<UnifyPair> result = new HashSet<>(); Set<UnifyPair> result = new HashSet<>();
@@ -972,66 +972,16 @@ public class RuleSet implements IRuleSet{
} }
@Override @Override
public Optional<Set<UnifyPair>> smallerFunN(UnifyPair pair, IFiniteClosure fc) { public Optional<Set<UnifyPair>> smallerFunN(UnifyPair pair) {
if(pair.getPairOp() != PairOperator.SMALLERDOT) if(pair.getPairOp() != PairOperator.SMALLERDOT)
return Optional.empty(); return Optional.empty();
UnifyType lhsType = pair.getLhsType(); UnifyType lhsType = pair.getLhsType();
UnifyType rhsType = pair.getRhsType(); UnifyType rhsType = pair.getRhsType();
if(!(rhsType instanceof FunNType)) if(!(lhsType instanceof PlaceholderType) || !(rhsType instanceof FunNType))
return Optional.empty(); return Optional.empty();
/* muss nach angepasst werden */
//FunN$$<...> <. FunctinalInterface<...> wird umgewandelt in FunN$$<...> <. FunN$$<... args aus FuntionalInterface ...>
if (lhsType instanceof ReferenceType) {
UnifyType typeFI = pair.getLhsType();
Optional<UnifyType> opt = fc.getRightHandedFunctionalInterfaceType(typeFI.getName());
if(!opt.isPresent())
return Optional.empty();
if (!(typeFI instanceof ReferenceType refType) || !(refType instanceof FunInterfaceType intf))
return Optional.empty();
var fiArgs = intf.getFunctionalInterfaceTypeArguments(refType);
var retType = fiArgs.getFirst();
var rhsArgs = intf.getFunctionalInterfaceTypeArguments(rhsType);
var rhsRet = rhsArgs.getFirst();
Set<UnifyPair> result = new HashSet<>();
//if (retType instanceof ExtendsType) {
result.add(new UnifyPair(retType, rhsRet, PairOperator.SMALLER));
//} else if (retType instanceof SuperType) {
// return Optional.empty();
//} else {
// result.add(new UnifyPair(lhsRet, retType, PairOperator.EQUALSDOT));
//}
for (var i = 1; i < fiArgs.size(); i++) {
var rh = rhsArgs.get(i);
var lh = fiArgs.get(i);
//if (rh instanceof SuperType) {
result.add(new UnifyPair(rh, lh, PairOperator.SMALLER));
//} else if (rh instanceof ExtendsType) {
// return Optional.empty();
//} else {
// result.add(new UnifyPair(lh, rh, PairOperator.EQUALSDOT));
//}
}
return Optional.of(result);
}
else {
if(!(lhsType instanceof PlaceholderType))
return Optional.empty();
}
FunNType funNRhsType = (FunNType) rhsType; FunNType funNRhsType = (FunNType) rhsType;
Set<UnifyPair> result = new HashSet<UnifyPair>(); Set<UnifyPair> result = new HashSet<UnifyPair>();
@@ -1068,121 +1018,6 @@ public class RuleSet implements IRuleSet{
return Optional.of(result); return Optional.of(result);
} }
@Override
public Optional<Set<UnifyPair>> reduceFIFunN(UnifyPair pair, IFiniteClosure fc) {
if(pair.getPairOp() != PairOperator.EQUALSDOT)
return Optional.empty();
UnifyType lhsType = pair.getLhsType();
UnifyType rhsType = pair.getRhsType();
if(!(rhsType instanceof FunNType))
return Optional.empty();
/* muss nach angepasst werden */
//FunN$$<...> <. FunctinalInterface<...> wird umgewandelt in FunN$$<...> <. FunN$$<... args aus FuntionalInterface ...>
if (lhsType instanceof ReferenceType) {
UnifyType typeFI = pair.getLhsType();
Optional<UnifyType> opt = fc.getRightHandedFunctionalInterfaceType(typeFI.getName());
if(!opt.isPresent())
return Optional.empty();
if (!(typeFI instanceof ReferenceType refType) || !(refType instanceof FunInterfaceType intf))
return Optional.empty();
var fiArgs = intf.getFunctionalInterfaceTypeArguments(refType);
var retType = fiArgs.getFirst();
var rhsArgs = intf.getFunctionalInterfaceTypeArguments(rhsType);
var rhsRet = rhsArgs.getFirst();
Set<UnifyPair> result = new HashSet<>();
//if (retType instanceof ExtendsType) {
result.add(new UnifyPair(retType, rhsRet, PairOperator.EQUALSDOT));
//} else if (retType instanceof SuperType) {
// return Optional.empty();
//} else {
// result.add(new UnifyPair(lhsRet, retType, PairOperator.EQUALSDOT));
//}
for (var i = 1; i < fiArgs.size(); i++) {
var rh = rhsArgs.get(i);
var lh = fiArgs.get(i);
//if (rh instanceof SuperType) {
result.add(new UnifyPair(rh, lh, PairOperator.EQUALSDOT));
//} else if (rh instanceof ExtendsType) {
// return Optional.empty();
//} else {
// result.add(new UnifyPair(lh, rh, PairOperator.EQUALSDOT));
//}
}
return Optional.of(result);
}
else {
return Optional.empty();
}
}
@Override
public Optional<Set<UnifyPair>> reduceFunNFi(UnifyPair pair, IFiniteClosure fc) {
if(pair.getPairOp() != PairOperator.EQUALSDOT)
return Optional.empty();
UnifyType lhsType = pair.getLhsType();
UnifyType rhsType = pair.getRhsType();
if(!(lhsType instanceof FunNType))
return Optional.empty();
//FunN$$<...> <. FunctinalInterface<...> wird umgewandelt in FunN$$<...> <. FunN$$<... args aus FuntionalInterface ...>
if (rhsType instanceof ReferenceType) {
UnifyType typeFI = pair.getRhsType();
Optional<UnifyType> opt = fc.getRightHandedFunctionalInterfaceType(typeFI.getName());
if(!opt.isPresent())
return Optional.empty();
if (!(typeFI instanceof ReferenceType refType) || !(refType instanceof FunInterfaceType intf))
return Optional.empty();
var fiArgs = intf.getFunctionalInterfaceTypeArguments(refType);
var retType = fiArgs.getFirst();
var lhsArgs = intf.getFunctionalInterfaceTypeArguments(lhsType);
var lhsRet = lhsArgs.getFirst();
Set<UnifyPair> result = new HashSet<>();
//if (retType instanceof ExtendsType) {
// result.add(new UnifyPair(lhsRet, retType, PairOperator.SMALLERDOTWC));
//} else if (retType instanceof SuperType) {
// return Optional.empty();
//} else {
result.add(new UnifyPair(lhsRet, retType, PairOperator.EQUALSDOT));
//}
for (var i = 1; i < fiArgs.size(); i++) {
var lh = lhsArgs.get(i);
var rh = fiArgs.get(i);
//if (rh instanceof SuperType) {
// result.add(new UnifyPair(lh, rh, PairOperator.SMALLERDOTWC));
//} else if (rh instanceof ExtendsType) {
// return Optional.empty();
//} else {
result.add(new UnifyPair(lh, rh, PairOperator.EQUALSDOT));
//}
}
return Optional.of(result);
}
else {
return Optional.empty();
}
}
@Override @Override
public Optional<UnifyPair> reduceTph(UnifyPair pair) { public Optional<UnifyPair> reduceTph(UnifyPair pair) {
if(pair.getPairOp() != PairOperator.SMALLERDOTWC) if(pair.getPairOp() != PairOperator.SMALLERDOTWC)
@@ -1190,7 +1025,7 @@ public class RuleSet implements IRuleSet{
UnifyType lhsType = pair.getLhsType(); UnifyType lhsType = pair.getLhsType();
UnifyType rhsType = pair.getRhsType(); UnifyType rhsType = pair.getRhsType();
if(!(lhsType instanceof PlaceholderType) || (!(rhsType instanceof ReferenceType) && !(rhsType instanceof FunNType))) if(!(lhsType instanceof PlaceholderType) || !(rhsType instanceof ReferenceType))
return Optional.empty(); return Optional.empty();
return Optional.of(new UnifyPair(lhsType, rhsType, PairOperator.EQUALSDOT, pair.getSubstitution(), pair.getBasePair())); return Optional.of(new UnifyPair(lhsType, rhsType, PairOperator.EQUALSDOT, pair.getSubstitution(), pair.getBasePair()));
@@ -1239,7 +1239,7 @@ public class TypeUnifyTask extends CancellableTask<CompletableFuture<Set<Set<Uni
// FunN Rules // FunN Rules
optSet = optSet.isPresent() ? optSet : rules.reduceFunN(pair); optSet = optSet.isPresent() ? optSet : rules.reduceFunN(pair);
optSet = optSet.isPresent() ? optSet : rules.greaterFunN(pair, fc); optSet = optSet.isPresent() ? optSet : rules.greaterFunN(pair, fc);
optSet = optSet.isPresent() ? optSet : rules.smallerFunN(pair, fc); optSet = optSet.isPresent() ? optSet : rules.smallerFunN(pair);
// One of the rules has been applied // One of the rules has been applied
if (optSet.isPresent()) { if (optSet.isPresent()) {
@@ -39,8 +39,7 @@ public class UnifyResultModel {
public void notify(Set<Set<UnifyPair>> eqPrimePrimeSet, UnifyContext context) { public void notify(Set<Set<UnifyPair>> eqPrimePrimeSet, UnifyContext context) {
Set<Set<UnifyPair>> eqPrimePrimeSetRet = eqPrimePrimeSet.stream().map(x -> { Set<Set<UnifyPair>> eqPrimePrimeSetRet = eqPrimePrimeSet.stream().map(x -> {
Optional<Set<UnifyPair>> res = new RuleSet(context.placeholderRegistry()).subst(x.stream().map(y -> { Optional<Set<UnifyPair>> res = new RuleSet(context.placeholderRegistry()).subst(x.stream().map(y -> {
if (y.getPairOp() == PairOperator.SMALLERDOTWC) if (y.getPairOp() == PairOperator.SMALLERDOTWC) y.setPairOp(PairOperator.EQUALSDOT);
y.setPairOp(PairOperator.EQUALSDOT);
return y; //alle Paare a <.? b erden durch a =. b ersetzt return y; //alle Paare a <.? b erden durch a =. b ersetzt
}).collect(Collectors.toCollection(HashSet::new))); }).collect(Collectors.toCollection(HashSet::new)));
if (res.isPresent()) {//wenn subst ein Erg liefert wurde was veraendert if (res.isPresent()) {//wenn subst ein Erg liefert wurde was veraendert
@@ -61,9 +61,7 @@ public interface IRuleSet {
*/ */
public Optional<Set<UnifyPair>> reduceFunN(UnifyPair pair); public Optional<Set<UnifyPair>> reduceFunN(UnifyPair pair);
public Optional<Set<UnifyPair>> greaterFunN(UnifyPair pair, IFiniteClosure fc); public Optional<Set<UnifyPair>> greaterFunN(UnifyPair pair, IFiniteClosure fc);
public Optional<Set<UnifyPair>> smallerFunN(UnifyPair pair, IFiniteClosure fc); public Optional<Set<UnifyPair>> smallerFunN(UnifyPair pair);
public Optional<Set<UnifyPair>> reduceFIFunN(UnifyPair pair, IFiniteClosure fc);
public Optional<Set<UnifyPair>> reduceFunNFi(UnifyPair pair, IFiniteClosure fc);
/** /**
* Checks whether the erase1-Rule applies to the pair. * Checks whether the erase1-Rule applies to the pair.
@@ -685,7 +685,6 @@ public class FiniteClosure //extends Ordering<UnifyType> //entfernt PL 2018-12-1
return Optional.empty(); return Optional.empty();
} }
@Override @Override
public Set<UnifyType> getAncestors(UnifyType t) { public Set<UnifyType> getAncestors(UnifyType t) {
if (!inheritanceGraph.containsKey(t)) if (!inheritanceGraph.containsKey(t))
+2 -9
View File
@@ -14,9 +14,7 @@ import org.junit.jupiter.api.Test;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.core.JavaTXCompiler; import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.util.Logger.LogLevel;
public class AllgemeinTest { public class AllgemeinTest {
@@ -31,7 +29,6 @@ public class AllgemeinTest {
@Test @Test
public void test() throws Exception { public void test() throws Exception {
ConsoleInterface.logLevel = LogLevel.DEBUG;
//String className = "GenTest"; //String className = "GenTest";
//String className = "Overloading_Generics"; //String className = "Overloading_Generics";
//String className = "Generics"; //String className = "Generics";
@@ -66,15 +63,11 @@ public class AllgemeinTest {
//String className = "Cycle"; //String className = "Cycle";
//String className = "TripleTest"; //String className = "TripleTest";
//String className = "WildcardList"; //String className = "WildcardList";
//String className = "List"; String className = "List";
//String className = "Box"; //String className = "Box";
//String className = "GenBox"; //String className = "GenBox";
//String className = "InnerInf"; //String className = "InnerInf";
//String className = "Foo"; //String className = "Foo";
//String className = "Kombinatoren_failure";
String className = "Kombinatoren_After_Lazy";
//String className = "Parser";
//PL 2019-10-24: genutzt fuer unterschiedliche Tests //PL 2019-10-24: genutzt fuer unterschiedliche Tests
path = System.getProperty("user.dir")+"/resources/AllgemeinTest/" + className + ".jav"; path = System.getProperty("user.dir")+"/resources/AllgemeinTest/" + className + ".jav";
//path = System.getProperty("user.dir")+"/src/test/resources/AllgemeinTest/Overloading_Generics.jav"; //path = System.getProperty("user.dir")+"/src/test/resources/AllgemeinTest/Overloading_Generics.jav";
@@ -83,7 +76,7 @@ public class AllgemeinTest {
///* ///*
compiler = new JavaTXCompiler( compiler = new JavaTXCompiler(
Lists.newArrayList(new File(path)), Lists.newArrayList(new File(path)),
Lists.newArrayList(new File(System.getProperty("user.dir")+"/resources/AllgemeinTest/")), Lists.newArrayList(new File(System.getProperty("user.dir")+"/resources/bytecode/classFiles/")),
new File(System.getProperty("user.dir")+"/resources/bytecode/classFiles/"), true); new File(System.getProperty("user.dir")+"/resources/bytecode/classFiles/"), true);
//*/ //*/
compiler.generateBytecode(); compiler.generateBytecode();
+38 -6
View File
@@ -1,8 +1,6 @@
import de.dhbwstuttgart.core.ConsoleInterface; import de.dhbwstuttgart.core.ConsoleInterface;
import de.dhbwstuttgart.util.Logger; import de.dhbwstuttgart.util.Logger;
import de.dhbwstuttgart.util.Logger.LogLevel;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -955,16 +953,44 @@ public class TestComplete {
var list1 = ConsCtor.newInstance(1, ConsCtor.newInstance(2, ConsCtor.newInstance(3, EmptyCtor.newInstance()))); var list1 = ConsCtor.newInstance(1, ConsCtor.newInstance(2, ConsCtor.newInstance(3, EmptyCtor.newInstance())));
var list2 = ConsCtor.newInstance(4, ConsCtor.newInstance(5, ConsCtor.newInstance(6, EmptyCtor.newInstance()))); var list2 = ConsCtor.newInstance(4, ConsCtor.newInstance(5, ConsCtor.newInstance(6, EmptyCtor.newInstance())));
var append = clazz.getDeclaredMethod("append", Cons, Cons); var append = clazz.getDeclaredMethod("append", List, List);
System.out.println(append.invoke(instance, list1, list2)); System.out.println(append.invoke(instance, list1, list2));
} }
@Test
public void testPatternMatchingSpecificity() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "PatternMatchingSpecificity.jav");
var clazz = classFiles.get("PatternMatchingSpecificity");
var instance = clazz.getDeclaredConstructor().newInstance();
var R = classFiles.get("R");
Integer i = 20;
var r = R.getDeclaredConstructor(Number.class).newInstance(i);
var m = clazz.getDeclaredMethod("m", R);
assertEquals(1, m.invoke(instance, r));
}
@Test @Test
public void testPatternMatchingZip() throws Exception { public void testPatternMatchingZip() throws Exception {
ConsoleInterface.logLevel = LogLevel.DEBUG; //ConsoleInterface.logLevel = Logger.LogLevel.DEBUG;
var classFiles = generateClassFiles(createClassLoader(), false,"PatternMatchingJava2.jav", "PatternMatching.jav"); var classFiles = generateClassFiles(createClassLoader(), "PatternMatching.jav");
var clazz = classFiles.get("PatternMatching"); var clazz = classFiles.get("PatternMatching");
var instance = clazz.getDeclaredConstructor().newInstance(); var instance = clazz.getDeclaredConstructor().newInstance();
var Cons = classFiles.get("Cons");
var Empty = classFiles.get("Empty");
var List = classFiles.get("List");
var ConsCtor = Cons.getDeclaredConstructor(Object.class, List);
var EmptyCtor = Empty.getDeclaredConstructor();
var list1 = ConsCtor.newInstance(1, ConsCtor.newInstance(2, ConsCtor.newInstance(3, EmptyCtor.newInstance())));
var list2 = ConsCtor.newInstance(4, ConsCtor.newInstance(5, ConsCtor.newInstance(6, EmptyCtor.newInstance())));
var zip = clazz.getDeclaredMethod("zip", List, List);
System.out.println(zip.invoke(instance, list1, list2));
} }
@Test @Test
@@ -1390,7 +1416,6 @@ public class TestComplete {
} }
@Test @Test
@Disabled("too slow")
public void testBug325() throws Exception { public void testBug325() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug325.jav"); var classFiles = generateClassFiles(createClassLoader(), "Bug325.jav");
var clazz = classFiles.get("Bug325"); var clazz = classFiles.get("Bug325");
@@ -1552,4 +1577,11 @@ public class TestComplete {
var clazz = classFiles.get("Bug390"); var clazz = classFiles.get("Bug390");
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of()); clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
} }
@Test
public void testBug392() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug392.jav");
var clazz = classFiles.get("Bug392");
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
}
} }
+1 -4
View File
@@ -19,14 +19,11 @@ public class TestTypeDeployment {
var path = Path.of(System.getProperty("user.dir"), "/resources/bytecode/javFiles/Cycle.jav"); var path = Path.of(System.getProperty("user.dir"), "/resources/bytecode/javFiles/Cycle.jav");
var file = path.toFile(); var file = path.toFile();
var compiler = new JavaTXCompiler(file); var compiler = new JavaTXCompiler(file);
compiler.parseAll();
var parsedSource = compiler.sourceFiles.get(file); var parsedSource = compiler.sourceFiles.get(file);
var tiResults = compiler.typeInference(file); var tiResults = compiler.typeInference(file);
Set<TypeInsert> tips = new HashSet<>(); Set<TypeInsert> tips = new HashSet<>();
for (var sf : compiler.sourceFiles.values()) {
Map<JavaClassName, byte[]> bytecode = compiler.generateBytecode(sf, tiResults);
}
System.out.println(compiler.getGeneratedGenerics()); System.out.println(compiler.getGeneratedGenerics());
for (int i = 0; i < tiResults.size(); i++) { for (int i = 0; i < tiResults.size(); i++) {
+1
View File
@@ -59,6 +59,7 @@ public class ServerTest {
// get information from the compiler // get information from the compiler
JavaTXCompiler compiler = new JavaTXCompiler(List.of(file)); JavaTXCompiler compiler = new JavaTXCompiler(List.of(file));
compiler.parseAll();
// NOW: simulate the call to method typeInference. Once via server and once locally // NOW: simulate the call to method typeInference. Once via server and once locally
// if everything works, they should neither interfere with each other nor differ in their result // if everything works, they should neither interfere with each other nor differ in their result
@@ -49,6 +49,7 @@ public class TestGenerics {
private static Result computeGenerics(String filename) throws IOException, ClassNotFoundException { private static Result computeGenerics(String filename) throws IOException, ClassNotFoundException {
var file = Path.of(rootDirectory + filename).toFile(); var file = Path.of(rootDirectory + filename).toFile();
var compiler = new JavaTXCompiler(List.of(file), List.of(file.getParentFile()), new File(bytecodeDirectory), true); var compiler = new JavaTXCompiler(List.of(file), List.of(file.getParentFile()), new File(bytecodeDirectory), true);
compiler.parseAll();
var inference = compiler.typeInference(file); var inference = compiler.typeInference(file);
compiler.generateBytecode(new File(bytecodeDirectory), inference); compiler.generateBytecode(new File(bytecodeDirectory), inference);
var sf = compiler.sourceFiles.get(file); var sf = compiler.sourceFiles.get(file);