159 lines
6.6 KiB
Java
159 lines
6.6 KiB
Java
package de.dhbwstuttgart.bytecode;
|
|
|
|
import de.dhbwstuttgart.target.tree.TargetGeneric;
|
|
import de.dhbwstuttgart.target.tree.type.*;
|
|
import org.objectweb.asm.ClassWriter;
|
|
import org.objectweb.asm.MethodVisitor;
|
|
import org.objectweb.asm.Type;
|
|
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Stream;
|
|
|
|
import static org.objectweb.asm.Opcodes.*;
|
|
|
|
/**
|
|
* //ToDo beschreiben
|
|
*
|
|
* @since Studienarbeit Type Erasure
|
|
* @author etiennezink
|
|
*/
|
|
public class FunNGenerator {
|
|
|
|
private static final String argumentGenericBase = "T";
|
|
private static final String returnGeneric = "R";
|
|
private static final String methodName = "apply";
|
|
private static final int bytecodeVersion = V1_8;
|
|
|
|
private static final String objectSuperType = Type.getInternalName(Object.class).replace('.','/');
|
|
private static final String objectSignature = applySignature(TargetType.Object);
|
|
|
|
private static final String VOID = "Ljava/lang/Void;";
|
|
|
|
public static class GenericParameters {
|
|
int start;
|
|
public List<TargetType> parameters = new ArrayList<>();
|
|
}
|
|
|
|
private static String applyDescriptor(TargetType type, GenericParameters gep) {
|
|
if (type == null) return VOID;
|
|
var res = "L" + type.getInternalName();
|
|
if (type instanceof TargetSpecializedType a) {
|
|
if (a.params().size() > 0) {
|
|
res += "<";
|
|
for (var param : a.params()) {
|
|
if (param instanceof TargetGenericType gp) {
|
|
gep.parameters.add(param);
|
|
res += "TT" + gep.start + ";";
|
|
gep.start += 1;
|
|
} else {
|
|
res += applyDescriptor(param, gep);
|
|
}
|
|
}
|
|
res += ">";
|
|
}
|
|
} else {
|
|
gep.parameters.add(null);
|
|
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)); }
|
|
|
|
public static String encodeType(TargetType type) {
|
|
if (type == null) return VOID;
|
|
return applyNameDescriptor(type).replace("/", "$").replace(";", "$_$");
|
|
}
|
|
|
|
public static byte[] generateSuperBytecode(int numberArguments) {
|
|
StringBuilder superFunNClassSignature = new StringBuilder("<");
|
|
StringBuilder superFunNMethodSignature = new StringBuilder("(");
|
|
StringBuilder superFunNMethodDescriptor = new StringBuilder("(");
|
|
|
|
for (int currentParameter = 1; currentParameter <= numberArguments; currentParameter++){
|
|
superFunNClassSignature.append(String.format("%s%d:%s", argumentGenericBase, currentParameter, objectSignature));
|
|
superFunNMethodSignature.append(String.format("T%s;", argumentGenericBase + currentParameter));
|
|
superFunNMethodDescriptor.append(objectSignature);
|
|
}
|
|
superFunNClassSignature.append(String.format("%s:%s>%s", returnGeneric, objectSignature, objectSignature));
|
|
superFunNMethodSignature.append(String.format(")T%s;", 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);
|
|
methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, methodName, superFunNMethodDescriptor.toString(), superFunNMethodSignature.toString(), null);
|
|
methodVisitor.visitEnd();
|
|
classWriter.visitEnd();
|
|
return classWriter.toByteArray();
|
|
}
|
|
|
|
public static String getSuperClassName(int numberArguments) {
|
|
return String.format("Fun%d$$", numberArguments);
|
|
}
|
|
|
|
public static byte[] generateSpecializedBytecode(List<TargetType> argumentTypes, TargetType returnType, GenericParameters gep) {
|
|
List<TargetType> parameters = Stream
|
|
.concat(argumentTypes.stream(), Stream.of(returnType))
|
|
.toList();
|
|
|
|
StringBuilder funNClassSignature = new StringBuilder(objectSignature + applyDescriptor(new TargetRefType(getSuperClassName(argumentTypes.size()), parameters), gep));
|
|
boolean containsGeneric = false;
|
|
|
|
String genericSignature = "<";
|
|
for (var i = 0; i < gep.start; 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())});
|
|
classWriter.visitEnd();
|
|
return classWriter.toByteArray();
|
|
}
|
|
|
|
private static void collectGenericTypeArguments(Map<TargetGenericType, Integer> generics, TargetType typeArgument) {
|
|
if (typeArgument instanceof TargetSpecializedType specializedType) {
|
|
for (var arg : specializedType.params())
|
|
collectGenericTypeArguments(generics, arg);
|
|
} else if (typeArgument instanceof TargetGenericType genericType) {
|
|
if (generics.containsKey(genericType))
|
|
generics.put(genericType, generics.get(genericType) + 1);
|
|
else generics.put(genericType, 0);
|
|
}
|
|
}
|
|
|
|
public static String getSpecializedClassName(List<TargetType> argumentTypes, TargetType returnType) {
|
|
return String.format("Fun%d$$%s%s",
|
|
argumentTypes.size(),
|
|
argumentTypes
|
|
.stream()
|
|
.map(FunNGenerator::encodeType)
|
|
.collect(Collectors.joining()),
|
|
encodeType(returnType));
|
|
}
|
|
|
|
public static List<TargetType> getArguments(List<TargetType> list) {
|
|
return list
|
|
.stream()
|
|
.limit(Math.max(0, list.size() - 1))
|
|
.collect(Collectors.toList());
|
|
}
|
|
|
|
public static TargetType getReturnType(List<TargetType> list) {
|
|
if(list.size() == 0)
|
|
throw new IndexOutOfBoundsException();
|
|
return list.get(list.size() - 1);
|
|
}
|
|
}
|