From 53869636f5bdfc1afdf001036f29a898f7e873d5 Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Mon, 13 Jun 2022 23:01:48 +0200 Subject: [PATCH] Improved HTML rendering of obfuscation mapping: added constructor signatures and modifiers for classes, fields and methods --- .../fr/pandacube/lib/core/util/Reflect.java | 19 +- .../lib/paper/reflect/NMSReflect.java | 181 +++++++++++++++--- 2 files changed, 170 insertions(+), 30 deletions(-) diff --git a/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java b/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java index 7d82cd9..52dcabf 100644 --- a/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java +++ b/Core/src/main/java/fr/pandacube/lib/core/util/Reflect.java @@ -295,6 +295,8 @@ public class Reflect { return cached; } + public abstract int getModifiers(); + } @@ -358,7 +360,10 @@ public class Reflect { setValue(null, value); } - + @Override + public int getModifiers() { + return get().getModifiers(); + } } @@ -388,6 +393,12 @@ public class Reflect { public Object invokeStatic(Object... values) throws ReflectiveOperationException { return invoke(null, values); } + + @Override + public int getModifiers() { + return get().getModifiers(); + } + } @@ -420,6 +431,12 @@ public class Reflect { public T instanciate(Object... values) throws ReflectiveOperationException { return get().newInstance(values); } + + @Override + public int getModifiers() { + return get().getModifiers(); + } + } diff --git a/Paper/src/main/java/fr/pandacube/lib/paper/reflect/NMSReflect.java b/Paper/src/main/java/fr/pandacube/lib/paper/reflect/NMSReflect.java index 3e4459f..3aa82a8 100644 --- a/Paper/src/main/java/fr/pandacube/lib/paper/reflect/NMSReflect.java +++ b/Paper/src/main/java/fr/pandacube/lib/paper/reflect/NMSReflect.java @@ -5,6 +5,8 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.StringReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -21,6 +23,7 @@ import fr.pandacube.lib.core.util.Log; import fr.pandacube.lib.core.util.Reflect; import fr.pandacube.lib.core.util.Reflect.ReflectClass; import fr.pandacube.lib.core.util.Reflect.ReflectField; +import fr.pandacube.lib.core.util.Reflect.ReflectMember; import fr.pandacube.lib.core.util.Reflect.ReflectMethod; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.format.MappingFormat; @@ -152,12 +155,13 @@ public class NMSReflect { html { background-color: #2F2F2F; color: white; + font-size: 14px; } table { border-collapse: collapse; width: 100%; margin: auto; - font-family: monospace; + font-family: Consolas, monospace; } tr:nth-child(2n) { background-color: #373737; @@ -190,6 +194,12 @@ public class NMSReflect { .fld { color: #8DDAF8; } + .st { + font-style: italic; + } + .st.fn { + font-weight: bold; + } """ + "\n" @@ -216,10 +226,10 @@ public class NMSReflect { /* package */ final String obfName; /* package */ final String mojName; - private final Map> methodsByObf = new TreeMap<>(); - private final Map> methodsByMoj = new TreeMap<>(); - private final Map> fieldsByObf = new TreeMap<>(); - private final Map> fieldsByMoj = new TreeMap<>(); + private final Map>> methodsByObf = new TreeMap<>(); + private final Map>> methodsByMoj = new TreeMap<>(); + private final Map>> fieldsByObf = new TreeMap<>(); + private final Map>> fieldsByMoj = new TreeMap<>(); private ReflectClass runtimeReflectClass = null; @@ -228,10 +238,12 @@ public class NMSReflect { mojName = binaryClassName(cls.getName(MOJ_NAMESPACE)); cls.getMethods().stream().map(MemberMapping::of).forEach(method -> { + method.declaringClass = this; methodsByObf.put(method.obfDesc.identifier, method); methodsByMoj.put(method.mojDesc.identifier, method); }); cls.getFields().stream().map(MemberMapping::of).forEach(field -> { + field.declaringClass = this; fieldsByObf.put(field.obfDesc.identifier, field); fieldsByMoj.put(field.mojDesc.identifier, field); }); @@ -273,11 +285,19 @@ public class NMSReflect { */ public ReflectMethod mojMethod(String mojName, Object... mojParametersType) throws ClassNotFoundException, NoSuchMethodException { MethodId mId = new MethodId(mojName, Type.toTypeList(Arrays.asList(mojParametersType))); - MemberMapping mm = methodsByMoj.get(mId); + MemberMapping> mm = methodsByMoj.get(mId); Objects.requireNonNull(mm, "Unable to find the Mojang mapped method " + mId); - MethodId reflectId = (IS_SERVER_OBFUSCATED ? mm.obfDesc : mm.mojDesc).identifier; - return runtimeReflectClass.method(reflectId.name, Type.toClassArray(reflectId.parametersType)); + try { + return mm.getReflectMember(); + } catch (ReflectiveOperationException e) { + if (e instanceof ClassNotFoundException cnfe) + throw cnfe; + if (e instanceof NoSuchMethodException nsme) + throw nsme; + // should not have another exception + throw new RuntimeException(e); + } } @@ -291,9 +311,16 @@ public class NMSReflect { * @throws NoSuchFieldException if there is no runtime method to represent the provided method. */ public ReflectField mojField(String mojName) throws NoSuchFieldException { - MemberMapping fm = fieldsByMoj.get(mojName); + MemberMapping> fm = fieldsByMoj.get(mojName); Objects.requireNonNull(fm, "Unable to find the Mojang mapped field '" + mojName + "'"); - return runtimeReflectClass.field((IS_SERVER_OBFUSCATED ? fm.obfDesc : fm.mojDesc).identifier); + try { + return fm.getReflectMember(); + } catch (ReflectiveOperationException e) { + if (e instanceof NoSuchFieldException nsfe) + throw nsfe; + // should not have another exception + throw new RuntimeException(e); + } } @@ -318,8 +345,9 @@ public class NMSReflect { private void printHTML(PrintStream out) { - out.println("" + classKind() + "" + nameToHTML(true) + "" + nameToHTML(false) + ""); + out.println("" + classModifiers() + "" + nameToHTML(true) + "" + nameToHTML(false) + ""); fieldsByObf.values().forEach(f -> f.printHTML(out)); + printConstructorsHTML(out); methodsByObf.values().forEach(m -> m.printHTML(out)); } @@ -370,8 +398,42 @@ public class NMSReflect { return "record"; if (clazz.isPrimitive()) return "primitive"; - return "Class"; + return "class"; } + + private String classModifiers() { + Class clazz = runtimeClass(); + int clModifiers = clazz.getModifiers(); + return modifiersToString(clModifiers) + " " + classKind(); + } + + + private void printConstructorsHTML(PrintStream out) { + String classObfSimpleName = obfName.substring(obfName.lastIndexOf('.') + 1); + String classMojSimpleName = mojName.substring(mojName.lastIndexOf('.') + 1); + for (Constructor ct : runtimeClass().getDeclaredConstructors()) { + List obfParams = new ArrayList<>(); + List mojParams = new ArrayList<>(); + for (Class param : ct.getParameterTypes()) { + ClassMapping cm = (IS_SERVER_OBFUSCATED ? CLASSES_BY_OBF : CLASSES_BY_MOJ).get(param.getName()); + if (cm == null) { + Type t = Type.of(param); + obfParams.add(t); + mojParams.add(t); + } + else { + obfParams.add(cm.toType(true)); + mojParams.add(cm.toType(false)); + } + } + out.println("" + + "" + modifiersToString(ct.getModifiers()) + "" + + "" + classObfSimpleName + "(" + obfParams.stream().map(t -> t.toHTML(true)).collect(Collectors.joining(", ")) + ")" + + "" + classMojSimpleName + "(" + mojParams.stream().map(t -> t.toHTML(false)).collect(Collectors.joining(", ")) + ")" + + ""); + } + + } } @@ -390,9 +452,14 @@ public class NMSReflect { return toString().compareTo(o.toString()); } - private String toHTML(boolean isObfClass) { + private String toHTML(boolean isObfClass, boolean isStatic, boolean isFinal) { String paramsHTML = parametersType.stream().map(p -> p.toHTML(isObfClass)).collect(Collectors.joining(", ")); - String identifierHTML = "" + name + "(" + paramsHTML + ")"; + String cl = "mtd"; + if (isStatic) + cl += " st"; + if (isFinal) + cl += " fn"; + String identifierHTML = "" + name + "(" + paramsHTML + ")"; return identifierHTML; } @@ -406,12 +473,18 @@ public class NMSReflect { private static record MemberDesc>(I identifier, Type returnType) { - private String toHTML(boolean isObfClass) { + private String toHTML(boolean isObfClass, boolean isStatic, boolean isFinal) { // TODO String identifierHTML = ""; if (identifier instanceof MethodId mId) - identifierHTML = mId.toHTML(isObfClass); - else if (identifier instanceof String n) - identifierHTML = "" + n + ""; + identifierHTML = mId.toHTML(isObfClass, isStatic, isFinal); + else if (identifier instanceof String n) { + String cl = "fld"; + if (isStatic) + cl += " st"; + if (isFinal) + cl += " fn"; + identifierHTML = "" + n + ""; + } return returnType.toHTML(isObfClass) + " " + identifierHTML; } @@ -447,25 +520,54 @@ public class NMSReflect { - private static class MemberMapping> { - private String type; + private static abstract class MemberMapping, R extends ReflectMember> { /* package */ MemberDesc obfDesc, mojDesc; - private MemberMapping(String type, MemberDesc obfDesc, MemberDesc mojDesc) { - this.type = type; + /* package */ ClassMapping declaringClass; + private MemberMapping(MemberDesc obfDesc, MemberDesc mojDesc) { this.obfDesc = obfDesc; this.mojDesc = mojDesc; } /* package */ void printHTML(PrintStream out) { - out.println("" + type + "" + obfDesc.toHTML(true) + "" + mojDesc.toHTML(false) + ""); + int mod = 0; + try { + mod = getReflectMember().getModifiers(); + } catch (ReflectiveOperationException e) { + // ignore + } + boolean isStatic = Modifier.isStatic(mod); + boolean isFinal = Modifier.isFinal(mod); + out.println("" + + "" + modifiersToString(mod) + "" + + "" + obfDesc.toHTML(true, isStatic, isFinal) + "" + + "" + mojDesc.toHTML(false, isStatic, isFinal) + "" + + ""); } - private static MemberMapping of(MappingTree.MethodMapping mioMapping) { - return new MemberMapping<>("Method", MemberDesc.of(mioMapping, OBF_NAMESPACE), MemberDesc.of(mioMapping, MOJ_NAMESPACE)); + /* package */ MemberDesc getReflectDesc() { + return (IS_SERVER_OBFUSCATED ? obfDesc : mojDesc); } - private static MemberMapping of(MappingTree.FieldMapping mioMapping) { - return new MemberMapping<>("Field", MemberDesc.of(mioMapping, OBF_NAMESPACE), MemberDesc.of(mioMapping, MOJ_NAMESPACE)); + /* package */ abstract R getReflectMember() throws ReflectiveOperationException; + + private static MemberMapping> of(MappingTree.MethodMapping mioMapping) { + return new MemberMapping<>(MemberDesc.of(mioMapping, OBF_NAMESPACE), MemberDesc.of(mioMapping, MOJ_NAMESPACE)) { + @Override + ReflectMethod getReflectMember() throws ClassNotFoundException, NoSuchMethodException { + MethodId id = getReflectDesc().identifier; + return declaringClass.runtimeReflectClass.method(id.name, Type.toClassArray(id.parametersType)); + } + }; + } + + private static MemberMapping> of(MappingTree.FieldMapping mioMapping) { + return new MemberMapping<>(MemberDesc.of(mioMapping, OBF_NAMESPACE), MemberDesc.of(mioMapping, MOJ_NAMESPACE)) { + @Override + ReflectField getReflectMember() throws NoSuchFieldException { + String id = getReflectDesc().identifier; + return declaringClass.runtimeReflectClass.field(id); + } + }; } } @@ -474,13 +576,34 @@ public class NMSReflect { - - /* package */ static String binaryClassName(String cl) { return cl.replace('/', '.'); } + + + private static String modifiersToString(int elModifiers) { + List modifiers = new ArrayList<>(); + + if (Modifier.isPublic(elModifiers)) + modifiers.add("public"); + if (Modifier.isProtected(elModifiers)) + modifiers.add("protected"); + if (Modifier.isPrivate(elModifiers)) + modifiers.add("private"); + + if (Modifier.isStatic(elModifiers)) + modifiers.add("static"); + + if (Modifier.isAbstract(elModifiers)) + modifiers.add("abstract"); + if (Modifier.isFinal(elModifiers)) + modifiers.add("final"); + + return String.join(" ", modifiers); + } + }