Skip to content

Commit 380851f

Browse files
committed
[GR-41096] Handle jdk.internal.module.ServicesCatalog.CLV correctly
PullRequest: graal/13035
2 parents 1a872f6 + 5687716 commit 380851f

File tree

11 files changed

+164
-100
lines changed

11 files changed

+164
-100
lines changed

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This changelog summarizes major changes to GraalVM Native Image.
1111
* (GR-42148) Adjust build output to report types (primitives, classes, interfaces, and arrays) instead of classes and revise the output schema of `-H:BuildOutputJSONFile`.
1212
* (GR-42375) Add `-H:±GenerateBuildArtifactsFile` option, which generates a `build-artifacts.json` file with a list of all artifacts produced by Native Image. `.build_artifacts.txt` files are now deprecated, disabled (can be re-enabled with env setting `NATIVE_IMAGE_DEPRECATED_BUILD_ARTIFACTS_TXT=true`), and will be removed in a future release.
1313
* (GR-34179) Improved debugging support on Windows: Debug information now includes information about Java types (contributed by Red Hat).
14+
* (GR-41096) Support services loaded through the `java.util.ServiceLoader.ModuleServicesLookupIterator`. An example of such service is the `com.sun.jndi.rmi.registry.RegistryContextFactory`.
1415

1516
## Version 22.3.0
1617
* (GR-35721) Remove old build output style and the `-H:±BuildOutputUseNewStyle` option.

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
import java.net.URL;
3030
import java.net.URLConnection;
3131
import java.nio.file.Path;
32+
import java.util.Arrays;
3233
import java.util.HashMap;
34+
import java.util.HashSet;
3335
import java.util.Map;
3436
import java.util.concurrent.ConcurrentHashMap;
3537
import java.util.function.BooleanSupplier;
@@ -42,6 +44,8 @@
4244
import com.oracle.svm.core.annotate.RecomputeFieldValue;
4345
import com.oracle.svm.core.annotate.Substitute;
4446
import com.oracle.svm.core.annotate.TargetClass;
47+
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
48+
import com.oracle.svm.core.feature.InternalFeature;
4549
import com.oracle.svm.core.jdk.JRTSupport.JRTDisabled;
4650
import com.oracle.svm.core.jdk.JRTSupport.JRTEnabled;
4751
import com.oracle.svm.core.option.HostedOptionKey;
@@ -77,6 +81,21 @@ public boolean getAsBoolean() {
7781
}
7882
}
7983

84+
@AutomaticallyRegisteredFeature
85+
class JRTDisableFeature implements InternalFeature {
86+
87+
@Override
88+
public boolean isInConfiguration(IsInConfigurationAccess access) {
89+
return !JRTSupport.Options.AllowJRTFileSystem.getValue();
90+
}
91+
92+
@SuppressWarnings("unchecked")
93+
@Override
94+
public void beforeAnalysis(BeforeAnalysisAccess access) {
95+
ServiceCatalogSupport.singleton().removeServicesFromServicesCatalog("java.nio.file.spi.FileSystemProvider", new HashSet<>(Arrays.asList("jdk.internal.jrtfs.JrtFileSystemProvider")));
96+
}
97+
}
98+
8099
// region Enable jimage/jrtfs
81100

82101
@TargetClass(className = "jdk.internal.jimage.ImageReader", innerClass = "SharedImageReader", onlyWith = JRTEnabled.class)

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

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import java.io.File;
3232
import java.io.InputStream;
3333
import java.io.PrintStream;
34+
import java.util.Arrays;
35+
import java.util.List;
3436
import java.util.Map;
3537
import java.util.Properties;
3638
import java.util.concurrent.ConcurrentHashMap;
@@ -72,6 +74,10 @@
7274
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
7375
import com.oracle.svm.core.thread.JavaThreads;
7476
import com.oracle.svm.core.util.VMError;
77+
import com.oracle.svm.util.ReflectionUtil;
78+
79+
import jdk.internal.loader.ClassLoaderValue;
80+
import jdk.internal.module.ServicesCatalog;
7581

7682
@TargetClass(java.lang.Object.class)
7783
@SuppressWarnings("static-method")
@@ -720,16 +726,42 @@ private static boolean hasClassPath() {
720726
}
721727

722728
/**
723-
* All ClassLoaderValue are reset at run time for now. See also
724-
* {@link Target_java_lang_ClassLoader#classLoaderValueMap} for resetting of individual class
725-
* loaders.
729+
* Most {@link ClassLoaderValue}s are reset. For the list of preserved transformers see
730+
* {@link ClassLoaderValueMapFieldValueTransformer}.
726731
*/
727732
// Checkstyle: stop
728-
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClass = ConcurrentHashMap.class)//
733+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ClassLoaderValueMapFieldValueTransformer.class, isFinal = true)//
729734
static ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP;
730735
// Checkstyle: resume
731736
}
732737

