2929import com .oracle .svm .core .jdk11 .BootModuleLayerSupport ;
3030import com .oracle .svm .core .jdk .JDK11OrLater ;
3131import com .oracle .svm .core .util .VMError ;
32+ import com .oracle .svm .util .ModuleSupport ;
3233import com .oracle .svm .util .ReflectionUtil ;
3334import org .graalvm .nativeimage .ImageSingletons ;
3435import org .graalvm .nativeimage .Platform ;
4546import java .lang .reflect .Constructor ;
4647import java .lang .reflect .Field ;
4748import java .lang .reflect .InvocationTargetException ;
49+ import java .lang .reflect .Method ;
4850import java .nio .file .Path ;
51+ import java .util .ArrayList ;
52+ import java .util .Arrays ;
53+ import java .util .HashMap ;
54+ import java .util .HashSet ;
4955import java .util .List ;
5056import java .util .Map ;
57+ import java .util .NoSuchElementException ;
5158import java .util .Optional ;
5259import java .util .Set ;
5360import java .util .function .Function ;
5461import java .util .stream .Collectors ;
62+ import java .util .stream .Stream ;
5563
5664/**
5765 * This feature:
8694@ AutomaticFeature
8795@ Platforms (Platform .HOSTED_ONLY .class )
8896public final class ModuleLayerFeature implements Feature {
89-
90- private Field moduleNameToModuleField ;
91- private Field moduleParentsField ;
9297 private Constructor <ModuleLayer > moduleLayerConstructor ;
98+ private Field moduleLayerNameToModuleField ;
99+ private Field moduleLayerParentsField ;
100+ private NameToModuleSynthesizer nameToModuleSynthesizer ;
93101
94102 @ Override
95103 public boolean isInConfiguration (IsInConfigurationAccess access ) {
@@ -99,17 +107,19 @@ public boolean isInConfiguration(IsInConfigurationAccess access) {
99107 @ Override
100108 public void afterRegistration (AfterRegistrationAccess access ) {
101109 ImageSingletons .add (BootModuleLayerSupport .class , new BootModuleLayerSupport ());
102- moduleNameToModuleField = ReflectionUtil .lookupField (ModuleLayer .class , "nameToModule" );
103- moduleParentsField = ReflectionUtil .lookupField (ModuleLayer .class , "parents" );
104110 moduleLayerConstructor = ReflectionUtil .lookupConstructor (ModuleLayer .class , Configuration .class , List .class , Function .class );
111+ moduleLayerNameToModuleField = ReflectionUtil .lookupField (ModuleLayer .class , "nameToModule" );
112+ moduleLayerParentsField = ReflectionUtil .lookupField (ModuleLayer .class , "parents" );
113+ nameToModuleSynthesizer = new NameToModuleSynthesizer ();
105114 }
106115
107116 @ Override
108117 public void beforeAnalysis (BeforeAnalysisAccess access ) {
109118 FeatureImpl .BeforeAnalysisAccessImpl accessImpl = (FeatureImpl .BeforeAnalysisAccessImpl ) access ;
110- Map <String , Module > baseModules = ModuleLayer .boot ().modules ()
119+ Set <String > baseModules = ModuleLayer .boot ().modules ()
111120 .stream ()
112- .collect (Collectors .toMap (Module ::getName , m -> m ));
121+ .map (Module ::getName )
122+ .collect (Collectors .toSet ());
113123 ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer (accessImpl .imageClassLoader , baseModules );
114124 BootModuleLayerSupport .instance ().setBootLayer (runtimeBootLayer );
115125 }
@@ -119,49 +129,73 @@ public void afterAnalysis(AfterAnalysisAccess access) {
119129 FeatureImpl .AfterAnalysisAccessImpl accessImpl = (FeatureImpl .AfterAnalysisAccessImpl ) access ;
120130 AnalysisUniverse universe = accessImpl .getUniverse ();
121131
122- Map < String , Module > reachableModules = universe .getTypes ()
132+ Stream < Module > analysisReachableModules = universe .getTypes ()
123133 .stream ()
124134 .filter (t -> t .isReachable () && !t .isArray ())
125135 .map (t -> t .getJavaClass ().getModule ())
126- .distinct ()
127- .filter (m -> m .isNamed () && !m .getDescriptor ().modifiers ().contains (ModuleDescriptor .Modifier .SYNTHETIC ))
128- .collect (Collectors .toMap (Module ::getName , m -> m ));
136+ .distinct ();
137+
138+ Set <String > allReachableModules = analysisReachableModules
139+ .filter (Module ::isNamed )
140+ .filter (m -> !m .getDescriptor ().modifiers ().contains (ModuleDescriptor .Modifier .SYNTHETIC ))
141+ .flatMap (ModuleLayerFeature ::extractRequiredModuleNames )
142+ .collect (Collectors .toSet ());
129143
130- ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer (accessImpl .imageClassLoader , reachableModules );
144+ ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer (accessImpl .imageClassLoader , allReachableModules );
131145 BootModuleLayerSupport .instance ().setBootLayer (runtimeBootLayer );
132146 }
133147
134- private ModuleLayer synthesizeRuntimeBootLayer (ImageClassLoader cl , Map <String , Module > reachableModules ) {
148+ /*
149+ * Creates a stream of module names that are reachable from a given module through "requires"
150+ */
151+ private static Stream <String > extractRequiredModuleNames (Module m ) {
152+ Stream <String > requiredModules = m .getDescriptor ().requires ().stream ().map (ModuleDescriptor .Requires ::name );
153+ return Stream .concat (Stream .of (m .getName ()), requiredModules );
154+ }
155+
156+ private ModuleLayer synthesizeRuntimeBootLayer (ImageClassLoader cl , Set <String > reachableModules ) {
135157 Configuration cf = synthesizeRuntimeBootLayerConfiguration (cl .modulepath (), reachableModules );
136158 try {
137159 ModuleLayer runtimeBootLayer = moduleLayerConstructor .newInstance (cf , List .of (), null );
138- patchRuntimeBootLayer (runtimeBootLayer , reachableModules );
139- // Ensure that the lazy field ModuleLayer.modules gets set
140- runtimeBootLayer .modules ();
160+ Map <String , Module > nameToModule = nameToModuleSynthesizer .synthesizeNameToModule (runtimeBootLayer , cl .getClassLoader ());
161+ patchRuntimeBootLayer (runtimeBootLayer , nameToModule );
141162 return runtimeBootLayer ;
142163 } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex ) {
143164 throw VMError .shouldNotReachHere ("Failed to synthesize the runtime boot module layer." , ex );
144165 }
145166 }
146167
147- private static Configuration synthesizeRuntimeBootLayerConfiguration (List <Path > mp , Map <String , Module > reachableModules ) {
168+ private static Configuration synthesizeRuntimeBootLayerConfiguration (List <Path > mp , Set <String > reachableModules ) {
148169 ModuleFinder beforeFinder = new BootModuleLayerModuleFinder ();
149170 ModuleFinder afterFinder = ModuleFinder .of (mp .toArray (Path []::new ));
150- Set < String > roots = reachableModules . keySet ();
171+
151172 try {
152- return Configuration .empty ().resolve (beforeFinder , afterFinder , roots );
173+ ModuleFinder composed = ModuleFinder .compose (beforeFinder , afterFinder );
174+ List <String > missingModules = new ArrayList <>();
175+ for (String module : reachableModules ) {
176+ Optional <ModuleReference > mref = composed .find (module );
177+ if (mref .isEmpty ()) {
178+ missingModules .add (module );
179+ }
180+ }
181+ reachableModules .removeAll (missingModules );
182+
183+ return Configuration .empty ().resolve (beforeFinder , afterFinder , reachableModules );
153184 } catch (FindException | ResolutionException | SecurityException ex ) {
154185 throw VMError .shouldNotReachHere ("Failed to synthesize the runtime boot module layer configuration." , ex );
155186 }
156187 }
157188
158- private void patchRuntimeBootLayer (ModuleLayer runtimeBootLayer , Map <String , Module > reachableModules ) {
189+ private void patchRuntimeBootLayer (ModuleLayer runtimeBootLayer , Map <String , Module > nameToModule ) {
159190 try {
160- moduleNameToModuleField .set (runtimeBootLayer , reachableModules );
161- moduleParentsField .set (runtimeBootLayer , List .of (ModuleLayer .empty ()));
191+ moduleLayerNameToModuleField .set (runtimeBootLayer , nameToModule );
192+ moduleLayerParentsField .set (runtimeBootLayer , List .of (ModuleLayer .empty ()));
162193 } catch (IllegalAccessException ex ) {
163194 throw VMError .shouldNotReachHere ("Failed to patch the runtime boot module layer." , ex );
164195 }
196+
197+ // Ensure that the lazy modules field gets set
198+ runtimeBootLayer .modules ();
165199 }
166200
167201 static class BootModuleLayerModuleFinder implements ModuleFinder {
@@ -184,4 +218,167 @@ public Set<ModuleReference> findAll() {
184218 .collect (Collectors .toSet ());
185219 }
186220 }
221+
222+ private static final class NameToModuleSynthesizer {
223+ private final Module everyoneModule ;
224+ private final Set <Module > everyoneSet ;
225+ private final Constructor <Module > moduleConstructor ;
226+ private final Field moduleLayerField ;
227+ private final Field moduleReadsField ;
228+ private final Field moduleOpenPackagesField ;
229+ private final Field moduleExportedPackagesField ;
230+ private final Method moduleFindModuleMethod ;
231+
232+ NameToModuleSynthesizer () {
233+ Method classGetDeclaredMethods0Method = ReflectionUtil .lookupMethod (Class .class , "getDeclaredFields0" , boolean .class );
234+ try {
235+ ModuleSupport .openModuleByClass (Module .class , ModuleLayerFeature .class );
236+ Field [] moduleClassFields = (Field []) classGetDeclaredMethods0Method .invoke (Module .class , false );
237+
238+ Field everyoneModuleField = findFieldByName (moduleClassFields , "EVERYONE_MODULE" );
239+ everyoneModuleField .setAccessible (true );
240+ everyoneModule = (Module ) everyoneModuleField .get (null );
241+
242+ moduleLayerField = findFieldByName (moduleClassFields , "layer" );
243+ moduleReadsField = findFieldByName (moduleClassFields , "reads" );
244+ moduleOpenPackagesField = findFieldByName (moduleClassFields , "openPackages" );
245+ moduleExportedPackagesField = findFieldByName (moduleClassFields , "exportedPackages" );
246+ moduleLayerField .setAccessible (true );
247+ moduleReadsField .setAccessible (true );
248+ moduleOpenPackagesField .setAccessible (true );
249+ moduleExportedPackagesField .setAccessible (true );
250+ } catch (ReflectiveOperationException | NoSuchElementException ex ) {
251+ throw VMError .shouldNotReachHere ("Failed to find the value of EVERYONE_MODULE field of Module class." , ex );
252+ }
253+ everyoneSet = Set .of (everyoneModule );
254+ moduleConstructor = ReflectionUtil .lookupConstructor (Module .class , ClassLoader .class , ModuleDescriptor .class );
255+ moduleFindModuleMethod = ReflectionUtil .lookupMethod (Module .class , "findModule" , String .class , Map .class , Map .class , List .class );
256+ }
257+
258+ private static Field findFieldByName (Field [] fields , String name ) {
259+ return Arrays .stream (fields ).filter (f -> f .getName ().equals (name )).findAny ().get ();
260+ }
261+
262+ /**
263+ * This method creates Module instances that will populate the runtime boot module layer of
264+ * the image. This implementation is copy-pasted from Module#defineModules(Configuration,
265+ * Function, ModuleLayer) with few simplifications (removing multiple classloader support)
266+ * and removal of VM state updates (otherwise we would be re-defining modules to the host
267+ * VM).
268+ */
269+ Map <String , Module > synthesizeNameToModule (ModuleLayer runtimeBootLayer , ClassLoader cl )
270+ throws IllegalAccessException , InvocationTargetException , InstantiationException {
271+ Configuration cf = runtimeBootLayer .configuration ();
272+
273+ int cap = (int ) (cf .modules ().size () / 0.75f + 1.0f );
274+ Map <String , Module > nameToModule = new HashMap <>(cap );
275+
276+ /*
277+ * Remove mapping of modules to classloaders. Create module instances without defining
278+ * them to the VM
279+ */
280+ for (ResolvedModule resolvedModule : cf .modules ()) {
281+ ModuleReference mref = resolvedModule .reference ();
282+ ModuleDescriptor descriptor = mref .descriptor ();
283+ String name = descriptor .name ();
284+ Module m = moduleConstructor .newInstance (cl , descriptor );
285+ moduleLayerField .set (m , runtimeBootLayer );
286+ nameToModule .put (name , m );
287+ }
288+
289+ /*
290+ * Setup readability and exports/opens. This part is unchanged, save for field setters
291+ * and VM update removals
292+ */
293+ for (ResolvedModule resolvedModule : cf .modules ()) {
294+ ModuleReference mref = resolvedModule .reference ();
295+ ModuleDescriptor descriptor = mref .descriptor ();
296+
297+ String mn = descriptor .name ();
298+ Module m = nameToModule .get (mn );
299+ assert m != null ;
300+
301+ Set <Module > reads = new HashSet <>();
302+ for (ResolvedModule other : resolvedModule .reads ()) {
303+ Module m2 = nameToModule .get (other .name ());
304+ reads .add (m2 );
305+ }
306+ moduleReadsField .set (m , reads );
307+
308+ if (!descriptor .isOpen () && !descriptor .isAutomatic ()) {
309+ if (descriptor .opens ().isEmpty ()) {
310+ Map <String , Set <Module >> exportedPackages = new HashMap <>();
311+ for (ModuleDescriptor .Exports exports : m .getDescriptor ().exports ()) {
312+ String source = exports .source ();
313+ if (exports .isQualified ()) {
314+ Set <Module > targets = new HashSet <>();
315+ for (String target : exports .targets ()) {
316+ Module m2 = nameToModule .get (target );
317+ if (m2 != null ) {
318+ targets .add (m2 );
319+ }
320+ }
321+ if (!targets .isEmpty ()) {
322+ exportedPackages .put (source , targets );
323+ }
324+ } else {
325+ exportedPackages .put (source , everyoneSet );
326+ }
327+ }
328+ moduleExportedPackagesField .set (m , exportedPackages );
329+ } else {
330+ Map <String , Set <Module >> openPackages = new HashMap <>();
331+ Map <String , Set <Module >> exportedPackages = new HashMap <>();
332+ for (ModuleDescriptor .Opens opens : descriptor .opens ()) {
333+ String source = opens .source ();
334+ if (opens .isQualified ()) {
335+ Set <Module > targets = new HashSet <>();
336+ for (String target : opens .targets ()) {
337+ Module m2 = (Module ) moduleFindModuleMethod .invoke (null , target , Map .of (), nameToModule , runtimeBootLayer .parents ());
338+ if (m2 != null ) {
339+ targets .add (m2 );
340+ }
341+ }
342+ if (!targets .isEmpty ()) {
343+ openPackages .put (source , targets );
344+ }
345+ } else {
346+ openPackages .put (source , everyoneSet );
347+ }
348+ }
349+
350+ for (ModuleDescriptor .Exports exports : descriptor .exports ()) {
351+ String source = exports .source ();
352+ Set <Module > openToTargets = openPackages .get (source );
353+ if (openToTargets != null && openToTargets .contains (everyoneModule )) {
354+ continue ;
355+ }
356+
357+ if (exports .isQualified ()) {
358+ Set <Module > targets = new HashSet <>();
359+ for (String target : exports .targets ()) {
360+ Module m2 = (Module ) moduleFindModuleMethod .invoke (null , target , Map .of (), nameToModule , runtimeBootLayer .parents ());
361+ if (m2 != null ) {
362+ if (openToTargets == null || !openToTargets .contains (m2 )) {
363+ targets .add (m2 );
364+ }
365+ }
366+ }
367+ if (!targets .isEmpty ()) {
368+ exportedPackages .put (source , targets );
369+ }
370+ } else {
371+ exportedPackages .put (source , everyoneSet );
372+ }
373+ }
374+
375+ moduleOpenPackagesField .set (m , openPackages );
376+ moduleExportedPackagesField .set (m , exportedPackages );
377+ }
378+ }
379+ }
380+
381+ return nameToModule ;
382+ }
383+ }
187384}
0 commit comments