Skip to content

Commit 6c34eaf

Browse files
committed
Calculate root module set
1 parent e47ac0f commit 6c34eaf

File tree

1 file changed

+94
-18
lines changed

1 file changed

+94
-18
lines changed

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555

5656
import org.graalvm.nativeimage.ImageSingletons;
5757

58+
import com.oracle.graal.pointsto.meta.AnalysisType;
5859
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
5960
import com.oracle.svm.core.SubstrateUtil;
6061
import com.oracle.svm.core.feature.InternalFeature;
@@ -148,35 +149,67 @@ public void afterAnalysis(AfterAnalysisAccess access) {
148149
FeatureImpl.AfterAnalysisAccessImpl accessImpl = (FeatureImpl.AfterAnalysisAccessImpl) access;
149150
AnalysisUniverse universe = accessImpl.getUniverse();
150151

151-
Stream<Module> analysisReachableModules = universe.getTypes()
152-
.stream()
153-
.filter(t -> t.isReachable() && !t.isArray())
152+
/*
153+
* Parse explicitly added modules via --add-modules. This is done early as this information
154+
* is required when filtering the analysis reachable module set.
155+
*/
156+
Set<String> extraModules = new HashSet<>();
157+
List<String> nonExplicit = List.of("ALL-DEFAULT", "ALL-SYSTEM", "ALL-MODULE-PATH");
158+
String explicitlyAddedModules = System.getProperty(ModuleSupport.PROPERTY_IMAGE_EXPLICITLY_ADDED_MODULES, "");
159+
if (!explicitlyAddedModules.isEmpty()) {
160+
extraModules.addAll(Arrays.asList(SubstrateUtil.split(explicitlyAddedModules, ",")));
161+
}
162+
163+
/**
164+
* If we are running on module path and if user did not explicitly add special --add-module
165+
* args as defined in the nonExplicit list, then the root modules only include reachable
166+
* modules. Otherwise, we calculate root modules based on reachable modules and include
167+
* modules as specified in the https://openjdk.org/jeps/261. See
168+
* {@link ModuleLayerFeature.typeIsModuleRootOrReachable(AnalysisType)} for more details.
169+
*/
170+
Stream<AnalysisType> rootModuleTypes;
171+
if (accessImpl.getApplicationClassPath().isEmpty() && nonExplicit.stream().noneMatch(extraModules::contains)) {
172+
rootModuleTypes = universe.getTypes()
173+
.stream()
174+
.filter(ModuleLayerFeature::typeIsReachable);
175+
} else {
176+
rootModuleTypes = universe.getTypes()
177+
.stream()
178+
.filter(ModuleLayerFeature::typeIsModuleRootOrReachable);
179+
180+
/*
181+
* Also make sure to include modules not seen by the analysis.
182+
*/
183+
Set<String> extraUndiscoveredModules = ModuleLayer.boot().modules()
184+
.stream()
185+
.filter(ModuleLayerFeature::isModuleRoot)
186+
.map(Module::getName)
187+
.collect(Collectors.toSet());
188+
extraModules.addAll(extraUndiscoveredModules);
189+
}
190+
191+
Stream<Module> runtimeImageModules = rootModuleTypes
154192
.map(t -> t.getJavaClass().getModule())
155193
.distinct();
156194

157-
Set<Module> analysisReachableNamedModules = analysisReachableModules
195+
Set<Module> runtimeImageNamedModules = runtimeImageModules
158196
.filter(Module::isNamed)
159197
.collect(Collectors.toSet());
160198

161-
Set<String> extraModules = new HashSet<>();
162-
199+
/*
200+
* Include the rest of the extra modules that might not have been included by the above
201+
* process.
202+
*/
163203
extraModules.addAll(ImageSingletons.lookup(ResourcesFeature.class).includedResourcesModules);
164-
165-
String explicitlyAddedModules = System.getProperty(ModuleSupport.PROPERTY_IMAGE_EXPLICITLY_ADDED_MODULES, "");
166-
if (!explicitlyAddedModules.isEmpty()) {
167-
extraModules.addAll(Arrays.asList(SubstrateUtil.split(explicitlyAddedModules, ",")));
168-
}
169-
170-
List<String> nonExplicit = List.of("ALL-DEFAULT", "ALL-SYSTEM", "ALL-MODULE-PATH");
171204
extraModules.stream().filter(Predicate.not(nonExplicit::contains)).forEach(moduleName -> {
172205
Optional<?> module = accessImpl.imageClassLoader.findModule(moduleName);
173206
if (module.isEmpty()) {
174207
VMError.shouldNotReachHere("Explicitly required module " + moduleName + " is not available");
175208
}
176-
analysisReachableNamedModules.add((Module) module.get());
209+
runtimeImageNamedModules.add((Module) module.get());
177210
});
178211

179-
Set<Module> analysisReachableSyntheticModules = analysisReachableNamedModules
212+
Set<Module> analysisReachableSyntheticModules = runtimeImageNamedModules
180213
.stream()
181214
.filter(ModuleLayerFeatureUtils::isModuleSynthetic)
182215
.collect(Collectors.toSet());
@@ -186,23 +219,66 @@ public void afterAnalysis(AfterAnalysisAccess access) {
186219
* layer. This order is important because in order to synthesize a module layer, all of its
187220
* parent module layers also need to be synthesized as well.
188221
*/
189-
List<ModuleLayer> reachableModuleLayers = analysisReachableNamedModules
222+
List<ModuleLayer> reachableModuleLayers = runtimeImageNamedModules
190223
.stream()
191224
.map(Module::getLayer)
192225
.filter(Objects::nonNull)
193226
.distinct()
194227
.sorted(Comparator.comparingInt(ModuleLayerFeatureUtils::distanceFromBootModuleLayer))
195228
.collect(Collectors.toList());
196229

197-
List<ModuleLayer> runtimeModuleLayers = synthesizeRuntimeModuleLayers(accessImpl, reachableModuleLayers, analysisReachableNamedModules, analysisReachableSyntheticModules);
230+
List<ModuleLayer> runtimeModuleLayers = synthesizeRuntimeModuleLayers(accessImpl, reachableModuleLayers, runtimeImageNamedModules, analysisReachableSyntheticModules);
198231
ModuleLayer runtimeBootLayer = runtimeModuleLayers.get(0);
199232
BootModuleLayerSupport.instance().setBootLayer(runtimeBootLayer);
200233

201234
/*
202235
* Ensure that runtime modules have the same relations (i.e., reads, opens and exports) as
203236
* the originals.
204237
*/
205-
replicateVisibilityModifications(runtimeBootLayer, accessImpl.imageClassLoader, analysisReachableNamedModules);
238+
replicateVisibilityModifications(runtimeBootLayer, accessImpl.imageClassLoader, runtimeImageNamedModules);
239+
}
240+
241+
private static boolean typeIsReachable(AnalysisType t) {
242+
return t.isReachable() && !t.isArray();
243+
}
244+
245+
private static boolean typeIsModuleRootOrReachable(AnalysisType t) {
246+
if (typeIsReachable(t)) {
247+
return true;
248+
}
249+
250+
Module m = t.getJavaClass().getModule();
251+
return isModuleRoot(m);
252+
}
253+
254+
private static boolean isModuleRoot(Module m) {
255+
if (!m.isNamed()) {
256+
return false;
257+
}
258+
259+
/*
260+
* When the main class of the application is loaded from the class path into the unnamed
261+
* module of the application class loader, then the default set of root modules for the
262+
* unnamed module is computed as follows (https://openjdk.org/jeps/261):
263+
*
264+
* 1. The java.se module is a root, if it exists. If it does not exist then every java.*
265+
* module on the upgrade module path or among the system modules that exports at least one
266+
* package, without qualification, is a root.
267+
*
268+
* 2. Every non-java.* module on the upgrade module path or among the system modules that
269+
* exports at least one package, without qualification, is also a root.
270+
*/
271+
if (m.getName().startsWith("java.")) {
272+
return true;
273+
} else {
274+
for (String pn : m.getPackages()) {
275+
if (m.isExported(pn)) {
276+
return true;
277+
}
278+
}
279+
}
280+
281+
return false;
206282
}
207283

208284
/*

0 commit comments

Comments
 (0)