5555
5656import org .graalvm .nativeimage .ImageSingletons ;
5757
58+ import com .oracle .graal .pointsto .meta .AnalysisType ;
5859import com .oracle .graal .pointsto .meta .AnalysisUniverse ;
5960import com .oracle .svm .core .SubstrateUtil ;
6061import 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