From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Mariell Hoversholm Date: Mon, 27 Apr 2020 12:31:59 +0200 Subject: [PATCH] Prioritise own classes where possible This adds the server property `Paper.DisableClassPrioritization` to disable prioritization of own classes for plugins' classloaders. This value is by default not present, and this will therefore break any plugins which abuse behaviour related to not using their own classes while still loading their own. This is often an issue with failing to relocate or shade properly, such as when shading plugin APIs like Vault. A plugin's classloader will first look in the same jar as it is loading in for a requested class, then load it. It does not re-use other plugins' classes if it has the chance to avoid doing so. If a class is not found in the same jar as it is loading for and it does find it elsewhere, it will still choose the class elsewhere. This is intended behaviour, as it will only prioritise classes it has in its own jar, no other plugins' classes will be prioritised in any other order than the one they were registered in. The patch in general terms just loads the class in the plugin's jar before it starts looking elsewhere for it. diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java index dfa44d9a0f0e270fddc4f30b845c2b0e23008033..80236a0934861902db7f15571d0d9b4902e70045 100644 --- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java @@ -51,6 +51,7 @@ import org.yaml.snakeyaml.error.YAMLException; */ public final class JavaPluginLoader implements PluginLoader { final Server server; + private static final boolean DISABLE_CLASS_PRIORITIZATION = Boolean.getBoolean("Paper.DisableClassPrioritization"); // Paper private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")}; private final Map classLoadLock = new java.util.HashMap(); // Paper private final Map classLoadLockCount = new java.util.HashMap(); // Paper @@ -203,6 +204,11 @@ public final class JavaPluginLoader implements PluginLoader { @Nullable Class getClassByName(final String name, boolean resolve, PluginDescriptionFile description) { + // Paper start - prioritize self + return getClassByName(name, resolve, description, null); + } + Class getClassByName(final String name, boolean resolve, PluginDescriptionFile description, PluginClassLoader requester) { + // Paper end // Paper start - make MT safe java.util.concurrent.locks.ReentrantReadWriteLock lock; synchronized (classLoadLock) { @@ -210,6 +216,13 @@ public final class JavaPluginLoader implements PluginLoader { classLoadLockCount.compute(name, (x, prev) -> prev != null ? prev + 1 : 1); } lock.writeLock().lock();try { + // Paper start - prioritize self + if (!DISABLE_CLASS_PRIORITIZATION && requester != null) { + try { + return requester.loadClass0(name, false, false, ((SimplePluginManager) server.getPluginManager()).isTransitiveDepend(description, requester.getDescription())); + } catch (ClassNotFoundException cnfe) {} + } + // Paper end // Paper end for (PluginClassLoader loader : loaders) { try { diff --git a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java index 4fa5f7140ea97e1b6a63808b59115bfb1a85cb32..cd1907e8895ece9b780617635b71937596c0f982 100644 --- a/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java +++ b/src/main/java/org/bukkit/plugin/java/PluginClassLoader.java @@ -33,7 +33,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot public JavaPlugin getPlugin() { return plugin; } // Spigot private final JavaPluginLoader loader; private final Map> classes = new ConcurrentHashMap>(); - private final PluginDescriptionFile description; + private final PluginDescriptionFile description; PluginDescriptionFile getDescription() { return description; } // Paper private final File dataFolder; private final File file; private final JarFile jar; @@ -123,7 +123,7 @@ public final class PluginClassLoader extends URLClassLoader { // Spigot if (checkGlobal) { // This ignores the libraries of other plugins, unless they are transitive dependencies. - Class result = loader.getClassByName(name, resolve, description); + Class result = loader.getClassByName(name, resolve, description, this); // Paper - prioritize self if (result != null) { // If the class was loaded from a library instead of a PluginClassLoader, we can assume that its associated plugin is a transitive dependency and can therefore skip this check.