738+
final class ClassLoaderValueMapFieldValueTransformer implements FieldValueTransformer {
739+
@Override
740+
public Object transform(Object receiver, Object originalValue) {
741+
if (originalValue == null) {
742+
return null;
743+
}
744+
745+
ConcurrentHashMap<?, ?> original = (ConcurrentHashMap<?, ?>) originalValue;
746+
List<ClassLoaderValue<?>> clvs = Arrays.asList(
747+
ReflectionUtil.readField(ServicesCatalog.class, "CLV", null),
748+
ReflectionUtil.readField(ModuleLayer.class, "CLV", null));
749+
750+
var res = new ConcurrentHashMap<>();
751+
for (ClassLoaderValue<?> clv : clvs) {
752+
if (clv == null) {
753+
throw VMError.shouldNotReachHere("Field must not be null. Please check what changed in the JDK.");
754+
}
755+
var catalog = original.get(clv);
756+
if (catalog != null) {
757+
res.put(clv, catalog);
758+
}
759+
}
760+
761+
return res;
762+
}
763+
}
764+
733765
/** Dummy class to have a class with the file's name. */
734766
public final class JavaLangSubstitutions {
735767

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2023, 2023, 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 java.util.List;
28+
import java.util.Set;
29+
import java.util.concurrent.ConcurrentHashMap;
30+
import java.util.stream.Collectors;
31+
32+
import org.graalvm.nativeimage.ImageSingletons;
33+
import org.graalvm.nativeimage.Platform;
34+
import org.graalvm.nativeimage.Platforms;
35+
import org.graalvm.nativeimage.hosted.Feature;
36+
37+
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
38+
import com.oracle.svm.core.util.VMError;
39+
import com.oracle.svm.util.ReflectionUtil;
40+
41+
import jdk.internal.module.ServicesCatalog;
42+
43+
@AutomaticallyRegisteredImageSingleton
44+
@Platforms(Platform.HOSTED_ONLY.class)
45+
public class ServiceCatalogSupport {
46+
final ConcurrentHashMap<String, Set<String>> omittedServiceProviders = new ConcurrentHashMap<>();
47+
boolean sealed;
48+
49+
public static ServiceCatalogSupport singleton() {
50+
return ImageSingletons.lookup(ServiceCatalogSupport.class);
51+
}
52+
53+
public void seal() {
54+
sealed = true;
55+
}
56+
57+
public void removeServicesFromServicesCatalog(String serviceProvider, Set<String> services) {
58+
VMError.guarantee(!sealed,
59+
"Removing services from a catalog is allowed only before analysis. ServiceCatalogSupport.removeServicesFromServicesCatalog called during or after analysis. ");
60+
omittedServiceProviders.put(serviceProvider, services);
61+
}
62+
63+
@SuppressWarnings("unchecked")
64+
public void enableServiceCatalogMapTransformer(Feature.BeforeAnalysisAccess access) {
65+
access.registerFieldValueTransformer(ReflectionUtil.lookupField(ServicesCatalog.class, "map"), (receiver, original) -> {
66+
VMError.guarantee(sealed);
67+
ConcurrentHashMap<String, List<ServicesCatalog.ServiceProvider>> map = (ConcurrentHashMap<String, List<ServicesCatalog.ServiceProvider>>) original;
68+
final ConcurrentHashMap<String, List<ServicesCatalog.ServiceProvider>> res = new ConcurrentHashMap<>();
69+
map.forEach((key, value) -> {
70+
if (omittedServiceProviders.containsKey(key)) {
71+
var omittedServices = omittedServiceProviders.get(key);
72+
List<ServicesCatalog.ServiceProvider> filtered = value.stream()
73+
.filter(v -> !omittedServices.contains(v.providerName()))
74+
.collect(Collectors.toList());
75+
res.put(key, filtered);
76+
} else {
77+
res.put(key, value);
78+
}
79+
});
80+
return res;
81+
});
82+
}
83+
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.concurrent.ConcurrentHashMap;
3737
import java.util.stream.Stream;
3838

39+
import jdk.internal.loader.ClassLoaderValue;
3940
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
4041

4142
import com.oracle.svm.core.SubstrateUtil;
@@ -193,11 +194,10 @@ private Class<?> findLoadedClass0(String name) {
193194
}
194195

195196
/**
196-
* All ClassLoaderValue are reset at run time for now. See also
197-
* {@link Target_jdk_internal_loader_BootLoader#CLASS_LOADER_VALUE_MAP} for resetting of the
198-
* boot class loader.
197+
* Most {@link ClassLoaderValue}s are reset. For the list of preserved transformers see
198+
* {@link ClassLoaderValueMapFieldValueTransformer}.
199199
*/
200-
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClass = ConcurrentHashMap.class)//
200+
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = ClassLoaderValueMapFieldValueTransformer.class)//
201201
volatile ConcurrentHashMap<?, ?> classLoaderValueMap;
202202

