Hinzufügen von FunNGenerator Tests und Bugfixing bei generateSpecializedBytecode().

TPHs werden nun direkt zu Generics substituiert und TypeToSignature wurde verbessert, sodass korrekte Signaturen für Generics generiert werden.
This commit is contained in:
Etienne Zink 2022-04-03 15:47:03 +02:00
parent 1b5eacf921
commit 5b970f9359
3 changed files with 144 additions and 30 deletions

View File

@ -56,11 +56,11 @@ public final class FunNGenerator implements FunNUtilities{
for (int currentParameter = 1; currentParameter <= numberArguments; currentParameter++){ for (int currentParameter = 1; currentParameter <= numberArguments; currentParameter++){
superFunNClassSignature.append(String.format("%s%d:%s",argumentGenericBase, currentParameter, objectSignature)); superFunNClassSignature.append(String.format("%s%d:%s",argumentGenericBase, currentParameter, objectSignature));
superFunNMethodSignature.append(String.format("T%s;", applySignature( new GenericRefType(argumentGenericBase + currentParameter, null)))); superFunNMethodSignature.append(applySignature( new GenericRefType(argumentGenericBase + currentParameter, null)));
superFunNMethodDescriptor.append(objectSignature); superFunNMethodDescriptor.append(objectSignature);
} }
superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature)); superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature));
superFunNMethodSignature.append(String.format(")T%s;", applySignature(new GenericRefType(returnGeneric, null)))); superFunNMethodSignature.append(String.format(")%s", applySignature(new GenericRefType(returnGeneric, null))));
superFunNMethodDescriptor.append(String.format(")%s", objectSignature)); superFunNMethodDescriptor.append(String.format(")%s", objectSignature));
ClassWriter classWriter = new ClassWriter(0); ClassWriter classWriter = new ClassWriter(0);
@ -81,8 +81,10 @@ public final class FunNGenerator implements FunNUtilities{
public byte[] generateSpecializedBytecode(List<RefTypeOrTPHOrWildcardOrGeneric> argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType) { public byte[] generateSpecializedBytecode(List<RefTypeOrTPHOrWildcardOrGeneric> argumentTypes, RefTypeOrTPHOrWildcardOrGeneric returnType) {
Objects.requireNonNull(argumentTypes); Objects.requireNonNull(argumentTypes);
Objects.requireNonNull(returnType); Objects.requireNonNull(returnType);
//generates a list of all params and substitutes the TPH
List<RefTypeOrTPHOrWildcardOrGeneric> parameters = Stream List<RefTypeOrTPHOrWildcardOrGeneric> parameters = Stream
.concat(argumentTypes.stream(), Stream.of(returnType)) .concat(argumentTypes.stream(), Stream.of(returnType))
.map(FunNGenerator::substituteTPH)
.collect(Collectors.toList()); .collect(Collectors.toList());
RefType superFunN = new RefType(new JavaClassName(getSuperClassName(argumentTypes.size())), parameters , null); RefType superFunN = new RefType(new JavaClassName(getSuperClassName(argumentTypes.size())), parameters , null);
StringBuilder funNClassSignature = new StringBuilder(objectSignature + (superFunN.acceptTV(new TypeToSignature(false)))); StringBuilder funNClassSignature = new StringBuilder(objectSignature + (superFunN.acceptTV(new TypeToSignature(false))));
@ -94,12 +96,6 @@ public final class FunNGenerator implements FunNUtilities{
GenericRefType generic = (GenericRefType) typeArgument; GenericRefType generic = (GenericRefType) typeArgument;
String signatureOfArgument = generic.getParsedName(); String signatureOfArgument = generic.getParsedName();
if(genericSignature.contains(signatureOfArgument)) continue; if(genericSignature.contains(signatureOfArgument)) continue;
genericSignature += String.format("%s:%s", signatureOfArgument, applyDescriptor(generic));
containsGeneric = true;
} else if(typeArgument instanceof TypePlaceholder){
TypePlaceholder placeholder = (TypePlaceholder) typeArgument;
String signatureOfArgument = applySignature(placeholder).substring(1);
if(genericSignature.contains(signatureOfArgument)) continue;
genericSignature += String.format("%s:%s", signatureOfArgument, objectSignature); genericSignature += String.format("%s:%s", signatureOfArgument, objectSignature);
containsGeneric = true; containsGeneric = true;
} }
@ -166,4 +162,12 @@ public final class FunNGenerator implements FunNUtilities{
* @return the name for the type {@code a} which should be used in the specialized name for FunN. * @return the name for the type {@code a} which should be used in the specialized name for FunN.
*/ */
private String applyNameDescriptor(RefTypeOrTPHOrWildcardOrGeneric a){ return a instanceof TypePlaceholder ? "LTPH;" : String.format("L%s;", applyDescriptor(a)); } private String applyNameDescriptor(RefTypeOrTPHOrWildcardOrGeneric a){ return a instanceof TypePlaceholder ? "LTPH;" : String.format("L%s;", applyDescriptor(a)); }
private static RefTypeOrTPHOrWildcardOrGeneric substituteTPH(RefTypeOrTPHOrWildcardOrGeneric t) {
if (t instanceof TypePlaceholder) {
TypePlaceholder tph = (TypePlaceholder) t;
return new GenericRefType(tph.getName()+"$", t.getOffset());
}
return t;
}
} }

View File

@ -102,9 +102,20 @@ public class TypeToSignature implements TypeVisitor<String> {
return sig; return sig;
} }
/**
* Changed that the correct signature is returned:
* returns now T...; expect of only ...
* where ... is {@code genericRefType.getParsedName()}
*
* @since Studienarbeit Type Erasure
* @author etiennezink
*
* @param genericRefType
* @return
*/
@Override @Override
public String visit(GenericRefType genericRefType) { public String visit(GenericRefType genericRefType) {
return genericRefType.getParsedName().replace(".", "/"); return String.format("T%s;", genericRefType.getParsedName()).replace(".", "/");
} }
private Optional<GenericsGeneratorResult> getEqualTPHFromClassConstraints(List<GenericsGeneratorResult> listOfConstraints, String tph) { private Optional<GenericsGeneratorResult> getEqualTPHFromClassConstraints(List<GenericsGeneratorResult> listOfConstraints, String tph) {

View File

@ -8,12 +8,13 @@ import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import java.util.Arrays; import java.util.Arrays;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue; import static org.objectweb.asm.Opcodes.*;
public class FunNGeneratorTest { public class FunNGeneratorTest {
@ -23,108 +24,206 @@ public class FunNGeneratorTest {
static GenericRefType genericT = new GenericRefType("T", null); static GenericRefType genericT = new GenericRefType("T", null);
@BeforeClass @BeforeClass
public static void StartUp(){ public static void setUp(){
funNGenerator = FunNGenerator.getInstance(); funNGenerator = FunNGenerator.getInstance();
} }
@Test @Test
public void SuperClassName_0(){ public void superClassName_0(){
var superClassName = funNGenerator.getSuperClassName(0); var superClassName = funNGenerator.getSuperClassName(0);
assertEquals("Fun0$$", superClassName); assertEquals("Fun0$$", superClassName);
} }
@Test @Test
public void SuperClassName_1(){ public void superClassName_1(){
var superClassName = funNGenerator.getSuperClassName(1); var superClassName = funNGenerator.getSuperClassName(1);
assertEquals("Fun1$$", superClassName); assertEquals("Fun1$$", superClassName);
} }
@Test @Test
public void SpecializedClassName_VoidVoid(){ public void specializedClassName_VoidVoid(){
var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(), voidType); var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(), voidType);
assertEquals("Fun0$$Ljava$lang$Void$_$", specializedClassName); assertEquals("Fun0$$Ljava$lang$Void$_$", specializedClassName);
} }
@Test @Test
public void SpecializedClassName_VoidInt(){ public void specializedClassName_VoidInt(){
var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(), integerType); var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(), integerType);
assertEquals("Fun0$$Ljava$lang$Integer$_$", specializedClassName); assertEquals("Fun0$$Ljava$lang$Integer$_$", specializedClassName);
} }
@Test @Test
public void SpecializedClassName_IntInt(){ public void specializedClassName_IntInt(){
var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(integerType), integerType); var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(integerType), integerType);
assertEquals("Fun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$", specializedClassName); assertEquals("Fun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$", specializedClassName);
} }
@Test @Test
public void SpecializedClassName_IntT(){ public void specializedClassName_IntT(){
var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(integerType), genericT); var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(integerType), genericT);
assertEquals("Fun1$$Ljava$lang$Integer$_$LT$_$", specializedClassName); assertEquals("Fun1$$Ljava$lang$Integer$_$LT$_$", specializedClassName);
} }
@Test @Test
public void SpecializedClassName_IntTPH(){ public void specializedClassName_IntTPH(){
var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(integerType), TypePlaceholder.fresh(null)); var specializedClassName = funNGenerator.getSpecializedClassName(Arrays.asList(integerType), TypePlaceholder.fresh(null));
assertEquals("Fun1$$Ljava$lang$Integer$_$LTPH$_$", specializedClassName); assertEquals("Fun1$$Ljava$lang$Integer$_$LTPH$_$", specializedClassName);
} }
@Test @Test
public void Signature_IntInt(){ public void signature_IntInt(){
var classSignature = funNGenerator.getSpecializedSignature(Arrays.asList(integerType), integerType); var classSignature = funNGenerator.getSpecializedSignature(Arrays.asList(integerType), integerType);
assertEquals("LFun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$;", classSignature); assertEquals("LFun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$;", classSignature);
} }
@Test @Test
public void Signature_IntT(){ public void signature_IntT(){
var classSignature = funNGenerator.getSpecializedSignature(Arrays.asList(integerType), genericT); var classSignature = funNGenerator.getSpecializedSignature(Arrays.asList(integerType), genericT);
assertEquals("LFun1$$Ljava$lang$Integer$_$LT$_$;", classSignature); assertEquals("LFun1$$Ljava$lang$Integer$_$LT$_$;", classSignature);
} }
@Test @Test
public void Descriptor_IntInt(){ public void descriptor_IntInt(){
var classSignature = funNGenerator.getSpecializedDescriptor(Arrays.asList(integerType), integerType); var classSignature = funNGenerator.getSpecializedDescriptor(Arrays.asList(integerType), integerType);
//does not have to contain L and ; because TypeToDescriptor returns the descriptor without these characters as well //does not have to contain L and ; because TypeToDescriptor returns the descriptor without these characters as well
assertEquals("Fun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$", classSignature); assertEquals("Fun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$", classSignature);
} }
@Test @Test
public void Descriptor_IntT(){ public void descriptor_IntT(){
var classSignature = funNGenerator.getSpecializedDescriptor(Arrays.asList(integerType), genericT); var classSignature = funNGenerator.getSpecializedDescriptor(Arrays.asList(integerType), genericT);
//does not have to contain L and ; because TypeToDescriptor returns the descriptor without these characters as well //does not have to contain L and ; because TypeToDescriptor returns the descriptor without these characters as well
assertEquals("Fun1$$Ljava$lang$Integer$_$LT$_$", classSignature); assertEquals("Fun1$$Ljava$lang$Integer$_$LT$_$", classSignature);
} }
@Test @Test
public void GetArguments_Empty(){ public void getArguments_Empty(){
var arguments = funNGenerator.getArguments(Arrays.asList()); var arguments = funNGenerator.getArguments(Arrays.asList());
assertTrue(arguments.isEmpty()); assertTrue(arguments.isEmpty());
} }
@Test @Test
public void GetArguments_Int(){ public void getArguments_Int(){
var arguments = funNGenerator.getArguments(Arrays.asList(integerType)); var arguments = funNGenerator.getArguments(Arrays.asList(integerType));
assertTrue(arguments.isEmpty()); assertTrue(arguments.isEmpty());
} }
@Test @Test
public void GetArguments_IntT(){ public void getArguments_IntT(){
var arguments = funNGenerator.getArguments(Arrays.asList(integerType, genericT)); var arguments = funNGenerator.getArguments(Arrays.asList(integerType, genericT));
assertTrue(arguments.size() == 1); assertTrue(arguments.size() == 1);
assertTrue(arguments.contains(integerType)); assertTrue(arguments.contains(integerType));
} }
@Test @Test
public void GetArguments_IntTInt(){ public void getArguments_IntTInt(){
var arguments = funNGenerator.getArguments(Arrays.asList(integerType, genericT, integerType)); var arguments = funNGenerator.getArguments(Arrays.asList(integerType, genericT, integerType));
assertTrue(arguments.size() == 2); assertTrue(arguments.size() == 2);
assertTrue(arguments.contains(integerType)); assertTrue(arguments.contains(integerType));
assertTrue(arguments.contains(genericT)); assertTrue(arguments.contains(genericT));
} }
//ToDo Etienne: get return Type Test @Test
public void getReturnType_Empty(){
var returnType = funNGenerator.getReturnType(Arrays.asList());
assertNull(returnType);
}
//ToDo Etienne: Super Bytecode Test @Test
public void getReturnType_Int(){
var returnType = funNGenerator.getReturnType(Arrays.asList(integerType));
assertEquals(integerType, returnType);
}
//ToDo Etienne: Specialized Bytecode Test @Test
public void getReturnType_IntT(){
var returnType = funNGenerator.getReturnType(Arrays.asList(integerType, genericT));
assertEquals(genericT, returnType);
}
@Test
public void superBytecode_0(){
var superBytecode = funNGenerator.generateSuperBytecode(0);
assertArrayEquals(superBytecodeReference_0(), superBytecode);
}
@Test
public void superBytecode_1(){
var superBytecode = funNGenerator.generateSuperBytecode(1);
assertArrayEquals(superBytecodeReference_1(), superBytecode);
}
@Test
public void superBytecode_2(){
var superBytecode = funNGenerator.generateSuperBytecode(2);
assertArrayEquals(superBytecodeReference_2(), superBytecode);
}
@Test
public void specializedBytecode_VoidInt(){
var specializedBytecode = funNGenerator.generateSpecializedBytecode(Arrays.asList(), integerType);
assertArrayEquals(specializedBytecodeReference_VoidInt(), specializedBytecode);
}
@Test
public void specializedBytecode_IntInt(){
var specializedBytecode = funNGenerator.generateSpecializedBytecode(Arrays.asList(integerType), integerType);
assertArrayEquals(specializedBytecodeReference_IntInt(), specializedBytecode);
}
@Test
public void specializedBytecode_TIntInt(){
var specializedBytecode = funNGenerator.generateSpecializedBytecode(Arrays.asList(genericT, integerType), integerType);
assertArrayEquals(specializedBytecodeReference_TIntInt(), specializedBytecode);
}
//super bytecode reference methods
private static byte[] superBytecodeReference_0() {
var classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "Fun0$$", "<R:Ljava/lang/Object;>Ljava/lang/Object;", "java/lang/Object", null);
var methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "apply", "()Ljava/lang/Object;", "()TR;", null);
methodVisitor.visitEnd();
classWriter.visitEnd();
return classWriter.toByteArray();
}
private static byte[] superBytecodeReference_1() {
var classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "Fun1$$", "<T1:Ljava/lang/Object;R:Ljava/lang/Object;>Ljava/lang/Object;", "java/lang/Object", null);
var methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "apply", "(Ljava/lang/Object;)Ljava/lang/Object;", "(TT1;)TR;", null);
methodVisitor.visitEnd();
classWriter.visitEnd();
return classWriter.toByteArray();
}
private static byte[] superBytecodeReference_2() {
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "Fun2$$", "<T1:Ljava/lang/Object;T2:Ljava/lang/Object;R:Ljava/lang/Object;>Ljava/lang/Object;", "java/lang/Object", null);
var methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", "(TT1;TT2;)TR;", null);
methodVisitor.visitEnd();
classWriter.visitEnd();
return classWriter.toByteArray();
}
//specialized bytecode reference methods
private static byte[] specializedBytecodeReference_VoidInt() {
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "Fun0$$Ljava$lang$Integer$_$", "Ljava/lang/Object;LFun0$$<Ljava/lang/Integer;>;", "java/lang/Object", new String[]{"Fun0$$"});
classWriter.visitEnd();
return classWriter.toByteArray();
}
private static byte[] specializedBytecodeReference_IntInt() {
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "Fun1$$Ljava$lang$Integer$_$Ljava$lang$Integer$_$", "Ljava/lang/Object;LFun1$$<Ljava/lang/Integer;Ljava/lang/Integer;>;", "java/lang/Object", new String[]{"Fun1$$"});
classWriter.visitEnd();
return classWriter.toByteArray();
}
private static byte[] specializedBytecodeReference_TIntInt() {
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "Fun2$$LT$_$Ljava$lang$Integer$_$Ljava$lang$Integer$_$", "<T:Ljava/lang/Object;>Ljava/lang/Object;LFun2$$<TT;Ljava/lang/Integer;Ljava/lang/Integer;>;", "java/lang/Object", new String[]{"Fun2$$"});
classWriter.visitEnd();
return classWriter.toByteArray();
}
} }