Remap custom classloader. Close #16.
This commit is contained in:
parent
18b5977853
commit
e2784ffb0f
|
@ -5,6 +5,7 @@ import io.izzel.arclight.common.asm.SwitchTableFixer;
|
||||||
import io.izzel.arclight.common.bridge.bukkit.JavaPluginLoaderBridge;
|
import io.izzel.arclight.common.bridge.bukkit.JavaPluginLoaderBridge;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
|
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
|
||||||
import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper;
|
import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.RemappingClassLoader;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.PluginDescriptionFile;
|
import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
import org.bukkit.plugin.java.JavaPluginLoader;
|
import org.bukkit.plugin.java.JavaPluginLoader;
|
||||||
|
@ -25,7 +26,7 @@ import java.util.jar.JarFile;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
@Mixin(targets = "org.bukkit.plugin.java.PluginClassLoader", remap = false)
|
@Mixin(targets = "org.bukkit.plugin.java.PluginClassLoader", remap = false)
|
||||||
public class PluginClassLoaderMixin extends URLClassLoader {
|
public class PluginClassLoaderMixin extends URLClassLoader implements RemappingClassLoader {
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
@Shadow @Final private Map<String, Class<?>> classes;
|
@Shadow @Final private Map<String, Class<?>> classes;
|
||||||
|
@ -38,6 +39,14 @@ public class PluginClassLoaderMixin extends URLClassLoader {
|
||||||
|
|
||||||
private ClassLoaderRemapper remapper;
|
private ClassLoaderRemapper remapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoaderRemapper getRemapper() {
|
||||||
|
if (remapper == null) {
|
||||||
|
remapper = ArclightRemapper.createClassLoaderRemapper(this);
|
||||||
|
}
|
||||||
|
return remapper;
|
||||||
|
}
|
||||||
|
|
||||||
public PluginClassLoaderMixin(URL[] urls) {
|
public PluginClassLoaderMixin(URL[] urls) {
|
||||||
super(urls);
|
super(urls);
|
||||||
}
|
}
|
||||||
|
@ -73,10 +82,7 @@ public class PluginClassLoaderMixin extends URLClassLoader {
|
||||||
|
|
||||||
classBytes = SwitchTableFixer.INSTANCE.processClass(classBytes);
|
classBytes = SwitchTableFixer.INSTANCE.processClass(classBytes);
|
||||||
classBytes = Bukkit.getUnsafe().processClass(description, path, classBytes);
|
classBytes = Bukkit.getUnsafe().processClass(description, path, classBytes);
|
||||||
if (remapper == null) {
|
classBytes = this.getRemapper().remapClass(classBytes);
|
||||||
remapper = ArclightRemapper.INSTANCE.createClassLoaderRemapper(this);
|
|
||||||
}
|
|
||||||
classBytes = remapper.remapClass(classBytes);
|
|
||||||
|
|
||||||
int dot = name.lastIndexOf('.');
|
int dot = name.lastIndexOf('.');
|
||||||
if (dot != -1) {
|
if (dot != -1) {
|
||||||
|
|
|
@ -55,10 +55,11 @@ public class ArclightRemapper {
|
||||||
this.toBukkitMapping.setFallbackInheritanceProvider(inheritanceProvider);
|
this.toBukkitMapping.setFallbackInheritanceProvider(inheritanceProvider);
|
||||||
this.transformerList.add(ArclightInterfaceInvokerGen.INSTANCE);
|
this.transformerList.add(ArclightInterfaceInvokerGen.INSTANCE);
|
||||||
this.transformerList.add(ArclightRedirectAdapter.INSTANCE);
|
this.transformerList.add(ArclightRedirectAdapter.INSTANCE);
|
||||||
|
this.transformerList.add(ClassLoaderAdapter.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassLoaderRemapper createClassLoaderRemapper(ClassLoader classLoader) {
|
public static ClassLoaderRemapper createClassLoaderRemapper(ClassLoader classLoader) {
|
||||||
return new ClassLoaderRemapper(copyOf(toNmsMapping), copyOf(toBukkitMapping), classLoader);
|
return new ClassLoaderRemapper(INSTANCE.copyOf(INSTANCE.toNmsMapping), INSTANCE.copyOf(INSTANCE.toBukkitMapping), classLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PluginTransformer> getTransformerList() {
|
public List<PluginTransformer> getTransformerList() {
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
package io.izzel.arclight.common.mod.util.remapper;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import io.izzel.arclight.common.mod.ArclightMod;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.generated.RemappingURLClassLoader;
|
||||||
|
import org.apache.logging.log4j.Marker;
|
||||||
|
import org.apache.logging.log4j.MarkerManager;
|
||||||
|
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.FieldInsnNode;
|
||||||
|
import org.objectweb.asm.tree.FieldNode;
|
||||||
|
import org.objectweb.asm.tree.InsnList;
|
||||||
|
import org.objectweb.asm.tree.InsnNode;
|
||||||
|
import org.objectweb.asm.tree.JumpInsnNode;
|
||||||
|
import org.objectweb.asm.tree.LabelNode;
|
||||||
|
import org.objectweb.asm.tree.MethodInsnNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.TypeInsnNode;
|
||||||
|
import org.objectweb.asm.tree.VarInsnNode;
|
||||||
|
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.SecureClassLoader;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ClassLoaderAdapter implements PluginTransformer {
|
||||||
|
|
||||||
|
public static final ClassLoaderAdapter INSTANCE = new ClassLoaderAdapter();
|
||||||
|
private static final Marker MARKER = MarkerManager.getMarker("CLADAPTER");
|
||||||
|
private static final String CLASSLOADER = "java/lang/ClassLoader";
|
||||||
|
|
||||||
|
private final Map<Type, Map.Entry<Type, Integer>> defineClassTypes = ImmutableMap.<Type, Map.Entry<Type, Integer>>builder()
|
||||||
|
.put(Type.getMethodType("(Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class;"), Maps.immutableEntry(Type.getType(SecureClassLoader.class), 1))
|
||||||
|
.put(Type.getMethodType("(Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/CodeSource;)Ljava/lang/Class;"), Maps.immutableEntry(Type.getType(SecureClassLoader.class), 1))
|
||||||
|
.put(Type.getMethodType("([BII)Ljava/lang/Class;"), Maps.immutableEntry(Type.getType(ClassLoader.class), 0))
|
||||||
|
.put(Type.getMethodType("(Ljava/lang/String;[BII)Ljava/lang/Class;"), Maps.immutableEntry(Type.getType(ClassLoader.class), 1))
|
||||||
|
.put(Type.getMethodType("(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;"), Maps.immutableEntry(Type.getType(ClassLoader.class), 1))
|
||||||
|
.put(Type.getMethodType("(Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/ProtectionDomain;)Ljava/lang/Class;"), Maps.immutableEntry(Type.getType(ClassLoader.class), 1))
|
||||||
|
.build();
|
||||||
|
private final Map<String, String> classLoaderTypes = ImmutableMap.<String, String>builder()
|
||||||
|
.put(Type.getInternalName(URLClassLoader.class), Type.getInternalName(RemappingURLClassLoader.class))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleClass(ClassNode node, ClassLoaderRemapper remapper) {
|
||||||
|
for (MethodNode methodNode : node.methods) {
|
||||||
|
for (AbstractInsnNode insnNode : methodNode.instructions) {
|
||||||
|
if (insnNode.getOpcode() == Opcodes.NEW) {
|
||||||
|
TypeInsnNode typeInsnNode = (TypeInsnNode) insnNode;
|
||||||
|
String replace = classLoaderTypes.get(typeInsnNode.desc);
|
||||||
|
if (replace != null) {
|
||||||
|
AbstractInsnNode next = typeInsnNode.getNext();
|
||||||
|
while (next != null && (next.getOpcode() != Opcodes.INVOKESPECIAL || !((MethodInsnNode) next).name.equals("<init>") || !((MethodInsnNode) next).owner.equals(typeInsnNode.desc))) {
|
||||||
|
next = next.getNext();
|
||||||
|
}
|
||||||
|
if (next == null) continue;
|
||||||
|
ArclightMod.LOGGER.debug(MARKER, "Found new {}/{} call in {} {}", typeInsnNode.desc, ((MethodInsnNode) next).name + ((MethodInsnNode) next).desc, node.name, methodNode.name + methodNode.desc);
|
||||||
|
((MethodInsnNode) next).owner = replace;
|
||||||
|
typeInsnNode.desc = replace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClassInfo info = classInfo(node);
|
||||||
|
if (info == null) return;
|
||||||
|
ArclightMod.LOGGER.debug(MARKER, "Transforming classloader class {}", node.name);
|
||||||
|
if (!info.remapping) {
|
||||||
|
implementIntf(node);
|
||||||
|
}
|
||||||
|
for (MethodNode methodNode : node.methods) {
|
||||||
|
for (AbstractInsnNode insnNode : methodNode.instructions) {
|
||||||
|
if (insnNode instanceof MethodInsnNode) {
|
||||||
|
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
|
||||||
|
if (methodInsnNode.getOpcode() == Opcodes.INVOKESPECIAL && methodNode.name.equals("<init>") && methodInsnNode.name.equals("<init>") && methodInsnNode.owner.equals(node.superName)) {
|
||||||
|
methodInsnNode.owner = info.superName;
|
||||||
|
}
|
||||||
|
if (methodInsnNode.name.equals("defineClass")) {
|
||||||
|
Type descType = Type.getMethodType(methodInsnNode.desc);
|
||||||
|
Map.Entry<Type, Integer> entry = defineClassTypes.get(descType);
|
||||||
|
if (entry != null && GlobalClassRepo.inheritanceProvider().getAll(methodInsnNode.owner).contains(entry.getKey().getInternalName())) {
|
||||||
|
ArclightMod.LOGGER.debug(MARKER, "Found defineClass {} call in {} {}", descType.getInternalName(), node.name, methodNode.name + methodNode.desc);
|
||||||
|
int index = entry.getValue();
|
||||||
|
InsnList insnList = new InsnList();
|
||||||
|
Type[] argumentTypes = descType.getArgumentTypes();
|
||||||
|
int[] argsMap = argsMap(argumentTypes);
|
||||||
|
storeArgs(argumentTypes, argsMap, methodNode, insnList);
|
||||||
|
insnList.add(new InsnNode(Opcodes.DUP));
|
||||||
|
insnList.add(new VarInsnNode(argumentTypes[index].getOpcode(Opcodes.ILOAD), methodNode.maxLocals + argsMap[index]));
|
||||||
|
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(ClassLoaderAdapter.class), "remapClassContent", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false));
|
||||||
|
insnList.add(new TypeInsnNode(Opcodes.CHECKCAST, argumentTypes[index].getInternalName()));
|
||||||
|
insnList.add(new VarInsnNode(argumentTypes[index].getOpcode(Opcodes.ISTORE), methodNode.maxLocals + argsMap[index]));
|
||||||
|
loadArgs(argumentTypes, argsMap, methodNode, insnList);
|
||||||
|
methodNode.instructions.insertBefore(methodInsnNode, insnList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.superName = info.superName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static Object remapClassContent(ClassLoader classLoader, Object classContent) {
|
||||||
|
ArclightMod.LOGGER.trace(MARKER, "Remapping class content {} from classloader {}", classContent, classLoader);
|
||||||
|
if (!(classLoader instanceof RemappingClassLoader)) {
|
||||||
|
throw new IllegalArgumentException("" + classLoader + " is not a remapping class loader!");
|
||||||
|
}
|
||||||
|
byte[] classBytes;
|
||||||
|
if (classContent instanceof byte[]) {
|
||||||
|
classBytes = ((byte[]) classContent);
|
||||||
|
} else if (classContent instanceof ByteBuffer) {
|
||||||
|
classBytes = new byte[((ByteBuffer) classContent).remaining()];
|
||||||
|
((ByteBuffer) classContent).get(classBytes);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("" + classContent + " is not a recognized class content type!");
|
||||||
|
}
|
||||||
|
byte[] bytes = ((RemappingClassLoader) classLoader).getRemapper().remapClass(classBytes);
|
||||||
|
if (classContent instanceof byte[]) {
|
||||||
|
return bytes;
|
||||||
|
} else {
|
||||||
|
return ByteBuffer.wrap(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] argsMap(Type[] args) {
|
||||||
|
int[] ints = new int[args.length];
|
||||||
|
int offset = 0;
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
Type arg = args[i];
|
||||||
|
ints[i] = offset;
|
||||||
|
offset += arg.getSize();
|
||||||
|
}
|
||||||
|
return ints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void storeArgs(Type[] args, int[] argsMap, MethodNode node, InsnList list) {
|
||||||
|
int start = node.maxLocals;
|
||||||
|
for (int i = args.length - 1; i >= 0; i--) {
|
||||||
|
Type arg = args[i];
|
||||||
|
list.add(new VarInsnNode(arg.getOpcode(Opcodes.ISTORE), start + argsMap[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadArgs(Type[] args, int[] argsMap, MethodNode node, InsnList list) {
|
||||||
|
int start = node.maxLocals;
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
Type arg = args[i];
|
||||||
|
list.add(new VarInsnNode(arg.getOpcode(Opcodes.ILOAD), start + argsMap[i]));
|
||||||
|
node.maxLocals += argsMap[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void implementIntf(ClassNode node) {
|
||||||
|
ArclightMod.LOGGER.debug(MARKER, "Implementing RemappingClassLoader for class {}", node.name);
|
||||||
|
FieldNode remapper = new FieldNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC, "remapper", Type.getDescriptor(ClassLoaderRemapper.class), null, null);
|
||||||
|
MethodNode methodNode = new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "getRemapper", Type.getMethodDescriptor(Type.getType(ClassLoaderRemapper.class)), null, null);
|
||||||
|
InsnList list = new InsnList();
|
||||||
|
LabelNode labelNode = new LabelNode();
|
||||||
|
list.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
list.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, remapper.name, remapper.desc));
|
||||||
|
list.add(new JumpInsnNode(Opcodes.IFNONNULL, labelNode));
|
||||||
|
list.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
list.add(new InsnNode(Opcodes.DUP));
|
||||||
|
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(ArclightRemapper.class), "createClassLoaderRemapper", Type.getMethodDescriptor(Type.getType(ClassLoaderRemapper.class), Type.getType(ClassLoader.class)), false));
|
||||||
|
list.add(new FieldInsnNode(Opcodes.PUTFIELD, node.name, remapper.name, remapper.desc));
|
||||||
|
list.add(labelNode);
|
||||||
|
list.add(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
list.add(new FieldInsnNode(Opcodes.GETFIELD, node.name, remapper.name, remapper.desc));
|
||||||
|
list.add(new InsnNode(Opcodes.ARETURN));
|
||||||
|
methodNode.instructions = list;
|
||||||
|
node.fields.add(remapper);
|
||||||
|
node.methods.add(methodNode);
|
||||||
|
node.interfaces.add(Type.getInternalName(RemappingClassLoader.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassInfo classInfo(ClassNode node) {
|
||||||
|
ClassInfo info = new ClassInfo();
|
||||||
|
Collection<String> parents = GlobalClassRepo.inheritanceProvider().getAll(node.superName);
|
||||||
|
if (!parents.contains(CLASSLOADER)) return null;
|
||||||
|
for (String s : classLoaderTypes.keySet()) {
|
||||||
|
if (parents.contains(s)) {
|
||||||
|
info.remapping = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String s = classLoaderTypes.get(node.superName);
|
||||||
|
if (s != null) {
|
||||||
|
info.superName = s;
|
||||||
|
} else {
|
||||||
|
info.superName = node.superName;
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassInfo {
|
||||||
|
|
||||||
|
private String superName;
|
||||||
|
private boolean remapping;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.izzel.arclight.common.mod.util.remapper;
|
||||||
|
|
||||||
|
import cpw.mods.modlauncher.api.ITransformingClassLoader;
|
||||||
|
|
||||||
|
public interface RemappingClassLoader {
|
||||||
|
|
||||||
|
ClassLoaderRemapper getRemapper();
|
||||||
|
|
||||||
|
static ClassLoader asTransforming(ClassLoader classLoader) {
|
||||||
|
boolean found = false;
|
||||||
|
while (classLoader != null) {
|
||||||
|
if (classLoader instanceof ITransformingClassLoader || classLoader instanceof RemappingClassLoader) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
classLoader = classLoader.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found ? classLoader : RemappingClassLoader.class.getClassLoader();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package io.izzel.arclight.common.mod.util.remapper.generated;
|
||||||
|
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper;
|
||||||
|
import io.izzel.arclight.common.mod.util.remapper.RemappingClassLoader;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.JarURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLStreamHandlerFactory;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
|
||||||
|
public class RemappingURLClassLoader extends URLClassLoader implements RemappingClassLoader {
|
||||||
|
|
||||||
|
static {
|
||||||
|
ClassLoader.registerAsParallelCapable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemappingURLClassLoader(URL[] urls, ClassLoader parent) {
|
||||||
|
super(urls, RemappingClassLoader.asTransforming(parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemappingURLClassLoader(URL[] urls) {
|
||||||
|
super(urls, RemappingClassLoader.asTransforming(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemappingURLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
|
||||||
|
super(urls, RemappingClassLoader.asTransforming(parent), factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
|
Class<?> result = null;
|
||||||
|
String path = name.replace('.', '/').concat(".class");
|
||||||
|
URL resource = this.getResource(path);
|
||||||
|
if (resource != null) {
|
||||||
|
try {
|
||||||
|
URLConnection connection = resource.openConnection();
|
||||||
|
byte[] bytes = getRemapper().remapClass(ByteStreams.toByteArray(connection.getInputStream()));
|
||||||
|
int i = name.lastIndexOf('.');
|
||||||
|
if (i != -1) {
|
||||||
|
String pkgName = name.substring(0, i);
|
||||||
|
if (getPackage(pkgName) == null) {
|
||||||
|
if (connection instanceof JarURLConnection && ((JarURLConnection) connection).getManifest() != null) {
|
||||||
|
this.definePackage(pkgName, ((JarURLConnection) connection).getManifest(), ((JarURLConnection) connection).getJarFileURL());
|
||||||
|
} else {
|
||||||
|
this.definePackage(pkgName, null, null, null, null, null, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CodeSource codeSource;
|
||||||
|
if (connection instanceof JarURLConnection) {
|
||||||
|
codeSource = new CodeSource(((JarURLConnection) connection).getJarFileURL(), ((JarURLConnection) connection).getJarEntry().getCodeSigners());
|
||||||
|
} else {
|
||||||
|
codeSource = null;
|
||||||
|
}
|
||||||
|
result = this.defineClass(name, bytes, 0, bytes.length, codeSource);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ClassNotFoundException(name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
throw new ClassNotFoundException(name);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClassLoaderRemapper remapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoaderRemapper getRemapper() {
|
||||||
|
if (remapper == null) {
|
||||||
|
remapper = ArclightRemapper.createClassLoaderRemapper(this);
|
||||||
|
}
|
||||||
|
return remapper;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user