Skip to content

Commit 2334a13

Browse files
committed
[GR-52333] Refactor and update module system native substitutions
PullRequest: graal/17104
2 parents 62ac53a + 5664813 commit 2334a13

File tree

5 files changed

+180
-172
lines changed

5 files changed

+180
-172
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ModuleUtil.java renamed to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ModuleNative.java

Lines changed: 155 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,150 @@
3030
import java.util.Map;
3131
import java.util.Objects;
3232
import java.util.Set;
33-
import java.util.stream.Collectors;
3433

3534
import javax.lang.model.SourceVersion;
3635

3736
import com.oracle.svm.core.SubstrateUtil;
3837

39-
public final class ModuleUtil {
40-
private ModuleUtil() {
38+
public final class ModuleNative {
39+
private ModuleNative() {
4140
}
4241

42+
/**
43+
* Re-implementations of native methods from {@code src/hotspot/share/classfile/modules.cpp}.
44+
* See {@link Target_java_lang_Module} for more information on module system native
45+
* substitutions.
46+
*/
47+
48+
/**
49+
* {@code Modules::define_module}.
50+
*/
51+
public static void defineModule(Module module, boolean isOpen, Object[] pns) {
52+
if (Objects.isNull(module)) {
53+
throw new NullPointerException("Null module object");
54+
}
55+
56+
if (Objects.isNull(module.getName())) {
57+
throw new IllegalArgumentException("Module name cannot be null");
58+
}
59+
60+
if (module.getName().equals("java.base")) {
61+
if (isOpen) {
62+
// Checkstyle: stop
63+
throw new AssertionError("java.base module cannot be open");
64+
// Checkstyle: resume
65+
}
66+
67+
for (Object pn : pns) {
68+
checkPackageNameForModule(pn, "java.base");
69+
}
70+
71+
if (module.getClassLoader() != null) {
72+
throw new IllegalArgumentException("Class loader must be the boot class loader");
73+
}
74+
75+
synchronized (moduleLock) {
76+
boolean duplicateJavaBase = bootLayerContainsModule("java.base");
77+
if (duplicateJavaBase) {
78+
throw new InternalError("Module java.base is already defined");
79+
}
80+
}
81+
82+
return;
83+
}
84+
85+
ClassLoader loader = module.getClassLoader();
86+
if (Objects.nonNull(loader) && loader.getClass().getName().equals("jdk.internal.reflect.DelegatingClassLoader")) {
87+
throw new IllegalArgumentException("Class loader is an invalid delegating class loader");
88+
}
89+
90+
boolean javaPkgDisallowed = !Objects.isNull(loader) && !Objects.equals(loader, ClassLoader.getPlatformClassLoader());
91+
for (Object pn : pns) {
92+
checkPackageNameForModule(pn, module.getName());
93+
if (javaPkgDisallowed && isPackageNameForbidden(pn.toString())) {
94+
throw new IllegalArgumentException("Class loader (instance of): " + loader.getClass().getName() +
95+
" tried to define prohibited package name: " + pn);
96+
}
97+
}
98+
99+
String definedPackage = null;
100+
boolean moduleAlreadyDefined;
101+
synchronized (moduleLock) {
102+
moduleAlreadyDefined = isModuleDefinedToLoader(loader, module.getName());
103+
if (!moduleAlreadyDefined) {
104+
List<String> definedPackages = getPackagesDefinedToLoader(loader);
105+
for (Object pn : pns) {
106+
String pnString = pn.toString();
107+
if (definedPackages.contains(pnString)) {
108+
definedPackage = pnString;
109+
break;
110+
}
111+
}
112+
}
113+
}
114+
115+
if (moduleAlreadyDefined) {
116+
throw new IllegalStateException("Module " + module.getName() + " is already defined");
117+
} else if (Objects.nonNull(definedPackage)) {
118+
Module moduleContainingDefinedPackage = SubstrateUtil.cast(getModuleContainingPackage(loader, definedPackage), Module.class);
119+
if (moduleContainingDefinedPackage.isNamed()) {
120+
throw new IllegalStateException("Package " + definedPackage + " is already in another module, " + moduleContainingDefinedPackage.getName() + ", defined to the class loader");
121+
} else {
122+
throw new IllegalStateException("Package " + definedPackage + " is already in the unnamed module defined to the class loader");
123+
}
124+
}
125+
126+
synchronized (moduleLock) {
127+
addDefinedModule(loader, module);
128+
}
129+
}
130+
131+
/**
132+
* {@code Modules::add_reads_module}.
133+
*/
134+
public static void addReads(Module from, @SuppressWarnings("unused") Module to) {
135+
checkIsNull(from, FROM_MODULE_TAG);
136+
}
137+
138+
/**
139+
* {@code Modules::add_module_exports_qualified}.
140+
*/
141+
public static void addExports(Module from, String pn, Module to) {
142+
checkIsNull(to, TO_MODULE_TAG);
143+
addExportsToAll(from, pn);
144+
}
145+
146+
/**
147+
* {@code Modules::add_module_exports}.
148+
*/
149+
public static void addExportsToAll(Module from, String pn) {
150+
checkIsNull(pn, PACKAGE_TAG);
151+
checkIsNull(from, FROM_MODULE_TAG);
152+
checkIsPackageContainedInModule(pn, from, FROM_MODULE_TAG);
153+
}
154+
155+
/**
156+
* {@code Modules::add_module_exports_to_all_unnamed}.
157+
*/
158+
public static void addExportsToAllUnnamed(Module module, String pn) {
159+
checkIsNull(module, MODULE_TAG);
160+
checkIsNull(pn, PACKAGE_TAG);
161+
checkIsPackageContainedInModule(pn, module, MODULE_TAG);
162+
}
163+
164+
/**
165+
* Module bookkeeping and utility methods used by substitutions.
166+
*/
167+
168+
private static final String PACKAGE_TAG = "module";
169+
private static final String MODULE_TAG = "module";
170+
private static final String FROM_MODULE_TAG = "from_" + MODULE_TAG;
171+
private static final String TO_MODULE_TAG = "to_" + MODULE_TAG;
43172
private static final Object moduleLock = new Object();
44173
private static final Map<ClassLoader, Set<Module>> definedModules = new HashMap<>();
45174

46-
public static Map<ClassLoader, Set<Module>> getDefinedModules() {
47-
if (definedModules.size() == 0) {
175+
private static Map<ClassLoader, Set<Module>> getDefinedModules() {
176+
if (definedModules.isEmpty()) {
48177
for (Module module : ModuleLayer.boot().modules()) {
49178
Set<Module> modules = definedModules.get(module.getClassLoader());
50179
if (Objects.isNull(modules)) {
@@ -59,35 +188,37 @@ public static Map<ClassLoader, Set<Module>> getDefinedModules() {
59188
return definedModules;
60189
}
61190

62-
public static void checkFromModuleAndPackageNullability(Module from, String pn) {
63-
if (Objects.isNull(from)) {
64-
throw new NullPointerException("The from_module is null");
65-
}
66-
67-
if (Objects.isNull(pn)) {
68-
throw new NullPointerException("The package is null");
191+
private static void checkIsNull(Object o, String tag) {
192+
if (Objects.isNull(o)) {
193+
throw new NullPointerException(tag + " is null");
69194
}
70195
}
71196

72-
public static boolean isPackageNameForbidden(String pn) {
197+
private static boolean isPackageNameForbidden(String pn) {
73198
if (!pn.startsWith("java")) {
74199
return false;
75200
}
76201
char trailingChar = pn.length() < 5 ? '.' : pn.charAt("java".length());
77202
return trailingChar == '.';
78203
}
79204

80-
public static boolean isValidPackageName(String pn) {
205+
private static void checkPackageNameForModule(Object pn, String module) {
206+
if (Objects.isNull(pn) || !(pn instanceof String pnString)) {
207+
throw new IllegalArgumentException("Bad package name");
208+
}
209+
81210
// It is OK to use SourceVersion.isName here even though it calls String.split()
82211
// because pattern "\\." will take the fast path in the String.split() method
83-
return Objects.nonNull(pn) && SourceVersion.isName(pn);
212+
if (!SourceVersion.isName(pnString)) {
213+
throw new IllegalArgumentException("Invalid package name: " + pnString + " for module: " + module);
214+
}
84215
}
85216

86-
public static boolean isModuleDefinedToLoader(ClassLoader loader, String moduleName) {
217+
private static boolean isModuleDefinedToLoader(ClassLoader loader, String moduleName) {
87218
return getDefinedModules().getOrDefault(loader, Set.of()).stream().anyMatch(m -> m.getName().equals(moduleName));
88219
}
89220

90-
public static void addDefinedModule(ClassLoader loader, Module module) {
221+
private static void addDefinedModule(ClassLoader loader, Module module) {
91222
Set<Module> modules = getDefinedModules().get(loader);
92223
if (Objects.isNull(modules)) {
93224
modules = new HashSet<>();
@@ -98,30 +229,23 @@ public static void addDefinedModule(ClassLoader loader, Module module) {
98229
}
99230
}
100231

101-
public static void checkIsPackageContainedInModule(String pn, Module module) {
102-
ClassLoader loader = module.getClassLoader() == null ? ClassLoader.getPlatformClassLoader() : module.getClassLoader();
103-
Package definedPackage = loader.getDefinedPackage(pn);
104-
if (definedPackage != null) {
105-
Target_java_lang_NamedPackage namedPackage = SubstrateUtil.cast(definedPackage, Target_java_lang_NamedPackage.class);
106-
Module actualModule = namedPackage.module;
107-
if (!actualModule.equals(module)) {
108-
throw new IllegalArgumentException("Package " + pn + " found in module " + actualModule.getName() +
109-
", not in module: " + module.getName());
110-
}
232+
private static void checkIsPackageContainedInModule(String pn, Module module, String tag) {
233+
if (!module.isNamed() || module.getDescriptor().isOpen()) {
234+
return;
111235
}
112236
if (!module.getPackages().contains(pn)) {
113-
throw new IllegalArgumentException("Package " + pn + " not found in from_module " + module.getName());
237+
throw new IllegalArgumentException("Package " + pn + " not found in " + tag + " " + module.getName());
114238
}
115239
}
116240

117-
public static List<String> getPackagesDefinedToLoader(ClassLoader loader) {
241+
private static List<String> getPackagesDefinedToLoader(ClassLoader loader) {
118242
return getDefinedModules().getOrDefault(loader, Set.of())
119243
.stream()
120244
.flatMap(m -> m.getPackages().stream())
121-
.collect(Collectors.toUnmodifiableList());
245+
.toList();
122246
}
123247

124-
public static Object getModuleContainingPackage(ClassLoader loader, String pn) {
248+
private static Object getModuleContainingPackage(ClassLoader loader, String pn) {
125249
return getDefinedModules().getOrDefault(loader, Set.of())
126250
.stream()
127251
.filter(m -> m.getPackages().contains(pn))
@@ -132,84 +256,4 @@ public static boolean bootLayerContainsModule(String name) {
132256
return ModuleLayer.boot().modules().stream().anyMatch(m -> m.getName().equals(name));
133257
}
134258

135-
public static void defineModule(Module module, boolean isOpen, List<String> pns) {
136-
if (Objects.isNull(module)) {
137-
throw new NullPointerException("Null module object");
138-
}
139-
140-
if (Objects.isNull(module.getName())) {
141-
throw new IllegalArgumentException("Module name cannot be null");
142-
}
143-
144-
if (module.getName().equals("java.base")) {
145-
if (isOpen) {
146-
throw new AssertionError("The java.base module cannot be open");
147-
}
148-
149-
for (String pn : pns) {
150-
if (!ModuleUtil.isValidPackageName(pn)) {
151-
throw new IllegalArgumentException("Invalid package name: " + pn + " for module: java.base");
152-
}
153-
}
154-
155-
if (module.getClassLoader() != null) {
156-
throw new IllegalArgumentException("Class loader must be the boot class loader");
157-
}
158-
159-
synchronized (moduleLock) {
160-
boolean duplicateJavaBase = ModuleUtil.bootLayerContainsModule("java.base");
161-
if (duplicateJavaBase) {
162-
throw new InternalError("Module java.base is already defined");
163-
}
164-
}
165-
166-
return;
167-
}
168-
169-
ClassLoader loader = module.getClassLoader();
170-
if (Objects.isNull(loader) || loader.getClass().getName().equals("jdk.internal.reflect.DelegatingClassLoader")) {
171-
throw new IllegalArgumentException("Class loader is an invalid delegating class loader");
172-
}
173-
174-
for (String pn : pns) {
175-
if (!ModuleUtil.isValidPackageName(pn)) {
176-
throw new IllegalArgumentException("Invalid package name: " + pn + " for module: " + module.getName());
177-
}
178-
179-
if (loader != ClassLoader.getPlatformClassLoader() && ModuleUtil.isPackageNameForbidden(pn)) {
180-
throw new IllegalArgumentException("Class loader (instance of): " + loader.getClass().getName() +
181-
" tried to define prohibited package name: " + pn);
182-
}
183-
}
184-
185-
String definedPackage = null;
186-
boolean moduleAlreadyDefined;
187-
synchronized (moduleLock) {
188-
moduleAlreadyDefined = ModuleUtil.isModuleDefinedToLoader(loader, module.getName());
189-
if (!moduleAlreadyDefined) {
190-
List<String> definedPackages = ModuleUtil.getPackagesDefinedToLoader(loader);
191-
for (String pn : pns) {
192-
if (definedPackages.contains(pn)) {
193-
definedPackage = pn;
194-
break;
195-
}
196-
}
197-
}
198-
}
199-
200-
if (moduleAlreadyDefined) {
201-
throw new IllegalStateException("Module " + module.getName() + " is already defined");
202-
} else if (Objects.nonNull(definedPackage)) {
203-
Module moduleContainingDefinedPackage = SubstrateUtil.cast(ModuleUtil.getModuleContainingPackage(loader, definedPackage), Module.class);
204-
if (moduleContainingDefinedPackage.isNamed()) {
205-
throw new IllegalStateException("Package " + definedPackage + " is already in another module, " + moduleContainingDefinedPackage.getName() + ", defined to the class loader");
206-
} else {
207-
throw new IllegalStateException("Package " + definedPackage + " is already in the unnamed module defined to the class loader");
208-
}
209-
}
210-
211-
synchronized (moduleLock) {
212-
ModuleUtil.addDefinedModule(loader, module);
213-
}
214-
}
215259
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeModuleSupport.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,23 @@
3333
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
3434
import com.oracle.svm.core.heap.UnknownObjectField;
3535

36+
/**
37+
* Runtime module support singleton, containing the runtime boot module layer. The boot module layer
38+
* is synthesized by a feature during native image generation, after analysis (as module layer
39+
* synthesizing requires analysis information). For convenience, this singleton also contains
40+
* hosted-only hosted-to-runtime module mappers used by other parts of the module system during the
41+
* image build. These are important, as every hosted module has its own synthesized runtime
42+
* counterpart. The lookup function is implemented inside the module layer synthesis feature. See
43+
* {@code ModuleLayerFeature} for more information.
44+
*/
3645
public final class RuntimeModuleSupport {
3746

3847
public static RuntimeModuleSupport instance() {
3948
return ImageSingletons.lookup(RuntimeModuleSupport.class);
4049
}
4150

42-
@UnknownObjectField(availability = AfterHostedUniverse.class) private ModuleLayer bootLayer;
51+
@UnknownObjectField(availability = AfterHostedUniverse.class) //
52+
private ModuleLayer bootLayer;
4353

4454
@Platforms(Platform.HOSTED_ONLY.class) //
4555
private Function<Module, Module> hostedToRuntimeModuleMapper;

0 commit comments

Comments
 (0)