-
Notifications
You must be signed in to change notification settings - Fork 20
/
Copy pathModuleURLClassLoader.java
162 lines (141 loc) · 6.16 KB
/
ModuleURLClassLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package me.earth.headlessmc.modlauncher;
import dev.xdark.deencapsulation.Deencapsulation;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.module.Configuration;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.security.CodeSigner;
import java.security.CodeSource;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* An {@link URLClassLoader} that looks through the modules of the given java.lang.module.Configuration to find classes.
*/
@SuppressWarnings("Since15")
public class ModuleURLClassLoader extends URLClassLoader implements Java9Classloader {
private static final Logger LOGGER = Logger.getLogger(ModuleURLClassLoader.class.getName());
private static final MethodHandle LAYER_BIND_TO_LOADER;
private final AtomicInteger classesLoaded = new AtomicInteger();
private Configuration configuration;
static {
ClassLoader.registerAsParallelCapable();
try {
Deencapsulation.deencapsulate(MethodHandles.Lookup.class);
Field hackfield = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
hackfield.setAccessible(true);
MethodHandles.Lookup hack = (MethodHandles.Lookup) hackfield.get(null);
LAYER_BIND_TO_LOADER = hack.findSpecial(ModuleLayer.class, "bindToLoader", MethodType.methodType(Void.TYPE, ClassLoader.class), ModuleLayer.class);
} catch (IllegalAccessException | NoSuchMethodException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
/**
* Constructs a new ModuleURLClassLoader for the given classpath and parent loader.
* For this classloader to work properly .setConfiguration needs to be called!
*
* @param urls the classpath this Classloader loads from.
* @param parent the parent Classloader to delegate to.
*/
public ModuleURLClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
public Class<?> findClass(String moduleName, String name) {
try {
LOGGER.fine("Classes loaded: " + classesLoaded.incrementAndGet());
Optional<ResolvedModule> module = configuration.findModule(moduleName);
if (!module.isPresent()) {
LOGGER.severe("Failed to find module " + moduleName + " for class " + name + ", using default findClass");
return findClass(name);
}
LOGGER.fine("Found module " + module.get() + " for class " + name);
Class<?> clazz = loadFromModule(module.get(), name);
if (clazz == null) {
LOGGER.severe("Failed to find class " + name + " in module " + module + ", using default findClass");
return findClass(name);
}
return clazz;
} catch (ClassNotFoundException e) {
LOGGER.severe("Failed to find class " + name + " in module " + moduleName + ", returning null...");
return null;
}
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
LOGGER.fine("Classes loaded: " + classesLoaded.incrementAndGet());
LOGGER.fine("Searching for " + name);
// this is like the least performant thing ever,
// but the ModuleURLClassLoader will only load about 183 classes.
for (ResolvedModule module : configuration.modules()) {
LOGGER.fine("Checking " + module.name() + " for " + name);
Class<?> clazz = loadFromModule(module, name);
if (clazz != null) {
return clazz;
}
}
LOGGER.fine("Failed to find: " + name + " in modules, using findClass");
return super.findClass(name);
}
/**
* Sets the Configuration that this Classloader looks up classes and modules in.
*
* @param configuration the configuration this Classloader will lookup from.
*/
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
private @Nullable Class<?> loadFromModule(ResolvedModule module, String name) {
ModuleReference ref = module.reference();
try (ModuleReader reader = ref.open()) {
String rn = name.replace('.', '/').concat(".class");
ByteBuffer bb = reader.read(rn).orElse(null);
if (bb == null) {
return null;
}
try {
LOGGER.fine("Found class " + name + " in module " + module.name());
return defineClass(name, bb, getCodeSource(ref));
} finally {
reader.release(bb);
}
} catch (Throwable e) {
LOGGER.log(Level.SEVERE, "Failed to find " + name + " in module " + module, e);
}
return null;
}
// jdk.internal.loader.Loader.LoadedModule
private CodeSource getCodeSource(ModuleReference reference) {
URL url = null;
if (reference.location().isPresent()) {
try {
url = reference.location().get().toURL();
} catch (MalformedURLException | IllegalArgumentException ignored) { }
}
return new CodeSource(url, (CodeSigner[]) null);
}
/**
* Binds the given classloader to the given layer.
* Calls the package-private method ModuleLayer.bindToLoader for the given classloader on the given layer.
*
* @param classLoader the classloader to bind to the layer.
* @param layer the layer to bind the classloader to.
*/
public static void bindToLayer(ModuleURLClassLoader classLoader, ModuleLayer layer) {
try {
LAYER_BIND_TO_LOADER.invokeExact((ModuleLayer) layer, (ClassLoader) classLoader);
} catch (Throwable throwable) {
throw new IllegalStateException(throwable);
}
}
}