mirror of
https://github.com/Minestom/Minestom.git
synced 2024-11-18 00:25:30 +01:00
Start of tree-based classloading
This commit is contained in:
parent
35f7b27135
commit
47eb0084eb
@ -1,6 +1,6 @@
|
||||
package net.minestom.server;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.mixins.MixinCodeModifier;
|
||||
import net.minestom.server.extras.selfmodification.mixins.MixinServiceMinestom;
|
||||
import org.spongepowered.asm.launch.MixinBootstrap;
|
||||
@ -12,15 +12,15 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Used to launch Minestom with the {@link MinestomOverwriteClassLoader} to allow for self-modifications
|
||||
* Used to launch Minestom with the {@link MinestomRootClassLoader} to allow for self-modifications
|
||||
*/
|
||||
public final class Bootstrap {
|
||||
|
||||
public static void bootstrap(String mainClassFullName, String[] args) {
|
||||
try {
|
||||
ClassLoader classLoader = MinestomOverwriteClassLoader.getInstance();
|
||||
ClassLoader classLoader = MinestomRootClassLoader.getInstance();
|
||||
startMixin(args);
|
||||
MinestomOverwriteClassLoader.getInstance().addCodeModifier(new MixinCodeModifier());
|
||||
MinestomRootClassLoader.getInstance().addCodeModifier(new MixinCodeModifier());
|
||||
|
||||
MixinServiceMinestom.gotoPreinitPhase();
|
||||
// ensure extensions are loaded when starting the server
|
||||
@ -53,6 +53,6 @@ public final class Bootstrap {
|
||||
doInit.invoke(null, CommandLineOptions.ofArgs(Arrays.asList(args)));
|
||||
|
||||
MixinBootstrap.getPlatform().inject();
|
||||
Mixins.getConfigs().forEach(c -> MinestomOverwriteClassLoader.getInstance().protectedPackages.add(c.getConfig().getMixinPackage()));
|
||||
Mixins.getConfigs().forEach(c -> MinestomRootClassLoader.getInstance().protectedPackages.add(c.getConfig().getMixinPackage()));
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ import com.google.gson.Gson;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.minestom.dependencies.DependencyGetter;
|
||||
import net.minestom.dependencies.maven.MavenRepository;
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomExtensionClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -76,7 +77,7 @@ public class ExtensionManager {
|
||||
// TODO: If we want modifications to be possible, we need to add these urls to the current classloader
|
||||
// TODO: Indeed, without adding the urls, the classloader is not able to load the bytecode of extension classes
|
||||
// TODO: Whether we want to allow extensions to modify one-another is our choice now.
|
||||
loader = newClassLoader(urls);
|
||||
loader = newClassLoader(discoveredExtension, urls);
|
||||
|
||||
// Create ExtensionDescription (authors, version etc.)
|
||||
String extensionName = discoveredExtension.getName();
|
||||
@ -340,8 +341,8 @@ public class ExtensionManager {
|
||||
if (!(cl instanceof URLClassLoader)) {
|
||||
throw new IllegalStateException("Current class loader is not a URLClassLoader, but " + cl + ". This prevents adding URLs into the classpath at runtime.");
|
||||
}
|
||||
if(cl instanceof MinestomOverwriteClassLoader) {
|
||||
((MinestomOverwriteClassLoader) cl).addURL(dependency); // no reflection warnings for us!
|
||||
if(cl instanceof MinestomRootClassLoader) {
|
||||
((MinestomRootClassLoader) cl).addURL(dependency); // no reflection warnings for us!
|
||||
} else {
|
||||
try {
|
||||
Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
@ -364,8 +365,12 @@ public class ExtensionManager {
|
||||
* @param urls {@link URL} (usually a JAR) that should be loaded.
|
||||
*/
|
||||
@NotNull
|
||||
public URLClassLoader newClassLoader(@NotNull URL[] urls) {
|
||||
return URLClassLoader.newInstance(urls, ExtensionManager.class.getClassLoader());
|
||||
public URLClassLoader newClassLoader(@NotNull DiscoveredExtension extension, @NotNull URL[] urls) {
|
||||
MinestomRootClassLoader root = MinestomRootClassLoader.getInstance();
|
||||
MinestomExtensionClassLoader loader = new MinestomExtensionClassLoader(extension.getName(), urls, root);
|
||||
// TODO: tree structure
|
||||
root.addChild(loader);
|
||||
return loader;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -393,11 +398,11 @@ public class ExtensionManager {
|
||||
*/
|
||||
private void setupCodeModifiers(@NotNull List<DiscoveredExtension> extensions) {
|
||||
final ClassLoader cl = getClass().getClassLoader();
|
||||
if (!(cl instanceof MinestomOverwriteClassLoader)) {
|
||||
if (!(cl instanceof MinestomRootClassLoader)) {
|
||||
log.warn("Current class loader is not a MinestomOverwriteClassLoader, but " + cl + ". This disables code modifiers (Mixin support is therefore disabled)");
|
||||
return;
|
||||
}
|
||||
MinestomOverwriteClassLoader modifiableClassLoader = (MinestomOverwriteClassLoader) cl;
|
||||
MinestomRootClassLoader modifiableClassLoader = (MinestomRootClassLoader) cl;
|
||||
log.info("Start loading code modifiers...");
|
||||
for (DiscoveredExtension extension : extensions) {
|
||||
try {
|
||||
|
@ -3,7 +3,7 @@ package net.minestom.server.extras.selfmodification;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
/**
|
||||
* Will be called by {@link MinestomOverwriteClassLoader} to transform classes at load-time
|
||||
* Will be called by {@link MinestomRootClassLoader} to transform classes at load-time
|
||||
*/
|
||||
public abstract class CodeModifier {
|
||||
/**
|
||||
|
@ -0,0 +1,70 @@
|
||||
package net.minestom.server.extras.selfmodification;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class MinestomExtensionClassLoader extends URLClassLoader {
|
||||
/**
|
||||
* Root ClassLoader, everything goes through it before any attempt at loading is done inside this classloader
|
||||
*/
|
||||
private final MinestomRootClassLoader root;
|
||||
private final List<MinestomExtensionClassLoader> children = new LinkedList<>();
|
||||
|
||||
public MinestomExtensionClassLoader(String name, URL[] urls, MinestomRootClassLoader root) {
|
||||
super(name, urls, root);
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
return root.loadClass(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
return root.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes the name is not null, nor it does represent a protected class
|
||||
* @param name
|
||||
* @return
|
||||
* @throws ClassNotFoundException if the class is not found inside this classloader
|
||||
*/
|
||||
public Class<?> loadClassAsChild(String name, boolean resolve) throws ClassNotFoundException {
|
||||
for(MinestomExtensionClassLoader child : children) {
|
||||
try {
|
||||
Class<?> loaded = child.loadClassAsChild(name, resolve);
|
||||
return loaded;
|
||||
} catch (ClassNotFoundException e) {
|
||||
// move on to next child
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> loadedClass = findLoadedClass(name);
|
||||
if(loadedClass != null) {
|
||||
return loadedClass;
|
||||
}
|
||||
// not in children, attempt load in this classloader
|
||||
String path = name.replace(".", "/") + ".class";
|
||||
InputStream in = getResourceAsStream(path);
|
||||
if(in == null) {
|
||||
throw new ClassNotFoundException("Could not load class "+name);
|
||||
}
|
||||
try(in) {
|
||||
byte[] bytes = in.readAllBytes();
|
||||
bytes = root.transformBytes(bytes, name);
|
||||
Class<?> clazz = defineClass(name, bytes, 0, bytes.length);
|
||||
if(resolve) {
|
||||
resolveClass(clazz);
|
||||
}
|
||||
return clazz;
|
||||
} catch (IOException e) {
|
||||
throw new ClassNotFoundException("Could not load class "+name, e);
|
||||
}
|
||||
}
|
||||
}
|
@ -22,9 +22,9 @@ import java.util.Set;
|
||||
* Class Loader that can modify class bytecode when they are loaded
|
||||
*/
|
||||
@Slf4j
|
||||
public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
public class MinestomRootClassLoader extends URLClassLoader {
|
||||
|
||||
private static MinestomOverwriteClassLoader INSTANCE;
|
||||
private static MinestomRootClassLoader INSTANCE;
|
||||
|
||||
/**
|
||||
* Classes that cannot be loaded/modified by this classloader.
|
||||
@ -60,17 +60,18 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
|
||||
// TODO: priorities?
|
||||
private final List<CodeModifier> modifiers = new LinkedList<>();
|
||||
private final List<MinestomExtensionClassLoader> children = new LinkedList<>();
|
||||
|
||||
private MinestomOverwriteClassLoader(ClassLoader parent) {
|
||||
super("Minestom ClassLoader", extractURLsFromClasspath(), parent);
|
||||
private MinestomRootClassLoader(ClassLoader parent) {
|
||||
super("Minestom Root ClassLoader", extractURLsFromClasspath(), parent);
|
||||
asmClassLoader = newChild(new URL[0]);
|
||||
}
|
||||
|
||||
public static MinestomOverwriteClassLoader getInstance() {
|
||||
public static MinestomRootClassLoader getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (MinestomOverwriteClassLoader.class) {
|
||||
synchronized (MinestomRootClassLoader.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new MinestomOverwriteClassLoader(MinestomOverwriteClassLoader.class.getClassLoader());
|
||||
INSTANCE = new MinestomRootClassLoader(MinestomRootClassLoader.class.getClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,7 +117,7 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
return define(name, loadBytes(name, true), resolve);
|
||||
return define(name, resolve);
|
||||
} catch (Exception ex) {
|
||||
log.trace("Fail to load class, resorting to parent loader: " + name, ex);
|
||||
// fail to load class, let parent load
|
||||
@ -137,13 +138,29 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
return true;
|
||||
}
|
||||
|
||||
private Class<?> define(String name, byte[] bytes, boolean resolve) {
|
||||
private Class<?> define(String name, boolean resolve) throws IOException, ClassNotFoundException {
|
||||
try {
|
||||
byte[] bytes = loadBytes(name, true);
|
||||
Class<?> defined = defineClass(name, bytes, 0, bytes.length);
|
||||
log.trace("Loaded with code modifiers: " + name);
|
||||
if (resolve) {
|
||||
resolveClass(defined);
|
||||
}
|
||||
return defined;
|
||||
} catch (ClassNotFoundException e) {
|
||||
// could not load inside this classloader, attempt with children
|
||||
Class<?> defined = null;
|
||||
for(MinestomExtensionClassLoader subloader : children) {
|
||||
try {
|
||||
defined = subloader.loadClassAsChild(name, resolve);
|
||||
log.trace("Loaded from child {}: {}", subloader, name);
|
||||
return defined;
|
||||
} catch (ClassNotFoundException e1) {
|
||||
// not found inside this child, move on to next
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,9 +180,16 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
if(input == null) {
|
||||
throw new ClassNotFoundException("Could not find resource "+path);
|
||||
}
|
||||
byte[] bytes = input.readAllBytes();
|
||||
if (transform && !isProtected(name)) {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
byte[] originalBytes = input.readAllBytes();
|
||||
if(transform) {
|
||||
return transformBytes(originalBytes, name);
|
||||
}
|
||||
return originalBytes;
|
||||
}
|
||||
|
||||
byte[] transformBytes(byte[] classBytecode, String name) {
|
||||
if (!isProtected(name)) {
|
||||
ClassReader reader = new ClassReader(classBytecode);
|
||||
ClassNode node = new ClassNode();
|
||||
reader.accept(node, 0);
|
||||
boolean modified = false;
|
||||
@ -185,11 +209,11 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
}
|
||||
};
|
||||
node.accept(writer);
|
||||
bytes = writer.toByteArray();
|
||||
classBytecode = writer.toByteArray();
|
||||
log.trace("Modified " + name);
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
return classBytecode;
|
||||
}
|
||||
|
||||
// overriden to increase access (from protected to public)
|
||||
@ -237,4 +261,8 @@ public class MinestomOverwriteClassLoader extends URLClassLoader {
|
||||
public List<CodeModifier> getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
public void addChild(MinestomExtensionClassLoader loader) {
|
||||
children.add(loader);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.spongepowered.asm.service.IClassBytecodeProvider;
|
||||
@ -11,9 +11,9 @@ import java.io.IOException;
|
||||
* Provides class bytecode for Mixin
|
||||
*/
|
||||
public class MinestomBytecodeProvider implements IClassBytecodeProvider {
|
||||
private final MinestomOverwriteClassLoader classLoader;
|
||||
private final MinestomRootClassLoader classLoader;
|
||||
|
||||
public MinestomBytecodeProvider(MinestomOverwriteClassLoader classLoader) {
|
||||
public MinestomBytecodeProvider(MinestomRootClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import org.spongepowered.asm.service.IClassProvider;
|
||||
|
||||
import java.net.URL;
|
||||
@ -9,9 +9,9 @@ import java.net.URL;
|
||||
* Provides classes for Mixin
|
||||
*/
|
||||
public class MinestomClassProvider implements IClassProvider {
|
||||
private final MinestomOverwriteClassLoader classLoader;
|
||||
private final MinestomRootClassLoader classLoader;
|
||||
|
||||
public MinestomClassProvider(MinestomOverwriteClassLoader classLoader) {
|
||||
public MinestomClassProvider(MinestomRootClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package net.minestom.server.extras.selfmodification.mixins;
|
||||
|
||||
import net.minestom.server.extras.selfmodification.MinestomOverwriteClassLoader;
|
||||
import net.minestom.server.extras.selfmodification.MinestomRootClassLoader;
|
||||
import org.spongepowered.asm.launch.platform.container.ContainerHandleVirtual;
|
||||
import org.spongepowered.asm.launch.platform.container.IContainerHandle;
|
||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||
@ -13,7 +13,7 @@ import java.util.Collections;
|
||||
|
||||
public class MixinServiceMinestom extends MixinServiceAbstract {
|
||||
|
||||
private final MinestomOverwriteClassLoader classLoader;
|
||||
private final MinestomRootClassLoader classLoader;
|
||||
private final MinestomClassProvider classProvider;
|
||||
private final MinestomBytecodeProvider bytecodeProvider;
|
||||
private final MixinAuditTrailMinestom auditTrail;
|
||||
@ -22,7 +22,7 @@ public class MixinServiceMinestom extends MixinServiceAbstract {
|
||||
|
||||
public MixinServiceMinestom() {
|
||||
INSTANCE = this;
|
||||
this.classLoader = MinestomOverwriteClassLoader.getInstance();
|
||||
this.classLoader = MinestomRootClassLoader.getInstance();
|
||||
classProvider = new MinestomClassProvider(classLoader);
|
||||
bytecodeProvider = new MinestomBytecodeProvider(classLoader);
|
||||
auditTrail = new MixinAuditTrailMinestom();
|
||||
|
Loading…
Reference in New Issue
Block a user