Compare commits

..

1 Commits

Author SHA1 Message Date
julian b3ba2714da visualize constraints for mplr26 paper
Build and Test with Maven / Build-and-test-with-Maven (push) Failing after 1m6s
2026-02-24 15:18:27 +01:00
25 changed files with 519 additions and 731 deletions
-10
View File
@@ -1,10 +0,0 @@
import java.lang.String;
public class Bug389 {
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,12 +0,0 @@
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 swap = new Bug389();
swap.swap(func).apply(1).apply(2).apply(3);
}
}
@@ -1,14 +1,14 @@
public sealed interface List<T> permits Cons, Empty {} 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> {}
public record Pair<T1, T2>(T1 a, T2 b) {} public record Tuple<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 Tuple(x, y), zip(xs, ys));
} }
public zip(Empty(), Empty()) { return new Empty<>(); } public zip(Empty x, Empty y) { 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(); }
@@ -2,24 +2,18 @@ import java.lang.Object;
import List; import List;
import Cons; import Cons;
import Empty; import Empty;
import Pair; import Tuple;
public class PatternMatchingJava { public class PatternMatchingJava {
public <A, B> Cons<Pair<A, B>> zip(Cons<A> a, Cons<B> b) { public zip(a, b) {
switch (a) { switch (a) {
case Cons(x, Cons xs) -> { case Cons(x, Cons xs) -> {
switch (b) { switch (b) {
case Cons(y, Cons ys) -> { return new Cons<>(new Pair<>(x, y), zip(xs, ys)); } case Cons(y, Cons ys) -> { return new Cons<>(new Tuple<>(x, y), zip(xs, ys)); }
}; case Cons(y, Empty()) -> { return new Empty<>(); }
}
case Cons(x, Empty xs) -> {
switch (b) {
case Cons(y, Empty ys) -> { return new Cons<>(new Pair<>(x, y), zip(xs, ys)); }
}; };
} }
case Cons(x, Empty()) -> { return new Empty<>(); }
}; };
} }
public <A, B> Empty<Pair<A, B>> zip(Empty<A> a, Empty<B>) {
return new Empty<>();
}
} }
@@ -1,5 +1,5 @@
public sealed interface List<T> permits Cons, Empty {} 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> {}
public record Pair<T1, T2>(T1 a, T2 b) {} public record Tuple<T1, T2>(T1 a, T2 b) {}
@@ -1,7 +1,7 @@
import java.lang.Boolean; import java.lang.Boolean;
import java.lang.Object; import java.lang.Object;
public sealed interface List<T> permits Cons, Empty {} 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> {}
@@ -1,9 +0,0 @@
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; }
}
@@ -4,7 +4,6 @@ import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.exceptions.NotImplementedException; import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface; import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.Pattern;
import de.dhbwstuttgart.target.generate.ASTToTargetAST; import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import de.dhbwstuttgart.target.tree.*; import de.dhbwstuttgart.target.tree.*;
import de.dhbwstuttgart.target.tree.expression.*; import de.dhbwstuttgart.target.tree.expression.*;
@@ -1499,13 +1498,12 @@ public class Codegen {
state.exitScope(); state.exitScope();
} }
private void extractField(State state, TargetType ret, TargetType type, int i, ClassOrInterface clazz) { private void extractField(State state, 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) {
@@ -1568,7 +1566,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, null, cp.type(), i, clazz); extractField(state, 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);
@@ -1670,7 +1668,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, subPattern.type(), cp.type(), i, clazz); extractField(state, 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,11 +20,14 @@ 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.*; import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
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;
@@ -32,12 +35,9 @@ 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,7 +62,6 @@ 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;
@@ -83,9 +82,7 @@ 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();
@@ -127,11 +124,7 @@ 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) {
@@ -161,7 +154,11 @@ 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) {
@@ -425,6 +422,43 @@ public class JavaTXCompiler {
return typeInference(List.of(file)); return typeInference(List.of(file));
} }
public static String constraintSetToDot(ConstraintSet<Pair> c){
Iterator<String> colors = List.of(
"#FF0000", "#FF7F00", "#FFFF00", "#7FFF00", "#00FF00", "#00FF7F", "#00FFFF", "#007FFF",
"#0000FF", "#7F00FF", "#FF00FF", "#FF007F", "#BF0000", "#BF5F00", "#BFBF00", "#5FBF00",
"#00BF00", "#00BF5F", "#00BFBF", "#005FBF", "#0000BF", "#5F00BF", "#BF00BF", "#BF005F",
"#800000", "#804000", "#808000", "#408000", "#008000", "#008040", "#008080", "#004080",
"#000080", "#400080", "#800080", "#800040", "#FF4040", "#FF8040", "#FFFF40", "#80FF40",
"#40FF40", "#40FF80", "#40FFFF", "#4080FF", "#4040FF", "#8040FF", "#FF40FF", "#FF4080",
"#BF4040", "#BF8040", "#BFBF40", "#80BF40", "#40BF40", "#40BF80", "#40BFBF", "#4080BF",
"#4040BF", "#8040BF", "#BF40BF", "#BF4080", "#FF8080", "#FFBF80", "#FFFF80", "#BFFF80",
"#80FF80", "#80FFBF", "#80FFFF", "#80BFFF", "#8080FF", "#BF80FF", "#FF80FF", "#FF80BF").iterator();
StringBuilder sb = new StringBuilder();
sb.append("diagraph G{");
//and constraints to dot
sb.append(c.getUndConstraints().stream().map(Pair::toDot).collect(Collectors.joining("\n")));
//or constraints to dot
for (var orConstSet : c.getOderConstraints()){
for(var cons : orConstSet){
String color = colors.next();
sb.append(cons.stream().map(x -> x.toDot(color)).collect(Collectors.joining("\n")));
}
}
sb.append("}");
return sb.toString();
}
public static String q(String s) {
return "\"" + s.replace("\"", "\\\"") + "\"";
}
public List<ResultSet> typeInference(List<File> files) throws ClassNotFoundException, IOException { public List<ResultSet> typeInference(List<File> files) throws ClassNotFoundException, IOException {
Set<ClassOrInterface> allClasses = new HashSet<>(); Set<ClassOrInterface> allClasses = new HashSet<>();
@@ -441,6 +475,36 @@ public class JavaTXCompiler {
TYPE ty = new TYPE(definedClasses, allClasses); TYPE ty = new TYPE(definedClasses, allClasses);
var cons = ty.getConstraints(); var cons = ty.getConstraints();
var ANDconstraints = cons.getUndConstraints();
var ORConstraints = cons.getOderConstraints();
var orIterator = ORConstraints.iterator();
while(orIterator.hasNext()){
Set<Constraint<Pair>> y = orIterator.next();
if (y.isEmpty()) orIterator.remove();
else if (y.size() == 1){
ANDconstraints.addAll(y.iterator().next()); // add the OR constraint to the AND constraint since we only have one option
orIterator.remove();
}
}
String dot = constraintSetToDot(cons);
ANDconstraints.forEach(System.out::println);
for (var orc : ORConstraints){
System.out.println();
System.out.println("------");
System.out.println();
for(var hashs : orc){
System.out.print(hashs);
System.out.print("\n | \n");
}
}
Set<Set<UnifyPair>> results = new HashSet<>(); Set<Set<UnifyPair>> results = new HashSet<>();
PlaceholderRegistry placeholderRegistry = new PlaceholderRegistry(); PlaceholderRegistry placeholderRegistry = new PlaceholderRegistry();
@@ -689,7 +753,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, ClassNotFoundException { private void parse(SourceFileContext tree, File sourceFile) throws IOException, java.lang.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>();
@@ -700,18 +764,8 @@ 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) {
@@ -722,11 +776,7 @@ public class JavaTXCompiler {
writeClassFile(classes, outputPath == null ? file.getParentFile() : outputPath, outputPath == null); writeClassFile(classes, outputPath == null ? file.getParentFile() : outputPath, outputPath == null);
} }
} else { } else {
var treeList = generateSources(sources); for (var file : sourceFiles.keySet()) {
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);
@@ -736,7 +786,7 @@ public class JavaTXCompiler {
} }
} }
private Map<JavaClassName, byte[]> generateBytecode(File sourceFile) throws ClassNotFoundException, IOException { public 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);
@@ -770,91 +820,11 @@ 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 target = converter.convert(clazz); var codegen = new Codegen(converter.convert(clazz), this, converter);
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,11 +58,14 @@ 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);
@@ -700,7 +700,7 @@ public class StatementGenerator {
case ConditionalassignexpressionContext condassign: case ConditionalassignexpressionContext condassign:
return convert(condassign); return convert(condassign);
default: default:
throw new NotImplementedException(expression.getClass().toString()); throw new NotImplementedException();
} }
} }
@@ -64,8 +64,6 @@ public class TypeGenerator {
switch (typeContext.primitiveType().getText()) { switch (typeContext.primitiveType().getText()) {
case "boolean": case "boolean":
return new RefType(ASTFactory.createClass(Boolean.class).getClassName(), typeContext.getStart()); return new RefType(ASTFactory.createClass(Boolean.class).getClassName(), typeContext.getStart());
case "char":
return new RefType(ASTFactory.createClass(Character.class).getClassName(), typeContext.getStart());
case "int": case "int":
return new RefType(ASTFactory.createClass(Integer.class).getClassName(), typeContext.getStart()); return new RefType(ASTFactory.createClass(Integer.class).getClassName(), typeContext.getStart());
case "double": case "double":
@@ -73,7 +71,7 @@ public class TypeGenerator {
case "float": case "float":
return new RefType(ASTFactory.createClass(Float.class).getClassName(), typeContext.getStart()); return new RefType(ASTFactory.createClass(Float.class).getClassName(), typeContext.getStart());
default: default:
throw new NotImplementedException(typeContext.primitiveType().getText()); throw new NotImplementedException();
} }
} else if (!typeContext.LBRACK().isEmpty()) { // ArrayType über eckige Klammer prüfen } else if (!typeContext.LBRACK().isEmpty()) { // ArrayType über eckige Klammer prüfen
// JavaTXParser.logger.info(unannTypeContext.getText()); // JavaTXParser.logger.info(unannTypeContext.getText());
@@ -229,6 +229,9 @@ public class ASTFactory {
public static Method createMethod(java.lang.reflect.Method jreMethod, String signature, java.lang.Class inClass, Boolean isInherited, Boolean isImplemented) { public static Method createMethod(java.lang.reflect.Method jreMethod, String signature, java.lang.Class inClass, Boolean isInherited, Boolean isImplemented) {
String name = jreMethod.getName(); String name = jreMethod.getName();
RefTypeOrTPHOrWildcardOrGeneric returnType; RefTypeOrTPHOrWildcardOrGeneric returnType;
if (inClass.getName().equals("Swap")){
System.out.println();
}
Type jreRetType; Type jreRetType;
if (jreMethod.getGenericReturnType() != null) { if (jreMethod.getGenericReturnType() != null) {
jreRetType = jreMethod.getGenericReturnType(); jreRetType = jreMethod.getGenericReturnType();
@@ -28,7 +28,6 @@ public class MethodCall extends Statement
//sind Tphs, repraesentieren im Resultset die Signatur der aufgerufenen Methoden, letztes Element ist der Returntyp //sind Tphs, repraesentieren im Resultset die Signatur der aufgerufenen Methoden, letztes Element ist der Returntyp
public final ArrayList<TypePlaceholder> signature; public final ArrayList<TypePlaceholder> signature;
public int modifiers;
public MethodCall(RefTypeOrTPHOrWildcardOrGeneric retType, Receiver receiver, String methodName, ArgumentList argumentList, public MethodCall(RefTypeOrTPHOrWildcardOrGeneric retType, Receiver receiver, String methodName, ArgumentList argumentList,
RefTypeOrTPHOrWildcardOrGeneric receiverType, ArrayList<TypePlaceholder> signature, Token offset){ RefTypeOrTPHOrWildcardOrGeneric receiverType, ArrayList<TypePlaceholder> signature, Token offset){
@@ -4,7 +4,6 @@ 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.*;
@@ -12,20 +11,17 @@ 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.target.Target; import de.dhbwstuttgart.syntaxtree.visual.ASTPrinter;
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 javax.swing.text.html.Option; import java.lang.annotation.Target;
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
@@ -65,14 +61,10 @@ public class ASTToTargetAST {
return converter.result; return converter.result;
} }
public record Generics(IGenerics javaGenerics, IGenerics txGenerics) { public record Generics(JavaGenerics javaGenerics, TxGenerics 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));
} }
public static Generics nullGenerics() {
return new Generics(null, new ResultSet(Set.of()));
}
} }
public IByteArrayClassLoader classLoader; public IByteArrayClassLoader classLoader;
@@ -102,15 +94,10 @@ public class ASTToTargetAST {
tphsInMethods.put(currentMethod, set); tphsInMethods.put(currentMethod, set);
} }
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, JavaTXCompiler compiler) {
return findMethod(owner, name, argumentList, Generics.nullGenerics().javaGenerics(), compiler);
}
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) { public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) {
Optional<Method> method = Optional.empty(); Optional<Method> method = Optional.empty();
while (method.isEmpty()) { while (method.isEmpty()) {
method = owner.getMethods().stream().filter(m -> m.name.equals(name) && method = owner.getMethods().stream().filter(m -> m.name.equals(name) && parameterEquals(m.getParameterList(), argumentList, generics)).findFirst();
parameterEquals(m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
if (owner.getClassName().toString().equals("java.lang.Object")) break; if (owner.getClassName().toString().equals("java.lang.Object")) break;
owner = compiler.getClass(owner.getSuperClass().getName()); owner = compiler.getClass(owner.getSuperClass().getName());
} }
@@ -118,16 +105,16 @@ public class ASTToTargetAST {
} }
Optional<Constructor> findConstructor(ClassOrInterface owner, List<TargetType> argumentList, IGenerics generics) { Optional<Constructor> findConstructor(ClassOrInterface owner, List<TargetType> argumentList, IGenerics generics) {
return owner.getConstructors().stream().filter(c -> return owner.getConstructors().stream().filter(c -> parameterEquals(c.getParameterList(), argumentList, generics)).findFirst();
parameterEquals(c.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
} }
static boolean parameterEquals(List<TargetType> pars, List<TargetType> arguments) { static boolean parameterEquals(ParameterList parameterList, List<TargetType> arguments, IGenerics generics) {
var pars = parameterList.getFormalparalist();
if (pars.size() != arguments.size()) if (pars.size() != arguments.size())
return false; return false;
for (var i = 0; i < pars.size(); i++) { for (var i = 0; i < pars.size(); i++) {
var type1 = pars.get(i); var type1 = generics.getTargetType(pars.get(i).getType());
var type2 = arguments.get(i); var type2 = arguments.get(i);
if (type1 instanceof TargetGenericType) if (type1 instanceof TargetGenericType)
return true; return true;
@@ -160,182 +147,190 @@ public class ASTToTargetAST {
return ret; return ret;
} }
private static UnifyType toUnifyType(TargetType a) { // This finds a common sealed interface type to group together methods that use different records
return switch(a) { // This function should do matching or unification
case TargetExtendsWildcard targetExtendsWildcard -> new ExtendsType(toUnifyType(targetExtendsWildcard.innerType())); private List<ClassOrInterface> commonSuperInterfaceTypes(TargetType a, TargetType b) {
case TargetSuperWildcard targetSuperWildcard -> new SuperType(toUnifyType(targetSuperWildcard.innerType())); if (a instanceof TargetGenericType && b instanceof TargetGenericType) return List.of(ASTFactory.createObjectClass());
case TargetGenericType targetGenericType -> new PlaceholderType(targetGenericType.name(), JavaTXCompiler.defaultClientPlaceholderRegistry); if (a instanceof TargetRefType ta && b instanceof TargetGenericType)
case TargetPrimitiveType _ -> throw new NotImplementedException(); return List.of(compiler.getClass(new JavaClassName(ta.name())));
case TargetFunNType targetFunNType -> FunNType.getFunNType(new TypeParams(targetFunNType.params().stream().map(ASTToTargetAST::toUnifyType).toList())); if (b instanceof TargetRefType tb && a instanceof TargetGenericType)
case TargetRefType targetRefType -> new ReferenceType(targetRefType.name(), new TypeParams(targetRefType.params().stream().map(ASTToTargetAST::toUnifyType).toList())); return List.of(compiler.getClass(new JavaClassName(tb.name())));
};
}
private static TargetType toTargetType(UnifyType a) {
return switch (a) {
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); // 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();
};
}
// TODO This should be moved elsewhere
private static boolean typesStrictlyEqual(TargetType a, TargetType b) {
if (a instanceof TargetRefType ta && b instanceof TargetRefType tb) { if (a instanceof TargetRefType ta && b instanceof TargetRefType tb) {
if (!Objects.equals(ta.name(), tb.name())) return false; var res = new HashSet<ClassOrInterface>();
if (ta.params().size() != tb.params().size()) return false;
for (var i = 0; i < ta.params().size(); i++) { var cla = compiler.getClass(new JavaClassName(ta.name()));
if (!typesStrictlyEqual(ta.params().get(i), tb.params().get(i))) var clb = compiler.getClass(new JavaClassName(tb.name()));
return false;
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 true; return res.stream().toList();
} }
// Generics are considered equal return List.of();
if (a instanceof TargetGenericType && b instanceof TargetGenericType)
return true;
return Objects.equals(a, b);
} }
private static Optional<TargetType> unify(TargetType a, TargetType b) { private boolean canCombine(Signature m1, Signature m2) {
if (typesStrictlyEqual(a, b)) return Optional.of(a); var pl1 = m1.java.parameters();
var unify = new MartelliMontanariUnify(); var pl2 = m2.java.parameters();
var ua = toUnifyType(a); if (pl1.size() != pl2.size()) return false;
var unifier = unify.unify(Set.of(ua, toUnifyType(b))); if (pl1.isEmpty()) return false;
if (unifier.isEmpty()) return Optional.empty(); for (var i = 0; i < pl1.size(); i++) {
return Optional.of(toTargetType(unifier.get().apply(ua))); var p1 = pl1.get(i).pattern();
var p2 = pl2.get(i).pattern();
// 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 &&
pc1.type().equals(pc2.type())) continue;
if (!p1.equals(p2)) return false;
}
return true;
} }
private static Optional<List<MethodParameter>> unify(List<MethodParameter> a, List<MethodParameter> b) { private record Combination(MethodWithTphs a, MethodWithTphs b) {
if (a.size() != b.size()) return Optional.empty(); @Override
var result = new ArrayList<MethodParameter>(); public boolean equals(Object o) {
for (var i = 0; i < a.size(); i++) { if (!(o instanceof Combination(MethodWithTphs a1, MethodWithTphs b1))) return false;
var u = unify(a.get(i).pattern().type(), b.get(i).pattern().type()); return this.a.equals(a1) && this.b.equals(b1) ||
if (u.isEmpty()) return Optional.empty(); this.a.equals(b1) && this.b.equals(a1);
// 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); @Override
} public int hashCode() {
return Objects.hashCode(a) + Objects.hashCode(b);
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 static Optional<TargetMethod.Signature> unify(TargetMethod.Signature a, TargetMethod.Signature b) { private List<List<MethodWithTphs>> groupOverloads(ClassOrInterface input, List<Method> methods) {
var returnType = unify(a.returnType(), b.returnType()); var mapOfTargetMethods = new HashMap<Generics, MethodWithTphs[]>();
if (returnType.isEmpty()) return Optional.empty(); for (var gen : all) {
var res = unify(a.parameters(), b.parameters()); mapOfTargetMethods.put(gen, new MethodWithTphs[methods.size()]);
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);
}
private static Optional<MethodGroup> unify(MethodGroup a, MethodGroup b) {
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(); for (var i = 0; i < methods.size(); i++) {
} var method = methods.get(i);
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 mtph = new MethodWithTphs(m.method, m.generics, m.signature); var resultMethods = mapOfTargetMethods.get(m.generics);
a.add(new MethodGroup(m.signature, Set.of(mtph))); resultMethods[i] = new MethodWithTphs(m.method, m.generics, m.signature);
} }
} }
System.out.println("============== INPUT =============="); System.out.println("============== INPUT ==============");
for (var m : a) { for (var m : mapOfTargetMethods.values()) {
System.out.println(m); for (var v : m) if (v != null) System.out.println(v.signature.java.returnType() + " " + v.method.name + " " + v.signature.java().parameters());
System.out.println();
} }
// Algorithm var allCombinations = new HashSet<Set<Combination>>();
// 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;
var R = new HashSet<MethodGroup>(); for (var g2 : all) {
var i = new HashSet<>(a); if (g1 == g2) continue; // No need to combine the same method
var resMeth2 = mapOfTargetMethods.get(g2);
var m2 = resMeth2[i];
if (m2 == null) continue;
while (!i.isEmpty()) { var combinations = new HashSet<Combination>();
var m = i.iterator().next();
i.remove(m); if (canCombine(m1.signature, m2.signature)) {
R.add(m); //System.out.println(" Combining " + m1.signature.java.getSignature() + " and " + m2.signature.java.getSignature());
var a1 = new HashSet<>(a); combinations.add(new Combination(m1, m2));
for (var m1 : a1) { for (var j = 0; j < methods.size(); j++) {
if (m1 != m) { if (j == i) continue;
var u_opt = unify(m, m1); var m3 = resMeth2[j];
if (u_opt.isPresent()) { if (m3 == null) continue;
var u = u_opt.get(); var m4 = resMeth1[j];
//System.out.println("Unified " + m + " AND " + m1 + "\n\t" + u); if (m4 == null) continue;
i.remove(m1); combinations.add(new Combination(m4, m3));
R.remove(m); //System.out.println("Also Combining " + m4.signature.java.getSignature() + " and " + m3.signature.java.getSignature());
R.remove(m1); }
R.add(u); } else {
a.add(u); //System.out.println(" Not Combining " + m1.signature.java.getSignature() + " and " + m2.signature.java.getSignature());
} /*else { }
System.out.println("Couldn't unify " + m + " AND " + m1); if (!combinations.isEmpty()) allCombinations.add(combinations);
}*/
} }
} }
} }
System.out.println("============== OUTPUT =============="); if (allCombinations.isEmpty()) allCombinations.add(new HashSet<>());
for (var mg : R) {
System.out.println(mg.methods.size() + " " + mg); // Combine back into output format
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);
}
} }
return R; var result = r0.stream().map(l -> l.stream().toList()).toList();
//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) {
@@ -369,48 +364,25 @@ 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 m0 = groupMethods(input, input.getMethods()); var methods = groupOverloads(input, input.getMethods()).stream().map(m -> generatePatternOverloads(input, m)).flatMap(List::stream)
.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); staticConstructor = this.convert(init, init.generics.javaGenerics);
} }
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);
} }
@Deprecated
public List<MethodParameter> convert(ParameterList input) { public List<MethodParameter> convert(ParameterList input) {
var res = new ArrayList<MethodParameter>(); return convert(input, all.getFirst().javaGenerics);
for (var i = 0; i < input.getFormalparalist().size(); i++) {
var param = input.getFormalparalist().get(i);
var pattern = (TargetPattern) convert(param, Generics.nullGenerics().javaGenerics);
if (pattern instanceof TargetComplexPattern) pattern = pattern.withName("__var" + i);
res.add(new MethodParameter(pattern));
}
return res;
} }
public List<MethodParameter> convert(ParameterList input, IGenerics generics) { public List<MethodParameter> convert(ParameterList input, IGenerics generics) {
@@ -494,31 +466,34 @@ public class ASTToTargetAST {
} }
} }
private static TargetExpression generateCall(TargetMethod method, List<TargetLocalVar> params, TargetType classType) { private TargetType unwrap(TargetType type) {
var mParams = new ArrayList<TargetExpression>(); if (type instanceof TargetRefType ref) {
for (var i = 0; i < params.size(); i++) { if (!ref.params().isEmpty()) return new TargetRefType(ref.name());
var tpe = method.signature().parameters().get(i).pattern().type();
mParams.add(new TargetLocalVar(tpe, params.get(i).name()));
} }
TargetExpression caseBody = new TargetMethodCall( 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>();
for (var i = 0; i < params.size(); i++) {
var tpe = method.signature().parameters().get(i).pattern().type();
mParams.add(new TargetLocalVar(tpe, params.get(i).name()));
}
TargetExpression caseBody = new TargetMethodCall(
method.signature().returnType(), method.signature().returnType(),
new TargetThis(classType), new TargetThis(classType),
mParams, mParams,
classType, classType,
method.name(), method.name(),
false, false, method.isPrivate() false, false, method.isPrivate()
); );
if (method.signature().returnType() != null) { if (method.signature().returnType() != null) {
caseBody = new TargetReturn(caseBody); caseBody = new TargetReturn(caseBody);
} }
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>();
@@ -552,130 +527,53 @@ public class ASTToTargetAST {
if (!patternsRec.get(j).equals(param.pattern())) return false; if (!patternsRec.get(j).equals(param.pattern())) return false;
j++; j++;
} }
return false; return true;
}).toList(); }).toList();
if (candidates.isEmpty()) { //System.out.println(offset + " -> " + lastPattern);
var body = new TargetBlock(List.of(generateCall(method, params, classType))); //candidates.forEach(m -> System.out.println(m.getSignature()));
cases.add(new TargetSwitch.Case(List.of(lastPattern), body)); var caseBody = generatePatternOverloadsRec(offset + 1, expr, params, patternsRec, candidates, classType);
} else { var body = new TargetBlock(List.of(caseBody));
//candidates.forEach(m -> System.out.println(m.getSignature())); var case_ = new TargetSwitch.Case(List.of(lastPattern), body);
var caseBody = generatePatternOverloadsRec(offset + 1, expr, params, patternsRec, candidates, classType);
var body = new TargetBlock(List.of(caseBody));
var case_ = new TargetSwitch.Case(List.of(lastPattern), body);
cases.add(case_); cases.add(case_);
}
} }
return new TargetSwitch(switchExpr, cases, null, true); return new TargetSwitch(switchExpr, cases, null, true);
} }
// TODO What happens if a record implements two sealed interfaces? private List<TargetMethod> generatePatternOverloads(ClassOrInterface clazz, List<MethodWithTphs> overloadedMethods) {
private Optional<TargetType> commonSealedInterface(TargetType a, TargetType b) {
if (!(a instanceof TargetRefType ar && b instanceof TargetRefType ab)) return Optional.empty();
var cla = compiler.getClass(new JavaClassName(ar.name()));
var clb = compiler.getClass(new JavaClassName(ab.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());
if (intersection.size() != 1) return Optional.empty();
return Optional.of(new TargetRefType(intersection.stream().findFirst().get().getName().toString()));
}
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();
// 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 ra = firstMethod.signature().returnType();
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
sortBySpecificity(overloadedMethods); var firstMethod = convert(overloadedMethods.getFirst(), overloadedMethods.getFirst().generics.javaGenerics);
var firstMethod = convert(overloadedMethods.getFirst());
if (overloadedMethods.size() == 1) return List.of(firstMethod); if (overloadedMethods.size() == 1) return List.of(firstMethod);
var signatureParams = group.signature.java.parameters(); var secondMethod = convert(overloadedMethods.get(1), overloadedMethods.get(1).generics.javaGenerics);
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) {
@@ -694,6 +592,12 @@ 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());
@@ -703,7 +607,9 @@ 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 bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, group.signature.java, group.signature.tx); var signature = new TargetMethod.Signature(Set.of(), signatureParams, returnType);
var bridgeMethod = new TargetMethod(firstMethod.access(), firstMethod.name(), block, signature, firstMethod.txSignature());
res.add(bridgeMethod); res.add(bridgeMethod);
return res; return res;
} }
@@ -749,15 +655,6 @@ 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) {
@@ -772,31 +669,14 @@ 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());
}
@Override private List<MethodWithTphs> convert(ClassOrInterface currentClass, Method method) {
public int hashCode() { List<MethodWithTphs> result = new ArrayList<>();
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<>();
@@ -821,7 +701,6 @@ 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());
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));
@@ -875,7 +754,7 @@ public class ASTToTargetAST {
for (var i = 0; i < tspec.params().size(); i++) { for (var i = 0; i < tspec.params().size(); i++) {
var param = tspec.params().get(i); var param = tspec.params().get(i);
if (param instanceof TargetSpecializedType fn) { if (param instanceof TargetSpecializedType fn) {
collectArguments(fn, newParams); collectArguments(tspec, newParams);
} else { } else {
newParams.add(param); newParams.add(param);
} }
@@ -216,10 +216,6 @@ public class StatementToTargetExpression implements ASTVisitor {
return ASTToTargetAST.findMethod(converter.compiler.getClass(className), name, args, generics, compiler); return ASTToTargetAST.findMethod(converter.compiler.getClass(className), name, args, generics, compiler);
} }
Optional<Method> findMethod(JavaClassName className, String name, List<TargetType> args, JavaTXCompiler compiler) {
return ASTToTargetAST.findMethod(converter.compiler.getClass(className), name, args, compiler);
}
@Override @Override
public void visit(MethodCall methodCall) { public void visit(MethodCall methodCall) {
var receiverType = converter.convert(methodCall.receiver.getType(), generics); var receiverType = converter.convert(methodCall.receiver.getType(), generics);
@@ -250,7 +246,7 @@ public class StatementToTargetExpression implements ASTVisitor {
} else if (!isFunNType) { } else if (!isFunNType) {
receiverClass = converter.compiler.getClass(receiverName); receiverClass = converter.compiler.getClass(receiverName);
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!"); if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow(); foundMethod = findMethod(receiverName, methodCall.name, signature, generics, converter.compiler).orElseThrow();
} }
if (!isFunNType) { if (!isFunNType) {
@@ -3,9 +3,4 @@ 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,13 +1,12 @@
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, List<TargetRefType> permittedSubtypes) implements TargetStructure { public record TargetInterface(int modifiers, JavaClassName qualifiedName, Set<TargetGeneric> generics, Set<TargetGeneric> txGenerics, List<TargetMethod> methods, List<TargetType> implementingInterfaces, TargetMethod staticConstructor) implements TargetStructure {
@Override @Override
public TargetType superType() { public TargetType superType() {
return null; return null;
@@ -85,11 +85,12 @@ public class Constraint<A extends IConstraintElement> extends HashSet<A> impleme
} }
public String toString() { public String toString() {
return super.toString() + "\nisInherited = " + isInherited return super.toString();
+ " isOveridden = " + isImplemented // + "\nisInherited = " + isInherited
+ " msc[" + methodSignatureConstraint.size() + "] = " + methodSignatureConstraint // + " isOveridden = " + isImplemented
//" + extendsContraint: " + (extendConstraint != null ? extendConstraint.toStringBase() : "null" ) // + " msc[" + methodSignatureConstraint.size() + "] = " + methodSignatureConstraint
+ "\n"; // //" + extendsContraint: " + (extendConstraint != null ? extendConstraint.toStringBase() : "null" )
// + "\n";
} }
public String toStringBase() { public String toStringBase() {
@@ -4,6 +4,7 @@ import de.dhbwstuttgart.server.packet.dataContainers.ISerializableData;
import de.dhbwstuttgart.server.packet.dataContainers.KeyStorage; import de.dhbwstuttgart.server.packet.dataContainers.KeyStorage;
import de.dhbwstuttgart.server.packet.dataContainers.serialized.SerialMap; import de.dhbwstuttgart.server.packet.dataContainers.serialized.SerialMap;
import de.dhbwstuttgart.typeinference.unify.UnifyContext; import de.dhbwstuttgart.typeinference.unify.UnifyContext;
import java.io.Serializable; import java.io.Serializable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -13,58 +14,60 @@ import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.typeinference.unify.model.PairOperator; import de.dhbwstuttgart.typeinference.unify.model.PairOperator;
import static de.dhbwstuttgart.core.JavaTXCompiler.q;
public class Pair implements Serializable, IConstraintElement, ISerializableData { public class Pair implements Serializable, IConstraintElement, ISerializableData {
public final RefTypeOrTPHOrWildcardOrGeneric TA1; public final RefTypeOrTPHOrWildcardOrGeneric TA1;
public final RefTypeOrTPHOrWildcardOrGeneric TA2; public final RefTypeOrTPHOrWildcardOrGeneric TA2;
private SourceLoc location; private SourceLoc location;
private PairOperator eOperator = PairOperator.SMALLER; private PairOperator eOperator = PairOperator.SMALLER;
private boolean noUnification = false; private boolean noUnification = false;
private Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2) { private Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2) {
this.TA1 = TA1; this.TA1 = TA1;
this.TA2 = TA2; this.TA2 = TA2;
if (TA1 == null || TA2 == null) if (TA1 == null || TA2 == null)
throw new NullPointerException(); throw new NullPointerException();
eOperator = PairOperator.SMALLER; eOperator = PairOperator.SMALLER;
} }
public Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2, PairOperator eOp) { public Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2, PairOperator eOp) {
// Konstruktor // Konstruktor
this(TA1, TA2); this(TA1, TA2);
this.eOperator = eOp; this.eOperator = eOp;
} }
public Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2, PairOperator e0p, SourceLoc location) { public Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2, PairOperator e0p, SourceLoc location) {
this(TA1, TA2, e0p); this(TA1, TA2, e0p);
this.location = location; this.location = location;
} }
public Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2, PairOperator eOp, boolean noUnification) { public Pair(RefTypeOrTPHOrWildcardOrGeneric TA1, RefTypeOrTPHOrWildcardOrGeneric TA2, PairOperator eOp, boolean noUnification) {
// Konstruktor // Konstruktor
this(TA1, TA2); this(TA1, TA2);
this.eOperator = eOp; this.eOperator = eOp;
this.noUnification = noUnification; this.noUnification = noUnification;
} }
public SourceLoc getLocation() { public SourceLoc getLocation() {
return this.location; return this.location;
} }
public String toString() { public String toString() {
// otth: Gibt ein Paar als String aus --> zum Debuggen und Vergleichen // otth: Gibt ein Paar als String aus --> zum Debuggen und Vergleichen
String strElement1 = "NULL"; String strElement1 = "NULL";
String strElement2 = "NULL"; String strElement2 = "NULL";
String Operator = "<."; String Operator = "<.";
if (TA1 != null) if (TA1 != null)
strElement1 = TA1.toString(); strElement1 = TA1.toString();
if (TA2 != null) if (TA2 != null)
strElement2 = TA2.toString(); strElement2 = TA2.toString();
/* PL ausskommentiert 2018-05-24 /* PL ausskommentiert 2018-05-24
if(OperatorEqual()) if(OperatorEqual())
@@ -75,103 +78,125 @@ public class Pair implements Serializable, IConstraintElement, ISerializableData
Operator = "<?"; Operator = "<?";
*/ */
return "\n(P: " + strElement1 + " " + eOperator.toString() + " " + strElement2 + ")"; return "\n(P: " + strElement1 + " " + eOperator.toString() + " " + strElement2 + ")";
/*- Equals: " + bEqual*/ /*- Equals: " + bEqual*/
} }
/** /**
* <br/>Author: ¯Â¿Â½rg ¯Â¿Â½uerle * <br/>Author: ¯Â¿Â½rg ¯Â¿Â½uerle
* *
* @param obj * @param obj
* @return * @return
*/ */
public boolean equals(Object obj) { public boolean equals(Object obj) {
return ( return (
(obj instanceof Pair pairObj) && (obj instanceof Pair pairObj) &&
pairObj.TA1.equals(this.TA1) && pairObj.TA1.equals(this.TA1) &&
pairObj.TA2.equals(this.TA2) pairObj.TA2.equals(this.TA2)
); );
} }
/** /**
* Author: Arne ¼dtke<br/> * Author: Arne ¼dtke<br/>
* Abfrage, ob Operator vom Typ Equal ist. * Abfrage, ob Operator vom Typ Equal ist.
*/ */
public boolean OperatorEqual() { public boolean OperatorEqual() {
return eOperator == PairOperator.EQUALSDOT; return eOperator == PairOperator.EQUALSDOT;
} }
/** /**
* Author: Arne ¼dtke<br/> * Author: Arne ¼dtke<br/>
* Abfrage, ob Operator vom Typ Smaller ist. * Abfrage, ob Operator vom Typ Smaller ist.
*/ */
public boolean OperatorSmaller() { public boolean OperatorSmaller() {
return eOperator == PairOperator.SMALLER; return eOperator == PairOperator.SMALLER;
} }
/** /**
* Author: Arne ¼dtke<br/> * Author: Arne ¼dtke<br/>
* Abfrage, ob Operator vom Typ SmallerExtends ist. * Abfrage, ob Operator vom Typ SmallerExtends ist.
*/ */
public boolean OperatorSmallerExtends() { public boolean OperatorSmallerExtends() {
return eOperator == PairOperator.SMALLERDOTWC; return eOperator == PairOperator.SMALLERDOTWC;
} }
/** /**
* Author: Arne ¼dtke<br/> * Author: Arne ¼dtke<br/>
* Gibt den Operator zurück. * Gibt den Operator zurück.
*/ */
public PairOperator GetOperator() { public PairOperator GetOperator() {
return eOperator; return eOperator;
} }
public boolean OperatorSmallerDot() { public boolean OperatorSmallerDot() {
return eOperator == PairOperator.SMALLERDOT; return eOperator == PairOperator.SMALLERDOT;
} }
static public Map<String, TypePlaceholder> generateTPHMap(ConstraintSet<Pair> constraints) { static public Map<String, TypePlaceholder> generateTPHMap(ConstraintSet<Pair> constraints) {
HashMap<String, TypePlaceholder> ret = new HashMap<>(); HashMap<String, TypePlaceholder> ret = new HashMap<>();
constraints.map((Pair p) -> { constraints.map((Pair p) -> {
if (p.TA1 instanceof TypePlaceholder) { if (p.TA1 instanceof TypePlaceholder) {
ret.put(((TypePlaceholder) p.TA1).getName(), (TypePlaceholder) p.TA1); ret.put(((TypePlaceholder) p.TA1).getName(), (TypePlaceholder) p.TA1);
} }
if (p.TA2 instanceof TypePlaceholder) { if (p.TA2 instanceof TypePlaceholder) {
ret.put(((TypePlaceholder) p.TA2).getName(), (TypePlaceholder) p.TA2); ret.put(((TypePlaceholder) p.TA2).getName(), (TypePlaceholder) p.TA2);
} }
return null; return null;
}); });
return ret; return ret;
} }
@Override @Override
public SerialMap toSerial(KeyStorage keyStorage) { public SerialMap toSerial(KeyStorage keyStorage) {
// because toString() will output TA1 and TA2 recursively, we can ignore potential infinite recursion here too // because toString() will output TA1 and TA2 recursively, we can ignore potential infinite recursion here too
SerialMap serialized = new SerialMap(); SerialMap serialized = new SerialMap();
serialized.put("ta1", this.TA1.toSerial(keyStorage)); serialized.put("ta1", this.TA1.toSerial(keyStorage));
serialized.put("ta2", this.TA2.toSerial(keyStorage)); serialized.put("ta2", this.TA2.toSerial(keyStorage));
serialized.put("op", this.eOperator.toString()); serialized.put("op", this.eOperator.toString());
serialized.put("noUnification", this.noUnification ? 1 : 0); serialized.put("noUnification", this.noUnification ? 1 : 0);
serialized.put("location", this.location == null ? null : this.location.toSerial(keyStorage)); serialized.put("location", this.location == null ? null : this.location.toSerial(keyStorage));
return serialized; return serialized;
} }
public static Pair fromSerial(SerialMap data, UnifyContext context) { public static Pair fromSerial(SerialMap data, UnifyContext context) {
String op = data.getValue("op").getOf(String.class); String op = data.getValue("op").getOf(String.class);
SerialMap ta1 = data.getMap("ta1"); SerialMap ta1 = data.getMap("ta1");
SerialMap ta2 = data.getMap("ta2"); SerialMap ta2 = data.getMap("ta2");
boolean noUnification = data.getValue("noUnification").getOf(Integer.class) == 1; boolean noUnification = data.getValue("noUnification").getOf(Integer.class) == 1;
SerialMap location = data.getMapOrNull("location"); SerialMap location = data.getMapOrNull("location");
var pair = new Pair( var pair = new Pair(
RefTypeOrTPHOrWildcardOrGeneric.fromSerial(ta1, context), RefTypeOrTPHOrWildcardOrGeneric.fromSerial(ta1, context),
RefTypeOrTPHOrWildcardOrGeneric.fromSerial(ta2, context), RefTypeOrTPHOrWildcardOrGeneric.fromSerial(ta2, context),
PairOperator.fromString(op), PairOperator.fromString(op),
noUnification noUnification
); );
if (location != null) pair.location = SourceLoc.fromSerial(location); if (location != null) pair.location = SourceLoc.fromSerial(location);
return pair; return pair;
} }
public String toDot(String color) {
return q(this.TA1.toString()) +
" -> " +
q(this.TA2.toString()) +
" [label=" +
q(this.GetOperator().toString()) +
",color=" + q(color) + "]" +
";\n";
}
public String toDot() {
return q(this.TA1.toString()) +
" -> " +
q(this.TA2.toString()) +
" [label=" +
q(this.GetOperator().toString()) +
"]" +
";\n";
}
} }
// ino.end // ino.end
@@ -35,8 +35,8 @@ public class TYPE {
this.definedClasses = definedClasses; this.definedClasses = definedClasses;
} }
public ConstraintSet getConstraints() { public ConstraintSet<Pair> getConstraints() {
ConstraintSet ret = new ConstraintSet(); ConstraintSet<Pair> ret = new ConstraintSet<>();
for (ClassOrInterface cl : definedClasses) { for (ClassOrInterface cl : definedClasses) {
Set<ClassOrInterface> allClasses = TypeUnifyTaskHelper.getPresizedHashSet(allAvailableClasses.size()); Set<ClassOrInterface> allClasses = TypeUnifyTaskHelper.getPresizedHashSet(allAvailableClasses.size());
allClasses.addAll(allAvailableClasses); allClasses.addAll(allAvailableClasses);
@@ -45,9 +45,9 @@ public class TYPE {
return ret; return ret;
} }
private ConstraintSet getConstraintsClass(ClassOrInterface cl, TypeInferenceInformation info) { private ConstraintSet<Pair> getConstraintsClass(ClassOrInterface cl, TypeInferenceInformation info) {
ConstraintSet ret = new ConstraintSet(); ConstraintSet<Pair> ret = new ConstraintSet<>();
ConstraintSet methConstrains; ConstraintSet<Pair> methConstrains;
for(Method m : cl.getMethods()){ for(Method m : cl.getMethods()){
ret.addAll(methConstrains = getConstraintsMethod(m,info, cl)); ret.addAll(methConstrains = getConstraintsMethod(m,info, cl));
m.constraints.addAll(methConstrains); m.constraints.addAll(methConstrains);
@@ -86,11 +86,11 @@ public class TYPE {
} }
*/ */
private ConstraintSet getConstraintsMethod(Method m, TypeInferenceInformation info, ClassOrInterface currentClass) { private ConstraintSet<Pair> getConstraintsMethod(Method m, TypeInferenceInformation info, ClassOrInterface currentClass) {
if(m.block == null)return new ConstraintSet(); //Abstrakte Methoden generieren keine Constraints if(m.block == null)return new ConstraintSet<Pair>(); //Abstrakte Methoden generieren keine Constraints
TypeInferenceBlockInformation blockInfo = new TypeInferenceBlockInformation(info.getAvailableClasses(), currentClass, m); TypeInferenceBlockInformation blockInfo = new TypeInferenceBlockInformation(info.getAvailableClasses(), currentClass, m);
TYPEStmt methodScope = new TYPEStmt(blockInfo); TYPEStmt methodScope = new TYPEStmt(blockInfo);
ConstraintSet constraintSet = new ConstraintSet(); ConstraintSet<Pair> constraintSet = new ConstraintSet<>();
if (m.name.equals("main") && Modifier.isStatic(m.modifier) && m.getParameterList().getFormalparalist().size() == 1) { if (m.name.equals("main") && Modifier.isStatic(m.modifier) && m.getParameterList().getFormalparalist().size() == 1) {
// Add constraint for main method // Add constraint for main method
+2 -44
View File
@@ -953,48 +953,20 @@ 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", List, List); var append = clazz.getDeclaredMethod("append", Cons, Cons);
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 = Logger.LogLevel.DEBUG;
var classFiles = generateClassFiles(createClassLoader(), "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
public void testPatternMatchingZipJava() throws Exception { public void testPatternMatchingZipJava() throws Exception {
ConsoleInterface.logLevel = Logger.LogLevel.DEBUG;
var classFiles = generateClassFiles(createClassLoader(), false, "PatternMatchingJava.jav", "PatternMatchingJava2.jav"); var classFiles = generateClassFiles(createClassLoader(), false, "PatternMatchingJava.jav", "PatternMatchingJava2.jav");
var clazz = classFiles.get("PatternMatchingJava"); var clazz = classFiles.get("PatternMatchingJava");
var instance = clazz.getDeclaredConstructor().newInstance(); var instance = clazz.getDeclaredConstructor().newInstance();
@@ -1564,24 +1536,10 @@ public class TestComplete {
// TODO This logs output that we should validate // TODO This logs output that we should validate
} }
@Test
public void testBug389() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), false, "Bug389.jav", "Bug389Main.jav");
var clazz = classFiles.get("Bug389Main");
clazz.getDeclaredMethod("main", List.class).invoke(null, List.of());
}
@Test @Test
public void testBug390() throws Exception { public void testBug390() throws Exception {
var classFiles = generateClassFiles(createClassLoader(), "Bug390.jav"); var classFiles = generateClassFiles(createClassLoader(), "Bug390.jav");
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());
}
} }
+4 -1
View File
@@ -19,11 +19,14 @@ 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,7 +59,6 @@ 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,7 +49,6 @@ 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);