From a1eb5d55fc91fdcc7d24d2a5232a01a873db89a4 Mon Sep 17 00:00:00 2001 From: Ivan Ristovic Date: Sat, 30 Nov 2024 16:55:54 +0100 Subject: [PATCH 1/3] Update runtime CLV maps --- .../svm/core/jdk/JavaLangSubstitutions.java | 22 +-- .../jdk/RuntimeClassLoaderValueSupport.java | 185 ++++++++++++++++++ .../jdk/Target_java_lang_ModuleLayer.java | 17 +- ...t_jdk_internal_module_ServicesCatalog.java | 47 +++++ .../oracle/svm/hosted/ModuleLayerFeature.java | 30 ++- 5 files changed, 277 insertions(+), 24 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java index ed92c22bf7b2..e748403a552d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java @@ -33,9 +33,7 @@ import java.io.PrintStream; import java.net.URL; import java.security.Permission; -import java.util.Arrays; import java.util.Enumeration; -import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; @@ -81,7 +79,6 @@ import jdk.graal.compiler.replacements.nodes.UnaryMathIntrinsicNode; import jdk.graal.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation; import jdk.internal.loader.ClassLoaderValue; -import jdk.internal.module.ServicesCatalog; @TargetClass(java.lang.Object.class) @SuppressWarnings("static-method") @@ -725,24 +722,7 @@ public Object transform(Object receiver, Object originalValue) { if (originalValue == null) { return null; } - - ConcurrentHashMap original = (ConcurrentHashMap) originalValue; - List> clvs = Arrays.asList( - ReflectionUtil.readField(ServicesCatalog.class, "CLV", null), - ReflectionUtil.readField(ModuleLayer.class, "CLV", null)); - - var res = new ConcurrentHashMap<>(); - for (ClassLoaderValue clv : clvs) { - if (clv == null) { - throw VMError.shouldNotReachHere("Field must not be null. Please check what changed in the JDK."); - } - var catalog = original.get(clv); - if (catalog != null) { - res.put(clv, catalog); - } - } - - return res; + return RuntimeClassLoaderValueSupport.instance().getClassLoaderValueMapForLoader((ClassLoader) receiver); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java new file mode 100644 index 000000000000..9f6ec1e966cb --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.jdk; + +import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; +import com.oracle.svm.core.heap.UnknownObjectField; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.util.ReflectionUtil; +import jdk.internal.loader.ClassLoaderValue; +import jdk.internal.loader.ClassLoaders; +import jdk.internal.module.ServicesCatalog; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; + +/** + *

+ * Runtime support for {@link ClassLoaderValue} (CLV) mappings in the JDK. + * {@link jdk.internal.loader.BootLoader} has a static CLV, whereas every {@link ClassLoader} + * instance has its own {@link ClassLoaderValue} map. Most CLV mappings are cleared for runtime, + * except for {@link ServicesCatalog} and {@link ModuleLayer} CLV mappings. + *

+ *

+ * As these mappings contain {@link ModuleLayer} (and through it, also {@link Module}) references, + * it is necessary that we do not capture hosted {@link Module} and {@link ModuleLayer} instances. + * Since {@link ModuleLayer} synthesis occurs after analysis, this singleton is only populated after + * analysis (hence the {@link UnknownObjectField} annotation). The fields in this singleton are used + * inside {@link org.graalvm.nativeimage.hosted.FieldValueTransformer}s for CLV mappings (see + * {@link ClassLoaderValueMapFieldValueTransformer}, {@link ModuleLayerCLVTransformer} and + * {@link ServicesCatalogCLVTransformer}). + *

+ */ +@AutomaticallyRegisteredImageSingleton +public final class RuntimeClassLoaderValueSupport { + + public static RuntimeClassLoaderValueSupport instance() { + return ImageSingletons.lookup(RuntimeClassLoaderValueSupport.class); + } + + @UnknownObjectField(availability = AfterHostedUniverse.class) // + private ConcurrentHashMap bootLoaderCLV = new ConcurrentHashMap<>(); + + @UnknownObjectField(availability = AfterHostedUniverse.class) // + private ConcurrentHashMap> classLoaderValueMaps = new ConcurrentHashMap<>(); + + @UnknownObjectField(availability = AfterHostedUniverse.class) // + ClassLoaderValue servicesCatalogCLV = new ClassLoaderValue<>(); + + @UnknownObjectField(availability = AfterHostedUniverse.class) // + ClassLoaderValue> moduleLayerCLV = new ClassLoaderValue<>(); + + @Platforms(Platform.HOSTED_ONLY.class) // + public void update(ClassLoader imageClassLoader, List runtimeModuleLayers) { + for (ModuleLayer runtimeLayer : runtimeModuleLayers) { + Set loaders = runtimeLayer.modules().stream() + .map(Module::getClassLoader) + .collect(Collectors.toSet()); + for (ClassLoader loader : loaders) { + bindRuntimeModuleLayerToLoader(runtimeLayer, loader); + registerServicesCatalog(loader); + } + } + } + + @Platforms(Platform.HOSTED_ONLY.class) // + private ClassLoader hostedBootLoader; + + @Platforms(Platform.HOSTED_ONLY.class) // + ConcurrentHashMap getClassLoaderValueMapForLoader(ClassLoader loader) { + if (loader == null) { + return bootLoaderCLV; + } else { + return classLoaderValueMaps.computeIfAbsent(loader, k -> new ConcurrentHashMap<>()); + } + } + + @Platforms(Platform.HOSTED_ONLY.class) + private ClassLoader resolveClassLoader(ClassLoader loaderOrNull) { + if (loaderOrNull == null) { + if (hostedBootLoader != null) { + return hostedBootLoader; + } + Method method = ReflectionUtil.lookupMethod(ClassLoaders.class, "bootLoader"); + hostedBootLoader = ReflectionUtil.invokeMethod(method, null); + return hostedBootLoader; + } else { + return loaderOrNull; + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) // + @Platforms(Platform.HOSTED_ONLY.class) // + private void bindRuntimeModuleLayerToLoader(ModuleLayer layer, ClassLoader loaderOrNull) { + ClassLoader loader = resolveClassLoader(loaderOrNull); + + /** + * Runtime {@link ModuleLayer}s are synthesized and bound to loaders after analysis. This + * implementation is identical to {@link java.lang.ModuleLayer.bindToLoader}. + */ + List list = moduleLayerCLV.get(loader); + if (list == null) { + list = new CopyOnWriteArrayList<>(); + List previous = moduleLayerCLV.putIfAbsent(loader, list); + if (previous != null) { + list = previous; + } + } + list.add(layer); + + /* + * Register the module layer in the appropriate CLV for the given loader. + */ + var classLoaderValueMap = getClassLoaderValueMapForLoader(loader); + ((ConcurrentHashMap) classLoaderValueMap).put(moduleLayerCLV, list); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) // + @Platforms(Platform.HOSTED_ONLY.class) // + private void registerServicesCatalog(ClassLoader loaderOrNull) { + ClassLoader loader = resolveClassLoader(loaderOrNull); + + /* + * Register the catalog in the ServicesCatalog CLV. + */ + ServicesCatalog servicesCatalog = getHostedServiceCatalogForLoader(loader); + if (servicesCatalog == null) { + servicesCatalog = ServicesCatalog.create(); + } + servicesCatalogCLV.putIfAbsent(loader, servicesCatalog); + + /* + * Register the module layer in the appropriate CLV for the given loader. + */ + var classLoaderValueMap = getClassLoaderValueMapForLoader(loader); + ((ConcurrentHashMap) classLoaderValueMap).put(servicesCatalogCLV, servicesCatalog); + } + + @Platforms(Platform.HOSTED_ONLY.class) // + private final Field classLoaderCLVField = ReflectionUtil.lookupField(ClassLoader.class, "classLoaderValueMap"); + + @Platforms(Platform.HOSTED_ONLY.class) // + private final ClassLoaderValue hostedServicesCatalogCLV = ReflectionUtil.readField(ServicesCatalog.class, "CLV", null); + + @Platforms(Platform.HOSTED_ONLY.class) // + private ServicesCatalog getHostedServiceCatalogForLoader(ClassLoader loader) { + try { + ConcurrentHashMap hostedLoaderCLV = (ConcurrentHashMap) classLoaderCLVField.get(loader); + ServicesCatalog servicesCatalog = (ServicesCatalog) hostedLoaderCLV.get(hostedServicesCatalogCLV); + return servicesCatalog; + } catch (IllegalAccessException e) { + throw VMError.shouldNotReachHere("Failed to query ClassLoader.classLoaderValueMap", e); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ModuleLayer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ModuleLayer.java index 25d1e4dff325..b1144eaef57b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ModuleLayer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ModuleLayer.java @@ -24,16 +24,31 @@ */ package com.oracle.svm.core.jdk; +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import jdk.internal.loader.ClassLoaderValue; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; + +import java.util.List; @SuppressWarnings("unused") @TargetClass(value = java.lang.ModuleLayer.class) final class Target_java_lang_ModuleLayer { - @SuppressWarnings("unused") @Substitute public static ModuleLayer boot() { return RuntimeModuleSupport.instance().getBootLayer(); } + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ModuleLayerCLVTransformer.class, isFinal = true) // + static ClassLoaderValue> CLV; +} + +final class ModuleLayerCLVTransformer implements FieldValueTransformer { + @Override + public Object transform(Object receiver, Object originalValue) { + return originalValue != null ? RuntimeClassLoaderValueSupport.instance().moduleLayerCLV : null; + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java new file mode 100644 index 000000000000..bc4cec8d4c92 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_module_ServicesCatalog.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.jdk; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.TargetClass; +import jdk.internal.loader.ClassLoaderValue; +import jdk.internal.module.ServicesCatalog; +import org.graalvm.nativeimage.hosted.FieldValueTransformer; + +@SuppressWarnings("unused") +@TargetClass(value = ServicesCatalog.class) +final class Target_jdk_internal_module_ServicesCatalog { + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ServicesCatalogCLVTransformer.class, isFinal = true) // + static ClassLoaderValue CLV; +} + +final class ServicesCatalogCLVTransformer implements FieldValueTransformer { + @Override + public Object transform(Object receiver, Object originalValue) { + return originalValue != null ? RuntimeClassLoaderValueSupport.instance().servicesCatalogCLV : null; + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 4ab506a555df..3af202871488 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -56,6 +56,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.oracle.svm.core.jdk.RuntimeClassLoaderValueSupport; +import jdk.internal.module.ServicesCatalog; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -254,6 +256,7 @@ public void afterAnalysis(AfterAnalysisAccess access) { List runtimeModuleLayers = synthesizeRuntimeModuleLayers(accessImpl, reachableModuleLayers, runtimeImageNamedModules, analysisReachableSyntheticModules, rootModules); ModuleLayer runtimeBootLayer = runtimeModuleLayers.getFirst(); RuntimeModuleSupport.instance().setBootLayer(runtimeBootLayer); + RuntimeClassLoaderValueSupport.instance().update(accessImpl.imageClassLoader.getClassLoader(), runtimeModuleLayers); /* * Ensure that runtime modules have the same relations (i.e., reads, opens and exports) as @@ -451,11 +454,18 @@ private ModuleLayer synthesizeRuntimeModuleLayer(List parentLayers, runtimeModuleLayer = moduleLayerFeatureUtils.createNewModuleLayerInstance(runtimeModuleLayerConfiguration); Map nameToModule = moduleLayerFeatureUtils.synthesizeNameToModule(accessImpl, runtimeModuleLayer, clf); for (Module syntheticModule : syntheticModules) { + /* + * Ignore proxy modules where loader is imageClassLoader. + */ + if (syntheticModule.getClassLoader() == cl.getClassLoader()) { + continue; + } Module runtimeSyntheticModule = moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(syntheticModule, accessImpl); nameToModule.putIfAbsent(runtimeSyntheticModule.getName(), runtimeSyntheticModule); moduleLayerFeatureUtils.patchModuleLayerField(accessImpl, runtimeSyntheticModule, runtimeModuleLayer); } - patchRuntimeModuleLayer(accessImpl, runtimeModuleLayer, nameToModule, parentLayers); + ServicesCatalog servicesCatalog = synthesizeRuntimeModuleLayerServicesCatalog(nameToModule); + patchRuntimeModuleLayer(accessImpl, runtimeModuleLayer, nameToModule, parentLayers, servicesCatalog); accessImpl.rescanField(runtimeModuleLayer, moduleLayerFeatureUtils.moduleLayerModulesField); return runtimeModuleLayer; } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) { @@ -463,6 +473,14 @@ private ModuleLayer synthesizeRuntimeModuleLayer(List parentLayers, } } + private static ServicesCatalog synthesizeRuntimeModuleLayerServicesCatalog(Map nameToModule) { + ServicesCatalog servicesCatalog = ServicesCatalog.create(); + for (Module m : nameToModule.values()) { + servicesCatalog.register(m); + } + return servicesCatalog; + } + private void replicateVisibilityModifications(ModuleLayer runtimeBootLayer, AfterAnalysisAccessImpl accessImpl, ImageClassLoader cl, Set analysisReachableNamedModules, Set analysisReachableUnnamedModules) { List applicationModules = findApplicationModules(runtimeBootLayer, cl.applicationModulePath()); @@ -590,10 +608,11 @@ private static Configuration synthesizeRuntimeModuleLayerConfiguration(ModuleFin } } - private void patchRuntimeModuleLayer(AnalysisAccessBase accessImpl, ModuleLayer runtimeModuleLayer, Map nameToModule, List parents) { + private void patchRuntimeModuleLayer(AnalysisAccessBase accessImpl, ModuleLayer runtimeModuleLayer, Map nameToModule, List parents, ServicesCatalog servicesCatalog) { try { moduleLayerFeatureUtils.patchModuleLayerNameToModuleField(accessImpl, runtimeModuleLayer, nameToModule); moduleLayerFeatureUtils.patchModuleLayerParentsField(accessImpl, runtimeModuleLayer, parents); + moduleLayerFeatureUtils.patchModuleLayerServicesCatalogField(accessImpl, runtimeModuleLayer, servicesCatalog); } catch (IllegalAccessException ex) { throw VMError.shouldNotReachHere("Failed to patch the runtime boot module layer.", ex); } @@ -629,6 +648,7 @@ private static final class ModuleLayerFeatureUtils { private final Constructor moduleLayerConstructor; private final Field moduleLayerNameToModuleField; private final Field moduleLayerParentsField; + private final Field moduleLayerServicesCatalogField; private final Field moduleLayerModulesField; private final Field moduleReferenceLocationField; private final Field moduleReferenceImplLocationField; @@ -692,6 +712,7 @@ private static final class ModuleLayerFeatureUtils { moduleLayerConstructor = ReflectionUtil.lookupConstructor(ModuleLayer.class, Configuration.class, List.class, Function.class); moduleLayerNameToModuleField = ReflectionUtil.lookupField(ModuleLayer.class, "nameToModule"); moduleLayerParentsField = ReflectionUtil.lookupField(ModuleLayer.class, "parents"); + moduleLayerServicesCatalogField = ReflectionUtil.lookupField(ModuleLayer.class, "servicesCatalog"); moduleLayerModulesField = ReflectionUtil.lookupField(ModuleLayer.class, "modules"); moduleReferenceLocationField = ReflectionUtil.lookupField(ModuleReference.class, "location"); moduleReferenceImplLocationField = ReflectionUtil.lookupField(ModuleReferenceImpl.class, "location"); @@ -1078,6 +1099,11 @@ void patchModuleLayerParentsField(AnalysisAccessBase accessImpl, ModuleLayer mod accessImpl.rescanField(moduleLayer, moduleLayerParentsField); } + void patchModuleLayerServicesCatalogField(AnalysisAccessBase accessImpl, ModuleLayer moduleLayer, ServicesCatalog servicesCatalog) throws IllegalAccessException { + moduleLayerServicesCatalogField.set(moduleLayer, servicesCatalog); + accessImpl.rescanField(moduleLayer, moduleLayerServicesCatalogField); + } + ClassLoader getClassLoaderForBootLayerModule(String name) { Optional module = ModuleLayer.boot().findModule(name); assert module.isPresent(); From 21a6dcae2d8b4d24d118a3ca0701fd36433c9892 Mon Sep 17 00:00:00 2001 From: Ivan Ristovic Date: Wed, 4 Dec 2024 13:36:30 +0100 Subject: [PATCH 2/3] Ensure builder class loader is patched in runtime modules --- .../jdk/RuntimeClassLoaderValueSupport.java | 2 +- .../oracle/svm/hosted/ModuleLayerFeature.java | 37 +++++++++++++------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java index 9f6ec1e966cb..94e692509fa6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RuntimeClassLoaderValueSupport.java @@ -81,7 +81,7 @@ public static RuntimeClassLoaderValueSupport instance() { ClassLoaderValue> moduleLayerCLV = new ClassLoaderValue<>(); @Platforms(Platform.HOSTED_ONLY.class) // - public void update(ClassLoader imageClassLoader, List runtimeModuleLayers) { + public void update(List runtimeModuleLayers) { for (ModuleLayer runtimeLayer : runtimeModuleLayers) { Set loaders = runtimeLayer.modules().stream() .map(Module::getClassLoader) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 3af202871488..652acdedf0c6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -56,8 +56,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.oracle.svm.core.jdk.RuntimeClassLoaderValueSupport; -import jdk.internal.module.ServicesCatalog; +import jdk.internal.loader.ClassLoaders; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -72,6 +71,7 @@ import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.jdk.Resources; +import com.oracle.svm.core.jdk.RuntimeClassLoaderValueSupport; import com.oracle.svm.core.jdk.RuntimeModuleSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl; @@ -84,6 +84,7 @@ import jdk.internal.module.DefaultRoots; import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.ModuleReferenceImpl; +import jdk.internal.module.ServicesCatalog; import jdk.internal.module.SystemModuleFinders; /** @@ -226,9 +227,15 @@ public void afterAnalysis(AfterAnalysisAccess access) { runtimeImageNamedModules.add((Module) module.get()); }); + /* + * We need to include synthetic modules in the runtime module system. Some modules, such as + * jdk.proxy, are created for all class loaders, so we need to make sure to not include + * those made for the builder class loader. + */ Set analysisReachableSyntheticModules = runtimeImageNamedModules .stream() .filter(ModuleLayerFeatureUtils::isModuleSynthetic) + .filter(m -> m.getClassLoader() != accessImpl.imageClassLoader.getClassLoader()) .collect(Collectors.toSet()); /* @@ -256,7 +263,7 @@ public void afterAnalysis(AfterAnalysisAccess access) { List runtimeModuleLayers = synthesizeRuntimeModuleLayers(accessImpl, reachableModuleLayers, runtimeImageNamedModules, analysisReachableSyntheticModules, rootModules); ModuleLayer runtimeBootLayer = runtimeModuleLayers.getFirst(); RuntimeModuleSupport.instance().setBootLayer(runtimeBootLayer); - RuntimeClassLoaderValueSupport.instance().update(accessImpl.imageClassLoader.getClassLoader(), runtimeModuleLayers); + RuntimeClassLoaderValueSupport.instance().update(runtimeModuleLayers); /* * Ensure that runtime modules have the same relations (i.e., reads, opens and exports) as @@ -454,12 +461,6 @@ private ModuleLayer synthesizeRuntimeModuleLayer(List parentLayers, runtimeModuleLayer = moduleLayerFeatureUtils.createNewModuleLayerInstance(runtimeModuleLayerConfiguration); Map nameToModule = moduleLayerFeatureUtils.synthesizeNameToModule(accessImpl, runtimeModuleLayer, clf); for (Module syntheticModule : syntheticModules) { - /* - * Ignore proxy modules where loader is imageClassLoader. - */ - if (syntheticModule.getClassLoader() == cl.getClassLoader()) { - continue; - } Module runtimeSyntheticModule = moduleLayerFeatureUtils.getOrCreateRuntimeModuleForHostedModule(syntheticModule, accessImpl); nameToModule.putIfAbsent(runtimeSyntheticModule.getName(), runtimeSyntheticModule); moduleLayerFeatureUtils.patchModuleLayerField(accessImpl, runtimeSyntheticModule, runtimeModuleLayer); @@ -807,11 +808,24 @@ public ModuleFinder getAppModuleFinder() { } } + private ClassLoader getRuntimeLoaderFor(ClassLoader hostedLoader) { + /* + * Make sure to replace builder class loader with the application class loader. This is + * the case, for example, for library support modules. + */ + if (hostedLoader == imageClassLoader.getClassLoader()) { + return ClassLoaders.appClassLoader(); + } else { + return hostedLoader; + } + } + public Module getRuntimeModuleForHostedModule(Module hostedModule, boolean optional) { return getRuntimeModuleForHostedModule(hostedModule.getClassLoader(), hostedModule.getName(), optional); } - public Module getRuntimeModuleForHostedModule(ClassLoader loader, String hostedModuleName, boolean optional) { + public Module getRuntimeModuleForHostedModule(ClassLoader hostedLoader, String hostedModuleName, boolean optional) { + ClassLoader loader = getRuntimeLoaderFor(hostedLoader); Map loaderRuntimeModules = runtimeModules.get(loader); if (loaderRuntimeModules == null) { if (optional) { @@ -847,8 +861,9 @@ public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule, Analy } } - public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader loader, String hostedModuleName, ModuleDescriptor runtimeModuleDescriptor, AnalysisAccessBase access, + public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader hostedLoader, String hostedModuleName, ModuleDescriptor runtimeModuleDescriptor, AnalysisAccessBase access, boolean enableNativeAccess) { + ClassLoader loader = getRuntimeLoaderFor(hostedLoader); synchronized (runtimeModules) { Module runtimeModule = getRuntimeModuleForHostedModule(loader, hostedModuleName, true); if (runtimeModule != null) { From 45880cee9cff45c85f28e232bb338ce452fcdd5a Mon Sep 17 00:00:00 2001 From: Ivan Ristovic Date: Wed, 4 Dec 2024 22:01:09 +0100 Subject: [PATCH 3/3] Use HostedSubstrateUtil for runtime classloader resolving --- .../oracle/svm/hosted/ModuleLayerFeature.java | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index 652acdedf0c6..ee22e75c7e62 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.hosted; -import static com.oracle.svm.core.util.VMError.shouldNotReachHereAtRuntime; - import java.lang.module.Configuration; import java.lang.module.FindException; import java.lang.module.ModuleDescriptor; @@ -56,7 +54,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.internal.loader.ClassLoaders; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -73,6 +70,7 @@ import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.RuntimeClassLoaderValueSupport; import com.oracle.svm.core.jdk.RuntimeModuleSupport; +import com.oracle.svm.core.util.HostedSubstrateUtil; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl.AfterAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.AnalysisAccessBase; @@ -87,6 +85,8 @@ import jdk.internal.module.ServicesCatalog; import jdk.internal.module.SystemModuleFinders; +import static com.oracle.svm.core.util.VMError.shouldNotReachHereAtRuntime; + /** * This feature: *
    @@ -808,24 +808,12 @@ public ModuleFinder getAppModuleFinder() { } } - private ClassLoader getRuntimeLoaderFor(ClassLoader hostedLoader) { - /* - * Make sure to replace builder class loader with the application class loader. This is - * the case, for example, for library support modules. - */ - if (hostedLoader == imageClassLoader.getClassLoader()) { - return ClassLoaders.appClassLoader(); - } else { - return hostedLoader; - } - } - public Module getRuntimeModuleForHostedModule(Module hostedModule, boolean optional) { return getRuntimeModuleForHostedModule(hostedModule.getClassLoader(), hostedModule.getName(), optional); } public Module getRuntimeModuleForHostedModule(ClassLoader hostedLoader, String hostedModuleName, boolean optional) { - ClassLoader loader = getRuntimeLoaderFor(hostedLoader); + ClassLoader loader = HostedSubstrateUtil.getRuntimeClassLoader(hostedLoader); Map loaderRuntimeModules = runtimeModules.get(loader); if (loaderRuntimeModules == null) { if (optional) { @@ -863,7 +851,7 @@ public Module getOrCreateRuntimeModuleForHostedModule(Module hostedModule, Analy public Module getOrCreateRuntimeModuleForHostedModule(ClassLoader hostedLoader, String hostedModuleName, ModuleDescriptor runtimeModuleDescriptor, AnalysisAccessBase access, boolean enableNativeAccess) { - ClassLoader loader = getRuntimeLoaderFor(hostedLoader); + ClassLoader loader = HostedSubstrateUtil.getRuntimeClassLoader(hostedLoader); synchronized (runtimeModules) { Module runtimeModule = getRuntimeModuleForHostedModule(loader, hostedModuleName, true); if (runtimeModule != null) {