@@ -147,7 +147,6 @@ public void afterRegistration(AfterRegistrationAccess access) {
147147 @ Override
148148 public void afterAnalysis (AfterAnalysisAccess access ) {
149149 FeatureImpl .AfterAnalysisAccessImpl accessImpl = (FeatureImpl .AfterAnalysisAccessImpl ) access ;
150- AnalysisUniverse universe = accessImpl .getUniverse ();
151150
152151 /*
153152 * Parse explicitly added modules via --add-modules. This is done early as this information
@@ -159,51 +158,9 @@ public void afterAnalysis(AfterAnalysisAccess access) {
159158 extraModules .addAll (Arrays .asList (SubstrateUtil .split (explicitlyAddedModules , "," )));
160159 }
161160
162- /**
163- * If we are running on module path and if user did not explicitly add special --add-module
164- * args as defined in the nonExplicit list, then the root modules only include reachable
165- * modules. Otherwise, we calculate root modules based on reachable modules and include
166- * modules as specified in the https://openjdk.org/jeps/261. See
167- * {@link ModuleLayerFeature.typeIsModuleRootOrReachable(AnalysisType)} for more details.
168- */
169- Stream <Module > rootModules ;
170161 List <String > nonExplicit = List .of ("ALL-DEFAULT" , "ALL-SYSTEM" , "ALL-MODULE-PATH" );
171- if (accessImpl .getApplicationClassPath ().isEmpty () && nonExplicit .stream ().noneMatch (extraModules ::contains )) {
172- rootModules = universe .getTypes ()
173- .stream ()
174- .filter (ModuleLayerFeature ::typeIsReachable )
175- .map (t -> t .getJavaClass ().getModule ());
176- } else {
177- Optional <Module > javaSeModule = universe .getTypes ()
178- .stream ()
179- .filter (ModuleLayerFeature ::typeIsReachable )
180- .map (t -> t .getJavaClass ().getModule ())
181- .filter (m -> m .isNamed () && m .getName ().equals ("java.se" ))
182- .findFirst ();
183- if (javaSeModule .isPresent ()) {
184- /*
185- * The java.se module is a root, if it exists.
186- */
187- rootModules = Stream .of (javaSeModule .get ());
188- } else {
189- rootModules = universe .getTypes ()
190- .stream ()
191- .filter (ModuleLayerFeature ::typeIsModuleRootOrReachable )
192- .map (t -> t .getJavaClass ().getModule ());
193-
194- /*
195- * Also make sure to include modules not seen by the analysis.
196- */
197- Set <String > extraUndiscoveredModules = ModuleLayer .boot ().modules ()
198- .stream ()
199- .filter (ModuleLayerFeature ::isModuleRoot )
200- .map (Module ::getName )
201- .collect (Collectors .toSet ());
202- extraModules .addAll (extraUndiscoveredModules );
203- }
204- }
205162
206- Stream <Module > runtimeImageModules = rootModules . distinct ( );
163+ Stream <Module > runtimeImageModules = calculateRootModulesAndUpdateExtraModules ( accessImpl , nonExplicit , extraModules );
207164
208165 Set <Module > runtimeImageNamedModules = runtimeImageModules
209166 .filter (Module ::isNamed )
@@ -251,6 +208,67 @@ public void afterAnalysis(AfterAnalysisAccess access) {
251208 replicateVisibilityModifications (runtimeBootLayer , accessImpl .imageClassLoader , runtimeImageNamedModules );
252209 }
253210
211+ /**
212+ * If we are running on module path and if user did not explicitly add special --add-module args
213+ * as defined in the nonExplicit list, then the root modules only include reachable modules.
214+ * Otherwise, we calculate root modules based on reachable modules and include modules as
215+ * specified in the JEP 261 (see <a href="https://openjdk.org/jeps/261"></a>).
216+ */
217+ private static Stream <Module > calculateRootModulesAndUpdateExtraModules (FeatureImpl .AfterAnalysisAccessImpl accessImpl , Collection <String > nonExplicit , Collection <String > extraModules ) {
218+ AnalysisUniverse universe = accessImpl .getUniverse ();
219+
220+ Stream <Module > rootModules ;
221+ if (accessImpl .getApplicationClassPath ().isEmpty () && nonExplicit .stream ().noneMatch (extraModules ::contains )) {
222+ /*
223+ * When running on the module path, reachable modules on the module path are root
224+ * modules
225+ */
226+ rootModules = universe .getTypes ()
227+ .stream ()
228+ .filter (ModuleLayerFeature ::typeIsReachable )
229+ .map (t -> t .getJavaClass ().getModule ());
230+ } else {
231+ Optional <Module > javaSeModule = universe .getTypes ()
232+ .stream ()
233+ .filter (ModuleLayerFeature ::typeIsReachable )
234+ .map (t -> t .getJavaClass ().getModule ())
235+ .filter (m -> m .isNamed () && m .getName ().equals ("java.se" ))
236+ .findFirst ();
237+ if (javaSeModule .isPresent ()) {
238+ /*
239+ * java.se module is a root, if it exists.
240+ */
241+ rootModules = Stream .of (javaSeModule .get ());
242+ } else {
243+ /*
244+ * If java.se is not present, then:
245+ *
246+ * 1. Every java.* module on the upgrade module path or among the system modules
247+ * that exports at least one package, without qualification, is a root.
248+ *
249+ * 2. Every non-java.* module on the upgrade module path or among the system modules
250+ * that exports at least one package, without qualification, is also a root.
251+ */
252+ rootModules = universe .getTypes ()
253+ .stream ()
254+ .filter (ModuleLayerFeature ::typeIsModuleRootOrReachable )
255+ .map (t -> t .getJavaClass ().getModule ());
256+
257+ /*
258+ * Also make sure to include modules not seen by the analysis.
259+ */
260+ Set <String > extraUndiscoveredModules = ModuleLayer .boot ().modules ()
261+ .stream ()
262+ .filter (ModuleLayerFeature ::moduleExportsPackagesUnconditionally )
263+ .map (Module ::getName )
264+ .collect (Collectors .toSet ());
265+ extraModules .addAll (extraUndiscoveredModules );
266+ }
267+ }
268+
269+ return rootModules .distinct ();
270+ }
271+
254272 private static boolean typeIsReachable (AnalysisType t ) {
255273 return t .isReachable () && !t .isArray ();
256274 }
@@ -261,37 +279,11 @@ private static boolean typeIsModuleRootOrReachable(AnalysisType t) {
261279 }
262280
263281 Module m = t .getJavaClass ().getModule ();
264- return isModuleRoot (m );
282+ return moduleExportsPackagesUnconditionally (m );
265283 }
266284
267- private static boolean isModuleRoot (Module m ) {
268- if (!m .isNamed ()) {
269- return false ;
270- }
271-
272- /*
273- * When the main class of the application is loaded from the class path into the unnamed
274- * module of the application class loader, then the default set of root modules for the
275- * unnamed module is computed as follows (https://openjdk.org/jeps/261):
276- *
277- * 1. The java.se module is a root, if it exists. If it does not exist then every java.*
278- * module on the upgrade module path or among the system modules that exports at least one
279- * package, without qualification, is a root.
280- *
281- * 2. Every non-java.* module on the upgrade module path or among the system modules that
282- * exports at least one package, without qualification, is also a root.
283- */
284- if (m .getName ().startsWith ("java." )) {
285- return true ;
286- } else {
287- for (String pn : m .getPackages ()) {
288- if (m .isExported (pn )) {
289- return true ;
290- }
291- }
292- }
293-
294- return false ;
285+ private static boolean moduleExportsPackagesUnconditionally (Module m ) {
286+ return m .isNamed () && m .getPackages ().stream ().anyMatch (m ::isExported );
295287 }
296288
297289 /*
0 commit comments