Compare commits

...

8 Commits

17 changed files with 579 additions and 99 deletions

@ -0,0 +1,11 @@
import java.lang.String;
class TXGenerics {
a;
b;
test() {
var c = new Cycle();
c.m(a, b);
}
}

@ -5,7 +5,7 @@ public class TestContraVariant {
x = y;
return y;
}
main(x) {
return m(x);
}

@ -1,10 +1,12 @@
public class TestTwoCalls {
// <O> O -> O
id(b) {
var c = b;
return c;
}
// <T, S> (S, T) -> T
main(x,y) {
id(x);
return id(y);

@ -1000,7 +1000,7 @@ public class Codegen {
private void generateConstructor(TargetConstructor constructor) {
MethodVisitor mv = cw.visitMethod(constructor.access() | ACC_PUBLIC, "<init>", constructor.getDescriptor(), constructor.getSignature(), null);
if (constructor.txGenerics() != null)
mv.visitAttribute(new JavaTXSignatureAttribute(cw.newConst(constructor.getTXSignature())));
mv.visitAttribute(new JavaTXSignatureAttribute(constructor.getTXSignature()));
mv.visitCode();
var state = new State(null, mv, 1);
@ -1027,7 +1027,7 @@ public class Codegen {
// TODO The older codegen has set ACC_PUBLIC for all methods, good for testing but bad for everything else
MethodVisitor mv = cw.visitMethod(method.access() | ACC_PUBLIC, method.name(), method.getDescriptor(), method.getSignature(), null);
if (method.txSignature() != null) {
mv.visitAttribute(new JavaTXSignatureAttribute(cw.newConst(method.getTXSignature())));
mv.visitAttribute(new JavaTXSignatureAttribute(method.getTXSignature()));
}
System.out.println(method.getDescriptor());
@ -1044,11 +1044,14 @@ public class Codegen {
}
private static String generateSignature(TargetClass clazz, Set<TargetGeneric> generics) {
String ret = "<";
for (var generic : generics) {
ret += generic.name() + ":" + generic.bound().toDescriptor();
String ret = "";
if (generics.size() > 0) {
ret += "<";
for (var generic : generics) {
ret += generic.name() + ":" + generic.bound().toDescriptor();
}
ret += ">";
}
ret += ">";
ret += clazz.superType().toDescriptor();
return ret;
@ -1060,7 +1063,7 @@ public class Codegen {
clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new)
);
if (clazz.txGenerics() != null)
cw.visitAttribute(new JavaTXSignatureAttribute(cw.newConst(generateSignature(clazz, clazz.txGenerics()))));
cw.visitAttribute(new JavaTXSignatureAttribute(generateSignature(clazz, clazz.txGenerics())));
clazz.fields().forEach(this::generateField);
clazz.constructors().forEach(this::generateConstructor);

@ -3,10 +3,13 @@ package de.dhbwstuttgart.bytecode;
import org.objectweb.asm.*;
public class JavaTXSignatureAttribute extends Attribute {
final int signature;
public String signature;
protected JavaTXSignatureAttribute(int signature) {
public JavaTXSignatureAttribute() {
super("JavaTXSignature");
}
protected JavaTXSignatureAttribute(String signature) {
this();
this.signature = signature;
}
@ -14,13 +17,14 @@ public class JavaTXSignatureAttribute extends Attribute {
protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) {
var data = new byte[length];
System.arraycopy(classReader.b, offset, data, 0, length);
return new JavaTXSignatureAttribute(data[0] << 8 | data[1]);
var constantPoolOffset = data[0] << 8 | data[1];
return new JavaTXSignatureAttribute((String) classReader.readConst(constantPoolOffset, charBuffer));
}
@Override
protected ByteVector write(ClassWriter classWriter, byte[] code, int codeLength, int maxStack, int maxLocals) {
var data = new ByteVector();
data.putShort(this.signature);
data.putShort(classWriter.newConst(this.signature));
return data;
}
}

@ -49,4 +49,17 @@ public class GenericDeclarationList extends SyntaxTreeNode implements Iterable<G
public String toString() {
return this.gtvs.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GenericDeclarationList that = (GenericDeclarationList) o;
return gtvs.equals(that.gtvs);
}
@Override
public int hashCode() {
return Objects.hash(gtvs);
}
}

@ -6,6 +6,7 @@ import org.antlr.v4.runtime.Token;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Entspricht einem GenericTypeVar, jedoch mit Bounds
@ -45,7 +46,7 @@ public class GenericTypeVar extends SyntaxTreeNode
public String toString()
{
return "BoGTV " + this.name;
return "BoGTV " + this.name + " " + this.bounds;
}
public String getName(){
@ -61,4 +62,17 @@ public class GenericTypeVar extends SyntaxTreeNode
public void accept(ASTVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GenericTypeVar that = (GenericTypeVar) o;
return bounds.equals(that.bounds) && name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(bounds, name);
}
}

@ -1,19 +1,16 @@
package de.dhbwstuttgart.syntaxtree.factory;
import java.io.IOException;
import java.lang.reflect.*;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.bytecode.JavaTXSignatureAttribute;
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.GenericContext;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.parser.scope.JavaClassRegistry;
import de.dhbwstuttgart.syntaxtree.Field;
import de.dhbwstuttgart.syntaxtree.Method;
import de.dhbwstuttgart.syntaxtree.type.*;
@ -22,7 +19,14 @@ import de.dhbwstuttgart.syntaxtree.*;
import de.dhbwstuttgart.syntaxtree.statement.Block;
import de.dhbwstuttgart.syntaxtree.statement.Statement;
import de.dhbwstuttgart.syntaxtree.type.WildcardType;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.util.Pair;
import javassist.bytecode.SignatureAttribute;
import org.antlr.v4.runtime.Token;
import org.apache.commons.io.IOUtils;
import org.objectweb.asm.*;
import org.objectweb.asm.signature.SignatureReader;
import org.objectweb.asm.signature.SignatureVisitor;
/**
* Anmerkung:
@ -32,21 +36,76 @@ import org.antlr.v4.runtime.Token;
public class ASTFactory {
public static ClassOrInterface createClass(java.lang.Class jreClass){
// TODO Inner classes
var methodSignatures = new HashMap<Pair<String, String>, String>();
String classSignature = null;
// Load class with asm to figure out if there's a JavaTX signature
try {
var path = jreClass.getName().replace('.', '/') + ".class";
var classLoader = jreClass.getClassLoader();
if (classLoader != null) {
var bytes = IOUtils.toByteArray(Objects.requireNonNull(classLoader.getResourceAsStream(path)));
var classReader = new ClassReader(bytes);
var classVisitor = new ClassVisitor(Opcodes.ASM7) {
String classSignature;
@Override
public void visitAttribute(Attribute attribute) {
if (attribute.type.equals("JavaTXSignature")) {
classSignature = ((JavaTXSignatureAttribute) attribute).signature;
}
super.visitAttribute(attribute);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
classSignature = signature;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
methodSignatures.put(new Pair<>(name, descriptor), signature);
return new MethodVisitor(Opcodes.ASM7) {
@Override
public void visitAttribute(Attribute attribute) {
if (attribute.type.equals("JavaTXSignature")) {
methodSignatures.put(new Pair<>(name, descriptor), ((JavaTXSignatureAttribute) attribute).signature);
}
super.visitAttribute(attribute);
}
};
}
};
classReader.accept(classVisitor, new Attribute[]{new JavaTXSignatureAttribute()}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
classSignature = classVisitor.classSignature;
}
} catch (IOException e) {
// Skip
}
JavaClassName name = new JavaClassName(jreClass.getName());
List<Method> methoden = new ArrayList<>();
List<de.dhbwstuttgart.syntaxtree.Constructor> konstruktoren = new ArrayList<>();
for(java.lang.reflect.Constructor constructor : jreClass.getConstructors()){
createConstructor(constructor, jreClass).map(c -> konstruktoren.add(c));
var signature = methodSignatures.get(new Pair<>(constructor.getName(), org.objectweb.asm.Type.getConstructorDescriptor(constructor)));
createConstructor(constructor, signature, jreClass).map(c -> konstruktoren.add(c));
}
Set<java.lang.reflect.Method> allMethods = new HashSet<>(Arrays.asList(jreClass.getMethods()));
Set<java.lang.reflect.Method> allDeclaredMethods = new HashSet<>(Arrays.asList(jreClass.getDeclaredMethods()));
Set<java.lang.reflect.Method> allInheritedMethods = new HashSet<>(allMethods);
allInheritedMethods.removeAll(allDeclaredMethods);
for(java.lang.reflect.Method method : allDeclaredMethods){
methoden.add(createMethod(method, jreClass, false));
var signature = methodSignatures.get(new Pair<>(method.getName(), org.objectweb.asm.Type.getMethodDescriptor(method)));
methoden.add(createMethod(method, signature, jreClass, false));
}
for(java.lang.reflect.Method method : allInheritedMethods){
methoden.add(createMethod(method, jreClass, true));
var signature = methodSignatures.get(new Pair<>(method.getName(), org.objectweb.asm.Type.getMethodDescriptor(method)));
methoden.add(createMethod(method, signature, jreClass, true));
}
List<Field> felder = new ArrayList<>();
for(java.lang.reflect.Field field : jreClass.getDeclaredFields()){
@ -72,7 +131,8 @@ public class ASTFactory {
for(Type jreInterface : jreClass.getGenericInterfaces()){
implementedInterfaces.add((RefType) createType(jreInterface));
}
GenericDeclarationList genericDeclarationList = createGenerics(jreClass.getTypeParameters(), jreClass, null);
GenericDeclarationList genericDeclarationList = createGenerics(jreClass.getTypeParameters(), jreClass, null, classSignature);
Token offset = new NullToken(); //Braucht keinen Offset, da diese Klasse nicht aus einem Quellcode geparst wurde
@ -87,7 +147,7 @@ public class ASTFactory {
// return createClass(classType).getType();
//}
private static Optional<de.dhbwstuttgart.syntaxtree.Constructor> createConstructor(Constructor constructor, Class inClass) {
private static Optional<de.dhbwstuttgart.syntaxtree.Constructor> createConstructor(Constructor constructor, String signature, Class inClass) {
String name = constructor.getName();
RefTypeOrTPHOrWildcardOrGeneric returnType = createType(inClass);
Parameter[] jreParams = constructor.getParameters();
@ -101,7 +161,7 @@ public class ASTFactory {
}
ParameterList parameterList = new ParameterList(params, new NullToken());
Block block = new Block(new ArrayList<Statement>(), new NullToken());
GenericDeclarationList gtvDeclarations = createGenerics(constructor.getTypeParameters(), inClass, constructor.getName());
GenericDeclarationList gtvDeclarations = createGenerics(constructor.getTypeParameters(), inClass, constructor.getName(), signature);
Token offset = new NullToken();
int modifier = constructor.getModifiers();
@ -112,7 +172,7 @@ public class ASTFactory {
return Optional.of(new de.dhbwstuttgart.syntaxtree.Constructor(modifier, name,returnType, parameterList, block, gtvDeclarations, offset /*, new ArrayList<>() geloescht PL 2018-11-24 */));
}
public static Method createMethod(java.lang.reflect.Method jreMethod, java.lang.Class inClass, Boolean isInherited){
public static Method createMethod(java.lang.reflect.Method jreMethod, String signature, java.lang.Class inClass, Boolean isInherited){
String name = jreMethod.getName();
RefTypeOrTPHOrWildcardOrGeneric returnType;
Type jreRetType;
@ -133,19 +193,142 @@ public class ASTFactory {
}
ParameterList parameterList = new ParameterList(params, new NullToken());
Block block = new Block(new ArrayList<Statement>(), new NullToken());
GenericDeclarationList gtvDeclarations = createGenerics(jreMethod.getTypeParameters(), inClass, jreMethod.getName());
GenericDeclarationList gtvDeclarations = createGenerics(jreMethod.getTypeParameters(), inClass, jreMethod.getName(), signature);
Token offset = new NullToken();
return new Method(jreMethod.getModifiers(), name,returnType, parameterList, block, gtvDeclarations, offset, isInherited);
}
public static GenericDeclarationList createGenerics(TypeVariable[] typeParameters, Class context, String methodName){
List<de.dhbwstuttgart.syntaxtree.GenericTypeVar> gtvs = new ArrayList<>();
for(TypeVariable jreTV : typeParameters){
de.dhbwstuttgart.syntaxtree.GenericTypeVar gtv = createGeneric(jreTV, jreTV.getName(), context, methodName);
gtvs.add(gtv);
public static GenericDeclarationList createGenerics(TypeVariable[] typeParameters, Class context, String methodName, String signature) {
if (signature == null) {
List<de.dhbwstuttgart.syntaxtree.GenericTypeVar> gtvs = new ArrayList<>();
for(TypeVariable jreTV : typeParameters){
de.dhbwstuttgart.syntaxtree.GenericTypeVar gtv = createGeneric(jreTV, jreTV.getName(), context, methodName);
gtvs.add(gtv);
}
return new GenericDeclarationList(gtvs, new NullToken());
} else {
var res = createGenerics(signature);
return res;
}
return new GenericDeclarationList(gtvs,new NullToken());
}
public static GenericDeclarationList createGenerics(String signature) {
if (signature == null) return new GenericDeclarationList(new ArrayList<>(), new NullToken());
var gtvs = new ArrayList<GenericTypeVar>();
var signatureVisitor = new SignatureVisitor(Opcodes.ASM7) {
List<RefTypeOrTPHOrWildcardOrGeneric> bounds = new ArrayList<>();
final Stack<RefTypeOrTPHOrWildcardOrGeneric> bound = new Stack<>();
final Stack<RefType> classTypes = new Stack<>();
// All hail the mighty visitor pattern
final SignatureVisitor doNothing = new SignatureVisitor(Opcodes.ASM7) {};
char wildcard = '=';
@Override
public SignatureVisitor visitSuperclass() {
return doNothing;
}
@Override
public SignatureVisitor visitParameterType() {
return doNothing;
}
@Override
public SignatureVisitor visitReturnType() {
return doNothing;
}
@Override
public SignatureVisitor visitExceptionType() {
return doNothing;
}
@Override
public void visitFormalTypeParameter(String name) {
bounds = new ArrayList<>();
gtvs.add(new GenericTypeVar(name, bounds, new NullToken(), new NullToken()));
}
@Override
public void visitTypeVariable(String name) {
var refType = new GenericRefType(name, new NullToken());
if (classTypes.isEmpty()) {
((List<RefTypeOrTPHOrWildcardOrGeneric>) gtvs.get(gtvs.size() - 1).getBounds()).add(refType);
} else {
pushType(refType);
}
}
@Override
public void visitClassType(String name) {
var refType = new RefType(new JavaClassName(name.replaceAll("/", ".")), new ArrayList<>(), new NullToken());
classTypes.push(refType);
pushType(refType);
}
void pushType(RefTypeOrTPHOrWildcardOrGeneric refType) {
if (wildcard == SignatureVisitor.SUPER) {
bound.push(new SuperWildcardType(refType, new NullToken()));
} else if (wildcard == SignatureVisitor.EXTENDS) {
bound.push(new ExtendsWildcardType(refType, new NullToken()));
} else {
bound.push(refType);
}
}
@Override
public SignatureVisitor visitTypeArgument(char wildcard) {
this.wildcard = wildcard;
return this;
}
boolean equals(RefTypeOrTPHOrWildcardOrGeneric a, RefTypeOrTPHOrWildcardOrGeneric b) {
if (b instanceof SuperWildcardType wc)
return equals(a, wc.getInnerType());
else if (b instanceof ExtendsWildcardType wc)
return equals(a, wc.getInnerType());
return a == b;
}
@Override
public void visitEnd() {
wildcard = '=';
var classType = (RefType) classTypes.pop();
if (!classTypes.isEmpty()) {
var next = classTypes.peek();
var par = bound.pop();
var toAdd = new ArrayList<RefTypeOrTPHOrWildcardOrGeneric>();
while (!equals(next, par)) {
toAdd.add(par);
par = bound.pop();
}
var element = par;
if (par instanceof WildcardType wc) {
element = wc.getInnerType();
}
Collections.reverse(toAdd);
((RefType) element).getParaList().addAll(toAdd);
bound.push(par);
} else {
if (bound.peek() != classType) {
classType.getParaList().add(bound.pop());
bounds.add(classType);
} else {
bounds.add(bound.pop());
}
}
}
};
var sr = new SignatureReader(signature);
sr.accept(signatureVisitor);
return new GenericDeclarationList(gtvs, new NullToken());
}
private static RefTypeOrTPHOrWildcardOrGeneric createType(java.lang.reflect.Type type){

@ -5,6 +5,8 @@ import de.dhbwstuttgart.syntaxtree.ASTVisitor;
import de.dhbwstuttgart.typeinference.result.ResultSetVisitor;
import org.antlr.v4.runtime.Token;
import java.util.Objects;
/**
* Stellt eine Wildcard mit oberer Grenze dar.
* z.B. void test(? extends Number var){..}
@ -54,9 +56,16 @@ public class ExtendsWildcardType extends WildcardType{
visitor.visit(this);
}
@Override
public int hashCode() {
return Objects.hashCode(this.innerType);
}
@Override
public boolean equals(Object o) {
// TODO Auto-generated method stub
return false;
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ExtendsWildcardType that = (ExtendsWildcardType) o;
return that.innerType.equals(this.innerType);
}
}

@ -4,6 +4,8 @@ import de.dhbwstuttgart.syntaxtree.ASTVisitor;
import de.dhbwstuttgart.typeinference.result.ResultSetVisitor;
import org.antlr.v4.runtime.Token;
import java.util.Objects;
public class GenericRefType extends RefTypeOrTPHOrWildcardOrGeneric
{
private String name;
@ -33,14 +35,20 @@ public class GenericRefType extends RefTypeOrTPHOrWildcardOrGeneric
visitor.visit(this);
}
@Override
public boolean equals(Object o) {
// TODO Auto-generated method stub
return false;
}
@Override
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GenericRefType that = (GenericRefType) o;
return name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String toString()
{
return "GTV " + this.name;

@ -2,9 +2,12 @@ package de.dhbwstuttgart.syntaxtree.type;
import de.dhbwstuttgart.syntaxtree.ASTVisitor;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.typeinference.result.ResultSetVisitor;
import org.antlr.v4.runtime.Token;
import java.util.Objects;
/**
* Stellt eine Wildcard mit unterer Grenze dar.
* z.B. void test(? super Integer var){..}
@ -65,9 +68,16 @@ public class SuperWildcardType extends WildcardType{
visitor.visit(this);
}
@Override
public int hashCode() {
return Objects.hashCode(this.innerType);
}
@Override
public boolean equals(Object o) {
// TODO Auto-generated method stub
return false;
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SuperWildcardType that = (SuperWildcardType) o;
return that.innerType.equals(this.innerType);
}
}

@ -16,8 +16,10 @@ import de.dhbwstuttgart.target.tree.expression.TargetExpression;
import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.constraints.Pair;
import de.dhbwstuttgart.typeinference.result.*;
import org.objectweb.asm.Attribute;
import java.util.*;
import java.util.concurrent.CyclicBarrier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -170,7 +172,6 @@ public class ASTToTargetAST {
for (var pair : simplifiedConstraints) {
if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right)) {
addToPairs(result, new PairTPHsmallerTPH(pair.left, equality.getOrDefault(pair.right, pair.right)));
typeVariables.add(pair.right);
}
}
}
@ -196,7 +197,7 @@ public class ASTToTargetAST {
RefTypeOrTPHOrWildcardOrGeneric T2 = superType;
if (T2 instanceof TypePlaceholder tph) T2 = equality.getOrDefault(tph, tph);
System.out.println("T1s: " + T1s + "\nT2: " + T2);
System.out.println("T1s: " + T1s + " T2: " + T2);
//Ende
superType = methodCall.receiverType;
@ -211,7 +212,7 @@ public class ASTToTargetAST {
var optMethod = findMethod(owner, methodCall.name, methodCall.getArgumentList());
if (optMethod.isEmpty()) return;
var method = optMethod.get();
var generics = generics(owner, method).javaGenerics();
var generics = generics(owner, method).txGenerics();
// transitive and
var all = transitiveClosure(generics);
@ -226,24 +227,24 @@ public class ASTToTargetAST {
// Loop from hell
outer:
for (var tph : typeVariables) {
if (typeVariablesOfClass.contains(tph)) continue;
for (var R1 : typeVariables) {
if (typeVariablesOfClass.contains(R1)) continue;
for (var generic : all) {
if (!(generic.getRight() instanceof TypePlaceholder type))
continue;
for (var pair : simplifiedConstraints) {
if (!(pair.left.equals(tph) && pair.right.equals(generic.getLeft())))
if (!(pair.left.equals(R1) && pair.right.equals(generic.getLeft())))
continue;
for (var tph2 : typeVariables) {
for (var R2 : typeVariables) {
for (var pair2 : simplifiedConstraints) {
if (!(pair2.right.equals(tph2) && pair2.left.equals(type)))
if (!(pair2.right.equals(R2) && pair2.left.equals(type)))
continue;
if (tph.equals(tph2)) continue;
if (!T1s.contains(tph) || !tph2.equals(T2)) continue;
if (R1.equals(R2)) continue;
if (!T1s.contains(R1) || !R2.equals(T2)) continue;
var newPair = new PairTPHsmallerTPH(tph, tph2);
var newPair = new PairTPHsmallerTPH(R1, R2);
newPairs.add(newPair);
if (!containsRelation(result, newPair))
@ -268,7 +269,7 @@ public class ASTToTargetAST {
@Override
public void visit(Assign assign) {
superType = assign.lefSide.getType();
superType = assign.rightSide.getType();
assign.rightSide.accept(this);
}
@ -521,9 +522,6 @@ public class ASTToTargetAST {
super.visit(methodCall);
typeVariables.addAll(findTypeVariables(methodCall.getType(), equality));
}
@Override
public void visit(Assign assign) {}
});
}
@ -554,10 +552,11 @@ public class ASTToTargetAST {
methodFindConstraints(owner, method, simplifiedConstraints, txTypeVariables, classGenerics.userDefinedGenerics, txTypeVariablesOfClass, txResult, txEquality);
{ // Java Generics
eliminateTransitives(javaResult);
var referenced = new HashSet<TypePlaceholder>();
eliminateCycles(javaResult, equality, referenced);
eliminateInfima(javaResult, equality, referenced);
eliminateInfima(javaResult, equality);
var usedTphs = new HashSet<TypePlaceholder>();
// For eliminating inner type variables we need to figure out which ones are actually used
@ -571,11 +570,13 @@ public class ASTToTargetAST {
eliminateInnerTypeVariables(referenced, javaResult);
equalizeTypeVariables(javaResult, equality);
usedTPHsOfMethods.put(method, usedTphs);
addMissingObjectBounds(javaResult, genericsOfClass);
}
{
var referenced = new HashSet<TypePlaceholder>();
// JavaTX Generics
eliminateInfima(txResult, txEquality, referenced);
eliminateTransitives(txResult);
eliminateInfima(txResult, txEquality);
for (var param : method.getParameterList().getFormalparalist()) {
referenced.addAll(findTypeVariables(param.getType(), txEquality));
@ -584,12 +585,36 @@ public class ASTToTargetAST {
referenced.addAll(txTypeVariablesOfClass);
eliminateInnerTypeVariables(referenced, txResult);
addMissingObjectBounds(txResult, txGenericsOfClass);
}
System.out.println(method.name + ": " + txResult + " & " + javaResult);
return generics;
}
private void eliminateChain(Set<ResultPair<?, ?>> result, List<TypePlaceholder> chain) {
for (var pair : new HashSet<>(result)) {
if (pair instanceof PairTPHsmallerTPH ptph && chain.get(chain.size() - 1).equals(ptph.left)) {
if (chain.contains(ptph.right)) return;
var copy = new ArrayList<>(chain);
copy.add(ptph.right);
if (copy.size() > 2)
result.remove(new PairTPHsmallerTPH(chain.get(0), ptph.right));
eliminateChain(result, copy);
}
}
}
void eliminateTransitives(Set<ResultPair<?,?>> result) {
for (var pair : new HashSet<>(result)) if (pair instanceof PairTPHsmallerTPH ptph) {
var first = ptph.left;
var chain = new ArrayList<TypePlaceholder>();
chain.add(ptph.left);
chain.add(ptph.right);
eliminateChain(result, chain);
}
}
void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set<ResultPair<?, ?>> generics, Map<TypePlaceholder, TypePlaceholder> equality) {
if (type instanceof TypePlaceholder tph) {
tph = equality.getOrDefault(tph, tph);
@ -634,34 +659,86 @@ public class ASTToTargetAST {
findAllBounds(field.getType(), txResult, txEquality);
}
eliminateTransitives(javaResult);
eliminateTransitives(txResult);
System.out.println(javaResult);
var referenced = new HashSet<TypePlaceholder>();
eliminateCycles(javaResult, equality, referenced);
eliminateInfima(javaResult, equality, referenced);
eliminateInfima(javaResult, equality);
var txReferenced = new HashSet<TypePlaceholder>();
eliminateInfima(txResult, txEquality, txReferenced);
eliminateInfima(txResult, txEquality);
eliminateInnerTypeVariablesOfClass(classOrInterface, javaResult, equality, referenced);
equalizeTypeVariables(javaResult, equality);
eliminateInnerTypeVariablesOfClass(classOrInterface, txResult, txEquality, txReferenced);
addMissingObjectBounds(javaResult, null);
addMissingObjectBounds(txResult, null);
System.out.println("Class " + classOrInterface.getClassName().getClassName() + ": " + txResult + ", " + javaResult);
return generics;
}
void addMissingObjectBounds(Set<ResultPair<?,?>> result, Set<ResultPair<?, ?>> filter) {
outer: for (var p1 : new HashSet<>(result)) {
if (p1 instanceof PairTPHsmallerTPH ptph) {
for (var p2 : new HashSet<>(result)) {
if (ptph.right.equals(p2.getLeft()))
continue outer;
}
if (filter == null || filter.stream().noneMatch((pair) -> pair.getLeft().equals(ptph.right)))
result.add(new PairTPHequalRefTypeOrWildcardType(ptph.right, OBJECT));
}
}
}
void equalizeTypeVariables(Set<ResultPair<?, ?>> input, Map<TypePlaceholder, TypePlaceholder> equality) {
System.out.println(input);
for (var pair : new HashSet<>(input)) {
if (pair instanceof PairTPHsmallerTPH ptph) {
System.out.println(pair + " " + ptph.left.getVariance() + " " + ptph.right.getVariance());
if (ptph.left.getVariance() == 1 && ptph.right.getVariance() == -1) {
addToEquality(equality, ptph.left, ptph.right);
input.remove(ptph);
for (var pair2 : new HashSet<>(simplifiedConstraints)) {
if (pair2.right.equals(ptph.left)) {
simplifiedConstraints.remove(pair2);
simplifiedConstraints.add(new PairTPHsmallerTPH(pair2.left, ptph.right));
var chain = new ArrayList<TypePlaceholder>();
chain.add(ptph.left);
chain.add(ptph.right);
outer: while (true) {
var added = false;
for (var pair2 : input) {
if (pair2 instanceof PairTPHsmallerTPH ptph2 && ptph2.left.equals(chain.get(chain.size() - 1))) {
if (chain.contains(ptph2.right)) break outer;
chain.add(ptph2.right);
added = true;
}
}
if (!added) break;
}
var variance = chain.get(0).getVariance();
if (variance != 1) continue;
var index = 0;
for (var tph : chain) {
if (variance == 1 && tph.getVariance() == -1) {
variance = -1;
}
if (variance == -1 && tph.getVariance() == 1) {
break;
}
index++;
}
if (variance == 1) continue;
var start = chain.get(0);
var prev = start;
for (var i = 1; i < index; i++) {
var cur = chain.get(i);
addToEquality(equality, cur, start);
input.remove(new PairTPHsmallerTPH(prev, chain.get(i)));
for (var pair2 : new HashSet<>(input)) {
// TODO Maybe this would be unnecessary if we were to add the = constraints later on
if (pair2 instanceof PairTPHequalRefTypeOrWildcardType && pair2.getLeft().equals(cur)) {
input.remove(pair2);
input.add(new PairTPHequalRefTypeOrWildcardType(start, pair2.getRight()));
}
}
prev = chain.get(i);
}
}
}
@ -739,6 +816,15 @@ public class ASTToTargetAST {
var cycles = findCycles(input);
for (var cycle : cycles) {
var newTph = TypePlaceholder.fresh(new NullToken());
var variance = cycle.get(0).getVariance();
for (var tph : cycle) {
if (tph.getVariance() != variance) {
variance = 0;
break;
}
}
newTph.setVariance(variance);
referenced.add(newTph);
addToPairs(input, new PairTPHequalRefTypeOrWildcardType(newTph, OBJECT));
cycle.add(cycle.get(0)); // Make it a complete cycle
@ -752,7 +838,7 @@ public class ASTToTargetAST {
}
}
void eliminateInfima(Set<ResultPair<?, ?>> input, Map<TypePlaceholder, TypePlaceholder> equality, Set<TypePlaceholder> referenced) {
void eliminateInfima(Set<ResultPair<?, ?>> input, Map<TypePlaceholder, TypePlaceholder> equality) {
var foundInfima = false;
do {
foundInfima = false;
@ -767,7 +853,16 @@ public class ASTToTargetAST {
if (infima.size() > 1) {
foundInfima = true;
var newTph = TypePlaceholder.fresh(new NullToken());
referenced.add(newTph);
var variance = infima.stream().findFirst().get().right.getVariance();
for (var pair : infima) {
if (pair.right.getVariance() != variance) {
variance = 0;
break;
}
}
newTph.setVariance(variance);
//referenced.add(newTph);
addToPairs(input, new PairTPHsmallerTPH(left, newTph));
input.removeAll(infima);
for (var infimum : infima) {

@ -22,11 +22,15 @@ public record TargetMethod(int access, String name, TargetBlock block, Signature
}
public static String getSignature(Set<TargetGeneric> generics, List<MethodParameter> parameters, TargetType returnType) {
String ret = "<";
for (var generic : generics) {
ret += generic.name() + ":" + generic.bound().toDescriptor();
String ret = "";
if (generics.size() > 0) {
ret += "<";
for (var generic : generics) {
ret += generic.name() + ":" + generic.bound().toDescriptor();
}
ret += ">";
}
ret += ">(";
ret += "(";
for (var param : parameters) {
ret += param.type().toDescriptor();
}

@ -1,5 +1,6 @@
package de.dhbwstuttgart.util;
import java.util.Objects;
import java.util.Optional;
public class Pair<T, T1> {
@ -22,4 +23,17 @@ public class Pair<T, T1> {
public String toString() {
return "(" + key.toString() + "," + value.toString() + ")\n";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(key, pair.key) && Objects.equals(value, pair.value);
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
}

@ -0,0 +1,105 @@
import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.GenericContext;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.GenericDeclarationList;
import de.dhbwstuttgart.syntaxtree.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.target.generate.ASTToTargetAST;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
public class GenericsParserTest {
@Test
public void testMethodNoGenerics() {
var signature = "()V";
var generics = ASTFactory.createGenerics(signature);
assertEquals(generics, new GenericDeclarationList(List.of(), new NullToken()));
}
@Test
public void testMethodSimpleGenerics() {
var signature = "<T:Ljava/lang/Object;>()V";
var generics = ASTFactory.createGenerics(signature);
assertEquals(generics, new GenericDeclarationList(
List.of(new GenericTypeVar("T", List.of(ASTToTargetAST.OBJECT), new NullToken(), new NullToken())), new NullToken())
);
}
@Test
public void testMethodExtends() {
var signature = "<T:Ljava/lang/Class<TT;>;>()V";
var generics = ASTFactory.createGenerics(signature);
assertEquals(generics, new GenericDeclarationList(
List.of(new GenericTypeVar("T", List.of(
new RefType(new JavaClassName("java.lang.Class"), List.of(new GenericRefType("T", new NullToken())), new NullToken())),
new NullToken(), new NullToken())), new NullToken()
)
);
}
@Test
public void testMethodVariance() {
var signature = "<T:Ljava/lang/Class<+TT;>;>()V";
var generics = ASTFactory.createGenerics(signature);
assertEquals(generics, new GenericDeclarationList(
List.of(new GenericTypeVar("T", List.of(
new RefType(new JavaClassName("java.lang.Class"), List.of(
new ExtendsWildcardType(new GenericRefType("T", new NullToken()), new NullToken())
), new NullToken())),
new NullToken(), new NullToken())),
new NullToken())
);
}
@Test
public void testMethodTypeVars() {
var signature = "<A:TB;B:LClassA;C:LClassB<TA;>;D:TB;>()V";
var generics = ASTFactory.createGenerics(signature);
assertEquals(generics, new GenericDeclarationList(
List.of(
new GenericTypeVar("A", List.of(new GenericRefType("B", new NullToken())), new NullToken(), new NullToken()),
new GenericTypeVar("B", List.of(new RefType(new JavaClassName("ClassA"), new NullToken())), new NullToken(), new NullToken()),
new GenericTypeVar("C", List.of(new RefType(new JavaClassName("ClassB"),
List.of(new GenericRefType("A", new NullToken())),
new NullToken())), new NullToken(), new NullToken()
),
new GenericTypeVar("D", List.of(new GenericRefType("B", new NullToken())), new NullToken(), new NullToken())
),
new NullToken()
));
}
@Test
public void testMethodComplex() {
var signature = "<T:LClassA<+TT;LClassB<+LClassA;-LClassC<LClassA;>;>;>;U:LClassC<LClassC;>;>()V";
var generics = ASTFactory.createGenerics(signature);
assertEquals(generics, new GenericDeclarationList(
List.of(new GenericTypeVar("T", List.of(
new RefType(new JavaClassName("ClassA"), List.of(
new ExtendsWildcardType(new GenericRefType("T", new NullToken()), new NullToken()),
new RefType(new JavaClassName("ClassB"), List.of(
new ExtendsWildcardType(new RefType(new JavaClassName("ClassA"), new NullToken()), new NullToken()),
new SuperWildcardType(
new RefType(new JavaClassName("ClassC"), List.of(
new RefType(new JavaClassName("ClassA"), new NullToken())
), new NullToken()), new NullToken())
), new NullToken())
), new NullToken())
), new NullToken(), new NullToken()),
new GenericTypeVar("U", List.of(
new RefType(new JavaClassName("ClassC"), List.of(
new RefType(new JavaClassName("ClassC"), new NullToken())
), new NullToken())
), new NullToken(), new NullToken())),
new NullToken()
));
}
}

@ -575,6 +575,11 @@ public class TestComplete {
assertEquals(fstArgm2, m2.getGenericReturnType());
}
@Test
public void testTXGenerics() throws Exception {
var classFiles = generateClassFiles(new ByteArrayClassLoader(), "TXGenerics.jav");
var instance = classFiles.get("TXGenerics").getDeclaredConstructor().newInstance();
}
@Test
public void typedIdTest() throws Exception {

@ -90,16 +90,15 @@ public class TestGenerics {
var generics = result.genericsResults.get(0);
assertEquals(0, generics.get(result.clazz).size());
assertEquals(2, generics.get(m).size());
assertEquals(1, generics.get(m).size());
assertEquals(2, generics.get(main).size());
var N = generics.getBounds(m.getParameterList().getParameterAt(0).getType(), result.clazz, m);
var NChain = new BoundsList(new Bound(true, ASTToTargetAST.OBJECT));
assertEquals(N, NChain);
var N2 = generics.getBounds(m.getReturnType(), result.clazz, m);
var Q = generics.getBounds(m.getReturnType(), result.clazz, m);
var QChain = new BoundsList(new Bound(true, TypePlaceholder.of("N")), new Bound(true, ASTToTargetAST.OBJECT));
assertEquals(Q, QChain);
var NChain = new BoundsList(new Bound(true, ASTToTargetAST.OBJECT));
assertEquals(N, N2);
assertEquals(N2, NChain);
var R = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main);
assertEquals(R, NChain);
@ -304,17 +303,18 @@ public class TestGenerics {
var main = result.findMethod("main");
var generics = result.genericsResults.get(0);
var Q = generics.getBounds(id.getReturnType(), result.clazz, id);
var N = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id);
assertEquals(Q, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT)));
assertEquals(N, new BoundsList(new Bound(true, TypePlaceholder.of("Q")), new Bound(true, ASTToTargetAST.OBJECT)));
var O = generics.getBounds(id.getReturnType(), result.clazz, id);
var O2 = generics.getBounds(id.getParameterList().getParameterAt(0).getType(), result.clazz, id);
assertEquals(O, O2);
assertEquals(O2, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT)));
var Q2 = generics.getBounds(main.getReturnType(), result.clazz, main);
var R = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main);
var S = generics.getBounds(main.getParameterList().getParameterAt(1).getType(), result.clazz, main);
assertEquals(Q2, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT)));
assertEquals(R, new BoundsList(new Bound(true, TypePlaceholder.of("Q")), new Bound(true, ASTToTargetAST.OBJECT)));
assertEquals(S, new BoundsList(new Bound(true, TypePlaceholder.of("Q")), new Bound(true, ASTToTargetAST.OBJECT)));
// TODO Maybe test in other ways if the parameter generics equals the return generics
var S = generics.getBounds(main.getReturnType(), result.clazz, main);
var S2 = generics.getBounds(main.getParameterList().getParameterAt(0).getType(), result.clazz, main);
var T = generics.getBounds(main.getParameterList().getParameterAt(1).getType(), result.clazz, main);
assertEquals(S, S2);
assertEquals(S2, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT)));
assertEquals(T, new BoundsList(new Bound(true, ASTToTargetAST.OBJECT)));
}
@Test