From d5f39d57cc6f6ba99043baa6f8f0e696ab7bb35c Mon Sep 17 00:00:00 2001 From: IzzelAliz Date: Tue, 9 Feb 2021 00:03:59 +0800 Subject: [PATCH] Refactor redirect logic, should break nothing --- arclight-common/build.gradle | 2 + .../util/remapper/ArclightEnumExtender.java | 15 +- .../remapper/ArclightRedirectAdapter.java | 378 ++++++++++-------- .../mod/util/remapper/ArclightRemapper.java | 13 + .../util/remapper/ClassLoaderRemapper.java | 36 +- .../generated/ArclightReflectionHandler.java | 326 ++++++++++----- .../izzel/arclight/common/util/ArrayUtil.java | 20 + arclight-forge-1.16/build.gradle | 2 + 8 files changed, 510 insertions(+), 282 deletions(-) create mode 100644 arclight-common/src/main/java/io/izzel/arclight/common/util/ArrayUtil.java diff --git a/arclight-common/build.gradle b/arclight-common/build.gradle index fffdab20..e7aa22b8 100644 --- a/arclight-common/build.gradle +++ b/arclight-common/build.gradle @@ -47,6 +47,7 @@ repositories { maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url = 'https://files.minecraftforge.net/maven/' } maven { url = 'https://jitpack.io/' } + maven { url = 'https://maven.izzel.io/releases' } } dependencies { @@ -66,6 +67,7 @@ dependencies { implementation 'net.md-5:bungeecord-chat:1.16-R0.4' implementation 'mysql:mysql-connector-java:5.1.49' implementation 'org.yaml:snakeyaml:1.27' + implementation 'io.izzel:tools:1.0.+' implementation project(':arclight-api') implementation project(':i18n-config') } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightEnumExtender.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightEnumExtender.java index c7ce438b..edc61fb8 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightEnumExtender.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightEnumExtender.java @@ -11,7 +11,6 @@ import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.InsnNode; -import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; @@ -25,6 +24,8 @@ import java.util.List; import java.util.Locale; import java.util.Set; +import static io.izzel.arclight.common.mod.util.remapper.ArclightRedirectAdapter.loadInt; + // 你好 // 不要抄(笑) @SuppressWarnings("unused") @@ -89,18 +90,6 @@ public class ArclightEnumExtender { } } - private static AbstractInsnNode loadInt(int i) { - if (i >= -1 && i < 6) { - return new InsnNode(Opcodes.ICONST_0 + i); - } else if (i >= -128 && i < 128) { - return new IntInsnNode(Opcodes.BIPUSH, i); - } else if (i >= -32768 && i < 32768) { - return new IntInsnNode(Opcodes.SIPUSH, i); - } else { - return new LdcInsnNode(i); - } - } - private static void tryCreateCtor(ClassNode node) { boolean found = false; for (MethodNode method : node.methods) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java index 542b1190..c5b44ba0 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRedirectAdapter.java @@ -1,8 +1,13 @@ package io.izzel.arclight.common.mod.util.remapper; +import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableMap; -import io.izzel.arclight.common.mod.ArclightMod; +import com.google.common.collect.Multimap; import io.izzel.arclight.common.mod.util.remapper.generated.ArclightReflectionHandler; +import io.izzel.arclight.common.util.ArrayUtil; +import io.izzel.tools.product.Product; +import io.izzel.tools.product.Product2; +import io.izzel.tools.product.Product4; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; import org.objectweb.asm.Handle; @@ -10,131 +15,75 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.InvokeDynamicInsnNode; import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TypeInsnNode; +import org.spongepowered.asm.util.Bytecode; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ListIterator; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashMap; import java.util.Map; -import java.util.Objects; public class ArclightRedirectAdapter implements PluginTransformer { public static final ArclightRedirectAdapter INSTANCE = new ArclightRedirectAdapter(); private static final Marker MARKER = MarkerManager.getMarker("REDIRECT"); private static final String REPLACED_NAME = Type.getInternalName(ArclightReflectionHandler.class); - private static final Map METHOD_REDIRECTS = ImmutableMap - .builder() - .put( - method(Opcodes.INVOKEVIRTUAL, Field.class, "getName"), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFieldGetName", Field.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Class.class, "getField", String.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectGetField", Class.class, String.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Class.class, "getDeclaredField", String.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectGetDeclaredField", Class.class, String.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Class.class, "getName"), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectClassGetName", Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Class.class, "getCanonicalName"), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectClassGetCanonicalName", Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Class.class, "getSimpleName"), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectClassGetSimpleName", Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Method.class, "getName"), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectMethodGetName", Method.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Class.class, "getMethod", String.class, Class[].class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectGetMethod", Class.class, String.class, Class[].class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Class.class, "getDeclaredMethod", String.class, Class[].class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectGetDeclaredMethod", Class.class, String.class, Class[].class) - ) - .put( - method(Opcodes.INVOKESTATIC, Class.class, "forName", String.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectForName", String.class) - ) - .put( - method(Opcodes.INVOKESTATIC, Class.class, "forName", String.class, boolean.class, ClassLoader.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectForName", String.class, boolean.class, ClassLoader.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, Package.class, "getName"), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectPackageGetName", Package.class) - ) - .put( - method(Opcodes.INVOKESTATIC, MethodType.class, "fromMethodDescriptorString", String.class, ClassLoader.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFromDescStr", String.class, ClassLoader.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, MethodHandles.Lookup.class, "findStatic", Class.class, String.class, MethodType.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFindStatic", MethodHandles.Lookup.class, Class.class, String.class, MethodType.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, MethodHandles.Lookup.class, "findVirtual", Class.class, String.class, MethodType.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFindVirtual", MethodHandles.Lookup.class, Class.class, String.class, MethodType.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, MethodHandles.Lookup.class, "findSpecial", Class.class, String.class, MethodType.class, Class.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFindSpecial", MethodHandles.Lookup.class, Class.class, String.class, MethodType.class, Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, MethodHandles.Lookup.class, "findGetter", Class.class, String.class, Class.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFindGetter", MethodHandles.Lookup.class, Class.class, String.class, Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, MethodHandles.Lookup.class, "findSetter", Class.class, String.class, Class.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFindSetter", MethodHandles.Lookup.class, Class.class, String.class, Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, MethodHandles.Lookup.class, "findStaticGetter", Class.class, String.class, Class.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFindStaticGetter", MethodHandles.Lookup.class, Class.class, String.class, Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, MethodHandles.Lookup.class, "findStaticSetter", Class.class, String.class, Class.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectFindStaticSetter", MethodHandles.Lookup.class, Class.class, String.class, Class.class) - ) - .put( - method(Opcodes.INVOKEVIRTUAL, ClassLoader.class, "loadClass", String.class), - method(Opcodes.INVOKESTATIC, ArclightReflectionHandler.class, "redirectClassLoaderLoadClass", ClassLoader.class, String.class) - ) - .build(); - private static final String METHOD_SIG = Type.getInternalName(Method.class); - private static final String INVOKE_SIG = Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object[].class)); + private static final Multimap> METHOD_MODIFY = HashMultimap.create(); + private static final Multimap> METHOD_REDIRECT = HashMultimap.create(); + private static final Map[], String, Class[]>> METHOD_TO_HANDLER = new HashMap<>(); + + static { + redirect(Field.class, "getName", "fieldGetName"); + redirect(Method.class, "getName", "methodGetName"); + redirect(Class.class, "getCanonicalName", "classGetCanonicalName"); + redirect(Class.class, "getSimpleName", "classGetSimpleName"); + modify(Class.class, "getName", "classGetName"); + modify(Package.class, "getName", "packageGetName"); + modify(Class.class, "forName", "classForName", String.class); + modify(Class.class, "forName", "classForName", String.class, boolean.class, ClassLoader.class); + modify(Class.class, "getField", "classGetField", String.class); + modify(Class.class, "getDeclaredField", "classGetDeclaredField", String.class); + modify(Class.class, "getMethod", "classGetMethod", String.class, Class[].class); + modify(Class.class, "getDeclaredMethod", "classGetDeclaredMethod", String.class, Class[].class); + modify(MethodType.class, "fromMethodDescriptorString", "fromDescStr", String.class, ClassLoader.class); + modify(MethodHandles.Lookup.class, "findStatic", "lookupFindStatic", Class.class, String.class, MethodType.class); + modify(MethodHandles.Lookup.class, "findVirtual", "lookupFindVirtual", Class.class, String.class, MethodType.class); + modify(MethodHandles.Lookup.class, "findSpecial", "lookupFindSpecial", Class.class, String.class, MethodType.class, Class.class); + modify(MethodHandles.Lookup.class, "findGetter", "lookupFindGetter", Class.class, String.class, Class.class); + modify(MethodHandles.Lookup.class, "findSetter", "lookupFindSetter", Class.class, String.class, Class.class); + modify(MethodHandles.Lookup.class, "findStaticGetter", "lookupFindStaticGetter", Class.class, String.class, Class.class); + modify(MethodHandles.Lookup.class, "findStaticSetter", "lookupFindStaticSetter", Class.class, String.class, Class.class); + modify(ClassLoader.class, "loadClass", "classLoaderLoadClass", String.class); + // todo not enable this yet + // modify(Method.class, "invoke", "methodInvoke", Object.class, Object[].class); + } + + public static Product4[], String, Class[]> getInvokeRule(Method method) { + return METHOD_TO_HANDLER.get(method); + } @Override public void handleClass(ClassNode node, ClassLoaderRemapper remapper) { - redirect(node, remapper.getGeneratedHandler()); + redirect(node, remapper); } - private static void redirect(ClassNode classNode, String generatedOwner) { - boolean defineClassFound = false; + private static void redirect(ClassNode classNode, ClassLoaderRemapper remapper) { for (MethodNode methodNode : classNode.methods) { - ListIterator iterator = methodNode.instructions.iterator(); - while (iterator.hasNext()) { - AbstractInsnNode insnNode = iterator.next(); + for (AbstractInsnNode insnNode : methodNode.instructions) { if (insnNode instanceof MethodInsnNode) { MethodInsnNode from = (MethodInsnNode) insnNode; - MethodInsnNode newNode = find(from, generatedOwner); - if (newNode != null) { - iterator.set(newNode); - } + process(from, methodNode.instructions, remapper); } else if (insnNode.getOpcode() == Opcodes.INVOKEDYNAMIC) { InvokeDynamicInsnNode invokeDynamic = (InvokeDynamicInsnNode) insnNode; Object[] bsmArgs = invokeDynamic.bsmArgs; @@ -143,96 +92,185 @@ public class ArclightRedirectAdapter implements PluginTransformer { if (bsmArg instanceof Handle) { Handle handle = (Handle) bsmArg; if (toOpcode(handle.getTag()) != -1) { - MethodInsnNode node = find(handle, generatedOwner); - if (node != null) { - bsmArgs[i] = new Handle(toHandle(node.getOpcode()), node.owner, node.name, node.desc, node.itf); - } + bsmArgs[i] = processHandle(handle, remapper); } } } - } else if (insnNode.getOpcode() == Opcodes.LDC) { - defineClassFound |= "defineClass".equals(((LdcInsnNode) insnNode).cst); - } - } - } - if (defineClassFound) { - for (MethodNode methodNode : classNode.methods) { - for (AbstractInsnNode insnNode : methodNode.instructions) { - if (insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) { - MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; - if (methodInsnNode.owner.equals(METHOD_SIG) && methodInsnNode.name.equals("invoke") && methodInsnNode.desc.equals(INVOKE_SIG)) { - methodInsnNode.setOpcode(Opcodes.INVOKESTATIC); - methodInsnNode.owner = generatedOwner; - methodInsnNode.name = "redirectDefineClassInvoke"; - methodInsnNode.desc = "(L" + METHOD_SIG + ";" + methodInsnNode.desc.substring(1); - ArclightMod.LOGGER.debug(MARKER, "Redirect candidate defineClass method invoke in {}/{} {}" - , classNode.name, methodNode.name, methodNode.desc); - } - } } } } } - private static MethodInsnNode find(Handle handle, String generatedOwner) { - for (Map.Entry entry : METHOD_REDIRECTS.entrySet()) { - MethodInsnNode key = entry.getKey(); - if ( - key.getOpcode() == toOpcode(handle.getTag()) && - Objects.equals(key.owner, handle.getOwner()) && - Objects.equals(key.name, handle.getName()) && - Objects.equals(key.desc, handle.getDesc())) { - MethodInsnNode to = entry.getValue(); - if (REPLACED_NAME.equals(to.owner)) { - MethodInsnNode clone = (MethodInsnNode) to.clone(ImmutableMap.of()); - clone.owner = generatedOwner; - return clone; + private static Handle processHandle(Handle handle, ClassLoaderRemapper remapper) { + String key = handle.getName() + handle.getDesc(); + Collection> col = METHOD_REDIRECT.get(key); + for (Product2 methodRedirect : col) { + if (isSuperType(handle.getOwner(), methodRedirect._1)) { + MethodInsnNode node = methodRedirect._2; + String owner = REPLACED_NAME.equals(node.owner) ? remapper.getGeneratedHandler() : node.owner; + return new Handle(toHandle(node.getOpcode()), owner, node.name, node.desc, node.itf); + } + } + return handle; + } + + private static void process(MethodInsnNode node, InsnList insnList, ClassLoaderRemapper remapper) { + String key = node.name + node.desc; + Collection> modifyArgsCol = METHOD_MODIFY.get(key); + for (Product2 modifyArgs : modifyArgsCol) { + if (isSuperType(node.owner, modifyArgs._1)) { + MethodInsnNode handlerNode; + if (REPLACED_NAME.equals(modifyArgs._2.owner)) { + handlerNode = (MethodInsnNode) modifyArgs._2.clone(ImmutableMap.of()); + handlerNode.owner = remapper.getGeneratedHandler(); } else { - return to; + handlerNode = modifyArgs._2; } + processModify(node, insnList, handlerNode); + return; } } - return null; - } - - private static MethodInsnNode find(MethodInsnNode from, String generatedOwner) { - for (Map.Entry entry : METHOD_REDIRECTS.entrySet()) { - MethodInsnNode key = entry.getKey(); - if ( - key.getOpcode() == from.getOpcode() && - Objects.equals(key.owner, from.owner) && - Objects.equals(key.name, from.name) && - Objects.equals(key.desc, from.desc)) { - MethodInsnNode to = entry.getValue(); - if (REPLACED_NAME.equals(to.owner)) { - MethodInsnNode clone = (MethodInsnNode) to.clone(ImmutableMap.of()); - clone.owner = generatedOwner; - return clone; + Collection> methodRedirectCol = METHOD_REDIRECT.get(key); + for (Product2 methodRedirect : methodRedirectCol) { + if (isSuperType(node.owner, methodRedirect._1)) { + MethodInsnNode handlerNode; + if (REPLACED_NAME.equals(methodRedirect._2.owner)) { + handlerNode = (MethodInsnNode) methodRedirect._2.clone(ImmutableMap.of()); + handlerNode.owner = remapper.getGeneratedHandler(); } else { - return to; + handlerNode = methodRedirect._2; + } + processMethodRedirect(node, insnList, handlerNode); + return; + } + } + } + + private static boolean isSuperType(String sub, String sup) { + return sub.equals(sup) || GlobalClassRepo.inheritanceProvider().getAll(sub).contains(sup); + } + + private static void processMethodRedirect(MethodInsnNode node, InsnList insnList, MethodInsnNode handlerNode) { + insnList.set(node, handlerNode); + } + + private static void processModify(MethodInsnNode node, InsnList insnList, MethodInsnNode handlerNode) { + InsnList list = new InsnList(); + list.add(handlerNode); + Type methodType = Type.getMethodType(node.desc); + Type[] types = methodType.getArgumentTypes(); + if (node.getOpcode() != Opcodes.INVOKESTATIC) { + types = ArrayUtil.prepend(types, Type.getObjectType(node.owner), Type[]::new); + } + if (types.length == 1) { + if (node.desc.startsWith("()")) { + String retDesc = methodType.getReturnType().getDescriptor(); + if (handlerNode.desc.equals("(" + retDesc + ")" + retDesc)) { // handle(obj.method()) + insnList.insert(node, handlerNode); + return; + } + } else { + String desc = types[0].getDescriptor(); + if (handlerNode.desc.equals("(" + desc + ")" + desc)) { // object.call(handle(arg0)) + insnList.insertBefore(node, handlerNode); + return; } } } - return null; + for (int i = 0, argumentTypesLength = types.length; i < argumentTypesLength; i++) { + Type type = types[i]; + if (i > 0) { + swap(list, types[i - 1]); + } + if (argumentTypesLength > 1 && i != argumentTypesLength - 1) { + list.add(new InsnNode(Opcodes.DUP)); + } + list.add(loadInt(i)); + list.add(new InsnNode(Opcodes.AALOAD)); + cast(list, type); + } + insnList.insertBefore(node, list); } - private static MethodInsnNode method(int opcode, Class cl, String name, Class... pTypes) { + private static void swap(InsnList list, Type top) { + if (top.getSize() == 1) { + list.add(new InsnNode(Opcodes.SWAP)); + } else { + list.add(new InsnNode(Opcodes.DUP2_X1)); + list.add(new InsnNode(Opcodes.POP2)); + } + } + + private static void cast(InsnList list, Type type) { + if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { + String internalName = type.getInternalName(); + if (!"java/lang/Object".equals(internalName)) { + list.add(new TypeInsnNode(Opcodes.CHECKCAST, internalName)); + } + } else { + String boxingType = Bytecode.getBoxingType(type); + String unboxingMethod = Bytecode.getUnboxingMethod(type); + list.add(new TypeInsnNode(Opcodes.CHECKCAST, boxingType)); + list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, boxingType, unboxingMethod, "()" + type.getDescriptor(), false)); + } + } + + private static void modify(Class owner, String name, String handlerName, Class... args) { + addRule(true, owner, name, handlerName, args); + } + + private static void redirect(Class owner, String name, String handlerName, Class... args) { + addRule(false, owner, name, handlerName, args); + } + + private static void addRule(boolean modifyArgs, Class owner, String name, String handlerName, Class... args) { + Method original = methodOf(owner, name, args); + Class[] handlerArgs; + if (!Modifier.isStatic(original.getModifiers())) { + handlerArgs = ArrayUtil.prepend(args, owner, Class[]::new); + } else { + handlerArgs = args; + } + Method handler = methodOf(ArclightReflectionHandler.class, "redirect" + capitalize(handlerName), handlerArgs); + METHOD_REDIRECT.put(name + Type.getMethodDescriptor(original), Product.of(Type.getInternalName(owner), methodNodeOf(handler))); + Product2[]> handleProd; + if (modifyArgs) { + Method modifyHandler; + try { + modifyHandler = methodOf(ArclightReflectionHandler.class, "handle" + capitalize(handlerName), handlerArgs); + } catch (RuntimeException e) { + handlerArgs[0] = original.getReturnType(); + modifyHandler = methodOf(ArclightReflectionHandler.class, "handle" + capitalize(handlerName), handlerArgs); + } + METHOD_MODIFY.put(name + Type.getMethodDescriptor(original), Product.of(Type.getInternalName(owner), methodNodeOf(modifyHandler))); + handleProd = Product.of("handle" + capitalize(handlerName), handlerArgs); + } else { + handleProd = Product.of(null, null); + } + METHOD_TO_HANDLER.put(original, Product.of("redirect" + capitalize(handlerName), handlerArgs, handleProd._1, handleProd._2)); + } + + private static String capitalize(String name) { + return Character.toUpperCase(name.charAt(0)) + name.substring(1); + } + + private static Method methodOf(Class owner, String name, Class... args) { try { - return method(opcode, cl.getMethod(name, pTypes)); + return owner.getMethod(name, args); } catch (Exception e) { try { - return method(opcode, cl.getDeclaredMethod(name, pTypes)); - } catch (NoSuchMethodException ex) { - throw new RuntimeException(ex); + return owner.getDeclaredMethod(name, args); + } catch (NoSuchMethodException e2) { + throw new RuntimeException(e2); } } } - private static MethodInsnNode method(int opcode, Method method) { + private static MethodInsnNode methodNodeOf(Method method) { String owner = Type.getInternalName(method.getDeclaringClass()); String name = method.getName(); String desc = Type.getMethodDescriptor(method); - return new MethodInsnNode(opcode, owner, name, desc); + return new MethodInsnNode(Opcodes.INVOKESTATIC, owner, name, desc); } private static int toOpcode(int handleType) { @@ -260,4 +298,16 @@ public class ArclightRedirectAdapter implements PluginTransformer { return -1; } } + + static AbstractInsnNode loadInt(int i) { + if (i >= -1 && i < 6) { + return new InsnNode(Opcodes.ICONST_0 + i); + } else if (i >= -128 && i < 128) { + return new IntInsnNode(Opcodes.BIPUSH, i); + } else if (i >= -32768 && i < 32768) { + return new IntInsnNode(Opcodes.SIPUSH, i); + } else { + return new LdcInsnNode(i); + } + } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRemapper.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRemapper.java index 24bb7f25..2d4d0efa 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRemapper.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightRemapper.java @@ -9,8 +9,10 @@ import net.md_5.specialsource.InheritanceMap; import net.md_5.specialsource.JarMapping; import net.md_5.specialsource.provider.ClassLoaderProvider; import net.md_5.specialsource.provider.JointProvider; +import org.apache.commons.io.FileUtils; import java.io.BufferedReader; +import java.io.File; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; @@ -18,11 +20,22 @@ import java.util.List; public class ArclightRemapper { public static final ArclightRemapper INSTANCE; + public static final File DUMP; static { ArclightI18nLogger.getLogger("Arclight").info("loading-mapping"); try { INSTANCE = new ArclightRemapper(); + String property = System.getProperty("arclight.remapper.dump"); + if (property != null) { + DUMP = new File(property); + if (!DUMP.exists()) { + DUMP.mkdirs(); + } + FileUtils.forceDelete(DUMP); + } else { + DUMP = null; + } } catch (Exception e) { throw new RuntimeException(e); } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java index 089fb114..0654816a 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java @@ -23,9 +23,11 @@ import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodInsnNode; import org.spongepowered.asm.service.MixinService; +import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.nio.file.Files; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -43,6 +45,7 @@ public class ClassLoaderRemapper extends LenientJarRemapper { private final JarRemapper toBukkitRemapper; private final ClassLoader classLoader; private final String generatedHandler; + private final Class generatedHandlerClass; public ClassLoaderRemapper(JarMapping jarMapping, JarMapping toBukkitMapping, ClassLoader classLoader) { super(jarMapping); @@ -52,7 +55,8 @@ public class ClassLoaderRemapper extends LenientJarRemapper { this.jarMapping.setFallbackInheritanceProvider(GlobalClassRepo.inheritanceProvider()); this.toBukkitMapping.setFallbackInheritanceProvider(GlobalClassRepo.inheritanceProvider()); this.toBukkitRemapper = new LenientJarRemapper(this.toBukkitMapping); - this.generatedHandler = generateReflectionHandler(); + this.generatedHandlerClass = generateReflectionHandler(); + this.generatedHandler = Type.getInternalName(generatedHandlerClass); GlobalClassRepo.INSTANCE.addRepo(new ClassLoaderRepo(this.classLoader)); } @@ -76,6 +80,10 @@ public class ClassLoaderRemapper extends LenientJarRemapper { return generatedHandler; } + public Class getGeneratedHandlerClass() { + return generatedHandlerClass; + } + // BiMap: srg -> bukkit private final Map> cacheFields = new ConcurrentHashMap<>(); private final Map, Map>> cacheMethods = new ConcurrentHashMap<>(); @@ -286,12 +294,12 @@ public class ClassLoaderRemapper extends LenientJarRemapper { ClassWriter wr = new PluginClassWriter(ClassWriter.COMPUTE_FRAMES); node.accept(wr); - return wr.toByteArray(); + return dump(wr.toByteArray()); } private static final AtomicInteger COUNTER = new AtomicInteger(); - private String generateReflectionHandler() { + private Class generateReflectionHandler() { try { ClassNode node = MixinService.getService().getBytecodeProvider().getClassNode(Type.getInternalName(ArclightReflectionHandler.class)); Preconditions.checkNotNull(node, "node"); @@ -300,12 +308,13 @@ public class ClassLoaderRemapper extends LenientJarRemapper { ClassVisitor visitor = new ClassRemapper(writer, new NameRemapper(name)); node.accept(visitor); byte[] bytes = writer.toByteArray(); + dump(bytes); Class cl = Unsafe.defineClass(name.replace('/', '.'), bytes, 0, bytes.length, getClass().getClassLoader(), getClass().getProtectionDomain()); Unsafe.ensureClassInitialized(cl); Field remapper = cl.getField("remapper"); remapper.set(null, this); - return name; + return cl; } catch (Exception e) { throw new RuntimeException(e); } @@ -385,4 +394,23 @@ public class ClassLoaderRemapper extends LenientJarRemapper { return result; } } + + private static byte[] dump(byte[] bytes) { + try { + if (ArclightRemapper.DUMP != null) { + String className = new ClassReader(bytes).getClassName() + ".class"; + int index = className.lastIndexOf('/'); + if (index != -1) { + File file = new File(ArclightRemapper.DUMP, className.substring(0, index)); + file.mkdirs(); + Files.write(file.toPath().resolve(className.substring(index + 1)), bytes); + } else { + Files.write(ArclightRemapper.DUMP.toPath().resolve(className), bytes); + } + } + } catch (Exception e) { + LOGGER.error("Failed to dump class " + new ClassReader(bytes).getClassName(), e); + } + return bytes; + } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/generated/ArclightReflectionHandler.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/generated/ArclightReflectionHandler.java index 5e5395e4..f8e3f25b 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/generated/ArclightReflectionHandler.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/generated/ArclightReflectionHandler.java @@ -2,8 +2,10 @@ package io.izzel.arclight.common.mod.util.remapper.generated; import io.izzel.arclight.api.ArclightVersion; import io.izzel.arclight.api.Unsafe; -import io.izzel.arclight.common.mod.util.remapper.ClassLoaderAdapter; +import io.izzel.arclight.common.mod.util.remapper.ArclightRedirectAdapter; import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper; +import io.izzel.arclight.common.util.ArrayUtil; +import io.izzel.tools.product.Product4; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Type; @@ -11,11 +13,13 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.security.CodeSource; import java.security.Permissions; import java.security.ProtectionDomain; +import java.util.Arrays; @SuppressWarnings("unused") public class ArclightReflectionHandler extends ClassLoader { @@ -24,27 +28,9 @@ public class ArclightReflectionHandler extends ClassLoader { public static ClassLoaderRemapper remapper; - // bukkit -> srg - public static Class redirectForName(String cl) throws ClassNotFoundException { - return redirectForName(cl, true, Unsafe.getCallerClass().getClassLoader()); - } - - // bukkit -> srg - public static Class redirectForName(String cl, boolean initialize, ClassLoader classLoader) throws ClassNotFoundException { - if (!cl.startsWith(PREFIX)) { - return Class.forName(cl, initialize, classLoader); - } - try { - String replace = remapper.mapType(cl.replace('.', '/')).replace('/', '.'); - return Class.forName(replace, initialize, classLoader); - } catch (ClassNotFoundException e) { // nested/inner class - int i = cl.lastIndexOf('.'); - if (i > 0) { - String replace = cl.substring(0, i).replace('.', '/') + "$" + cl.substring(i + 1); - replace = remapper.mapType(replace).replace('/', '.').replace('$', '.'); - return Class.forName(replace, initialize, classLoader); - } else throw e; - } + // srg -> bukkit + public static String redirectFieldGetName(Field field) { + return remapper.tryMapFieldToBukkit(field.getDeclaringClass(), field.getName(), field); } // srg -> bukkit @@ -52,61 +38,20 @@ public class ArclightReflectionHandler extends ClassLoader { return remapper.tryMapMethodToBukkit(method.getDeclaringClass(), method); } - // bukkit -> srg - public static Method redirectGetMethod(Class cl, String bukkitName, Class... pTypes) throws NoSuchMethodException { - Method method = remapper.tryMapMethodToSrg(cl, bukkitName, pTypes); - if (method != null) { - return method; - } else { - return cl.getMethod(bukkitName, pTypes); - } - } - - // bukkit -> srg - public static Method redirectGetDeclaredMethod(Class cl, String bukkitName, Class... pTypes) throws NoSuchMethodException { - Method method = remapper.tryMapMethodToSrg(cl, bukkitName, pTypes); - if (method != null) { - return method; - } else { - return cl.getDeclaredMethod(bukkitName, pTypes); - } - } - - // srg -> bukkit - public static String redirectFieldGetName(Field field) { - return remapper.tryMapFieldToBukkit(field.getDeclaringClass(), field.getName(), field); - } - - // bukkit -> srg - public static Field redirectGetField(Class cl, String bukkitName) throws NoSuchFieldException { - String field = remapper.tryMapFieldToSrg(cl, bukkitName); - return cl.getField(field); - } - - // bukkit -> srg - public static Field redirectGetDeclaredField(Class cl, String bukkitName) throws NoSuchFieldException { - String field = remapper.tryMapDecFieldToSrg(cl, bukkitName); - return cl.getDeclaredField(field); - } - - // srg -> bukkit - public static String redirectClassGetName(Class cl) { - String internalName = Type.getInternalName(cl); - Type type = Type.getObjectType(remapper.toBukkitRemapper().mapType(internalName)); - return type.getInternalName().replace('/', '.'); - } - // srg -> bukkit public static String redirectClassGetCanonicalName(Class cl) { - String canonicalName = cl.getCanonicalName(); - if (canonicalName == null) { - return null; - } if (cl.isArray()) { String name = redirectClassGetCanonicalName(cl.getComponentType()); if (name == null) return null; return name + "[]"; } + if (cl.isLocalClass() || cl.isAnonymousClass()) { + return null; + } + String canonicalName = cl.getCanonicalName(); + if (canonicalName == null) { + return null; + } Class enclosingClass = cl.getEnclosingClass(); if (enclosingClass == null) { return redirectClassGetName(cl); @@ -138,8 +83,19 @@ public class ArclightReflectionHandler extends ClassLoader { } // srg -> bukkit - public static String redirectPackageGetName(Package pkg) { - String name = pkg.getName(); + public static String handleClassGetName(String cl) { + return remapper.toBukkitRemapper().mapType(cl.replace('.', '/')).replace('/', '.'); + } + + // srg -> bukkit + public static String redirectClassGetName(Class cl) { + String internalName = Type.getInternalName(cl); + Type type = Type.getObjectType(remapper.toBukkitRemapper().mapType(internalName)); + return type.getInternalName().replace('/', '.'); + } + + // srg -> bukkit + public static String handlePackageGetName(String name) { if (name.startsWith(PREFIX)) { return PREFIX + "server." + ArclightVersion.current().packageName(); } else { @@ -147,6 +103,111 @@ public class ArclightReflectionHandler extends ClassLoader { } } + // srg -> bukkit + public static String redirectPackageGetName(Package pkg) { + return handlePackageGetName(pkg.getName()); + } + + // bukkit -> srg + public static String handleClassForName(String cl) { + String mapped = remapper.map(cl.replace('.', '/')).replace('/', '.'); + if (mapped.equals(cl) && cl.startsWith(PREFIX)) { + int i = cl.lastIndexOf('.'); + if (i > 0) { + String nest = cl.substring(0, i).replace('.', '/') + "$" + cl.substring(i + 1); + String replace = remapper.map(nest).replace('/', '.'); + return replace.equals(nest) ? mapped : replace; + } + } + return mapped; + } + + // bukkit -> srg + public static Class redirectClassForName(String cl) throws ClassNotFoundException { + return redirectClassForName(cl, true, Unsafe.getCallerClass().getClassLoader()); + } + + // bukkit -> srg + public static Object[] handleClassForName(String cl, boolean initialize, ClassLoader classLoader) { + return new Object[]{handleClassForName(cl), initialize, classLoader}; + } + + // bukkit -> srg + public static Class redirectClassForName(String cl, boolean initialize, ClassLoader classLoader) throws ClassNotFoundException { + if (!cl.startsWith(PREFIX)) { + return Class.forName(cl, initialize, classLoader); + } + try { + String replace = remapper.mapType(cl.replace('.', '/')).replace('/', '.'); + return Class.forName(replace, initialize, classLoader); + } catch (ClassNotFoundException e) { // nested/inner class + int i = cl.lastIndexOf('.'); + if (i > 0) { + String replace = cl.substring(0, i).replace('.', '/') + "$" + cl.substring(i + 1); + replace = remapper.mapType(replace).replace('/', '.').replace('$', '.'); + return Class.forName(replace, initialize, classLoader); + } else throw e; + } + } + + // bukkit -> srg + public static Object[] handleClassGetField(Class cl, String bukkitName) { + return new Object[]{cl, remapper.tryMapFieldToSrg(cl, bukkitName)}; + } + + // bukkit -> srg + public static Field redirectClassGetField(Class cl, String bukkitName) throws NoSuchFieldException { + String field = remapper.tryMapFieldToSrg(cl, bukkitName); + return cl.getField(field); + } + + // bukkit -> srg + public static Object[] handleClassGetDeclaredField(Class cl, String bukkitName) { + return handleClassGetField(cl, bukkitName); + } + + // bukkit -> srg + public static Field redirectClassGetDeclaredField(Class cl, String bukkitName) throws NoSuchFieldException { + String field = remapper.tryMapDecFieldToSrg(cl, bukkitName); + return cl.getDeclaredField(field); + } + + // bukkit -> srg + public static Object[] handleClassGetMethod(Class cl, String bukkitName, Class... pTypes) { + Method method = remapper.tryMapMethodToSrg(cl, bukkitName, pTypes); + return new Object[]{cl, method == null ? bukkitName : method.getName(), pTypes}; + } + + // bukkit -> srg + public static Method redirectClassGetMethod(Class cl, String bukkitName, Class... pTypes) throws NoSuchMethodException { + Method method = remapper.tryMapMethodToSrg(cl, bukkitName, pTypes); + if (method != null) { + return method; + } else { + return cl.getMethod(bukkitName, pTypes); + } + } + + // bukkit -> srg + public static Object[] handleClassGetDeclaredMethod(Class cl, String bukkitName, Class... pTypes) { + return handleClassGetMethod(cl, bukkitName, pTypes); + } + + // bukkit -> srg + public static Method redirectClassGetDeclaredMethod(Class cl, String bukkitName, Class... pTypes) throws NoSuchMethodException { + Method method = remapper.tryMapMethodToSrg(cl, bukkitName, pTypes); + if (method != null) { + return method; + } else { + return cl.getDeclaredMethod(bukkitName, pTypes); + } + } + + // bukkit -> srg + public static Object[] handleFromDescStr(String desc, ClassLoader classLoader) { + return new Object[]{remapper.mapMethodDesc(desc), classLoader}; + } + // bukkit -> srg public static MethodType redirectFromDescStr(String desc, ClassLoader classLoader) { String methodDesc = remapper.mapMethodDesc(desc); @@ -154,7 +215,13 @@ public class ArclightReflectionHandler extends ClassLoader { } // bukkit -> srg - public static MethodHandle redirectFindStatic(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType) throws NoSuchMethodException, IllegalAccessException { + public static Object[] handleLookupFindStatic(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType) { + Method method = remapper.tryMapMethodToSrg(cl, name, methodType.parameterArray()); + return new Object[]{lookup, cl, method == null ? name : method.getName(), methodType}; + } + + // bukkit -> srg + public static MethodHandle redirectLookupFindStatic(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType) throws NoSuchMethodException, IllegalAccessException { Method method = remapper.tryMapMethodToSrg(cl, name, methodType.parameterArray()); if (method != null) { return lookup.findStatic(cl, method.getName(), methodType); @@ -164,14 +231,12 @@ public class ArclightReflectionHandler extends ClassLoader { } // bukkit -> srg - public static MethodHandle redirectFindVirtual(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType) throws NoSuchMethodException, IllegalAccessException { - if (ClassLoaderAdapter.isDefineClassMethod(cl, name, methodType)) { - Class[] pTypes = methodType.parameterArray(); - Class[] classes = new Class[pTypes.length + 1]; - classes[0] = ClassLoader.class; - System.arraycopy(pTypes, 0, classes, 1, pTypes.length); - return lookup.findStatic(ArclightReflectionHandler.class, name, MethodType.methodType(Class.class, classes)); - } + public static Object[] handleLookupFindVirtual(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType) { + return handleLookupFindStatic(lookup, cl, name, methodType); + } + + // bukkit -> srg + public static MethodHandle redirectLookupFindVirtual(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType) throws NoSuchMethodException, IllegalAccessException { Method method = remapper.tryMapMethodToSrg(cl, name, methodType.parameterArray()); if (method != null) { return lookup.findVirtual(cl, method.getName(), methodType); @@ -181,7 +246,13 @@ public class ArclightReflectionHandler extends ClassLoader { } // bukkit -> srg - public static MethodHandle redirectFindSpecial(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType, Class spec) throws NoSuchMethodException, IllegalAccessException { + public static Object[] handleLookupFindSpecial(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType, Class spec) { + Method method = remapper.tryMapMethodToSrg(cl, name, methodType.parameterArray()); + return new Object[]{lookup, cl, method == null ? name : method.getName(), methodType, spec}; + } + + // bukkit -> srg + public static MethodHandle redirectLookupFindSpecial(MethodHandles.Lookup lookup, Class cl, String name, MethodType methodType, Class spec) throws NoSuchMethodException, IllegalAccessException { Method method = remapper.tryMapMethodToSrg(cl, name, methodType.parameterArray()); if (method != null) { return lookup.findSpecial(cl, method.getName(), methodType, spec); @@ -191,49 +262,102 @@ public class ArclightReflectionHandler extends ClassLoader { } // bukkit -> srg - public static MethodHandle redirectFindGetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { + public static Object[] handleLookupFindGetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) { + String field = remapper.tryMapFieldToSrg(cl, name); + return new Object[]{lookup, cl, field, type}; + } + + // bukkit -> srg + public static MethodHandle redirectLookupFindGetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { String field = remapper.tryMapFieldToSrg(cl, name); return lookup.findGetter(cl, field, type); } // bukkit -> srg - public static MethodHandle redirectFindSetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { + public static Object[] handleLookupFindSetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) { + return handleLookupFindGetter(lookup, cl, name, type); + } + + // bukkit -> srg + public static MethodHandle redirectLookupFindSetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { String field = remapper.tryMapFieldToSrg(cl, name); return lookup.findSetter(cl, field, type); } // bukkit -> srg - public static MethodHandle redirectFindStaticGetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { + public static Object[] handleLookupFindStaticGetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) { + return handleLookupFindGetter(lookup, cl, name, type); + } + + // bukkit -> srg + public static MethodHandle redirectLookupFindStaticGetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { String field = remapper.tryMapFieldToSrg(cl, name); return lookup.findStaticGetter(cl, field, type); } // bukkit -> srg - public static MethodHandle redirectFindStaticSetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { + public static Object[] handleLookupFindStaticSetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) { + return handleLookupFindGetter(lookup, cl, name, type); + } + + // bukkit -> srg + public static MethodHandle redirectLookupFindStaticSetter(MethodHandles.Lookup lookup, Class cl, String name, Class type) throws IllegalAccessException, NoSuchFieldException { String field = remapper.tryMapFieldToSrg(cl, name); return lookup.findStaticSetter(cl, field, type); } + public static Object[] handleClassLoaderLoadClass(ClassLoader loader, String binaryName) { + return new Object[]{loader, binaryName.startsWith(PREFIX) ? + remapper.mapType(binaryName.replace('.', '/')).replace('/', '.') + : binaryName}; + } + // bukkit -> srg - public static Class redirectClassLoaderLoadClass(ClassLoader loader, String canonicalName) throws ClassNotFoundException { - if (!canonicalName.startsWith(PREFIX)) { - return loader.loadClass(canonicalName); + public static Class redirectClassLoaderLoadClass(ClassLoader loader, String binaryName) throws ClassNotFoundException { + if (!binaryName.startsWith(PREFIX)) { + return loader.loadClass(binaryName); } - String replace = remapper.mapType(canonicalName.replace('.', '/')).replace('/', '.'); + String replace = remapper.mapType(binaryName.replace('.', '/')).replace('/', '.'); return loader.loadClass(replace); } - public static Object redirectDefineClassInvoke(Method method, Object src, Object[] param) throws Exception { - if (method.getDeclaringClass() == ArclightReflectionHandler.class && method.getName().equals("defineClass")) { - Class[] classes = new Class[method.getParameterCount() + 1]; - classes[0] = ClassLoader.class; - System.arraycopy(method.getParameterTypes(), 0, classes, 1, method.getParameterCount()); - method = ArclightReflectionHandler.class.getMethod(method.getName(), classes); - Object[] args = new Object[param.length + 1]; - args[0] = src; - System.arraycopy(param, 0, args, 1, param.length); - return method.invoke(null, args); - } else return method.invoke(src, param); + public static Object[] handleMethodInvoke(Method method, Object src, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Product4[], String, Class[]> invokeRule = ArclightRedirectAdapter.getInvokeRule(method); + if (invokeRule != null) { + if (invokeRule._3 != null && method.getParameterCount() > 0) { + Method handleMethod = remapper.getGeneratedHandlerClass().getMethod(invokeRule._3, invokeRule._4); + if (handleMethod.getReturnType().isArray()) { + return (Object[]) handleMethod.invoke(null, ArrayUtil.prepend(param, src)); + } else { + return new Object[]{method, src, handleMethod.invoke(null, param)}; + } + } else { + Method handleMethod = remapper.getGeneratedHandlerClass().getMethod(invokeRule._1, invokeRule._2); + return new Object[]{handleMethod, null, ArrayUtil.prepend(param, src)}; + } + } else { + return new Object[]{method, src, param}; + } + } + + public static Object redirectMethodInvoke(Method method, Object src, Object[] param) throws Throwable { + Product4[], String, Class[]> invokeRule = ArclightRedirectAdapter.getInvokeRule(method); + if (invokeRule != null) { + if (invokeRule._3 != null && method.getParameterCount() > 0) { + Method handleMethod = remapper.getGeneratedHandlerClass().getMethod(invokeRule._3, invokeRule._4); + if (handleMethod.getReturnType().isArray()) { + Object[] result = (Object[]) handleMethod.invoke(null, ArrayUtil.prepend(param, src)); + return method.invoke(result[0], Arrays.copyOfRange(result, 1, result.length)); + } else { + return method.invoke(src, handleMethod.invoke(null, param)); + } + } else { + Method handleMethod = remapper.getGeneratedHandlerClass().getMethod(invokeRule._1, invokeRule._2); + return handleMethod.invoke(null, ArrayUtil.prepend(param, src)); + } + } else { + return method.invoke(src, param); + } } public static Class defineClass(ClassLoader loader, byte[] b, int off, int len) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/util/ArrayUtil.java b/arclight-common/src/main/java/io/izzel/arclight/common/util/ArrayUtil.java new file mode 100644 index 00000000..20433d60 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/util/ArrayUtil.java @@ -0,0 +1,20 @@ +package io.izzel.arclight.common.util; + +import java.util.function.IntFunction; + +public class ArrayUtil { + + public static Object[] prepend(Object[] array, Object obj) { + Object[] newArray = new Object[array.length + 1]; + newArray[0] = obj; + System.arraycopy(array, 0, newArray, 1, array.length); + return newArray; + } + + public static T[] prepend(T[] array, T obj, IntFunction factory) { + T[] newArray = factory.apply(array.length + 1); + newArray[0] = obj; + System.arraycopy(array, 0, newArray, 1, array.length); + return newArray; + } +} diff --git a/arclight-forge-1.16/build.gradle b/arclight-forge-1.16/build.gradle index 1be0b21a..0fe3809e 100644 --- a/arclight-forge-1.16/build.gradle +++ b/arclight-forge-1.16/build.gradle @@ -56,6 +56,7 @@ repositories { maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url = 'https://files.minecraftforge.net/maven/' } maven { url = 'https://jitpack.io/' } + maven { url = 'https://maven.izzel.io/releases' } } def embedLibs = [/*'org.spongepowered:mixin:0.8.1',*/ 'org.ow2.asm:asm-util:9.0', @@ -81,6 +82,7 @@ dependencies { embed 'net.md-5:bungeecord-chat:1.16-R0.4@jar' embed "org.spigotmc:spigot-api:$minecraftVersion-R0.1-SNAPSHOT@jar" embed 'com.github.ArclightTeam:mixin-tools:1.0.0@jar' + embed 'io.izzel:tools:1.0.+' annotationProcessor 'org.spongepowered:mixin:0.8.2:processor' annotationProcessor 'com.github.ArclightTeam:mixin-tools:1.0.0' }