Flatten generic function types

This commit is contained in:
Daniel Holle 2023-05-24 13:53:23 +02:00
parent 677d68428d
commit 67a582b4a2
9 changed files with 98 additions and 58 deletions

View File

@ -17,7 +17,7 @@ public class Faculty {
public getFact(x) { public getFact(java.lang.Integer x) {
return fact.apply(x); return fact.apply(x);
} }
} }

View File

@ -1,16 +1,15 @@
package de.dhbwstuttgart.bytecode; package de.dhbwstuttgart.bytecode;
import de.dhbwstuttgart.target.tree.TargetGeneric; import de.dhbwstuttgart.target.tree.TargetGeneric;
import de.dhbwstuttgart.target.tree.type.TargetGenericType; import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.target.tree.type.TargetRefType;
import de.dhbwstuttgart.target.tree.type.TargetSpecializedType;
import de.dhbwstuttgart.target.tree.type.TargetType;
import org.objectweb.asm.ClassWriter; import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -32,7 +31,24 @@ public class FunNGenerator {
private static final String objectSuperType = Type.getInternalName(Object.class).replace('.','/'); private static final String objectSuperType = Type.getInternalName(Object.class).replace('.','/');
private static final String objectSignature = applySignature(TargetType.Object); private static final String objectSignature = applySignature(TargetType.Object);
private static String applyDescriptor(TargetType a) { return a.toDescriptor(); }
private static String applyDescriptor(TargetType type, int[] start) {
var res = "L" + type.getInternalName() + "<";
if (type instanceof TargetSpecializedType a)
for (var param : a.params()) {
if (param instanceof TargetGenericType gp) {
res += "TT" + start[0] + ";";
start[0] += 1;
} else {
res += applyDescriptor(param, start);
}
}
else return type.toDescriptor();
res += ">;";
return res;
}
private static String applySignature(TargetType a) { return a.toSignature(); } private static String applySignature(TargetType a) { return a.toSignature(); }
private static String applyNameDescriptor(TargetType a){ return a instanceof TargetGenericType ? "LTPH;" : String.format("%s;", applySignature(a)); } private static String applyNameDescriptor(TargetType a){ return a instanceof TargetGenericType ? "LTPH;" : String.format("%s;", applySignature(a)); }
@ -68,22 +84,20 @@ public class FunNGenerator {
public static byte[] generateSpecializedBytecode(List<TargetType> argumentTypes, TargetType returnType) { public static byte[] generateSpecializedBytecode(List<TargetType> argumentTypes, TargetType returnType) {
List<TargetType> parameters = Stream List<TargetType> parameters = Stream
.concat(argumentTypes.stream(), Stream.of(returnType)) .concat(argumentTypes.stream(), Stream.of(returnType))
.collect(Collectors.toList()); .toList();
StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters)));
var start = new int[]{0};
StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters), start));
boolean containsGeneric = false; boolean containsGeneric = false;
String genericSignature = "<"; String genericSignature = "<";
var generics = new HashSet<TargetGenericType>(); for (var i = 0; i < start[0]; i++)
for (TargetType typeArgument : parameters) { genericSignature += String.format("T%d:%s", i, objectSignature);
collectGenericTypeArguments(generics, typeArgument);
}
for (var generic : generics) {
genericSignature += String.format("%s:%s", generic.name(), objectSignature);
containsGeneric = true; containsGeneric = true;
}
genericSignature += ">"; genericSignature += ">";
if (containsGeneric) funNClassSignature.insert(0, genericSignature); if (containsGeneric) funNClassSignature.insert(0, genericSignature);
System.out.println(funNClassSignature.toString());
ClassWriter classWriter = new ClassWriter(0); ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSpecializedClassName(argumentTypes, returnType), funNClassSignature.toString(), objectSuperType, new String[]{getSuperClassName(argumentTypes.size())}); classWriter.visit(bytecodeVersion, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, getSpecializedClassName(argumentTypes, returnType), funNClassSignature.toString(), objectSuperType, new String[]{getSuperClassName(argumentTypes.size())});
@ -91,12 +105,14 @@ public class FunNGenerator {
return classWriter.toByteArray(); return classWriter.toByteArray();
} }
private static void collectGenericTypeArguments(HashSet<TargetGenericType> generics, TargetType typeArgument) { private static void collectGenericTypeArguments(Map<TargetGenericType, Integer> generics, TargetType typeArgument) {
if (typeArgument instanceof TargetSpecializedType specializedType) { if (typeArgument instanceof TargetSpecializedType specializedType) {
for (var arg : specializedType.params()) for (var arg : specializedType.params())
collectGenericTypeArguments(generics, arg); collectGenericTypeArguments(generics, arg);
} else if (typeArgument instanceof TargetGenericType genericType) { } else if (typeArgument instanceof TargetGenericType genericType) {
generics.add(genericType); if (generics.containsKey(genericType))
generics.put(genericType, generics.get(genericType) + 1);
else generics.put(genericType, 0);
} }
} }
@ -112,14 +128,6 @@ public class FunNGenerator {
.replace(";", "$_$"); .replace(";", "$_$");
} }
public static String getSpecializedDescriptor(List<TargetType> argumentTypes, TargetType returnType) {
return applyDescriptor(new TargetRefType(getSpecializedClassName(argumentTypes, returnType)));
}
public static String getSpecializedSignature(List<TargetType> argumentTypes, TargetType returnType) {
return applySignature(new TargetRefType(getSpecializedClassName(argumentTypes, returnType)));
}
public static List<TargetType> getArguments(List<TargetType> list) { public static List<TargetType> getArguments(List<TargetType> list) {
return list return list
.stream() .stream()

View File

@ -898,7 +898,7 @@ public class JavaTXCompiler {
return generatedClasses; return generatedClasses;
} }
private void writeClassFile(Map<JavaClassName, byte[]> classFiles, File path) throws IOException { public void writeClassFile(Map<JavaClassName, byte[]> classFiles, File path) throws IOException {
FileOutputStream output; FileOutputStream output;
for (JavaClassName name : classFiles.keySet()) { for (JavaClassName name : classFiles.keySet()) {
byte[] bytecode = classFiles.get(name); byte[] bytecode = classFiles.get(name);

View File

@ -15,6 +15,7 @@ import de.dhbwstuttgart.target.tree.expression.TargetExpression;
import de.dhbwstuttgart.target.tree.type.*; import de.dhbwstuttgart.target.tree.type.*;
import de.dhbwstuttgart.typeinference.result.*; import de.dhbwstuttgart.typeinference.result.*;
import java.lang.annotation.Target;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -52,8 +53,7 @@ public class ASTToTargetAST {
} }
@Override @Override
void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result) { void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced) {
var referenced = new HashSet<TypePlaceholder>();
eliminateInfima(result, referenced); eliminateInfima(result, referenced);
eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced);
} }
@ -72,8 +72,7 @@ public class ASTToTargetAST {
} }
@Override @Override
void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result) { void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced) {
var referenced = new HashSet<TypePlaceholder>();
eliminateCycles(result, referenced); eliminateCycles(result, referenced);
eliminateInfima(result, referenced); eliminateInfima(result, referenced);
eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced);
@ -523,7 +522,7 @@ public class ASTToTargetAST {
usedTPHsOfMethods.put(method, usedTphs); usedTPHsOfMethods.put(method, usedTphs);
eliminateInnerTypeVariables(referenced, result); eliminateInnerTypeVariables(referenced, result);
addMissingObjectBounds(result, classGenerics); addMissingObjectBounds(result, classGenerics, usedTphs);
System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result); System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result);
return result; return result;
@ -582,7 +581,7 @@ public class ASTToTargetAST {
} }
} }
abstract void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result); abstract void generics(ClassOrInterface classOrInterface, Set<ResultPair<?, ?>> result, Set<TypePlaceholder> referenced);
Set<ResultPair<?, ?>> generics(ClassOrInterface classOrInterface) { Set<ResultPair<?, ?>> generics(ClassOrInterface classOrInterface) {
if (computedGenericsOfClasses.containsKey(classOrInterface)) if (computedGenericsOfClasses.containsKey(classOrInterface))
@ -595,24 +594,31 @@ public class ASTToTargetAST {
findAllBounds(field.getType(), javaResult, equality); findAllBounds(field.getType(), javaResult, equality);
} }
var referenced = new HashSet<TypePlaceholder>();
eliminateTransitives(javaResult); eliminateTransitives(javaResult);
generics(classOrInterface, javaResult); generics(classOrInterface, javaResult, referenced);
addMissingObjectBounds(javaResult, null);
var referencedByClass = new HashSet<TypePlaceholder>();
for (var field : classOrInterface.getFieldDecl()) {
findTphs(field.getType(), referencedByClass);
}
addMissingObjectBounds(javaResult, null, referencedByClass);
System.out.println(this.getClass().getSimpleName() + " Class " + classOrInterface.getClassName().getClassName() + ": " + javaResult); System.out.println(this.getClass().getSimpleName() + " Class " + classOrInterface.getClassName().getClassName() + ": " + javaResult);
return javaResult; return javaResult;
} }
void addMissingObjectBounds(Set<ResultPair<?,?>> result, Set<ResultPair<?, ?>> classGenerics) { void addMissingObjectBounds(Set<ResultPair<?,?>> result, Set<ResultPair<?, ?>> classGenerics, Set<TypePlaceholder> usedTphs) {
outer: for (var p1 : new HashSet<>(result)) { outer: for (var tph : usedTphs) {
if (p1 instanceof PairTPHsmallerTPH ptph) { tph = equality.getOrDefault(tph, tph);
for (var p2 : new HashSet<>(result)) { for (var p1 : new HashSet<>(result)) {
if (ptph.right.equals(p2.getLeft())) if (p1.getLeft().equals(tph)) continue outer;
continue outer;
}
if (classGenerics == null || classGenerics.stream().noneMatch((pair) -> pair.getLeft().equals(ptph.right)))
addToPairs(result, new PairTPHequalRefTypeOrWildcardType(ptph.right, OBJECT));
} }
TypePlaceholder finalTph = tph;
if (classGenerics == null || classGenerics.stream().noneMatch((pair) -> pair.getLeft().equals(finalTph)))
addToPairs(result, new PairTPHequalRefTypeOrWildcardType(tph, OBJECT));
} }
} }
@ -1136,6 +1142,19 @@ public class ASTToTargetAST {
return convert(input, generics.javaGenerics); return convert(input, generics.javaGenerics);
} }
static TargetType flattenFunNType(List<TargetType> params) {
var newParams = new ArrayList<TargetType>();
for (var i = 0; i < params.size(); i++) {
var param = params.get(i);
if (param instanceof TargetSpecializedType fn) {
newParams.addAll(fn.params());
} else {
newParams.add(param);
}
}
return TargetFunNType.fromParams(params, newParams);
}
protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input, GenerateGenerics generics) { protected TargetType convert(RefTypeOrTPHOrWildcardOrGeneric input, GenerateGenerics generics) {
return input.acceptTV(new TypeVisitor<>() { return input.acceptTV(new TypeVisitor<>() {
@Override @Override
@ -1158,7 +1177,7 @@ public class ASTToTargetAST {
var clazz = classLoader.loadClass(code); var clazz = classLoader.loadClass(code);
auxiliaries.put(clazz.getName(), code); auxiliaries.put(clazz.getName(), code);
} }
return new TargetFunNType(params.size() - 1, params); return flattenFunNType(params);
} }
return new TargetRefType(name, params); return new TargetRefType(name, params);
} }

View File

@ -28,6 +28,8 @@ public class GenericsResultSet extends AbstractSet<ResultPair<?, ?>> {
public Optional<ResultPair<?, ?>> getResultPairFor(TypePlaceholder tph) { public Optional<ResultPair<?, ?>> getResultPairFor(TypePlaceholder tph) {
var tph2 = equality.getOrDefault(tph, tph); var tph2 = equality.getOrDefault(tph, tph);
return this.stream().filter(pair -> pair.getLeft().equals(tph2)).findFirst(); return this.stream().filter(pair -> {
return pair.getLeft().equals(tph2);
}).findFirst();
} }
} }

