Skip to content

Commit 6b70235

Browse files
committed
Update runtime CLV maps
1 parent 320d02e commit 6b70235

File tree

5 files changed

+277
-24
lines changed

5 files changed

+277
-24
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@
3333
import java.io.PrintStream;
3434
import java.net.URL;
3535
import java.security.Permission;
36-
import java.util.Arrays;
3736
import java.util.Enumeration;
38-
import java.util.List;
3937
import java.util.Map;
4038
import java.util.Properties;
4139
import java.util.concurrent.ConcurrentHashMap;
@@ -81,7 +79,6 @@
8179
import jdk.graal.compiler.replacements.nodes.UnaryMathIntrinsicNode;
8280
import jdk.graal.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation;
8381
import jdk.internal.loader.ClassLoaderValue;
84-
import jdk.internal.module.ServicesCatalog;
8582

8683
@TargetClass(java.lang.Object.class)
8784
@SuppressWarnings("static-method")
@@ -724,24 +721,7 @@ public Object transform(Object receiver, Object originalValue) {
724721
if (originalValue == null) {
725722
return null;
726723
}
727-
728-
ConcurrentHashMap<?, ?> original = (ConcurrentHashMap<?, ?>) originalValue;
729-
List<ClassLoaderValue<?>> clvs = Arrays.asList(
730-
ReflectionUtil.readField(ServicesCatalog.class, "CLV", null),
731-
ReflectionUtil.readField(ModuleLayer.class, "CLV", null));
732-
733-
var res = new ConcurrentHashMap<>();
734-
for (ClassLoaderValue<?> clv : clvs) {
735-
if (clv == null) {
736-
throw VMError.shouldNotReachHere("Field must not be null. Please check what changed in the JDK.");
737-
}
738-
var catalog = original.get(clv);
739-
if (catalog != null) {
740-
res.put(clv, catalog);
741-
}
742-
}
743-
744-
return res;
724+
return RuntimeClassLoaderValueSupport.instance().getClassLoaderValueMapForLoader((ClassLoader) receiver);
745725
}
746726
}
747727

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk;
26+
27+
import com.oracle.svm.core.BuildPhaseProvider.AfterHostedUniverse;
28+
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
29+
import com.oracle.svm.core.heap.UnknownObjectField;
30+
import com.oracle.svm.core.util.VMError;
31+
import com.oracle.svm.util.ReflectionUtil;
32+
import jdk.internal.loader.ClassLoaderValue;
33+
import jdk.internal.loader.ClassLoaders;
34+
import jdk.internal.module.ServicesCatalog;
35+
import org.graalvm.nativeimage.ImageSingletons;
36+
import org.graalvm.nativeimage.Platform;
37+
import org.graalvm.nativeimage.Platforms;
38+
39+
import java.lang.reflect.Field;
40+
import java.lang.reflect.Method;
41+
import java.util.List;
42+
import java.util.Set;
43+
import java.util.concurrent.ConcurrentHashMap;
44+
import java.util.concurrent.CopyOnWriteArrayList;
45+
import java.util.stream.Collectors;
46+
47+
/**
48+
* <p>
49+
* Runtime support for {@link ClassLoaderValue} (CLV) mappings in the JDK.
50+
* {@link jdk.internal.loader.BootLoader} has a static CLV, whereas every {@link ClassLoader}
51+
* instance has its own {@link ClassLoaderValue} map. Most CLV mappings are cleared for runtime,
52+
* except for {@link ServicesCatalog} and {@link ModuleLayer} CLV mappings.
53+
* </p>
54+
* <p>
55+
* As these mappings contain {@link ModuleLayer} (and through it, also {@link Module}) references,
56+
* it is necessary that we do not capture hosted {@link Module} and {@link ModuleLayer} instances.
57+
* Since {@link ModuleLayer} synthesis occurs after analysis, this singleton is only populated after
58+
* analysis (hence the {@link UnknownObjectField} annotation). The fields in this singleton are used
59+
* inside {@link org.graalvm.nativeimage.hosted.FieldValueTransformer}s for CLV mappings (see
60+
* {@link ClassLoaderValueMapFieldValueTransformer}, {@link ModuleLayerCLVTransformer} and
61+
* {@link ServicesCatalogCLVTransformer}).
62+
* </p>
63+
*/
64+
@AutomaticallyRegisteredImageSingleton
65+
public final class RuntimeClassLoaderValueSupport {
66+
67+
public static RuntimeClassLoaderValueSupport instance() {
68+
return ImageSingletons.lookup(RuntimeClassLoaderValueSupport.class);
69+
}
70+
71+
@UnknownObjectField(availability = AfterHostedUniverse.class) //
72+
private ConcurrentHashMap<?, ?> bootLoaderCLV = new ConcurrentHashMap<>();
73+
74+
@UnknownObjectField(availability = AfterHostedUniverse.class) //
75+
private ConcurrentHashMap<ClassLoader, ConcurrentHashMap<?, ?>> classLoaderValueMaps = new ConcurrentHashMap<>();
76+
77+
@UnknownObjectField(availability = AfterHostedUniverse.class) //
78+
ClassLoaderValue<ServicesCatalog> servicesCatalogCLV = new ClassLoaderValue<>();
79+
80+
@UnknownObjectField(availability = AfterHostedUniverse.class) //
81+
ClassLoaderValue<List<ModuleLayer>> moduleLayerCLV = new ClassLoaderValue<>();
82+
83+
@Platforms(Platform.HOSTED_ONLY.class) //
84+
public void update(ClassLoader imageClassLoader, List<ModuleLayer> runtimeModuleLayers) {
85+
for (ModuleLayer runtimeLayer : runtimeModuleLayers) {
86+
Set<ClassLoader> loaders = runtimeLayer.modules().stream()
87+
.map(Module::getClassLoader)
88+
.collect(Collectors.toSet());
89+
for (ClassLoader loader : loaders) {
90+
bindRuntimeModuleLayerToLoader(runtimeLayer, loader);
91+
registerServicesCatalog(loader);
92+
}
93+
}
94+
}
95+
96+
@Platforms(Platform.HOSTED_ONLY.class) //
97+
private ClassLoader hostedBootLoader;
98+
99+
@Platforms(Platform.HOSTED_ONLY.class) //
100+
ConcurrentHashMap<?, ?> getClassLoaderValueMapForLoader(ClassLoader loader) {
101+
if (loader == null) {
102+
return bootLoaderCLV;
103+
} else {
104+
return classLoaderValueMaps.computeIfAbsent(loader, k -> new ConcurrentHashMap<>());
105+
}
106+
}
107+
108+
@Platforms(Platform.HOSTED_ONLY.class)
109+
private ClassLoader resolveClassLoader(ClassLoader loaderOrNull) {
110+
if (loaderOrNull == null) {
111+
if (hostedBootLoader != null) {
112+
return hostedBootLoader;
113+
}
114+
Method method = ReflectionUtil.lookupMethod(ClassLoaders.class, "bootLoader");
115+
hostedBootLoader = ReflectionUtil.invokeMethod(method, null);
116+
return hostedBootLoader;
117+
} else {
118+
return loaderOrNull;
119+
}
120+
}
121+
122+
@SuppressWarnings({"unchecked", "rawtypes"}) //
123+
@Platforms(Platform.HOSTED_ONLY.class) //
124+
private void bindRuntimeModuleLayerToLoader(ModuleLayer layer, ClassLoader loaderOrNull) {
125+
ClassLoader loader = resolveClassLoader(loaderOrNull);
126+
127+
/**
128+
* Runtime {@link ModuleLayer}s are synthesized and bound to loaders after analysis. This
129+
* implementation is identical to {@link java.lang.ModuleLayer.bindToLoader}.
130+
*/
131+
List<ModuleLayer> list = moduleLayerCLV.get(loader);
132+
if (list == null) {
133+
list = new CopyOnWriteArrayList<>();
134+
List<ModuleLayer> previous = moduleLayerCLV.putIfAbsent(loader, list);
135+
if (previous != null) {
136+
list = previous;
137+
}
138+
}
139+
list.add(layer);
140+
141+
/*
142+
* Register the module layer in the appropriate CLV for the given loader.
143+
*/
144+
var classLoaderValueMap = getClassLoaderValueMapForLoader(loader);
145+
((ConcurrentHashMap) classLoaderValueMap).put(moduleLayerCLV, list);
146+
}
147+
148+
@SuppressWarnings({"unchecked", "rawtypes"}) //
149+
@Platforms(Platform.HOSTED_ONLY.class) //
150+
private void registerServicesCatalog(ClassLoader loaderOrNull) {
151+
ClassLoader loader = resolveClassLoader(loaderOrNull);
152+
153+
/*
154+
* Register the catalog in the ServicesCatalog CLV.
155+
*/
156+
ServicesCatalog servicesCatalog = getHostedServiceCatalogForLoader(loader);
157+
if (servicesCatalog == null) {
158+
servicesCatalog = ServicesCatalog.create();
159+
}
160+
servicesCatalogCLV.putIfAbsent(loader, servicesCatalog);
161+
162+
/*
163+
* Register the module layer in the appropriate CLV for the given loader.
164+
*/
165+
var classLoaderValueMap = getClassLoaderValueMapForLoader(loader);
166+
((ConcurrentHashMap) classLoaderValueMap).put(servicesCatalogCLV, servicesCatalog);
167+
}
168+
169+
@Platforms(Platform.HOSTED_ONLY.class) //
170+
private final Field classLoaderCLVField = ReflectionUtil.lookupField(ClassLoader.class, "classLoaderValueMap");
171+
172+
@Platforms(Platform.HOSTED_ONLY.class) //
173+
private final ClassLoaderValue<ServicesCatalog> hostedServicesCatalogCLV = ReflectionUtil.readField(ServicesCatalog.class, "CLV", null);
174+
175+
@Platforms(Platform.HOSTED_ONLY.class) //
176+
private ServicesCatalog getHostedServiceCatalogForLoader(ClassLoader loader) {
177+
try {
178+
ConcurrentHashMap<?, ?> hostedLoaderCLV = (ConcurrentHashMap<?, ?>) classLoaderCLVField.get(loader);
179+
ServicesCatalog servicesCatalog = (ServicesCatalog) hostedLoaderCLV.get(hostedServicesCatalogCLV);
180+
return servicesCatalog;
181+
} catch (IllegalAccessException e) {
182+
throw VMError.shouldNotReachHere("Failed to query ClassLoader.classLoaderValueMap", e);
183+
}
184+
}
185+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_java_lang_ModuleLayer.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,31 @@
2424
*/
2525
package com.oracle.svm.core.jdk;
2626

27+
import com.oracle.svm.core.annotate.Alias;
28+
import com.oracle.svm.core.annotate.RecomputeFieldValue;
2729
import com.oracle.svm.core.annotate.Substitute;
2830
import com.oracle.svm.core.annotate.TargetClass;
31+
import jdk.internal.loader.ClassLoaderValue;
32+
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
33+
34+
import java.util.List;
2935

3036
@SuppressWarnings("unused")
3137
@TargetClass(value = java.lang.ModuleLayer.class)
3238
final class Target_java_lang_ModuleLayer {
3339

34-
@SuppressWarnings("unused")
3540
@Substitute
3641
public static ModuleLayer boot() {
3742
return RuntimeModuleSupport.instance().getBootLayer();
3843
}
44+
45+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ModuleLayerCLVTransformer.class, isFinal = true) //
46+
static ClassLoaderValue<List<ModuleLayer>> CLV;
47+
}
48+
49+
final class ModuleLayerCLVTransformer implements FieldValueTransformer {
50+
@Override
51+
public Object transform(Object receiver, Object originalValue) {
52+
return originalValue != null ? RuntimeClassLoaderValueSupport.instance().moduleLayerCLV : null;
53+
}
3954
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk;
26+
27+
import com.oracle.svm.core.annotate.Alias;
28+
import com.oracle.svm.core.annotate.RecomputeFieldValue;
29+
import com.oracle.svm.core.annotate.TargetClass;
30+
import jdk.internal.loader.ClassLoaderValue;
31+
import jdk.internal.module.ServicesCatalog;
32+
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
33+
34+
@SuppressWarnings("unused")
35+
@TargetClass(value = ServicesCatalog.class)
36+
final class Target_jdk_internal_module_ServicesCatalog {
37+
38+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ServicesCatalogCLVTransformer.class, isFinal = true) //
39+
static ClassLoaderValue<ServicesCatalog> CLV;
40+
}
41+
42+
final class ServicesCatalogCLVTransformer implements FieldValueTransformer {
43+
@Override
44+
public Object transform(Object receiver, Object originalValue) {
45+
return originalValue != null ? RuntimeClassLoaderValueSupport.instance().servicesCatalogCLV : null;
46+
}
47+
}

0 commit comments

Comments
 (0)