Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c0d653d2 | ||
|
|
f7e5a1f8a2 | ||
|
|
8f6e6e1980 | ||
|
|
912f3d381e | ||
|
|
7e24bbd552 | ||
|
|
50f2572644 | ||
|
|
e1518c8b37 | ||
|
|
f7a85db191 | ||
|
|
41d5f661e1 | ||
|
|
b7f46c428f |
@@ -1,11 +1,9 @@
|
|||||||
import java.lang.String;
|
import java.lang.String;
|
||||||
import java.lang.Integer;
|
import java.lang.Integer;
|
||||||
|
|
||||||
sealed interface List permits LinkedElem, Elem {}
|
sealed interface List<T> permits LinkedElem, Elem {}
|
||||||
|
public record LinkedElem<T>(T a, List<T> l) implements List<T>{}
|
||||||
|
public record Elem<T>(T c) implements List<T>{}
|
||||||
public record LinkedElem<T>(T a,List l) implements List{}
|
|
||||||
public record Elem<T>(T c) implements List{}
|
|
||||||
|
|
||||||
public class GenericRecordSwitchCase {
|
public class GenericRecordSwitchCase {
|
||||||
public main(o) {
|
public main(o) {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import java.lang.Object;
|
|||||||
|
|
||||||
public class HelloWorld {
|
public class HelloWorld {
|
||||||
public static hello() {
|
public static hello() {
|
||||||
System.out.println((Object)"Hello World!");
|
System.out.println("Hello World!");
|
||||||
|
System.out.println("Bye World!");
|
||||||
|
System.out.println("The end!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,25 @@
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.lang.String;
|
import java.lang.Integer;
|
||||||
|
|
||||||
public class Sorting{
|
public class Sorting {
|
||||||
merge(a, b){
|
List<Integer> merge(List<Integer> a, List<Integer> b) {
|
||||||
a.addAll(b);
|
var r = new ArrayList<>();
|
||||||
return a;
|
for (var i = 0, j = 0; i < a.size() || j < b.size();)
|
||||||
|
if (j == b.size() || (i < a.size() && a.get(i) <= b.get(j)))
|
||||||
|
r.add(a.get(i++));
|
||||||
|
else r.add(b.get(j++));
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
sort(in){
|
split(list) {
|
||||||
var firstHalf = in;
|
var mid = list.size() / 2;
|
||||||
var secondHalf = in;
|
return List.of(list.subList(0, mid), list.subList(mid, list.size()));
|
||||||
return merge(sort(firstHalf), sort(secondHalf));
|
}
|
||||||
}
|
|
||||||
|
public sort(in) {
|
||||||
|
if (in.size() <= 1) return in;
|
||||||
/*
|
var halves = split(in);
|
||||||
void sort(a){
|
return merge(sort(halves.get(0)), sort(halves.get(1)));
|
||||||
a = merge(a,a);
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -350,6 +350,8 @@ public class Codegen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TargetType largerType(TargetType left, TargetType right) {
|
private TargetType largerType(TargetType left, TargetType right) {
|
||||||
|
if (left instanceof TargetExtendsWildcard wc) left = wc.innerType();
|
||||||
|
if (right instanceof TargetExtendsWildcard wc) right = wc.innerType();
|
||||||
if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
|
if (left.equals(TargetType.String) || right.equals(TargetType.String)) {
|
||||||
return TargetType.String;
|
return TargetType.String;
|
||||||
} else if (left.equals(TargetType.Double) || right.equals(TargetType.Double)) {
|
} else if (left.equals(TargetType.Double) || right.equals(TargetType.Double)) {
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package de.dhbwstuttgart.exceptions;
|
|||||||
public class DebugException extends RuntimeException {
|
public class DebugException extends RuntimeException {
|
||||||
|
|
||||||
public DebugException(String message) {
|
public DebugException(String message) {
|
||||||
System.err.print(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -482,8 +482,8 @@ public class StatementGenerator {
|
|||||||
IdentifierContext identifierCtx = recordPatternCtx.identifier();
|
IdentifierContext identifierCtx = recordPatternCtx.identifier();
|
||||||
var text = (identifierCtx != null) ? identifierCtx.getText() : null;
|
var text = (identifierCtx != null) ? identifierCtx.getText() : null;
|
||||||
//Hier evtl. Typ anpassen -> wenn kein Typ bekannt ist push neuen Typ auf Hashtable
|
//Hier evtl. Typ anpassen -> wenn kein Typ bekannt ist push neuen Typ auf Hashtable
|
||||||
var type = recordPatternCtx.type == null ? TypePlaceholder.fresh(recordPatternCtx.getStart()) : TypeGenerator.convert(recordPatternCtx.type, reg, generics);
|
|
||||||
var ctor = TypeGenerator.convert(recordPatternCtx.ctor, reg, generics);
|
var ctor = TypeGenerator.convert(recordPatternCtx.ctor, reg, generics);
|
||||||
|
var type = recordPatternCtx.type == null ? ctor : TypeGenerator.convert(recordPatternCtx.type, reg, generics);
|
||||||
if (text != null) localVars.put(text, type);
|
if (text != null) localVars.put(text, type);
|
||||||
var ret = new RecordPattern(subPattern, text, type, (RefType)ctor, recordPatternCtx.getStart());
|
var ret = new RecordPattern(subPattern, text, type, (RefType)ctor, recordPatternCtx.getStart());
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@@ -115,8 +115,10 @@ public class ASTToTargetAST {
|
|||||||
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) {
|
public static Optional<Method> findMethod(ClassOrInterface owner, String name, List<TargetType> argumentList, IGenerics generics, JavaTXCompiler compiler) {
|
||||||
Optional<Method> method = Optional.empty();
|
Optional<Method> method = Optional.empty();
|
||||||
while (method.isEmpty()) {
|
while (method.isEmpty()) {
|
||||||
method = owner.getMethods().stream().filter(m -> m.name.equals(name) &&
|
method = owner.getMethods().stream().filter(m -> {
|
||||||
parameterEquals(m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList)).findFirst();
|
return m.name.equals(name) &&
|
||||||
|
parameterEquals(m.getParameterList().getFormalparalist().stream().map(p -> generics.getTargetType(p.getType())).toList(), argumentList);
|
||||||
|
}).findFirst();
|
||||||
if (owner.getClassName().toString().equals("java.lang.Object")) break;
|
if (owner.getClassName().toString().equals("java.lang.Object")) break;
|
||||||
owner = compiler.getClass(owner.getSuperClass().getName());
|
owner = compiler.getClass(owner.getSuperClass().getName());
|
||||||
}
|
}
|
||||||
@@ -331,15 +333,16 @@ public class ASTToTargetAST {
|
|||||||
var u_opt = unify(m, m1);
|
var u_opt = unify(m, m1);
|
||||||
if (u_opt.isPresent()) {
|
if (u_opt.isPresent()) {
|
||||||
var u = u_opt.get();
|
var u = u_opt.get();
|
||||||
//Target.logger.info("Unified " + m + " AND " + m1 + "\n\t" + u);
|
//System.out.println("Unified " + m + " AND " + m1 + "\n\t" + u);
|
||||||
i.remove(m1);
|
i.remove(m1);
|
||||||
R.remove(m);
|
R.remove(m);
|
||||||
R.remove(m1);
|
R.remove(m1);
|
||||||
R.add(u);
|
R.add(u);
|
||||||
a.add(u);
|
a.add(u);
|
||||||
} /*else {
|
m = u;
|
||||||
Target.logger.info("Couldn't unify " + m + " AND " + m1);
|
} else {
|
||||||
}*/
|
//System.out.println("Couldn't unify " + m + " AND " + m1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -381,8 +384,21 @@ public class ASTToTargetAST {
|
|||||||
TargetBlock finalFieldInitializer = fieldInitializer;
|
TargetBlock finalFieldInitializer = fieldInitializer;
|
||||||
|
|
||||||
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).toList();
|
var superInterfaces = input.getSuperInterfaces().stream().map(clazz -> convert(clazz, generics.javaGenerics, compiler)).toList();
|
||||||
var constructors = input.getConstructors().stream().map(constructor -> this.convert(input, constructor, finalFieldInitializer, generics)).flatMap(List::stream).toList();
|
var constructors = input.getConstructors().stream().map(constructor -> {
|
||||||
var fields = input.getFieldDecl().stream().map(f -> convert(f, generics.javaGenerics)).toList();
|
try {
|
||||||
|
return this.convert(input, constructor, finalFieldInitializer, generics);
|
||||||
|
} catch (DiscardResultSet discard) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).filter(Objects::nonNull).flatMap(List::stream).toList();
|
||||||
|
var fields = input.getFieldDecl().stream().map(f -> {
|
||||||
|
var possibleTypes = new HashSet<TargetType>();
|
||||||
|
for (var g : all) possibleTypes.add(convert(f.getType(), g.javaGenerics(), compiler));
|
||||||
|
if (possibleTypes.size() > 1)
|
||||||
|
compiler.warn(new CompilerWarning(f.getOffset(), "Multiple possible types for field " + f.getName() + ": " + possibleTypes + " please select one"));
|
||||||
|
return convert(f, generics.javaGenerics);
|
||||||
|
}).toList();
|
||||||
var m0 = groupMethods(input, input.getMethods());
|
var m0 = groupMethods(input, input.getMethods());
|
||||||
|
|
||||||
var m1 = new ArrayList<TargetMethod>();
|
var m1 = new ArrayList<TargetMethod>();
|
||||||
@@ -851,7 +867,13 @@ public class ASTToTargetAST {
|
|||||||
for (var tph : tphs) {
|
for (var tph : tphs) {
|
||||||
var left = a.getTargetType(tph);
|
var left = a.getTargetType(tph);
|
||||||
var right = b.getTargetType(tph);
|
var right = b.getTargetType(tph);
|
||||||
if (!Objects.equals(left, right)) return true;
|
if (left instanceof TargetExtendsWildcard wc) left = wc.innerType();
|
||||||
|
if (right instanceof TargetExtendsWildcard wc) right = wc.innerType();
|
||||||
|
|
||||||
|
if (!Objects.equals(left, right)) {
|
||||||
|
System.out.println(tph + " " + left + " " + right);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -889,10 +911,13 @@ public class ASTToTargetAST {
|
|||||||
signatures.add(new Signature(javaSignature, txSignature, generics));
|
signatures.add(new Signature(javaSignature, txSignature, generics));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!signatures.isEmpty()) {
|
for (var signature : new ArrayList<>(signatures)) {
|
||||||
var signature = signatures.getFirst();
|
try {
|
||||||
// We need to convert once to find out what TPHs are existing in the given method
|
|
||||||
convert(new MethodWithTphs(method, signature.generics, signature));
|
convert(new MethodWithTphs(method, signature.generics, signature));
|
||||||
|
} catch (DiscardResultSet discard) {
|
||||||
|
// If a result set is discarded we skip it from now on
|
||||||
|
signatures.removeIf(s -> s == signature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var signature : signatures) {
|
for (var signature : signatures) {
|
||||||
@@ -980,7 +1005,7 @@ public class ASTToTargetAST {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSubtype(TargetType test, TargetType other) {
|
public boolean isSubtype(TargetType test, TargetType other) {
|
||||||
if (other.equals(TargetType.Object)) return true;
|
if (other.equals(TargetType.Object)) return true;
|
||||||
if (test instanceof TargetFunNType tfun && other instanceof TargetFunNType ofun)
|
if (test instanceof TargetFunNType tfun && other instanceof TargetFunNType ofun)
|
||||||
return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun));
|
return isSubtype(new FunNGenerator.GenericParameters(tfun), new FunNGenerator.GenericParameters(ofun));
|
||||||
@@ -989,6 +1014,12 @@ public class ASTToTargetAST {
|
|||||||
var otherClass = compiler.getClass(new JavaClassName(other.name()));
|
var otherClass = compiler.getClass(new JavaClassName(other.name()));
|
||||||
if (testClass == null) return false;
|
if (testClass == null) return false;
|
||||||
while (testClass != null) {
|
while (testClass != null) {
|
||||||
|
if (otherClass.isInterface()) {
|
||||||
|
for (var superInterface : testClass.getSuperInterfaces()) {
|
||||||
|
if (superInterface.getName().equals(otherClass.getClassName())) return true;
|
||||||
|
if (isSubtype(new TargetRefType(superInterface.getName().toString()), other)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (testClass.equals(otherClass)) return true;
|
if (testClass.equals(otherClass)) return true;
|
||||||
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break;
|
if (testClass.getClassName().equals(new JavaClassName("java.lang.Object"))) break;
|
||||||
testClass = compiler.getClass(testClass.getSuperClass().getName());
|
testClass = compiler.getClass(testClass.getSuperClass().getName());
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
|
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DiscardResultSet extends RuntimeException {
|
||||||
|
public DiscardResultSet() {}
|
||||||
|
}
|
||||||
@@ -153,7 +153,6 @@ public abstract class GenerateGenerics implements IGenerics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Target.logger.info("Simplified constraints: " + simplifiedConstraints);
|
Target.logger.info("Simplified constraints: " + simplifiedConstraints);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record GenericsState(Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes, Map<TypePlaceholder, TypePlaceholder> equality) {}
|
public record GenericsState(Map<TPH, RefTypeOrTPHOrWildcardOrGeneric> concreteTypes, Map<TypePlaceholder, TypePlaceholder> equality) {}
|
||||||
@@ -1009,7 +1008,9 @@ public abstract class GenerateGenerics implements IGenerics {
|
|||||||
public TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
|
public TargetType getTargetType(RefTypeOrTPHOrWildcardOrGeneric in) {
|
||||||
if (in instanceof TypePlaceholder tph) {
|
if (in instanceof TypePlaceholder tph) {
|
||||||
if (equality.containsKey(tph)) {
|
if (equality.containsKey(tph)) {
|
||||||
return getTargetType(equality.get(tph));
|
var tph2 = equality.get(tph);
|
||||||
|
// Sanity check, they should not be equal!
|
||||||
|
if (!Objects.equals(in, tph2)) return getTargetType(tph2);
|
||||||
}
|
}
|
||||||
var type = concreteTypes.get(new TPH(tph));
|
var type = concreteTypes.get(new TPH(tph));
|
||||||
if (type == null) return new TargetGenericType(tph.getName());
|
if (type == null) return new TargetGenericType(tph.getName());
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package de.dhbwstuttgart.target.generate;
|
package de.dhbwstuttgart.target.generate;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import de.dhbwstuttgart.core.JavaTXCompiler;
|
import de.dhbwstuttgart.core.JavaTXCompiler;
|
||||||
import de.dhbwstuttgart.exceptions.DebugException;
|
import de.dhbwstuttgart.exceptions.DebugException;
|
||||||
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
import de.dhbwstuttgart.exceptions.NotImplementedException;
|
||||||
@@ -222,6 +223,19 @@ public class StatementToTargetExpression implements ASTVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodCall methodCall) {
|
public void visit(MethodCall methodCall) {
|
||||||
|
|
||||||
|
// Look at the signature in all result sets and check if a more specific insertion exists.
|
||||||
|
// If so, discard this result set by throwing an exception
|
||||||
|
for (var tph : Iterables.concat(List.of(methodCall.receiver.getType()), methodCall.signatureArguments())) {
|
||||||
|
var currentType = converter.convert(tph, generics);
|
||||||
|
for (var g2 : converter.all) if (g2.javaGenerics() != generics) {
|
||||||
|
var type = g2.javaGenerics().getTargetType(tph);
|
||||||
|
if (!Objects.equals(type, currentType) && converter.isSubtype(type, currentType)) {
|
||||||
|
throw new DiscardResultSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var receiverType = converter.convert(methodCall.receiver.getType(), generics);
|
var receiverType = converter.convert(methodCall.receiver.getType(), generics);
|
||||||
var isFunNType = receiverType instanceof TargetFunNType;
|
var isFunNType = receiverType instanceof TargetFunNType;
|
||||||
|
|
||||||
@@ -250,18 +264,20 @@ public class StatementToTargetExpression implements ASTVisitor {
|
|||||||
} else if (!isFunNType) {
|
} else if (!isFunNType) {
|
||||||
receiverClass = converter.compiler.getClass(receiverName);
|
receiverClass = converter.compiler.getClass(receiverName);
|
||||||
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
|
if (receiverClass == null) throw new DebugException("Class " + receiverName + " does not exist!");
|
||||||
foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow();
|
foundMethod = findMethod(receiverName, methodCall.name, signature, converter.compiler).orElseThrow(
|
||||||
|
() -> new DebugException("Method " + methodCall.name + " not found (" + signature + ") on class " + receiverName)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isFunNType) {
|
if (!isFunNType) {
|
||||||
returnType = converter.convert(foundMethod.getReturnType(), generics);
|
// NOTE Not using the direct conversion method on converter to bypass adding the TPH to the used TPH list
|
||||||
|
returnType = ASTToTargetAST.convert(foundMethod.getReturnType(), generics, converter.compiler);
|
||||||
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> converter.convert(e.getType(), generics)).toList();
|
argList = foundMethod.getParameterList().getFormalparalist().stream().map(e -> converter.convert(e.getType(), generics)).toList();
|
||||||
isStatic = Modifier.isStatic(foundMethod.modifier);
|
isStatic = Modifier.isStatic(foundMethod.modifier);
|
||||||
isPrivate = Modifier.isPrivate(foundMethod.modifier);
|
isPrivate = Modifier.isPrivate(foundMethod.modifier);
|
||||||
isInterface = receiverClass.isInterface();
|
isInterface = receiverClass.isInterface();
|
||||||
}
|
}
|
||||||
|
|
||||||
//System.out.println(argList);
|
|
||||||
result = new TargetMethodCall(
|
result = new TargetMethodCall(
|
||||||
converter.convert(methodCall.getType(), generics), returnType, argList,
|
converter.convert(methodCall.getType(), generics), returnType, argList,
|
||||||
converter.convert(methodCall.receiver, generics),
|
converter.convert(methodCall.receiver, generics),
|
||||||
|
|||||||
@@ -1,311 +0,0 @@
|
|||||||
package de.dhbwstuttgart.typeinference.dependency;
|
|
||||||
|
|
||||||
import de.dhbwstuttgart.core.JavaTXCompiler;
|
|
||||||
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.AbstractASTWalker;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.Constructor;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.Method;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.BinaryExpr;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.BoolExpression;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.CastExpr;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.DoStmt;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.Expression;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.ExpressionReceiver;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.ForEachStmt;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.ForStmt;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.IfStmt;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.InstanceOf;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.MethodCall;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.NewClass;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.Receiver;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.Statement;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.StaticClassName;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.Super;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.SuperCall;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.This;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.ThisCall;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.Throw;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.statement.WhileStmt;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefType;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.IdentityHashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* --- Comment AI generated ---
|
|
||||||
* Builds the method/constructor call graph for the classes currently being compiled together --
|
|
||||||
* the input to splitting whole-program type inference into per-strongly-connected-component
|
|
||||||
* batches, so that only mutually recursive methods have to be inferred jointly.
|
|
||||||
*
|
|
||||||
* Scope, deliberately:
|
|
||||||
* <ul>
|
|
||||||
* <li>Nodes are methods and constructors with a body, declared directly in one of
|
|
||||||
* {@code definedClasses}. Field initializers are NOT modeled as graph nodes here: a field is
|
|
||||||
* never generalized (it has exactly one type, never a per-use-site instantiated scheme like a
|
|
||||||
* method does), so it never needs SCC/mutual-recursion treatment -- only a simple topological
|
|
||||||
* position relative to whatever it calls, which a separate, much simpler pass can handle.</li>
|
|
||||||
* <li>Inherited method copies ({@code Method.isInherited}, produced by
|
|
||||||
* {@code JavaTXCompiler.addMethods}) are skipped. This builder is meant to run right after
|
|
||||||
* parsing and before {@code addMethods} has run, so in practice those copies should not exist
|
|
||||||
* yet; the check is kept as a defensive no-op in case that ordering assumption changes.</li>
|
|
||||||
* <li>Call targets are resolved using ordinary Java scoping where the receiver's type is known
|
|
||||||
* syntactically without needing inference: member lookup through the current class's
|
|
||||||
* hierarchy for unqualified/{@code this}-qualified calls (a member always shadows a
|
|
||||||
* same-named import per JLS 15.12.1), direct resolution for {@code TypeName.foo()} calls,
|
|
||||||
* {@code new Foo()}, and receivers with an explicit declared type. Only when the receiver's
|
|
||||||
* own type is itself not yet known (an omitted/inferred TPH -- e.g. the result of a chained
|
|
||||||
* call) does resolution fall back to a conservative name+arity match across all classes being
|
|
||||||
* compiled. That fallback can only ever add edges that don't exist at runtime
|
|
||||||
* (over-approximation, which just merges SCCs unnecessarily); it can never omit a real edge,
|
|
||||||
* which is the property that actually matters for soundness.</li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public class CallGraphBuilder {
|
|
||||||
|
|
||||||
|
|
||||||
private final JavaTXCompiler compiler;
|
|
||||||
private final Set<ClassOrInterface> definedClasses;
|
|
||||||
private final IdentityHashMap<Method, DependencyNode> nodes = new IdentityHashMap<>();
|
|
||||||
|
|
||||||
public CallGraphBuilder(JavaTXCompiler compiler, Set<ClassOrInterface> definedClasses) {
|
|
||||||
this.compiler = compiler;
|
|
||||||
this.definedClasses = definedClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DependencyGraph build() {
|
|
||||||
for (ClassOrInterface cl : definedClasses) {
|
|
||||||
for (Method m : cl.getMethods()) {
|
|
||||||
if (!m.isInherited && m.block != null) nodeFor(cl, m);
|
|
||||||
}
|
|
||||||
for (Constructor c : cl.getConstructors()) {
|
|
||||||
if (c.block != null) nodeFor(cl, c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<DependencyNode, Set<DependencyNode>> edges = new LinkedHashMap<>();
|
|
||||||
for (Map.Entry<Method, DependencyNode> entry : nodes.entrySet()) {
|
|
||||||
DependencyNode from = entry.getValue();
|
|
||||||
EdgeCollector collector = new EdgeCollector(from.getOwner());
|
|
||||||
entry.getKey().block.accept(collector);
|
|
||||||
edges.put(from, collector.targets);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DependencyGraph(new LinkedHashSet<>(nodes.values()), edges);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DependencyNode nodeFor(ClassOrInterface owner, Method m) {
|
|
||||||
return nodes.computeIfAbsent(m, mm -> new DependencyNode(owner, mm));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassOrInterface resolve(JavaClassName name) {
|
|
||||||
return compiler.getClass(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassOrInterface typeToClass(RefTypeOrTPHOrWildcardOrGeneric type) {
|
|
||||||
return type instanceof RefType rt ? resolve(rt.getName()) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassOrInterface superClassOf(ClassOrInterface cl) {
|
|
||||||
return cl.getSuperClass() == null ? null : resolve(cl.getSuperClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ClassOrInterface> hierarchyOf(ClassOrInterface start) {
|
|
||||||
List<ClassOrInterface> result = new ArrayList<>();
|
|
||||||
Set<JavaClassName> visited = new HashSet<>();
|
|
||||||
Deque<ClassOrInterface> queue = new ArrayDeque<>();
|
|
||||||
queue.add(start);
|
|
||||||
while (!queue.isEmpty()) {
|
|
||||||
ClassOrInterface cl = queue.poll();
|
|
||||||
if (cl == null || !visited.add(cl.getClassName())) continue;
|
|
||||||
result.add(cl);
|
|
||||||
if (cl.getSuperClass() != null) queue.add(resolve(cl.getSuperClass().getName()));
|
|
||||||
for (RefType parent : cl.getSuperInterfaces()) {
|
|
||||||
queue.add(resolve(parent.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matches(Method m, String name, int arity) {
|
|
||||||
return m.getName().equals(name) && m.getParameterList().getFormalparalist().size() == arity;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hierarchyDeclaresName(ClassOrInterface start, String name) {
|
|
||||||
for (ClassOrInterface cl : hierarchyOf(start)) {
|
|
||||||
for (Method m : cl.getMethods()) {
|
|
||||||
if (!m.isInherited && m.getName().equals(name)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<DependencyNode> resolveInHierarchy(ClassOrInterface start, String name, int arity) {
|
|
||||||
Set<DependencyNode> found = new LinkedHashSet<>();
|
|
||||||
for (ClassOrInterface cl : hierarchyOf(start)) {
|
|
||||||
if (!definedClasses.contains(cl)) continue;
|
|
||||||
for (Method m : cl.getMethods()) {
|
|
||||||
if (!m.isInherited && m.block != null && matches(m, name, arity)) found.add(nodeFor(cl, m));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<DependencyNode> resolveConservatively(String name, int arity) {
|
|
||||||
Set<DependencyNode> found = new LinkedHashSet<>();
|
|
||||||
for (ClassOrInterface cl : definedClasses) {
|
|
||||||
for (Method m : cl.getMethods()) {
|
|
||||||
if (!m.isInherited && m.block != null && matches(m, name, arity)) found.add(nodeFor(cl, m));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClassOrInterface receiverClass(Receiver receiver, ClassOrInterface owner) {
|
|
||||||
if (receiver instanceof StaticClassName scn) {
|
|
||||||
return typeToClass(scn.getType());
|
|
||||||
}
|
|
||||||
if (receiver instanceof ExpressionReceiver er) {
|
|
||||||
Expression inner = er.expr;
|
|
||||||
if (inner instanceof This) return owner;
|
|
||||||
if (inner instanceof Super) return superClassOf(owner);
|
|
||||||
if (inner instanceof NewClass nc) return typeToClass(nc.getType());
|
|
||||||
// Any other expression (local var, field, parameter, ...): only resolvable when its
|
|
||||||
// own declared type is an explicit RefType rather than an omitted/inferred TPH.
|
|
||||||
if (!(inner.getType() instanceof TypePlaceholder)) return typeToClass(inner.getType());
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EdgeCollector extends AbstractASTWalker {
|
|
||||||
private final ClassOrInterface owner;
|
|
||||||
final Set<DependencyNode> targets = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
EdgeCollector(ClassOrInterface owner) {
|
|
||||||
this.owner = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(MethodCall methodCall) {
|
|
||||||
super.visit(methodCall);
|
|
||||||
int arity = methodCall.getArgumentList().getArguments().size();
|
|
||||||
targets.addAll(resolveCall(methodCall.receiver, methodCall.name, arity));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(BinaryExpr binary) {
|
|
||||||
binary.lexpr.accept(this);
|
|
||||||
binary.rexpr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(BoolExpression logical) {
|
|
||||||
logical.lexpr.accept(this);
|
|
||||||
logical.rexpr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(CastExpr castExpr) {
|
|
||||||
castExpr.expr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(InstanceOf instanceOf) {
|
|
||||||
instanceOf.getExpression().accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(Throw aThrow) {
|
|
||||||
aThrow.expr.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(IfStmt ifStmt) {
|
|
||||||
ifStmt.expr.accept(this);
|
|
||||||
super.visit(ifStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(WhileStmt whileStmt) {
|
|
||||||
whileStmt.expr.accept(this);
|
|
||||||
super.visit(whileStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(DoStmt doStmt) {
|
|
||||||
doStmt.expr.accept(this);
|
|
||||||
super.visit(doStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ForStmt forStmt) {
|
|
||||||
for (Statement s : forStmt.initializer) s.accept(this);
|
|
||||||
if (forStmt.condition != null) forStmt.condition.accept(this);
|
|
||||||
for (Expression e : forStmt.loopExpr) e.accept(this);
|
|
||||||
super.visit(forStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ForEachStmt forEachStmt) {
|
|
||||||
forEachStmt.expression.accept(this);
|
|
||||||
super.visit(forEachStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(SuperCall superCall) {
|
|
||||||
superCall.getArgumentList().accept(this);
|
|
||||||
ClassOrInterface parent = superClassOf(owner);
|
|
||||||
if (parent != null && definedClasses.contains(parent)) {
|
|
||||||
int arity = superCall.getArgumentList().getArguments().size();
|
|
||||||
for (Constructor c : parent.getConstructors()) {
|
|
||||||
if (c.getParameterList().getFormalparalist().size() == arity) targets.add(nodeFor(parent, c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(ThisCall thisCall) {
|
|
||||||
thisCall.getArgumentList().accept(this);
|
|
||||||
int arity = thisCall.getArgumentList().getArguments().size();
|
|
||||||
for (Constructor c : owner.getConstructors()) {
|
|
||||||
if (c.getParameterList().getFormalparalist().size() == arity) targets.add(nodeFor(owner, c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NewClass newClass) {
|
|
||||||
super.visit(newClass);
|
|
||||||
ClassOrInterface target = typeToClass(newClass.getType());
|
|
||||||
if (target != null && definedClasses.contains(target)) {
|
|
||||||
int arity = newClass.getArgumentList().getArguments().size();
|
|
||||||
for (Constructor c : target.getConstructors()) {
|
|
||||||
if (c.getParameterList().getFormalparalist().size() == arity) targets.add(nodeFor(target, c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<DependencyNode> resolveCall(Receiver receiver, String name, int arity) {
|
|
||||||
// Unqualified / this-qualified calls follow JLS 15.12.1: a member always shadows a
|
|
||||||
// same-named import, so search the current class's hierarchy first -- but unlike an
|
|
||||||
// explicitly typed receiver (below), finding nothing there is not a resolved "no
|
|
||||||
// edges", it means falling through to import resolution, approximated conservatively.
|
|
||||||
if (receiver instanceof ExpressionReceiver er && er.expr instanceof This) {
|
|
||||||
if (hierarchyDeclaresName(owner, name)) return resolveInHierarchy(owner, name, arity);
|
|
||||||
return resolveConservatively(name, arity);
|
|
||||||
}
|
|
||||||
ClassOrInterface known = receiverClass(receiver, owner);
|
|
||||||
if (known != null) return resolveInHierarchy(known, name, arity);
|
|
||||||
return resolveConservatively(name, arity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package de.dhbwstuttgart.typeinference.dependency;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A directed graph over DependencyNodes: an edge from A to B means A's body contains a
|
|
||||||
* call that may resolve to B
|
|
||||||
*/
|
|
||||||
public final class DependencyGraph {
|
|
||||||
|
|
||||||
private final Set<DependencyNode> nodes;
|
|
||||||
private final Map<DependencyNode, Set<DependencyNode>> edges;
|
|
||||||
|
|
||||||
public DependencyGraph(Set<DependencyNode> nodes, Map<DependencyNode, Set<DependencyNode>> edges) {
|
|
||||||
this.nodes = Collections.unmodifiableSet(new LinkedHashSet<>(nodes));
|
|
||||||
Map<DependencyNode, Set<DependencyNode>> copy = new LinkedHashMap<>();
|
|
||||||
for (DependencyNode n : this.nodes) {
|
|
||||||
copy.put(n, Collections.unmodifiableSet(new LinkedHashSet<>(edges.getOrDefault(n, Set.of()))));
|
|
||||||
}
|
|
||||||
this.edges = Collections.unmodifiableMap(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<DependencyNode> nodes() {
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The nodes {@code node}'s body may call into. */
|
|
||||||
public Set<DependencyNode> callees(DependencyNode node) {
|
|
||||||
return edges.getOrDefault(node, Set.of());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package de.dhbwstuttgart.typeinference.dependency;
|
|
||||||
|
|
||||||
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.Method;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public final class DependencyNode {
|
|
||||||
|
|
||||||
private final ClassOrInterface owner;
|
|
||||||
private final Method method;
|
|
||||||
|
|
||||||
DependencyNode(ClassOrInterface owner, Method method) {
|
|
||||||
this.owner = Objects.requireNonNull(owner);
|
|
||||||
this.method = Objects.requireNonNull(method);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassOrInterface getOwner() {
|
|
||||||
return owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Method getMethod() {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return owner.getClassName().getClassName() + "." + method.getName() + "/"
|
|
||||||
+ method.getParameterList().getFormalparalist().size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-125
@@ -1,125 +0,0 @@
|
|||||||
package de.dhbwstuttgart.typeinference.dependency;
|
|
||||||
|
|
||||||
import de.dhbwstuttgart.core.JavaTXCompiler;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.Method;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.SourceFile;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter;
|
|
||||||
import de.dhbwstuttgart.typeinference.result.ResultPair;
|
|
||||||
import de.dhbwstuttgart.typeinference.result.ResultSet;
|
|
||||||
import de.dhbwstuttgart.typeinference.typeAlgo.TYPE;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.PlaceholderRegistry;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.TypeUnify;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyContext;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyResultListenerImpl;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyResultModel;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyTaskModel;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.model.FiniteClosure;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;
|
|
||||||
import de.dhbwstuttgart.util.Logger;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class GeneralizingBytecodeDemo {
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
if (args.length == 0) {
|
|
||||||
System.out.println("usage: GeneralizingBytecodeDemo <file1.jav> [file2.jav ...]");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<File> files = Arrays.stream(args).map(File::new).toList();
|
|
||||||
JavaTXCompiler compiler = new JavaTXCompiler(files);
|
|
||||||
compiler.parseAll();
|
|
||||||
|
|
||||||
Set<ClassOrInterface> definedClasses = new HashSet<>();
|
|
||||||
Set<ClassOrInterface> allClasses = new HashSet<>();
|
|
||||||
for (File f : files) {
|
|
||||||
SourceFile sf = compiler.sourceFiles.get(f);
|
|
||||||
definedClasses.addAll(sf.KlassenVektor);
|
|
||||||
allClasses.addAll(compiler.getAvailableClasses(f));
|
|
||||||
allClasses.addAll(sf.availableClasses);
|
|
||||||
}
|
|
||||||
allClasses.removeAll(definedClasses);
|
|
||||||
allClasses.addAll(definedClasses);
|
|
||||||
|
|
||||||
DependencyGraph graph = new CallGraphBuilder(compiler, definedClasses).build();
|
|
||||||
List<Set<DependencyNode>> sccs = StronglyConnectedComponents.compute(graph);
|
|
||||||
|
|
||||||
TYPE ty = new TYPE(definedClasses, allClasses);
|
|
||||||
Logger logger = new Logger("GeneralizingBytecodeDemo");
|
|
||||||
PlaceholderRegistry placeholderRegistry = new PlaceholderRegistry();
|
|
||||||
FiniteClosure finiteClosure = UnifyTypeFactory.generateFC(
|
|
||||||
allClasses.stream().toList(), logger, compiler.getClassLoader(), compiler, placeholderRegistry);
|
|
||||||
UnifyTaskModel usedTasks = new UnifyTaskModel();
|
|
||||||
|
|
||||||
Function<UnifyPair, UnifyPair> distributeInnerVars = x -> {
|
|
||||||
var lhs = x.getLhsType();
|
|
||||||
var rhs = x.getRhsType();
|
|
||||||
if (lhs instanceof PlaceholderType lp && rhs instanceof PlaceholderType rp && (lp.isInnerType() || rp.isInnerType())) {
|
|
||||||
lp.setInnerType(true);
|
|
||||||
rp.setInnerType(true);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
};
|
|
||||||
|
|
||||||
Set<ResultPair> combined = new LinkedHashSet<>();
|
|
||||||
|
|
||||||
System.out.println("Solving " + sccs.size() + " SCCs independently, materializing each into the AST before the next SCC runs:");
|
|
||||||
for (int i = 0; i < sccs.size(); i++) {
|
|
||||||
Set<DependencyNode> component = sccs.get(i);
|
|
||||||
List<Map.Entry<ClassOrInterface, Method>> group = component.stream()
|
|
||||||
.<Map.Entry<ClassOrInterface, Method>>map(n -> new AbstractMap.SimpleEntry<>(n.getOwner(), n.getMethod()))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
var cons = ty.getConstraintsFor(group);
|
|
||||||
var unifyCons = UnifyTypeFactory.convert(compiler, cons, placeholderRegistry).map(distributeInnerVars);
|
|
||||||
|
|
||||||
UnifyResultModel urm = new UnifyResultModel(cons, finiteClosure);
|
|
||||||
UnifyResultListenerImpl listener = new UnifyResultListenerImpl();
|
|
||||||
urm.addUnifyResultListener(listener);
|
|
||||||
UnifyContext context = new UnifyContext(logger, true, urm, usedTasks, placeholderRegistry);
|
|
||||||
TypeUnify.unifyParallel(unifyCons.getUndConstraints(), unifyCons.getOderConstraints(), finiteClosure, context);
|
|
||||||
|
|
||||||
System.out.println("[" + i + "] " + component);
|
|
||||||
List<ResultSet> results = listener.getResults();
|
|
||||||
if (results.isEmpty()) {
|
|
||||||
System.out.println(" (no results)");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ResultSet sccResult = results.getFirst();
|
|
||||||
System.out.println(" isolated result: " + sccResult.getSortedResults());
|
|
||||||
combined.addAll(sccResult.results);
|
|
||||||
|
|
||||||
for (Map.Entry<ClassOrInterface, Method> entry : group) {
|
|
||||||
GeneralizingSccDemo.materialize(entry.getKey(), entry.getValue(), sccResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultSet whole = new ResultSet(combined);
|
|
||||||
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Source after materialization:");
|
|
||||||
for (File f : files) {
|
|
||||||
System.out.println(ASTTypePrinter.print(compiler.sourceFiles.get(f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Generating bytecode from the union of all SCCs' isolated ResultSets ...");
|
|
||||||
for (File f : files) {
|
|
||||||
SourceFile sf = compiler.sourceFiles.get(f);
|
|
||||||
var classes = compiler.generateBytecode(sf, List.of(whole));
|
|
||||||
compiler.writeClassFile(classes, f);
|
|
||||||
System.out.println("wrote " + classes.keySet());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
package de.dhbwstuttgart.typeinference.dependency;
|
|
||||||
|
|
||||||
import de.dhbwstuttgart.core.JavaTXCompiler;
|
|
||||||
import de.dhbwstuttgart.parser.NullToken;
|
|
||||||
import de.dhbwstuttgart.parser.scope.JavaClassName;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.Constructor;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.GenericDeclarationList;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.Method;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.ParameterList;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.Pattern;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.SourceFile;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefType;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.visual.ASTTypePrinter;
|
|
||||||
import de.dhbwstuttgart.typeinference.result.ResultPair;
|
|
||||||
import de.dhbwstuttgart.typeinference.result.ResultSet;
|
|
||||||
import de.dhbwstuttgart.typeinference.typeAlgo.TYPE;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.PlaceholderRegistry;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.TypeUnify;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyContext;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyResultListenerImpl;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyResultModel;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.UnifyTaskModel;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.model.FiniteClosure;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
|
|
||||||
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;
|
|
||||||
import de.dhbwstuttgart.util.Logger;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class GeneralizingSccDemo {
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
if (args.length == 0) {
|
|
||||||
System.out.println("usage: GeneralizingSccDemo <file1.jav> [file2.jav ...]");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<File> files = Arrays.stream(args).map(File::new).toList();
|
|
||||||
JavaTXCompiler compiler = new JavaTXCompiler(files);
|
|
||||||
compiler.parseAll();
|
|
||||||
|
|
||||||
Set<ClassOrInterface> definedClasses = new HashSet<>();
|
|
||||||
Set<ClassOrInterface> allClasses = new HashSet<>();
|
|
||||||
for (File f : files) {
|
|
||||||
SourceFile sf = compiler.sourceFiles.get(f);
|
|
||||||
definedClasses.addAll(sf.KlassenVektor);
|
|
||||||
allClasses.addAll(compiler.getAvailableClasses(f));
|
|
||||||
allClasses.addAll(sf.availableClasses);
|
|
||||||
}
|
|
||||||
allClasses.removeAll(definedClasses);
|
|
||||||
allClasses.addAll(definedClasses);
|
|
||||||
|
|
||||||
System.out.println("Source with TPH names (unresolved type slots only):");
|
|
||||||
for (File f : files) {
|
|
||||||
System.out.println(ASTTypePrinter.print(compiler.sourceFiles.get(f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
DependencyGraph graph = new CallGraphBuilder(compiler, definedClasses).build();
|
|
||||||
List<Set<DependencyNode>> sccs = StronglyConnectedComponents.compute(graph);
|
|
||||||
|
|
||||||
TYPE ty = new TYPE(definedClasses, allClasses);
|
|
||||||
Logger logger = new Logger("GeneralizingSccDemo");
|
|
||||||
PlaceholderRegistry placeholderRegistry = new PlaceholderRegistry();
|
|
||||||
FiniteClosure finiteClosure = UnifyTypeFactory.generateFC(
|
|
||||||
allClasses.stream().toList(), logger, compiler.getClassLoader(), compiler, placeholderRegistry);
|
|
||||||
UnifyTaskModel usedTasks = new UnifyTaskModel();
|
|
||||||
|
|
||||||
Function<UnifyPair, UnifyPair> distributeInnerVars = x -> {
|
|
||||||
var lhs = x.getLhsType();
|
|
||||||
var rhs = x.getRhsType();
|
|
||||||
if (lhs instanceof PlaceholderType lp && rhs instanceof PlaceholderType rp && (lp.isInnerType() || rp.isInnerType())) {
|
|
||||||
lp.setInnerType(true);
|
|
||||||
rp.setInnerType(true);
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
};
|
|
||||||
|
|
||||||
System.out.println("Solving " + sccs.size() + " SCCs independently, materializing each into the AST before the next SCC runs:");
|
|
||||||
for (int i = 0; i < sccs.size(); i++) {
|
|
||||||
Set<DependencyNode> component = sccs.get(i);
|
|
||||||
List<Map.Entry<ClassOrInterface, Method>> group = component.stream()
|
|
||||||
.<Map.Entry<ClassOrInterface, Method>>map(n -> new AbstractMap.SimpleEntry<>(n.getOwner(), n.getMethod()))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
var cons = ty.getConstraintsFor(group);
|
|
||||||
var unifyCons = UnifyTypeFactory.convert(compiler, cons, placeholderRegistry).map(distributeInnerVars);
|
|
||||||
|
|
||||||
UnifyResultModel urm = new UnifyResultModel(cons, finiteClosure);
|
|
||||||
UnifyResultListenerImpl listener = new UnifyResultListenerImpl();
|
|
||||||
urm.addUnifyResultListener(listener);
|
|
||||||
UnifyContext context = new UnifyContext(logger, true, urm, usedTasks, placeholderRegistry);
|
|
||||||
TypeUnify.unifyParallel(unifyCons.getUndConstraints(), unifyCons.getOderConstraints(), finiteClosure, context);
|
|
||||||
|
|
||||||
System.out.println("[" + i + "] " + component + (component.size() > 1 ? " <- mutually recursive, solved jointly" : ""));
|
|
||||||
System.out.println(" raw constraints (pre-unify, AST-level types): " + cons);
|
|
||||||
System.out.println(" unify constraints (UnifyPair, and-constraints only): " + unifyCons.getUndConstraints());
|
|
||||||
if (!unifyCons.getOderConstraints().isEmpty()) {
|
|
||||||
System.out.println(" unify constraints (or-constraints): " + unifyCons.getOderConstraints());
|
|
||||||
}
|
|
||||||
List<ResultSet> results = listener.getResults();
|
|
||||||
if (results.isEmpty()) {
|
|
||||||
System.out.println(" (no results)");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ResultSet sccResult = results.getFirst();
|
|
||||||
System.out.println(" isolated result: " + sccResult.getSortedResults());
|
|
||||||
|
|
||||||
for (Map.Entry<ClassOrInterface, Method> entry : group) {
|
|
||||||
Method materialized = materialize(entry.getKey(), entry.getValue(), sccResult);
|
|
||||||
System.out.println(" materialized " + entry.getKey().getClassName().getClassName() + "." + materialized.getName()
|
|
||||||
+ ": generics=" + describeGenerics(materialized) + " params=" + describeParams(materialized)
|
|
||||||
+ " returns=" + describe(materialized.getReturnType()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println();
|
|
||||||
System.out.println("Source after materialization (still prints TPH names for anything left, e.g. method bodies -- only signatures were rewritten):");
|
|
||||||
for (File f : files) {
|
|
||||||
System.out.println(ASTTypePrinter.print(compiler.sourceFiles.get(f)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Method materialize(ClassOrInterface owner, Method method, ResultSet sccResult) {
|
|
||||||
Set<TypePlaceholder> used = new LinkedHashSet<>();
|
|
||||||
for (Pattern p : method.getParameterList().getFormalparalist()) collectTPHs(p.getType(), used);
|
|
||||||
collectTPHs(method.getReturnType(), used);
|
|
||||||
if (used.isEmpty()) return method;
|
|
||||||
|
|
||||||
Set<TypePlaceholder> free = new LinkedHashSet<>();
|
|
||||||
for (TypePlaceholder tph : used) {
|
|
||||||
if (sccResult.resolveType(tph).resolvedType instanceof TypePlaceholder) free.add(tph);
|
|
||||||
}
|
|
||||||
|
|
||||||
UnionFind uf = new UnionFind();
|
|
||||||
for (ResultPair rp : sccResult.results) {
|
|
||||||
if (rp.getLeft() instanceof TypePlaceholder l && rp.getRight() instanceof TypePlaceholder r
|
|
||||||
&& free.contains(l) && free.contains(r)) {
|
|
||||||
uf.union(l, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<TypePlaceholder, List<TypePlaceholder>> groups = new LinkedHashMap<>();
|
|
||||||
for (TypePlaceholder tph : free) groups.computeIfAbsent(uf.find(tph), k -> new ArrayList<>()).add(tph);
|
|
||||||
|
|
||||||
Map<TypePlaceholder, RefTypeOrTPHOrWildcardOrGeneric> substitution = new HashMap<>();
|
|
||||||
List<GenericTypeVar> newGenerics = new ArrayList<>();
|
|
||||||
RefType objectBound = new RefType(new JavaClassName("java.lang.Object"), new NullToken());
|
|
||||||
for (List<TypePlaceholder> members : groups.values()) {
|
|
||||||
String genName = members.get(0).getName();
|
|
||||||
newGenerics.add(new GenericTypeVar(genName, List.of(objectBound), new NullToken(), new NullToken()));
|
|
||||||
for (TypePlaceholder member : members) substitution.put(member, new GenericRefType(genName, new NullToken()));
|
|
||||||
}
|
|
||||||
for (TypePlaceholder tph : used) {
|
|
||||||
if (!free.contains(tph)) substitution.put(tph, sccResult.resolveType(tph).resolvedType);
|
|
||||||
}
|
|
||||||
if (substitution.isEmpty()) return method;
|
|
||||||
|
|
||||||
List<Pattern> newParams = new ArrayList<>();
|
|
||||||
for (Pattern p : method.getParameterList().getFormalparalist()) {
|
|
||||||
newParams.add(p.withType(substitute(p.getType(), substitution)));
|
|
||||||
}
|
|
||||||
ParameterList newParamList = new ParameterList(newParams, method.getParameterList().getOffset());
|
|
||||||
RefTypeOrTPHOrWildcardOrGeneric newReturnType = substitute(method.getReturnType(), substitution);
|
|
||||||
GenericDeclarationList newGenericDecl = new GenericDeclarationList(newGenerics, new NullToken());
|
|
||||||
|
|
||||||
Method rewritten = method instanceof Constructor
|
|
||||||
? new Constructor(method.modifier, method.name, newReturnType, newParamList, method.block, newGenericDecl, method.getOffset())
|
|
||||||
: new Method(method.modifier, method.name, newReturnType, newParamList, method.block, newGenericDecl, method.getOffset());
|
|
||||||
|
|
||||||
replaceMethod(owner, method, rewritten);
|
|
||||||
return rewritten;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void collectTPHs(RefTypeOrTPHOrWildcardOrGeneric type, Set<TypePlaceholder> out) {
|
|
||||||
if (type instanceof TypePlaceholder tph) out.add(tph);
|
|
||||||
else if (type instanceof RefType rt) for (var p : rt.getParaList()) collectTPHs(p, out);
|
|
||||||
else if (type instanceof ExtendsWildcardType w) collectTPHs(w.getInnerType(), out);
|
|
||||||
else if (type instanceof SuperWildcardType w) collectTPHs(w.getInnerType(), out);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RefTypeOrTPHOrWildcardOrGeneric substitute(RefTypeOrTPHOrWildcardOrGeneric type, Map<TypePlaceholder, RefTypeOrTPHOrWildcardOrGeneric> sub) {
|
|
||||||
if (type instanceof TypePlaceholder tph) return sub.getOrDefault(tph, tph);
|
|
||||||
if (type instanceof RefType rt) return new RefType(rt.getName(), rt.getParaList().stream().map(p -> substitute(p, sub)).toList(), rt.getOffset());
|
|
||||||
if (type instanceof ExtendsWildcardType w) return new ExtendsWildcardType(substitute(w.getInnerType(), sub), w.getOffset());
|
|
||||||
if (type instanceof SuperWildcardType w) return new SuperWildcardType(substitute(w.getInnerType(), sub), w.getOffset());
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void replaceMethod(ClassOrInterface owner, Method original, Method replacement) {
|
|
||||||
if (original instanceof Constructor) {
|
|
||||||
var list = owner.getConstructors();
|
|
||||||
for (int i = 0; i < list.size(); i++) if (list.get(i) == original) { list.set(i, (Constructor) replacement); return; }
|
|
||||||
} else {
|
|
||||||
var list = owner.getMethods();
|
|
||||||
for (int i = 0; i < list.size(); i++) if (list.get(i) == original) { list.set(i, replacement); return; }
|
|
||||||
}
|
|
||||||
throw new IllegalStateException("method not found in owner's own list: " + original);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String describeGenerics(Method m) {
|
|
||||||
List<String> names = new ArrayList<>();
|
|
||||||
m.getGenerics().forEach(gtv -> names.add(gtv.getName()));
|
|
||||||
return names.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String describeParams(Method m) {
|
|
||||||
List<String> params = new ArrayList<>();
|
|
||||||
m.getParameterList().getFormalparalist().forEach(p -> params.add(describe(p.getType())));
|
|
||||||
return params.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String describe(RefTypeOrTPHOrWildcardOrGeneric type) {
|
|
||||||
if (type instanceof GenericRefType g) return g.getParsedName().toString();
|
|
||||||
if (type instanceof TypePlaceholder tph) return "TPH " + tph.getName();
|
|
||||||
return type.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class UnionFind {
|
|
||||||
private final Map<TypePlaceholder, TypePlaceholder> parent = new HashMap<>();
|
|
||||||
|
|
||||||
TypePlaceholder find(TypePlaceholder x) {
|
|
||||||
parent.putIfAbsent(x, x);
|
|
||||||
TypePlaceholder p = parent.get(x);
|
|
||||||
if (p != x) { p = find(p); parent.put(x, p); }
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void union(TypePlaceholder a, TypePlaceholder b) {
|
|
||||||
TypePlaceholder ra = find(a), rb = find(b);
|
|
||||||
if (ra != rb) parent.put(ra, rb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package de.dhbwstuttgart.typeinference.dependency;
|
|
||||||
|
|
||||||
import de.dhbwstuttgart.syntaxtree.SourceFile;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
|
|
||||||
import de.dhbwstuttgart.syntaxtree.visual.OutputGenerator;
|
|
||||||
import de.dhbwstuttgart.typeinference.result.ResultSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints a SourceFile the same way ASTTypePrinter/OutputGenerator do, except every TypePlaceholder
|
|
||||||
* the given ResultSet resolves is printed as its inferred type instead of "TPH <name>". Falls back
|
|
||||||
* to "TPH <name>" for any TPH the ResultSet doesn't cover (e.g. one that belongs to an SCC whose
|
|
||||||
* result wasn't merged in -- see SccUnifyDemo).
|
|
||||||
*/
|
|
||||||
public class ResolvedSourcePrinter extends OutputGenerator {
|
|
||||||
|
|
||||||
private final ResultSet resultSet;
|
|
||||||
|
|
||||||
private ResolvedSourcePrinter(StringBuilder out, ResultSet resultSet) {
|
|
||||||
super(out);
|
|
||||||
this.resultSet = resultSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String print(SourceFile sourceFile, ResultSet resultSet) {
|
|
||||||
StringBuilder out = new StringBuilder();
|
|
||||||
new ResolvedSourcePrinter(out, resultSet).visit(sourceFile);
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(TypePlaceholder typePlaceholder) {
|
|
||||||
var resolved = resultSet.resolveType(typePlaceholder).resolvedType;
|
|
||||||
if (resolved instanceof TypePlaceholder) {
|
|
||||||
out.append("TPH ").append(typePlaceholder.getName());
|
|
||||||
} else {
|
|
||||||
// resolved type may itself contain nested TPHs (e.g. a generic parameter) --
|
|
||||||
// recurse through the same visitor so those get resolved too, not just the outer type.
|
|
||||||
resolved.accept(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-82
@@ -1,82 +0,0 @@
|
|||||||
package de.dhbwstuttgart.typeinference.dependency;
|
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* --- Comment AI generated ---
|
|
||||||
* Computes the strongly connected components of a {@link DependencyGraph} via Tarjan's
|
|
||||||
* algorithm, returning them in the order the SCC-scoped inference driver needs to process them:
|
|
||||||
* for every edge u -> v (u calls v) with u and v in different components, the component
|
|
||||||
* containing v appears BEFORE the component containing u -- i.e. a method's callees are always
|
|
||||||
* fully processed (and, in the inference driver, generalized) before the method itself.
|
|
||||||
*
|
|
||||||
* This is not a separate reversal step: it falls directly out of Tarjan's algorithm, since a
|
|
||||||
* component is only popped off the stack (and appended to the result) once the DFS has finished
|
|
||||||
* exploring all of its outgoing edges, which means every component it points to has already been
|
|
||||||
* popped.
|
|
||||||
*
|
|
||||||
* Implementation is recursive, so its stack depth is bounded by the depth of the call graph
|
|
||||||
* among the classes being compiled together, not by the size of the program. That is fine for
|
|
||||||
* realistic programs; a pathologically deep call chain could still overflow the JVM stack, same
|
|
||||||
* tradeoff most textbook Tarjan implementations make.
|
|
||||||
*/
|
|
||||||
public final class StronglyConnectedComponents {
|
|
||||||
|
|
||||||
private final DependencyGraph graph;
|
|
||||||
private final Map<DependencyNode, Integer> index = new HashMap<>();
|
|
||||||
private final Map<DependencyNode, Integer> lowlink = new HashMap<>();
|
|
||||||
private final Set<DependencyNode> onStack = new HashSet<>();
|
|
||||||
private final Deque<DependencyNode> stack = new ArrayDeque<>();
|
|
||||||
private final List<Set<DependencyNode>> result = new ArrayList<>();
|
|
||||||
private int counter = 0;
|
|
||||||
|
|
||||||
private StronglyConnectedComponents(DependencyGraph graph) {
|
|
||||||
this.graph = graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Set<DependencyNode>> compute(DependencyGraph graph) {
|
|
||||||
StronglyConnectedComponents scc = new StronglyConnectedComponents(graph);
|
|
||||||
for (DependencyNode node : graph.nodes()) {
|
|
||||||
if (!scc.index.containsKey(node)) {
|
|
||||||
scc.strongConnect(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scc.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void strongConnect(DependencyNode v) {
|
|
||||||
index.put(v, counter);
|
|
||||||
lowlink.put(v, counter);
|
|
||||||
counter++;
|
|
||||||
stack.push(v);
|
|
||||||
onStack.add(v);
|
|
||||||
|
|
||||||
for (DependencyNode w : graph.callees(v)) {
|
|
||||||
if (!index.containsKey(w)) {
|
|
||||||
strongConnect(w);
|
|
||||||
lowlink.put(v, Math.min(lowlink.get(v), lowlink.get(w)));
|
|
||||||
} else if (onStack.contains(w)) {
|
|
||||||
lowlink.put(v, Math.min(lowlink.get(v), index.get(w)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lowlink.get(v).equals(index.get(v))) {
|
|
||||||
Set<DependencyNode> component = new LinkedHashSet<>();
|
|
||||||
DependencyNode w;
|
|
||||||
do {
|
|
||||||
w = stack.pop();
|
|
||||||
onStack.remove(w);
|
|
||||||
component.add(w);
|
|
||||||
} while (w != v);
|
|
||||||
result.add(component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,26 +45,6 @@ public class TYPE {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates constraints for an explicit subset of methods/constructors (e.g. one strongly
|
|
||||||
* connected component of the call graph) instead of every method of every defined class.
|
|
||||||
* Uses the same TypeInferenceInformation (the whole program's classes, same as
|
|
||||||
* {@link #getConstraints()}) -- this does not change what a method's constraints can refer
|
|
||||||
* to, only which methods' constraints get generated and later unified together.
|
|
||||||
*/
|
|
||||||
public ConstraintSet<Pair> getConstraintsFor(Collection<Map.Entry<ClassOrInterface, Method>> group) {
|
|
||||||
ConstraintSet<Pair> ret = new ConstraintSet<>();
|
|
||||||
Set<ClassOrInterface> allClasses = TypeUnifyTaskHelper.getPresizedHashSet(allAvailableClasses.size());
|
|
||||||
allClasses.addAll(allAvailableClasses);
|
|
||||||
TypeInferenceInformation info = new TypeInferenceInformation(allClasses);
|
|
||||||
for (Map.Entry<ClassOrInterface, Method> entry : group) {
|
|
||||||
ClassOrInterface cl = entry.getKey();
|
|
||||||
Method m = entry.getValue();
|
|
||||||
ret.addAll(m instanceof Constructor c ? getConstraintsConstructor(c, info, cl) : getConstraintsMethod(m, info, cl));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConstraintSet getConstraintsClass(ClassOrInterface cl, TypeInferenceInformation info) {
|
private ConstraintSet getConstraintsClass(ClassOrInterface cl, TypeInferenceInformation info) {
|
||||||
ConstraintSet ret = new ConstraintSet();
|
ConstraintSet ret = new ConstraintSet();
|
||||||
ConstraintSet methConstrains;
|
ConstraintSet methConstrains;
|
||||||
|
|||||||
@@ -392,8 +392,9 @@ public class TYPEStmt implements StatementVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BoolExpression expr) {
|
public void visit(BoolExpression expr) {
|
||||||
expr.rexpr.accept(this);
|
|
||||||
expr.lexpr.accept(this);
|
expr.lexpr.accept(this);
|
||||||
|
expr.rexpr.accept(this);
|
||||||
|
|
||||||
constraintsSet.addUndConstraint(new Pair(bool, expr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
|
constraintsSet.addUndConstraint(new Pair(bool, expr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
|
||||||
constraintsSet.addUndConstraint(new Pair(bool, expr.lexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
|
constraintsSet.addUndConstraint(new Pair(bool, expr.lexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
|
||||||
constraintsSet.addUndConstraint(new Pair(bool, expr.rexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
|
constraintsSet.addUndConstraint(new Pair(bool, expr.rexpr.getType(), PairOperator.EQUALSDOT, loc(expr.getOffset())));
|
||||||
|
|||||||
@@ -33,13 +33,11 @@ public class Logger {
|
|||||||
private static void initLogger() {
|
private static void initLogger() {
|
||||||
if (defaultWriter != null) return;
|
if (defaultWriter != null) return;
|
||||||
if (!ConsoleInterface.writeLogFiles) return;
|
if (!ConsoleInterface.writeLogFiles) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(logFolder.toPath());
|
Files.createDirectories(logFolder.toPath());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Could not create directory for log files: " + logFolder, e);
|
throw new RuntimeException("Could not create directory for log files: " + logFolder, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
|
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
|
||||||
var currentTimestamp = LocalDateTime.now().format(formatter);
|
var currentTimestamp = LocalDateTime.now().format(formatter);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -320,9 +320,17 @@ public class TestComplete {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void overloadingSortingTest() throws Exception {
|
public void sortingTest() throws Exception {
|
||||||
var classFiles = generateClassFiles(createClassLoader(), "Sorting.jav");
|
var classFiles = generateClassFiles(createClassLoader(), "Sorting.jav");
|
||||||
var instance = classFiles.get("Sorting").getDeclaredConstructor().newInstance();
|
var Sorting = classFiles.get("Sorting");
|
||||||
|
var instance = Sorting.getDeclaredConstructor().newInstance();
|
||||||
|
|
||||||
|
var unsorted = List.of(10, 2, 1, 20, -1, 5);
|
||||||
|
var sorted = List.of(-1, 1, 2, 5, 10, 20);
|
||||||
|
var sort = Sorting.getDeclaredMethod("sort", List.class);
|
||||||
|
|
||||||
|
var result = sort.invoke(instance, unsorted);
|
||||||
|
assertEquals(sorted, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user