203203
/**

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

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,13 @@
2626
package com.oracle.svm.core.jdk;
2727

2828
import java.lang.reflect.Constructor;
29-
import java.net.URL;
3029
import java.security.AccessControlContext;
3130
import java.util.ArrayList;
32-
import java.util.Enumeration;
33-
import java.util.HashSet;
3431
import java.util.List;
35-
import java.util.ServiceConfigurationError;
36-
import java.util.ServiceLoader;
37-
import java.util.Set;
38-
39-
import org.graalvm.compiler.core.common.SuppressFBWarnings;
4032

4133
import com.oracle.svm.core.annotate.Alias;
4234
import com.oracle.svm.core.annotate.RecomputeFieldValue;
43-
import com.oracle.svm.core.annotate.Substitute;
4435
import com.oracle.svm.core.annotate.TargetClass;
45-
import com.oracle.svm.core.annotate.TargetElement;
4636

4737
/**
4838
* Disable the module based iteration in favour of classpath based iteration. See
@@ -64,82 +54,6 @@ final class Target_java_util_ServiceLoader {
6454
private List<?> instantiatedProviders;
6555
}
6656

67-
@TargetClass(value = java.util.ServiceLoader.class, innerClass = "ModuleServicesLookupIterator")
68-
final class Target_java_util_ServiceLoader_ModuleServicesLookupIterator {
69-
@SuppressWarnings("unused")
70-
@Substitute
71-
Target_java_util_ServiceLoader_ModuleServicesLookupIterator(Target_java_util_ServiceLoader outer) {
72-
}
73-
74-
@SuppressWarnings("static-method")
75-
@Substitute
76-
boolean hasNext() {
77-
return false;
78-
}
79-
}
80-
81-
@TargetClass(value = java.util.ServiceLoader.class, innerClass = "LazyClassPathLookupIterator")
82-
final class Target_java_util_ServiceLoader_LazyClassPathLookupIterator {
83-
@Alias//
84-
ServiceLoader.Provider<?> nextProvider;
85-
@Alias//
86-
ServiceConfigurationError nextError;
87-
88-
/* Has to be initialized here, because we're substituting the constructor. */
89-
@SuppressWarnings("unused")//
90-
@Alias//
91-
Set<String> providerNames = new HashSet<>();
92-
93-
/* Manage correct ref to the enclosing object ourselves so that we can access it. */
94-
@Alias//
95-
@TargetElement(name = "this$0")//
96-
Target_java_util_ServiceLoader outer;
97-
98-
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)//
99-
Enumeration<URL> configs;
100-
101-
@Substitute
102-
Target_java_util_ServiceLoader_LazyClassPathLookupIterator(Target_java_util_ServiceLoader outer) {
103-
this.outer = outer;
104-
}
105-
106-
@Alias
107-
private native Class<?> nextProviderClass();
108-
109-
/**
110-
* Modified version of java.util.ServiceLoader.LazyClassPathLookupIterator#hasNextService.
111-
*/
112-
@SuppressFBWarnings(value = "BC_IMPOSSIBLE_CAST", justification = "substitution hides acual type")
113-
@Substitute
114-
private boolean hasNextService() {
115-
while (nextProvider == null && nextError == null) {
116-
try {
117-
Class<?> clazz = nextProviderClass();
118-
if (clazz == null) {
119-
return false;
120-
}
121-
122-
/*-
123-
* if (clazz.getModule().isNamed()) {
124-
* // ignore class if in named module
125-
* continue;
126-
* }
127-
*/
128-
129-
if (outer.service.isAssignableFrom(clazz)) {
130-
Constructor<?> ctor = outer.getConstructor(clazz);
131-
nextProvider = (ServiceLoader.Provider<?>) ((Object) (new Target_java_util_ServiceLoader_ProviderImpl(outer.service, clazz, ctor, outer.acc)));
132-
} else {
133-
Target_java_util_ServiceLoader.fail(outer.service, clazz.getName() + " not a subtype");
134-
}
135-
} catch (ServiceConfigurationError e) {
136-
nextError = e;
137-
}
138-
}
139-
return true;
140-
}
141-
}
142-
14357
@TargetClass(value = java.util.ServiceLoader.class, innerClass = "ProviderImpl")
14458
final class Target_java_util_ServiceLoader_ProviderImpl {
14559

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@
219219
import com.oracle.svm.core.hub.DynamicHub;
220220
import com.oracle.svm.core.hub.LayoutEncoding;
221221
import com.oracle.svm.core.image.ImageHeapLayouter;
222+
import com.oracle.svm.core.jdk.ServiceCatalogSupport;
222223
import com.oracle.svm.core.option.HostedOptionValues;
223224
import com.oracle.svm.core.option.RuntimeOptionValues;
224225
import com.oracle.svm.core.option.SubstrateOptionsParser;
@@ -750,7 +751,9 @@ protected boolean runPointsToAnalysis(String imageName, OptionValues options, De
750751
try (Indent ignored = debug.logAndIndent("run analysis")) {
751752
try (Indent ignored1 = debug.logAndIndent("process analysis initializers")) {
752753
BeforeAnalysisAccessImpl config = new BeforeAnalysisAccessImpl(featureHandler, loader, bb, nativeLibraries, debug);
754+
ServiceCatalogSupport.singleton().enableServiceCatalogMapTransformer(config);
753755
featureHandler.forEachFeature(feature -> feature.beforeAnalysis(config));
756+
ServiceCatalogSupport.singleton().seal();
754757
bb.getHostVM().getClassInitializationSupport().setConfigurationSealed(true);
755758
}
756759

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ServiceLoaderFeature.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,7 @@ public static class Options {
134134
// before because implementation classes were instantiated using runtime reflection instead of
135135
// ServiceLoader (and thus weren't reachable in analysis).
136136

137-
protected final Set<String> serviceProvidersToSkip = new HashSet<>(Arrays.asList(
138-
"com.sun.jndi.rmi.registry.RegistryContextFactory" // GR-26547
139-
));
137+
protected final Set<String> serviceProvidersToSkip = new HashSet<>();
140138

141139
/** Copy of private field {@code ServiceLoader.PREFIX}. */
142140
private static final String LOCATION_PREFIX = "META-INF/services/";
@@ -161,7 +159,6 @@ public boolean isInConfiguration(IsInConfigurationAccess access) {
161159

162160
@Override
163161
public void afterRegistration(AfterRegistrationAccess access) {
164-
// TODO write a more sophisticated include/exclude filter to handle cases like GR-27605 ?
165162
servicesToSkip.addAll(Options.ServiceLoaderFeatureExcludeServices.getValue().values());
166163
serviceProvidersToSkip.addAll(Options.ServiceLoaderFeatureExcludeServiceProviders.getValue().values());
167164
}

substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/resources/jre.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,5 +238,16 @@
238238
"justification": "In the JDK 17 Arrays#sort and Arrays#parallelSort share the same DualPivotQuicksort#sort(array,parallelism,low,high) implementation that uses ForkJoinPool for parallelism > 1. This causes a spurious privileged call being found in the Arrays#sort, so we allow Arrays#sort to avoid this false positive and keep Arrays#parallelSort denied."
239239
}
240240
]
241+
},
242+
{
243+
"name": "java.util.ServiceLoader$ModuleServicesLookupIterator",
244+
"methods": [
245+
{
246+
"name": "loaderFor",
247+
"parameterTypes" : [
248+
"java.lang.Module"
249+
]
250+
}
251+
]
241252
}
242253
]

truffle/mx.truffle/macro-truffle.properties

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Args = -H:Features=com.oracle.svm.truffle.TruffleFeature,com.oracle.svm.truffle.
55
--initialize-at-build-time=com.oracle.truffle \
66
--initialize-at-build-time=org.graalvm.shadowed.org.jcodings \
77
--initialize-at-build-time=com.oracle.truffle.tools.utils.json \
8-
--initialize-at-build-time=org.graalvm.shadowed.org.jline,org.graalvm.shadowed.org.fusesource.jansi
8+
--initialize-at-build-time=org.graalvm.shadowed.org.jline,org.graalvm.shadowed.org.fusesource.jansi \
9+
--initialize-at-run-time=sun.rmi \
10+
--initialize-at-run-time=java.rmi
11+
912
JavaArgs = -Dtruffle.TruffleRuntime=com.oracle.svm.truffle.api.SubstrateTruffleRuntime \
1013
-Dgraalvm.ForcePolyglotInvalid=false

0 commit comments

Comments
 (0)