diff --git a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java index d32aa8513..d9b417879 100644 --- a/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java +++ b/src/main/java/de/dhbwstuttgart/bytecode/Codegen.java @@ -708,7 +708,7 @@ public class Codegen { parameters.addAll(lambda.params()); impl = new TargetMethod( - 0, name, Set.of(), + 0, name, Set.of(), Set.of(), parameters, lambda.returnType(), lambda.block() ); generateMethod(impl); @@ -980,6 +980,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 +1007,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 +1021,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 +1034,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/JavaTXSignatureAttribute.java b/src/main/java/de/dhbwstuttgart/bytecode/JavaTXSignatureAttribute.java index f185494cd..cc9d1487e 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/target/generate/ASTToTargetAST.java b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java index 95395f71c..4416a4f5f 100644 --- a/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java +++ b/src/main/java/de/dhbwstuttgart/target/generate/ASTToTargetAST.java @@ -501,26 +501,42 @@ public class ASTToTargetAST { } javaResult.addAll(txResult); // Same result until here - eliminateCycles(javaResult); - eliminateInfima(javaResult); - eliminateInfima(txResult); - // 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())); + var eq = new HashMap<>(equality); + { // Java Generics + eliminateCycles(javaResult); + eliminateInfima(javaResult); + + 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())); + } + usedTphs.addAll(findTypeVariables(method.getReturnType())); + var referenced = new HashSet<>(usedTphs); + referenced.addAll(typeVariablesOfClass); + + eliminateInnerTypeVariables(referenced, javaResult); + equalizeTypeVariables(javaResult); + usedTPHsOfMethods.put(method, usedTphs); } - usedTphs.addAll(findTypeVariables(method.getReturnType())); - var referenced = new HashSet<>(usedTphs); - referenced.addAll(typeVariablesOfClass); - eliminateInnerTypeVariables(referenced, javaResult); - eliminateInnerTypeVariables(referenced, txResult); + var storedEq = equality; //TODO Hack: pass equality as parameter instead + equality = eq; + { // JavaTX Generics + eliminateInfima(txResult); - usedTPHsOfMethods.put(method, usedTphs); + var referenced = new HashSet(); + for (var param : method.getParameterList().getFormalparalist()) { + referenced.addAll(findTypeVariables(param.getType())); + } + referenced.addAll(findTypeVariables(method.getReturnType())); + referenced.addAll(typeVariablesOfClass); - equalizeTypeVariables(javaResult); + eliminateInnerTypeVariables(referenced, txResult); + } + equality = storedEq; System.out.println(method.name + ": " + txResult + " & " + javaResult); @@ -898,18 +914,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 +937,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(), @@ -970,8 +989,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(), input); + var txGenerics = collectMethodGenerics(generics.txGenerics(), input); + + result.add(new TargetConstructor(input.modifier, javaGenerics, txGenerics, params, convert(input.block), fieldInitializer)); parameterSet.add(params); } } @@ -989,10 +1010,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(), input); + var txGenerics = collectMethodGenerics(generics.txGenerics(), input); + result.add(new TargetMethod( input.modifier, - input.name, convertedGenerics, params, + input.name, javaGenerics, txGenerics, params, convert(input.getReturnType()), convert(input.block) )); diff --git a/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java b/src/main/java/de/dhbwstuttgart/target/tree/TargetClass.java index 50cb9f719..781df996a 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 4d2cca14e..7d3aa3896 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 9d7e9159d..c78afbece 100644 --- a/src/test/java/targetast/TestComplete.java +++ b/src/test/java/targetast/TestComplete.java @@ -579,4 +579,10 @@ public class TestComplete { 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(); + } }