View File

@ -4,16 +4,20 @@ import de.dhbwstuttgart.bytecode.FunNGenerator;
import java.util.List; import java.util.List;
public record TargetFunNType(int N, List<TargetType> params) implements TargetSpecializedType { public record TargetFunNType(String name, List<TargetType> params) implements TargetSpecializedType {
@Override public static TargetFunNType fromParams(List<TargetType> params) {
public String getInternalName() { return fromParams(params, params);
return FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params)); }
public static TargetFunNType fromParams(List<TargetType> params, List<TargetType> realParams) {
var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params));
return new TargetFunNType(name, realParams);
} }
@Override @Override
public String name() { public String getInternalName() {
return getInternalName(); return name;
} }
@Override @Override

View File

@ -186,7 +186,13 @@ class TypeToInsertString implements ResultSetVisitor{
@Override @Override
public void visit(TypePlaceholder typePlaceholder) { public void visit(TypePlaceholder typePlaceholder) {
insert += ((TypePlaceholder) constraints.getResultPairFor(typePlaceholder).get().getLeft()).getName(); ResultPair<?, ?> resultPair = null;
if (constraints != null)
resultPair = constraints.getResultPairFor(typePlaceholder).orElse(null);
if (resultPair == null)
resultPair = classConstraints.getResultPairFor(typePlaceholder).get();
insert += ((TypePlaceholder)resultPair.getLeft()).getName();
} }
@Override @Override

View File

@ -17,6 +17,7 @@ import static org.junit.Assert.*;
import org.objectweb.asm.Opcodes; import org.objectweb.asm.Opcodes;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Target;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
@ -341,7 +342,7 @@ public class TestCodegen {
public void testLambda() throws Exception { public void testLambda() throws Exception {
var classLoader = new ByteArrayClassLoader(); var classLoader = new ByteArrayClassLoader();
//var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class")); //var fun = classLoader.loadClass(Path.of(System.getProperty("user.dir"), "src/test/java/targetast/Fun1$$.class"));
var interfaceType = new TargetFunNType(1, List.of(TargetType.Integer)); var interfaceType = TargetFunNType.fromParams(List.of(TargetType.Integer));
var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "CGLambda"); var targetClass = new TargetClass(Opcodes.ACC_PUBLIC, "CGLambda");
targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of( targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(