From 67a582b4a23933d28b681d60221d50f6e960fbad Mon Sep 17 00:00:00 2001 From: Daniel Holle Date: Wed, 24 May 2023 13:53:23 +0200 Subject: [PATCH] Flatten generic function types --- resources/bytecode/javFiles/Faculty.jav | 2 +- resources/bytecode/javFiles/Tph2.jav | 8 +-- .../dhbwstuttgart/bytecode/FunNGenerator.java | 58 +++++++++++-------- .../de/dhbwstuttgart/core/JavaTXCompiler.java | 2 +- .../target/generate/ASTToTargetAST.java | 55 ++++++++++++------ .../target/generate/GenericsResultSet.java | 4 +- .../target/tree/type/TargetFunNType.java | 16 +++-- .../typedeployment/TypeInsertFactory.java | 8 ++- src/test/java/targetast/TestCodegen.java | 3 +- 9 files changed, 98 insertions(+), 58 deletions(-) diff --git a/resources/bytecode/javFiles/Faculty.jav b/resources/bytecode/javFiles/Faculty.jav index 14007ece..276d8df6 100644 --- a/resources/bytecode/javFiles/Faculty.jav +++ b/resources/bytecode/javFiles/Faculty.jav @@ -17,7 +17,7 @@ public class Faculty { - public getFact(x) { + public getFact(java.lang.Integer x) { return fact.apply(x); } } diff --git a/resources/bytecode/javFiles/Tph2.jav b/resources/bytecode/javFiles/Tph2.jav index 2f9c53e6..da9b220a 100644 --- a/resources/bytecode/javFiles/Tph2.jav +++ b/resources/bytecode/javFiles/Tph2.jav @@ -1,8 +1,8 @@ public class Tph2 { - id = x->x; - id3 (x) { - return id.apply(x); - } + id = x->x; + id3 (x) { + return id.apply(x); + } /* m(a,b){ diff --git a/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java b/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java index ee3b6c27..e25a229c 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/FunNGenerator.java @@ -1,16 +1,15 @@ package de.dhbwstuttgart.bytecode; import de.dhbwstuttgart.target.tree.TargetGeneric; -import de.dhbwstuttgart.target.tree.type.TargetGenericType; -import de.dhbwstuttgart.target.tree.type.TargetRefType; -import de.dhbwstuttgart.target.tree.type.TargetSpecializedType; -import de.dhbwstuttgart.target.tree.type.TargetType; +import de.dhbwstuttgart.target.tree.type.*; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; 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 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 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 argumentTypes, TargetType returnType) { List parameters = Stream .concat(argumentTypes.stream(), Stream.of(returnType)) - .collect(Collectors.toList()); - StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters))); + .toList(); + + var start = new int[]{0}; + StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters), start)); boolean containsGeneric = false; String genericSignature = "<"; - var generics = new HashSet(); - for (TargetType typeArgument : parameters) { - collectGenericTypeArguments(generics, typeArgument); - } - for (var generic : generics) { - genericSignature += String.format("%s:%s", generic.name(), objectSignature); - containsGeneric = true; - } + for (var i = 0; i < start[0]; i++) + genericSignature += String.format("T%d:%s", i, objectSignature); + containsGeneric = true; genericSignature += ">"; if (containsGeneric) funNClassSignature.insert(0, genericSignature); + System.out.println(funNClassSignature.toString()); 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())}); @@ -91,12 +105,14 @@ public class FunNGenerator { return classWriter.toByteArray(); } - private static void collectGenericTypeArguments(HashSet generics, TargetType typeArgument) { + private static void collectGenericTypeArguments(Map generics, TargetType typeArgument) { if (typeArgument instanceof TargetSpecializedType specializedType) { for (var arg : specializedType.params()) collectGenericTypeArguments(generics, arg); } 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(";", "$_$"); } - public static String getSpecializedDescriptor(List argumentTypes, TargetType returnType) { - return applyDescriptor(new TargetRefType(getSpecializedClassName(argumentTypes, returnType))); - } - - public static String getSpecializedSignature(List argumentTypes, TargetType returnType) { - return applySignature(new TargetRefType(getSpecializedClassName(argumentTypes, returnType))); - } - public static List getArguments(List list) { return list .stream() diff --git a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java index 33cb5b29..7e802569 100644 --- a/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java +++ b/src/main/java/de/dhbwstuttgart/core/JavaTXCompiler.java @@ -898,7 +898,7 @@ public class JavaTXCompiler { return generatedClasses; } - private void writeClassFile(Map classFiles, File path) throws IOException { + public void writeClassFile(Map classFiles, File path) throws IOException { FileOutputStream output; for (JavaClassName name : classFiles.keySet()) { byte[] bytecode = classFiles.get(name); diff --git a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index f2c963a4..7fb10f76 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -15,6 +15,7 @@ import de.dhbwstuttgart.target.tree.expression.TargetExpression; import de.dhbwstuttgart.target.tree.type.*; import de.dhbwstuttgart.typeinference.result.*; +import java.lang.annotation.Target; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -52,8 +53,7 @@ public class ASTToTargetAST { } @Override - void generics(ClassOrInterface classOrInterface, Set> result) { - var referenced = new HashSet(); + void generics(ClassOrInterface classOrInterface, Set> result, Set referenced) { eliminateInfima(result, referenced); eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); } @@ -72,8 +72,7 @@ public class ASTToTargetAST { } @Override - void generics(ClassOrInterface classOrInterface, Set> result) { - var referenced = new HashSet(); + void generics(ClassOrInterface classOrInterface, Set> result, Set referenced) { eliminateCycles(result, referenced); eliminateInfima(result, referenced); eliminateInnerTypeVariablesOfClass(classOrInterface, result, referenced); @@ -523,7 +522,7 @@ public class ASTToTargetAST { usedTPHsOfMethods.put(method, usedTphs); eliminateInnerTypeVariables(referenced, result); - addMissingObjectBounds(result, classGenerics); + addMissingObjectBounds(result, classGenerics, usedTphs); System.out.println(this.getClass().getSimpleName() + " " + method.name + ": " + result); return result; @@ -582,7 +581,7 @@ public class ASTToTargetAST { } } - abstract void generics(ClassOrInterface classOrInterface, Set> result); + abstract void generics(ClassOrInterface classOrInterface, Set> result, Set referenced); Set> generics(ClassOrInterface classOrInterface) { if (computedGenericsOfClasses.containsKey(classOrInterface)) @@ -595,24 +594,31 @@ public class ASTToTargetAST { findAllBounds(field.getType(), javaResult, equality); } + var referenced = new HashSet(); eliminateTransitives(javaResult); - generics(classOrInterface, javaResult); - addMissingObjectBounds(javaResult, null); + generics(classOrInterface, javaResult, referenced); + + var referencedByClass = new HashSet(); + for (var field : classOrInterface.getFieldDecl()) { + findTphs(field.getType(), referencedByClass); + } + + addMissingObjectBounds(javaResult, null, referencedByClass); System.out.println(this.getClass().getSimpleName() + " Class " + classOrInterface.getClassName().getClassName() + ": " + javaResult); return javaResult; } - void addMissingObjectBounds(Set> result, Set> classGenerics) { - 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 (classGenerics == null || classGenerics.stream().noneMatch((pair) -> pair.getLeft().equals(ptph.right))) - addToPairs(result, new PairTPHequalRefTypeOrWildcardType(ptph.right, OBJECT)); + void addMissingObjectBounds(Set> result, Set> classGenerics, Set usedTphs) { + outer: for (var tph : usedTphs) { + tph = equality.getOrDefault(tph, tph); + for (var p1 : new HashSet<>(result)) { + if (p1.getLeft().equals(tph)) continue outer; } + + 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); } + static TargetType flattenFunNType(List params) { + var newParams = new ArrayList(); + 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) { return input.acceptTV(new TypeVisitor<>() { @Override @@ -1158,7 +1177,7 @@ public class ASTToTargetAST { var clazz = classLoader.loadClass(code); auxiliaries.put(clazz.getName(), code); } - return new TargetFunNType(params.size() - 1, params); + return flattenFunNType(params); } return new TargetRefType(name, params); } diff --git a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java index bc488da0..8092e44e 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/GenericsResultSet.java @@ -28,6 +28,8 @@ public class GenericsResultSet extends AbstractSet> { public Optional> getResultPairFor(TypePlaceholder 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(); } } diff --git a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetFunNType.java b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetFunNType.java index afbf9f4e..f35f1043 100644 --- a/src/main/java/de/dhbwstuttgart/target/tree/type/TargetFunNType.java +++ b/src/main/java/de/dhbwstuttgart/target/tree/type/TargetFunNType.java @@ -4,16 +4,20 @@ import de.dhbwstuttgart.bytecode.FunNGenerator; import java.util.List; -public record TargetFunNType(int N, List params) implements TargetSpecializedType { +public record TargetFunNType(String name, List params) implements TargetSpecializedType { - @Override - public String getInternalName() { - return FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params)); + public static TargetFunNType fromParams(List params) { + return fromParams(params, params); + } + + public static TargetFunNType fromParams(List params, List realParams) { + var name = FunNGenerator.getSpecializedClassName(FunNGenerator.getArguments(params), FunNGenerator.getReturnType(params)); + return new TargetFunNType(name, realParams); } @Override - public String name() { - return getInternalName(); + public String getInternalName() { + return name; } @Override diff --git a/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java b/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java index 3cae417f..be49fde2 100644 --- a/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java +++ b/src/main/java/de/dhbwstuttgart/typedeployment/TypeInsertFactory.java @@ -186,7 +186,13 @@ class TypeToInsertString implements ResultSetVisitor{ @Override 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 diff --git a/src/test/java/targetast/TestCodegen.java b/src/test/java/targetast/TestCodegen.java index 3d949f5d..00cc85bc 100644 --- a/src/test/java/targetast/TestCodegen.java +++ b/src/test/java/targetast/TestCodegen.java @@ -17,6 +17,7 @@ import static org.junit.Assert.*; import org.objectweb.asm.Opcodes; import java.io.IOException; +import java.lang.annotation.Target; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; @@ -341,7 +342,7 @@ public class TestCodegen { public void testLambda() throws Exception { var classLoader = new ByteArrayClassLoader(); //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"); targetClass.addConstructor(Opcodes.ACC_PUBLIC, List.of(), new TargetBlock(List.of(