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 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 argumentTypes, TargetType returnType, GenericParameters gep) { List 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 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 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 getArguments(List list) { return list .stream() .limit(Math.max(0, list.size() - 1)) .collect(Collectors.toList()); } public static TargetType getReturnType(List list) { if(list.size() == 0) throw new IndexOutOfBoundsException(); return list.get(list.size() - 1); } }