Commit fd3fa25
authored
Fix loading package-info classes (#56)
## The issue
Currently, SJH's `ModuleClassLoader` is unable to load `package-info`
classes, which are placed in the unnamed module when it calls
`definePackage`.
Java offers 2 ways of defining packages - one with version information
and another with a module parameter, which are mutually exclusive.
However, our use case requires both package versioning information and
the module to be present.
`ModuleClassLoader` is designed to only load classes from named modules.
When calling `Class.getPackage().getPackageInfo()`, Java will try to
load the package-info class using `ClassLoader#findClass(String
moduleName, String name)`, passing in `null` for the moduleName
parameter as the package is placed in an unnamed module. Here, MCL
expects `moduleName` to be non-null at all times. However, that is not
true according to java's
[documentation](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ClassLoader.html#findResource(java.lang.String,java.lang.String)):
> **Parameters:**
moduleName - The module name; or null to find a resource in the [unnamed
module](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ClassLoader.html#getUnnamedModule())
for this class loader
MCL will happily pass this parameter to `Configuration#findModule`,
which expects a non-null value, resulting in a NPE.
### Steps to reproduce
1. Invoke `getPackage().getPackageInfo()` on your favourite class, which
is loaded by a `ModuleClassLoader`
2. Expect a NPE
### Expected behavior
Much like other classes, `package-info` classes should be placed in
named modules, too, rather than being loaded in the unnamed module.
## The solution
To make java load package-info classes in their module, we must replace
the value of the `NamedPackage#module` field. Since java puts no
restrictions in place that would prevent us from setting a named module
on a `NamedPackage` instance, we reflectively replace the default
unnamed module value with the package's actual module.
Step 1: Grab the trusted method lookup instance and create a field
handle for the package module field
```java
var trustedLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
trustedLookupField.setAccessible(true);
MethodHandles.Lookup trustedLookup = (MethodHandles.Lookup) trustedLookupField.get(null);
Class<?> namedPackage = Class.forName("java.lang.NamedPackage");
PKG_MODULE_HANDLE = trustedLookup.findVarHandle(namedPackage, "module", Module.class);
```
Step 2: After defining each class (necessary for the module to be
known), ensure its package is placed in a named module
```java
// Get package module
Module value = (Module) PKG_MODULE_HANDLE.get(pkg);
// Check if the value is not a named module
if (value == null || !value.isNamed()) {
try {
// Replace the value with the loaded class's module
PKG_MODULE_HANDLE.set(pkg, module);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
```1 parent e47246e commit fd3fa25
File tree
6 files changed
+76
-1
lines changed- src
- main/java/cpw/mods/cl
- testjar1/java/cpw/mods/cl/testjar1
- test/java/cpw/mods/cl/test
6 files changed
+76
-1
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
172 | 172 | | |
173 | 173 | | |
174 | 174 | | |
175 | | - | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
176 | 178 | | |
177 | 179 | | |
178 | 180 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
27 | 29 | | |
28 | 30 | | |
29 | 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 | + | |
30 | 60 | | |
31 | 61 | | |
32 | 62 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
0 commit comments