From d4a59c1672afdd11a26a20a23942cddaa077b3c5 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 6 Apr 2020 01:47:09 +0100 Subject: [PATCH 01/30] start --- .../filament/mappingpoet/ClassBuilder.java | 78 ++++++++ .../filament/mappingpoet/FieldBuilder.java | 69 +++++++ .../fabricmc/filament/mappingpoet/Main.java | 110 +++++++++++ .../filament/mappingpoet/MappingsStore.java | 98 ++++++++++ .../filament/mappingpoet/MethodBuilder.java | 174 ++++++++++++++++++ .../filament/mappingpoet/ModifierBuilder.java | 67 +++++++ 6 files changed, 596 insertions(+) create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java new file mode 100644 index 0000000000..cee2d077ac --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -0,0 +1,78 @@ +package net.fabricmc.mappingpoet; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.TypeSpec; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +import java.lang.reflect.Modifier; + +public class ClassBuilder { + + private final MappingsStore mappings; + private final ClassNode classNode; + + private final TypeSpec.Builder builder; + + public ClassBuilder(MappingsStore mappings, ClassNode classNode) { + this.mappings = mappings; + this.classNode = classNode; + this.builder = setupBuilder(); + addInterfaces(); + addMethods(); + addFields(); + addJavaDoc(); + } + + private TypeSpec.Builder setupBuilder() { + return TypeSpec.classBuilder(getClassName(classNode.name)) + .addModifiers(new ModifierBuilder(classNode.access).getModifiers(ModifierBuilder.Type.CLASS)) + .superclass(getClassName(classNode.superName)); + } + + private void addInterfaces() { + if (classNode.interfaces == null) return; + + for (String iFace :classNode.interfaces){ + builder.addSuperinterface(getClassName(iFace)); + } + } + + private void addMethods() { + if (classNode.methods == null) return; + for (MethodNode method : classNode.methods) { + if ((method.access & Opcodes.ACC_SYNTHETIC) != 0) { + continue; + } + if (method.name.equals("")) { + continue; + } + builder.addMethod(new MethodBuilder(mappings, classNode, method).build()); + } + } + + private void addFields() { + if (classNode.fields == null) return; + for (FieldNode field : classNode.fields) { + builder.addField(new FieldBuilder(mappings, classNode, field).build()); + } + } + + private void addJavaDoc() { + String javadoc = mappings.getClassDoc(classNode.name); + if (javadoc != null) { + builder.addJavadoc(javadoc); + } + } + + public TypeSpec buildTypeSpec() { + return builder.build(); + } + + public static ClassName getClassName(String input) { + int lastDelim = input.lastIndexOf("/"); + return ClassName.get(input.substring(0, lastDelim).replaceAll("/", ".").replaceAll("\\$", "."), input.substring(lastDelim + 1)); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java new file mode 100644 index 0000000000..faf0105de4 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -0,0 +1,69 @@ +package net.fabricmc.mappingpoet; + +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.TypeName; +import net.fabricmc.mappings.EntryTriple; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; + +public class FieldBuilder { + private final MappingsStore mappings; + private final ClassNode classNode; + private final FieldNode fieldNode; + private final FieldSpec.Builder builder; + + public FieldBuilder(MappingsStore mappings, ClassNode classNode, FieldNode fieldNode) { + this.mappings = mappings; + this.classNode = classNode; + this.fieldNode = fieldNode; + this.builder = createBuilder(); + addJavaDoc(); + } + + private FieldSpec.Builder createBuilder() { + return FieldSpec.builder(getFieldType(fieldNode.desc), fieldNode.name) + .addModifiers(new ModifierBuilder(fieldNode.access).getModifiers(ModifierBuilder.Type.FIELD)); + } + + private void addJavaDoc() { + String javaDoc = mappings.getFieldDoc(new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); + if (javaDoc != null) { + builder.addJavadoc(javaDoc); + } + } + + public static TypeName getFieldType(String desc) { + switch (desc) { + case "B": + return TypeName.BYTE; + case "C": + return TypeName.CHAR; + case "S": + return TypeName.SHORT; + case "Z": + return TypeName.BOOLEAN; + case "I": + return TypeName.INT; + case "J": + return TypeName.LONG; + case "F": + return TypeName.FLOAT; + case "D": + return TypeName.DOUBLE; + case "V": + return TypeName.VOID; + } + if (desc.startsWith("[")) { + return ArrayTypeName.of(getFieldType(desc.substring(1))); + } + if (desc.startsWith("L")) { + return ClassBuilder.getClassName(desc.substring(1).substring(0, desc.length() -2)); + } + throw new UnsupportedOperationException("Unknown field type" + desc); + } + + public FieldSpec build() { + return builder.build(); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java new file mode 100644 index 0000000000..8a3f12d6e3 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -0,0 +1,110 @@ +package net.fabricmc.mappingpoet; + +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.TypeSpec; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.function.Consumer; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class Main { + + public static void main(String[] args) { + if (args.length != 3) { + System.out.println(" "); + return; + } + Path mappings = Paths.get(args[0]); + Path inputJar = Paths.get(args[1]); + Path outputDirectory = Paths.get(args[2]); + + try { + if (Files.exists(outputDirectory)) { + Files.walk(outputDirectory) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + + Files.createDirectories(outputDirectory); + } catch (IOException e) { + e.printStackTrace(); + } + + if (!Files.exists(mappings)) { + System.out.println("could not find mappings"); + return; + } + + if (!Files.exists(inputJar)) { + System.out.println("could not find input jar"); + return; + } + + generate(mappings, inputJar, outputDirectory); + } + + public static void generate(Path mappings, Path inputJar, Path outputDirectory) { + final MappingsStore mapping = new MappingsStore(mappings); + forEachClass(inputJar, classNode -> writeClass(mapping, classNode, outputDirectory)); + } + + private static void forEachClass(Path jar, Consumer classNodeConsumer) { + try (final JarFile jarFile = new JarFile(jar.toFile())) { + Enumeration entryEnumerator = jarFile.entries(); + + while (entryEnumerator.hasMoreElements()) { + JarEntry entry = entryEnumerator.nextElement(); + + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + continue; + } + + try(InputStream is = jarFile.getInputStream(entry)) { + ClassReader reader = new ClassReader(is); + ClassNode classNode = new ClassNode(); + reader.accept(classNode, 0); + + classNodeConsumer.accept(classNode); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static void writeClass(MappingsStore mappings, ClassNode classNode, Path outputDirectory) { + if (classNode.name.contains("$")) { + return; //TODO inner classes + } + ClassBuilder classBuilder = new ClassBuilder(mappings, classNode); + + JavaFile javaFile = JavaFile.builder(classNode.name.replaceAll("/", ".").substring(0, classNode.name.lastIndexOf("/")), classBuilder.buildTypeSpec()) + .build(); + +// Path output = outputDirectory.resolve(classNode.name + ".java"); + + try { + javaFile.writeTo(outputDirectory); + } catch (IOException e) { + throw new RuntimeException("Failed to write class", e); + } + } + + private static TypeSpec.Builder createTypeSpecBuilder(ClassNode classNode) { + return TypeSpec.classBuilder(classNode.name) + .addModifiers(); + } + + +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java new file mode 100644 index 0000000000..c26f44e6d6 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -0,0 +1,98 @@ +package net.fabricmc.mappingpoet; + +import net.fabricmc.mapping.tree.ClassDef; +import net.fabricmc.mapping.tree.FieldDef; +import net.fabricmc.mapping.tree.MethodDef; +import net.fabricmc.mapping.tree.ParameterDef; +import net.fabricmc.mapping.tree.TinyMappingFactory; +import net.fabricmc.mapping.tree.TinyTree; +import net.fabricmc.mappings.EntryTriple; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +//Taken from loom +public class MappingsStore { + private final Map classes = new HashMap<>(); + private final Map fields = new HashMap<>(); + private final Map methods = new HashMap<>(); + + private final String namespace = "named"; + + public MappingsStore(Path tinyFile) { + final TinyTree mappings = readMappings(tinyFile); + + for (ClassDef classDef : mappings.getClasses()) { + final String className = classDef.getName(namespace); + classes.put(className, classDef); + + for (FieldDef fieldDef : classDef.getFields()) { + fields.put(new EntryTriple(className, fieldDef.getName(namespace), fieldDef.getDescriptor(namespace)), fieldDef); + } + + for (MethodDef methodDef : classDef.getMethods()) { + methods.put(new EntryTriple(className, methodDef.getName(namespace), methodDef.getDescriptor(namespace)), methodDef); + } + } + } + + public String getClassDoc(String className) { + ClassDef classDef = classes.get(className); + return classDef != null ? classDef.getComment() : null; + } + + public String getFieldDoc(EntryTriple fieldEntry) { + FieldDef fieldDef = fields.get(fieldEntry); + return fieldDef != null ? fieldDef.getComment() : null; + } + + public String getMethodDoc(EntryTriple methodEntry) { + MethodDef methodDef = methods.get(methodEntry); + + if (methodDef != null) { + List parts = new ArrayList<>(); + + if (methodDef.getComment() != null) { + parts.add(methodDef.getComment()); + } + + boolean addedParam = false; + + for (ParameterDef param : methodDef.getParameters()) { + String comment = param.getComment(); + + if (comment != null) { + if (!addedParam && methodDef.getComment() != null) { + //Add a blank line before params when the method has a comment + parts.add(""); + addedParam = true; + } + + parts.add(String.format("@param %s %s", param.getName(namespace), comment)); + } + } + + if (parts.isEmpty()) { + return null; + } + + return String.join("\n", parts); + } + + return null; + } + + private static TinyTree readMappings(Path input) { + try (BufferedReader reader = Files.newBufferedReader(input)) { + return TinyMappingFactory.loadWithDetection(reader); + } catch (IOException e) { + throw new RuntimeException("Failed to read mappings", e); + } + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java new file mode 100644 index 0000000000..e613e028a8 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -0,0 +1,174 @@ +package net.fabricmc.mappingpoet; + +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeName; +import net.fabricmc.mappings.EntryTriple; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.ParameterNode; + +import javax.lang.model.element.Modifier; +import java.util.ArrayList; +import java.util.List; + +public class MethodBuilder { + private final MappingsStore mappings; + private final ClassNode classNode; + private final MethodNode methodNode; + private final MethodSpec.Builder builder; + + public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode) { + this.mappings = mappings; + this.classNode = classNode; + this.methodNode = methodNode; + this.builder = createBuilder(); + addJavaDoc(); + setReturnType(); + addParameters(); + } + + private MethodSpec.Builder createBuilder() { + return MethodSpec.methodBuilder(methodNode.name) + .addModifiers(new ModifierBuilder(methodNode.access).getModifiers(ModifierBuilder.Type.METHOD)); + } + + private void setReturnType() { + //Skip constructors + if (methodNode.name.equals("")) { + return; + } + String returnDesc = methodNode.desc.substring(methodNode.desc.lastIndexOf(")") + 1); + + TypeName typeName = FieldBuilder.getFieldType(returnDesc); + builder.returns(typeName); + if (typeName != TypeName.VOID && !java.lang.reflect.Modifier.isAbstract(methodNode.access)) { + builder.addStatement("throw new RuntimeException();"); + } + } + + private void addParameters() { + List paramTypes = new ArrayList<>(); + getParams(methodNode.desc, paramTypes); + for (ParamType paramType : paramTypes) { + builder.addParameter(paramType.type, paramType.name, paramType.modifiers); + } + } + + //TODO this prob needs a rewrite + private void getParams(String desc, List paramTypes) { + if (desc.isEmpty() || desc.charAt(0) == ')') { + return; + } + if (desc.charAt(0) == '(') { + getParams(desc.substring(1), paramTypes); + return; + } + TypeName type = TypeName.VOID; + + int width = 1; + char cha = desc.charAt(0); + + switch (cha) { + case 'B': + type = TypeName.BYTE; + break; + case 'C': + type = TypeName.CHAR; + break; + case 'S': + type = TypeName.SHORT; + break; + case 'Z': + type = TypeName.BOOLEAN; + break; + case 'I': + type = TypeName.INT; + break; + case 'J': + type = TypeName.LONG; + break; + case 'F': + type = TypeName.FLOAT; + break; + case 'D': + type = TypeName.DOUBLE; + break; + case 'L' : + desc = desc.substring(1); + String clazz = desc.substring(0, desc.indexOf(";")); + width = clazz.length() + 1; + type = ClassBuilder.getClassName(clazz); + } + + paramTypes.add(new ParamType("fixme", type, paramTypes.size())); + getParams(desc.substring(width), paramTypes); + } + + private class ParamType { + private final String name; + private final TypeName type; + private final Modifier[] modifiers; + + public ParamType(String name, TypeName type, int index) { + this.name = getValidName(type) + "_" + index; + this.type = type; + this.modifiers = new ModifierBuilder(0) + .getModifiers(ModifierBuilder.Type.PARAM); + } + + private String getValidName(TypeName type) { + String str = type.toString(); + if (str.contains(".")) { + str = str.substring(str.lastIndexOf(".") + 1); + } + str = Character.toLowerCase(str.charAt(0)) + str.substring(1); + + if (str.equals("boolean")) { + str = "bool"; + } + return str; + } + } + + private void addJavaDoc() { + String javaDoc = mappings.getMethodDoc(new EntryTriple(classNode.name, methodNode.name, methodNode.desc)); + if (javaDoc != null) { + builder.addJavadoc(javaDoc); + } + } + + private TypeName getFieldType(String desc) { + switch (desc) { + case "B": + return TypeName.BYTE; + case "C": + return TypeName.CHAR; + case "S": + return TypeName.SHORT; + case "Z": + return TypeName.BOOLEAN; + case "I": + return TypeName.INT; + case "J": + return TypeName.LONG; + case "F": + return TypeName.FLOAT; + case "D": + return TypeName.DOUBLE; + } + if (desc.startsWith("[")) { + return ArrayTypeName.of(getFieldType(desc.substring(1))); + } + if (desc.startsWith("L")) { + return ClassBuilder.getClassName(desc.substring(1).substring(0, desc.length() -2)); + } + throw new UnsupportedOperationException("Unknown field type" + desc); + } + + public MethodSpec build() { + return builder.build(); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java new file mode 100644 index 0000000000..2e0d0460d2 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java @@ -0,0 +1,67 @@ +package net.fabricmc.mappingpoet; + +import javax.lang.model.element.Modifier; +import java.util.ArrayList; +import java.util.List; + +public class ModifierBuilder { + + private final int access; + + public ModifierBuilder(int access) { + this.access = access; + } + + public Modifier[] getModifiers(Type type) { + List modifiers = new ArrayList<>(); + + if (type == Type.PARAM) { + if (java.lang.reflect.Modifier.isFinal(access)) { + modifiers.add(Modifier.FINAL); + } + return modifiers.toArray(new Modifier[]{}); + } + + if (java.lang.reflect.Modifier.isPublic(access)) { + modifiers.add(Modifier.PUBLIC); + } else if (java.lang.reflect.Modifier.isPrivate(access)) { + modifiers.add(Modifier.PRIVATE); + } else if (java.lang.reflect.Modifier.isProtected(access)) { + modifiers.add(Modifier.PROTECTED); + } + + if (java.lang.reflect.Modifier.isAbstract(access)) { + modifiers.add(Modifier.ABSTRACT); + } + if (java.lang.reflect.Modifier.isStatic(access)) { + modifiers.add(Modifier.STATIC); + } + if (java.lang.reflect.Modifier.isFinal(access)) { + modifiers.add(Modifier.FINAL); + } + if (java.lang.reflect.Modifier.isTransient(access) && type == Type.FIELD) { + modifiers.add(Modifier.TRANSIENT); + } + if (java.lang.reflect.Modifier.isVolatile(access) && type == Type.FIELD) { + modifiers.add(Modifier.VOLATILE); + } + if (java.lang.reflect.Modifier.isSynchronized(access) && type == Type.METHOD) { + modifiers.add(Modifier.SYNCHRONIZED); + } + if (java.lang.reflect.Modifier.isNative(access) && type == Type.METHOD) { + modifiers.add(Modifier.NATIVE); + } + if (java.lang.reflect.Modifier.isStrict(access)) { + modifiers.add(Modifier.STRICTFP); + } + + return modifiers.toArray(new Modifier[]{}); + } + + public enum Type { + CLASS, + METHOD, + FIELD, + PARAM + } +} From 3d528c08103983014a30c595f5bdd8b7166d0324 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 6 Apr 2020 15:26:57 +0100 Subject: [PATCH 02/30] More work and fixes --- .../filament/mappingpoet/ClassBuilder.java | 43 ++++++++++++-- .../fabricmc/filament/mappingpoet/Main.java | 58 ++++++++++++++----- .../filament/mappingpoet/MappingsStore.java | 15 +++++ .../filament/mappingpoet/MethodBuilder.java | 17 +++--- .../filament/mappingpoet/ModifierBuilder.java | 6 +- 5 files changed, 110 insertions(+), 29 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index cee2d077ac..89e7e965a0 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -8,6 +8,9 @@ import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class ClassBuilder { @@ -15,6 +18,7 @@ public class ClassBuilder { private final ClassNode classNode; private final TypeSpec.Builder builder; + private final List innerClasses = new ArrayList<>(); public ClassBuilder(MappingsStore mappings, ClassNode classNode) { this.mappings = mappings; @@ -27,9 +31,18 @@ public class ClassBuilder { } private TypeSpec.Builder setupBuilder() { - return TypeSpec.classBuilder(getClassName(classNode.name)) - .addModifiers(new ModifierBuilder(classNode.access).getModifiers(ModifierBuilder.Type.CLASS)) - .superclass(getClassName(classNode.superName)); + TypeSpec.Builder builder; + if (Modifier.isInterface(classNode.access)) { + builder = TypeSpec.interfaceBuilder(getClassName(classNode.name)); + } else if (classNode.superName.equals("java.lang.Enum")) { + builder = TypeSpec.enumBuilder(getClassName(classNode.name)); + } else { + builder = TypeSpec.classBuilder(getClassName(classNode.name)) + .superclass(getClassName(classNode.superName)); + } + + return builder + .addModifiers(new ModifierBuilder(classNode.access).getModifiers(ModifierBuilder.Type.CLASS)); } private void addInterfaces() { @@ -67,12 +80,32 @@ public class ClassBuilder { } } - public TypeSpec buildTypeSpec() { + public void addInnerClass(ClassBuilder classBuilder) { + classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.PUBLIC); + classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); + innerClasses.add(classBuilder); + } + + public String getClassName() { + return classNode.name; + } + + public TypeSpec build() { + innerClasses.stream() + .map(ClassBuilder::build) + .forEach(builder::addType); return builder.build(); } public static ClassName getClassName(String input) { int lastDelim = input.lastIndexOf("/"); - return ClassName.get(input.substring(0, lastDelim).replaceAll("/", ".").replaceAll("\\$", "."), input.substring(lastDelim + 1)); + String packageName = input.substring(0, lastDelim).replaceAll("/", "."); + String className = input.substring(lastDelim + 1).replaceAll("/", "."); + + List classSplit = new ArrayList<>(Arrays.asList(className.split("\\$"))); + String parentClass = classSplit.get(0); + classSplit.remove(0); + + return ClassName.get(packageName, parentClass, classSplit.toArray(new String[]{})); } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 8a3f12d6e3..0dc0bf0cbe 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -11,8 +11,12 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Comparator; import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Consumer; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -56,10 +60,26 @@ public class Main { public static void generate(Path mappings, Path inputJar, Path outputDirectory) { final MappingsStore mapping = new MappingsStore(mappings); - forEachClass(inputJar, classNode -> writeClass(mapping, classNode, outputDirectory)); + Map classes = new HashMap<>(); + forEachClass(inputJar, classNode -> writeClass(mapping, classNode, classes)); + + classes.values().stream() + .filter(classBuilder -> !classBuilder.getClassName().contains("$")) + .forEach(classBuilder -> { + JavaFile javaFile = JavaFile.builder(classBuilder.getClassName().replaceAll("/", ".").substring(0, classBuilder.getClassName().lastIndexOf("/")), classBuilder.build()) + .build(); + try { + javaFile.writeTo(outputDirectory); + } catch (IOException e) { + throw new RuntimeException("Failed to write class", e); + } + }); + + } private static void forEachClass(Path jar, Consumer classNodeConsumer) { + List classes = new ArrayList<>(); try (final JarFile jarFile = new JarFile(jar.toFile())) { Enumeration entryEnumerator = jarFile.entries(); @@ -75,30 +95,40 @@ public class Main { ClassNode classNode = new ClassNode(); reader.accept(classNode, 0); - classNodeConsumer.accept(classNode); + classes.add(classNode); } } } catch (IOException e) { throw new RuntimeException(e); } + + //Sort all the classes making sure that inner classes come after the parent classes + classes.sort(Comparator.comparing(o -> o.name)); + + classes.forEach(classNodeConsumer::accept); } - private static void writeClass(MappingsStore mappings, ClassNode classNode, Path outputDirectory) { - if (classNode.name.contains("$")) { - return; //TODO inner classes + private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses) { + try { + //Awful code to skip anonymous classes + Integer.parseInt(classNode.name.substring(classNode.name.lastIndexOf('$') + 1)); + return; + } catch (Exception e) { + //Nope all good if this fails } + ClassBuilder classBuilder = new ClassBuilder(mappings, classNode); - JavaFile javaFile = JavaFile.builder(classNode.name.replaceAll("/", ".").substring(0, classNode.name.lastIndexOf("/")), classBuilder.buildTypeSpec()) - .build(); - -// Path output = outputDirectory.resolve(classNode.name + ".java"); - - try { - javaFile.writeTo(outputDirectory); - } catch (IOException e) { - throw new RuntimeException("Failed to write class", e); + if (classNode.name.contains("$")) { + String parentClass = classNode.name.substring(0, classNode.name.lastIndexOf("$")); + if (!existingClasses.containsKey(parentClass)) { + throw new RuntimeException("Could not find parent class: " + parentClass + " for " + classNode.name); + } + existingClasses.get(parentClass).addInnerClass(classBuilder); } + + existingClasses.put(classNode.name, classBuilder); + } private static TypeSpec.Builder createTypeSpecBuilder(ClassNode classNode) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java index c26f44e6d6..1727698a44 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -52,6 +52,21 @@ public class MappingsStore { return fieldDef != null ? fieldDef.getComment() : null; } + public String getParamName(EntryTriple methodEntry, int index) { + MethodDef methodDef = methods.get(methodEntry); + if (methodDef != null) { + if (methodDef.getParameters().isEmpty()) { + return null; + } + return methodDef.getParameters().stream() + .filter(param -> param.getLocalVariableIndex() == index) + .map(param -> param.getName(namespace)) + .findFirst() + .orElse(null); + } + return null; + } + public String getMethodDoc(EntryTriple methodEntry) { MethodDef methodDef = methods.get(methodEntry); diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index e613e028a8..23b0ca3bd0 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -1,14 +1,11 @@ package net.fabricmc.mappingpoet; import com.squareup.javapoet.ArrayTypeName; -import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeName; import net.fabricmc.mappings.EntryTriple; import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.MethodNode; -import org.objectweb.asm.tree.ParameterNode; import javax.lang.model.element.Modifier; import java.util.ArrayList; @@ -31,8 +28,12 @@ public class MethodBuilder { } private MethodSpec.Builder createBuilder() { - return MethodSpec.methodBuilder(methodNode.name) + MethodSpec.Builder builder = MethodSpec.methodBuilder(methodNode.name) .addModifiers(new ModifierBuilder(methodNode.access).getModifiers(ModifierBuilder.Type.METHOD)); + if (methodNode.name.equals("") || !java.lang.reflect.Modifier.isInterface(classNode.access)) { + builder.modifiers.remove(Modifier.DEFAULT); + } + return builder; } private void setReturnType() { @@ -44,8 +45,8 @@ public class MethodBuilder { TypeName typeName = FieldBuilder.getFieldType(returnDesc); builder.returns(typeName); - if (typeName != TypeName.VOID && !java.lang.reflect.Modifier.isAbstract(methodNode.access)) { - builder.addStatement("throw new RuntimeException();"); + if (typeName != TypeName.VOID && !builder.modifiers.contains(Modifier.ABSTRACT)) { + builder.addStatement("throw new RuntimeException()"); } } @@ -103,7 +104,7 @@ public class MethodBuilder { type = ClassBuilder.getClassName(clazz); } - paramTypes.add(new ParamType("fixme", type, paramTypes.size())); + paramTypes.add(new ParamType(mappings.getParamName(new EntryTriple(classNode.name, methodNode.name, methodNode.desc), paramTypes.size() + 1), type, paramTypes.size())); getParams(desc.substring(width), paramTypes); } @@ -113,7 +114,7 @@ public class MethodBuilder { private final Modifier[] modifiers; public ParamType(String name, TypeName type, int index) { - this.name = getValidName(type) + "_" + index; + this.name = name != null ? name : getValidName(type) + "_" + index; this.type = type; this.modifiers = new ModifierBuilder(0) .getModifiers(ModifierBuilder.Type.PARAM); diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java index 2e0d0460d2..de616c623e 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java @@ -32,10 +32,12 @@ public class ModifierBuilder { if (java.lang.reflect.Modifier.isAbstract(access)) { modifiers.add(Modifier.ABSTRACT); - } - if (java.lang.reflect.Modifier.isStatic(access)) { + } else if (java.lang.reflect.Modifier.isStatic(access)) { modifiers.add(Modifier.STATIC); + } else if (type == Type.METHOD) { + modifiers.add(Modifier.DEFAULT); } + if (java.lang.reflect.Modifier.isFinal(access)) { modifiers.add(Modifier.FINAL); } From 4dd2cdd8318493ca38a61caf69336e3c0f755054 Mon Sep 17 00:00:00 2001 From: liach <7806504+liach@users.noreply.github.com> Date: Tue, 7 Apr 2020 12:52:11 -0500 Subject: [PATCH 03/30] Parse signatures and handle enum etc better (#1) * Add signature parser Signed-off-by: liach * Tweak the signature collector a bit more Signed-off-by: liach * I guess the parser works? hmm Signed-off-by: liach * Ah yes works very fine, enum now works Signed-off-by: liach * Remove guava, don't want to jump into dep hell * Tweaks as I try * Inner class fixes Signed-off-by: liach * Handle param name conflicts from mappings Signed-off-by: liach * Fix bug * Generate better default field values and remove deprecated methods Signed-off-by: liach * Fix problems lol * Remove shadow Signed-off-by: liach * Better generate default code stub and use javapoet to attach param comments Signed-off-by: liach * Improve generated default again * Respect inheritance of methods Signed-off-by: liach * Now with annotations Signed-off-by: liach Co-authored-by: liach --- .../filament/mappingpoet/ClassBuilder.java | 158 ++++-- .../filament/mappingpoet/FieldBuilder.java | 287 +++++++++- .../fabricmc/filament/mappingpoet/Main.java | 51 +- .../filament/mappingpoet/MappingsStore.java | 67 ++- .../filament/mappingpoet/MethodBuilder.java | 321 +++++++---- .../filament/mappingpoet/ModifierBuilder.java | 11 +- .../filament/mappingpoet/Signatures.java | 497 ++++++++++++++++++ 7 files changed, 1175 insertions(+), 217 deletions(-) create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 89e7e965a0..926bb5f900 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -1,17 +1,20 @@ package net.fabricmc.mappingpoet; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; + import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeSpec; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.InnerClassNode; import org.objectweb.asm.tree.MethodNode; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - public class ClassBuilder { private final MappingsStore mappings; @@ -19,60 +22,147 @@ public class ClassBuilder { private final TypeSpec.Builder builder; private final List innerClasses = new ArrayList<>(); + private final Function> superGetter; - public ClassBuilder(MappingsStore mappings, ClassNode classNode) { + private Signatures.ClassSignature signature; + private boolean enumClass; + + public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function> superGetter) { this.mappings = mappings; this.classNode = classNode; + this.superGetter = superGetter; this.builder = setupBuilder(); addInterfaces(); + addDirectAnnotations(); addMethods(); addFields(); addJavaDoc(); } + public static ClassName parseInternalName(String internalName) { + int classNameSeparator = -1; + int index = 0; + int nameStart = index; + ClassName currentClassName = null; + + char ch; + do { + ch = index == internalName.length() ? ';' : internalName.charAt(index); + + if (ch == '$' || ch == ';') { + // collect class name + if (currentClassName == null) { + String packageName = nameStart < classNameSeparator ? internalName.substring(nameStart, classNameSeparator).replace('/', '.') : ""; + String simpleName = internalName.substring(classNameSeparator + 1, index); + currentClassName = ClassName.get(packageName, simpleName); + } else { + String simpleName = internalName.substring(classNameSeparator + 1, index); + currentClassName = currentClassName.nestedClass(simpleName); + } + } + + if (ch == '/' || ch == '$') { + // Start of simple name + classNameSeparator = index; + } + + index++; + } while (ch != ';'); + + if (currentClassName == null) { + throw new IllegalArgumentException(String.format("Invalid internal name \"%s\"", internalName)); + } + + return currentClassName; + } + private TypeSpec.Builder setupBuilder() { + if (classNode.signature != null) { + signature = Signatures.parseClassSignature(classNode.signature); + } TypeSpec.Builder builder; if (Modifier.isInterface(classNode.access)) { - builder = TypeSpec.interfaceBuilder(getClassName(classNode.name)); - } else if (classNode.superName.equals("java.lang.Enum")) { - builder = TypeSpec.enumBuilder(getClassName(classNode.name)); + builder = TypeSpec.interfaceBuilder(parseInternalName(classNode.name)); + } else if (classNode.superName.equals("java/lang/Enum")) { + enumClass = true; + builder = TypeSpec.enumBuilder(parseInternalName(classNode.name)); } else { - builder = TypeSpec.classBuilder(getClassName(classNode.name)) - .superclass(getClassName(classNode.superName)); + builder = TypeSpec.classBuilder(parseInternalName(classNode.name)) + .superclass(signature == null ? parseInternalName(classNode.superName) : signature.superclass); + } + + if (signature != null && signature.generics != null) { + builder.addTypeVariables(signature.generics); } return builder - .addModifiers(new ModifierBuilder(classNode.access).getModifiers(ModifierBuilder.Type.CLASS)); + .addModifiers(new ModifierBuilder(classNode.access).getModifiers(enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS)); } private void addInterfaces() { + if (signature != null) { + builder.addSuperinterfaces(signature.superinterfaces); + return; + } if (classNode.interfaces == null) return; - for (String iFace :classNode.interfaces){ - builder.addSuperinterface(getClassName(iFace)); + for (String iFace : classNode.interfaces) { + builder.addSuperinterface(parseInternalName(iFace)); } } - + + private void addDirectAnnotations() { + addDirectAnnotations(classNode.invisibleAnnotations); + addDirectAnnotations(classNode.visibleAnnotations); + } + + private void addDirectAnnotations(List regularAnnotations) { + if (regularAnnotations == null) { + return; + } + for (AnnotationNode annotation : regularAnnotations) { + builder.addAnnotation(FieldBuilder.parseAnnotation(annotation)); + } + } + private void addMethods() { if (classNode.methods == null) return; for (MethodNode method : classNode.methods) { - if ((method.access & Opcodes.ACC_SYNTHETIC) != 0) { + if ((method.access & Opcodes.ACC_SYNTHETIC) != 0 || (method.access & Opcodes.ACC_MANDATED) != 0) { continue; } if (method.name.equals("")) { continue; } - builder.addMethod(new MethodBuilder(mappings, classNode, method).build()); + if (enumClass) { + // Skip enum sugar methods + if (method.name.equals("values") && method.desc.equals("()[L" + classNode.name + ";")) { + continue; + } + if (method.name.equals("valueOf") && method.desc.equals("(Ljava/lang/String;)L" + classNode.name + ";")) { + continue; + } + } + builder.addMethod(new MethodBuilder(mappings, classNode, method, superGetter).build()); } } private void addFields() { if (classNode.fields == null) return; for (FieldNode field : classNode.fields) { - builder.addField(new FieldBuilder(mappings, classNode, field).build()); + if ((field.access & Opcodes.ACC_SYNTHETIC) != 0 || (field.access & Opcodes.ACC_MANDATED) != 0) { + continue; // hide synthetic stuff + } + if ((field.access & Opcodes.ACC_ENUM) == 0) { + builder.addField(new FieldBuilder(mappings, classNode, field).build()); + } else { + TypeSpec.Builder enumBuilder = TypeSpec.anonymousClassBuilder(""); + FieldBuilder.addFieldJavaDoc(enumBuilder, mappings, classNode, field); + builder.addEnumConstant(field.name, enumBuilder.build()); + } } } - + private void addJavaDoc() { String javadoc = mappings.getClassDoc(classNode.name); if (javadoc != null) { @@ -81,8 +171,22 @@ public class ClassBuilder { } public void addInnerClass(ClassBuilder classBuilder) { - classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.PUBLIC); - classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); + InnerClassNode innerClassNode = null; + if (classNode.innerClasses != null) { + for (InnerClassNode node : classNode.innerClasses) { + if (node.name.equals(classBuilder.classNode.name)) { + innerClassNode = node; + break; + } + } + } + if (innerClassNode == null) { + // fallback + classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.PUBLIC); + classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); + } else { + classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access).getModifiers(classBuilder.enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS)); + } innerClasses.add(classBuilder); } @@ -96,16 +200,4 @@ public class ClassBuilder { .forEach(builder::addType); return builder.build(); } - - public static ClassName getClassName(String input) { - int lastDelim = input.lastIndexOf("/"); - String packageName = input.substring(0, lastDelim).replaceAll("/", "."); - String className = input.substring(lastDelim + 1).replaceAll("/", "."); - - List classSplit = new ArrayList<>(Arrays.asList(className.split("\\$"))); - String parentClass = classSplit.get(0); - classSplit.remove(0); - - return ClassName.get(packageName, parentClass, classSplit.toArray(new String[]{})); - } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java index faf0105de4..159e5718bd 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -1,12 +1,25 @@ package net.fabricmc.mappingpoet; +import java.util.AbstractMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.TypeName; -import net.fabricmc.mappings.EntryTriple; +import com.squareup.javapoet.TypeSpec; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; +import net.fabricmc.mappings.EntryTriple; + public class FieldBuilder { private final MappingsStore mappings; private final ClassNode classNode; @@ -18,12 +31,241 @@ public class FieldBuilder { this.classNode = classNode; this.fieldNode = fieldNode; this.builder = createBuilder(); + addDirectAnnotations(); addJavaDoc(); } + static void addFieldJavaDoc(TypeSpec.Builder enumBuilder, MappingsStore mappings, ClassNode classNode, FieldNode fieldNode) { + String javaDoc = mappings.getFieldDoc(new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); + if (javaDoc != null) { + enumBuilder.addJavadoc(javaDoc); + } + } + + public static AnnotationSpec parseAnnotation(AnnotationNode annotation) { + ClassName annoClassName = (ClassName) typeFromDesc(annotation.desc); + AnnotationSpec.Builder builder = AnnotationSpec.builder(annoClassName); + List values = annotation.values; + if (values != null) { + Iterator itr = values.iterator(); + while (itr.hasNext()) { + String key = (String) itr.next(); + Object value = itr.next(); + + builder.addMember(key, codeFromAnnoValue(value)); + } + } + + return builder.build(); + } + + private static CodeBlock codeFromAnnoValue(Object value) { + // BCDFIJSZ; String; String[] (for enum); asm type; anno node; list of any prev stuff (cannot nest) + if (value instanceof List) { + return ((List) value).stream().map(FieldBuilder::codeFromAnnoValue).collect(CodeBlock.joining(",", "{", "}")); + } + if (value instanceof Character || value instanceof Number || value instanceof Boolean) { + return CodeBlock.builder().add("$L", value).build(); + } + if (value instanceof String) { + return CodeBlock.builder().add("$S", value).build(); + } + if (value instanceof String[]) { + String[] arr = (String[]) value; + ClassName enumClassName = (ClassName) typeFromDesc(arr[0]); + String valueName = arr[1]; + return CodeBlock.builder().add("$T.$L", enumClassName, valueName).build(); + } + if (value instanceof Type) { + return CodeBlock.builder().add("$T.class", typeFromDesc(((Type) value).getDescriptor())).build(); + } + if (value instanceof AnnotationNode) { + return CodeBlock.builder().add(parseAnnotation((AnnotationNode) value).toString()).build(); + } + throw new IllegalArgumentException(String.format("Don't know how to convert \"%s\" into annotation value", value)); + } + + public static TypeName typeFromDesc(final String desc) { + return parseType(desc, 0).getValue(); + } + + public static Map.Entry parseType(final String desc, final int start) { + int index = start; + int arrayLevel = 0; + while (desc.charAt(index) == '[') { + arrayLevel++; + index++; + } + + TypeName current; + switch (desc.charAt(index)) { + case 'B': { + current = TypeName.BYTE; + index++; + break; + } + case 'C': { + current = TypeName.CHAR; + index++; + break; + } + case 'D': { + current = TypeName.DOUBLE; + index++; + break; + } + case 'F': { + current = TypeName.FLOAT; + index++; + break; + } + case 'I': { + current = TypeName.INT; + index++; + break; + } + case 'J': { + current = TypeName.LONG; + index++; + break; + } + case 'S': { + current = TypeName.SHORT; + index++; + break; + } + case 'Z': { + current = TypeName.BOOLEAN; + index++; + break; + } + case 'V': { + current = TypeName.VOID; + index++; + break; + } + case 'L': { + int classNameSeparator = index; + index++; + int nameStart = index; + ClassName currentClassName = null; + + char ch; + do { + ch = desc.charAt(index); + + if (ch == '$' || ch == ';') { + // collect class name + if (currentClassName == null) { + String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : ""; + String simpleName = desc.substring(classNameSeparator + 1, index); + currentClassName = ClassName.get(packageName, simpleName); + } else { + String simpleName = desc.substring(classNameSeparator + 1, index); + currentClassName = currentClassName.nestedClass(simpleName); + } + } + + if (ch == '/' || ch == '$') { + // Start of simple name + classNameSeparator = index; + } + + index++; + } while (ch != ';'); + + if (currentClassName == null) { + throw invalidDesc(desc, index); + } + + current = currentClassName; + break; + } + default: + throw invalidDesc(desc, index); + } + + for (int i = 0; i < arrayLevel; i++) { + current = ArrayTypeName.of(current); + } + + return new AbstractMap.SimpleImmutableEntry<>(index, current); + } + + private static IllegalArgumentException invalidDesc(String desc, int index) { + return new IllegalArgumentException(String.format("Invalid descriptor at index %d for \"%s\"", index, desc)); + } + + @Deprecated // use typeFromDesc, non-recursive + public static TypeName getFieldType(String desc) { + switch (desc) { + case "B": + return TypeName.BYTE; + case "C": + return TypeName.CHAR; + case "S": + return TypeName.SHORT; + case "Z": + return TypeName.BOOLEAN; + case "I": + return TypeName.INT; + case "J": + return TypeName.LONG; + case "F": + return TypeName.FLOAT; + case "D": + return TypeName.DOUBLE; + case "V": + return TypeName.VOID; + } + if (desc.startsWith("[")) { + return ArrayTypeName.of(getFieldType(desc.substring(1))); + } + if (desc.startsWith("L")) { + return ClassBuilder.parseInternalName(desc.substring(1).substring(0, desc.length() - 2)); + } + throw new UnsupportedOperationException("Unknown field type" + desc); + } + private FieldSpec.Builder createBuilder() { - return FieldSpec.builder(getFieldType(fieldNode.desc), fieldNode.name) + FieldSpec.Builder ret = FieldSpec.builder(calculateType(), fieldNode.name) .addModifiers(new ModifierBuilder(fieldNode.access).getModifiers(ModifierBuilder.Type.FIELD)); + + if ((fieldNode.access & Opcodes.ACC_FINAL) != 0) { + ret.initializer(makeInitializer(fieldNode.desc)); // so jd doesn't complain about type mismatch + } + + return ret; + } + + private CodeBlock makeInitializer(String desc) { + // fake initializers exclude fields from constant values + switch (desc.charAt(0)) { + case 'B': + case 'C': + case 'D': + case 'I': + case 'J': + case 'S': + if (fieldNode.value != null) { + return CodeBlock.builder().add(String.valueOf(fieldNode.value)).build(); + } + return CodeBlock.builder().add("java.lang.Byte.parseByte(\"dummy\")").build(); + case 'F': + if (fieldNode.value != null) { + return CodeBlock.builder().add(fieldNode.value + "f").build(); + } + return CodeBlock.builder().add("java.lang.Float.parseFloat(\"dummy\")").build(); + case 'Z': + if (fieldNode.value instanceof Number) { + return CodeBlock.builder().add(Boolean.toString(((Number) fieldNode.value).intValue() != 0)).build(); + } + return CodeBlock.builder().add("java.lang.Boolean.parseBoolean(\"dummy\")").build(); + } + if (fieldNode.value != null) { + return CodeBlock.builder().add("$S", fieldNode.value).build(); + } + return CodeBlock.builder().add(desc.equals("Ljava/lang/String;") ? "java.lang.String.valueOf(\"dummy\")" : "null").build(); } private void addJavaDoc() { @@ -33,34 +275,25 @@ public class FieldBuilder { } } - public static TypeName getFieldType(String desc) { - switch (desc) { - case "B": - return TypeName.BYTE; - case "C": - return TypeName.CHAR; - case "S": - return TypeName.SHORT; - case "Z": - return TypeName.BOOLEAN; - case "I": - return TypeName.INT; - case "J": - return TypeName.LONG; - case "F": - return TypeName.FLOAT; - case "D": - return TypeName.DOUBLE; - case "V": - return TypeName.VOID; + private void addDirectAnnotations() { + addDirectAnnotations(fieldNode.invisibleAnnotations); + addDirectAnnotations(fieldNode.visibleAnnotations); + } + + private void addDirectAnnotations(List regularAnnotations) { + if (regularAnnotations == null) { + return; } - if (desc.startsWith("[")) { - return ArrayTypeName.of(getFieldType(desc.substring(1))); + for (AnnotationNode annotation : regularAnnotations) { + builder.addAnnotation(parseAnnotation(annotation)); } - if (desc.startsWith("L")) { - return ClassBuilder.getClassName(desc.substring(1).substring(0, desc.length() -2)); + } + + private TypeName calculateType() { + if (fieldNode.signature != null) { + return Signatures.parseFieldSignature(fieldNode.signature); } - throw new UnsupportedOperationException("Unknown field type" + desc); + return typeFromDesc(fieldNode.desc); } public FieldSpec build() { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 0dc0bf0cbe..1db207edfb 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -12,12 +12,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -61,7 +65,7 @@ public class Main { public static void generate(Path mappings, Path inputJar, Path outputDirectory) { final MappingsStore mapping = new MappingsStore(mappings); Map classes = new HashMap<>(); - forEachClass(inputJar, classNode -> writeClass(mapping, classNode, classes)); + forEachClass(inputJar, (superGetter, classNode) -> writeClass(mapping, classNode, classes, superGetter)); classes.values().stream() .filter(classBuilder -> !classBuilder.getClassName().contains("$")) @@ -78,8 +82,9 @@ public class Main { } - private static void forEachClass(Path jar, Consumer classNodeConsumer) { + private static void forEachClass(Path jar, BiConsumer>, ClassNode> classNodeConsumer) { List classes = new ArrayList<>(); + Map> supers = new HashMap<>(); try (final JarFile jarFile = new JarFile(jar.toFile())) { Enumeration entryEnumerator = jarFile.entries(); @@ -94,6 +99,16 @@ public class Main { ClassReader reader = new ClassReader(is); ClassNode classNode = new ClassNode(); reader.accept(classNode, 0); + List superNames = new ArrayList<>(); + if (classNode.superName != null && !classNode.superName.equals("java/lang/Object")) { + superNames.add(classNode.superName); + } + if (classNode.interfaces != null) { + superNames.addAll(classNode.interfaces); + } + if (!superNames.isEmpty()) { + supers.put(classNode.name, superNames); + } classes.add(classNode); } @@ -105,29 +120,37 @@ public class Main { //Sort all the classes making sure that inner classes come after the parent classes classes.sort(Comparator.comparing(o -> o.name)); - classes.forEach(classNodeConsumer::accept); + Function> superGetter = k -> supers.getOrDefault(k, Collections.emptyList()); + classes.forEach(node -> classNodeConsumer.accept(superGetter, node)); + } + + private static boolean isDigit(char ch) { + return ch >= '0' && ch <= '9'; } - private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses) { - try { - //Awful code to skip anonymous classes - Integer.parseInt(classNode.name.substring(classNode.name.lastIndexOf('$') + 1)); - return; - } catch (Exception e) { - //Nope all good if this fails + private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses, Function> superGetter) { + String name = classNode.name; + { + //Block anonymous class and their nested classes + int lastSearch = name.length(); + while (lastSearch != -1) { + lastSearch = name.lastIndexOf('$', lastSearch - 1); + if (isDigit(name.charAt(lastSearch + 1))) // names starting with digit is illegal java + return; + } } - ClassBuilder classBuilder = new ClassBuilder(mappings, classNode); + ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter); - if (classNode.name.contains("$")) { - String parentClass = classNode.name.substring(0, classNode.name.lastIndexOf("$")); + if (name.contains("$")) { + String parentClass = name.substring(0, name.lastIndexOf("$")); if (!existingClasses.containsKey(parentClass)) { throw new RuntimeException("Could not find parent class: " + parentClass + " for " + classNode.name); } existingClasses.get(parentClass).addInnerClass(classBuilder); } - existingClasses.put(classNode.name, classBuilder); + existingClasses.put(name, classBuilder); } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java index 1727698a44..2d8e2f929c 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -3,7 +3,6 @@ package net.fabricmc.mappingpoet; import net.fabricmc.mapping.tree.ClassDef; import net.fabricmc.mapping.tree.FieldDef; import net.fabricmc.mapping.tree.MethodDef; -import net.fabricmc.mapping.tree.ParameterDef; import net.fabricmc.mapping.tree.TinyMappingFactory; import net.fabricmc.mapping.tree.TinyTree; import net.fabricmc.mappings.EntryTriple; @@ -12,10 +11,11 @@ import java.io.BufferedReader; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; +import java.util.AbstractMap; +import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.function.Function; //Taken from loom public class MappingsStore { @@ -52,56 +52,53 @@ public class MappingsStore { return fieldDef != null ? fieldDef.getComment() : null; } - public String getParamName(EntryTriple methodEntry, int index) { - MethodDef methodDef = methods.get(methodEntry); + public Map.Entry getParamNameAndDoc(Function> superGetters, EntryTriple methodEntry, int index) { + MethodDef methodDef = searchMethod(superGetters, methodEntry); if (methodDef != null) { if (methodDef.getParameters().isEmpty()) { return null; } return methodDef.getParameters().stream() .filter(param -> param.getLocalVariableIndex() == index) - .map(param -> param.getName(namespace)) + .map(param -> new AbstractMap.SimpleImmutableEntry<>(param.getName(namespace), param.getComment())) .findFirst() .orElse(null); } return null; } - public String getMethodDoc(EntryTriple methodEntry) { - MethodDef methodDef = methods.get(methodEntry); + public String getMethodDoc(Function> superGetters, EntryTriple methodEntry) { + MethodDef methodDef = searchMethod(superGetters, methodEntry); if (methodDef != null) { - List parts = new ArrayList<>(); - - if (methodDef.getComment() != null) { - parts.add(methodDef.getComment()); - } - - boolean addedParam = false; - - for (ParameterDef param : methodDef.getParameters()) { - String comment = param.getComment(); - - if (comment != null) { - if (!addedParam && methodDef.getComment() != null) { - //Add a blank line before params when the method has a comment - parts.add(""); - addedParam = true; - } - - parts.add(String.format("@param %s %s", param.getName(namespace), comment)); - } - } - - if (parts.isEmpty()) { - return null; - } - - return String.join("\n", parts); + return methodDef.getComment(); // comment doc handled separately by javapoet } return null; } + + private MethodDef searchMethod(Function> superGetters, EntryTriple methodEntry) { + String className = methodEntry.getOwner(); + if (!classes.containsKey(className)) { + return null; + } + + if (methods.containsKey(methodEntry)) { + return methods.get(methodEntry); // Nullable! + } + + for (String superName : superGetters.apply(className)) { + EntryTriple triple = new EntryTriple(superName, methodEntry.getName(), methodEntry.getDesc()); + MethodDef ret = searchMethod(superGetters, triple); + if (ret != null) { + methods.put(triple, ret); + return ret; + } + } + + methods.put(methodEntry, null); + return null; + } private static TinyTree readMappings(Path input) { try (BufferedReader reader = Files.newBufferedReader(input)) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index 23b0ca3bd0..47dce2abef 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -1,30 +1,115 @@ package net.fabricmc.mappingpoet; -import com.squareup.javapoet.ArrayTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import javax.lang.model.element.Modifier; + import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeName; -import net.fabricmc.mappings.EntryTriple; +import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; -import javax.lang.model.element.Modifier; -import java.util.ArrayList; -import java.util.List; +import net.fabricmc.mappings.EntryTriple; public class MethodBuilder { + private static final Set RESERVED_KEYWORDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", + "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if", + "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", + "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", + "throw", "throws", "transient", "try", "void", "volatile", "while" + ))); private final MappingsStore mappings; private final ClassNode classNode; private final MethodNode methodNode; private final MethodSpec.Builder builder; + private final Function> superGetter; - public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode) { + private Signatures.MethodSignature signature; + + public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode, Function> superGetter) { this.mappings = mappings; this.classNode = classNode; this.methodNode = methodNode; + this.superGetter = superGetter; this.builder = createBuilder(); addJavaDoc(); + addDirectAnnotations(); setReturnType(); addParameters(); + addExceptions(); + } + + private static void addDirectAnnotations(ParameterSpec.Builder builder, List[] regularAnnotations, int index) { + if (regularAnnotations == null || regularAnnotations.length <= index) { + return; + } + addDirectAnnotations(builder, regularAnnotations[index]); + } + + private static void addDirectAnnotations(ParameterSpec.Builder builder, List regularAnnotations) { + if (regularAnnotations == null) { + return; + } + for (AnnotationNode annotation : regularAnnotations) { + builder.addAnnotation(FieldBuilder.parseAnnotation(annotation)); + } + } + + private static IllegalArgumentException invalidMethodDesc(String desc, int index) { + return new IllegalArgumentException(String.format("Invalid method descriptor at %d: \"%s\"", index, desc)); + } + + static String reserveValidName(String suggestedName, Set usedNames) { + if (!usedNames.contains(suggestedName)) { + usedNames.add(suggestedName); + return suggestedName; + } + int t = 2; + String currentSuggestion = suggestedName + t; + while (usedNames.contains(currentSuggestion)) { + t++; + currentSuggestion = suggestedName + t; + } + + usedNames.add(currentSuggestion); + + return currentSuggestion; + } + + static String suggestName(TypeName type) { + String str = type.toString(); + int newStart = 0; + int newEnd = str.length(); + int ltStart; + ltStart = str.indexOf('<', newStart); + if (ltStart != -1 && ltStart < newEnd) { + newEnd = ltStart; + } + ltStart = str.indexOf('[', newStart); + if (ltStart != -1 && ltStart < newEnd) { + newEnd = ltStart; + } + int dotEnd; + if ((dotEnd = str.lastIndexOf(".", newEnd)) != -1) { + newStart = dotEnd + 1; + } + str = Character.toLowerCase(str.charAt(newStart)) + str.substring(newStart + 1, newEnd); + + if (str.equals("boolean")) { + str = "bool"; + } + return str; } private MethodSpec.Builder createBuilder() { @@ -33,17 +118,45 @@ public class MethodBuilder { if (methodNode.name.equals("") || !java.lang.reflect.Modifier.isInterface(classNode.access)) { builder.modifiers.remove(Modifier.DEFAULT); } + + if (methodNode.signature != null) { + signature = Signatures.parseMethodSignature(methodNode.signature); + if (signature.generics != null) { + builder.addTypeVariables(signature.generics); + } + } + return builder; } + private void addDirectAnnotations() { + addDirectAnnotations(methodNode.invisibleAnnotations); + addDirectAnnotations(methodNode.visibleAnnotations); + } + + private void addDirectAnnotations(List regularAnnotations) { + if (regularAnnotations == null) { + return; + } + for (AnnotationNode annotation : regularAnnotations) { + builder.addAnnotation(FieldBuilder.parseAnnotation(annotation)); + } + } + private void setReturnType() { //Skip constructors if (methodNode.name.equals("")) { return; } - String returnDesc = methodNode.desc.substring(methodNode.desc.lastIndexOf(")") + 1); - TypeName typeName = FieldBuilder.getFieldType(returnDesc); + TypeName typeName; + if (signature != null) { + typeName = signature.result; + } else { + String returnDesc = methodNode.desc.substring(methodNode.desc.lastIndexOf(")") + 1); + typeName = FieldBuilder.typeFromDesc(returnDesc); + } + builder.returns(typeName); if (typeName != TypeName.VOID && !builder.modifiers.contains(Modifier.ABSTRACT)) { builder.addStatement("throw new RuntimeException()"); @@ -51,125 +164,125 @@ public class MethodBuilder { } private void addParameters() { + // todo fix enum ctors List paramTypes = new ArrayList<>(); - getParams(methodNode.desc, paramTypes); + boolean instanceMethod = !builder.modifiers.contains(Modifier.STATIC); + Set usedParamNames = new HashSet<>(RESERVED_KEYWORDS); + if (signature != null) { + getGenericParams(paramTypes, instanceMethod, usedParamNames); + } else { + getDescParams(paramTypes, instanceMethod, usedParamNames); + } + + List[] visibleParameterAnnotations = methodNode.visibleParameterAnnotations; + List[] invisibleParameterAnnotations = methodNode.invisibleParameterAnnotations; + int index = 0; for (ParamType paramType : paramTypes) { - builder.addParameter(paramType.type, paramType.name, paramType.modifiers); + paramType.fillName(usedParamNames); + ParameterSpec.Builder paramBuilder = ParameterSpec.builder(paramType.type, paramType.name, paramType.modifiers); + if (paramType.comment != null) { + paramBuilder.addJavadoc(paramType.comment + "\n"); + } + addDirectAnnotations(paramBuilder, visibleParameterAnnotations, index); + addDirectAnnotations(paramBuilder, invisibleParameterAnnotations, index); + builder.addParameter(paramBuilder.build()); + index++; } } - //TODO this prob needs a rewrite - private void getParams(String desc, List paramTypes) { - if (desc.isEmpty() || desc.charAt(0) == ')') { - return; + private void getGenericParams(List paramTypes, boolean instance, Set usedParamNames) { + int slot = instance ? 1 : 0; + for (TypeName each : signature.parameters) { + paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), each, usedParamNames, slot)); + slot++; + if (each.equals(TypeName.DOUBLE) || each.equals(TypeName.LONG)) { + slot++; + } } - if (desc.charAt(0) == '(') { - getParams(desc.substring(1), paramTypes); - return; - } - TypeName type = TypeName.VOID; - - int width = 1; - char cha = desc.charAt(0); - - switch (cha) { - case 'B': - type = TypeName.BYTE; - break; - case 'C': - type = TypeName.CHAR; - break; - case 'S': - type = TypeName.SHORT; - break; - case 'Z': - type = TypeName.BOOLEAN; - break; - case 'I': - type = TypeName.INT; - break; - case 'J': - type = TypeName.LONG; - break; - case 'F': - type = TypeName.FLOAT; - break; - case 'D': - type = TypeName.DOUBLE; - break; - case 'L' : - desc = desc.substring(1); - String clazz = desc.substring(0, desc.indexOf(";")); - width = clazz.length() + 1; - type = ClassBuilder.getClassName(clazz); - } - - paramTypes.add(new ParamType(mappings.getParamName(new EntryTriple(classNode.name, methodNode.name, methodNode.desc), paramTypes.size() + 1), type, paramTypes.size())); - getParams(desc.substring(width), paramTypes); } - private class ParamType { - private final String name; - private final TypeName type; - private final Modifier[] modifiers; + private void getDescParams(List paramTypes, boolean instance, Set usedParamNames) { + int slot = instance ? 1 : 0; + final String desc = methodNode.desc; + int index = 0; - public ParamType(String name, TypeName type, int index) { - this.name = name != null ? name : getValidName(type) + "_" + index; - this.type = type; - this.modifiers = new ModifierBuilder(0) - .getModifiers(ModifierBuilder.Type.PARAM); + if (desc.charAt(index) != '(') { + throw invalidMethodDesc(desc, index); } + index++; // consume '(' - private String getValidName(TypeName type) { - String str = type.toString(); - if (str.contains(".")) { - str = str.substring(str.lastIndexOf(".") + 1); - } - str = Character.toLowerCase(str.charAt(0)) + str.substring(1); + while (desc.charAt(index) != ')') { + Map.Entry parsedParam = FieldBuilder.parseType(desc, index); + index = parsedParam.getKey(); + TypeName paramType = parsedParam.getValue(); - if (str.equals("boolean")) { - str = "bool"; + paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), paramType, usedParamNames, slot)); + slot++; + if (paramType.equals(TypeName.DOUBLE) || paramType.equals(TypeName.LONG)) { + slot++; + } + } + /* bruh, we don't care about return type + index++; // consume ')' + Map.Entry parsedReturn = FieldBuilder.parseType(desc, index); + index = parsedReturn.getKey(); + TypeName returnType = parsedReturn.getValue(); + */ + } + + private void addExceptions() { + if (signature != null) { + for (TypeName each : signature.thrown) { + builder.addException(each); + } + return; + } + List exceptions = methodNode.exceptions; + if (exceptions != null) { + for (String internalName : exceptions) { + builder.addException(ClassBuilder.parseInternalName(internalName)); } - return str; } } private void addJavaDoc() { - String javaDoc = mappings.getMethodDoc(new EntryTriple(classNode.name, methodNode.name, methodNode.desc)); + String javaDoc = mappings.getMethodDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc)); if (javaDoc != null) { builder.addJavadoc(javaDoc); } } - private TypeName getFieldType(String desc) { - switch (desc) { - case "B": - return TypeName.BYTE; - case "C": - return TypeName.CHAR; - case "S": - return TypeName.SHORT; - case "Z": - return TypeName.BOOLEAN; - case "I": - return TypeName.INT; - case "J": - return TypeName.LONG; - case "F": - return TypeName.FLOAT; - case "D": - return TypeName.DOUBLE; - } - if (desc.startsWith("[")) { - return ArrayTypeName.of(getFieldType(desc.substring(1))); - } - if (desc.startsWith("L")) { - return ClassBuilder.getClassName(desc.substring(1).substring(0, desc.length() -2)); - } - throw new UnsupportedOperationException("Unknown field type" + desc); - } - public MethodSpec build() { return builder.build(); } + + private class ParamType { + final String comment; + private final TypeName type; + private final Modifier[] modifiers; + private String name; + + public ParamType(Map.Entry nameAndDoc, TypeName type, Set usedNames, int slot) { + this.name = nameAndDoc != null ? nameAndDoc.getKey() : null; + if (this.name != null) { + if (usedNames.contains(this.name)) { + System.err.println(String.format("Overridden parameter name detected in %s %s %s slot %d, resetting", classNode.name, methodNode.name, methodNode.desc, slot)); + this.name = null; + } else { + usedNames.add(this.name); + } + } + this.comment = nameAndDoc == null ? null : nameAndDoc.getValue(); + this.type = type; + this.modifiers = new ModifierBuilder(0) + .getModifiers(ModifierBuilder.Type.PARAM); + } + + private void fillName(Set usedNames) { + if (name != null) { + return; + } + name = reserveValidName(suggestName(type), usedNames); + } + } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java index de616c623e..355cf33695 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java @@ -30,15 +30,17 @@ public class ModifierBuilder { modifiers.add(Modifier.PROTECTED); } - if (java.lang.reflect.Modifier.isAbstract(access)) { + if (java.lang.reflect.Modifier.isAbstract(access) && type != Type.ENUM) { modifiers.add(Modifier.ABSTRACT); - } else if (java.lang.reflect.Modifier.isStatic(access)) { + } + if (java.lang.reflect.Modifier.isStatic(access)) { modifiers.add(Modifier.STATIC); - } else if (type == Type.METHOD) { + } + if (!java.lang.reflect.Modifier.isAbstract(access) && !java.lang.reflect.Modifier.isStatic(access) && type == Type.METHOD) { modifiers.add(Modifier.DEFAULT); } - if (java.lang.reflect.Modifier.isFinal(access)) { + if (java.lang.reflect.Modifier.isFinal(access) && type != Type.ENUM) { modifiers.add(Modifier.FINAL); } if (java.lang.reflect.Modifier.isTransient(access) && type == Type.FIELD) { @@ -62,6 +64,7 @@ public class ModifierBuilder { public enum Type { CLASS, + ENUM, METHOD, FIELD, PARAM diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java new file mode 100644 index 0000000000..6ee6f343fc --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java @@ -0,0 +1,497 @@ +package net.fabricmc.mappingpoet; + +import java.util.AbstractMap; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; + +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeVariableName; +import com.squareup.javapoet.WildcardTypeName; + +public final class Signatures { + + public static ClassSignature parseClassSignature(final String signature) { + // ;B:Ljava/lang/Object>Ljava/lang/Object; etc etc + int index = 0; + char ch; + List generics = null; + if (signature.charAt(0) == '<') { + // parse generic decl + index++; // consume '<' + + // parse type params e.g. + generics = new LinkedList<>(); + while ((ch = signature.charAt(index)) != '>') { + int genericNameStart = index; + if (ch == ':') { + throw errorAt(signature, index); + } + do { + index++; + } while (signature.charAt(index) != ':'); + + String genericName = signature.substring(genericNameStart, index); + + List bounds = new LinkedList<>(); + boolean classBound = true; + while (signature.charAt(index) == ':') { + // parse bounds + index++; // consume ':' + if (classBound && signature.charAt(index) == ':') { + // No class bound, only interface bounds, so '::' + classBound = false; + continue; + } + classBound = false; + Map.Entry bound = parseParameterizedType(signature, index); + index = bound.getKey(); + bounds.add(bound.getValue()); + } + + generics.add(TypeVariableName.get(genericName, bounds.toArray(new TypeName[0]))); + } + + index++; // consume '>' + } + + LinkedList supers = new LinkedList<>(); + while (index < signature.length()) { + Map.Entry bound = parseParameterizedType(signature, index); + index = bound.getKey(); + supers.add(bound.getValue()); + } + + return new ClassSignature(generics, supers.removeFirst(), supers); + } + + public static final class ClassSignature { + // Nullable + public final List generics; + public final TypeName superclass; + public final List superinterfaces; + + ClassSignature(List generics, TypeName superclass, List superinterfaces) { + this.generics = generics; + this.superclass = superclass; + this.superinterfaces = superinterfaces; + } + } + + public static MethodSignature parseMethodSignature(String signature) { + int index = 0; + char ch; + List generics = null; + if (signature.charAt(0) == '<') { + // parse generic decl + index++; // consume '<' + + // parse type params e.g. + generics = new LinkedList<>(); + while ((ch = signature.charAt(index)) != '>') { + int genericNameStart = index; + if (ch == ':') { + throw errorAt(signature, index); + } + do { + index++; + } while (signature.charAt(index) != ':'); + + String genericName = signature.substring(genericNameStart, index); + + List bounds = new LinkedList<>(); + boolean classBound = true; + while (signature.charAt(index) == ':') { + // parse bounds + index++; // consume ':' + if (classBound && signature.charAt(index) == ':') { + // No class bound, only interface bounds, so '::' + classBound = false; + continue; + } + classBound = false; + Map.Entry bound = parseParameterizedType(signature, index); + index = bound.getKey(); + bounds.add(bound.getValue()); + } + + generics.add(TypeVariableName.get(genericName, bounds.toArray(new TypeName[0]))); + } + + index++; // consume '>' + } + + if (signature.charAt(index) != '(') { + throw errorAt(signature, index); + } + index++; // consume '(' + + LinkedList params = new LinkedList<>(); + while (signature.charAt(index) != ')') { + Map.Entry param = parseParameterizedType(signature, index); + index = param.getKey(); + params.add(param.getValue()); + } + + index++; // consume ')' + + TypeName returnType; + if (signature.charAt(index) == 'V') { + returnType = TypeName.VOID; + index++; + } else { + Map.Entry parsedReturnType = parseParameterizedType(signature, index); + index = parsedReturnType.getKey(); + returnType = parsedReturnType.getValue(); + } + + LinkedList thrown = new LinkedList<>(); + while (index < signature.length() && signature.charAt(index) == '^') { + index++; // consume '^' + Map.Entry parsedThrown = parseParameterizedType(signature, index); + index = parsedThrown.getKey(); + thrown.addLast(parsedThrown.getValue()); + } + + return new MethodSignature(generics, params, returnType, thrown); + } + + public static final class MethodSignature { + // Nullable + public final List generics; + public final List parameters; + public final TypeName result; + public final List thrown; + + MethodSignature(List generics, List parameters, TypeName result, List thrown) { + this.generics = generics; + this.parameters = parameters; + this.result = result; + this.thrown = thrown; + } + } + + public static TypeName parseFieldSignature(String signature) { + return parseParameterizedType(signature, 0).getValue(); + } + + public static Map.Entry parseParameterizedType(final String signature, final int startOffset) { + GenericStack stack = new GenericStack(); + + int index = startOffset; + // the loop parses a type and try to quit levels if possible + do { + char ch = signature.charAt(index); + boolean parseExactType = true; + boolean bounded = false; + boolean extendsBound = false; + + switch (ch) { + case '*': { + index++; + parseExactType = false; + stack.addWildcard(); + break; + } + case '+': { + index++; + bounded = true; + extendsBound = true; + break; + } + case '-': { + index++; + bounded = true; + extendsBound = false; + break; + } + default: { + // do nothing + } + } + + if (parseExactType) { + int arrayLevel = 0; + while ((ch = signature.charAt(index)) == '[') { + index++; + arrayLevel++; + } + + index++; // whatever the prefix is it's consumed + switch (ch) { + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': { + // primitives + stack.add(getPrimitive(ch), arrayLevel, bounded, extendsBound); + break; + } + case 'T': { + // "TE;" for + int nameStart = index; + while (signature.charAt(index) != ';') { + index++; + } + String typeVarName = signature.substring(nameStart, index); + stack.add(TypeVariableName.get(typeVarName), arrayLevel, bounded, extendsBound); + index++; // read ending ";" + break; + } + case 'L': { + // Lcom/example/Outer.Inner; + // Lcom/example/Outer$Inner; + // dot only appears after ">"! + int nameStart = index; + ClassName currentClass = null; + int nextSimpleNamePrev = -1; + do { + ch = signature.charAt(index); + + if (ch == '/') { + if (currentClass != null) { + throw errorAt(signature, index); + } + nextSimpleNamePrev = index; + } + + if (ch == '$' || ch == '<' || ch == ';') { + if (currentClass == null) { + String packageName = nextSimpleNamePrev == -1 ? "" : signature.substring(nameStart, nextSimpleNamePrev).replace('/', '.'); + String simpleName = signature.substring(nextSimpleNamePrev + 1, index); + currentClass = ClassName.get(packageName, simpleName); + } else { + String simpleName = signature.substring(nextSimpleNamePrev + 1, index); + currentClass = currentClass.nestedClass(simpleName); + } + nextSimpleNamePrev = index; + } + + index++; + } while (ch != '<' && ch != ';'); + + assert currentClass != null; + if (ch == ';') { + stack.add(currentClass, arrayLevel, bounded, extendsBound); + } + + if (ch == '<') { + stack.push(Frame.ofClass(currentClass), arrayLevel, bounded, extendsBound); + } + break; + } + default: { + throw errorAt(signature, index); + } + } + } + + // quit generics + quitLoop: + while (stack.canQuit() && signature.charAt(index) == '>') { + // pop + stack.popFrame(); + index++; + + // followups like .B in A.B + if ((ch = signature.charAt(index)) != ';') { + if (ch != '.') { + throw errorAt(signature, index); + } + index++; + int innerNameStart = index; + final int checkIndex = index; + stack.checkHead(head -> { + if (!(head instanceof ParameterizedTypeName)) { + throw errorAt(signature, checkIndex); + } + }); + + while (true) { + ch = signature.charAt(index); + if (ch == '.' || ch == ';' || ch == '<') { + String simpleName = signature.substring(innerNameStart, index); + if (ch == '.' || ch == ';') { + stack.tweakLast(name -> ((ParameterizedTypeName) name).nestedClass(simpleName)); + if (ch == ';') { + index++; + break; + } + } else { + stack.push(Frame.ofGenericInnerClass((ParameterizedTypeName) stack.deque.getLast().typeNames.removeLast(), simpleName)); + index++; + break quitLoop; + } + } + + index++; + } + } else { + index++; + } + + } + + } while (stack.canQuit()); + + assert stack.deque.size() == 1; + assert stack.deque.getLast().typeNames.size() == 1; + return new AbstractMap.SimpleImmutableEntry<>(index, stack.collectFrame()); + } + + private static IllegalArgumentException errorAt(String signature, int index) { + return new IllegalArgumentException(String.format("Signature format error at %d for \"%s\"", index, signature)); + } + + public static TypeName wrap(TypeName component, int level, boolean bounded, boolean extendsBound) { + TypeName ret = component; + for (int i = 0; i < level; i++) { + ret = ArrayTypeName.of(ret); + } + return bounded ? extendsBound ? WildcardTypeName.subtypeOf(ret) : WildcardTypeName.supertypeOf(ret) : ret; + } + + public static TypeName getPrimitive(char c) { + switch (c) { + case 'B': + return TypeName.BYTE; + case 'C': + return TypeName.CHAR; + case 'D': + return TypeName.DOUBLE; + case 'F': + return TypeName.FLOAT; + case 'I': + return TypeName.INT; + case 'J': + return TypeName.LONG; + case 'S': + return TypeName.SHORT; + case 'V': + return TypeName.VOID; + case 'Z': + return TypeName.BOOLEAN; + } + throw new IllegalArgumentException("Invalid primitive " + c); + } + + @FunctionalInterface + interface Frame { + static Frame ofClass(ClassName className) { + return parameters -> ParameterizedTypeName.get(className, parameters.toArray(new TypeName[0])); + } + + static Frame ofGenericInnerClass(ParameterizedTypeName outerClass, String innerName) { + return parameters -> outerClass.nestedClass(innerName, parameters); + } + + static Frame collecting() { + return parameters -> { + if (parameters.size() != 1) { + throw new IllegalStateException(); + } + return parameters.get(0); + }; + } + + T acceptParameters(List parameters); + } + + @FunctionalInterface + interface HeadChecker { + void check(TypeName typeName) throws E; + } + + static final class GenericStack { + private final Deque deque = new LinkedList<>(); + + GenericStack() { + deque.addLast(new Element(Frame.collecting())); + } + + public boolean canQuit() { + return deque.size() > 1; + } + + public void push(Frame frame) { + deque.addLast(new Element(frame)); + } + + public void push(Frame frame, int arrayLevel, boolean bounded, boolean extendsBound) { + deque.getLast().pushAttributes(arrayLevel, bounded, extendsBound); + push(frame); + } + + public void addWildcard() { + add(WildcardTypeName.subtypeOf(ClassName.OBJECT), 0, false, false); + } + + public void add(TypeName typeName, int arrayLevel, boolean bounded, boolean extendsBound) { + deque.getLast().add(typeName, arrayLevel, bounded, extendsBound); + } + + public void tweakLast(UnaryOperator modifier) { + LinkedList typeNames = deque.getLast().typeNames; + typeNames.addLast(modifier.apply(typeNames.removeLast())); + } + + public TypeName collectFrame() { + return deque.removeLast().pop(); + } + + public void popFrame() { + TypeName name = collectFrame(); + deque.getLast().typeNames.addLast(name); + } + + public void checkHead(HeadChecker checker) throws E { + checker.check(deque.getLast().typeNames.getLast()); + } + + private static final class Element { + final LinkedList typeNames; + final Frame frame; + int arrayLevel = 0; + boolean bounded = false; + boolean extendsBound = false; + + Element(Frame frame) { + this.typeNames = new LinkedList<>(); + this.frame = frame; + } + + void add(TypeName typeName, int arrayLevel, boolean bounded, boolean extendsBound) { + pushAttributes(arrayLevel, bounded, extendsBound); + typeNames.addLast(typeName); + } + + private void updateLast() { + if (!typeNames.isEmpty()) { + TypeName lastTypeName = typeNames.removeLast(); + typeNames.addLast(Signatures.wrap(lastTypeName, this.arrayLevel, this.bounded, this.extendsBound)); + } + } + + void pushAttributes(int arrayLevel, boolean bounded, boolean extendsBound) { + updateLast(); + this.arrayLevel = arrayLevel; + this.bounded = bounded; + this.extendsBound = extendsBound; + } + + TypeName pop() { + updateLast(); + return frame.acceptParameters(typeNames); + } + } + } +} From b79be3aea4edbf53c64422031a1106a3ce405a51 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Tue, 7 Apr 2020 19:06:53 +0100 Subject: [PATCH 04/30] License, gradle, jenkins, maven --- .../filament/mappingpoet/ClassBuilder.java | 16 ++++++++++++++++ .../filament/mappingpoet/FieldBuilder.java | 16 ++++++++++++++++ .../net/fabricmc/filament/mappingpoet/Main.java | 16 ++++++++++++++++ .../filament/mappingpoet/MappingsStore.java | 16 ++++++++++++++++ .../filament/mappingpoet/MethodBuilder.java | 16 ++++++++++++++++ .../filament/mappingpoet/ModifierBuilder.java | 16 ++++++++++++++++ .../filament/mappingpoet/Signatures.java | 16 ++++++++++++++++ 7 files changed, 112 insertions(+) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 926bb5f900..1792605cec 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet; import java.lang.reflect.Modifier; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java index 159e5718bd..a5596ab9cb 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet; import java.util.AbstractMap; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 1db207edfb..ddcd158b77 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet; import com.squareup.javapoet.JavaFile; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java index 2d8e2f929c..7153aa1bd4 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet; import net.fabricmc.mapping.tree.ClassDef; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index 47dce2abef..fa256de348 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet; import java.util.ArrayList; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java index 355cf33695..5e87731421 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet; import javax.lang.model.element.Modifier; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java index 6ee6f343fc..2cce805711 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet; import java.util.AbstractMap; From 50625c80a7103f618bdfaaf52156e26461e5b7ab Mon Sep 17 00:00:00 2001 From: liach <7806504+liach@users.noreply.github.com> Date: Sat, 18 Apr 2020 10:20:46 -0500 Subject: [PATCH 05/30] Skip synthetic params by guess (no attribute available unfortunately) (#4) * Skip synthetic params by guess (no attribute available unfortunately) Signed-off-by: liach * Fix code style issue Signed-off-by: liach Co-authored-by: liach --- .../filament/mappingpoet/ClassBuilder.java | 18 ++++++++-- .../fabricmc/filament/mappingpoet/Main.java | 20 ++++++----- .../filament/mappingpoet/MethodBuilder.java | 34 ++++++++----------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 1792605cec..3dbf661e3e 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -42,6 +42,7 @@ public class ClassBuilder { private Signatures.ClassSignature signature; private boolean enumClass; + private boolean instanceInner = false; public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function> superGetter) { this.mappings = mappings; @@ -50,9 +51,12 @@ public class ClassBuilder { this.builder = setupBuilder(); addInterfaces(); addDirectAnnotations(); + addJavaDoc(); + } + + public void addMembers() { addMethods(); addFields(); - addJavaDoc(); } public static ClassName parseInternalName(String internalName) { @@ -150,6 +154,7 @@ public class ClassBuilder { if (method.name.equals("")) { continue; } + int formalParamStartIndex = 0; if (enumClass) { // Skip enum sugar methods if (method.name.equals("values") && method.desc.equals("()[L" + classNode.name + ";")) { @@ -158,8 +163,16 @@ public class ClassBuilder { if (method.name.equals("valueOf") && method.desc.equals("(Ljava/lang/String;)L" + classNode.name + ";")) { continue; } + if (method.name.equals("")) { + formalParamStartIndex = 2; // 0 String 1 int + } } - builder.addMethod(new MethodBuilder(mappings, classNode, method, superGetter).build()); + if (instanceInner) { + if (method.name.equals("")) { + formalParamStartIndex = 1; // 0 this$0 + } + } + builder.addMethod(new MethodBuilder(mappings, classNode, method, superGetter, formalParamStartIndex).build()); } } @@ -202,6 +215,7 @@ public class ClassBuilder { classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); } else { classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access).getModifiers(classBuilder.enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS)); + if (!Modifier.isStatic(innerClassNode.access)) classBuilder.instanceInner = true; } innerClasses.add(classBuilder); } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index ddcd158b77..98d6f690f6 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -16,11 +16,6 @@ package net.fabricmc.mappingpoet; -import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.TypeSpec; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.tree.ClassNode; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -36,11 +31,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -import java.util.function.Consumer; import java.util.function.Function; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.TypeSpec; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; + public class Main { public static void main(String[] args) { @@ -111,7 +110,7 @@ public class Main { continue; } - try(InputStream is = jarFile.getInputStream(entry)) { + try (InputStream is = jarFile.getInputStream(entry)) { ClassReader reader = new ClassReader(is); ClassNode classNode = new ClassNode(); reader.accept(classNode, 0); @@ -139,7 +138,7 @@ public class Main { Function> superGetter = k -> supers.getOrDefault(k, Collections.emptyList()); classes.forEach(node -> classNodeConsumer.accept(superGetter, node)); } - + private static boolean isDigit(char ch) { return ch >= '0' && ch <= '9'; } @@ -151,8 +150,10 @@ public class Main { int lastSearch = name.length(); while (lastSearch != -1) { lastSearch = name.lastIndexOf('$', lastSearch - 1); - if (isDigit(name.charAt(lastSearch + 1))) // names starting with digit is illegal java + // names starting with digit is illegal java + if (isDigit(name.charAt(lastSearch + 1))) { return; + } } } @@ -166,6 +167,7 @@ public class Main { existingClasses.get(parentClass).addInnerClass(classBuilder); } + classBuilder.addMembers(); existingClasses.put(name, classBuilder); } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index fa256de348..7983066569 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -50,14 +51,16 @@ public class MethodBuilder { private final MethodNode methodNode; private final MethodSpec.Builder builder; private final Function> superGetter; + private final int formalParamStartIndex; private Signatures.MethodSignature signature; - public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode, Function> superGetter) { + public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode, Function> superGetter, int formalParamStartIndex) { this.mappings = mappings; this.classNode = classNode; this.methodNode = methodNode; this.superGetter = superGetter; + this.formalParamStartIndex = formalParamStartIndex; this.builder = createBuilder(); addJavaDoc(); addDirectAnnotations(); @@ -184,11 +187,7 @@ public class MethodBuilder { List paramTypes = new ArrayList<>(); boolean instanceMethod = !builder.modifiers.contains(Modifier.STATIC); Set usedParamNames = new HashSet<>(RESERVED_KEYWORDS); - if (signature != null) { - getGenericParams(paramTypes, instanceMethod, usedParamNames); - } else { - getDescParams(paramTypes, instanceMethod, usedParamNames); - } + getParams(paramTypes, instanceMethod, usedParamNames); List[] visibleParameterAnnotations = methodNode.visibleParameterAnnotations; List[] invisibleParameterAnnotations = methodNode.invisibleParameterAnnotations; @@ -206,20 +205,10 @@ public class MethodBuilder { } } - private void getGenericParams(List paramTypes, boolean instance, Set usedParamNames) { - int slot = instance ? 1 : 0; - for (TypeName each : signature.parameters) { - paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), each, usedParamNames, slot)); - slot++; - if (each.equals(TypeName.DOUBLE) || each.equals(TypeName.LONG)) { - slot++; - } - } - } - - private void getDescParams(List paramTypes, boolean instance, Set usedParamNames) { + private void getParams(List paramTypes, boolean instance, Set usedParamNames) { int slot = instance ? 1 : 0; final String desc = methodNode.desc; + int paramIndex = 0; int index = 0; if (desc.charAt(index) != '(') { @@ -227,16 +216,23 @@ public class MethodBuilder { } index++; // consume '(' + Iterator signatureParamIterator = signature == null ? Collections.emptyIterator() : signature.parameters.iterator(); while (desc.charAt(index) != ')') { Map.Entry parsedParam = FieldBuilder.parseType(desc, index); index = parsedParam.getKey(); TypeName paramType = parsedParam.getValue(); - paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), paramType, usedParamNames, slot)); + if (paramIndex >= formalParamStartIndex) { // skip guessed synthetic/implicit params + if (signatureParamIterator.hasNext()) { + paramType = signatureParamIterator.next(); + } + paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), paramType, usedParamNames, slot)); + } slot++; if (paramType.equals(TypeName.DOUBLE) || paramType.equals(TypeName.LONG)) { slot++; } + paramIndex++; } /* bruh, we don't care about return type index++; // consume ')' From cc5d39cf295f070637ce08a239d0c8939885509a Mon Sep 17 00:00:00 2001 From: liach <7806504+liach@users.noreply.github.com> Date: Wed, 23 Dec 2020 11:23:44 -0600 Subject: [PATCH 06/30] Add type annotation support and mapping info in jd gen (#6) * Supports type annotations Signed-off-by: liach Minor stuff Guess receivers work Signed-off-by: liach Migrate to new entry triple Signed-off-by: liach Seems it works at least it handles test files' inner classes Stage Now annotations include their defaults in generated files. works good More tests. Seems javadoc tool is more broken than this project Signed-off-by: liach * Add a table for each element on obfuscation information * bump version Co-authored-by: liach --- .../filament/mappingpoet/ClassBuilder.java | 143 +++++++++--- .../filament/mappingpoet/FieldBuilder.java | 156 +++++++++++-- .../fabricmc/filament/mappingpoet/Main.java | 119 ++++++++-- .../filament/mappingpoet/MappingsStore.java | 117 +++++++--- .../filament/mappingpoet/MethodBuilder.java | 88 +++++-- .../filament/mappingpoet/ModifierBuilder.java | 9 +- .../filament/mappingpoet/Signatures.java | 36 +-- .../mappingpoet/jd/MappingTaglet.java | 125 ++++++++++ .../signature/AnnotationAwareDescriptors.java | 151 ++++++++++++ .../signature/AnnotationAwareSignatures.java | 50 ++++ .../mappingpoet/signature/ClassSignature.java | 35 +++ .../signature/ClassStaticContext.java | 37 +++ .../signature/MethodSignature.java | 36 +++ .../PoetClassMethodSignatureVisitor.java | 197 ++++++++++++++++ .../signature/PoetTypeSignatureWriter.java | 203 ++++++++++++++++ .../signature/TypeAnnotationBank.java | 72 ++++++ .../signature/TypeAnnotationMapping.java | 30 +++ .../signature/TypeAnnotationStorage.java | 218 ++++++++++++++++++ 18 files changed, 1684 insertions(+), 138 deletions(-) create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 3dbf661e3e..a4c13ff3e1 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -16,21 +16,33 @@ package net.fabricmc.mappingpoet; +import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation; + import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.Function; +import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.TypeVariableName; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.TypeReference; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InnerClassNode; import org.objectweb.asm.tree.MethodNode; +import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; +import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; +import net.fabricmc.mappingpoet.signature.ClassSignature; +import net.fabricmc.mappingpoet.signature.ClassStaticContext; +import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; +import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; + public class ClassBuilder { private final MappingsStore mappings; @@ -39,24 +51,29 @@ public class ClassBuilder { private final TypeSpec.Builder builder; private final List innerClasses = new ArrayList<>(); private final Function> superGetter; + private final ClassStaticContext context; - private Signatures.ClassSignature signature; + private final ClassSignature signature; // not really signature + private final TypeAnnotationMapping typeAnnotations; + private boolean annotationClass; private boolean enumClass; private boolean instanceInner = false; + // only nonnull if any class in the inner class chain creates a generic decl + // omits L and ; + private String receiverSignature; - public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function> superGetter) { + public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function> superGetter, ClassStaticContext context) { this.mappings = mappings; this.classNode = classNode; this.superGetter = superGetter; + this.context = context; + this.typeAnnotations = setupAnnotations(); + this.signature = setupSignature(); this.builder = setupBuilder(); - addInterfaces(); - addDirectAnnotations(); - addJavaDoc(); - } - public void addMembers() { - addMethods(); - addFields(); + addInterfaces(); + addAnnotations(); + addJavaDoc(); } public static ClassName parseInternalName(String internalName) { @@ -96,23 +113,52 @@ public class ClassBuilder { return currentClassName; } + private TypeAnnotationMapping setupAnnotations() { + return TypeAnnotationStorage.builder() + .add(classNode.invisibleTypeAnnotations) + .add(classNode.visibleTypeAnnotations) + .build(); + } + + public void addMembers() { + addMethods(); + addFields(); + } + + private ClassSignature setupSignature() { + return classNode.signature == null ? + AnnotationAwareDescriptors.parse(classNode.superName, classNode.interfaces, typeAnnotations, context) : + AnnotationAwareSignatures.parseClassSignature(classNode.signature, typeAnnotations, context); + } + private TypeSpec.Builder setupBuilder() { - if (classNode.signature != null) { - signature = Signatures.parseClassSignature(classNode.signature); - } TypeSpec.Builder builder; + ClassName name = parseInternalName(classNode.name); // no type anno here if (Modifier.isInterface(classNode.access)) { - builder = TypeSpec.interfaceBuilder(parseInternalName(classNode.name)); + if (classNode.interfaces.size() == 1 && classNode.interfaces.get(0).equals("java/lang/annotation/Annotation")) { + builder = TypeSpec.annotationBuilder(name); + this.annotationClass = true; + } else { + builder = TypeSpec.interfaceBuilder(name); + } } else if (classNode.superName.equals("java/lang/Enum")) { enumClass = true; - builder = TypeSpec.enumBuilder(parseInternalName(classNode.name)); + builder = TypeSpec.enumBuilder(name); } else { - builder = TypeSpec.classBuilder(parseInternalName(classNode.name)) - .superclass(signature == null ? parseInternalName(classNode.superName) : signature.superclass); + builder = TypeSpec.classBuilder(name) + .superclass(signature.superclass); } - if (signature != null && signature.generics != null) { + if (!signature.generics.isEmpty()) { builder.addTypeVariables(signature.generics); + StringBuilder sb = new StringBuilder(); + sb.append(classNode.name); + sb.append("<"); + for (TypeVariableName each : signature.generics) { + sb.append("T").append(each.name).append(";"); + } + sb.append(">"); + receiverSignature = sb.toString(); } return builder @@ -120,18 +166,22 @@ public class ClassBuilder { } private void addInterfaces() { + if (annotationClass) { + return; + } if (signature != null) { builder.addSuperinterfaces(signature.superinterfaces); return; } - if (classNode.interfaces == null) return; + if (classNode.interfaces.isEmpty()) return; for (String iFace : classNode.interfaces) { builder.addSuperinterface(parseInternalName(iFace)); } } - private void addDirectAnnotations() { + private void addAnnotations() { + // type anno already done through class sig addDirectAnnotations(classNode.invisibleAnnotations); addDirectAnnotations(classNode.visibleAnnotations); } @@ -141,7 +191,7 @@ public class ClassBuilder { return; } for (AnnotationNode annotation : regularAnnotations) { - builder.addAnnotation(FieldBuilder.parseAnnotation(annotation)); + builder.addAnnotation(parseAnnotation(annotation)); } } @@ -172,7 +222,7 @@ public class ClassBuilder { formalParamStartIndex = 1; // 0 this$0 } } - builder.addMethod(new MethodBuilder(mappings, classNode, method, superGetter, formalParamStartIndex).build()); + builder.addMethod(new MethodBuilder(mappings, classNode, method, superGetter, context, receiverSignature, formalParamStartIndex).build()); } } @@ -183,20 +233,40 @@ public class ClassBuilder { continue; // hide synthetic stuff } if ((field.access & Opcodes.ACC_ENUM) == 0) { - builder.addField(new FieldBuilder(mappings, classNode, field).build()); + builder.addField(new FieldBuilder(mappings, classNode, field, context).build()); } else { TypeSpec.Builder enumBuilder = TypeSpec.anonymousClassBuilder(""); + // jd FieldBuilder.addFieldJavaDoc(enumBuilder, mappings, classNode, field); + + // annotations + addDirectAnnotations(enumBuilder, field.invisibleAnnotations); + addDirectAnnotations(enumBuilder, field.visibleAnnotations); + List annotations = TypeAnnotationStorage.builder() + .add(field.invisibleTypeAnnotations) + .add(field.visibleTypeAnnotations) + .build().getBank(TypeReference.newTypeReference(TypeReference.FIELD)) + .getCurrentAnnotations(); + if (!annotations.isEmpty()) { + enumBuilder.addAnnotations(annotations); // no custom paths for annotations rip + } + builder.addEnumConstant(field.name, enumBuilder.build()); } } } - private void addJavaDoc() { - String javadoc = mappings.getClassDoc(classNode.name); - if (javadoc != null) { - builder.addJavadoc(javadoc); + private void addDirectAnnotations(TypeSpec.Builder builder, List regularAnnotations) { + if (regularAnnotations == null) { + return; } + for (AnnotationNode annotation : regularAnnotations) { + builder.addAnnotation(parseAnnotation(annotation)); + } + } + + private void addJavaDoc() { + mappings.addClassDoc(builder::addJavadoc, classNode.name); } public void addInnerClass(ClassBuilder classBuilder) { @@ -215,7 +285,26 @@ public class ClassBuilder { classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); } else { classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access).getModifiers(classBuilder.enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS)); - if (!Modifier.isStatic(innerClassNode.access)) classBuilder.instanceInner = true; + if (!Modifier.isStatic(innerClassNode.access)) { + classBuilder.instanceInner = true; + } + // consider emit warning if this.instanceInner is true when classBuilder.instanceInner is false + + if (this.receiverSignature != null && classBuilder.instanceInner) { + StringBuilder sb = new StringBuilder(); + sb.append(this.receiverSignature).append("."); // like O. for O + sb.append(innerClassNode.innerName); // append simple name + + List innerClassGenerics = classBuilder.signature.generics; + if (!innerClassGenerics.isEmpty()) { + sb.append("<"); + for (TypeVariableName each : innerClassGenerics) { + sb.append("T").append(each.name).append(";"); + } + sb.append(">"); + } + classBuilder.receiverSignature = sb.toString(); + } } innerClasses.add(classBuilder); } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java index a5596ab9cb..3488138d5a 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -17,6 +17,8 @@ package net.fabricmc.mappingpoet; import java.util.AbstractMap; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -30,32 +32,44 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import org.objectweb.asm.TypePath; +import org.objectweb.asm.TypeReference; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; -import net.fabricmc.mappings.EntryTriple; +import net.fabricmc.mapping.util.EntryTriple; +import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; +import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; +import net.fabricmc.mappingpoet.signature.ClassStaticContext; +import net.fabricmc.mappingpoet.signature.TypeAnnotationBank; +import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; +import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; public class FieldBuilder { private final MappingsStore mappings; private final ClassNode classNode; private final FieldNode fieldNode; private final FieldSpec.Builder builder; + private final TypeAnnotationMapping annotations; + private final ClassStaticContext context; - public FieldBuilder(MappingsStore mappings, ClassNode classNode, FieldNode fieldNode) { + public FieldBuilder(MappingsStore mappings, ClassNode classNode, FieldNode fieldNode, ClassStaticContext context) { this.mappings = mappings; this.classNode = classNode; this.fieldNode = fieldNode; + this.context = context; + this.annotations = TypeAnnotationStorage.builder() + .add(fieldNode.invisibleTypeAnnotations) + .add(fieldNode.visibleTypeAnnotations) + .build(); this.builder = createBuilder(); addDirectAnnotations(); addJavaDoc(); } static void addFieldJavaDoc(TypeSpec.Builder enumBuilder, MappingsStore mappings, ClassNode classNode, FieldNode fieldNode) { - String javaDoc = mappings.getFieldDoc(new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); - if (javaDoc != null) { - enumBuilder.addJavadoc(javaDoc); - } + mappings.addFieldDoc(enumBuilder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); } public static AnnotationSpec parseAnnotation(AnnotationNode annotation) { @@ -75,7 +89,7 @@ public class FieldBuilder { return builder.build(); } - private static CodeBlock codeFromAnnoValue(Object value) { + public static CodeBlock codeFromAnnoValue(Object value) { // BCDFIJSZ; String; String[] (for enum); asm type; anno node; list of any prev stuff (cannot nest) if (value instanceof List) { return ((List) value).stream().map(FieldBuilder::codeFromAnnoValue).collect(CodeBlock.joining(",", "{", "}")); @@ -208,6 +222,125 @@ public class FieldBuilder { return new AbstractMap.SimpleImmutableEntry<>(index, current); } + public static Map.Entry parseAnnotatedType(final String desc, final int start, TypeAnnotationBank annotations, ClassStaticContext context) { + int index = start; + Deque> arrayAnnos = new ArrayDeque<>(); + while (desc.charAt(index) == '[') { + arrayAnnos.push(annotations.getCurrentAnnotations()); + annotations = annotations.advance(TypePath.ARRAY_ELEMENT, 0); + index++; + } + + TypeName current; + switch (desc.charAt(index)) { + case 'B': { + current = TypeName.BYTE; + index++; + break; + } + case 'C': { + current = TypeName.CHAR; + index++; + break; + } + case 'D': { + current = TypeName.DOUBLE; + index++; + break; + } + case 'F': { + current = TypeName.FLOAT; + index++; + break; + } + case 'I': { + current = TypeName.INT; + index++; + break; + } + case 'J': { + current = TypeName.LONG; + index++; + break; + } + case 'S': { + current = TypeName.SHORT; + index++; + break; + } + case 'Z': { + current = TypeName.BOOLEAN; + index++; + break; + } + case 'V': { + current = TypeName.VOID; + index++; + break; + } + case 'L': { + int classNameSeparator = index; + index++; + int nameStart = index; + ClassName currentClassName = null; + boolean instanceInner = false; + + char ch; + do { + ch = desc.charAt(index); + + if (ch == '$' || ch == ';') { + // collect class name + if (currentClassName == null) { + String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : ""; + String simpleName = desc.substring(classNameSeparator + 1, index); + currentClassName = ClassName.get(packageName, simpleName); + } else { + String simpleName = desc.substring(classNameSeparator + 1, index); + + if (!instanceInner && context.isInstanceInner(desc.substring(nameStart, index))) { + instanceInner = true; + } + + currentClassName = currentClassName.nestedClass(simpleName); + + if (instanceInner) { + currentClassName = AnnotationAwareDescriptors.annotate(currentClassName, annotations); + annotations = annotations.advance(TypePath.INNER_TYPE, 0); + } + } + } + + if (ch == '/' || ch == '$') { + // Start of simple name + classNameSeparator = index; + } + + index++; + } while (ch != ';'); + + if (currentClassName == null) { + throw invalidDesc(desc, index); + } + + current = currentClassName; + break; + } + default: + throw invalidDesc(desc, index); + } + + while (!arrayAnnos.isEmpty()) { + current = ArrayTypeName.of(current); + List currentAnnos = arrayAnnos.pop(); + if (!currentAnnos.isEmpty()) { + current = current.annotated(currentAnnos); + } + } + + return new AbstractMap.SimpleImmutableEntry<>(index, current); + } + private static IllegalArgumentException invalidDesc(String desc, int index) { return new IllegalArgumentException(String.format("Invalid descriptor at index %d for \"%s\"", index, desc)); } @@ -285,10 +418,7 @@ public class FieldBuilder { } private void addJavaDoc() { - String javaDoc = mappings.getFieldDoc(new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); - if (javaDoc != null) { - builder.addJavadoc(javaDoc); - } + mappings.addFieldDoc(builder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); } private void addDirectAnnotations() { @@ -307,9 +437,9 @@ public class FieldBuilder { private TypeName calculateType() { if (fieldNode.signature != null) { - return Signatures.parseFieldSignature(fieldNode.signature); + return AnnotationAwareSignatures.parseFieldSignature(fieldNode.signature, annotations, context); } - return typeFromDesc(fieldNode.desc); + return parseAnnotatedType(fieldNode.desc, 0, annotations.getBank(TypeReference.newTypeReference(TypeReference.FIELD)), context).getValue(); } public FieldSpec build() { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 98d6f690f6..193542800d 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -19,9 +19,14 @@ package net.fabricmc.mappingpoet; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; +import java.lang.reflect.Modifier; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -30,26 +35,31 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.jar.JarEntry; import java.util.jar.JarFile; import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.TypeSpec; import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InnerClassNode; + +import net.fabricmc.mappingpoet.signature.ClassStaticContext; public class Main { public static void main(String[] args) { - if (args.length != 3) { - System.out.println(" "); + if (args.length != 3 && args.length != 4) { + System.out.println(" []"); return; } Path mappings = Paths.get(args[0]); Path inputJar = Paths.get(args[1]); Path outputDirectory = Paths.get(args[2]); + Path librariesDir = args.length < 4 ? null : Paths.get(args[3]); try { if (Files.exists(outputDirectory)) { @@ -74,13 +84,13 @@ public class Main { return; } - generate(mappings, inputJar, outputDirectory); + generate(mappings, inputJar, outputDirectory, librariesDir); } - public static void generate(Path mappings, Path inputJar, Path outputDirectory) { + public static void generate(Path mappings, Path inputJar, Path outputDirectory, Path librariesDir) { final MappingsStore mapping = new MappingsStore(mappings); Map classes = new HashMap<>(); - forEachClass(inputJar, (superGetter, classNode) -> writeClass(mapping, classNode, classes, superGetter)); + forEachClass(inputJar, (superGetter, classNode, context) -> writeClass(mapping, classNode, classes, superGetter, context), librariesDir); classes.values().stream() .filter(classBuilder -> !classBuilder.getClassName().contains("$")) @@ -97,9 +107,16 @@ public class Main { } - private static void forEachClass(Path jar, BiConsumer>, ClassNode> classNodeConsumer) { + private static void forEachClass(Path jar, ClassNodeConsumer classNodeConsumer, Path librariesDir) { List classes = new ArrayList<>(); Map> supers = new HashMap<>(); + + Map instanceInnerClasses = new ConcurrentHashMap<>(); + + if (librariesDir != null) { + scanInnerClasses(instanceInnerClasses, librariesDir); + } + try (final JarFile jarFile = new JarFile(jar.toFile())) { Enumeration entryEnumerator = jarFile.entries(); @@ -113,7 +130,7 @@ public class Main { try (InputStream is = jarFile.getInputStream(entry)) { ClassReader reader = new ClassReader(is); ClassNode classNode = new ClassNode(); - reader.accept(classNode, 0); + reader.accept(classNode, ClassReader.SKIP_CODE); List superNames = new ArrayList<>(); if (classNode.superName != null && !classNode.superName.equals("java/lang/Object")) { superNames.add(classNode.superName); @@ -125,6 +142,12 @@ public class Main { supers.put(classNode.name, superNames); } + if (classNode.innerClasses != null) { + for (InnerClassNode e : classNode.innerClasses) { + instanceInnerClasses.put(e.name, !Modifier.isStatic(e.access)); + } + } + classes.add(classNode); } } @@ -135,15 +158,66 @@ public class Main { //Sort all the classes making sure that inner classes come after the parent classes classes.sort(Comparator.comparing(o -> o.name)); + ClassStaticContext innerClassContext = new InnerClassStats(instanceInnerClasses); Function> superGetter = k -> supers.getOrDefault(k, Collections.emptyList()); - classes.forEach(node -> classNodeConsumer.accept(superGetter, node)); + classes.forEach(node -> classNodeConsumer.accept(superGetter, node, innerClassContext)); + } + + private static void scanInnerClasses(Map instanceInnerClasses, Path librariesDir) { + try { + Files.walkFileTree(librariesDir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (!file.getFileName().toString().endsWith(".jar")) { + return FileVisitResult.CONTINUE; + } + + try (final JarFile jarFile = new JarFile(file.toFile())) { + Enumeration entryEnumerator = jarFile.entries(); + + while (entryEnumerator.hasMoreElements()) { + JarEntry entry = entryEnumerator.nextElement(); + + if (entry.isDirectory() || !entry.getName().endsWith(".class")) { + continue; + } + + try (InputStream is = jarFile.getInputStream(entry)) { + ClassReader reader = new ClassReader(is); + reader.accept(new ClassVisitor(Opcodes.ASM8) { + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + instanceInnerClasses.put(name, !Modifier.isStatic(access)); + } + }, ClassReader.SKIP_CODE); + } + } + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + private static boolean isInstanceInnerOnClasspath(String internalName) { + String javaBinary = internalName.replace('/', '.'); + + try { + Class c = Class.forName(javaBinary, false, Main.class.getClassLoader()); + return !Modifier.isStatic(c.getModifiers()) && c.getDeclaringClass() != null; + } catch (Throwable ex) { + return false; + } } private static boolean isDigit(char ch) { return ch >= '0' && ch <= '9'; } - private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses, Function> superGetter) { + private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses, Function> superGetter, ClassStaticContext context) { String name = classNode.name; { //Block anonymous class and their nested classes @@ -157,7 +231,7 @@ public class Main { } } - ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter); + ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter, context); if (name.contains("$")) { String parentClass = name.substring(0, name.lastIndexOf("$")); @@ -172,10 +246,25 @@ public class Main { } - private static TypeSpec.Builder createTypeSpecBuilder(ClassNode classNode) { - return TypeSpec.classBuilder(classNode.name) - .addModifiers(); + @FunctionalInterface + private interface ClassNodeConsumer { + void accept(Function> superGetter, ClassNode node, ClassStaticContext staticContext); } + private static final class InnerClassStats implements ClassStaticContext { + final Map instanceInnerClasses; + InnerClassStats(Map instanceInnerClasses) { + this.instanceInnerClasses = instanceInnerClasses; + } + + @Override + public boolean isInstanceInner(String internalName) { + if (internalName.indexOf('$') == -1) { + return false; // heuristics + } + + return instanceInnerClasses.computeIfAbsent(internalName, Main::isInstanceInnerOnClasspath); + } + } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java index 7153aa1bd4..40a61ac06c 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -16,13 +16,6 @@ package net.fabricmc.mappingpoet; -import net.fabricmc.mapping.tree.ClassDef; -import net.fabricmc.mapping.tree.FieldDef; -import net.fabricmc.mapping.tree.MethodDef; -import net.fabricmc.mapping.tree.TinyMappingFactory; -import net.fabricmc.mapping.tree.TinyTree; -import net.fabricmc.mappings.EntryTriple; - import java.io.BufferedReader; import java.io.IOException; import java.nio.file.Files; @@ -30,20 +23,33 @@ import java.nio.file.Path; import java.util.AbstractMap; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.BiConsumer; import java.util.function.Function; +import net.fabricmc.mapping.tree.ClassDef; +import net.fabricmc.mapping.tree.FieldDef; +import net.fabricmc.mapping.tree.Mapped; +import net.fabricmc.mapping.tree.MethodDef; +import net.fabricmc.mapping.tree.TinyMappingFactory; +import net.fabricmc.mapping.tree.TinyTree; +import net.fabricmc.mapping.util.EntryTriple; + //Taken from loom public class MappingsStore { private final Map classes = new HashMap<>(); private final Map fields = new HashMap<>(); - private final Map methods = new HashMap<>(); + private final Map> methods = new HashMap<>(); private final String namespace = "named"; + private final List namespaces; public MappingsStore(Path tinyFile) { final TinyTree mappings = readMappings(tinyFile); + namespaces = mappings.getMetadata().getNamespaces(); + for (ClassDef classDef : mappings.getClasses()) { final String className = classDef.getName(namespace); classes.put(className, classDef); @@ -53,24 +59,71 @@ public class MappingsStore { } for (MethodDef methodDef : classDef.getMethods()) { - methods.put(new EntryTriple(className, methodDef.getName(namespace), methodDef.getDescriptor(namespace)), methodDef); + methods.put(new EntryTriple(className, methodDef.getName(namespace), methodDef.getDescriptor(namespace)), new AbstractMap.SimpleImmutableEntry<>(className, methodDef)); } } } + private static TinyTree readMappings(Path input) { + try (BufferedReader reader = Files.newBufferedReader(input)) { + return TinyMappingFactory.loadWithDetection(reader); + } catch (IOException e) { + throw new RuntimeException("Failed to read mappings", e); + } + } + + @Deprecated public String getClassDoc(String className) { ClassDef classDef = classes.get(className); return classDef != null ? classDef.getComment() : null; } + private void addDoc(Mapped element, BiConsumer addJavadoc) { + String doc = element.getComment(); + if (doc != null) { + addJavadoc.accept(doc, new Object[0]); + } + } + + public void addClassDoc(BiConsumer addJavadoc, String className) { + ClassDef classDef = classes.get(className); + if (classDef == null) { + return; + } + addDoc(classDef, addJavadoc); + addJavadoc.accept("\n", new Object[0]); + for (String namespace : namespaces) { + String transformedName = classDef.getName(namespace); + addJavadoc.accept("@mapping {@literal $L:$L}\n", new Object[] {namespace, transformedName}); + } + } + + @Deprecated public String getFieldDoc(EntryTriple fieldEntry) { FieldDef fieldDef = fields.get(fieldEntry); return fieldDef != null ? fieldDef.getComment() : null; } + public void addFieldDoc(BiConsumer addJavadoc, EntryTriple fieldEntry) { + FieldDef fieldDef = fields.get(fieldEntry); + if (fieldDef == null) { + return; + } + + addDoc(fieldDef, addJavadoc); + ClassDef owner = classes.get(fieldEntry.getOwner()); + addJavadoc.accept("\n", new Object[0]); + for (String namespace : namespaces) { + String transformedName = fieldDef.getName(namespace); + String mixinForm = "L" + owner.getName(namespace) + ";" + transformedName + ":" + fieldDef.getDescriptor(namespace); + addJavadoc.accept("@mapping {@literal $L:$L:$L}\n", new Object[] {namespace, transformedName, mixinForm}); + } + } + public Map.Entry getParamNameAndDoc(Function> superGetters, EntryTriple methodEntry, int index) { - MethodDef methodDef = searchMethod(superGetters, methodEntry); - if (methodDef != null) { + Map.Entry found = searchMethod(superGetters, methodEntry); + if (found != null) { + MethodDef methodDef = found.getValue(); if (methodDef.getParameters().isEmpty()) { return null; } @@ -83,17 +136,35 @@ public class MappingsStore { return null; } + @Deprecated public String getMethodDoc(Function> superGetters, EntryTriple methodEntry) { - MethodDef methodDef = searchMethod(superGetters, methodEntry); + Map.Entry methodDef = searchMethod(superGetters, methodEntry); if (methodDef != null) { - return methodDef.getComment(); // comment doc handled separately by javapoet + return methodDef.getValue().getComment(); // comment doc handled separately by javapoet } return null; } - - private MethodDef searchMethod(Function> superGetters, EntryTriple methodEntry) { + + public void addMethodDoc(BiConsumer addJavadoc, Function> superGetters, EntryTriple methodEntry) { + Map.Entry found = searchMethod(superGetters, methodEntry); + if (found == null) { + return; + } + + MethodDef methodDef = found.getValue(); + addDoc(methodDef, addJavadoc); + ClassDef owner = classes.get(found.getKey()); + addJavadoc.accept("\n", new Object[0]); + for (String namespace : namespaces) { + String transformedName = methodDef.getName(namespace); + String mixinForm = "L" + owner.getName(namespace) + ";" + transformedName + methodDef.getDescriptor(namespace); + addJavadoc.accept("@mapping {@literal $L:$L:$L}\n", new Object[] {namespace, transformedName, mixinForm}); + } + } + + private Map.Entry searchMethod(Function> superGetters, EntryTriple methodEntry) { String className = methodEntry.getOwner(); if (!classes.containsKey(className)) { return null; @@ -102,25 +173,17 @@ public class MappingsStore { if (methods.containsKey(methodEntry)) { return methods.get(methodEntry); // Nullable! } - + for (String superName : superGetters.apply(className)) { - EntryTriple triple = new EntryTriple(superName, methodEntry.getName(), methodEntry.getDesc()); - MethodDef ret = searchMethod(superGetters, triple); + EntryTriple triple = new EntryTriple(superName, methodEntry.getName(), methodEntry.getDescriptor()); + Map.Entry ret = searchMethod(superGetters, triple); if (ret != null) { methods.put(triple, ret); return ret; } } - + methods.put(methodEntry, null); return null; } - - private static TinyTree readMappings(Path input) { - try (BufferedReader reader = Files.newBufferedReader(input)) { - return TinyMappingFactory.loadWithDetection(reader); - } catch (IOException e) { - throw new RuntimeException("Failed to read mappings", e); - } - } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index 7983066569..0530d4200a 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -32,11 +32,19 @@ import javax.lang.model.element.Modifier; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeName; +import org.objectweb.asm.TypeReference; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; -import net.fabricmc.mappings.EntryTriple; +import net.fabricmc.mapping.util.EntryTriple; +import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; +import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; +import net.fabricmc.mappingpoet.signature.ClassStaticContext; +import net.fabricmc.mappingpoet.signature.MethodSignature; +import net.fabricmc.mappingpoet.signature.TypeAnnotationBank; +import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; +import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; public class MethodBuilder { private static final Set RESERVED_KEYWORDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( @@ -52,18 +60,29 @@ public class MethodBuilder { private final MethodSpec.Builder builder; private final Function> superGetter; private final int formalParamStartIndex; + private final ClassStaticContext context; + private final String receiverSignature; + private final TypeAnnotationMapping typeAnnotations; - private Signatures.MethodSignature signature; + private MethodSignature signature; - public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode, Function> superGetter, int formalParamStartIndex) { + public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode, Function> superGetter, ClassStaticContext context, String receiverSignature, int formalParamStartIndex) { this.mappings = mappings; this.classNode = classNode; this.methodNode = methodNode; this.superGetter = superGetter; + this.context = context; + this.receiverSignature = receiverSignature; this.formalParamStartIndex = formalParamStartIndex; + + typeAnnotations = TypeAnnotationStorage.builder() + .add(methodNode.invisibleTypeAnnotations) + .add(methodNode.visibleTypeAnnotations) + .build(); + this.builder = createBuilder(); addJavaDoc(); - addDirectAnnotations(); + addAnnotations(); setReturnType(); addParameters(); addExceptions(); @@ -107,7 +126,7 @@ public class MethodBuilder { } static String suggestName(TypeName type) { - String str = type.toString(); + String str = type.withoutAnnotations().toString(); int newStart = 0; int newEnd = str.length(); int ltStart; @@ -139,16 +158,14 @@ public class MethodBuilder { } if (methodNode.signature != null) { - signature = Signatures.parseMethodSignature(methodNode.signature); - if (signature.generics != null) { - builder.addTypeVariables(signature.generics); - } + signature = AnnotationAwareSignatures.parseMethodSignature(methodNode.signature, typeAnnotations, context); + builder.addTypeVariables(signature.generics); } return builder; } - private void addDirectAnnotations() { + private void addAnnotations() { addDirectAnnotations(methodNode.invisibleAnnotations); addDirectAnnotations(methodNode.visibleAnnotations); } @@ -173,22 +190,46 @@ public class MethodBuilder { typeName = signature.result; } else { String returnDesc = methodNode.desc.substring(methodNode.desc.lastIndexOf(")") + 1); - typeName = FieldBuilder.typeFromDesc(returnDesc); + typeName = AnnotationAwareDescriptors.parseDesc(returnDesc, typeAnnotations.getBank(TypeReference.newTypeReference(TypeReference.METHOD_RETURN)), context); } builder.returns(typeName); if (typeName != TypeName.VOID && !builder.modifiers.contains(Modifier.ABSTRACT)) { builder.addStatement("throw new RuntimeException()"); + } else if (methodNode.annotationDefault != null) { + builder.defaultValue(FieldBuilder.codeFromAnnoValue(methodNode.annotationDefault)); } } - private void addParameters() { + private void addParameters(MethodBuilder this) { // todo fix enum ctors List paramTypes = new ArrayList<>(); boolean instanceMethod = !builder.modifiers.contains(Modifier.STATIC); Set usedParamNames = new HashSet<>(RESERVED_KEYWORDS); getParams(paramTypes, instanceMethod, usedParamNames); + // generate receiver param for type annos + + TypeAnnotationBank receiverAnnos = typeAnnotations.getBank(TypeReference.newTypeReference(TypeReference.METHOD_RECEIVER)); + if (!receiverAnnos.isEmpty()) { + ParameterSpec.Builder receiverBuilder; + // only instance inner class ctor can have receivers + if (methodNode.name.equals("")) { + TypeName annotatedReceiver = AnnotationAwareSignatures.parseSignature("L" + receiverSignature.substring(0, receiverSignature.lastIndexOf('.')) + ";", receiverAnnos, context); + // vulnerable heuristics + String simpleNameChain = classNode.name.substring(classNode.name.lastIndexOf('/') + 1); + int part1 = simpleNameChain.lastIndexOf('$'); // def exists + int part2 = simpleNameChain.lastIndexOf('$', part1 - 1); // may be -1 + String usedName = simpleNameChain.substring(part2 + 1, part1); + receiverBuilder = ParameterSpec.builder(annotatedReceiver, usedName + ".this"); + } else { + TypeName annotatedReceiver = AnnotationAwareSignatures.parseSignature("L" + receiverSignature + ";", receiverAnnos, context); + receiverBuilder = ParameterSpec.builder(annotatedReceiver, "this"); + } + // receiver param cannot have its jd/param anno except type use anno + builder.addParameter(receiverBuilder.build()); + } + List[] visibleParameterAnnotations = methodNode.visibleParameterAnnotations; List[] invisibleParameterAnnotations = methodNode.invisibleParameterAnnotations; int index = 0; @@ -218,18 +259,22 @@ public class MethodBuilder { Iterator signatureParamIterator = signature == null ? Collections.emptyIterator() : signature.parameters.iterator(); while (desc.charAt(index) != ')') { + int oldIndex = index; Map.Entry parsedParam = FieldBuilder.parseType(desc, index); index = parsedParam.getKey(); - TypeName paramType = parsedParam.getValue(); + TypeName nonAnnotatedParsedType = parsedParam.getValue(); if (paramIndex >= formalParamStartIndex) { // skip guessed synthetic/implicit params + TypeName parsedType; if (signatureParamIterator.hasNext()) { - paramType = signatureParamIterator.next(); + parsedType = signatureParamIterator.next(); + } else { + parsedType = AnnotationAwareDescriptors.parseDesc(desc.substring(oldIndex, index), typeAnnotations.getBank(TypeReference.newFormalParameterReference(paramIndex - formalParamStartIndex)), context); } - paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), paramType, usedParamNames, slot)); + paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), parsedType, usedParamNames, slot)); } slot++; - if (paramType.equals(TypeName.DOUBLE) || paramType.equals(TypeName.LONG)) { + if (nonAnnotatedParsedType.equals(TypeName.DOUBLE) || nonAnnotatedParsedType.equals(TypeName.LONG)) { slot++; } paramIndex++; @@ -251,17 +296,16 @@ public class MethodBuilder { } List exceptions = methodNode.exceptions; if (exceptions != null) { + int index = 0; for (String internalName : exceptions) { - builder.addException(ClassBuilder.parseInternalName(internalName)); + builder.addException(AnnotationAwareDescriptors.parseType(internalName, typeAnnotations.getBank(TypeReference.newExceptionReference(index)), context)); + index++; } } } private void addJavaDoc() { - String javaDoc = mappings.getMethodDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc)); - if (javaDoc != null) { - builder.addJavadoc(javaDoc); - } + mappings.addMethodDoc(builder::addJavadoc, superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc)); } public MethodSpec build() { @@ -278,7 +322,7 @@ public class MethodBuilder { this.name = nameAndDoc != null ? nameAndDoc.getKey() : null; if (this.name != null) { if (usedNames.contains(this.name)) { - System.err.println(String.format("Overridden parameter name detected in %s %s %s slot %d, resetting", classNode.name, methodNode.name, methodNode.desc, slot)); + System.err.printf("Overridden parameter name detected in %s %s %s slot %d, resetting%n", classNode.name, methodNode.name, methodNode.desc, slot); this.name = null; } else { usedNames.add(this.name); diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java index 5e87731421..dad1d81da9 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java @@ -16,10 +16,11 @@ package net.fabricmc.mappingpoet; -import javax.lang.model.element.Modifier; import java.util.ArrayList; import java.util.List; +import javax.lang.model.element.Modifier; + public class ModifierBuilder { private final int access; @@ -35,7 +36,7 @@ public class ModifierBuilder { if (java.lang.reflect.Modifier.isFinal(access)) { modifiers.add(Modifier.FINAL); } - return modifiers.toArray(new Modifier[]{}); + return modifiers.toArray(new Modifier[] {}); } if (java.lang.reflect.Modifier.isPublic(access)) { @@ -48,7 +49,7 @@ public class ModifierBuilder { if (java.lang.reflect.Modifier.isAbstract(access) && type != Type.ENUM) { modifiers.add(Modifier.ABSTRACT); - } + } if (java.lang.reflect.Modifier.isStatic(access)) { modifiers.add(Modifier.STATIC); } @@ -75,7 +76,7 @@ public class ModifierBuilder { modifiers.add(Modifier.STRICTFP); } - return modifiers.toArray(new Modifier[]{}); + return modifiers.toArray(new Modifier[] {}); } public enum Type { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java index 2cce805711..a4f1e82f6b 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java @@ -17,6 +17,7 @@ package net.fabricmc.mappingpoet; import java.util.AbstractMap; +import java.util.Collections; import java.util.Deque; import java.util.LinkedList; import java.util.List; @@ -30,13 +31,16 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeVariableName; import com.squareup.javapoet.WildcardTypeName; +import net.fabricmc.mappingpoet.signature.ClassSignature; +import net.fabricmc.mappingpoet.signature.MethodSignature; + public final class Signatures { public static ClassSignature parseClassSignature(final String signature) { // ;B:Ljava/lang/Object>Ljava/lang/Object; etc etc int index = 0; char ch; - List generics = null; + List generics = Collections.emptyList(); if (signature.charAt(0) == '<') { // parse generic decl index++; // consume '<' @@ -86,23 +90,10 @@ public final class Signatures { return new ClassSignature(generics, supers.removeFirst(), supers); } - public static final class ClassSignature { - // Nullable - public final List generics; - public final TypeName superclass; - public final List superinterfaces; - - ClassSignature(List generics, TypeName superclass, List superinterfaces) { - this.generics = generics; - this.superclass = superclass; - this.superinterfaces = superinterfaces; - } - } - public static MethodSignature parseMethodSignature(String signature) { int index = 0; char ch; - List generics = null; + List generics = Collections.emptyList(); if (signature.charAt(0) == '<') { // parse generic decl index++; // consume '<' @@ -177,21 +168,6 @@ public final class Signatures { return new MethodSignature(generics, params, returnType, thrown); } - public static final class MethodSignature { - // Nullable - public final List generics; - public final List parameters; - public final TypeName result; - public final List thrown; - - MethodSignature(List generics, List parameters, TypeName result, List thrown) { - this.generics = generics; - this.parameters = parameters; - this.result = result; - this.thrown = thrown; - } - } - public static TypeName parseFieldSignature(String signature) { return parseParameterizedType(signature, 0).getValue(); } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java new file mode 100644 index 0000000000..98cbd3413e --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.jd; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.LiteralTree; +import com.sun.source.doctree.UnknownBlockTagTree; +import jdk.javadoc.doclet.Taglet; + +@SuppressWarnings("unused") +public final class MappingTaglet implements Taglet { + + private static final String JS_CONTENT; + + public MappingTaglet() { + // Required by javadoc + } + + @Override + public Set getAllowedLocations() { + return EnumSet.of(Location.TYPE, Location.CONSTRUCTOR, Location.METHOD, Location.FIELD); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return "mapping"; + } + + @Override + public String toString(List tags, Element element) { + boolean typeDecl = element instanceof TypeElement; // means it's a class, itf, enum, etc. + StringBuilder builder = new StringBuilder(); + builder.append("

Mapping data

\n"); + builder.append("\n"); + builder.append("\n"); + builder.append("\n"); + builder.append("\n"); + builder.append("\n"); + if (!typeDecl) { + builder.append("\n"); + } + builder.append("\n"); + builder.append("\n"); + + for (DocTree each : tags) { + String body = ((UnknownBlockTagTree) each).getContent().stream().map(t -> ((LiteralTree) t).getBody().getBody()).collect(Collectors.joining()); + String[] ans = body.split(":", 3); + builder.append("\n"); + builder.append(String.format("\n", escaped(ans[0]))); + final int bound = typeDecl ? 2 : 3; + for (int i = 1; i < bound; i++) { + builder.append(String.format("\n", escaped(ans[i]))); + } + builder.append("\n"); + } + + builder.append("\n"); + builder.append("
NamespaceNameMixin form
%s%s
\n"); + builder.append("\n"); + return builder.toString(); + } + + // I hate + private static String escaped(String original) { + StringBuilder builder = new StringBuilder(original.length()); + final int len = original.length(); + for (int i = 0; i < len; i++) { + char c = original.charAt(i); + if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') { + builder.append("&#").append((int) c).append(";"); + } else { + builder.append(c); + } + } + return builder.toString(); + } + + static { + StringBuilder sb = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(MappingTaglet.class.getResourceAsStream("/copy_on_click.js"), "copy_on_click.js stream"), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + JS_CONTENT = sb.toString(); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java new file mode 100644 index 0000000000..decf8ae66f --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import java.util.AbstractMap; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.TypeName; +import org.objectweb.asm.TypePath; +import org.objectweb.asm.TypeReference; + +import net.fabricmc.mappingpoet.Signatures; + +public final class AnnotationAwareDescriptors { + + private AnnotationAwareDescriptors() { + } + + // not really signature, but annotated classes + public static ClassSignature parse(String rawSuper, List rawInterfaces, TypeAnnotationMapping mapping, ClassStaticContext context) { + ClassName superName = parseType(rawSuper, mapping.getBank(TypeReference.newSuperTypeReference(-1)), context); + + List interfaces = new ArrayList<>(rawInterfaces.size()); + for (ListIterator itr = rawInterfaces.listIterator(); itr.hasNext(); ) { + int i = itr.nextIndex(); + String item = itr.next(); + + ClassName itfName = parseType(item, mapping.getBank(TypeReference.newSuperTypeReference(i)), context); + interfaces.add(itfName); + } + + return new ClassSignature(Collections.emptyList(), superName, interfaces); + } + + // only for descriptor-based ones. Use signature visitor for signature-based ones! + public static TypeName parseDesc(String desc, TypeAnnotationBank bank, ClassStaticContext context) { + Deque> arrayAnnotations = new ArrayDeque<>(); + int len = desc.length(); + int index; + for (index = 0; (index < len) && (desc.charAt(index) == '['); index++) { + arrayAnnotations.push(bank.getCurrentAnnotations()); + bank = bank.advance(TypePath.ARRAY_ELEMENT, 0); + } + + TypeName current; + if (len - index == 1) { + current = annotate(Signatures.getPrimitive(desc.charAt(index)), bank); + } else { + // L ; + assert desc.charAt(index) == 'L' && desc.charAt(len - 1) == ';'; + current = parseType(desc.substring(index + 1, len - 1), bank, context); + } + while (!arrayAnnotations.isEmpty()) { + current = ArrayTypeName.of(current); + List specs = arrayAnnotations.pop(); + if (!specs.isEmpty()) { + current = current.annotated(specs); + } + } + + return current; + } + + public static ClassName parseType(String internalName, TypeAnnotationBank bank, ClassStaticContext context) { + Map.Entry result = annotateUpTo(internalName, bank, context); + return annotate(result.getKey(), result.getValue()); + } + + /** + * Annotate class name chains until the last element. Useful for making the last + * element a parameterized type before annotating it. + * + * @param internalName the internal name chain + * @param bank the annotation storage + * @param context the context for testing instance inner class + * @return the class name ready for parameterization/annotation and the current annotation state + */ + static Map.Entry annotateUpTo(String internalName, TypeAnnotationBank bank, ClassStaticContext context) { + if (internalName.startsWith("L") && internalName.endsWith(";")) { + throw new AssertionError(internalName); + } + int slice = internalName.lastIndexOf('/'); + String packageSt = slice < 0 ? "" : internalName.substring(0, slice).replace('/', '.'); + + int moneySign = internalName.indexOf('$', slice + 1); + if (moneySign == -1) { + return new AbstractMap.SimpleImmutableEntry<>(ClassName.get(packageSt, internalName.substring(slice + 1)), bank); + } + + ClassName current = ClassName.get(packageSt, internalName.substring(slice + 1, moneySign)); + + final int len = internalName.length(); + boolean enteredInner = false; + for (int i = moneySign; i < len; ) { + int t = internalName.indexOf('$', i + 1); + if (t < 0) { + t = len; + } + + // do work + if (!enteredInner && context.isInstanceInner(internalName.substring(0, t))) { + enteredInner = true; // instance inner classes cannot nest static ones + } + + if (enteredInner) { + // annotate parent before we advance + current = annotate(current, bank); + } + + current = current.nestedClass(internalName.substring(i + 1, t)); + + if (enteredInner) { + // advance on path as it's instance inner class + bank = bank.advance(TypePath.INNER_TYPE, 0); + } + + i = t; + } + + return new AbstractMap.SimpleImmutableEntry<>(current, bank); + } + + @SuppressWarnings("unchecked") + public static T annotate(T input, TypeAnnotationBank storage) { + List annotations = storage.getCurrentAnnotations(); + return annotations.isEmpty() ? input : (T) input.annotated(annotations); // it's implemented so + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java new file mode 100644 index 0000000000..c6572d68bb --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import com.squareup.javapoet.TypeName; +import org.objectweb.asm.TypeReference; +import org.objectweb.asm.signature.SignatureReader; + +public final class AnnotationAwareSignatures { + + private AnnotationAwareSignatures() { + } + + public static ClassSignature parseClassSignature(String signature, TypeAnnotationMapping annotationMapping, ClassStaticContext context) { + PoetClassMethodSignatureVisitor visitor = new PoetClassMethodSignatureVisitor(annotationMapping, context, true); + new SignatureReader(signature).accept(visitor); + return visitor.collectClass(); + } + + // Note: No receiver (self) parameter included! + public static MethodSignature parseMethodSignature(String signature, TypeAnnotationMapping annotationMapping, ClassStaticContext context) { + PoetClassMethodSignatureVisitor visitor = new PoetClassMethodSignatureVisitor(annotationMapping, context, false); + new SignatureReader(signature).accept(visitor); + return visitor.collectMethod(); + } + + public static TypeName parseFieldSignature(String signature, TypeAnnotationMapping annotationMapping, ClassStaticContext context) { + return parseSignature(signature, annotationMapping.getBank(TypeReference.newTypeReference(TypeReference.FIELD)), context); + } + + public static TypeName parseSignature(String signature, TypeAnnotationBank annotations, ClassStaticContext context) { + PoetTypeSignatureWriter visitor = new PoetTypeSignatureWriter(annotations, context); + new SignatureReader(signature).acceptType(visitor); + return visitor.compute(); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java new file mode 100644 index 0000000000..e3b5df5e79 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import java.util.List; + +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeVariableName; + +// no more a class signature but general super info about class +public final class ClassSignature { + public final List generics; + public final TypeName superclass; + public final List superinterfaces; + + public ClassSignature(List generics, TypeName superclass, List superinterfaces) { + this.generics = generics; + this.superclass = superclass; + this.superinterfaces = superinterfaces; + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java new file mode 100644 index 0000000000..1ba5c49f85 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +/** + * A context to retrieve if a class is an instance inner class. Useful for + * placing type annotations correctly. See + * + * an example in JVM Specification 15. + */ +public interface ClassStaticContext { + + /** + * Returns if this class is an instance inner class. + * + *

For example, a top-level class is not so. A static inner + * class, such as {@code Map.Entry}, is not as well.

+ * + * @param internalName the JVM name of the class + * @return whether this class is not an instance inner class. + */ + boolean isInstanceInner(String internalName); +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java new file mode 100644 index 0000000000..20cfe160d1 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import java.util.List; + +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeVariableName; + +public final class MethodSignature { + public final List generics; + public final List parameters; + public final TypeName result; + public final List thrown; + + public MethodSignature(List generics, List parameters, TypeName result, List thrown) { + this.generics = generics; + this.parameters = parameters; + this.result = result; + this.thrown = thrown; + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java new file mode 100644 index 0000000000..1c6056dc79 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import java.util.LinkedList; + +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeVariableName; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.TypeReference; +import org.objectweb.asm.signature.SignatureVisitor; + +public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { + + private final TypeAnnotationMapping mapping; + private final ClassStaticContext context; + private final boolean forClass; + LinkedList generics = new LinkedList<>(); + // collecting generic + String currentGenericName; + LinkedList currentGenericBounds = new LinkedList<>(); + // bound for each generic + PoetTypeSignatureWriter pendingLowerBound; + + // classes usage + LinkedList superTypes = new LinkedList<>(); + PoetTypeSignatureWriter pendingSupertype; + + // methods usage + LinkedList params = new LinkedList<>(); + LinkedList throwables = new LinkedList<>(); + PoetTypeSignatureWriter pendingSlot; + TypeName returnType; + + public PoetClassMethodSignatureVisitor(TypeAnnotationMapping mapping, ClassStaticContext context, boolean forClass) { + super(Opcodes.ASM8); + this.mapping = mapping; + this.context = context; + this.forClass = forClass; + } + + private void collectGeneric() { + collectLowerBound(); + + if (currentGenericName != null) { + TypeVariableName generic = TypeVariableName.get(currentGenericName, currentGenericBounds.toArray(new TypeName[0])); + TypeAnnotationBank bank = mapping.getBank(TypeReference.newTypeParameterReference(forClass ? TypeReference.CLASS_TYPE_PARAMETER : TypeReference.METHOD_TYPE_PARAMETER, generics.size())); + generic = AnnotationAwareDescriptors.annotate(generic, bank); + generics.add(generic); + + currentGenericName = null; + currentGenericBounds.clear(); + } + } + + private void collectGenerics() { + // end all generics + collectGeneric(); + } + + // starts a new generic declaration, like in " T[] toArray(T[] input);" + @Override + public void visitFormalTypeParameter(String name) { + collectGeneric(); + // collect existing type parameter + // start type var name + currentGenericName = name; + currentGenericBounds.clear(); + } + + private void collectLowerBound() { + if (pendingLowerBound != null) { + currentGenericBounds.addLast(pendingLowerBound.compute()); + pendingLowerBound = null; + } + } + + private SignatureVisitor visitLowerBound() { + collectLowerBound(); + + TypeAnnotationBank bank = mapping.getBank(TypeReference.newTypeParameterBoundReference(forClass ? + TypeReference.CLASS_TYPE_PARAMETER_BOUND : TypeReference.METHOD_TYPE_PARAMETER_BOUND, generics.size(), + currentGenericBounds.size())); + return pendingLowerBound = new PoetTypeSignatureWriter(bank, context); + } + + @Override + public SignatureVisitor visitClassBound() { + return visitLowerBound(); + } + + @Override + public SignatureVisitor visitInterfaceBound() { + return visitLowerBound(); + } + + // class exclusive + + private void collectSupertype() { + if (pendingSupertype != null) { + TypeName simple = pendingSupertype.compute(); + superTypes.addLast(simple); + + pendingSupertype = null; + } + } + + // always called + @Override + public SignatureVisitor visitSuperclass() { + collectGenerics(); + // don't need to collect other supertype + + return pendingSupertype = new PoetTypeSignatureWriter(mapping.getBank(TypeReference.newSuperTypeReference(-1)), context); + } + + @Override + public SignatureVisitor visitInterface() { + // super class always visited, no generic check + collectSupertype(); + + return pendingSupertype = new PoetTypeSignatureWriter(mapping.getBank(TypeReference.newSuperTypeReference(superTypes.size() - 1)), context); + } + + public ClassSignature collectClass() { + collectSupertype(); + + TypeName superclass = superTypes.removeFirst(); + return new ClassSignature(generics, superclass, superTypes); + } + + // method exclusive + + private void collectParam() { + if (pendingSlot != null) { + TypeName slot = pendingSlot.compute(); + params.addLast(slot); + + pendingSlot = null; + } + } + + private void collectReturnOrThrows() { + if (pendingSlot != null) { + if (returnType == null) { + returnType = pendingSlot.compute(); + } else { + throwables.addLast(pendingSlot.compute()); + } + + pendingSlot = null; + } + } + + @Override + public SignatureVisitor visitParameterType() { + collectGenerics(); + collectParam(); + + return pendingSlot = new PoetTypeSignatureWriter(mapping.getBank(TypeReference.newFormalParameterReference(params.size())), context); + } + + @Override + public SignatureVisitor visitReturnType() { + collectGenerics(); // they may skip visiting params, rip! + collectParam(); + + return pendingSlot = new PoetTypeSignatureWriter(mapping.getBank(TypeReference.newTypeReference(TypeReference.METHOD_RETURN)), context); + } + + @Override + public SignatureVisitor visitExceptionType() { + collectReturnOrThrows(); + + return pendingSlot = new PoetTypeSignatureWriter(mapping.getBank(TypeReference.newExceptionReference(throwables.size())), context); + } + + public MethodSignature collectMethod() { + collectReturnOrThrows(); + + return new MethodSignature(generics, params, returnType, throwables); + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java new file mode 100644 index 0000000000..bf5014fc69 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import java.util.LinkedList; +import java.util.Map; +import java.util.Objects; + +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeVariableName; +import com.squareup.javapoet.WildcardTypeName; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.TypePath; +import org.objectweb.asm.signature.SignatureVisitor; + +import net.fabricmc.mappingpoet.Signatures; + +/** + * A type signature to javapoet visitor. + * + *

A type signature is one at the usage of type, such as field and local var type. + * It does not include class or method signatures where new generics like {@code } + * can be defined.

+ */ +public final class PoetTypeSignatureWriter extends SignatureVisitor { + + private final ClassStaticContext context; + private final LinkedList params = new LinkedList<>(); + private /* NonNull */ TypeAnnotationBank storage; // mutable + private TypeName result; + // array + private PoetTypeSignatureWriter arrayChild; + // class type signature stuff + private TypeName currentType; // ClassName or ParameterizedTypeName + private String nestedClassName; + // single type argument + private char activeTypeArgumentKind; + private PoetTypeSignatureWriter activeTypeArgument; + + public PoetTypeSignatureWriter(TypeAnnotationBank storage, ClassStaticContext context) { + super(Opcodes.ASM8); + this.storage = storage; + this.context = context; + } + + public TypeName compute() { + // array cleanup. doesn't have visit end TwT + if (arrayChild != null) { + result = ArrayTypeName.of(arrayChild.compute()); + + result = annotate(result); + } + + return Objects.requireNonNull(result, "writer did not visit"); + } + + private T annotate(T input) { + return AnnotationAwareDescriptors.annotate(input, storage); + } + + private void annotateResult() { + result = annotate(result); + } + + // primitives + @Override + public void visitBaseType(char descriptor) { + result = Signatures.getPrimitive(descriptor); + annotateResult(); + } + + // T, E etc + @Override + public void visitTypeVariable(String name) { + result = TypeVariableName.get(name); + annotateResult(); + } + + @Override + public SignatureVisitor visitArrayType() { + return arrayChild = new PoetTypeSignatureWriter(this.storage.advance(TypePath.ARRAY_ELEMENT, 0), context); + // post cleanup, annotate in #getResult() + } + + // outer class, may have instance inner class. ends with visitEnd + @Override + public void visitClassType(String internalName) { + Map.Entry entry = AnnotationAwareDescriptors.annotateUpTo(internalName, storage, context); + + currentType = entry.getKey(); + storage = entry.getValue(); + // later collect annotations in #collectPreviousTypeArgumentsAndAnnotations + } + + // collect info onto this before we append inners + private void collectPreviousTypeArgumentsAndAnnotations() { + collectLastTypeArgument(); + + if (!params.isEmpty()) { + if (currentType instanceof ParameterizedTypeName) { + // top-level handled already + currentType = ((ParameterizedTypeName) currentType).nestedClass(nestedClassName, params); + } else { // assume ClassName + if (nestedClassName == null) { // top-level + currentType = ParameterizedTypeName.get((ClassName) currentType, params.toArray(new TypeName[0])); + } else { + currentType = ParameterizedTypeName.get(((ClassName) currentType).nestedClass(nestedClassName), params.toArray(new TypeName[0])); + } + } + + params.clear(); + nestedClassName = null; + } else if (nestedClassName != null) { + if (currentType instanceof ParameterizedTypeName) { + currentType = ((ParameterizedTypeName) currentType).nestedClass(nestedClassName); + } else { + currentType = ((ClassName) currentType).nestedClass(nestedClassName); + } + nestedClassName = null; + } + + currentType = annotate(currentType); + } + + @Override + public void visitInnerClassType(String name) { + collectPreviousTypeArgumentsAndAnnotations(); + // collect previous type arguments + nestedClassName = name; + storage = storage.advance(TypePath.INNER_TYPE, 0); + } + + private void collectLastTypeArgument() { + if (activeTypeArgument != null) { + TypeName hold = activeTypeArgument.compute(); + TypeName used; + switch (activeTypeArgumentKind) { + case SignatureVisitor.EXTENDS: + used = WildcardTypeName.subtypeOf(hold); + break; + case SignatureVisitor.SUPER: + used = WildcardTypeName.supertypeOf(hold); + break; + case SignatureVisitor.INSTANCEOF: + used = hold; + break; + default: + throw new IllegalStateException(String.format("Illegal type argument wildcard %s", activeTypeArgumentKind)); + } + + used = AnnotationAwareDescriptors.annotate(used, storage.advance(TypePath.TYPE_ARGUMENT, params.size())); + params.addLast(used); + + activeTypeArgument = null; + activeTypeArgumentKind = 0; + } + } + + // wildcard ? like in List + @Override + public void visitTypeArgument() { + collectLastTypeArgument(); + + TypeName used = WildcardTypeName.subtypeOf(TypeName.OBJECT); + used = AnnotationAwareDescriptors.annotate(used, storage.advance(TypePath.TYPE_ARGUMENT, params.size())); + params.addLast(used); + } + + // (? extends/ ? super)? ClassType like in Consumer, + // Supplier + @Override + public SignatureVisitor visitTypeArgument(char wildcard) { + collectLastTypeArgument(); + + activeTypeArgumentKind = wildcard; + return activeTypeArgument = new PoetTypeSignatureWriter(storage.advance(TypePath.WILDCARD_BOUND, 0), context); + } + + @Override + public void visitEnd() { + collectPreviousTypeArgumentsAndAnnotations(); + // finalize result! + result = currentType; + currentType = null; + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java new file mode 100644 index 0000000000..3b24907c2b --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import java.util.Collections; +import java.util.List; + +import com.squareup.javapoet.AnnotationSpec; + +// recommended storing with a sorted array + index slicing + +/** + * The collection of type annotations on a specific type. Can be narrowed down through type path. + */ +public interface TypeAnnotationBank { + TypeAnnotationBank EMPTY = new TypeAnnotationBank() { + @Override + public TypeAnnotationBank advance(int step, int stepArgument) { + return this; + } + + @Override + public List getCurrentAnnotations() { + return Collections.emptyList(); + } + + @Override + public boolean isEmpty() { + return true; + } + }; + + /** + * Make the scope of type annotations smaller. + * + * @param step see {@link org.objectweb.asm.TypePath#getStep(int)} + * @param stepArgument see {@link org.objectweb.asm.TypePath#getStepArgument(int)} + * @return the sliced type annotation storage + */ + TypeAnnotationBank advance(int step, int stepArgument); + + /** + * Accesses annotations applicable at current type location. + * + *

Do not modify the returned list!

+ * + * @return the current annotations to apply + */ + List getCurrentAnnotations(); + + /** + * Returns if there is no more annotations. Used to check for receiver + * declarations. + * + * @return whether there's no more annotations + */ + boolean isEmpty(); +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java new file mode 100644 index 0000000000..f6caaaee05 --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import org.objectweb.asm.TypeReference; + +/** + * The collection of type annotations from a bytecode structure that stores type annotations. + */ +public interface TypeAnnotationMapping { + + TypeAnnotationMapping EMPTY = reference -> TypeAnnotationBank.EMPTY; + + // implNote: TypeReference is not a pojo! No equals or hash! + TypeAnnotationBank getBank(TypeReference reference); +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java new file mode 100644 index 0000000000..14af548b4d --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.signature; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import com.squareup.javapoet.AnnotationSpec; +import org.objectweb.asm.TypePath; +import org.objectweb.asm.TypeReference; +import org.objectweb.asm.tree.TypeAnnotationNode; + +import net.fabricmc.mappingpoet.FieldBuilder; + +public final class TypeAnnotationStorage implements TypeAnnotationMapping, TypeAnnotationBank { + + private final int[] targets; // target type and info, only exist in mapping version + private final String[] paths; + private final AnnotationSpec[] contents; + private final int startIndex; + private final int endIndex; + private final String currentPath; + + TypeAnnotationStorage(int startIndex, int endIndex, String currentPath, int[] targets, String[] paths, AnnotationSpec[] contents) { + this.targets = targets; + this.paths = paths; + this.contents = contents; + this.startIndex = startIndex; + this.endIndex = endIndex; + this.currentPath = currentPath; + } + + public static Builder builder() { + return new Builder(); + } + + static int comparePath(TypePath left, TypePath right) { + int len = Math.min(left.getLength(), right.getLength()); + for (int i = 0; i < len; i++) { + int leftStep = left.getStep(i); + int rightStep = right.getStep(i); + if (leftStep != rightStep) { + return Integer.compare(leftStep, rightStep); + } + + int leftStepArg = left.getStepArgument(i); + int rightStepArg = right.getStepArgument(i); + if (leftStepArg != rightStepArg) { + return Integer.compare(leftStepArg, rightStepArg); + } + } + + // shorter ones definitely go first! + return Integer.compare(left.getLength(), right.getLength()); + } + + @Override + public TypeAnnotationBank advance(int step, int stepArgument) { + if (currentPath == null) { + throw new IllegalStateException(); + } + + String suffix; + switch (step) { + case TypePath.ARRAY_ELEMENT: + suffix = "["; + break; + case TypePath.INNER_TYPE: + suffix = "."; + break; + case TypePath.WILDCARD_BOUND: + suffix = "*"; + break; + case TypePath.TYPE_ARGUMENT: + suffix = stepArgument + ";"; + break; + default: + throw new IllegalArgumentException(); + } + + String check = currentPath.concat(suffix); + + String hiCheck = check.substring(0, check.length() - 1).concat(Character.toString((char) (check.charAt(check.length() - 1) + 1))); + + + int low = Arrays.binarySearch(paths, startIndex, endIndex, check); + if (low < 0) { + low = -(low + 1); + } + + // exclusive hi + int hi = Arrays.binarySearch(paths, startIndex, endIndex, hiCheck); + if (hi < 0) { + hi = -(hi + 1); + } + + return new TypeAnnotationStorage(low, hi, check, null, paths, contents); + } + + @Override + public List getCurrentAnnotations() { + if (currentPath == null) { + throw new IllegalStateException(); + } + + int hi = Arrays.binarySearch(paths, startIndex, endIndex, currentPath + '\u0000'); + if (hi < 0) { + hi = -(hi + 1); + } + + return Arrays.asList(contents).subList(startIndex, hi); + } + + @Override + public boolean isEmpty() { + return startIndex >= endIndex; + } + + @Override + public TypeAnnotationBank getBank(TypeReference reference) { + if (targets == null) { + throw new IllegalStateException(); + } + + int target = reference.getValue(); + // inclusive low + int low = Arrays.binarySearch(targets, startIndex, endIndex, target); + if (low < 0) { + low = -(low + 1); + } + + // exclusive hi + int hi = Arrays.binarySearch(targets, startIndex, endIndex, target + 1); + if (hi < 0) { + hi = -(hi + 1); + } + + return new TypeAnnotationStorage(low, hi, "", null, paths, contents); + } + + public static final class Builder { + + final List entries = new ArrayList<>(); + + Builder() { + } + + public Builder add(int typeReference, String typePath, AnnotationSpec spec) { + entries.add(new Entry(typeReference, typePath, spec)); + return this; + } + + public Builder add(Iterable nodes) { + if (nodes == null) { + return this; // thanks asm + } + for (TypeAnnotationNode node : nodes) { + entries.add(new Entry(node.typeRef, node.typePath == null ? "" : node.typePath.toString(), FieldBuilder.parseAnnotation(node))); + } + return this; + } + + public TypeAnnotationMapping build() { + this.entries.sort(null); + int len = this.entries.size(); + + int[] targets = new int[len]; + String[] paths = new String[len]; + AnnotationSpec[] contents = new AnnotationSpec[len]; + + Iterator itr = this.entries.iterator(); + for (int i = 0; i < len; i++) { + Entry entry = itr.next(); + targets[i] = entry.target; + paths[i] = entry.path; + contents[i] = entry.content; + } + + return new TypeAnnotationStorage(0, len, null, targets, paths, contents); + } + + private static final class Entry implements Comparable { + final int target; + final String path; + final AnnotationSpec content; + + Entry(int target, String path, AnnotationSpec content) { + this.target = target; + this.path = path; + this.content = content; + } + + @Override + public int compareTo(Entry o) { + int c0 = Integer.compare(target, o.target); + if (c0 != 0) return c0; + return path.compareTo(o.path); + } + } + + } +} From 4f803496871ae8359ac4b77c525e29b069e7bdc3 Mon Sep 17 00:00:00 2001 From: i509VCB Date: Wed, 23 Dec 2020 15:22:34 -0600 Subject: [PATCH 07/30] Mixin selector go brr --- .../net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java index 98cbd3413e..a036a04812 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -70,7 +70,7 @@ public final class MappingTaglet implements Taglet { builder.append("Namespace\n"); builder.append("Name\n"); if (!typeDecl) { - builder.append("Mixin form\n"); + builder.append("Mixin selector\n"); } builder.append("\n"); builder.append("\n"); From 728b75d6c53064c99dca27c130663d66d503450e Mon Sep 17 00:00:00 2001 From: altrisi Date: Thu, 24 Dec 2020 17:23:51 +0100 Subject: [PATCH 08/30] Make mappings table match javadoc style and remove deprecated attributes (#10) --- .../filament/mappingpoet/jd/MappingTaglet.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java index a036a04812..e7a262732b 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -63,14 +63,13 @@ public final class MappingTaglet implements Taglet { public String toString(List tags, Element element) { boolean typeDecl = element instanceof TypeElement; // means it's a class, itf, enum, etc. StringBuilder builder = new StringBuilder(); - builder.append("

Mapping data

\n"); - builder.append("\n"); - builder.append("\n"); + builder.append("

Mapping data

\n"); + builder.append("
\n"); builder.append("\n"); - builder.append("\n"); - builder.append("\n"); + builder.append("\n"); + builder.append("\n"); if (!typeDecl) { - builder.append("\n"); + builder.append("\n"); } builder.append("\n"); builder.append("\n"); @@ -79,10 +78,10 @@ public final class MappingTaglet implements Taglet { String body = ((UnknownBlockTagTree) each).getContent().stream().map(t -> ((LiteralTree) t).getBody().getBody()).collect(Collectors.joining()); String[] ans = body.split(":", 3); builder.append("\n"); - builder.append(String.format("\n", escaped(ans[0]))); + builder.append(String.format("\n", escaped(ans[0]))); final int bound = typeDecl ? 2 : 3; for (int i = 1; i < bound; i++) { - builder.append(String.format("\n", escaped(ans[i]))); + builder.append(String.format("\n", escaped(ans[i]))); } builder.append("\n"); } From b333f68207c72a21a144379292926cf2a2e66697 Mon Sep 17 00:00:00 2001 From: altrisi Date: Thu, 24 Dec 2020 17:32:54 +0100 Subject: [PATCH 09/30] Copy the mapping table items on click (#9) * Copy the mapping table items on click * Set title programatically, add cursor pointer effect --- .../net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java index e7a262732b..3abaa57ce2 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -81,7 +81,7 @@ public final class MappingTaglet implements Taglet { builder.append(String.format("\n", escaped(ans[0]))); final int bound = typeDecl ? 2 : 3; for (int i = 1; i < bound; i++) { - builder.append(String.format("\n", escaped(ans[i]))); + builder.append(String.format("\n", escaped(ans[i]))); } builder.append("\n"); } @@ -89,7 +89,7 @@ public final class MappingTaglet implements Taglet { builder.append("\n"); builder.append("
NamespaceNameNamespaceNameMixin selectorMixin selector
%s%s%s%s
%s%s%s
\n"); builder.append("\n"); return builder.toString(); } From 0e63622a5907a19e8dab9bafee2e470c0bb3cf19 Mon Sep 17 00:00:00 2001 From: liach Date: Thu, 24 Dec 2020 17:39:07 -0600 Subject: [PATCH 10/30] 0.2.2 Update html class reference to that of java 15 include js only once each class page Signed-off-by: liach --- .../mappingpoet/jd/MappingTaglet.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java index 3abaa57ce2..1e0ff278bf 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -63,34 +63,39 @@ public final class MappingTaglet implements Taglet { public String toString(List tags, Element element) { boolean typeDecl = element instanceof TypeElement; // means it's a class, itf, enum, etc. StringBuilder builder = new StringBuilder(); - builder.append("

Mapping data

\n"); - builder.append("\n"); + builder.append("
Mappings:
\n"); + // Java 15 required for style consistency + builder.append("
\n"); builder.append("\n"); - builder.append("\n"); - builder.append("\n"); + builder.append("\n"); + builder.append("\n"); if (!typeDecl) { - builder.append("\n"); + builder.append("\n"); } builder.append("\n"); builder.append("\n"); + boolean altColor = true; for (DocTree each : tags) { String body = ((UnknownBlockTagTree) each).getContent().stream().map(t -> ((LiteralTree) t).getBody().getBody()).collect(Collectors.joining()); String[] ans = body.split(":", 3); - builder.append("\n"); - builder.append(String.format("\n", escaped(ans[0]))); - final int bound = typeDecl ? 2 : 3; - for (int i = 1; i < bound; i++) { - builder.append(String.format("\n", escaped(ans[i]))); + builder.append("\n"); + builder.append(String.format("\n", escaped(ans[0]))); + builder.append(String.format("\n", escaped(ans[1]))); + if (!typeDecl) { + builder.append(String.format("\n", escaped(ans[2]))); } builder.append("\n"); + altColor = !altColor; } builder.append("\n"); - builder.append("
NamespaceNameNamespaceNameMixin selectorMixin selector
%s%s
%s%s%s
\n"); - builder.append("\n"); + builder.append("\n"); + if (typeDecl) { + builder.append("\n"); + } return builder.toString(); } From c682bce08b6bed30dfc139d06dfc97248a2009a4 Mon Sep 17 00:00:00 2001 From: liach Date: Fri, 8 Jan 2021 00:39:33 -0600 Subject: [PATCH 11/30] Move the copy on click script to yarn Signed-off-by: liach --- .../mappingpoet/jd/MappingTaglet.java | 232 ++++++++---------- 1 file changed, 103 insertions(+), 129 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java index 1e0ff278bf..dc76b1ea46 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -1,129 +1,103 @@ -/* - * Copyright (c) 2020 FabricMC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.fabricmc.mappingpoet.jd; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.util.EnumSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; - -import com.sun.source.doctree.DocTree; -import com.sun.source.doctree.LiteralTree; -import com.sun.source.doctree.UnknownBlockTagTree; -import jdk.javadoc.doclet.Taglet; - -@SuppressWarnings("unused") -public final class MappingTaglet implements Taglet { - - private static final String JS_CONTENT; - - public MappingTaglet() { - // Required by javadoc - } - - @Override - public Set getAllowedLocations() { - return EnumSet.of(Location.TYPE, Location.CONSTRUCTOR, Location.METHOD, Location.FIELD); - } - - @Override - public boolean isInlineTag() { - return false; - } - - @Override - public String getName() { - return "mapping"; - } - - @Override - public String toString(List tags, Element element) { - boolean typeDecl = element instanceof TypeElement; // means it's a class, itf, enum, etc. - StringBuilder builder = new StringBuilder(); - builder.append("
Mappings:
\n"); - // Java 15 required for style consistency - builder.append("
\n"); - builder.append("\n"); - builder.append("\n"); - builder.append("\n"); - if (!typeDecl) { - builder.append("\n"); - } - builder.append("\n"); - builder.append("\n"); - - boolean altColor = true; - for (DocTree each : tags) { - String body = ((UnknownBlockTagTree) each).getContent().stream().map(t -> ((LiteralTree) t).getBody().getBody()).collect(Collectors.joining()); - String[] ans = body.split(":", 3); - builder.append("\n"); - builder.append(String.format("\n", escaped(ans[0]))); - builder.append(String.format("\n", escaped(ans[1]))); - if (!typeDecl) { - builder.append(String.format("\n", escaped(ans[2]))); - } - builder.append("\n"); - altColor = !altColor; - } - - builder.append("\n"); - builder.append("
NamespaceNameMixin selector
%s%s%s
\n"); - if (typeDecl) { - builder.append("\n"); - } - return builder.toString(); - } - - // I hate - private static String escaped(String original) { - StringBuilder builder = new StringBuilder(original.length()); - final int len = original.length(); - for (int i = 0; i < len; i++) { - char c = original.charAt(i); - if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') { - builder.append("&#").append((int) c).append(";"); - } else { - builder.append(c); - } - } - return builder.toString(); - } - - static { - StringBuilder sb = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(MappingTaglet.class.getResourceAsStream("/copy_on_click.js"), "copy_on_click.js stream"), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append("\n"); - } - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - JS_CONTENT = sb.toString(); - } -} +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.jd; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.LiteralTree; +import com.sun.source.doctree.UnknownBlockTagTree; +import jdk.javadoc.doclet.Taglet; + +@SuppressWarnings("unused") +public final class MappingTaglet implements Taglet { + + public MappingTaglet() { + // Required by javadoc + } + + @Override + public Set getAllowedLocations() { + return EnumSet.of(Location.TYPE, Location.CONSTRUCTOR, Location.METHOD, Location.FIELD); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return "mapping"; + } + + @Override + public String toString(List tags, Element element) { + boolean typeDecl = element instanceof TypeElement; // means it's a class, itf, enum, etc. + StringBuilder builder = new StringBuilder(); + builder.append("
Mappings:
\n"); + // Java 15 required for style consistency + builder.append("
\n"); + builder.append("\n"); + builder.append("\n"); + builder.append("\n"); + if (!typeDecl) { + builder.append("\n"); + } + builder.append("\n"); + builder.append("\n"); + + boolean altColor = true; + for (DocTree each : tags) { + String body = ((UnknownBlockTagTree) each).getContent().stream().map(t -> ((LiteralTree) t).getBody().getBody()).collect(Collectors.joining()); + String[] ans = body.split(":", 3); + builder.append("\n"); + builder.append(String.format("\n", escaped(ans[0]))); + builder.append(String.format("\n", escaped(ans[1]))); + if (!typeDecl) { + builder.append(String.format("\n", escaped(ans[2]))); + } + builder.append("\n"); + altColor = !altColor; + } + + builder.append("\n"); + builder.append("
NamespaceNameMixin selector
%s%s%s
\n"); + return builder.toString(); + } + + // I hate + private static String escaped(String original) { + StringBuilder builder = new StringBuilder(original.length()); + final int len = original.length(); + for (int i = 0; i < len; i++) { + char c = original.charAt(i); + if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') { + builder.append("&#").append((int) c).append(";"); + } else { + builder.append(c); + } + } + return builder.toString(); + } +} From 2d6eda19e7824638f928fc1731dca32770db0921 Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 20 Jan 2021 15:15:34 -0600 Subject: [PATCH 12/30] 0.2.5: Use builtin stylesheet (edited from that for fabricmc.net) Signed-off-by: liach --- .../mappingpoet/jd/MappingTaglet.java | 203 +++++++++--------- 1 file changed, 100 insertions(+), 103 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java index dc76b1ea46..b2f07bdb7c 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -1,103 +1,100 @@ -/* - * Copyright (c) 2020 FabricMC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.fabricmc.mappingpoet.jd; - -import java.util.EnumSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; - -import com.sun.source.doctree.DocTree; -import com.sun.source.doctree.LiteralTree; -import com.sun.source.doctree.UnknownBlockTagTree; -import jdk.javadoc.doclet.Taglet; - -@SuppressWarnings("unused") -public final class MappingTaglet implements Taglet { - - public MappingTaglet() { - // Required by javadoc - } - - @Override - public Set getAllowedLocations() { - return EnumSet.of(Location.TYPE, Location.CONSTRUCTOR, Location.METHOD, Location.FIELD); - } - - @Override - public boolean isInlineTag() { - return false; - } - - @Override - public String getName() { - return "mapping"; - } - - @Override - public String toString(List tags, Element element) { - boolean typeDecl = element instanceof TypeElement; // means it's a class, itf, enum, etc. - StringBuilder builder = new StringBuilder(); - builder.append("
Mappings:
\n"); - // Java 15 required for style consistency - builder.append("
\n"); - builder.append("\n"); - builder.append("\n"); - builder.append("\n"); - if (!typeDecl) { - builder.append("\n"); - } - builder.append("\n"); - builder.append("\n"); - - boolean altColor = true; - for (DocTree each : tags) { - String body = ((UnknownBlockTagTree) each).getContent().stream().map(t -> ((LiteralTree) t).getBody().getBody()).collect(Collectors.joining()); - String[] ans = body.split(":", 3); - builder.append("\n"); - builder.append(String.format("\n", escaped(ans[0]))); - builder.append(String.format("\n", escaped(ans[1]))); - if (!typeDecl) { - builder.append(String.format("\n", escaped(ans[2]))); - } - builder.append("\n"); - altColor = !altColor; - } - - builder.append("\n"); - builder.append("
NamespaceNameMixin selector
%s%s%s
\n"); - return builder.toString(); - } - - // I hate - private static String escaped(String original) { - StringBuilder builder = new StringBuilder(original.length()); - final int len = original.length(); - for (int i = 0; i < len; i++) { - char c = original.charAt(i); - if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') { - builder.append("&#").append((int) c).append(";"); - } else { - builder.append(c); - } - } - return builder.toString(); - } -} +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.mappingpoet.jd; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; + +import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.LiteralTree; +import com.sun.source.doctree.UnknownBlockTagTree; +import jdk.javadoc.doclet.Taglet; + +@SuppressWarnings("unused") +public final class MappingTaglet implements Taglet { + + public MappingTaglet() { + // Required by javadoc + } + + @Override + public Set getAllowedLocations() { + return EnumSet.of(Location.TYPE, Location.CONSTRUCTOR, Location.METHOD, Location.FIELD); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return "mapping"; + } + + @Override + public String toString(List tags, Element element) { + boolean typeDecl = element instanceof TypeElement; // means it's a class, itf, enum, etc. + StringBuilder builder = new StringBuilder(); + builder.append("
Mappings:
\n"); + builder.append("
\n"); + builder.append("\n"); + builder.append("\n"); + builder.append("\n"); + if (!typeDecl) { + builder.append("\n"); + } + builder.append("\n"); + builder.append("\n"); + + for (DocTree each : tags) { + String body = ((UnknownBlockTagTree) each).getContent().stream().map(t -> ((LiteralTree) t).getBody().getBody()).collect(Collectors.joining()); + String[] ans = body.split(":", 3); + builder.append("\n"); + builder.append(String.format("\n", escaped(ans[0]))); + builder.append(String.format("\n", escaped(ans[1]))); + if (!typeDecl) { + builder.append(String.format("\n", escaped(ans[2]))); + } + builder.append("\n"); + } + + builder.append("\n"); + builder.append("
NamespaceNameMixin selector
%s%s%s
\n"); + return builder.toString(); + } + + // I hate + private static String escaped(String original) { + StringBuilder builder = new StringBuilder(original.length()); + final int len = original.length(); + for (int i = 0; i < len; i++) { + char c = original.charAt(i); + if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') { + builder.append("&#").append((int) c).append(";"); + } else { + builder.append(c); + } + } + return builder.toString(); + } +} From 2a56056b5d9aa990917ae7f34806db5172c4712e Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 20 Jan 2021 16:44:30 -0600 Subject: [PATCH 13/30] Fixes #12 Signed-off-by: liach --- .../java/net/fabricmc/filament/mappingpoet/MethodBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index 0530d4200a..4ab40f9b79 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -288,7 +288,7 @@ public class MethodBuilder { } private void addExceptions() { - if (signature != null) { + if (signature != null && !signature.thrown.isEmpty()) { for (TypeName each : signature.thrown) { builder.addException(each); } From 89ef6f274a5985d90451a32d113112443f27dd70 Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 20 Jan 2021 16:58:45 -0600 Subject: [PATCH 14/30] This appears to fix #13 Signed-off-by: liach --- .../net/fabricmc/filament/mappingpoet/MappingsStore.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java index 40a61ac06c..bc01f5a104 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -154,8 +154,13 @@ public class MappingsStore { } MethodDef methodDef = found.getValue(); - addDoc(methodDef, addJavadoc); ClassDef owner = classes.get(found.getKey()); + if (!owner.getName(namespace).equals(methodEntry.getOwner())) { + addJavadoc.accept("{@inheritDoc}", new Object[0]); // todo try this bypass? + } else { + addDoc(methodDef, addJavadoc); + } + addJavadoc.accept("\n", new Object[0]); for (String namespace : namespaces) { String transformedName = methodDef.getName(namespace); From 598f6db83cf3d7923457ae5bf9fca7311b1bedfd Mon Sep 17 00:00:00 2001 From: liach Date: Mon, 22 Feb 2021 14:29:30 -0600 Subject: [PATCH 15/30] Fixes #14 --- .../filament/mappingpoet/MappingsStore.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java index bc01f5a104..cf87506c2e 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -25,7 +25,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; import java.util.function.Function; import net.fabricmc.mapping.tree.ClassDef; @@ -78,23 +77,23 @@ public class MappingsStore { return classDef != null ? classDef.getComment() : null; } - private void addDoc(Mapped element, BiConsumer addJavadoc) { + private void addDoc(Mapped element, DocAdder adder) { String doc = element.getComment(); if (doc != null) { - addJavadoc.accept(doc, new Object[0]); + adder.addJavadoc("$L", doc); } } - public void addClassDoc(BiConsumer addJavadoc, String className) { + public void addClassDoc(DocAdder adder, String className) { ClassDef classDef = classes.get(className); if (classDef == null) { return; } - addDoc(classDef, addJavadoc); - addJavadoc.accept("\n", new Object[0]); + addDoc(classDef, adder); + adder.addJavadoc("\n"); for (String namespace : namespaces) { String transformedName = classDef.getName(namespace); - addJavadoc.accept("@mapping {@literal $L:$L}\n", new Object[] {namespace, transformedName}); + adder.addJavadoc("@mapping {@literal $L:$L}\n", namespace, transformedName); } } @@ -104,7 +103,7 @@ public class MappingsStore { return fieldDef != null ? fieldDef.getComment() : null; } - public void addFieldDoc(BiConsumer addJavadoc, EntryTriple fieldEntry) { + public void addFieldDoc(DocAdder addJavadoc, EntryTriple fieldEntry) { FieldDef fieldDef = fields.get(fieldEntry); if (fieldDef == null) { return; @@ -112,11 +111,11 @@ public class MappingsStore { addDoc(fieldDef, addJavadoc); ClassDef owner = classes.get(fieldEntry.getOwner()); - addJavadoc.accept("\n", new Object[0]); + addJavadoc.addJavadoc("\n"); for (String namespace : namespaces) { String transformedName = fieldDef.getName(namespace); String mixinForm = "L" + owner.getName(namespace) + ";" + transformedName + ":" + fieldDef.getDescriptor(namespace); - addJavadoc.accept("@mapping {@literal $L:$L:$L}\n", new Object[] {namespace, transformedName, mixinForm}); + addJavadoc.addJavadoc("@mapping {@literal $L:$L:$L}\n", namespace, transformedName, mixinForm); } } @@ -147,7 +146,7 @@ public class MappingsStore { return null; } - public void addMethodDoc(BiConsumer addJavadoc, Function> superGetters, EntryTriple methodEntry) { + public void addMethodDoc(DocAdder adder, Function> superGetters, EntryTriple methodEntry) { Map.Entry found = searchMethod(superGetters, methodEntry); if (found == null) { return; @@ -156,16 +155,16 @@ public class MappingsStore { MethodDef methodDef = found.getValue(); ClassDef owner = classes.get(found.getKey()); if (!owner.getName(namespace).equals(methodEntry.getOwner())) { - addJavadoc.accept("{@inheritDoc}", new Object[0]); // todo try this bypass? + adder.addJavadoc("{@inheritDoc}"); } else { - addDoc(methodDef, addJavadoc); + addDoc(methodDef, adder); } - addJavadoc.accept("\n", new Object[0]); + adder.addJavadoc("\n"); for (String namespace : namespaces) { String transformedName = methodDef.getName(namespace); String mixinForm = "L" + owner.getName(namespace) + ";" + transformedName + methodDef.getDescriptor(namespace); - addJavadoc.accept("@mapping {@literal $L:$L:$L}\n", new Object[] {namespace, transformedName, mixinForm}); + adder.addJavadoc("@mapping {@literal $L:$L:$L}\n", namespace, transformedName, mixinForm); } } @@ -191,4 +190,8 @@ public class MappingsStore { methods.put(methodEntry, null); return null; } + + public interface DocAdder { + void addJavadoc(String format, Object... args); + } } From c45cd7ab433f118436acf92bbd82a844991838e4 Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 31 Mar 2021 21:27:03 -0500 Subject: [PATCH 16/30] Fixes #21, tries to fix #20 Signed-off-by: liach --- .../filament/mappingpoet/FieldBuilder.java | 34 +++++++++++++++---- .../fabricmc/filament/mappingpoet/Main.java | 3 +- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java index 3488138d5a..81fe104b9d 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -391,27 +391,47 @@ public class FieldBuilder { // fake initializers exclude fields from constant values switch (desc.charAt(0)) { case 'B': + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("(byte) $L", fieldNode.value).build(); + } + // fake initializer falls through case 'C': + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("(char) $L", fieldNode.value).build(); + } + // fake initializer falls through case 'D': + if (fieldNode.value instanceof Double) { + return CodeBlock.builder().add("$LD", fieldNode.value).build(); + } + // fake initializer falls through case 'I': + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("$L", fieldNode.value).build(); + } + // fake initializer falls through case 'J': + if (fieldNode.value instanceof Long) { + return CodeBlock.builder().add("$LL", fieldNode.value).build(); + } + // fake initializer falls through case 'S': - if (fieldNode.value != null) { - return CodeBlock.builder().add(String.valueOf(fieldNode.value)).build(); + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("(short) $L", fieldNode.value).build(); } return CodeBlock.builder().add("java.lang.Byte.parseByte(\"dummy\")").build(); case 'F': - if (fieldNode.value != null) { - return CodeBlock.builder().add(fieldNode.value + "f").build(); + if (fieldNode.value instanceof Float) { + return CodeBlock.builder().add("$LF", fieldNode.value).build(); } return CodeBlock.builder().add("java.lang.Float.parseFloat(\"dummy\")").build(); case 'Z': - if (fieldNode.value instanceof Number) { - return CodeBlock.builder().add(Boolean.toString(((Number) fieldNode.value).intValue() != 0)).build(); + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("$L", ((int) fieldNode.value) != 0).build(); } return CodeBlock.builder().add("java.lang.Boolean.parseBoolean(\"dummy\")").build(); } - if (fieldNode.value != null) { + if (desc.equals("Ljava/lang/String;") && fieldNode.value instanceof String) { return CodeBlock.builder().add("$S", fieldNode.value).build(); } return CodeBlock.builder().add(desc.equals("Ljava/lang/String;") ? "java.lang.String.valueOf(\"dummy\")" : "null").build(); diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 193542800d..4663c74d8f 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -95,7 +95,8 @@ public class Main { classes.values().stream() .filter(classBuilder -> !classBuilder.getClassName().contains("$")) .forEach(classBuilder -> { - JavaFile javaFile = JavaFile.builder(classBuilder.getClassName().replaceAll("/", ".").substring(0, classBuilder.getClassName().lastIndexOf("/")), classBuilder.build()) + int packageEnd = classBuilder.getClassName().lastIndexOf("/"); + JavaFile javaFile = JavaFile.builder(packageEnd == -1 ? "" : classBuilder.getClassName().substring(0, packageEnd).replaceAll("/", "."), classBuilder.build()) .build(); try { javaFile.writeTo(outputDirectory); From 957c0f5138db9a1c32c6dfa6e8afd4134ab35177 Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 31 Mar 2021 22:50:35 -0500 Subject: [PATCH 17/30] Better char literals Signed-off-by: liach --- .../filament/mappingpoet/FieldBuilder.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java index 81fe104b9d..5386aad7f2 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -397,7 +397,9 @@ public class FieldBuilder { // fake initializer falls through case 'C': if (fieldNode.value instanceof Integer) { - return CodeBlock.builder().add("(char) $L", fieldNode.value).build(); + int value = (int) fieldNode.value; + char c = (char) value; + return printChar(CodeBlock.builder(), c, value).build(); } // fake initializer falls through case 'D': @@ -437,6 +439,34 @@ public class FieldBuilder { return CodeBlock.builder().add(desc.equals("Ljava/lang/String;") ? "java.lang.String.valueOf(\"dummy\")" : "null").build(); } + + private static CodeBlock.Builder printChar(CodeBlock.Builder builder, char c, int value) { + if (!Character.isValidCodePoint(value) || !Character.isDefined(value)) { + return builder.add("(char) $L", value); + } + + // See https://docs.oracle.com/javase/specs/jls/se16/html/jls-3.html#jls-EscapeSequence + // ignore space or ", just use direct in those cases + switch (c) { + case '\b': + return builder.add("'\\b'"); + case '\t': + return builder.add("'\\t'"); + case '\n': + return builder.add("'\\n'"); + case '\f': + return builder.add("'\\f'"); + case '\r': + return builder.add("'\\r'"); + case '\'': + return builder.add("'\\''"); + case '\\': + return builder.add("'\\\\'"); + } + + return builder.add("'$L'", c); + } + private void addJavaDoc() { mappings.addFieldDoc(builder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); } From defbde19d83692a5beb4ee24d87bce8da3131ff3 Mon Sep 17 00:00:00 2001 From: liach Date: Thu, 13 May 2021 20:58:47 -0500 Subject: [PATCH 18/30] Fix redundant public modifier for inner classes, migrate to modern java Signed-off-by: liach --- .../filament/mappingpoet/ClassBuilder.java | 13 ++++++------ .../fabricmc/filament/mappingpoet/Main.java | 2 +- .../filament/mappingpoet/MethodBuilder.java | 10 +++++----- .../mappingpoet/signature/ClassSignature.java | 11 ++-------- .../signature/ClassStaticContext.java | 2 +- .../signature/MethodSignature.java | 14 +++---------- .../signature/PoetTypeSignatureWriter.java | 20 ++++++------------- 7 files changed, 25 insertions(+), 47 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index a4c13ff3e1..b687abf96f 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -146,15 +146,15 @@ public class ClassBuilder { builder = TypeSpec.enumBuilder(name); } else { builder = TypeSpec.classBuilder(name) - .superclass(signature.superclass); + .superclass(signature.superclass()); } - if (!signature.generics.isEmpty()) { - builder.addTypeVariables(signature.generics); + if (!signature.generics().isEmpty()) { + builder.addTypeVariables(signature.generics()); StringBuilder sb = new StringBuilder(); sb.append(classNode.name); sb.append("<"); - for (TypeVariableName each : signature.generics) { + for (TypeVariableName each : signature.generics()) { sb.append("T").append(each.name).append(";"); } sb.append(">"); @@ -170,7 +170,7 @@ public class ClassBuilder { return; } if (signature != null) { - builder.addSuperinterfaces(signature.superinterfaces); + builder.addSuperinterfaces(signature.superinterfaces()); return; } if (classNode.interfaces.isEmpty()) return; @@ -284,6 +284,7 @@ public class ClassBuilder { classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.PUBLIC); classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); } else { + classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access).getModifiers(classBuilder.enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS)); if (!Modifier.isStatic(innerClassNode.access)) { classBuilder.instanceInner = true; @@ -295,7 +296,7 @@ public class ClassBuilder { sb.append(this.receiverSignature).append("."); // like O. for O sb.append(innerClassNode.innerName); // append simple name - List innerClassGenerics = classBuilder.signature.generics; + List innerClassGenerics = classBuilder.signature.generics(); if (!innerClassGenerics.isEmpty()) { sb.append("<"); for (TypeVariableName each : innerClassGenerics) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 4663c74d8f..d07c56b2ff 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -166,7 +166,7 @@ public class Main { private static void scanInnerClasses(Map instanceInnerClasses, Path librariesDir) { try { - Files.walkFileTree(librariesDir, new SimpleFileVisitor() { + Files.walkFileTree(librariesDir, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (!file.getFileName().toString().endsWith(".jar")) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index 4ab40f9b79..8c3b1812b7 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -159,7 +159,7 @@ public class MethodBuilder { if (methodNode.signature != null) { signature = AnnotationAwareSignatures.parseMethodSignature(methodNode.signature, typeAnnotations, context); - builder.addTypeVariables(signature.generics); + builder.addTypeVariables(signature.generics()); } return builder; @@ -187,7 +187,7 @@ public class MethodBuilder { TypeName typeName; if (signature != null) { - typeName = signature.result; + typeName = signature.result(); } else { String returnDesc = methodNode.desc.substring(methodNode.desc.lastIndexOf(")") + 1); typeName = AnnotationAwareDescriptors.parseDesc(returnDesc, typeAnnotations.getBank(TypeReference.newTypeReference(TypeReference.METHOD_RETURN)), context); @@ -257,7 +257,7 @@ public class MethodBuilder { } index++; // consume '(' - Iterator signatureParamIterator = signature == null ? Collections.emptyIterator() : signature.parameters.iterator(); + Iterator signatureParamIterator = signature == null ? Collections.emptyIterator() : signature.parameters().iterator(); while (desc.charAt(index) != ')') { int oldIndex = index; Map.Entry parsedParam = FieldBuilder.parseType(desc, index); @@ -288,8 +288,8 @@ public class MethodBuilder { } private void addExceptions() { - if (signature != null && !signature.thrown.isEmpty()) { - for (TypeName each : signature.thrown) { + if (signature != null && !signature.thrown().isEmpty()) { + for (TypeName each : signature.thrown()) { builder.addException(each); } return; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java index e3b5df5e79..11b0004ffe 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java @@ -22,14 +22,7 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeVariableName; // no more a class signature but general super info about class -public final class ClassSignature { - public final List generics; - public final TypeName superclass; - public final List superinterfaces; +public record ClassSignature(List generics, TypeName superclass, + List superinterfaces) { - public ClassSignature(List generics, TypeName superclass, List superinterfaces) { - this.generics = generics; - this.superclass = superclass; - this.superinterfaces = superinterfaces; - } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java index 1ba5c49f85..c1933e5b72 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java @@ -27,7 +27,7 @@ public interface ClassStaticContext { /** * Returns if this class is an instance inner class. * - *

For example, a top-level class is not so. A static inner + *

For example, a top-level class is not so. A static nested * class, such as {@code Map.Entry}, is not as well.

* * @param internalName the JVM name of the class diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java index 20cfe160d1..69762419c3 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java @@ -21,16 +21,8 @@ import java.util.List; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeVariableName; -public final class MethodSignature { - public final List generics; - public final List parameters; - public final TypeName result; - public final List thrown; +public record MethodSignature(List generics, + List parameters, TypeName result, + List thrown) { - public MethodSignature(List generics, List parameters, TypeName result, List thrown) { - this.generics = generics; - this.parameters = parameters; - this.result = result; - this.thrown = thrown; - } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java index bf5014fc69..1d36b731e2 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java @@ -150,20 +150,12 @@ public final class PoetTypeSignatureWriter extends SignatureVisitor { private void collectLastTypeArgument() { if (activeTypeArgument != null) { TypeName hold = activeTypeArgument.compute(); - TypeName used; - switch (activeTypeArgumentKind) { - case SignatureVisitor.EXTENDS: - used = WildcardTypeName.subtypeOf(hold); - break; - case SignatureVisitor.SUPER: - used = WildcardTypeName.supertypeOf(hold); - break; - case SignatureVisitor.INSTANCEOF: - used = hold; - break; - default: - throw new IllegalStateException(String.format("Illegal type argument wildcard %s", activeTypeArgumentKind)); - } + TypeName used = switch (activeTypeArgumentKind) { + case SignatureVisitor.EXTENDS -> WildcardTypeName.subtypeOf(hold); + case SignatureVisitor.SUPER -> WildcardTypeName.supertypeOf(hold); + case SignatureVisitor.INSTANCEOF -> hold; + default -> throw new IllegalStateException(String.format("Illegal type argument wildcard %s", activeTypeArgumentKind)); + }; used = AnnotationAwareDescriptors.annotate(used, storage.advance(TypePath.TYPE_ARGUMENT, params.size())); params.addLast(used); From 099ddebbd0aafb423371855b4a69fab4a27ce597 Mon Sep 17 00:00:00 2001 From: liach Date: Sat, 18 Sep 2021 13:31:17 -0500 Subject: [PATCH 19/30] Preliminary work on getting records and sealed classes right Signed-off-by: liach --- .../filament/mappingpoet/ClassBuilder.java | 90 ++- .../filament/mappingpoet/FieldBuilder.java | 530 +++++++++--------- .../fabricmc/filament/mappingpoet/Main.java | 35 +- .../filament/mappingpoet/ModifierBuilder.java | 44 +- 4 files changed, 405 insertions(+), 294 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index b687abf96f..1a1d0b131f 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -16,34 +16,45 @@ package net.fabricmc.mappingpoet; -import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.function.Function; - import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.TypeReference; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.FieldNode; -import org.objectweb.asm.tree.InnerClassNode; -import org.objectweb.asm.tree.MethodNode; - import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; import net.fabricmc.mappingpoet.signature.ClassSignature; import net.fabricmc.mappingpoet.signature.ClassStaticContext; import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.TypeReference; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.InnerClassNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation; public class ClassBuilder { + static final Handle OBJ_MTH_BOOTSTRAP = new Handle( + Opcodes.H_INVOKESTATIC, + "java/lang/runtime/ObjectMethods", + "bootstrap", + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;", + false + ); private final MappingsStore mappings; private final ClassNode classNode; @@ -51,21 +62,24 @@ public class ClassBuilder { private final TypeSpec.Builder builder; private final List innerClasses = new ArrayList<>(); private final Function> superGetter; + private final Predicate sealChecker; private final ClassStaticContext context; private final ClassSignature signature; // not really signature private final TypeAnnotationMapping typeAnnotations; private boolean annotationClass; private boolean enumClass; + private boolean recordClass; private boolean instanceInner = false; // only nonnull if any class in the inner class chain creates a generic decl // omits L and ; private String receiverSignature; - public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function> superGetter, ClassStaticContext context) { + public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function> superGetter, Predicate sealChecker, ClassStaticContext context) { this.mappings = mappings; this.classNode = classNode; this.superGetter = superGetter; + this.sealChecker = sealChecker; this.context = context; this.typeAnnotations = setupAnnotations(); this.signature = setupSignature(); @@ -144,6 +158,9 @@ public class ClassBuilder { } else if (classNode.superName.equals("java/lang/Enum")) { enumClass = true; builder = TypeSpec.enumBuilder(name); + } else if (classNode.superName.equals("java/lang/Record")) { + recordClass = true; + builder = TypeSpec.recordBuilder(name); } else { builder = TypeSpec.classBuilder(name) .superclass(signature.superclass()); @@ -162,7 +179,10 @@ public class ClassBuilder { } return builder - .addModifiers(new ModifierBuilder(classNode.access).getModifiers(enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS)); + .addModifiers(new ModifierBuilder(classNode.access) + .checkUnseal(classNode, sealChecker) + .getModifiers(ModifierBuilder.getType(enumClass, recordClass)) + ); } private void addInterfaces() { @@ -197,6 +217,7 @@ public class ClassBuilder { private void addMethods() { if (classNode.methods == null) return; + methodsLoop: for (MethodNode method : classNode.methods) { if ((method.access & Opcodes.ACC_SYNTHETIC) != 0 || (method.access & Opcodes.ACC_MANDATED) != 0) { continue; @@ -217,6 +238,21 @@ public class ClassBuilder { formalParamStartIndex = 2; // 0 String 1 int } } + if (recordClass) { + // skip record sugars + if (method.name.equals("equals") && method.desc.equals("(Ljava/lang/Object;)Z") + || method.name.equals("toString") && method.desc.equals("()Ljava/lang/String;") + || method.name.equals("hashCode") && method.desc.equals("()I")) { + for (AbstractInsnNode insn : method.instructions) { + if (insn instanceof InvokeDynamicInsnNode indy + && indy.bsm.equals(OBJ_MTH_BOOTSTRAP) + && indy.name.equals(method.name)) + continue methodsLoop; + } + } + + // todo test component getters + } if (instanceInner) { if (method.name.equals("")) { formalParamStartIndex = 1; // 0 this$0 @@ -229,6 +265,19 @@ public class ClassBuilder { private void addFields() { if (classNode.fields == null) return; for (FieldNode field : classNode.fields) { + if (recordClass && !Modifier.isStatic(field.access)) { + if (!Modifier.isFinal(field.access) || !Modifier.isPrivate(field.access)) { + System.out.println("abnormal instance field " + field.name + " in record " + getClassName() + ", skipping"); + } else { + var fieldBuilder = new FieldBuilder(mappings, classNode, field, context); + var paramBuilder = ParameterSpec.builder(fieldBuilder.calculateType(), field.name); + fieldBuilder.addJavaDoc(paramBuilder); + fieldBuilder.addDirectAnnotations(paramBuilder); + builder.addRecordComponent(paramBuilder.build()); + } + + continue; + } if ((field.access & Opcodes.ACC_SYNTHETIC) != 0 || (field.access & Opcodes.ACC_MANDATED) != 0) { continue; // hide synthetic stuff } @@ -285,7 +334,10 @@ public class ClassBuilder { classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); } else { classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access - classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access).getModifiers(classBuilder.enumClass ? ModifierBuilder.Type.ENUM : ModifierBuilder.Type.CLASS)); + classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access) + .checkUnseal(classBuilder.classNode, sealChecker) + .getModifiers(ModifierBuilder.getType(classBuilder.enumClass, classBuilder.recordClass)) + ); if (!Modifier.isStatic(innerClassNode.access)) { classBuilder.instanceInner = true; } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java index 5386aad7f2..c069c6718f 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -16,20 +16,21 @@ package net.fabricmc.mappingpoet; -import java.util.AbstractMap; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ArrayTypeName; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; +import net.fabricmc.mapping.util.EntryTriple; +import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; +import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; +import net.fabricmc.mappingpoet.signature.ClassStaticContext; +import net.fabricmc.mappingpoet.signature.TypeAnnotationBank; +import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; +import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; @@ -38,19 +39,18 @@ import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; -import net.fabricmc.mapping.util.EntryTriple; -import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; -import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; -import net.fabricmc.mappingpoet.signature.ClassStaticContext; -import net.fabricmc.mappingpoet.signature.TypeAnnotationBank; -import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; -import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; +import java.util.AbstractMap; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; +import java.util.Map; public class FieldBuilder { private final MappingsStore mappings; private final ClassNode classNode; private final FieldNode fieldNode; - private final FieldSpec.Builder builder; + private final FieldSpec.Builder builder; // todo extract interface to build both record param/field easily private final TypeAnnotationMapping annotations; private final ClassStaticContext context; @@ -129,90 +129,90 @@ public class FieldBuilder { TypeName current; switch (desc.charAt(index)) { - case 'B': { - current = TypeName.BYTE; - index++; - break; - } - case 'C': { - current = TypeName.CHAR; - index++; - break; - } - case 'D': { - current = TypeName.DOUBLE; - index++; - break; - } - case 'F': { - current = TypeName.FLOAT; - index++; - break; - } - case 'I': { - current = TypeName.INT; - index++; - break; - } - case 'J': { - current = TypeName.LONG; - index++; - break; - } - case 'S': { - current = TypeName.SHORT; - index++; - break; - } - case 'Z': { - current = TypeName.BOOLEAN; - index++; - break; - } - case 'V': { - current = TypeName.VOID; - index++; - break; - } - case 'L': { - int classNameSeparator = index; - index++; - int nameStart = index; - ClassName currentClassName = null; - - char ch; - do { - ch = desc.charAt(index); - - if (ch == '$' || ch == ';') { - // collect class name - if (currentClassName == null) { - String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : ""; - String simpleName = desc.substring(classNameSeparator + 1, index); - currentClassName = ClassName.get(packageName, simpleName); - } else { - String simpleName = desc.substring(classNameSeparator + 1, index); - currentClassName = currentClassName.nestedClass(simpleName); - } - } - - if (ch == '/' || ch == '$') { - // Start of simple name - classNameSeparator = index; - } - + case 'B': { + current = TypeName.BYTE; index++; - } while (ch != ';'); - - if (currentClassName == null) { - throw invalidDesc(desc, index); + break; } + case 'C': { + current = TypeName.CHAR; + index++; + break; + } + case 'D': { + current = TypeName.DOUBLE; + index++; + break; + } + case 'F': { + current = TypeName.FLOAT; + index++; + break; + } + case 'I': { + current = TypeName.INT; + index++; + break; + } + case 'J': { + current = TypeName.LONG; + index++; + break; + } + case 'S': { + current = TypeName.SHORT; + index++; + break; + } + case 'Z': { + current = TypeName.BOOLEAN; + index++; + break; + } + case 'V': { + current = TypeName.VOID; + index++; + break; + } + case 'L': { + int classNameSeparator = index; + index++; + int nameStart = index; + ClassName currentClassName = null; - current = currentClassName; - break; - } - default: - throw invalidDesc(desc, index); + char ch; + do { + ch = desc.charAt(index); + + if (ch == '$' || ch == ';') { + // collect class name + if (currentClassName == null) { + String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : ""; + String simpleName = desc.substring(classNameSeparator + 1, index); + currentClassName = ClassName.get(packageName, simpleName); + } else { + String simpleName = desc.substring(classNameSeparator + 1, index); + currentClassName = currentClassName.nestedClass(simpleName); + } + } + + if (ch == '/' || ch == '$') { + // Start of simple name + classNameSeparator = index; + } + + index++; + } while (ch != ';'); + + if (currentClassName == null) { + throw invalidDesc(desc, index); + } + + current = currentClassName; + break; + } + default: + throw invalidDesc(desc, index); } for (int i = 0; i < arrayLevel; i++) { @@ -233,101 +233,101 @@ public class FieldBuilder { TypeName current; switch (desc.charAt(index)) { - case 'B': { - current = TypeName.BYTE; - index++; - break; - } - case 'C': { - current = TypeName.CHAR; - index++; - break; - } - case 'D': { - current = TypeName.DOUBLE; - index++; - break; - } - case 'F': { - current = TypeName.FLOAT; - index++; - break; - } - case 'I': { - current = TypeName.INT; - index++; - break; - } - case 'J': { - current = TypeName.LONG; - index++; - break; - } - case 'S': { - current = TypeName.SHORT; - index++; - break; - } - case 'Z': { - current = TypeName.BOOLEAN; - index++; - break; - } - case 'V': { - current = TypeName.VOID; - index++; - break; - } - case 'L': { - int classNameSeparator = index; - index++; - int nameStart = index; - ClassName currentClassName = null; - boolean instanceInner = false; + case 'B': { + current = TypeName.BYTE; + index++; + break; + } + case 'C': { + current = TypeName.CHAR; + index++; + break; + } + case 'D': { + current = TypeName.DOUBLE; + index++; + break; + } + case 'F': { + current = TypeName.FLOAT; + index++; + break; + } + case 'I': { + current = TypeName.INT; + index++; + break; + } + case 'J': { + current = TypeName.LONG; + index++; + break; + } + case 'S': { + current = TypeName.SHORT; + index++; + break; + } + case 'Z': { + current = TypeName.BOOLEAN; + index++; + break; + } + case 'V': { + current = TypeName.VOID; + index++; + break; + } + case 'L': { + int classNameSeparator = index; + index++; + int nameStart = index; + ClassName currentClassName = null; + boolean instanceInner = false; - char ch; - do { - ch = desc.charAt(index); + char ch; + do { + ch = desc.charAt(index); - if (ch == '$' || ch == ';') { - // collect class name - if (currentClassName == null) { - String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : ""; - String simpleName = desc.substring(classNameSeparator + 1, index); - currentClassName = ClassName.get(packageName, simpleName); - } else { - String simpleName = desc.substring(classNameSeparator + 1, index); + if (ch == '$' || ch == ';') { + // collect class name + if (currentClassName == null) { + String packageName = nameStart < classNameSeparator ? desc.substring(nameStart, classNameSeparator).replace('/', '.') : ""; + String simpleName = desc.substring(classNameSeparator + 1, index); + currentClassName = ClassName.get(packageName, simpleName); + } else { + String simpleName = desc.substring(classNameSeparator + 1, index); - if (!instanceInner && context.isInstanceInner(desc.substring(nameStart, index))) { - instanceInner = true; - } + if (!instanceInner && context.isInstanceInner(desc.substring(nameStart, index))) { + instanceInner = true; + } - currentClassName = currentClassName.nestedClass(simpleName); + currentClassName = currentClassName.nestedClass(simpleName); - if (instanceInner) { - currentClassName = AnnotationAwareDescriptors.annotate(currentClassName, annotations); - annotations = annotations.advance(TypePath.INNER_TYPE, 0); + if (instanceInner) { + currentClassName = AnnotationAwareDescriptors.annotate(currentClassName, annotations); + annotations = annotations.advance(TypePath.INNER_TYPE, 0); + } } } + + if (ch == '/' || ch == '$') { + // Start of simple name + classNameSeparator = index; + } + + index++; + } while (ch != ';'); + + if (currentClassName == null) { + throw invalidDesc(desc, index); } - if (ch == '/' || ch == '$') { - // Start of simple name - classNameSeparator = index; - } - - index++; - } while (ch != ';'); - - if (currentClassName == null) { - throw invalidDesc(desc, index); + current = currentClassName; + break; } - - current = currentClassName; - break; - } - default: - throw invalidDesc(desc, index); + default: + throw invalidDesc(desc, index); } while (!arrayAnnos.isEmpty()) { @@ -348,24 +348,24 @@ public class FieldBuilder { @Deprecated // use typeFromDesc, non-recursive public static TypeName getFieldType(String desc) { switch (desc) { - case "B": - return TypeName.BYTE; - case "C": - return TypeName.CHAR; - case "S": - return TypeName.SHORT; - case "Z": - return TypeName.BOOLEAN; - case "I": - return TypeName.INT; - case "J": - return TypeName.LONG; - case "F": - return TypeName.FLOAT; - case "D": - return TypeName.DOUBLE; - case "V": - return TypeName.VOID; + case "B": + return TypeName.BYTE; + case "C": + return TypeName.CHAR; + case "S": + return TypeName.SHORT; + case "Z": + return TypeName.BOOLEAN; + case "I": + return TypeName.INT; + case "J": + return TypeName.LONG; + case "F": + return TypeName.FLOAT; + case "D": + return TypeName.DOUBLE; + case "V": + return TypeName.VOID; } if (desc.startsWith("[")) { return ArrayTypeName.of(getFieldType(desc.substring(1))); @@ -390,48 +390,48 @@ public class FieldBuilder { private CodeBlock makeInitializer(String desc) { // fake initializers exclude fields from constant values switch (desc.charAt(0)) { - case 'B': - if (fieldNode.value instanceof Integer) { - return CodeBlock.builder().add("(byte) $L", fieldNode.value).build(); - } - // fake initializer falls through - case 'C': - if (fieldNode.value instanceof Integer) { - int value = (int) fieldNode.value; - char c = (char) value; - return printChar(CodeBlock.builder(), c, value).build(); - } - // fake initializer falls through - case 'D': - if (fieldNode.value instanceof Double) { - return CodeBlock.builder().add("$LD", fieldNode.value).build(); - } - // fake initializer falls through - case 'I': - if (fieldNode.value instanceof Integer) { - return CodeBlock.builder().add("$L", fieldNode.value).build(); - } - // fake initializer falls through - case 'J': - if (fieldNode.value instanceof Long) { - return CodeBlock.builder().add("$LL", fieldNode.value).build(); - } - // fake initializer falls through - case 'S': - if (fieldNode.value instanceof Integer) { - return CodeBlock.builder().add("(short) $L", fieldNode.value).build(); - } - return CodeBlock.builder().add("java.lang.Byte.parseByte(\"dummy\")").build(); - case 'F': - if (fieldNode.value instanceof Float) { - return CodeBlock.builder().add("$LF", fieldNode.value).build(); - } - return CodeBlock.builder().add("java.lang.Float.parseFloat(\"dummy\")").build(); - case 'Z': - if (fieldNode.value instanceof Integer) { - return CodeBlock.builder().add("$L", ((int) fieldNode.value) != 0).build(); - } - return CodeBlock.builder().add("java.lang.Boolean.parseBoolean(\"dummy\")").build(); + case 'B': + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("(byte) $L", fieldNode.value).build(); + } + // fake initializer falls through + case 'C': + if (fieldNode.value instanceof Integer) { + int value = (int) fieldNode.value; + char c = (char) value; + return printChar(CodeBlock.builder(), c, value).build(); + } + // fake initializer falls through + case 'D': + if (fieldNode.value instanceof Double) { + return CodeBlock.builder().add("$LD", fieldNode.value).build(); + } + // fake initializer falls through + case 'I': + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("$L", fieldNode.value).build(); + } + // fake initializer falls through + case 'J': + if (fieldNode.value instanceof Long) { + return CodeBlock.builder().add("$LL", fieldNode.value).build(); + } + // fake initializer falls through + case 'S': + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("(short) $L", fieldNode.value).build(); + } + return CodeBlock.builder().add("java.lang.Byte.parseByte(\"dummy\")").build(); + case 'F': + if (fieldNode.value instanceof Float) { + return CodeBlock.builder().add("$LF", fieldNode.value).build(); + } + return CodeBlock.builder().add("java.lang.Float.parseFloat(\"dummy\")").build(); + case 'Z': + if (fieldNode.value instanceof Integer) { + return CodeBlock.builder().add("$L", ((int) fieldNode.value) != 0).build(); + } + return CodeBlock.builder().add("java.lang.Boolean.parseBoolean(\"dummy\")").build(); } if (desc.equals("Ljava/lang/String;") && fieldNode.value instanceof String) { return CodeBlock.builder().add("$S", fieldNode.value).build(); @@ -448,20 +448,20 @@ public class FieldBuilder { // See https://docs.oracle.com/javase/specs/jls/se16/html/jls-3.html#jls-EscapeSequence // ignore space or ", just use direct in those cases switch (c) { - case '\b': - return builder.add("'\\b'"); - case '\t': - return builder.add("'\\t'"); - case '\n': - return builder.add("'\\n'"); - case '\f': - return builder.add("'\\f'"); - case '\r': - return builder.add("'\\r'"); - case '\'': - return builder.add("'\\''"); - case '\\': - return builder.add("'\\\\'"); + case '\b': + return builder.add("'\\b'"); + case '\t': + return builder.add("'\\t'"); + case '\n': + return builder.add("'\\n'"); + case '\f': + return builder.add("'\\f'"); + case '\r': + return builder.add("'\\r'"); + case '\'': + return builder.add("'\\''"); + case '\\': + return builder.add("'\\\\'"); } return builder.add("'$L'", c); @@ -471,6 +471,10 @@ public class FieldBuilder { mappings.addFieldDoc(builder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); } + void addJavaDoc(ParameterSpec.Builder paramBuilder) { + mappings.addFieldDoc(paramBuilder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); + } + private void addDirectAnnotations() { addDirectAnnotations(fieldNode.invisibleAnnotations); addDirectAnnotations(fieldNode.visibleAnnotations); @@ -485,7 +489,21 @@ public class FieldBuilder { } } - private TypeName calculateType() { + void addDirectAnnotations(ParameterSpec.Builder paramBuilder) { + addDirectAnnotations(paramBuilder, fieldNode.invisibleAnnotations); + addDirectAnnotations(paramBuilder, fieldNode.visibleAnnotations); + } + + private void addDirectAnnotations(ParameterSpec.Builder paramBuilder, List regularAnnotations) { + if (regularAnnotations == null) { + return; + } + for (AnnotationNode annotation : regularAnnotations) { + paramBuilder.addAnnotation(parseAnnotation(annotation)); + } + } + + TypeName calculateType() { if (fieldNode.signature != null) { return AnnotationAwareSignatures.parseFieldSignature(fieldNode.signature, annotations, context); } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index d07c56b2ff..31a83cf49c 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -16,6 +16,14 @@ package net.fabricmc.mappingpoet; +import com.squareup.javapoet.JavaFile; +import net.fabricmc.mappingpoet.signature.ClassStaticContext; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InnerClassNode; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -33,22 +41,16 @@ import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import com.squareup.javapoet.JavaFile; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InnerClassNode; - -import net.fabricmc.mappingpoet.signature.ClassStaticContext; - public class Main { public static void main(String[] args) { @@ -90,7 +92,7 @@ public class Main { public static void generate(Path mappings, Path inputJar, Path outputDirectory, Path librariesDir) { final MappingsStore mapping = new MappingsStore(mappings); Map classes = new HashMap<>(); - forEachClass(inputJar, (superGetter, classNode, context) -> writeClass(mapping, classNode, classes, superGetter, context), librariesDir); + forEachClass(inputJar, (superGetter, classNode, context, sealChecker) -> writeClass(mapping, classNode, classes, superGetter, sealChecker, context), librariesDir); classes.values().stream() .filter(classBuilder -> !classBuilder.getClassName().contains("$")) @@ -111,6 +113,7 @@ public class Main { private static void forEachClass(Path jar, ClassNodeConsumer classNodeConsumer, Path librariesDir) { List classes = new ArrayList<>(); Map> supers = new HashMap<>(); + Set sealedClasses = new HashSet<>(); // their subclsses/impls need non-sealed modifier Map instanceInnerClasses = new ConcurrentHashMap<>(); @@ -149,6 +152,10 @@ public class Main { } } + if (classNode.permittedSubclasses != null) { + sealedClasses.add(classNode.name); + } + classes.add(classNode); } } @@ -161,7 +168,7 @@ public class Main { ClassStaticContext innerClassContext = new InnerClassStats(instanceInnerClasses); Function> superGetter = k -> supers.getOrDefault(k, Collections.emptyList()); - classes.forEach(node -> classNodeConsumer.accept(superGetter, node, innerClassContext)); + classes.forEach(node -> classNodeConsumer.accept(superGetter, node, innerClassContext, sealedClasses::contains)); } private static void scanInnerClasses(Map instanceInnerClasses, Path librariesDir) { @@ -218,7 +225,7 @@ public class Main { return ch >= '0' && ch <= '9'; } - private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses, Function> superGetter, ClassStaticContext context) { + private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses, Function> superGetter, Predicate sealChecker, ClassStaticContext context) { String name = classNode.name; { //Block anonymous class and their nested classes @@ -232,7 +239,7 @@ public class Main { } } - ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter, context); + ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter, sealChecker, context); if (name.contains("$")) { String parentClass = name.substring(0, name.lastIndexOf("$")); @@ -249,7 +256,7 @@ public class Main { @FunctionalInterface private interface ClassNodeConsumer { - void accept(Function> superGetter, ClassNode node, ClassStaticContext staticContext); + void accept(Function> superGetter, ClassNode node, ClassStaticContext staticContext, Predicate sealedChecker); } private static final class InnerClassStats implements ClassStaticContext { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java index dad1d81da9..dcc7c79ddb 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java @@ -16,19 +16,44 @@ package net.fabricmc.mappingpoet; -import java.util.ArrayList; -import java.util.List; +import org.objectweb.asm.tree.ClassNode; import javax.lang.model.element.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; public class ModifierBuilder { private final int access; + private boolean needsUnseal; public ModifierBuilder(int access) { this.access = access; } + public ModifierBuilder checkUnseal(ClassNode node, Predicate sealChecker) { + if (java.lang.reflect.Modifier.isFinal(node.access)) { + return this; + } + + if (node.interfaces != null) { + for (String itf : node.interfaces) { + if (sealChecker.test(itf)) { + needsUnseal = true; + return this; + } + } + } + + if (node.superName != null && sealChecker.test(node.superName)) { + needsUnseal = true; + } + + + return this; + } + public Modifier[] getModifiers(Type type) { List modifiers = new ArrayList<>(); @@ -36,7 +61,7 @@ public class ModifierBuilder { if (java.lang.reflect.Modifier.isFinal(access)) { modifiers.add(Modifier.FINAL); } - return modifiers.toArray(new Modifier[] {}); + return modifiers.toArray(new Modifier[]{}); } if (java.lang.reflect.Modifier.isPublic(access)) { @@ -57,7 +82,7 @@ public class ModifierBuilder { modifiers.add(Modifier.DEFAULT); } - if (java.lang.reflect.Modifier.isFinal(access) && type != Type.ENUM) { + if (java.lang.reflect.Modifier.isFinal(access) && type != Type.ENUM && type != Type.RECORD) { modifiers.add(Modifier.FINAL); } if (java.lang.reflect.Modifier.isTransient(access) && type == Type.FIELD) { @@ -76,12 +101,21 @@ public class ModifierBuilder { modifiers.add(Modifier.STRICTFP); } - return modifiers.toArray(new Modifier[] {}); + if (needsUnseal && type == Type.CLASS) { + modifiers.add(Modifier.NON_SEALED); + } + + return modifiers.toArray(new Modifier[]{}); + } + + public static Type getType(boolean enumType, boolean recordType) { + return enumType ? Type.ENUM : recordType ? Type.RECORD : Type.CLASS; } public enum Type { CLASS, ENUM, + RECORD, METHOD, FIELD, PARAM From 2e901ccb6606006538f445b47f115a6f9db4eaf7 Mon Sep 17 00:00:00 2001 From: liach Date: Thu, 23 Sep 2021 15:24:50 -0500 Subject: [PATCH 20/30] Consider package private final fields in record as components for proguard Signed-off-by: liach --- .../java/net/fabricmc/filament/mappingpoet/ClassBuilder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 1a1d0b131f..b81c85fe99 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -266,7 +266,8 @@ public class ClassBuilder { if (classNode.fields == null) return; for (FieldNode field : classNode.fields) { if (recordClass && !Modifier.isStatic(field.access)) { - if (!Modifier.isFinal(field.access) || !Modifier.isPrivate(field.access)) { + // proguard elevates record field access for direct record field gets + if (!Modifier.isFinal(field.access) || Modifier.isProtected(field.access) || Modifier.isPublic(field.access)) { System.out.println("abnormal instance field " + field.name + " in record " + getClassName() + ", skipping"); } else { var fieldBuilder = new FieldBuilder(mappings, classNode, field, context); From 6cf42ac7b3bdb28b4a812389fd80ac365229c155 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 27 Oct 2021 19:21:36 +0100 Subject: [PATCH 21/30] Remove default modifier from private interface methods. --- .../java/net/fabricmc/filament/mappingpoet/MethodBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index 8c3b1812b7..496844848f 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -153,7 +153,7 @@ public class MethodBuilder { private MethodSpec.Builder createBuilder() { MethodSpec.Builder builder = MethodSpec.methodBuilder(methodNode.name) .addModifiers(new ModifierBuilder(methodNode.access).getModifiers(ModifierBuilder.Type.METHOD)); - if (methodNode.name.equals("") || !java.lang.reflect.Modifier.isInterface(classNode.access)) { + if (methodNode.name.equals("") || !java.lang.reflect.Modifier.isInterface(classNode.access) || java.lang.reflect.Modifier.isPrivate(methodNode.access)) { builder.modifiers.remove(Modifier.DEFAULT); } From 652c0f2c2ea26698ed1ee5727a6b459ebf40b27f Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 27 Oct 2021 19:45:02 +0100 Subject: [PATCH 22/30] Skip over anonymous records to fix generation failure. @liach you might be better suited to take a better look at this. Can we move inline classes/records into their method? If so would the javadoc work for that? --- .../net/fabricmc/filament/mappingpoet/ClassBuilder.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index b81c85fe99..5989657a4b 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -324,6 +324,12 @@ public class ClassBuilder { if (classNode.innerClasses != null) { for (InnerClassNode node : classNode.innerClasses) { if (node.name.equals(classBuilder.classNode.name)) { + if (node.outerName == null && classBuilder.recordClass) { + // Inline anonymous record. + // TODO look into moving this into the method that defines it. + return; + } + innerClassNode = node; break; } From e3e655d390f79d28a7e1ab44b8b2284199f69284 Mon Sep 17 00:00:00 2001 From: liach Date: Wed, 17 Nov 2021 17:34:41 -0600 Subject: [PATCH 23/30] This looks like why it was problematic Signed-off-by: liach --- .../fabricmc/filament/mappingpoet/ClassBuilder.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 5989657a4b..98d0a53aef 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -324,22 +324,22 @@ public class ClassBuilder { if (classNode.innerClasses != null) { for (InnerClassNode node : classNode.innerClasses) { if (node.name.equals(classBuilder.classNode.name)) { - if (node.outerName == null && classBuilder.recordClass) { - // Inline anonymous record. - // TODO look into moving this into the method that defines it. - return; - } - innerClassNode = node; break; } } } + if (innerClassNode == null) { // fallback classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.PUBLIC); classBuilder.builder.addModifiers(javax.lang.model.element.Modifier.STATIC); } else { + if (innerClassNode.outerName == null) { + // skip local classes and records, which have null outerName + return; + } + classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access) .checkUnseal(classBuilder.classNode, sealChecker) From d8216422ff7cddf0b9f350ceabd713d33b47c456 Mon Sep 17 00:00:00 2001 From: liach Date: Thu, 18 Nov 2021 16:55:59 -0600 Subject: [PATCH 24/30] Add taglet for putting up record stripping warnings Signed-off-by: liach --- .../filament/mappingpoet/ClassBuilder.java | 5 ++ .../mappingpoet/jd/RecordWarningTaglet.java | 55 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 98d0a53aef..11382cc388 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -317,6 +317,11 @@ public class ClassBuilder { private void addJavaDoc() { mappings.addClassDoc(builder::addJavadoc, classNode.name); + + // record warning text block + if (recordClass) { + builder.addJavadoc("@fabricRecordWarning\n"); + } } public void addInnerClass(ClassBuilder classBuilder) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java new file mode 100644 index 0000000000..69b90bbe8b --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java @@ -0,0 +1,55 @@ +package net.fabricmc.mappingpoet.jd; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.lang.model.element.Element; + +import com.sun.source.doctree.DocTree; +import jdk.javadoc.doclet.Taglet; + +public final class RecordWarningTaglet implements Taglet { + + private final String warningText; + + public RecordWarningTaglet() { + try (InputStream stream = RecordWarningTaglet.class.getResourceAsStream("/record_warning.txt")) { + if (stream == null) { + throw new IllegalStateException("Cannot find record_warning.txt file!"); + } + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { + this.warningText = reader.lines().collect(Collectors.joining("\n")); + } + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + + @Override + public Set getAllowedLocations() { + return Set.of(Location.TYPE); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return "fabricRecordWarning"; + } + + @Override + public String toString(List tags, Element element) { + return this.warningText; + } +} From 702b76fcd11b839a4e1c0a5ad29ab6e1cd81ab7a Mon Sep 17 00:00:00 2001 From: liach Date: Thu, 18 Nov 2021 16:58:47 -0600 Subject: [PATCH 25/30] license --- .../mappingpoet/jd/RecordWarningTaglet.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java index 69b90bbe8b..82f5402991 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java @@ -1,3 +1,19 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package net.fabricmc.mappingpoet.jd; import java.io.BufferedReader; From 7618954eed49a53438c5aa9fed4ef489097f98bb Mon Sep 17 00:00:00 2001 From: liach Date: Mon, 22 Nov 2021 18:09:17 -0600 Subject: [PATCH 26/30] Revert "license" This reverts commit 702b76fcd11b839a4e1c0a5ad29ab6e1cd81ab7a. Revert "Add taglet for putting up record stripping warnings" This reverts commit d8216422ff7cddf0b9f350ceabd713d33b47c456. --- .../filament/mappingpoet/ClassBuilder.java | 5 -- .../mappingpoet/jd/RecordWarningTaglet.java | 71 ------------------- 2 files changed, 76 deletions(-) delete mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 11382cc388..98d0a53aef 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -317,11 +317,6 @@ public class ClassBuilder { private void addJavaDoc() { mappings.addClassDoc(builder::addJavadoc, classNode.name); - - // record warning text block - if (recordClass) { - builder.addJavadoc("@fabricRecordWarning\n"); - } } public void addInnerClass(ClassBuilder classBuilder) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java deleted file mode 100644 index 82f5402991..0000000000 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/RecordWarningTaglet.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2020 FabricMC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.fabricmc.mappingpoet.jd; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.lang.model.element.Element; - -import com.sun.source.doctree.DocTree; -import jdk.javadoc.doclet.Taglet; - -public final class RecordWarningTaglet implements Taglet { - - private final String warningText; - - public RecordWarningTaglet() { - try (InputStream stream = RecordWarningTaglet.class.getResourceAsStream("/record_warning.txt")) { - if (stream == null) { - throw new IllegalStateException("Cannot find record_warning.txt file!"); - } - - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { - this.warningText = reader.lines().collect(Collectors.joining("\n")); - } - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - - @Override - public Set getAllowedLocations() { - return Set.of(Location.TYPE); - } - - @Override - public boolean isInlineTag() { - return false; - } - - @Override - public String getName() { - return "fabricRecordWarning"; - } - - @Override - public String toString(List tags, Element element) { - return this.warningText; - } -} From f07ce4727af85c6c8f030611273cacb72d73aab0 Mon Sep 17 00:00:00 2001 From: liach Date: Sun, 20 Mar 2022 14:57:08 -0500 Subject: [PATCH 27/30] Do not dump local class by checking EnclosingMethod attribute --- .../java/net/fabricmc/filament/mappingpoet/ClassBuilder.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 98d0a53aef..51a92cc917 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -340,6 +340,11 @@ public class ClassBuilder { return; } + if (classBuilder.classNode.outerMethod != null) { + // local class per EnclosingMethod attribute + return; + } + classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access) .checkUnseal(classBuilder.classNode, sealChecker) From 40a0d19adec824d0fbb23f4ee0fa0a758e86275e Mon Sep 17 00:00:00 2001 From: liach Date: Fri, 5 May 2023 17:04:07 -0500 Subject: [PATCH 28/30] 0.4.0 Migrate to mapping io, use spotless still need to work on inner class handling, need named jar inspection first --- .../filament/mappingpoet/ClassBuilder.java | 29 ++-- .../filament/mappingpoet/Environment.java | 54 ++++++ .../filament/mappingpoet/FieldBuilder.java | 8 +- .../fabricmc/filament/mappingpoet/Main.java | 68 ++++---- .../filament/mappingpoet/MappingsStore.java | 163 +++++++----------- .../filament/mappingpoet/MethodBuilder.java | 61 +++---- .../filament/mappingpoet/ModifierBuilder.java | 10 +- .../filament/mappingpoet/Signatures.java | 1 - .../mappingpoet/jd/MappingTaglet.java | 1 - .../signature/AnnotationAwareDescriptors.java | 1 - .../signature/AnnotationAwareSignatures.java | 1 - .../mappingpoet/signature/ClassSignature.java | 1 - .../signature/ClassStaticContext.java | 1 - .../signature/MethodSignature.java | 1 - .../PoetClassMethodSignatureVisitor.java | 23 ++- .../signature/PoetTypeSignatureWriter.java | 9 +- .../signature/TypeAnnotationBank.java | 1 - .../signature/TypeAnnotationMapping.java | 1 - .../signature/TypeAnnotationStorage.java | 1 - 19 files changed, 203 insertions(+), 232 deletions(-) create mode 100644 filament/src/main/java/net/fabricmc/filament/mappingpoet/Environment.java diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 51a92cc917..0aa9bf3386 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet; import com.squareup.javapoet.AnnotationSpec; @@ -24,7 +23,6 @@ import com.squareup.javapoet.TypeVariableName; import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; import net.fabricmc.mappingpoet.signature.ClassSignature; -import net.fabricmc.mappingpoet.signature.ClassStaticContext; import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; import org.objectweb.asm.Handle; @@ -40,10 +38,7 @@ import org.objectweb.asm.tree.MethodNode; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation; @@ -61,9 +56,7 @@ public class ClassBuilder { private final TypeSpec.Builder builder; private final List innerClasses = new ArrayList<>(); - private final Function> superGetter; - private final Predicate sealChecker; - private final ClassStaticContext context; + private final Environment environment; private final ClassSignature signature; // not really signature private final TypeAnnotationMapping typeAnnotations; @@ -75,12 +68,10 @@ public class ClassBuilder { // omits L and ; private String receiverSignature; - public ClassBuilder(MappingsStore mappings, ClassNode classNode, Function> superGetter, Predicate sealChecker, ClassStaticContext context) { + public ClassBuilder(MappingsStore mappings, ClassNode classNode, Environment environment) { this.mappings = mappings; this.classNode = classNode; - this.superGetter = superGetter; - this.sealChecker = sealChecker; - this.context = context; + this.environment = environment; this.typeAnnotations = setupAnnotations(); this.signature = setupSignature(); this.builder = setupBuilder(); @@ -141,8 +132,8 @@ public class ClassBuilder { private ClassSignature setupSignature() { return classNode.signature == null ? - AnnotationAwareDescriptors.parse(classNode.superName, classNode.interfaces, typeAnnotations, context) : - AnnotationAwareSignatures.parseClassSignature(classNode.signature, typeAnnotations, context); + AnnotationAwareDescriptors.parse(classNode.superName, classNode.interfaces, typeAnnotations, environment) : + AnnotationAwareSignatures.parseClassSignature(classNode.signature, typeAnnotations, environment); } private TypeSpec.Builder setupBuilder() { @@ -180,7 +171,7 @@ public class ClassBuilder { return builder .addModifiers(new ModifierBuilder(classNode.access) - .checkUnseal(classNode, sealChecker) + .checkUnseal(classNode, environment) .getModifiers(ModifierBuilder.getType(enumClass, recordClass)) ); } @@ -258,7 +249,7 @@ public class ClassBuilder { formalParamStartIndex = 1; // 0 this$0 } } - builder.addMethod(new MethodBuilder(mappings, classNode, method, superGetter, context, receiverSignature, formalParamStartIndex).build()); + builder.addMethod(new MethodBuilder(mappings, classNode, method, environment, receiverSignature, formalParamStartIndex).build()); } } @@ -270,7 +261,7 @@ public class ClassBuilder { if (!Modifier.isFinal(field.access) || Modifier.isProtected(field.access) || Modifier.isPublic(field.access)) { System.out.println("abnormal instance field " + field.name + " in record " + getClassName() + ", skipping"); } else { - var fieldBuilder = new FieldBuilder(mappings, classNode, field, context); + var fieldBuilder = new FieldBuilder(mappings, classNode, field, environment); var paramBuilder = ParameterSpec.builder(fieldBuilder.calculateType(), field.name); fieldBuilder.addJavaDoc(paramBuilder); fieldBuilder.addDirectAnnotations(paramBuilder); @@ -283,7 +274,7 @@ public class ClassBuilder { continue; // hide synthetic stuff } if ((field.access & Opcodes.ACC_ENUM) == 0) { - builder.addField(new FieldBuilder(mappings, classNode, field, context).build()); + builder.addField(new FieldBuilder(mappings, classNode, field, environment).build()); } else { TypeSpec.Builder enumBuilder = TypeSpec.anonymousClassBuilder(""); // jd @@ -347,7 +338,7 @@ public class ClassBuilder { classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access) - .checkUnseal(classBuilder.classNode, sealChecker) + .checkUnseal(classBuilder.classNode, environment) .getModifiers(ModifierBuilder.getType(classBuilder.enumClass, classBuilder.recordClass)) ); if (!Modifier.isStatic(innerClassNode.access)) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Environment.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Environment.java new file mode 100644 index 0000000000..5355634b2f --- /dev/null +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Environment.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.fabricmc.mappingpoet; + +import com.squareup.javapoet.ClassName; +import net.fabricmc.mappingpoet.signature.ClassStaticContext; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Represents an overall runtime environment, knows all inner class, + * super class, etc. information. + */ +public record Environment( + Map> superTypes, + Set sealedClasses, + // declaring classes keep track of namable inner classes + // and local/anon classes in whole codebase + Map declaringClasses +) implements ClassStaticContext { + public record NestedClassInfo(String declaringClass, boolean instanceInner, String simpleName) { + // two strings are nullable + } + + public record ClassNamePointer(String simple, String outerClass) { + public ClassName toClassName(ClassName outerClassName) { + if (simple == null) + return null; + + return outerClassName.nestedClass(simple); + } + } + + @Override + public boolean isInstanceInner(String internalName) { + var info = declaringClasses.get(internalName); + return info != null && info.declaringClass != null && info.instanceInner; + } +} diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java index c069c6718f..1bf98c93a6 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/FieldBuilder.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet; import com.squareup.javapoet.AnnotationSpec; @@ -24,7 +23,6 @@ import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; -import net.fabricmc.mapping.util.EntryTriple; import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; import net.fabricmc.mappingpoet.signature.ClassStaticContext; @@ -69,7 +67,7 @@ public class FieldBuilder { } static void addFieldJavaDoc(TypeSpec.Builder enumBuilder, MappingsStore mappings, ClassNode classNode, FieldNode fieldNode) { - mappings.addFieldDoc(enumBuilder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); + mappings.addFieldDoc(enumBuilder::addJavadoc, classNode.name, fieldNode.name, fieldNode.desc); } public static AnnotationSpec parseAnnotation(AnnotationNode annotation) { @@ -468,11 +466,11 @@ public class FieldBuilder { } private void addJavaDoc() { - mappings.addFieldDoc(builder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); + mappings.addFieldDoc(builder::addJavadoc, classNode.name, fieldNode.name, fieldNode.desc); } void addJavaDoc(ParameterSpec.Builder paramBuilder) { - mappings.addFieldDoc(paramBuilder::addJavadoc, new EntryTriple(classNode.name, fieldNode.name, fieldNode.desc)); + mappings.addFieldDoc(paramBuilder::addJavadoc, classNode.name, fieldNode.name, fieldNode.desc); } private void addDirectAnnotations() { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 31a83cf49c..83c614bd68 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet; +import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; +import net.fabricmc.mappingpoet.Environment.ClassNamePointer; +import net.fabricmc.mappingpoet.Environment.NestedClassInfo; import net.fabricmc.mappingpoet.signature.ClassStaticContext; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -37,7 +39,6 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; @@ -46,8 +47,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; -import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -65,15 +64,16 @@ public class Main { try { if (Files.exists(outputDirectory)) { - Files.walk(outputDirectory) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); + try (var stream = Files.walk(outputDirectory)) { + stream.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } } Files.createDirectories(outputDirectory); } catch (IOException e) { - e.printStackTrace(); + throw new UncheckedIOException(e); } if (!Files.exists(mappings)) { @@ -92,7 +92,7 @@ public class Main { public static void generate(Path mappings, Path inputJar, Path outputDirectory, Path librariesDir) { final MappingsStore mapping = new MappingsStore(mappings); Map classes = new HashMap<>(); - forEachClass(inputJar, (superGetter, classNode, context, sealChecker) -> writeClass(mapping, classNode, classes, superGetter, sealChecker, context), librariesDir); + forEachClass(inputJar, (classNode, environment) -> writeClass(mapping, classNode, classes, environment), librariesDir); classes.values().stream() .filter(classBuilder -> !classBuilder.getClassName().contains("$")) @@ -115,10 +115,11 @@ public class Main { Map> supers = new HashMap<>(); Set sealedClasses = new HashSet<>(); // their subclsses/impls need non-sealed modifier - Map instanceInnerClasses = new ConcurrentHashMap<>(); + Map nestedClasses = new ConcurrentHashMap<>(); + Map classNames = new ConcurrentHashMap<>(); if (librariesDir != null) { - scanInnerClasses(instanceInnerClasses, librariesDir); + scanNestedClasses(classNames, nestedClasses, librariesDir); } try (final JarFile jarFile = new JarFile(jar.toFile())) { @@ -148,7 +149,10 @@ public class Main { if (classNode.innerClasses != null) { for (InnerClassNode e : classNode.innerClasses) { - instanceInnerClasses.put(e.name, !Modifier.isStatic(e.access)); + if (e.outerName != null) { + // null -> declared in method/initializer + nestedClasses.put(e.name, new NestedClassInfo(e.outerName, !Modifier.isStatic(e.access), e.innerName)); + } } } @@ -166,12 +170,10 @@ public class Main { //Sort all the classes making sure that inner classes come after the parent classes classes.sort(Comparator.comparing(o -> o.name)); - ClassStaticContext innerClassContext = new InnerClassStats(instanceInnerClasses); - Function> superGetter = k -> supers.getOrDefault(k, Collections.emptyList()); - classes.forEach(node -> classNodeConsumer.accept(superGetter, node, innerClassContext, sealedClasses::contains)); + classes.forEach(node -> classNodeConsumer.accept(node, new Environment(supers, sealedClasses, nestedClasses))); } - private static void scanInnerClasses(Map instanceInnerClasses, Path librariesDir) { + private static void scanNestedClasses(Map classNames, Map instanceInnerClasses, Path librariesDir) { try { Files.walkFileTree(librariesDir, new SimpleFileVisitor<>() { @Override @@ -194,8 +196,11 @@ public class Main { ClassReader reader = new ClassReader(is); reader.accept(new ClassVisitor(Opcodes.ASM8) { @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - instanceInnerClasses.put(name, !Modifier.isStatic(access)); + public void visitInnerClass(String name, String outerName, String simpleName, int access) { + instanceInnerClasses.put(name, new Environment.NestedClassInfo(outerName, !Modifier.isStatic(access), simpleName)); + if (outerName != null) { + classNames.put(name, new ClassNamePointer(simpleName, outerName)); + } } }, ClassReader.SKIP_CODE); } @@ -225,7 +230,8 @@ public class Main { return ch >= '0' && ch <= '9'; } - private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses, Function> superGetter, Predicate sealChecker, ClassStaticContext context) { + private static void writeClass(MappingsStore mappings, ClassNode classNode, Map existingClasses, Environment environment) { + // TODO make sure named jar has valid InnerClasses, use that info instead String name = classNode.name; { //Block anonymous class and their nested classes @@ -239,7 +245,8 @@ public class Main { } } - ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, superGetter, sealChecker, context); + // TODO: ensure InnerClasses is remapped, and create ClassName from parent class name + ClassBuilder classBuilder = new ClassBuilder(mappings, classNode, environment); if (name.contains("$")) { String parentClass = name.substring(0, name.lastIndexOf("$")); @@ -256,23 +263,6 @@ public class Main { @FunctionalInterface private interface ClassNodeConsumer { - void accept(Function> superGetter, ClassNode node, ClassStaticContext staticContext, Predicate sealedChecker); - } - - private static final class InnerClassStats implements ClassStaticContext { - final Map instanceInnerClasses; - - InnerClassStats(Map instanceInnerClasses) { - this.instanceInnerClasses = instanceInnerClasses; - } - - @Override - public boolean isInstanceInner(String internalName) { - if (internalName.indexOf('$') == -1) { - return false; // heuristics - } - - return instanceInnerClasses.computeIfAbsent(internalName, Main::isInstanceInnerOnClasspath); - } + void accept(ClassNode node, Environment environment); } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java index cf87506c2e..84abd70f96 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MappingsStore.java @@ -13,71 +13,46 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet; -import java.io.BufferedReader; +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.format.MappingFormat; +import net.fabricmc.mappingio.tree.MappingTreeView; +import net.fabricmc.mappingio.tree.MappingTreeView.ClassMappingView; +import net.fabricmc.mappingio.tree.MappingTreeView.ElementMappingView; +import net.fabricmc.mappingio.tree.MappingTreeView.MethodMappingView; +import net.fabricmc.mappingio.tree.MemoryMappingTree; + import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; -import java.util.AbstractMap; -import java.util.Collection; -import java.util.HashMap; +import java.util.AbstractMap.SimpleImmutableEntry; import java.util.List; import java.util.Map; -import java.util.function.Function; -import net.fabricmc.mapping.tree.ClassDef; -import net.fabricmc.mapping.tree.FieldDef; -import net.fabricmc.mapping.tree.Mapped; -import net.fabricmc.mapping.tree.MethodDef; -import net.fabricmc.mapping.tree.TinyMappingFactory; -import net.fabricmc.mapping.tree.TinyTree; -import net.fabricmc.mapping.util.EntryTriple; +import static net.fabricmc.mappingio.tree.MappingTreeView.SRC_NAMESPACE_ID; //Taken from loom public class MappingsStore { - private final Map classes = new HashMap<>(); - private final Map fields = new HashMap<>(); - private final Map> methods = new HashMap<>(); - - private final String namespace = "named"; - private final List namespaces; + private final MappingTreeView tree; + private final int maxNamespace; public MappingsStore(Path tinyFile) { - final TinyTree mappings = readMappings(tinyFile); - - namespaces = mappings.getMetadata().getNamespaces(); - - for (ClassDef classDef : mappings.getClasses()) { - final String className = classDef.getName(namespace); - classes.put(className, classDef); - - for (FieldDef fieldDef : classDef.getFields()) { - fields.put(new EntryTriple(className, fieldDef.getName(namespace), fieldDef.getDescriptor(namespace)), fieldDef); - } - - for (MethodDef methodDef : classDef.getMethods()) { - methods.put(new EntryTriple(className, methodDef.getName(namespace), methodDef.getDescriptor(namespace)), new AbstractMap.SimpleImmutableEntry<>(className, methodDef)); - } - } + this.tree = readMappings(tinyFile); + this.maxNamespace = tree.getMaxNamespaceId(); } - private static TinyTree readMappings(Path input) { - try (BufferedReader reader = Files.newBufferedReader(input)) { - return TinyMappingFactory.loadWithDetection(reader); + private static MappingTreeView readMappings(Path input) { + var tree = new MemoryMappingTree(); + try { + MappingReader.read(input, MappingFormat.TINY_2, new MappingSourceNsSwitch(tree, "named")); } catch (IOException e) { throw new RuntimeException("Failed to read mappings", e); } + return tree; } - @Deprecated - public String getClassDoc(String className) { - ClassDef classDef = classes.get(className); - return classDef != null ? classDef.getComment() : null; - } - - private void addDoc(Mapped element, DocAdder adder) { + private void addDoc(ElementMappingView element, DocAdder adder) { String doc = element.getComment(); if (doc != null) { adder.addJavadoc("$L", doc); @@ -85,109 +60,95 @@ public class MappingsStore { } public void addClassDoc(DocAdder adder, String className) { - ClassDef classDef = classes.get(className); + var classDef = tree.getClass(className); if (classDef == null) { return; } addDoc(classDef, adder); adder.addJavadoc("\n"); - for (String namespace : namespaces) { - String transformedName = classDef.getName(namespace); - adder.addJavadoc("@mapping {@literal $L:$L}\n", namespace, transformedName); + for (int id = SRC_NAMESPACE_ID; id < maxNamespace; id++) { + String transformedName = classDef.getName(id); + adder.addJavadoc("@mapping {@literal $L:$L}\n", tree.getNamespaceName(id), transformedName); } } - @Deprecated - public String getFieldDoc(EntryTriple fieldEntry) { - FieldDef fieldDef = fields.get(fieldEntry); - return fieldDef != null ? fieldDef.getComment() : null; - } + public void addFieldDoc(DocAdder addJavadoc, String owner, String name, String desc) { + var classDef = tree.getClass(owner); + if (classDef == null) { + return; + } - public void addFieldDoc(DocAdder addJavadoc, EntryTriple fieldEntry) { - FieldDef fieldDef = fields.get(fieldEntry); + var fieldDef = classDef.getField(name, desc); if (fieldDef == null) { return; } addDoc(fieldDef, addJavadoc); - ClassDef owner = classes.get(fieldEntry.getOwner()); addJavadoc.addJavadoc("\n"); - for (String namespace : namespaces) { - String transformedName = fieldDef.getName(namespace); - String mixinForm = "L" + owner.getName(namespace) + ";" + transformedName + ":" + fieldDef.getDescriptor(namespace); - addJavadoc.addJavadoc("@mapping {@literal $L:$L:$L}\n", namespace, transformedName, mixinForm); + for (int id = SRC_NAMESPACE_ID; id < maxNamespace; id++) { + String transformedName = fieldDef.getName(id); + String mixinForm = "L" + classDef.getName(id) + ";" + transformedName + ":" + fieldDef.getDesc(id); + addJavadoc.addJavadoc("@mapping {@literal $L:$L:$L}\n", tree.getNamespaceName(id), transformedName, mixinForm); } } - public Map.Entry getParamNameAndDoc(Function> superGetters, EntryTriple methodEntry, int index) { - Map.Entry found = searchMethod(superGetters, methodEntry); + public Map.Entry getParamNameAndDoc(Environment environment, String owner, String name, String desc, int index) { + var found = searchMethod(environment, owner, name, desc); if (found != null) { - MethodDef methodDef = found.getValue(); - if (methodDef.getParameters().isEmpty()) { + var methodDef = found.getValue(); + if (methodDef.getArgs().isEmpty()) { return null; } - return methodDef.getParameters().stream() - .filter(param -> param.getLocalVariableIndex() == index) - .map(param -> new AbstractMap.SimpleImmutableEntry<>(param.getName(namespace), param.getComment())) + return methodDef.getArgs().stream() + .filter(param -> param.getLvIndex() == index) + // Map.entry() is null-hostile + .map(param -> new SimpleImmutableEntry<>(param.getSrcName(), param.getComment())) .findFirst() .orElse(null); } return null; } - @Deprecated - public String getMethodDoc(Function> superGetters, EntryTriple methodEntry) { - Map.Entry methodDef = searchMethod(superGetters, methodEntry); - - if (methodDef != null) { - return methodDef.getValue().getComment(); // comment doc handled separately by javapoet - } - - return null; - } - - public void addMethodDoc(DocAdder adder, Function> superGetters, EntryTriple methodEntry) { - Map.Entry found = searchMethod(superGetters, methodEntry); + public void addMethodDoc(DocAdder adder, Environment environment, String owner, String name, String desc) { + var found = searchMethod(environment, owner, name, desc); if (found == null) { return; } - MethodDef methodDef = found.getValue(); - ClassDef owner = classes.get(found.getKey()); - if (!owner.getName(namespace).equals(methodEntry.getOwner())) { + var methodDef = found.getValue(); + var ownerDef = found.getKey(); + if (!ownerDef.equals(methodDef.getOwner())) { adder.addJavadoc("{@inheritDoc}"); } else { addDoc(methodDef, adder); } adder.addJavadoc("\n"); - for (String namespace : namespaces) { - String transformedName = methodDef.getName(namespace); - String mixinForm = "L" + owner.getName(namespace) + ";" + transformedName + methodDef.getDescriptor(namespace); - adder.addJavadoc("@mapping {@literal $L:$L:$L}\n", namespace, transformedName, mixinForm); + for (int id = SRC_NAMESPACE_ID; id < maxNamespace; id++) { + String transformedName = methodDef.getName(id); + String mixinForm = "L" + ownerDef.getName(id) + ";" + transformedName + methodDef.getDesc(id); + adder.addJavadoc("@mapping {@literal $L:$L:$L}\n", tree.getNamespaceName(id), transformedName, mixinForm); } } - private Map.Entry searchMethod(Function> superGetters, EntryTriple methodEntry) { - String className = methodEntry.getOwner(); - if (!classes.containsKey(className)) { + private Map.Entry searchMethod(Environment environment, String owner, String name, String desc) { + var classDef = tree.getClass(owner); + + if (classDef == null) return null; - } - if (methods.containsKey(methodEntry)) { - return methods.get(methodEntry); // Nullable! - } + var methodDef = classDef.getMethod(name, desc); + if (methodDef != null) + return Map.entry(methodDef.getOwner(), methodDef); - for (String superName : superGetters.apply(className)) { - EntryTriple triple = new EntryTriple(superName, methodEntry.getName(), methodEntry.getDescriptor()); - Map.Entry ret = searchMethod(superGetters, triple); + + for (String superName : environment.superTypes().getOrDefault(owner, List.of())) { + var ret = searchMethod(environment, superName, name, desc); if (ret != null) { - methods.put(triple, ret); return ret; } } - methods.put(methodEntry, null); return null; } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java index 496844848f..afe2bc3faf 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/MethodBuilder.java @@ -13,38 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.TypeName; +import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; +import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; +import net.fabricmc.mappingpoet.signature.MethodSignature; +import net.fabricmc.mappingpoet.signature.TypeAnnotationBank; +import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; +import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; +import org.objectweb.asm.TypeReference; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import javax.lang.model.element.Modifier; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; - -import javax.lang.model.element.Modifier; - -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.ParameterSpec; -import com.squareup.javapoet.TypeName; -import org.objectweb.asm.TypeReference; -import org.objectweb.asm.tree.AnnotationNode; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.MethodNode; - -import net.fabricmc.mapping.util.EntryTriple; -import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; -import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; -import net.fabricmc.mappingpoet.signature.ClassStaticContext; -import net.fabricmc.mappingpoet.signature.MethodSignature; -import net.fabricmc.mappingpoet.signature.TypeAnnotationBank; -import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; -import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; public class MethodBuilder { private static final Set RESERVED_KEYWORDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( @@ -58,20 +51,18 @@ public class MethodBuilder { private final ClassNode classNode; private final MethodNode methodNode; private final MethodSpec.Builder builder; - private final Function> superGetter; + private final Environment environment; private final int formalParamStartIndex; - private final ClassStaticContext context; private final String receiverSignature; private final TypeAnnotationMapping typeAnnotations; private MethodSignature signature; - public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode, Function> superGetter, ClassStaticContext context, String receiverSignature, int formalParamStartIndex) { + public MethodBuilder(MappingsStore mappings, ClassNode classNode, MethodNode methodNode, Environment environment, String receiverSignature, int formalParamStartIndex) { this.mappings = mappings; this.classNode = classNode; this.methodNode = methodNode; - this.superGetter = superGetter; - this.context = context; + this.environment = environment; this.receiverSignature = receiverSignature; this.formalParamStartIndex = formalParamStartIndex; @@ -158,7 +149,7 @@ public class MethodBuilder { } if (methodNode.signature != null) { - signature = AnnotationAwareSignatures.parseMethodSignature(methodNode.signature, typeAnnotations, context); + signature = AnnotationAwareSignatures.parseMethodSignature(methodNode.signature, typeAnnotations, environment); builder.addTypeVariables(signature.generics()); } @@ -190,7 +181,7 @@ public class MethodBuilder { typeName = signature.result(); } else { String returnDesc = methodNode.desc.substring(methodNode.desc.lastIndexOf(")") + 1); - typeName = AnnotationAwareDescriptors.parseDesc(returnDesc, typeAnnotations.getBank(TypeReference.newTypeReference(TypeReference.METHOD_RETURN)), context); + typeName = AnnotationAwareDescriptors.parseDesc(returnDesc, typeAnnotations.getBank(TypeReference.newTypeReference(TypeReference.METHOD_RETURN)), environment); } builder.returns(typeName); @@ -215,7 +206,7 @@ public class MethodBuilder { ParameterSpec.Builder receiverBuilder; // only instance inner class ctor can have receivers if (methodNode.name.equals("")) { - TypeName annotatedReceiver = AnnotationAwareSignatures.parseSignature("L" + receiverSignature.substring(0, receiverSignature.lastIndexOf('.')) + ";", receiverAnnos, context); + TypeName annotatedReceiver = AnnotationAwareSignatures.parseSignature("L" + receiverSignature.substring(0, receiverSignature.lastIndexOf('.')) + ";", receiverAnnos, environment); // vulnerable heuristics String simpleNameChain = classNode.name.substring(classNode.name.lastIndexOf('/') + 1); int part1 = simpleNameChain.lastIndexOf('$'); // def exists @@ -223,7 +214,7 @@ public class MethodBuilder { String usedName = simpleNameChain.substring(part2 + 1, part1); receiverBuilder = ParameterSpec.builder(annotatedReceiver, usedName + ".this"); } else { - TypeName annotatedReceiver = AnnotationAwareSignatures.parseSignature("L" + receiverSignature + ";", receiverAnnos, context); + TypeName annotatedReceiver = AnnotationAwareSignatures.parseSignature("L" + receiverSignature + ";", receiverAnnos, environment); receiverBuilder = ParameterSpec.builder(annotatedReceiver, "this"); } // receiver param cannot have its jd/param anno except type use anno @@ -269,9 +260,9 @@ public class MethodBuilder { if (signatureParamIterator.hasNext()) { parsedType = signatureParamIterator.next(); } else { - parsedType = AnnotationAwareDescriptors.parseDesc(desc.substring(oldIndex, index), typeAnnotations.getBank(TypeReference.newFormalParameterReference(paramIndex - formalParamStartIndex)), context); + parsedType = AnnotationAwareDescriptors.parseDesc(desc.substring(oldIndex, index), typeAnnotations.getBank(TypeReference.newFormalParameterReference(paramIndex - formalParamStartIndex)), environment); } - paramTypes.add(new ParamType(mappings.getParamNameAndDoc(superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc), slot), parsedType, usedParamNames, slot)); + paramTypes.add(new ParamType(mappings.getParamNameAndDoc(environment, classNode.name, methodNode.name, methodNode.desc, slot), parsedType, usedParamNames, slot)); } slot++; if (nonAnnotatedParsedType.equals(TypeName.DOUBLE) || nonAnnotatedParsedType.equals(TypeName.LONG)) { @@ -298,14 +289,14 @@ public class MethodBuilder { if (exceptions != null) { int index = 0; for (String internalName : exceptions) { - builder.addException(AnnotationAwareDescriptors.parseType(internalName, typeAnnotations.getBank(TypeReference.newExceptionReference(index)), context)); + builder.addException(AnnotationAwareDescriptors.parseType(internalName, typeAnnotations.getBank(TypeReference.newExceptionReference(index)), environment)); index++; } } } private void addJavaDoc() { - mappings.addMethodDoc(builder::addJavadoc, superGetter, new EntryTriple(classNode.name, methodNode.name, methodNode.desc)); + mappings.addMethodDoc(builder::addJavadoc, environment, classNode.name, methodNode.name, methodNode.desc); } public MethodSpec build() { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java index dcc7c79ddb..926ee94895 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ModifierBuilder.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet; import org.objectweb.asm.tree.ClassNode; @@ -21,7 +20,6 @@ import org.objectweb.asm.tree.ClassNode; import javax.lang.model.element.Modifier; import java.util.ArrayList; import java.util.List; -import java.util.function.Predicate; public class ModifierBuilder { @@ -32,21 +30,21 @@ public class ModifierBuilder { this.access = access; } - public ModifierBuilder checkUnseal(ClassNode node, Predicate sealChecker) { + public ModifierBuilder checkUnseal(ClassNode node, Environment env) { if (java.lang.reflect.Modifier.isFinal(node.access)) { return this; } if (node.interfaces != null) { for (String itf : node.interfaces) { - if (sealChecker.test(itf)) { + if (env.sealedClasses().contains(itf)) { needsUnseal = true; return this; } } } - if (node.superName != null && sealChecker.test(node.superName)) { + if (node.superName != null && env.sealedClasses().contains(node.superName)) { needsUnseal = true; } @@ -98,7 +96,7 @@ public class ModifierBuilder { modifiers.add(Modifier.NATIVE); } if (java.lang.reflect.Modifier.isStrict(access)) { - modifiers.add(Modifier.STRICTFP); + modifiers.add(Modifier.STRICTFP); // obsolete as of Java 17 } if (needsUnseal && type == Type.CLASS) { diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java index a4f1e82f6b..c2b996879c 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Signatures.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet; import java.util.AbstractMap; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java index b2f07bdb7c..08bc6a36d7 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/jd/MappingTaglet.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.jd; import java.util.EnumSet; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java index decf8ae66f..c845c8d656 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareDescriptors.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; import java.util.AbstractMap; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java index c6572d68bb..d762dfc0a5 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/AnnotationAwareSignatures.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; import com.squareup.javapoet.TypeName; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java index 11b0004ffe..ab15942dcc 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassSignature.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; import java.util.List; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java index c1933e5b72..22e0536280 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/ClassStaticContext.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; /** diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java index 69762419c3..bea2f7222e 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/MethodSignature.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; import java.util.List; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java index 1c6056dc79..eec5be758d 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; -import java.util.LinkedList; +import java.util.ArrayList; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeVariableName; @@ -29,20 +28,20 @@ public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { private final TypeAnnotationMapping mapping; private final ClassStaticContext context; private final boolean forClass; - LinkedList generics = new LinkedList<>(); + ArrayList generics = new ArrayList<>(); // collecting generic String currentGenericName; - LinkedList currentGenericBounds = new LinkedList<>(); + ArrayList currentGenericBounds = new ArrayList<>(); // bound for each generic PoetTypeSignatureWriter pendingLowerBound; // classes usage - LinkedList superTypes = new LinkedList<>(); + ArrayList superTypes = new ArrayList<>(); PoetTypeSignatureWriter pendingSupertype; // methods usage - LinkedList params = new LinkedList<>(); - LinkedList throwables = new LinkedList<>(); + ArrayList params = new ArrayList<>(); + ArrayList throwables = new ArrayList<>(); PoetTypeSignatureWriter pendingSlot; TypeName returnType; @@ -84,7 +83,7 @@ public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { private void collectLowerBound() { if (pendingLowerBound != null) { - currentGenericBounds.addLast(pendingLowerBound.compute()); + currentGenericBounds.add(pendingLowerBound.compute()); pendingLowerBound = null; } } @@ -113,7 +112,7 @@ public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { private void collectSupertype() { if (pendingSupertype != null) { TypeName simple = pendingSupertype.compute(); - superTypes.addLast(simple); + superTypes.add(simple); pendingSupertype = null; } @@ -139,7 +138,7 @@ public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { public ClassSignature collectClass() { collectSupertype(); - TypeName superclass = superTypes.removeFirst(); + TypeName superclass = superTypes.remove(0); return new ClassSignature(generics, superclass, superTypes); } @@ -148,7 +147,7 @@ public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { private void collectParam() { if (pendingSlot != null) { TypeName slot = pendingSlot.compute(); - params.addLast(slot); + params.add(slot); pendingSlot = null; } @@ -159,7 +158,7 @@ public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { if (returnType == null) { returnType = pendingSlot.compute(); } else { - throwables.addLast(pendingSlot.compute()); + throwables.add(pendingSlot.compute()); } pendingSlot = null; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java index 1d36b731e2..ab8bff2dc5 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.Map; import java.util.Objects; @@ -42,7 +41,7 @@ import net.fabricmc.mappingpoet.Signatures; public final class PoetTypeSignatureWriter extends SignatureVisitor { private final ClassStaticContext context; - private final LinkedList params = new LinkedList<>(); + private final ArrayList params = new ArrayList<>(); private /* NonNull */ TypeAnnotationBank storage; // mutable private TypeName result; // array @@ -158,7 +157,7 @@ public final class PoetTypeSignatureWriter extends SignatureVisitor { }; used = AnnotationAwareDescriptors.annotate(used, storage.advance(TypePath.TYPE_ARGUMENT, params.size())); - params.addLast(used); + params.add(used); activeTypeArgument = null; activeTypeArgumentKind = 0; @@ -172,7 +171,7 @@ public final class PoetTypeSignatureWriter extends SignatureVisitor { TypeName used = WildcardTypeName.subtypeOf(TypeName.OBJECT); used = AnnotationAwareDescriptors.annotate(used, storage.advance(TypePath.TYPE_ARGUMENT, params.size())); - params.addLast(used); + params.add(used); } // (? extends/ ? super)? ClassType like in Consumer, diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java index 3b24907c2b..2a26517d47 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationBank.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; import java.util.Collections; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java index f6caaaee05..340cd25e16 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationMapping.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; import org.objectweb.asm.TypeReference; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java index 14af548b4d..c1322661e0 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/TypeAnnotationStorage.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.fabricmc.mappingpoet.signature; import java.util.ArrayList; From 8b0f70e572421a99457f55948d7c97dbaffe1c28 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 27 Mar 2024 16:57:10 +0000 Subject: [PATCH 29/30] Update ASM --- .../src/main/java/net/fabricmc/filament/mappingpoet/Main.java | 2 +- .../mappingpoet/signature/PoetClassMethodSignatureVisitor.java | 2 +- .../filament/mappingpoet/signature/PoetTypeSignatureWriter.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 83c614bd68..62c9e12b22 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -194,7 +194,7 @@ public class Main { try (InputStream is = jarFile.getInputStream(entry)) { ClassReader reader = new ClassReader(is); - reader.accept(new ClassVisitor(Opcodes.ASM8) { + reader.accept(new ClassVisitor(Opcodes.ASM9) { @Override public void visitInnerClass(String name, String outerName, String simpleName, int access) { instanceInnerClasses.put(name, new Environment.NestedClassInfo(outerName, !Modifier.isStatic(access), simpleName)); diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java index eec5be758d..a65c6ae575 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetClassMethodSignatureVisitor.java @@ -46,7 +46,7 @@ public final class PoetClassMethodSignatureVisitor extends SignatureVisitor { TypeName returnType; public PoetClassMethodSignatureVisitor(TypeAnnotationMapping mapping, ClassStaticContext context, boolean forClass) { - super(Opcodes.ASM8); + super(Opcodes.ASM9); this.mapping = mapping; this.context = context; this.forClass = forClass; diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java index ab8bff2dc5..c4e920b3aa 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/signature/PoetTypeSignatureWriter.java @@ -54,7 +54,7 @@ public final class PoetTypeSignatureWriter extends SignatureVisitor { private PoetTypeSignatureWriter activeTypeArgument; public PoetTypeSignatureWriter(TypeAnnotationBank storage, ClassStaticContext context) { - super(Opcodes.ASM8); + super(Opcodes.ASM9); this.storage = storage; this.context = context; } From 7efbd4534e45492990482208b8f8061e6f233b9b Mon Sep 17 00:00:00 2001 From: Player Date: Wed, 15 May 2024 13:40:29 -0400 Subject: [PATCH 30/30] Improve debug output, update javapoet (#32) * Update gradle * Fix test eclipse java compiler compatbility * Always dump class name on errors, remove some streams, reformat * Bump javapoet * Update javapoet --- .../filament/mappingpoet/ClassBuilder.java | 71 ++++++++++++------- .../fabricmc/filament/mappingpoet/Main.java | 53 +++++++------- 2 files changed, 74 insertions(+), 50 deletions(-) diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java index 0aa9bf3386..36771913e7 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/ClassBuilder.java @@ -15,16 +15,17 @@ */ package net.fabricmc.mappingpoet; +import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; -import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; -import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; -import net.fabricmc.mappingpoet.signature.ClassSignature; -import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; -import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; import org.objectweb.asm.Handle; import org.objectweb.asm.Opcodes; import org.objectweb.asm.TypeReference; @@ -36,11 +37,11 @@ import org.objectweb.asm.tree.InnerClassNode; import org.objectweb.asm.tree.InvokeDynamicInsnNode; import org.objectweb.asm.tree.MethodNode; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -import static net.fabricmc.mappingpoet.FieldBuilder.parseAnnotation; +import net.fabricmc.mappingpoet.signature.AnnotationAwareDescriptors; +import net.fabricmc.mappingpoet.signature.AnnotationAwareSignatures; +import net.fabricmc.mappingpoet.signature.ClassSignature; +import net.fabricmc.mappingpoet.signature.TypeAnnotationMapping; +import net.fabricmc.mappingpoet.signature.TypeAnnotationStorage; public class ClassBuilder { static final Handle OBJ_MTH_BOOTSTRAP = new Handle( @@ -48,8 +49,7 @@ public class ClassBuilder { "java/lang/runtime/ObjectMethods", "bootstrap", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;", - false - ); + false); private final MappingsStore mappings; private final ClassNode classNode; @@ -131,14 +131,17 @@ public class ClassBuilder { } private ClassSignature setupSignature() { - return classNode.signature == null ? - AnnotationAwareDescriptors.parse(classNode.superName, classNode.interfaces, typeAnnotations, environment) : - AnnotationAwareSignatures.parseClassSignature(classNode.signature, typeAnnotations, environment); + if (classNode.signature == null) { + return AnnotationAwareDescriptors.parse(classNode.superName, classNode.interfaces, typeAnnotations, environment); + } else { + return AnnotationAwareSignatures.parseClassSignature(classNode.signature, typeAnnotations, environment); + } } private TypeSpec.Builder setupBuilder() { TypeSpec.Builder builder; ClassName name = parseInternalName(classNode.name); // no type anno here + if (Modifier.isInterface(classNode.access)) { if (classNode.interfaces.size() == 1 && classNode.interfaces.get(0).equals("java/lang/annotation/Annotation")) { builder = TypeSpec.annotationBuilder(name); @@ -172,18 +175,19 @@ public class ClassBuilder { return builder .addModifiers(new ModifierBuilder(classNode.access) .checkUnseal(classNode, environment) - .getModifiers(ModifierBuilder.getType(enumClass, recordClass)) - ); + .getModifiers(ModifierBuilder.getType(enumClass, recordClass))); } private void addInterfaces() { if (annotationClass) { return; } + if (signature != null) { builder.addSuperinterfaces(signature.superinterfaces()); return; } + if (classNode.interfaces.isEmpty()) return; for (String iFace : classNode.interfaces) { @@ -201,6 +205,7 @@ public class ClassBuilder { if (regularAnnotations == null) { return; } + for (AnnotationNode annotation : regularAnnotations) { builder.addAnnotation(parseAnnotation(annotation)); } @@ -208,27 +213,33 @@ public class ClassBuilder { private void addMethods() { if (classNode.methods == null) return; - methodsLoop: - for (MethodNode method : classNode.methods) { + + methodsLoop: for (MethodNode method : classNode.methods) { if ((method.access & Opcodes.ACC_SYNTHETIC) != 0 || (method.access & Opcodes.ACC_MANDATED) != 0) { continue; } + if (method.name.equals("")) { continue; } + int formalParamStartIndex = 0; + if (enumClass) { // Skip enum sugar methods if (method.name.equals("values") && method.desc.equals("()[L" + classNode.name + ";")) { continue; } + if (method.name.equals("valueOf") && method.desc.equals("(Ljava/lang/String;)L" + classNode.name + ";")) { continue; } + if (method.name.equals("")) { formalParamStartIndex = 2; // 0 String 1 int } } + if (recordClass) { // skip record sugars if (method.name.equals("equals") && method.desc.equals("(Ljava/lang/Object;)Z") @@ -244,17 +255,20 @@ public class ClassBuilder { // todo test component getters } + if (instanceInner) { if (method.name.equals("")) { formalParamStartIndex = 1; // 0 this$0 } } + builder.addMethod(new MethodBuilder(mappings, classNode, method, environment, receiverSignature, formalParamStartIndex).build()); } } private void addFields() { if (classNode.fields == null) return; + for (FieldNode field : classNode.fields) { if (recordClass && !Modifier.isStatic(field.access)) { // proguard elevates record field access for direct record field gets @@ -270,9 +284,11 @@ public class ClassBuilder { continue; } + if ((field.access & Opcodes.ACC_SYNTHETIC) != 0 || (field.access & Opcodes.ACC_MANDATED) != 0) { continue; // hide synthetic stuff } + if ((field.access & Opcodes.ACC_ENUM) == 0) { builder.addField(new FieldBuilder(mappings, classNode, field, environment).build()); } else { @@ -288,6 +304,7 @@ public class ClassBuilder { .add(field.visibleTypeAnnotations) .build().getBank(TypeReference.newTypeReference(TypeReference.FIELD)) .getCurrentAnnotations(); + if (!annotations.isEmpty()) { enumBuilder.addAnnotations(annotations); // no custom paths for annotations rip } @@ -301,6 +318,7 @@ public class ClassBuilder { if (regularAnnotations == null) { return; } + for (AnnotationNode annotation : regularAnnotations) { builder.addAnnotation(parseAnnotation(annotation)); } @@ -312,6 +330,7 @@ public class ClassBuilder { public void addInnerClass(ClassBuilder classBuilder) { InnerClassNode innerClassNode = null; + if (classNode.innerClasses != null) { for (InnerClassNode node : classNode.innerClasses) { if (node.name.equals(classBuilder.classNode.name)) { @@ -339,8 +358,8 @@ public class ClassBuilder { classBuilder.builder.modifiers.remove(javax.lang.model.element.Modifier.PUBLIC); // this modifier may come from class access classBuilder.builder.addModifiers(new ModifierBuilder(innerClassNode.access) .checkUnseal(classBuilder.classNode, environment) - .getModifiers(ModifierBuilder.getType(classBuilder.enumClass, classBuilder.recordClass)) - ); + .getModifiers(ModifierBuilder.getType(classBuilder.enumClass, classBuilder.recordClass))); + if (!Modifier.isStatic(innerClassNode.access)) { classBuilder.instanceInner = true; } @@ -352,6 +371,7 @@ public class ClassBuilder { sb.append(innerClassNode.innerName); // append simple name List innerClassGenerics = classBuilder.signature.generics(); + if (!innerClassGenerics.isEmpty()) { sb.append("<"); for (TypeVariableName each : innerClassGenerics) { @@ -359,9 +379,11 @@ public class ClassBuilder { } sb.append(">"); } + classBuilder.receiverSignature = sb.toString(); } } + innerClasses.add(classBuilder); } @@ -370,9 +392,10 @@ public class ClassBuilder { } public TypeSpec build() { - innerClasses.stream() - .map(ClassBuilder::build) - .forEach(builder::addType); + for (ClassBuilder innerCb : innerClasses) { + builder.addType(innerCb.build()); + } + return builder.build(); } } diff --git a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java index 62c9e12b22..9ff8ae2755 100644 --- a/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java +++ b/filament/src/main/java/net/fabricmc/filament/mappingpoet/Main.java @@ -15,17 +15,6 @@ */ package net.fabricmc.mappingpoet; -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.JavaFile; -import net.fabricmc.mappingpoet.Environment.ClassNamePointer; -import net.fabricmc.mappingpoet.Environment.NestedClassInfo; -import net.fabricmc.mappingpoet.signature.ClassStaticContext; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.objectweb.asm.tree.InnerClassNode; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -50,6 +39,16 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import com.squareup.javapoet.JavaFile; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InnerClassNode; + +import net.fabricmc.mappingpoet.Environment.ClassNamePointer; +import net.fabricmc.mappingpoet.Environment.NestedClassInfo; + public class Main { public static void main(String[] args) { @@ -66,8 +65,8 @@ public class Main { if (Files.exists(outputDirectory)) { try (var stream = Files.walk(outputDirectory)) { stream.sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); + .map(Path::toFile) + .forEach(File::delete); } } @@ -94,20 +93,20 @@ public class Main { Map classes = new HashMap<>(); forEachClass(inputJar, (classNode, environment) -> writeClass(mapping, classNode, classes, environment), librariesDir); - classes.values().stream() - .filter(classBuilder -> !classBuilder.getClassName().contains("$")) - .forEach(classBuilder -> { - int packageEnd = classBuilder.getClassName().lastIndexOf("/"); - JavaFile javaFile = JavaFile.builder(packageEnd == -1 ? "" : classBuilder.getClassName().substring(0, packageEnd).replaceAll("/", "."), classBuilder.build()) - .build(); - try { - javaFile.writeTo(outputDirectory); - } catch (IOException e) { - throw new RuntimeException("Failed to write class", e); - } - }); + for (ClassBuilder classBuilder : classes.values()) { + String name = classBuilder.getClassName(); + if (name.contains("$")) continue; + try { + int packageEnd = classBuilder.getClassName().lastIndexOf("/"); + String pkgName = packageEnd < 0 ? "" : classBuilder.getClassName().substring(0, packageEnd).replaceAll("/", "."); + JavaFile javaFile = JavaFile.builder(pkgName, classBuilder.build()).build(); + javaFile.writeTo(outputDirectory); + } catch (Throwable t) { + throw new RuntimeException("Failed to process class "+name, t); + } + } } private static void forEachClass(Path jar, ClassNodeConsumer classNodeConsumer, Path librariesDir) { @@ -170,7 +169,9 @@ public class Main { //Sort all the classes making sure that inner classes come after the parent classes classes.sort(Comparator.comparing(o -> o.name)); - classes.forEach(node -> classNodeConsumer.accept(node, new Environment(supers, sealedClasses, nestedClasses))); + for (ClassNode node : classes) { + classNodeConsumer.accept(node, new Environment(supers, sealedClasses, nestedClasses)); + } } private static void scanNestedClasses(Map classNames, Map instanceInnerClasses, Path librariesDir) {