From 165b245cf1117e4957b4562dcc2afc1fabed1c0b Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Sun, 19 Sep 2021 14:01:07 +0200 Subject: [PATCH 1/7] Extend boot module layer to include required modules --- .../oracle/svm/hosted/ModuleLayerFeature.java | 205 +++++++++++++++--- 1 file changed, 180 insertions(+), 25 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 15cdecb526c2..a64c89eee839 100644 --- a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -45,13 +45,17 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Path; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * This feature: @@ -87,9 +91,17 @@ @Platforms(Platform.HOSTED_ONLY.class) public final class ModuleLayerFeature implements Feature { - private Field moduleNameToModuleField; - private Field moduleParentsField; + private Module everyoneModule; + private Set everyoneSet; + private Constructor moduleConstructor; private Constructor moduleLayerConstructor; + private Field moduleLayerField; + private Field moduleLayerNameToModuleField; + private Field moduleLayerParentsField; + private Field moduleReadsField; + private Field moduleOpenPackagesField; + private Field moduleExportedPackagesField; + private Method moduleFindModuleMethod; @Override public boolean isInConfiguration(IsInConfigurationAccess access) { @@ -99,17 +111,26 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.add(BootModuleLayerSupport.class, new BootModuleLayerSupport()); - moduleNameToModuleField = ReflectionUtil.lookupField(ModuleLayer.class, "nameToModule"); - moduleParentsField = ReflectionUtil.lookupField(ModuleLayer.class, "parents"); + everyoneModule = ReflectionUtil.readField(Module.class, "EVERYONE_MODULE", null); + everyoneSet = Set.of(everyoneModule); + moduleConstructor = ReflectionUtil.lookupConstructor(Module.class, ClassLoader.class, ModuleDescriptor.class); moduleLayerConstructor = ReflectionUtil.lookupConstructor(ModuleLayer.class, Configuration.class, List.class, Function.class); + moduleLayerField = ReflectionUtil.lookupField(Module.class, "layer"); + moduleLayerNameToModuleField = ReflectionUtil.lookupField(ModuleLayer.class, "nameToModule"); + moduleLayerParentsField = ReflectionUtil.lookupField(ModuleLayer.class, "parents"); + moduleReadsField = ReflectionUtil.lookupField(Module.class, "reads"); + moduleOpenPackagesField = ReflectionUtil.lookupField(Module.class, "openPackages"); + moduleExportedPackagesField = ReflectionUtil.lookupField(Module.class, "exportedPackages"); + moduleFindModuleMethod = ReflectionUtil.lookupMethod(Module.class, "findModule", String.class, Map.class, Map.class, List.class); } @Override public void beforeAnalysis(BeforeAnalysisAccess access) { FeatureImpl.BeforeAnalysisAccessImpl accessImpl = (FeatureImpl.BeforeAnalysisAccessImpl) access; - Map baseModules = ModuleLayer.boot().modules() - .stream() - .collect(Collectors.toMap(Module::getName, m -> m)); + Set baseModules = ModuleLayer.boot().modules() + .stream() + .map(Module::getName) + .collect(Collectors.toSet()); ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer(accessImpl.imageClassLoader, baseModules); BootModuleLayerSupport.instance().setBootLayer(runtimeBootLayer); } @@ -119,49 +140,183 @@ public void afterAnalysis(AfterAnalysisAccess access) { FeatureImpl.AfterAnalysisAccessImpl accessImpl = (FeatureImpl.AfterAnalysisAccessImpl) access; AnalysisUniverse universe = accessImpl.getUniverse(); - Map reachableModules = universe.getTypes() - .stream() - .filter(t -> t.isReachable() && !t.isArray()) - .map(t -> t.getJavaClass().getModule()) - .distinct() - .filter(m -> m.isNamed() && !m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) - .collect(Collectors.toMap(Module::getName, m -> m)); + Stream analysisReachableModules = universe.getTypes() + .stream() + .filter(t -> t.isReachable() && !t.isArray()) + .map(t -> t.getJavaClass().getModule()) + .distinct(); - ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer(accessImpl.imageClassLoader, reachableModules); + Set allReachableModules = analysisReachableModules + .filter(Module::isNamed) + .filter(m -> !m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) + .flatMap(ModuleLayerFeature::extractRequiredModuleNames) + .collect(Collectors.toSet()); + + ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer(accessImpl.imageClassLoader, allReachableModules); BootModuleLayerSupport.instance().setBootLayer(runtimeBootLayer); } - private ModuleLayer synthesizeRuntimeBootLayer(ImageClassLoader cl, Map reachableModules) { + private ModuleLayer synthesizeRuntimeBootLayer(ImageClassLoader cl, Set reachableModules) { Configuration cf = synthesizeRuntimeBootLayerConfiguration(cl.modulepath(), reachableModules); try { ModuleLayer runtimeBootLayer = moduleLayerConstructor.newInstance(cf, List.of(), null); - patchRuntimeBootLayer(runtimeBootLayer, reachableModules); - // Ensure that the lazy field ModuleLayer.modules gets set - runtimeBootLayer.modules(); + Map nameToModule = synthesizeNameToModule(runtimeBootLayer, cl.getClassLoader()); + patchRuntimeBootLayer(runtimeBootLayer, nameToModule); return runtimeBootLayer; } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) { throw VMError.shouldNotReachHere("Failed to synthesize the runtime boot module layer.", ex); } } - private static Configuration synthesizeRuntimeBootLayerConfiguration(List mp, Map reachableModules) { + private Configuration synthesizeRuntimeBootLayerConfiguration(List mp, Set reachableModules) { ModuleFinder beforeFinder = new BootModuleLayerModuleFinder(); ModuleFinder afterFinder = ModuleFinder.of(mp.toArray(Path[]::new)); - Set roots = reachableModules.keySet(); try { - return Configuration.empty().resolve(beforeFinder, afterFinder, roots); + return Configuration.empty().resolve(beforeFinder, afterFinder, reachableModules); } catch (FindException | ResolutionException | SecurityException ex) { throw VMError.shouldNotReachHere("Failed to synthesize the runtime boot module layer configuration.", ex); } } - private void patchRuntimeBootLayer(ModuleLayer runtimeBootLayer, Map reachableModules) { + private void patchRuntimeBootLayer(ModuleLayer runtimeBootLayer, Map nameToModule) { try { - moduleNameToModuleField.set(runtimeBootLayer, reachableModules); - moduleParentsField.set(runtimeBootLayer, List.of(ModuleLayer.empty())); + moduleLayerNameToModuleField.set(runtimeBootLayer, nameToModule); + moduleLayerParentsField.set(runtimeBootLayer, List.of(ModuleLayer.empty())); } catch (IllegalAccessException ex) { throw VMError.shouldNotReachHere("Failed to patch the runtime boot module layer.", ex); } + + // Ensure that the lazy modules field gets set + runtimeBootLayer.modules(); + } + + /** + * This method creates Module instances that will populate the runtime boot module layer of the image. + * This implementation is copy-pasted from {@link java.lang.Module#defineModules(Configuration, Function, ModuleLayer)} + * with few simplifications (removing multiple classloader support) and removal of VM state updates + * (otherwise we would be re-defining modules to the host VM). + */ + private Map synthesizeNameToModule(ModuleLayer runtimeBootLayer, ClassLoader cl) + throws IllegalAccessException, InvocationTargetException, InstantiationException { + Configuration cf = runtimeBootLayer.configuration(); + + int cap = (int) (cf.modules().size() / 0.75f + 1.0f); + Map nameToModule = new HashMap<>(cap); + + /* + * Remove mapping of modules to classloaders. + * Create module instances without defining them to the VM + */ + for (ResolvedModule resolvedModule : cf.modules()) { + ModuleReference mref = resolvedModule.reference(); + ModuleDescriptor descriptor = mref.descriptor(); + String name = descriptor.name(); + Module m = moduleConstructor.newInstance(cl, descriptor); + moduleLayerField.set(m, runtimeBootLayer); + nameToModule.put(name, m); + } + + /* + * Setup readability and exports/opens. + * This part is unchanged, save for field setters and VM update removals + */ + for (ResolvedModule resolvedModule : cf.modules()) { + ModuleReference mref = resolvedModule.reference(); + ModuleDescriptor descriptor = mref.descriptor(); + + String mn = descriptor.name(); + Module m = nameToModule.get(mn); + assert m != null; + + Set reads = new HashSet<>(); + for (ResolvedModule other : resolvedModule.reads()) { + Module m2 = nameToModule.get(other.name()); + reads.add(m2); + } + moduleReadsField.set(m, reads); + + if (!descriptor.isOpen() && !descriptor.isAutomatic()) { + if (descriptor.opens().isEmpty()) { + Map> exportedPackages = new HashMap<>(); + for (ModuleDescriptor.Exports exports : m.getDescriptor().exports()) { + String source = exports.source(); + if (exports.isQualified()) { + Set targets = new HashSet<>(); + for (String target : exports.targets()) { + Module m2 = nameToModule.get(target); + if (m2 != null) { + targets.add(m2); + } + } + if (!targets.isEmpty()) { + exportedPackages.put(source, targets); + } + } else { + exportedPackages.put(source, everyoneSet); + } + } + moduleExportedPackagesField.set(m, exportedPackages); + } else { + Map> openPackages = new HashMap<>(); + Map> exportedPackages = new HashMap<>(); + for (ModuleDescriptor.Opens opens : descriptor.opens()) { + String source = opens.source(); + if (opens.isQualified()) { + Set targets = new HashSet<>(); + for (String target : opens.targets()) { + Module m2 = (Module) moduleFindModuleMethod.invoke(null, target, Map.of(), nameToModule, runtimeBootLayer.parents()); + if (m2 != null) { + targets.add(m2); + } + } + if (!targets.isEmpty()) { + openPackages.put(source, targets); + } + } else { + openPackages.put(source, everyoneSet); + } + } + + for (ModuleDescriptor.Exports exports : descriptor.exports()) { + String source = exports.source(); + Set openToTargets = openPackages.get(source); + if (openToTargets != null && openToTargets.contains(everyoneModule)) { + continue; + } + + if (exports.isQualified()) { + Set targets = new HashSet<>(); + for (String target : exports.targets()) { + Module m2 = (Module) moduleFindModuleMethod.invoke(null, target, Map.of(), nameToModule, runtimeBootLayer.parents()); + if (m2 != null) { + if (openToTargets == null || !openToTargets.contains(m2)) { + targets.add(m2); + } + } + } + if (!targets.isEmpty()) { + exportedPackages.put(source, targets); + } + } else { + exportedPackages.put(source, everyoneSet); + } + } + + moduleOpenPackagesField.set(m, openPackages); + moduleExportedPackagesField.set(m, exportedPackages); + } + } + } + + return nameToModule; + } + + /* + * Creates a stream of module names that are reachable from a given module through "requires" + */ + private static Stream extractRequiredModuleNames(Module m) { + Stream requiredModules = m.getDescriptor().requires().stream().map(ModuleDescriptor.Requires::name); + return Stream.concat(Stream.of(m.getName()), requiredModules); } static class BootModuleLayerModuleFinder implements ModuleFinder { From 0b1dc17033c78c2069c5a9687fb4e728c0398cc9 Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Thu, 30 Sep 2021 11:08:11 +0200 Subject: [PATCH 2/7] Move reflection fields and nameToModule synthesizing logic into a final class --- .../oracle/svm/hosted/ModuleLayerFeature.java | 289 +++++++++--------- 1 file changed, 148 insertions(+), 141 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java index a64c89eee839..780e1ba1883d 100644 --- a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -90,18 +90,10 @@ @AutomaticFeature @Platforms(Platform.HOSTED_ONLY.class) public final class ModuleLayerFeature implements Feature { - - private Module everyoneModule; - private Set everyoneSet; - private Constructor moduleConstructor; private Constructor moduleLayerConstructor; - private Field moduleLayerField; private Field moduleLayerNameToModuleField; private Field moduleLayerParentsField; - private Field moduleReadsField; - private Field moduleOpenPackagesField; - private Field moduleExportedPackagesField; - private Method moduleFindModuleMethod; + private NameToModuleSynthesizer nameToModuleSynthesizer; @Override public boolean isInConfiguration(IsInConfigurationAccess access) { @@ -111,23 +103,16 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.add(BootModuleLayerSupport.class, new BootModuleLayerSupport()); - everyoneModule = ReflectionUtil.readField(Module.class, "EVERYONE_MODULE", null); - everyoneSet = Set.of(everyoneModule); - moduleConstructor = ReflectionUtil.lookupConstructor(Module.class, ClassLoader.class, ModuleDescriptor.class); moduleLayerConstructor = ReflectionUtil.lookupConstructor(ModuleLayer.class, Configuration.class, List.class, Function.class); - moduleLayerField = ReflectionUtil.lookupField(Module.class, "layer"); moduleLayerNameToModuleField = ReflectionUtil.lookupField(ModuleLayer.class, "nameToModule"); moduleLayerParentsField = ReflectionUtil.lookupField(ModuleLayer.class, "parents"); - moduleReadsField = ReflectionUtil.lookupField(Module.class, "reads"); - moduleOpenPackagesField = ReflectionUtil.lookupField(Module.class, "openPackages"); - moduleExportedPackagesField = ReflectionUtil.lookupField(Module.class, "exportedPackages"); - moduleFindModuleMethod = ReflectionUtil.lookupMethod(Module.class, "findModule", String.class, Map.class, Map.class, List.class); + nameToModuleSynthesizer = new NameToModuleSynthesizer(); } @Override public void beforeAnalysis(BeforeAnalysisAccess access) { FeatureImpl.BeforeAnalysisAccessImpl accessImpl = (FeatureImpl.BeforeAnalysisAccessImpl) access; - Set baseModules = ModuleLayer.boot().modules() + Set baseModules = ModuleLayer.boot().modules() .stream() .map(Module::getName) .collect(Collectors.toSet()); @@ -156,11 +141,19 @@ public void afterAnalysis(AfterAnalysisAccess access) { BootModuleLayerSupport.instance().setBootLayer(runtimeBootLayer); } + /* + * Creates a stream of module names that are reachable from a given module through "requires" + */ + private static Stream extractRequiredModuleNames(Module m) { + Stream requiredModules = m.getDescriptor().requires().stream().map(ModuleDescriptor.Requires::name); + return Stream.concat(Stream.of(m.getName()), requiredModules); + } + private ModuleLayer synthesizeRuntimeBootLayer(ImageClassLoader cl, Set reachableModules) { Configuration cf = synthesizeRuntimeBootLayerConfiguration(cl.modulepath(), reachableModules); try { ModuleLayer runtimeBootLayer = moduleLayerConstructor.newInstance(cf, List.of(), null); - Map nameToModule = synthesizeNameToModule(runtimeBootLayer, cl.getClassLoader()); + Map nameToModule = nameToModuleSynthesizer.synthesizeNameToModule(runtimeBootLayer, cl.getClassLoader()); patchRuntimeBootLayer(runtimeBootLayer, nameToModule); return runtimeBootLayer; } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) { @@ -190,153 +183,167 @@ private void patchRuntimeBootLayer(ModuleLayer runtimeBootLayer, Map synthesizeNameToModule(ModuleLayer runtimeBootLayer, ClassLoader cl) - throws IllegalAccessException, InvocationTargetException, InstantiationException { - Configuration cf = runtimeBootLayer.configuration(); + static class BootModuleLayerModuleFinder implements ModuleFinder { - int cap = (int) (cf.modules().size() / 0.75f + 1.0f); - Map nameToModule = new HashMap<>(cap); + @Override + public Optional find(String name) { + return ModuleLayer.boot() + .configuration() + .findModule(name) + .map(ResolvedModule::reference); + } - /* - * Remove mapping of modules to classloaders. - * Create module instances without defining them to the VM - */ - for (ResolvedModule resolvedModule : cf.modules()) { - ModuleReference mref = resolvedModule.reference(); - ModuleDescriptor descriptor = mref.descriptor(); - String name = descriptor.name(); - Module m = moduleConstructor.newInstance(cl, descriptor); - moduleLayerField.set(m, runtimeBootLayer); - nameToModule.put(name, m); + @Override + public Set findAll() { + return ModuleLayer.boot() + .configuration() + .modules() + .stream() + .map(ResolvedModule::reference) + .collect(Collectors.toSet()); + } + } + + private static final class NameToModuleSynthesizer { + private final Module everyoneModule; + private final Set everyoneSet; + private final Constructor moduleConstructor; + private final Field moduleLayerField; + private final Field moduleReadsField; + private final Field moduleOpenPackagesField; + private final Field moduleExportedPackagesField; + private final Method moduleFindModuleMethod; + + public NameToModuleSynthesizer() { + everyoneModule = ReflectionUtil.readField(Module.class, "EVERYONE_MODULE", null); + everyoneSet = Set.of(everyoneModule); + moduleConstructor = ReflectionUtil.lookupConstructor(Module.class, ClassLoader.class, ModuleDescriptor.class); + moduleLayerField = ReflectionUtil.lookupField(Module.class, "layer"); + moduleReadsField = ReflectionUtil.lookupField(Module.class, "reads"); + moduleOpenPackagesField = ReflectionUtil.lookupField(Module.class, "openPackages"); + moduleExportedPackagesField = ReflectionUtil.lookupField(Module.class, "exportedPackages"); + moduleFindModuleMethod = ReflectionUtil.lookupMethod(Module.class, "findModule", String.class, Map.class, Map.class, List.class); } - /* - * Setup readability and exports/opens. - * This part is unchanged, save for field setters and VM update removals + /** + * This method creates Module instances that will populate the runtime boot module layer of the image. + * This implementation is copy-pasted from {@link java.lang.Module#defineModules(Configuration, Function, ModuleLayer)} + * with few simplifications (removing multiple classloader support) and removal of VM state updates + * (otherwise we would be re-defining modules to the host VM). */ - for (ResolvedModule resolvedModule : cf.modules()) { - ModuleReference mref = resolvedModule.reference(); - ModuleDescriptor descriptor = mref.descriptor(); + Map synthesizeNameToModule(ModuleLayer runtimeBootLayer, ClassLoader cl) + throws IllegalAccessException, InvocationTargetException, InstantiationException { + Configuration cf = runtimeBootLayer.configuration(); - String mn = descriptor.name(); - Module m = nameToModule.get(mn); - assert m != null; + int cap = (int) (cf.modules().size() / 0.75f + 1.0f); + Map nameToModule = new HashMap<>(cap); - Set reads = new HashSet<>(); - for (ResolvedModule other : resolvedModule.reads()) { - Module m2 = nameToModule.get(other.name()); - reads.add(m2); + /* + * Remove mapping of modules to classloaders. + * Create module instances without defining them to the VM + */ + for (ResolvedModule resolvedModule : cf.modules()) { + ModuleReference mref = resolvedModule.reference(); + ModuleDescriptor descriptor = mref.descriptor(); + String name = descriptor.name(); + Module m = moduleConstructor.newInstance(cl, descriptor); + moduleLayerField.set(m, runtimeBootLayer); + nameToModule.put(name, m); } - moduleReadsField.set(m, reads); - if (!descriptor.isOpen() && !descriptor.isAutomatic()) { - if (descriptor.opens().isEmpty()) { - Map> exportedPackages = new HashMap<>(); - for (ModuleDescriptor.Exports exports : m.getDescriptor().exports()) { - String source = exports.source(); - if (exports.isQualified()) { - Set targets = new HashSet<>(); - for (String target : exports.targets()) { - Module m2 = nameToModule.get(target); - if (m2 != null) { - targets.add(m2); + /* + * Setup readability and exports/opens. + * This part is unchanged, save for field setters and VM update removals + */ + for (ResolvedModule resolvedModule : cf.modules()) { + ModuleReference mref = resolvedModule.reference(); + ModuleDescriptor descriptor = mref.descriptor(); + + String mn = descriptor.name(); + Module m = nameToModule.get(mn); + assert m != null; + + Set reads = new HashSet<>(); + for (ResolvedModule other : resolvedModule.reads()) { + Module m2 = nameToModule.get(other.name()); + reads.add(m2); + } + moduleReadsField.set(m, reads); + + if (!descriptor.isOpen() && !descriptor.isAutomatic()) { + if (descriptor.opens().isEmpty()) { + Map> exportedPackages = new HashMap<>(); + for (ModuleDescriptor.Exports exports : m.getDescriptor().exports()) { + String source = exports.source(); + if (exports.isQualified()) { + Set targets = new HashSet<>(); + for (String target : exports.targets()) { + Module m2 = nameToModule.get(target); + if (m2 != null) { + targets.add(m2); + } } + if (!targets.isEmpty()) { + exportedPackages.put(source, targets); + } + } else { + exportedPackages.put(source, everyoneSet); } - if (!targets.isEmpty()) { - exportedPackages.put(source, targets); - } - } else { - exportedPackages.put(source, everyoneSet); } - } - moduleExportedPackagesField.set(m, exportedPackages); - } else { - Map> openPackages = new HashMap<>(); - Map> exportedPackages = new HashMap<>(); - for (ModuleDescriptor.Opens opens : descriptor.opens()) { - String source = opens.source(); - if (opens.isQualified()) { - Set targets = new HashSet<>(); - for (String target : opens.targets()) { - Module m2 = (Module) moduleFindModuleMethod.invoke(null, target, Map.of(), nameToModule, runtimeBootLayer.parents()); - if (m2 != null) { - targets.add(m2); + moduleExportedPackagesField.set(m, exportedPackages); + } else { + Map> openPackages = new HashMap<>(); + Map> exportedPackages = new HashMap<>(); + for (ModuleDescriptor.Opens opens : descriptor.opens()) { + String source = opens.source(); + if (opens.isQualified()) { + Set targets = new HashSet<>(); + for (String target : opens.targets()) { + Module m2 = (Module) moduleFindModuleMethod.invoke(null, target, Map.of(), nameToModule, runtimeBootLayer.parents()); + if (m2 != null) { + targets.add(m2); + } } + if (!targets.isEmpty()) { + openPackages.put(source, targets); + } + } else { + openPackages.put(source, everyoneSet); } - if (!targets.isEmpty()) { - openPackages.put(source, targets); - } - } else { - openPackages.put(source, everyoneSet); } - } - for (ModuleDescriptor.Exports exports : descriptor.exports()) { - String source = exports.source(); - Set openToTargets = openPackages.get(source); - if (openToTargets != null && openToTargets.contains(everyoneModule)) { - continue; - } + for (ModuleDescriptor.Exports exports : descriptor.exports()) { + String source = exports.source(); + Set openToTargets = openPackages.get(source); + if (openToTargets != null && openToTargets.contains(everyoneModule)) { + continue; + } - if (exports.isQualified()) { - Set targets = new HashSet<>(); - for (String target : exports.targets()) { - Module m2 = (Module) moduleFindModuleMethod.invoke(null, target, Map.of(), nameToModule, runtimeBootLayer.parents()); - if (m2 != null) { - if (openToTargets == null || !openToTargets.contains(m2)) { - targets.add(m2); + if (exports.isQualified()) { + Set targets = new HashSet<>(); + for (String target : exports.targets()) { + Module m2 = (Module) moduleFindModuleMethod.invoke(null, target, Map.of(), nameToModule, runtimeBootLayer.parents()); + if (m2 != null) { + if (openToTargets == null || !openToTargets.contains(m2)) { + targets.add(m2); + } } } + if (!targets.isEmpty()) { + exportedPackages.put(source, targets); + } + } else { + exportedPackages.put(source, everyoneSet); } - if (!targets.isEmpty()) { - exportedPackages.put(source, targets); - } - } else { - exportedPackages.put(source, everyoneSet); } - } - moduleOpenPackagesField.set(m, openPackages); - moduleExportedPackagesField.set(m, exportedPackages); + moduleOpenPackagesField.set(m, openPackages); + moduleExportedPackagesField.set(m, exportedPackages); + } } } - } - - return nameToModule; - } - - /* - * Creates a stream of module names that are reachable from a given module through "requires" - */ - private static Stream extractRequiredModuleNames(Module m) { - Stream requiredModules = m.getDescriptor().requires().stream().map(ModuleDescriptor.Requires::name); - return Stream.concat(Stream.of(m.getName()), requiredModules); - } - - static class BootModuleLayerModuleFinder implements ModuleFinder { - @Override - public Optional find(String name) { - return ModuleLayer.boot() - .configuration() - .findModule(name) - .map(ResolvedModule::reference); - } - - @Override - public Set findAll() { - return ModuleLayer.boot() - .configuration() - .modules() - .stream() - .map(ResolvedModule::reference) - .collect(Collectors.toSet()); + return nameToModule; } } } From 93c9ed9d778fd1f10e961985e4e3fea6ab5cca85 Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Fri, 8 Oct 2021 10:29:44 +0200 Subject: [PATCH 3/7] Support for static required modules --- .../com/oracle/svm/hosted/ModuleLayerFeature.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 780e1ba1883d..2e54072ef57a 100644 --- a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -47,6 +47,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -164,7 +165,18 @@ private ModuleLayer synthesizeRuntimeBootLayer(ImageClassLoader cl, Set private Configuration synthesizeRuntimeBootLayerConfiguration(List mp, Set reachableModules) { ModuleFinder beforeFinder = new BootModuleLayerModuleFinder(); ModuleFinder afterFinder = ModuleFinder.of(mp.toArray(Path[]::new)); + try { + ModuleFinder composed = ModuleFinder.compose(beforeFinder, afterFinder); + List notFoundModules = new ArrayList<>(); + for (String module : reachableModules) { + Optional mref = composed.find(module); + if (mref.isEmpty()) { + notFoundModules.add(module); + } + } + reachableModules.removeAll(notFoundModules); + return Configuration.empty().resolve(beforeFinder, afterFinder, reachableModules); } catch (FindException | ResolutionException | SecurityException ex) { throw VMError.shouldNotReachHere("Failed to synthesize the runtime boot module layer configuration.", ex); From 6d816896293428e7d881003b6f71c61dde3a5449 Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Fri, 8 Oct 2021 11:09:36 +0200 Subject: [PATCH 4/7] Rename notFoundModules to missingModules --- .../src/com/oracle/svm/hosted/ModuleLayerFeature.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 2e54072ef57a..c0c0ede93a69 100644 --- a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -168,14 +168,14 @@ private Configuration synthesizeRuntimeBootLayerConfiguration(List mp, Set try { ModuleFinder composed = ModuleFinder.compose(beforeFinder, afterFinder); - List notFoundModules = new ArrayList<>(); + List missingModules = new ArrayList<>(); for (String module : reachableModules) { Optional mref = composed.find(module); if (mref.isEmpty()) { - notFoundModules.add(module); + missingModules.add(module); } } - reachableModules.removeAll(notFoundModules); + reachableModules.removeAll(missingModules); return Configuration.empty().resolve(beforeFinder, afterFinder, reachableModules); } catch (FindException | ResolutionException | SecurityException ex) { From 6255d137b14fb66f86b37d48337ff8fd5eadcf43 Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Fri, 8 Oct 2021 11:24:02 +0200 Subject: [PATCH 5/7] Style fixes --- .../src/com/oracle/svm/hosted/ModuleLayerFeature.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java index c0c0ede93a69..e88afc6adc73 100644 --- a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -162,7 +162,7 @@ private ModuleLayer synthesizeRuntimeBootLayer(ImageClassLoader cl, Set } } - private Configuration synthesizeRuntimeBootLayerConfiguration(List mp, Set reachableModules) { + private static Configuration synthesizeRuntimeBootLayerConfiguration(List mp, Set reachableModules) { ModuleFinder beforeFinder = new BootModuleLayerModuleFinder(); ModuleFinder afterFinder = ModuleFinder.of(mp.toArray(Path[]::new)); @@ -226,7 +226,7 @@ private static final class NameToModuleSynthesizer { private final Field moduleExportedPackagesField; private final Method moduleFindModuleMethod; - public NameToModuleSynthesizer() { + NameToModuleSynthesizer() { everyoneModule = ReflectionUtil.readField(Module.class, "EVERYONE_MODULE", null); everyoneSet = Set.of(everyoneModule); moduleConstructor = ReflectionUtil.lookupConstructor(Module.class, ClassLoader.class, ModuleDescriptor.class); @@ -239,7 +239,7 @@ public NameToModuleSynthesizer() { /** * This method creates Module instances that will populate the runtime boot module layer of the image. - * This implementation is copy-pasted from {@link java.lang.Module#defineModules(Configuration, Function, ModuleLayer)} + * This implementation is copy-pasted from Module#defineModules(Configuration, Function, ModuleLayer) * with few simplifications (removing multiple classloader support) and removal of VM state updates * (otherwise we would be re-defining modules to the host VM). */ From d39874f068a222a9b12fcdf15d93b7e4c1bd4413 Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Fri, 15 Oct 2021 18:45:06 +0200 Subject: [PATCH 6/7] Add workaround for reflection filters --- .../oracle/svm/hosted/ModuleLayerFeature.java | 32 ++++++++++++++++--- .../com/oracle/svm/util/ReflectionUtil.java | 5 ++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java index e88afc6adc73..99ed39b7c087 100644 --- a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -29,6 +29,7 @@ import com.oracle.svm.core.jdk11.BootModuleLayerSupport; import com.oracle.svm.core.jdk.JDK11OrLater; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; @@ -48,10 +49,12 @@ import java.lang.reflect.Method; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -227,16 +230,35 @@ private static final class NameToModuleSynthesizer { private final Method moduleFindModuleMethod; NameToModuleSynthesizer() { - everyoneModule = ReflectionUtil.readField(Module.class, "EVERYONE_MODULE", null); + Method classGetDeclaredMethods0Method = ReflectionUtil.lookupMethod(Class.class, "getDeclaredFields0", boolean.class); + try { + ModuleSupport.openModuleByClass(Module.class, ModuleLayerFeature.class); + Field[] moduleClassFields = (Field[]) classGetDeclaredMethods0Method.invoke(Module.class, false); + + Field everyoneModuleField = findFieldByName(moduleClassFields, "EVERYONE_MODULE"); + everyoneModuleField.setAccessible(true); + everyoneModule = (Module) everyoneModuleField.get(null); + + moduleLayerField = findFieldByName(moduleClassFields, "layer"); + moduleReadsField = findFieldByName(moduleClassFields, "reads"); + moduleOpenPackagesField = findFieldByName(moduleClassFields, "openPackages"); + moduleExportedPackagesField = findFieldByName(moduleClassFields, "exportedPackages"); + moduleLayerField.setAccessible(true); + moduleReadsField.setAccessible(true); + moduleOpenPackagesField.setAccessible(true); + moduleExportedPackagesField.setAccessible(true); + } catch (ReflectiveOperationException | NoSuchElementException ex) { + throw VMError.shouldNotReachHere("Failed to find the value of EVERYONE_MODULE field of Module class.", ex); + } everyoneSet = Set.of(everyoneModule); moduleConstructor = ReflectionUtil.lookupConstructor(Module.class, ClassLoader.class, ModuleDescriptor.class); - moduleLayerField = ReflectionUtil.lookupField(Module.class, "layer"); - moduleReadsField = ReflectionUtil.lookupField(Module.class, "reads"); - moduleOpenPackagesField = ReflectionUtil.lookupField(Module.class, "openPackages"); - moduleExportedPackagesField = ReflectionUtil.lookupField(Module.class, "exportedPackages"); moduleFindModuleMethod = ReflectionUtil.lookupMethod(Module.class, "findModule", String.class, Map.class, Map.class, List.class); } + private Field findFieldByName(Field[] fields, String name) { + return Arrays.stream(fields).filter(f -> f.getName().equals(name)).findAny().get(); + } + /** * This method creates Module instances that will populate the runtime boot module layer of the image. * This implementation is copy-pasted from Module#defineModules(Configuration, Function, ModuleLayer) diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java index c63895bbaadd..8b40bf12d983 100644 --- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java @@ -31,7 +31,10 @@ import java.lang.reflect.Method; /** - * This class contains utility methods for commonly used reflection functionality. + * This class contains utility methods for commonly used reflection functionality. Note that lookups + * will not work on JDK 17 in cases when the field/method is filtered. See + * jdk.internal.reflect.Reflection#fieldFilterMap for more information or + * com.oracle.svm.hosted.ModuleLayerFeature for an example of a workaround in such cases. */ public final class ReflectionUtil { From 8d88db0577737ed06dd48c68717f5f38a72be312 Mon Sep 17 00:00:00 2001 From: ivan-ristovic Date: Thu, 21 Oct 2021 15:49:54 +0200 Subject: [PATCH 7/7] Fix several style issues --- .../oracle/svm/hosted/ModuleLayerFeature.java | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 99ed39b7c087..3f0c27bec9d0 100644 --- a/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -117,9 +117,9 @@ public void afterRegistration(AfterRegistrationAccess access) { public void beforeAnalysis(BeforeAnalysisAccess access) { FeatureImpl.BeforeAnalysisAccessImpl accessImpl = (FeatureImpl.BeforeAnalysisAccessImpl) access; Set baseModules = ModuleLayer.boot().modules() - .stream() - .map(Module::getName) - .collect(Collectors.toSet()); + .stream() + .map(Module::getName) + .collect(Collectors.toSet()); ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer(accessImpl.imageClassLoader, baseModules); BootModuleLayerSupport.instance().setBootLayer(runtimeBootLayer); } @@ -130,16 +130,16 @@ public void afterAnalysis(AfterAnalysisAccess access) { AnalysisUniverse universe = accessImpl.getUniverse(); Stream analysisReachableModules = universe.getTypes() - .stream() - .filter(t -> t.isReachable() && !t.isArray()) - .map(t -> t.getJavaClass().getModule()) - .distinct(); + .stream() + .filter(t -> t.isReachable() && !t.isArray()) + .map(t -> t.getJavaClass().getModule()) + .distinct(); Set allReachableModules = analysisReachableModules - .filter(Module::isNamed) - .filter(m -> !m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) - .flatMap(ModuleLayerFeature::extractRequiredModuleNames) - .collect(Collectors.toSet()); + .filter(Module::isNamed) + .filter(m -> !m.getDescriptor().modifiers().contains(ModuleDescriptor.Modifier.SYNTHETIC)) + .flatMap(ModuleLayerFeature::extractRequiredModuleNames) + .collect(Collectors.toSet()); ModuleLayer runtimeBootLayer = synthesizeRuntimeBootLayer(accessImpl.imageClassLoader, allReachableModules); BootModuleLayerSupport.instance().setBootLayer(runtimeBootLayer); @@ -203,19 +203,19 @@ static class BootModuleLayerModuleFinder implements ModuleFinder { @Override public Optional find(String name) { return ModuleLayer.boot() - .configuration() - .findModule(name) - .map(ResolvedModule::reference); + .configuration() + .findModule(name) + .map(ResolvedModule::reference); } @Override public Set findAll() { return ModuleLayer.boot() - .configuration() - .modules() - .stream() - .map(ResolvedModule::reference) - .collect(Collectors.toSet()); + .configuration() + .modules() + .stream() + .map(ResolvedModule::reference) + .collect(Collectors.toSet()); } } @@ -255,26 +255,27 @@ private static final class NameToModuleSynthesizer { moduleFindModuleMethod = ReflectionUtil.lookupMethod(Module.class, "findModule", String.class, Map.class, Map.class, List.class); } - private Field findFieldByName(Field[] fields, String name) { + private static Field findFieldByName(Field[] fields, String name) { return Arrays.stream(fields).filter(f -> f.getName().equals(name)).findAny().get(); } /** - * This method creates Module instances that will populate the runtime boot module layer of the image. - * This implementation is copy-pasted from Module#defineModules(Configuration, Function, ModuleLayer) - * with few simplifications (removing multiple classloader support) and removal of VM state updates - * (otherwise we would be re-defining modules to the host VM). + * This method creates Module instances that will populate the runtime boot module layer of + * the image. This implementation is copy-pasted from Module#defineModules(Configuration, + * Function, ModuleLayer) with few simplifications (removing multiple classloader support) + * and removal of VM state updates (otherwise we would be re-defining modules to the host + * VM). */ Map synthesizeNameToModule(ModuleLayer runtimeBootLayer, ClassLoader cl) - throws IllegalAccessException, InvocationTargetException, InstantiationException { + throws IllegalAccessException, InvocationTargetException, InstantiationException { Configuration cf = runtimeBootLayer.configuration(); int cap = (int) (cf.modules().size() / 0.75f + 1.0f); Map nameToModule = new HashMap<>(cap); /* - * Remove mapping of modules to classloaders. - * Create module instances without defining them to the VM + * Remove mapping of modules to classloaders. Create module instances without defining + * them to the VM */ for (ResolvedModule resolvedModule : cf.modules()) { ModuleReference mref = resolvedModule.reference(); @@ -286,8 +287,8 @@ Map synthesizeNameToModule(ModuleLayer runtimeBootLayer, ClassLo } /* - * Setup readability and exports/opens. - * This part is unchanged, save for field setters and VM update removals + * Setup readability and exports/opens. This part is unchanged, save for field setters + * and VM update removals */ for (ResolvedModule resolvedModule : cf.modules()) { ModuleReference mref = resolvedModule.reference();