Skip to content

Commit ea41391

Browse files
committed
[GR-62124] Remove jdk.graal.compiler dependency on org.graalvm.nativeimage.
PullRequest: graal/20036
2 parents 64ee714 + 39ea681 commit ea41391

File tree

70 files changed

+805
-448
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+805
-448
lines changed

compiler/mx.compiler/suite.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@
170170
"dependencies" : [
171171
"sdk:WORD",
172172
"sdk:COLLECTIONS",
173-
"sdk:NATIVEIMAGE",
174173
"truffle:TRUFFLE_COMPILER",
175174
],
176175
"requires" : [
@@ -643,7 +642,6 @@
643642
"distDependencies" : [
644643
"sdk:COLLECTIONS",
645644
"sdk:WORD",
646-
"sdk:NATIVEIMAGE",
647645
"truffle:TRUFFLE_COMPILER",
648646
],
649647
"allowsJavadocWarnings": True,
@@ -680,6 +678,7 @@
680678
"jdk.graal.compiler.libgraal.loader"
681679
],
682680
"distDependencies" : [
681+
"sdk:NATIVEIMAGE",
683682
"sdk:NATIVEIMAGE_LIBGRAAL",
684683
"GRAAL",
685684
],
@@ -699,10 +698,12 @@
699698
"distDependencies": [
700699
"GRAAL",
701700
"GRAAL_MANAGEMENT",
701+
"sdk:NATIVEIMAGE",
702702
"sdk:NATIVEIMAGE_LIBGRAAL",
703+
"sdk:COLLECTIONS",
703704
"sdk:JNIUTILS",
704-
"sdk:NATIVEIMAGE",
705-
"sdk:NATIVEBRIDGE"
705+
"sdk:NATIVEBRIDGE",
706+
"truffle:TRUFFLE_COMPILER"
706707
],
707708
"maven": False,
708709
},

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@
2727
import java.io.IOException;
2828
import java.io.InputStream;
2929
import java.lang.module.ModuleDescriptor;
30+
import java.lang.reflect.AnnotatedElement;
31+
import java.lang.reflect.Constructor;
3032
import java.lang.reflect.Field;
33+
import java.lang.reflect.Method;
3134
import java.lang.reflect.Modifier;
3235
import java.nio.file.Path;
3336
import java.util.ArrayList;
@@ -37,13 +40,18 @@
3740
import java.util.Map;
3841
import java.util.ServiceLoader;
3942
import java.util.Set;
43+
import java.util.TreeMap;
44+
import java.util.TreeSet;
4045
import java.util.concurrent.ConcurrentHashMap;
4146
import java.util.function.BooleanSupplier;
4247
import java.util.function.Consumer;
48+
import java.util.stream.Collectors;
4349

50+
import jdk.graal.compiler.core.common.NativeImageSupport;
4451
import jdk.graal.compiler.hotspot.CompilerConfig;
4552
import org.graalvm.collections.EconomicMap;
4653
import org.graalvm.jniutils.NativeBridgeSupport;
54+
import org.graalvm.nativeimage.ImageInfo;
4755
import org.graalvm.nativeimage.ImageSingletons;
4856
import org.graalvm.nativeimage.Platform;
4957
import org.graalvm.nativeimage.Platforms;
@@ -54,6 +62,7 @@
5462
import org.graalvm.nativeimage.libgraal.LibGraalLoader;
5563

