Skip to content

Commit

Permalink
🐛 fix(classloader): yet another attempt at fixing classloading, this …
Browse files Browse the repository at this point in the history
…time with Mohist fixes! bumped to 0.0.5
  • Loading branch information
xtrm-en committed May 21, 2023
1 parent 28f1794 commit 3d8e0ba
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 146 deletions.
8 changes: 7 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ allprojects {
apply(plugin = "java-library")

group = "enterprises.stardust"
version = "0.0.4"
version = "0.0.5"

repositories {
mavenCentral()
Expand All @@ -22,6 +22,11 @@ allprojects {
includeGroup("com.github.xtrm-en")
}
}
maven("https://maven.mohistmc.com/") {
content {
includeGroup("com.mohistmc")
}
}
}

val shade by configurations.creating {
Expand All @@ -38,6 +43,7 @@ allprojects {
"implementation"("org.spigotmc", "spigot-api", "1.16.+")

if (project == rootProject) {
"implementation"("com.mohistmc", "mohistdev", "1.16.5-0.1")
"implementation"("org.apache.logging.log4j", "log4j-core", "2.14.1")
"shade"("com.github.xtrm-en", "deencapsulation", "42b829f373")
}
Expand Down
62 changes: 41 additions & 21 deletions src/main/java/cpw/mods/modlauncher/FukkitHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,79 @@
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
* @author xtrm
*/
public class FukkitHooks {
private static final Map<Class<?>, Logger> LOGGER_CACHE = new HashMap<>();
private static final Class<Launcher> LAUNCHER_CLASS = Launcher.class;

private static Logger getLogger(Class<?> clazz) {
return LOGGER_CACHE.computeIfAbsent(clazz, LogManager::getLogger);
}
private static final Logger LOGGER = LogManager.getLogger();
public static final boolean IS_MOHIST;

public static void addLaunchPlugin(ILaunchPluginService service) {
try {
Field field = LAUNCHER_CLASS.getDeclaredField("launchPlugins");
Field field = Launcher.class.getDeclaredField("launchPlugins");
field.setAccessible(true);
LaunchPluginHandler handler = (LaunchPluginHandler) field.get(Launcher.INSTANCE);

Field pluginsField = LaunchPluginHandler.class.getDeclaredField("plugins");
pluginsField.setAccessible(true);
//noinspection unchecked
Map<String, ILaunchPluginService> plugins = (Map<String, ILaunchPluginService>) pluginsField.get(handler);
Map<String, ILaunchPluginService> plugins =
(Map<String, ILaunchPluginService>) pluginsField.get(handler);
plugins.put(service.name(), service);

getLogger(handler.getClass()).info(
"[Fukkit] Injected launch plugin {}",
service.name()
);
LOGGER.info("Injected launch plugin {}", service.name());
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

public static void overrideTransformationServicesHandler() {
try {
Field field = LAUNCHER_CLASS.getDeclaredField("transformationServicesHandler");
Field field = Launcher.class.getDeclaredField("transformationServicesHandler");
field.setAccessible(true);
Object o = field.get(Launcher.INSTANCE);
FukkitTransformationServicesHandler handler = new FukkitTransformationServicesHandler(o);
field.set(Launcher.INSTANCE, handler);
field.set(
Launcher.INSTANCE,
new FukkitTransformationServicesHandler(
field.get(Launcher.INSTANCE)
)
);

getLogger(FukkitTransformationServicesHandler.SUPERCLASS)
.info("[Fukkit] Overridden transformationServicesHandler");
LOGGER.info("Overridden transformationServicesHandler");
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

// Called reflectively from FukkitTransformationService
@SuppressWarnings("unused")
@SuppressWarnings("unused") // Called
public static void hookClassLoader(IClassLoaderAccess bukkitClassLoader, ClassLoader parent) {
if (!(parent instanceof FukkitTransformingClassLoader)) {
LOGGER.warn(
"{}'s parent loader is not ours, skipping... ({})",
bukkitClassLoader,
parent
);
return;
}
LOGGER.info("Adding {} as a child loader", bukkitClassLoader);
FukkitTransformingClassLoader parentLoader = (FukkitTransformingClassLoader) parent;
parentLoader.registerChildLoader(bukkitClassLoader);
}

@SuppressWarnings("unused") // Called reflectively from FukkitTransformationService
public static void init() {
FukkitHooks.addLaunchPlugin(new FukkitLaunchPlugin());
}

static {
boolean isMohist = false;
try {
Class.forName("com.mohistmc.MohistMC");
isMohist = true;
LOGGER.info("Mohist detected.");
} catch (ClassNotFoundException ignored) {
}
IS_MOHIST = isMohist;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
* @author xtrm
*/
public class FukkitTransformationServicesHandler extends TransformationServicesHandler {
public static final Class<?> SUPERCLASS =
TransformationServicesHandler.class;

private static TransformStore transformStore;
private Map<String, TransformationServiceDecorator> serviceLookup;

Expand All @@ -36,7 +33,7 @@ public FukkitTransformationServicesHandler(Object delegate) {
}

private static TransformStore fetchTransformStore(Object delegate) {
if (delegate.getClass() != SUPERCLASS) {
if (delegate.getClass() != TransformationServicesHandler.class) {
throw new IllegalArgumentException("delegate is not an instance of TransformationServicesHandler");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,58 @@
* @author xtrm
*/
public class FukkitTransformingClassLoader extends TransformingClassLoader {
private static final Boolean DISABLE_PARALLEL =
Boolean.getBoolean("fukkit.disableParallel");
private static final Boolean DEBUG_LOGGING =
Boolean.getBoolean("fukkit.debugClassLogging");
private static final Logger LOGGER = LogManager.getLogger();
private final Set<ClassLoader> childLoaders = new HashSet<>();
private final Set<IClassLoaderAccess> childLoaders = new HashSet<>();
private boolean lockDelegation = false;

FukkitTransformingClassLoader(TransformStore transformStore, LaunchPluginHandler pluginHandler, TransformingClassLoaderBuilder builder, Environment environment) {
super(transformStore, pluginHandler, builder, environment);
}

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

try {
return super.loadClass(name, resolve);
} catch (ClassNotFoundException e) {
if (!lockDelegation) {
synchronized (getClassLoadingLock(name)) {
for (ClassLoader childLoader : this.childLoaders) {
try {
return childLoader.loadClass(name);
} catch (LinkageError error) {
error.printStackTrace();
} catch (Throwable ignored) {
}
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (!lockDelegation) {
for (IClassLoaderAccess loader : this.childLoaders) {
try {
this.lockDelegation = true;
if (DEBUG_LOGGING)
LOGGER.info("Trying to load class from child loader: {}", loader.getClass().getName());
return loader.fukkit$findClassAccessor(name);
} catch (ClassNotFoundException ignored) {
} finally {
this.lockDelegation = false;
}
}
throw e;
}
throw new ClassNotFoundException(name);
}

@SuppressWarnings("unused") // used in bytecode, see FukkitTransformer
public void registerChildLoader(ClassLoader loader) {
LOGGER.trace("Registering child classloader: {}", loader);
this.childLoaders.add(loader);
}

@SuppressWarnings("unused") // used in bytecode, see FukkitTransformer
public void lock() {
public Class<?> loadClassFromBukkit(String name) throws ClassNotFoundException {
if (DEBUG_LOGGING) LOGGER.info("Loading class from Bukkit: {}", name);
this.lockDelegation = true;
try {
return this.findClass(name);
} finally {
this.lockDelegation = false;
}
}

@SuppressWarnings("unused") // used in bytecode, see FukkitTransformer
public void unlock() {
this.lockDelegation = false;
public void registerChildLoader(IClassLoaderAccess loader) {
if (DEBUG_LOGGING) LOGGER.info("Registering child classloader: {}", loader.getClass().getName());
this.childLoaders.add(loader);
}

static {
// this *should* be fine
ClassLoader.registerAsParallelCapable();
if (!DISABLE_PARALLEL) {
// this *should* be fine
ClassLoader.registerAsParallelCapable();
} else {
LOGGER.info("Disabling classloader parallelism");
}
}
}
14 changes: 14 additions & 0 deletions src/main/java/cpw/mods/modlauncher/IClassLoaderAccess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cpw.mods.modlauncher;

/**
* @author xtrm
*/
@FunctionalInterface
public interface IClassLoaderAccess {
String METHOD_NAME = "fukkit$findClassAccessor";
String METHOD_DESC = "(Ljava/lang/String;)Ljava/lang/Class;";
String METHOD_SIGNATURE = "(Ljava/lang/String;)Ljava/lang/Class<*>;";

@SuppressWarnings("unuseld") // used in bytecode, see FukkitTransformer
Class<?> fukkit$findClassAccessor(String name) throws ClassNotFoundException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class FukkitDefiner {
"cpw.mods.modlauncher.FukkitLaunchPlugin",
"cpw.mods.modlauncher.FukkitTransformationServicesHandler",
"cpw.mods.modlauncher.FukkitTransformingClassLoader",
"cpw.mods.modlauncher.IClassLoaderAccess",
};

private static final Object unsafeInstance;
Expand Down
Loading

0 comments on commit 3d8e0ba

Please sign in to comment.