diff --git a/resources/bytecode/javFiles/OLFun.jav b/resources/bytecode/javFiles/OLFun.jav index adf8c6a8..50d30b73 100644 --- a/resources/bytecode/javFiles/OLFun.jav +++ b/resources/bytecode/javFiles/OLFun.jav @@ -9,5 +9,11 @@ public class OLFun { //f = x -> {return x + x;}; m(f, x) { x = f.apply(x+x); + return x; + } + + m2(y) { + m(x -> x * 2, y); + return; } } \ No newline at end of file diff --git a/resources/bytecode/javFiles/OLFun2.jav b/resources/bytecode/javFiles/OLFun2.jav index 406c0d98..6b6f38ac 100644 --- a/resources/bytecode/javFiles/OLFun2.jav +++ b/resources/bytecode/javFiles/OLFun2.jav @@ -8,6 +8,6 @@ public class OLFun2 { x; m(f){ - x = f.apply(x + x) + x = f.apply(x + x); } } \ No newline at end of file diff --git a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java index d32aa851..df56b333 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -82,6 +82,13 @@ public class Codegen { } } + private void popValue(State state, TargetType type) { + if (type.equals(TargetType.Double) || type.equals(TargetType.Long)) + state.mv.visitInsn(POP2); + else + state.mv.visitInsn(POP); + } + private void boxPrimitive(State state, TargetType type) { var mv = state.mv; if (type.equals(TargetType.Boolean) || type.equals(TargetType.boolean_)) { @@ -705,11 +712,15 @@ public class Codegen { } else { var name = "lambda$" + lambdaCounter++; var parameters = new ArrayList<>(lambda.captures()); - parameters.addAll(lambda.params()); + parameters.addAll(lambda.params().stream() + .map(param -> param.type() instanceof TargetGenericType ? new MethodParameter(TargetType.Object, param.name()) : param) + .toList()); impl = new TargetMethod( - 0, name, Set.of(), - parameters, lambda.returnType(), lambda.block() + 0, name, Set.of(), Set.of(), + parameters, + lambda.returnType() instanceof TargetGenericType ? TargetType.Object : lambda.returnType(), + lambda.block() ); generateMethod(impl); lambdas.put(lambda, impl); @@ -736,7 +747,9 @@ public class Codegen { var params = new ArrayList(); params.add(new TargetRefType(clazz.qualifiedName())); - params.addAll(lambda.captures().stream().map(MethodParameter::type).toList()); + params.addAll(lambda.captures().stream() + .map(MethodParameter::type) + .toList()); var descriptor = TargetMethod.getDescriptor(lambda.type(), params.toArray(TargetType[]::new)); mv.visitVarInsn(ALOAD, 0); @@ -760,9 +773,11 @@ public class Codegen { for (var e : block.statements()) { generate(state, e); if (e instanceof TargetMethodCall) { - if (e.type() != null) mv.visitInsn(POP); - } else if (e instanceof TargetStatementExpression) { - mv.visitInsn(POP); + if (e.type() != null) popValue(state, e.type()); + } else if (e instanceof TargetAssign) { + mv.visitInsn(POP); // TODO Nasty fix, we don't know if it is a primitive double or long + } else if (e instanceof TargetStatementExpression se) { + popValue(state, se.type()); } } state.exitScope(); @@ -879,7 +894,7 @@ public class Codegen { if (_for.increment() != null) { generate(state, _for.increment()); if (_for.increment().type() != null) { - mv.visitInsn(POP); + popValue(state, _for.increment().type()); } } mv.visitJumpInsn(GOTO, start); @@ -980,6 +995,9 @@ public class Codegen { private void generateConstructor(TargetConstructor constructor) { MethodVisitor mv = cw.visitMethod(constructor.access() | ACC_PUBLIC, "", constructor.getDescriptor(), constructor.getSignature(), null); + if (!constructor.txGenerics().isEmpty()) + mv.visitAttribute(new JavaTXSignatureAttribute(constructor.getTXSignature())); + mv.visitCode(); var state = new State(null, mv, 1); for (var param: constructor.parameters()) @@ -1004,6 +1022,9 @@ public class Codegen { private void generateMethod(TargetMethod method) { // 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.txGenerics().isEmpty()) + mv.visitAttribute(new JavaTXSignatureAttribute(method.getTXSignature())); + mv.visitCode(); var state = new State(method.returnType(), mv, method.isStatic() ? 0 : 1); for (var param: method.parameters()) @@ -1015,9 +1036,9 @@ public class Codegen { mv.visitEnd(); } - private static String generateSignature(TargetClass clazz) { + private static String generateSignature(TargetClass clazz, Set generics) { String ret = "<"; - for (var generic : clazz.generics()) { + for (var generic : generics) { ret += generic.name() + ":" + generic.bound().toDescriptor(); } ret += ">"; @@ -1028,9 +1049,12 @@ public class Codegen { public byte[] generate() { cw.visit(V1_8, clazz.modifiers() | ACC_PUBLIC | ACC_SUPER, clazz.qualifiedName(), - generateSignature(clazz), clazz.superType() != null ? clazz.superType().getInternalName(): "java/lang/Object", + generateSignature(clazz, clazz.generics()), clazz.superType() != null ? clazz.superType().getInternalName(): "java/lang/Object", clazz.implementingInterfaces().stream().map(TargetType::toSignature).toArray(String[]::new) ); + if (!clazz.txGenerics().isEmpty()) + cw.visitAttribute(new JavaTXSignatureAttribute(generateSignature(clazz, clazz.txGenerics()))); + clazz.fields().forEach(this::generateField); clazz.constructors().forEach(this::generateConstructor); clazz.methods().forEach(this::generateMethod); diff --git a/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java b/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java index c5ae16f7..49c9b736 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java @@ -31,7 +31,7 @@ public class FunNGenerator { private static String applyDescriptor(TargetType a) { return a.toDescriptor(); } private static String applySignature(TargetType a) { return a.toSignature(); } - private static String applyNameDescriptor(TargetType a){ return a instanceof TargetGenericType ? "LTPH;" : String.format("L%s;", applySignature(a)); } + private static String applyNameDescriptor(TargetType a){ return a instanceof TargetGenericType ? "LTPH;" : String.format("%s;", applySignature(a)); } public static byte[] generateSuperBytecode(int numberArguments) { StringBuilder superFunNClassSignature = new StringBuilder("<"); @@ -40,13 +40,15 @@ public class FunNGenerator { for (int currentParameter = 1; currentParameter <= numberArguments; currentParameter++){ superFunNClassSignature.append(String.format("%s%d:%s",argumentGenericBase, currentParameter, objectSignature)); - superFunNMethodSignature.append(String.format("T%s;", applySignature(new TargetRefType(argumentGenericBase + currentParameter)))); + superFunNMethodSignature.append(String.format("%s", applySignature(new TargetRefType(argumentGenericBase + currentParameter)))); superFunNMethodDescriptor.append(objectSignature); } superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature)); - superFunNMethodSignature.append(String.format(")T%s;", applySignature(new TargetRefType(returnGeneric)))); + superFunNMethodSignature.append(String.format(")%s", applySignature(new TargetRefType(returnGeneric)))); superFunNMethodDescriptor.append(String.format(")%s", objectSignature)); + System.out.println(superFunNMethodSignature); + ClassWriter classWriter = new ClassWriter(0); MethodVisitor methodVisitor; classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSuperClassName(numberArguments), superFunNClassSignature.toString(), objectSuperType, null); diff --git a/src/main/java/de/dhbwstuttgart/bytecode/JavaTXSignatureAttribute.java b/src/main/java/de/dhbwstuttgart/bytecode/JavaTXSignatureAttribute.java index f185494c..cc9d1487 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/JavaTXSignatureAttribute.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/JavaTXSignatureAttribute.java @@ -2,6 +2,9 @@ package de.dhbwstuttgart.bytecode; import org.objectweb.asm.*; +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; + public class JavaTXSignatureAttribute extends Attribute { final String signature; @@ -12,7 +15,9 @@ public class JavaTXSignatureAttribute extends Attribute { @Override protected Attribute read(ClassReader classReader, int offset, int length, char[] charBuffer, int codeAttributeOffset, Label[] labels) { - return super.read(classReader, offset, length, charBuffer, codeAttributeOffset, labels); + var data = new byte[length]; + System.arraycopy(classReader.b, offset, data, 0, length); + return new JavaTXSignatureAttribute(new String(data, StandardCharsets.UTF_8)); } @Override diff --git a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/TypeGenerator.java b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/TypeGenerator.java index d55e9349..0bf04247 100644 --- a/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/TypeGenerator.java +++ b/src/main/java/de/dhbwstuttgart/parser/SyntaxTreeGenerator/TypeGenerator.java @@ -2,6 +2,7 @@ package de.dhbwstuttgart.parser.SyntaxTreeGenerator; import de.dhbwstuttgart.exceptions.NotImplementedException; import de.dhbwstuttgart.exceptions.TypeinferenceException; +import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.parser.antlr.Java8Parser; import de.dhbwstuttgart.parser.antlr.Java8Parser.UnannClassType_lfno_unannClassOrInterfaceTypeContext; import de.dhbwstuttgart.parser.scope.GenericsRegistry; @@ -169,7 +170,7 @@ public class TypeGenerator { return new SuperWildcardType(convert(wildcardContext.wildcardBounds().referenceType(), reg, generics), wildcardContext.getStart()); } }else{ - throw new NotImplementedException(); //Wildcard ohne Bound + return new ExtendsWildcardType(new RefType(new JavaClassName("Object"), new NullToken()), wildcardContext.getStart()); } } diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 95395f71..86c94f7e 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -46,6 +46,7 @@ public class ASTToTargetAST { Set simplifiedConstraints = new HashSet<>(); Map concreteTypes = new HashMap<>(); Map equality = new HashMap<>(); + Map txEquality = new HashMap<>(); Sigma(ResultSet constraints) { ASTToTargetAST.this.sigma = this; @@ -55,6 +56,7 @@ public class ASTToTargetAST { simplifiedConstraints.add(p); } else if (constraint instanceof PairTPHEqualTPH p) { equality.put(p.getLeft(), p.getRight()); + txEquality.put(p.getLeft(), p.getRight()); } else if (constraint instanceof PairTPHequalRefTypeOrWildcardType p) { concreteTypes.put(this.equality.getOrDefault(p.left, p.left), p.right); } @@ -63,22 +65,22 @@ public class ASTToTargetAST { System.out.println("Simplified constraints: " + simplifiedConstraints); } - Set findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type) { + Set findTypeVariables(RefTypeOrTPHOrWildcardOrGeneric type, Map equality) { var result = new HashSet(); if (type instanceof TypePlaceholder tph) { tph = equality.getOrDefault(tph, tph); if (concreteTypes.containsKey(tph)) { - result.addAll(findTypeVariables(concreteTypes.get(tph))); + result.addAll(findTypeVariables(concreteTypes.get(tph), equality)); return result; } result.add(tph); } else if (type instanceof RefType refType) { for (var t : refType.getParaList()) - result.addAll(findTypeVariables(t)); + result.addAll(findTypeVariables(t, equality)); } else if (type instanceof ExtendsWildcardType wildcardType) { - result.addAll(findTypeVariables(wildcardType.getInnerType())); + result.addAll(findTypeVariables(wildcardType.getInnerType(), equality)); } else if (type instanceof SuperWildcardType wildcardType) { - result.addAll(findTypeVariables(wildcardType.getInnerType())); + result.addAll(findTypeVariables(wildcardType.getInnerType(), equality)); } return result; } @@ -135,143 +137,109 @@ public class ASTToTargetAST { return all; } - // Family of generated Generics - Generics generics(ClassOrInterface owner, Method method) { - if (computedGenericsOfMethods.containsKey(method)) - return computedGenericsOfMethods.get(method); - - Set> txResult = new HashSet<>(); - Set> javaResult = new HashSet<>(); - var generics = new Generics(javaResult, txResult); - computedGenericsOfMethods.put(method, generics); - - var genericsOfClass = generics(owner).txGenerics(); - var simplifiedConstraints = new HashSet<>(this.simplifiedConstraints); - - HashSet typeVariables = new HashSet<>(); - HashSet typeVariablesOfClass = new HashSet<>(); - - for (var pair : genericsOfClass) { - typeVariablesOfClass.add((TypePlaceholder) pair.getLeft()); - } - - typeVariables.addAll(findTypeVariables(method.getReturnType())); - for (var arg : method.getParameterList().getFormalparalist()) { - typeVariables.addAll(findTypeVariables(arg.getType())); - } - - method.block.accept(new TracingStatementVisitor() { - @Override - public void visit(LocalVarDecl localVarDecl) { - typeVariables.addAll(findTypeVariables(localVarDecl.getType())); - } - - @Override - public void visit(MethodCall methodCall) { - super.visit(methodCall); - typeVariables.addAll(findTypeVariables(methodCall.getType())); - } - - @Override - public void visit(Assign assign) {} - }); - - + private void methodFindConstraints( + ClassOrInterface owner, Method method, + Set simplifiedConstraints, + HashSet typeVariables, + HashSet typeVariablesOfClass, + Set> result, + Map equality + ) { // Type variables with bounds that are also type variables of the method for (var typeVariable : new HashSet<>(typeVariables)) { if (typeVariablesOfClass.contains(typeVariable)) continue; for (var pair : simplifiedConstraints) { if (pair.left.equals(typeVariable) && typeVariables.contains(pair.right)) { - addToPairs(txResult, new PairTPHsmallerTPH(pair.left, equality.getOrDefault(pair.right, pair.right))); + addToPairs(result, new PairTPHsmallerTPH(pair.left, equality.getOrDefault(pair.right, pair.right))); typeVariables.add(pair.right); } } } method.block.accept(new TracingStatementVisitor() { - - private RefTypeOrTPHOrWildcardOrGeneric superType = new Void(new NullToken()); - - @Override - public void visit(MethodCall methodCall) { - //Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg - //betrachtet werden muessen - //Definition 7.2 (Family of generated generics). T1 <. R1 <.^∗ R2 <. T2 - Set T1s = - methodCall.getArgumentList() - .getArguments() - .stream() - .map(TypableStatement::getType) - .collect(Collectors.toCollection(HashSet::new)) - .stream().filter(x -> x instanceof TypePlaceholder) - .map(tph -> equality.getOrDefault(tph, (TypePlaceholder) tph)) - .collect(Collectors.toCollection(HashSet::new)); - RefTypeOrTPHOrWildcardOrGeneric T2 = superType; + + private RefTypeOrTPHOrWildcardOrGeneric superType = new Void(new NullToken()); + + @Override + public void visit(MethodCall methodCall) { + //Anfang es werden Paare von TPHs gespeichert, die bei den Generated Generics ueber die Methodengrenzen hinweg + //betrachtet werden muessen + //Definition 7.2 (Family of generated generics). T1 <. R1 <.^∗ R2 <. T2 + Set T1s = + methodCall.getArgumentList() + .getArguments() + .stream() + .map(TypableStatement::getType) + .collect(Collectors.toCollection(HashSet::new)) + .stream().filter(x -> x instanceof TypePlaceholder) + .map(tph -> equality.getOrDefault(tph, (TypePlaceholder) tph)) + .collect(Collectors.toCollection(HashSet::new)); + RefTypeOrTPHOrWildcardOrGeneric T2 = superType; if (T2 instanceof TypePlaceholder tph) T2 = equality.getOrDefault(tph, tph); - System.out.println("T1s: " + T1s + "\nT2: " + T2); - //Ende - - superType = methodCall.receiverType; - methodCall.receiver.accept(this); - for(int i = 0;i>(); - for (var generic : all) { - toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft())); - } - all.addAll(toAdd); + // transitive and + var all = transitiveClosure(generics); + // reflexive + var toAdd = new HashSet>(); + for (var generic : all) { + toAdd.add(new PairTPHsmallerTPH((TypePlaceholder) generic.getLeft(), (TypePlaceholder) generic.getLeft())); + } + all.addAll(toAdd); - HashSet newPairs = new HashSet<>(); + HashSet newPairs = new HashSet<>(); - // Loop from hell - outer: - for (var tph : typeVariables) { - if (typeVariablesOfClass.contains(tph)) continue; - for (var generic : all) { - if (!(generic.getRight() instanceof TypePlaceholder type)) - continue; + // Loop from hell + outer: + for (var tph : typeVariables) { + if (typeVariablesOfClass.contains(tph)) 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()))) - continue; + for (var pair : simplifiedConstraints) { + if (!(pair.left.equals(tph) && pair.right.equals(generic.getLeft()))) + continue; - for (var tph2 : typeVariables) { - for (var pair2 : simplifiedConstraints) { - if (!(pair2.right.equals(tph2) && pair2.left.equals(type))) - continue; - if (tph.equals(tph2)) continue; - if (!T1s.contains(tph) || !tph2.equals(T2)) continue; + for (var tph2 : typeVariables) { + for (var pair2 : simplifiedConstraints) { + if (!(pair2.right.equals(tph2) && pair2.left.equals(type))) + continue; + if (tph.equals(tph2)) continue; + if (!T1s.contains(tph) || !tph2.equals(T2)) continue; - var newPair = new PairTPHsmallerTPH(tph, tph2); - newPairs.add(newPair); + var newPair = new PairTPHsmallerTPH(tph, tph2); + newPairs.add(newPair); - if (!containsRelation(txResult, newPair)) - addToPairs(txResult, newPair); - continue outer; - } - } - } - } - } - simplifiedConstraints.addAll(newPairs); - } - } - } + if (!containsRelation(result, newPair)) + addToPairs(result, newPair); + continue outer; + } + } + } + } + } + simplifiedConstraints.addAll(newPairs); + } + } + } @Override @@ -474,18 +442,19 @@ public class ASTToTargetAST { } if (minimalPair != null) - addToPairs(txResult, minimalPair); + addToPairs(result, minimalPair); } + // All unbounded type variables (bounds not in method) outer: for (var typeVariable : typeVariables) { if (typeVariablesOfClass.contains(typeVariable)) continue; - for (var pair : txResult) { + for (var pair : result) { if (pair.getLeft().equals(typeVariable)) continue outer; } - addToPairs(txResult, new PairTPHequalRefTypeOrWildcardType(typeVariable, OBJECT)); + addToPairs(result, new PairTPHequalRefTypeOrWildcardType(typeVariable, OBJECT)); } // All unbounded bounds @@ -496,44 +465,112 @@ public class ASTToTargetAST { continue outer; } if (!typeVariablesOfClass.contains(pair.right) && typeVariables.contains(pair.right)) { - addToPairs(txResult, new PairTPHequalRefTypeOrWildcardType(pair.right, OBJECT)); + addToPairs(result, new PairTPHequalRefTypeOrWildcardType(pair.right, OBJECT)); } } + } - javaResult.addAll(txResult); // Same result until here - eliminateCycles(javaResult); - eliminateInfima(javaResult); - eliminateInfima(txResult); + private void methodFindTypeVariables( + Method method, + Set> genericsOfClass, + Set typeVariablesOfClass, + Set typeVariables, + Map equality + ) { - // For eliminating inner type variables we need to figure out which ones are actually used - // TODO Maybe don't do this twice? - var usedTphs = new HashSet(); - for (var param : method.getParameterList().getFormalparalist()) { - usedTphs.addAll(findTypeVariables(param.getType())); + for (var pair : genericsOfClass) { + typeVariablesOfClass.add((TypePlaceholder) pair.getLeft()); } - usedTphs.addAll(findTypeVariables(method.getReturnType())); - var referenced = new HashSet<>(usedTphs); - referenced.addAll(typeVariablesOfClass); - eliminateInnerTypeVariables(referenced, javaResult); - eliminateInnerTypeVariables(referenced, txResult); + typeVariables.addAll(findTypeVariables(method.getReturnType(), equality)); + for (var arg : method.getParameterList().getFormalparalist()) { + typeVariables.addAll(findTypeVariables(arg.getType(), equality)); + } - usedTPHsOfMethods.put(method, usedTphs); + method.block.accept(new TracingStatementVisitor() { + @Override + public void visit(LocalVarDecl localVarDecl) { + typeVariables.addAll(findTypeVariables(localVarDecl.getType(), equality)); + } - equalizeTypeVariables(javaResult); + @Override + public void visit(MethodCall methodCall) { + super.visit(methodCall); + typeVariables.addAll(findTypeVariables(methodCall.getType(), equality)); + } + + @Override + public void visit(Assign assign) {} + }); + } + + // Family of generated Generics + Generics generics(ClassOrInterface owner, Method method) { + if (computedGenericsOfMethods.containsKey(method)) + return computedGenericsOfMethods.get(method); + + Set> txResult = new HashSet<>(); + Set> javaResult = new HashSet<>(); + var generics = new Generics(javaResult, txResult); + computedGenericsOfMethods.put(method, generics); + + var genericsOfClass = generics(owner).javaGenerics(); + var simplifiedConstraints = new HashSet<>(this.simplifiedConstraints); + + HashSet txTypeVariables = new HashSet<>(); + HashSet javaTypeVariables = new HashSet<>(); + HashSet txTypeVariablesOfClass = new HashSet<>(); + HashSet javaTypeVariablesOfClass = new HashSet<>(); + + methodFindTypeVariables(method, genericsOfClass, javaTypeVariablesOfClass, javaTypeVariables, equality); + methodFindTypeVariables(method, genericsOfClass, txTypeVariablesOfClass, txTypeVariables, txEquality); + + methodFindConstraints(owner, method, simplifiedConstraints, javaTypeVariables, javaTypeVariablesOfClass, javaResult, equality); + methodFindConstraints(owner, method, simplifiedConstraints, txTypeVariables, txTypeVariablesOfClass, txResult, txEquality); + + { // Java Generics + eliminateCycles(javaResult, equality); + eliminateInfima(javaResult, equality); + + var usedTphs = new HashSet(); + // For eliminating inner type variables we need to figure out which ones are actually used + // TODO Maybe don't do this twice? + for (var param : method.getParameterList().getFormalparalist()) { + usedTphs.addAll(findTypeVariables(param.getType(), equality)); + } + usedTphs.addAll(findTypeVariables(method.getReturnType(), equality)); + var referenced = new HashSet<>(usedTphs); + referenced.addAll(javaTypeVariablesOfClass); + + eliminateInnerTypeVariables(referenced, javaResult); + equalizeTypeVariables(javaResult, equality); + usedTPHsOfMethods.put(method, usedTphs); + } + { // JavaTX Generics + eliminateInfima(txResult, txEquality); + + var referenced = new HashSet(); + for (var param : method.getParameterList().getFormalparalist()) { + referenced.addAll(findTypeVariables(param.getType(), txEquality)); + } + referenced.addAll(findTypeVariables(method.getReturnType(), txEquality)); + referenced.addAll(txTypeVariablesOfClass); + + eliminateInnerTypeVariables(referenced, txResult); + } System.out.println(method.name + ": " + txResult + " & " + javaResult); return generics; } - void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set> generics) { + void findAllBounds(RefTypeOrTPHOrWildcardOrGeneric type, Set> generics, Map equality) { if (type instanceof TypePlaceholder tph) { tph = equality.getOrDefault(tph, tph); var concreteType = concreteTypes.get(tph); if (concreteType != null) { - findAllBounds(concreteType, generics); + findAllBounds(concreteType, generics, equality); return; } @@ -545,7 +582,7 @@ public class ASTToTargetAST { var pair = new PairTPHsmallerTPH(tph, right); if (!generics.contains(pair)) { generics.add(pair); - findAllBounds(right, generics); + findAllBounds(right, generics, equality); found = true; } } @@ -553,7 +590,7 @@ public class ASTToTargetAST { if (!found) generics.add(new PairTPHequalRefTypeOrWildcardType(tph, OBJECT)); } else if (type instanceof RefType refType) { - refType.getParaList().forEach(t -> findAllBounds(t, generics)); + refType.getParaList().forEach(t -> findAllBounds(t, generics, equality)); } } @@ -564,25 +601,29 @@ public class ASTToTargetAST { Set> txResult = new HashSet<>(); Set> javaResult = new HashSet<>(); var generics = new Generics(javaResult, txResult); - - for (var field : classOrInterface.getFieldDecl()) { - findAllBounds(field.getType(), txResult); - } computedGenericsOfClasses.put(classOrInterface, generics); - javaResult.addAll(txResult); - eliminateCycles(javaResult); - eliminateInfima(txResult); - eliminateInfima(javaResult); - eliminateInnerTypeVariablesOfClass(classOrInterface, txResult); - eliminateInnerTypeVariablesOfClass(classOrInterface, javaResult); - equalizeTypeVariables(javaResult); + for (var field : classOrInterface.getFieldDecl()) { + findAllBounds(field.getType(), javaResult, equality); + findAllBounds(field.getType(), txResult, txEquality); + } + + { // Java Generics + eliminateCycles(javaResult, equality); + eliminateInfima(javaResult, equality); + eliminateInnerTypeVariablesOfClass(classOrInterface, javaResult, equality); + equalizeTypeVariables(javaResult, equality); + } + { // TX Generics + eliminateInfima(txResult, txEquality); + eliminateInnerTypeVariablesOfClass(classOrInterface, txResult, txEquality); + } System.out.println("Class " + classOrInterface.getClassName().getClassName() + ": " + txResult); return generics; } - void equalizeTypeVariables(Set> input) { + void equalizeTypeVariables(Set> input, Map equality) { for (var pair : new HashSet<>(input)) { if (pair instanceof PairTPHsmallerTPH ptph) { if (ptph.left.getVariance() == 1 && ptph.right.getVariance() == -1) { @@ -599,24 +640,24 @@ public class ASTToTargetAST { } } - void findTphs(RefTypeOrTPHOrWildcardOrGeneric type, Set tphs) { + void findTphs(RefTypeOrTPHOrWildcardOrGeneric type, Set tphs, Map equality) { if (type instanceof RefType refType) { - refType.getParaList().forEach(t -> findTphs(t, tphs)); + refType.getParaList().forEach(t -> findTphs(t, tphs, equality)); } else if (type instanceof TypePlaceholder tph) { tph = equality.getOrDefault(tph, tph); var concreteType = concreteTypes.get(tph); if (concreteType != null) { - findTphs(concreteType, tphs); + findTphs(concreteType, tphs, equality); return; } tphs.add(tph); } } - void eliminateInnerTypeVariablesOfClass(ClassOrInterface classOrInterface, Set> input) { + void eliminateInnerTypeVariablesOfClass(ClassOrInterface classOrInterface, Set> input, Map equality) { Set referenced = new HashSet<>(); for (var field : classOrInterface.getFieldDecl()) { - findTphs(field.getType(), referenced); + findTphs(field.getType(), referenced, equality); } for (var method : classOrInterface.getMethods()) { generics(classOrInterface, method); @@ -668,7 +709,7 @@ public class ASTToTargetAST { input.addAll(output); } - void eliminateCycles(Set> input) { + void eliminateCycles(Set> input, Map equality) { var cycles = findCycles(input); for (var cycle : cycles) { var newTph = TypePlaceholder.fresh(new NullToken()); @@ -684,7 +725,7 @@ public class ASTToTargetAST { } } - void eliminateInfima(Set> input) { + void eliminateInfima(Set> input, Map equality) { var foundInfima = false; do { foundInfima = false; @@ -726,19 +767,19 @@ public class ASTToTargetAST { } while (foundInfima); } - RefTypeOrTPHOrWildcardOrGeneric getType(RefTypeOrTPHOrWildcardOrGeneric type) { + RefTypeOrTPHOrWildcardOrGeneric getType(RefTypeOrTPHOrWildcardOrGeneric type, Map equality) { if (type instanceof TypePlaceholder tph) { if (equality.containsKey(tph)) { - return getType(equality.get(tph)); + return getType(equality.get(tph), equality); } return concreteTypes.getOrDefault(tph, tph); } return type; } - TargetType getTargetType(TypePlaceholder tph) { + TargetType getTargetType(TypePlaceholder tph, Map equality) { if (equality.containsKey(tph)) { - return getTargetType(equality.get(tph)); + return getTargetType(equality.get(tph), equality); } var type = concreteTypes.get(tph); if (type == null) return new TargetGenericType(tph.getName()); @@ -898,18 +939,21 @@ public class ASTToTargetAST { public TargetClass convert(ClassOrInterface input) { currentClass = input; - Set generics = new HashSet<>(); + Set javaGenerics = new HashSet<>(); + Set txGenerics = new HashSet<>(); var genericsIter = input.getGenerics().iterator(); if (genericsIter.hasNext()) { // Add empty set of generics to cache so that it doesn't try to calculate it later sigma.computedGenericsOfClasses.put(input, new Generics(new HashSet<>(), new HashSet<>())); while (genericsIter.hasNext()) { var next = genericsIter.next(); - generics.addAll(convert(next)); + javaGenerics.addAll(convert(next)); } } else { // Generate generics only if there are no user defined ones - generics = convert(sigma.generics(input).javaGenerics()); + var generics = sigma.generics(input); + javaGenerics = convert(generics.javaGenerics()); + txGenerics = convert(generics.txGenerics()); } TargetBlock fieldInitializer = null; @@ -918,7 +962,7 @@ public class ASTToTargetAST { TargetBlock finalFieldInitializer = fieldInitializer; return new TargetClass(input.getModifiers(), input.getClassName().toString(), convert(input.getSuperClass()), - generics, + javaGenerics, txGenerics, input.getSuperInterfaces().stream().map(this::convert).toList(), input.getConstructors().stream().map(constructor -> this.convert(constructor, finalFieldInitializer)).flatMap(List::stream).toList(), input.getFieldDecl().stream().map(this::convert).toList(), @@ -935,7 +979,7 @@ public class ASTToTargetAST { return generics.stream().anyMatch(g -> g.name().equals(type.getParsedName())); } - private Set collectMethodGenerics(Set> generics, Method input) { + private Set collectMethodGenerics(Set> generics, Map equality, Method input) { var convertedGenerics = new HashSet<>(convert(generics)); outer: for (GenericTypeVar typeVar : input.getGenerics()) { @@ -946,12 +990,12 @@ public class ASTToTargetAST { } convertedGenerics.addAll(convert(typeVar)); } - var returnType = sigma.getType(input.getReturnType()); + var returnType = sigma.getType(input.getReturnType(), equality); if ((returnType instanceof GenericRefType refType) && !hasGeneric(convertedGenerics, refType)) { convertedGenerics.add(new TargetGeneric(refType.getParsedName(), convert(OBJECT))); } for (var param : input.getParameterList()) { - var type = sigma.getType(param.getType()); + var type = sigma.getType(param.getType(), equality); if (type instanceof GenericRefType refType && !hasGeneric(convertedGenerics, refType)) { convertedGenerics.add(new TargetGeneric(refType.getParsedName(), convert(OBJECT))); } @@ -970,8 +1014,10 @@ public class ASTToTargetAST { var generics = sigma.generics(currentClass, input); List params = convert(input.getParameterList()); if (parameterSet.stream().noneMatch(p -> p.equals(params))) { - var convertedGenerics = collectMethodGenerics(generics.javaGenerics(), input); - result.add(new TargetConstructor(input.modifier, convertedGenerics, params, convert(input.block), fieldInitializer)); + var javaGenerics = collectMethodGenerics(generics.javaGenerics(), sigma.equality, input); + var txGenerics = collectMethodGenerics(generics.txGenerics(), sigma.txEquality, input); + + result.add(new TargetConstructor(input.modifier, javaGenerics, txGenerics, params, convert(input.block), fieldInitializer)); parameterSet.add(params); } } @@ -989,10 +1035,12 @@ public class ASTToTargetAST { var generics = sigma.generics(currentClass, input); List params = convert(input.getParameterList()); if (parameterSet.stream().noneMatch(p -> p.equals(params))) { - var convertedGenerics = collectMethodGenerics(generics.javaGenerics(), input); + var javaGenerics = collectMethodGenerics(generics.javaGenerics(), sigma.equality, input); + var txGenerics = collectMethodGenerics(generics.txGenerics(), sigma.txEquality, input); + result.add(new TargetMethod( input.modifier, - input.name, convertedGenerics, params, + input.name, javaGenerics, txGenerics, params, convert(input.getReturnType()), convert(input.block) )); @@ -1060,7 +1108,7 @@ public class ASTToTargetAST { @Override public TargetType visit(TypePlaceholder typePlaceholder) { - return sigma.getTargetType(typePlaceholder); + return sigma.getTargetType(typePlaceholder, sigma.equality); } @Override diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java index e0b736c9..e350707e 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResult.java @@ -16,7 +16,7 @@ public class GenericsResult { } public Set> get(ClassOrInterface clazz) { - var generics = this.sigma.computedGenericsOfClasses.get(clazz); + var generics = this.sigma.computedGenericsOfClasses.get(clazz); if (generics == null) return Set.of(); return generics.txGenerics(); } @@ -67,7 +67,13 @@ public class GenericsResult { public RefTypeOrTPHOrWildcardOrGeneric resolve(RefTypeOrTPHOrWildcardOrGeneric type) { if (type instanceof TypePlaceholder tph) - return this.sigma.getType(tph); + return this.sigma.getType(tph, sigma.equality); + return type; + } + + public RefTypeOrTPHOrWildcardOrGeneric resolveTx(RefTypeOrTPHOrWildcardOrGeneric type) { + if (type instanceof TypePlaceholder tph) + return this.sigma.getType(tph, sigma.txEquality); return type; } } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java index 50cb9f71..781df996 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java @@ -10,14 +10,14 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public record TargetClass(int modifiers, String qualifiedName, TargetType superType, Set generics, List implementingInterfaces, +public record TargetClass(int modifiers, String qualifiedName, TargetType superType, Set generics, Set txGenerics, List implementingInterfaces, List constructors, List fields, List methods) { public TargetClass(int modifiers, String qualifiedName) { - this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), new HashSet<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } public TargetClass(int modifiers, String qualifiedName, List implementingInterfaces) { - this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + this(modifiers, qualifiedName, TargetType.Object, new HashSet<>(), new HashSet<>(), implementingInterfaces, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); } public String getName() { @@ -25,7 +25,7 @@ public record TargetClass(int modifiers, String qualifiedName, TargetType superT } public void addMethod(int access, String name, Set generics, List parameterTypes, TargetType returnType, TargetBlock block) { - this.methods.add(new TargetMethod(access, name, generics, parameterTypes, returnType, block)); + this.methods.add(new TargetMethod(access, name, generics, Set.of(), parameterTypes, returnType, block)); } public void addMethod(int access, String name, List parameterTypes, TargetType returnType, TargetBlock block) { @@ -33,7 +33,7 @@ public record TargetClass(int modifiers, String qualifiedName, TargetType superT } public void addConstructor(int access, Set generics, List paramterTypes, TargetBlock block) { - this.constructors.add(new TargetConstructor(access, generics, paramterTypes, block, null)); + this.constructors.add(new TargetConstructor(access, generics, Set.of(), paramterTypes, block, null)); } public void addConstructor(int access, List paramterTypes, TargetBlock block) { diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java index 4d2cca14..7d3aa389 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/TargetConstructor.java @@ -6,7 +6,7 @@ import de.dhbwstuttgart.target.tree.type.TargetType; import java.util.List; import java.util.Set; -public record TargetConstructor(int access, Set generics, List parameters, TargetBlock block, TargetBlock fieldInitializer) { +public record TargetConstructor(int access, Set generics, Set txGenerics, List parameters, TargetBlock block, TargetBlock fieldInitializer) { public String getDescriptor() { return TargetMethod.getDescriptor(null, parameters.stream().map(MethodParameter::type).toArray(TargetType[]::new)); @@ -15,5 +15,9 @@ public record TargetConstructor(int access, Set generics, List generics, List parameters, TargetType returnType, TargetBlock block) { +public record TargetMethod(int access, String name, Set generics, Set txGenerics, List parameters, TargetType returnType, TargetBlock block) { public static String getDescriptor(TargetType returnType, TargetType... parameters) { String ret = "("; for (var parameterType : parameters) { @@ -42,6 +42,10 @@ public record TargetMethod(int access, String name, Set generics, return getSignature(generics, parameters, returnType); } + public String getTXSignature() { + return getSignature(txGenerics, parameters, returnType); + } + public boolean isStatic() { return (access & Opcodes.ACC_STATIC) != 0; } diff --git a/src/test/java/targetast/TestComplete.java b/src/test/java/targetast/TestComplete.java index 9d7e9159..b8fc0b8d 100644 --- a/src/test/java/targetast/TestComplete.java +++ b/src/test/java/targetast/TestComplete.java @@ -575,8 +575,27 @@ public class TestComplete { } @Test + @Ignore("This one isn't working") public void boxTest() throws Exception { var classFiles = generateClassFiles("Box.jav", new ByteArrayClassLoader()); var instance = classFiles.get("Box_Main").getDeclaredConstructor().newInstance(); } + + @Test + public void cycleTest() throws Exception { + var classFiles = generateClassFiles("Cycle.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("Cycle").getDeclaredConstructor().newInstance(); + } + + @Test + public void olFunTest() throws Exception { + var classFiles = generateClassFiles("OLFun.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("OLFun").getDeclaredConstructor().newInstance(); + } + + @Test + public void olFun2Test() throws Exception { + var classFiles = generateClassFiles("OLFun2.jav", new ByteArrayClassLoader()); + var instance = classFiles.get("OLFun2").getDeclaredConstructor().newInstance(); + } } diff --git a/src/test/resources/target/Test.java b/src/test/resources/target/Test.java deleted file mode 100644 index f7e1a0bc..00000000 --- a/src/test/resources/target/Test.java +++ /dev/null @@ -1,9 +0,0 @@ -public class Test { - public void lambda() { - Interface mul2 = (Integer a) -> a * 2; - } -} - -interface Interface { - R apply(T t); -} \ No newline at end of file