5664
import jdk.graal.compiler.core.common.Fields;
65+
import jdk.graal.compiler.core.common.LibGraalSupport.HostedOnly;
5766
import jdk.graal.compiler.core.common.spi.ForeignCallSignature;
5867
import jdk.graal.compiler.debug.GraalError;
5968
import jdk.graal.compiler.graph.Edges;
@@ -140,6 +149,12 @@ private static Path readLibgraalJavaHome(ClassLoader cl) {
140149

141150
@Override
142151
public void afterRegistration(AfterRegistrationAccess access) {
152+
// Check that NativeImageSupport.inBuildtimeCode() and ImageInfo.inImageBuildtimeCode()
153+
// agree on the system property key and value they rely on.
154+
GraalError.guarantee(ImageInfo.PROPERTY_IMAGE_CODE_KEY.equals("org.graalvm.nativeimage.imagecode") &&
155+
ImageInfo.PROPERTY_IMAGE_CODE_VALUE_BUILDTIME.equals("buildtime"),
156+
"%s is out of sync with %s", NativeImageSupport.class, ImageInfo.class);
157+
143158
ImageSingletons.add(NativeBridgeSupport.class, new LibGraalNativeBridgeSupport());
144159

145160
// All qualified exports to libgraal modules need to be further exported to
@@ -283,12 +298,44 @@ private Map.Entry<long[], Long> computeReplacement(Object receiver) {
283298
}
284299
}
285300

301+
/**
302+
* List of reached elements annotated by {@link HostedOnly}.
303+
*/
304+
private final List<Object> reachedHostedOnlyElements = new ArrayList<>();
305+
306+
record HostedOnlyElementCallback(Object element, List<Object> reached) implements Consumer<DuringAnalysisAccess> {
307+
@Override
308+
public void accept(DuringAnalysisAccess duringAnalysisAccess) {
309+
reached.add(element);
310+
}
311+
}
312+
313+
private void registerHostedOnlyElements(BeforeAnalysisAccess access, AnnotatedElement... elements) {
314+
for (AnnotatedElement element : elements) {
315+
if (element.getAnnotation(HostedOnly.class) != null) {
316+
access.registerReachabilityHandler(new HostedOnlyElementCallback(element, reachedHostedOnlyElements), element);
317+
}
318+
}
319+
}
320+
286321
@SuppressWarnings("unchecked")
287322
@Override
288323
public void beforeAnalysis(BeforeAnalysisAccess access) {
289324
beforeAnalysisAccess = access;
290325
new FieldOffsetsTransformer().register(access);
291326

327+
for (String className : libgraalLoader.getClassModuleMap().keySet()) {
328+
try {
329+
Class<?> c = Class.forName(className, false, (ClassLoader) libgraalLoader);
330+
registerHostedOnlyElements(access, c);
331+
registerHostedOnlyElements(access, c.getDeclaredMethods());
332+
registerHostedOnlyElements(access, c.getDeclaredFields());
333+
registerHostedOnlyElements(access, c.getDeclaredConstructors());
334+
} catch (ClassNotFoundException e) {
335+
throw new GraalError(e);
336+
}
337+
}
338+
292339
/* Contains static fields that depend on HotSpotJVMCIRuntime */
293340
RuntimeClassInitialization.initializeAtRunTime(HotSpotModifiers.class);
294341
RuntimeClassInitialization.initializeAtRunTime(lookupClass("jdk.vm.ci.hotspot.HotSpotCompiledCodeStream"));
@@ -379,6 +426,40 @@ private void doLegacyJVMCIInitialization() {
379426
@Override
380427
public void afterAnalysis(AfterAnalysisAccess access) {
381428
optionCollector.afterAnalysis(access);
429+
430+
if (!reachedHostedOnlyElements.isEmpty()) {
431+
Map<String, Set<String>> suggestions = new TreeMap<>();
432+
for (var e : reachedHostedOnlyElements) {
433+
String name;
434+
String value;
435+
switch (e) {
436+
case Method m -> {
437+
name = "Method";
438+
value = m.getDeclaringClass().getName() + "." + m.getName();
439+
}
440+
case Constructor<?> c -> {
441+
name = "Method";
442+
Class<?> dc = c.getDeclaringClass();
443+
value = dc.getName() + "." + dc.getSimpleName();
444+
}
445+
case Field f -> {
446+
name = "Field";
447+
value = f.getDeclaringClass().getName() + "." + f.getName();
448+
}
449+
case Class<?> c -> {
450+
name = "Type";
451+
value = c.getName();
452+
}
453+
case null, default -> throw new GraalError("Unexpected element: ", e);
454+
}
455+
suggestions.computeIfAbsent(name, k -> new TreeSet<>()).add(value);
456+
}
457+
var sep = System.lineSeparator() + " ";
458+
var s = suggestions.entrySet().stream().map(e -> e.getValue().stream().map(v -> "-H:AbortOn" + e.getKey() + "Reachable" + "=" + v).collect(Collectors.joining(sep))).collect(
459+
Collectors.joining(sep));
460+
String anno = HostedOnly.class.getSimpleName();
461+
throw new IllegalArgumentException("@" + anno + " annotated elements reached. Add following Native Image flags to see why they are reachable:" + sep + s);
462+
}
382463
}
383464

384465
@Override

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyLibGraalContextChecks.java

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,17 @@
2626

2727
import java.util.List;
2828

29-
import jdk.graal.compiler.hotspot.HotSpotGraalCompilerFactory;
3029
import jdk.graal.compiler.nodes.StructuredGraph;
3130
import jdk.graal.compiler.nodes.java.LoadFieldNode;
3231
import jdk.graal.compiler.nodes.spi.CoreProviders;
3332
import jdk.graal.compiler.phases.VerifyPhase;
3433
import jdk.graal.compiler.serviceprovider.GraalServices;
3534
import jdk.vm.ci.meta.ResolvedJavaField;
36-
import jdk.vm.ci.meta.ResolvedJavaMethod;
3735
import jdk.vm.ci.meta.ResolvedJavaType;
3836
import jdk.vm.ci.services.Services;
39-
import org.graalvm.nativeimage.ImageInfo;
4037

4138
/**
42-
* Ensures that the only code directly accessing
43-
* {@link jdk.vm.ci.services.Services#IS_IN_NATIVE_IMAGE} is in
44-
* {@link jdk.graal.compiler.serviceprovider.GraalServices}. All other code must use one of the
45-
* following methods:
46-
* <ul>
47-
* <li>{@link GraalServices#isInLibgraal()}</li>
48-
* <li>{@link ImageInfo#inImageCode()}</li>
49-
* <li>{@link ImageInfo#inImageBuildtimeCode()}</li>
50-
* <li>{@link ImageInfo#inImageRuntimeCode()}</li>
51-
* </ul>
39+
* Ensures that no code accesses {@link jdk.vm.ci.services.Services#IS_IN_NATIVE_IMAGE}.
5240
*/
5341
public class VerifyLibGraalContextChecks extends VerifyPhase<CoreProviders> {
5442

@@ -57,16 +45,6 @@ public boolean checkContract() {
5745
return false;
5846
}
5947

60-
static boolean isAllowedToAccess(ResolvedJavaMethod method) {
61-
if (method.getDeclaringClass().toJavaName().equals(GraalServices.class.getName())) {
62-
return method.getName().equals("isBuildingLibgraal") || method.getName().equals("isInLibgraal");
63-
}
64-
if (method.getDeclaringClass().toJavaName().equals(HotSpotGraalCompilerFactory.class.getName())) {
65-
return method.getName().equals("createCompiler");
66-
}
67-
return false;
68-
}
69-
7048
@Override
7149
protected void verify(StructuredGraph graph, CoreProviders context) {
7250

@@ -78,13 +56,11 @@ protected void verify(StructuredGraph graph, CoreProviders context) {
7856
ResolvedJavaField field = load.field();
7957
if (field.getDeclaringClass().toJavaName().equals(Services.class.getName())) {
8058
if (field.getName().equals("IS_IN_NATIVE_IMAGE")) {
81-
if (!isAllowedToAccess(graph.method())) {
82-
throw new VerificationError("reading %s in %s is prohibited - use %s.isInLibgraal() instead",
83-
field.format("%H.%n"),
84-
graph.method().format("%H.%n(%p)"),
85-
GraalServices.class.getName());
59+
throw new VerificationError("reading %s in %s is prohibited - use %s.isInLibgraal() instead",
60+
field.format("%H.%n"),
61+
graph.method().format("%H.%n(%p)"),
62+
GraalServices.class.getName());
8663

87-
}
8864
}
8965
}
9066
}

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifySystemPropertyUsage.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package jdk.graal.compiler.core.test;
2626

27+
import jdk.graal.compiler.core.common.NativeImageSupport;
2728
import jdk.graal.compiler.nodes.StructuredGraph;
2829
import jdk.graal.compiler.nodes.java.MethodCallTargetNode;
2930
import jdk.graal.compiler.nodes.spi.CoreProviders;
@@ -89,6 +90,9 @@ protected void verify(StructuredGraph graph, CoreProviders context) {
8990
} else if (holderQualified.equals("jdk.graal.compiler.hotspot.HotSpotReplacementsImpl") && caller.getName().equals("registerSnippet")) {
9091
// We allow opening snippet registration in jargraal unit tests.
9192
return;
93+
} else if (holderQualified.equals(NativeImageSupport.class.getName())) {
94+
// Called as part of initializing GraalServices
95+
return;
9296
}
9397
for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) {
9498
ResolvedJavaMethod callee = t.targetMethod();
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2025, 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 jdk.graal.compiler.hotspot.test;
26+
27+
import java.io.IOException;
28+
import java.nio.file.Files;
29+
import java.nio.file.Path;
30+
import java.util.List;
31+
import java.util.Set;
32+
import java.util.regex.Pattern;
33+
import java.util.stream.Collectors;
34+
35+
import org.graalvm.nativeimage.ImageInfo;
36+
import org.junit.Test;
37+
38+
import jdk.graal.compiler.core.GraalCompiler;
39+
import jdk.graal.compiler.core.test.GraalCompilerTest;
40+
import jdk.graal.compiler.debug.GraalError;
41+
import jdk.graal.compiler.serviceprovider.GraalServices;
42+
import jdk.graal.compiler.test.SubprocessUtil;
43+
import jdk.graal.compiler.test.SubprocessUtil.Subprocess;
44+
45+
/**
46+
* Tests that {@code jdk.graal.compiler} does not have unwanted dependencies such as
47+
* {@code org.graalvm.nativeimage}.
48+
*/
49+
public class GraalModuleDependenciesTest extends GraalCompilerTest {
50+
51+
static Path getJavaExe() {
52+
Path javaHome = Path.of(System.getProperty("java.home"));
53+
boolean isWindows = GraalServices.getSavedProperty("os.name").contains("Windows");
54+
Path javaExe = javaHome.resolve(Path.of("bin", isWindows ? "java.exe" : "java"));
55+
if (!Files.isExecutable(javaExe)) {
56+
throw new GraalError("Java launcher %s does not exist or is not executable", javaExe);
57+
}
58+
return javaExe;
59+
}
60+
61+
private static Subprocess run(String... command) throws InterruptedException, IOException {
62+
Subprocess proc = SubprocessUtil.java(List.of(command));
63+
if (proc.exitCode != 0) {
64+
fail("Non-zero exit code:%n%s", proc.preserveArgfile());
65+
}
66+
return proc;
67+
}
68+
69+
private static String removeVersionSuffix(String moduleName) {
70+
int at = moduleName.indexOf('@');
71+
if (at == -1) {
72+
return moduleName;
73+
}
74+
return moduleName.substring(0, at);
75+
}
76+
77+
@Test
78+
public void test() throws InterruptedException, IOException {
79+
String javaExe = getJavaExe().toString();
80+
Subprocess proc = run(javaExe,
81+
"--limit-modules=jdk.graal.compiler",
82+
"--list-modules");
83+
84+
String graal = GraalCompiler.class.getModule().getName();
85+
String nativeImage = ImageInfo.class.getModule().getName();
86+
Set<String> moduleNames = proc.output.stream().map(GraalModuleDependenciesTest::removeVersionSuffix).collect(Collectors.toSet());
87+
if (!moduleNames.contains(graal)) {
88+
fail("Missing Graal (%s):%n%s", graal, proc.preserveArgfile());
89+
}
90+
if (moduleNames.contains(nativeImage)) {
91+
fail("Native Image API (%s) should not be a dependency of Graal (%s):%n%s", nativeImage, graal, proc.preserveArgfile());
92+
}
93+
94+
proc = run(javaExe,
95+
"--limit-modules=jdk.graal.compiler",
96+
"-XX:+EagerJVMCI",
97+
"-Djdk.graal.ShowConfiguration=info",
98+
"--version");
99+
Pattern expect = Pattern.compile("^Using .* loaded from class files");
100+
if (proc.output.stream().noneMatch(line -> expect.matcher(line).find())) {
101+
fail("Did not find line matching %s%n%s", expect, proc.preserveArgfile());
102+
}
103+
}
104+
}

0 commit comments

Comments
 (0)