Refactor redirect logic, should break nothing

This commit is contained in:
IzzelAliz 2021-02-09 00:03:59 +08:00
parent ac27079e3f
commit d5f39d57cc
8 changed files with 510 additions and 282 deletions

View File

@ -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')
}

View File

@ -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) {

View File

@ -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<MethodInsnNode, MethodInsnNode> METHOD_REDIRECTS = ImmutableMap
.<MethodInsnNode, MethodInsnNode>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<String, Product2<String, MethodInsnNode>> METHOD_MODIFY = HashMultimap.create();
private static final Multimap<String, Product2<String, MethodInsnNode>> METHOD_REDIRECT = HashMultimap.create();
private static final Map<Method, Product4<String, Class<?>[], 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<?>[], 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<AbstractInsnNode> 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<MethodInsnNode, MethodInsnNode> 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<Product2<String, MethodInsnNode>> col = METHOD_REDIRECT.get(key);
for (Product2<String, MethodInsnNode> 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<Product2<String, MethodInsnNode>> modifyArgsCol = METHOD_MODIFY.get(key);
for (Product2<String, MethodInsnNode> 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<MethodInsnNode, MethodInsnNode> 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<Product2<String, MethodInsnNode>> methodRedirectCol = METHOD_REDIRECT.get(key);
for (Product2<String, MethodInsnNode> 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<String, Class<?>[]> 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);
}
}
}

View File

@ -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);
}

View File

@ -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<String, BiMap<Field, String>> cacheFields = new ConcurrentHashMap<>();
private final Map<String, Map.Entry<Map<Method, String>, Map<WrappedMethod, Method>>> 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;
}
}

View File

@ -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<?>[], 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<?>[], 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) {

View File

@ -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> T[] prepend(T[] array, T obj, IntFunction<T[]> factory) {
T[] newArray = factory.apply(array.length + 1);
newArray[0] = obj;
System.arraycopy(array, 0, newArray, 1, array.length);
return newArray;
}
}

View File

@ -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'
}