Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions compiler/mx.compiler/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@
"dependencies" : [
"sdk:WORD",
"sdk:COLLECTIONS",
"sdk:NATIVEIMAGE",
"truffle:TRUFFLE_COMPILER",
],
"requires" : [
Expand Down Expand Up @@ -643,7 +642,6 @@
"distDependencies" : [
"sdk:COLLECTIONS",
"sdk:WORD",
"sdk:NATIVEIMAGE",
"truffle:TRUFFLE_COMPILER",
],
"allowsJavadocWarnings": True,
Expand Down Expand Up @@ -680,6 +678,7 @@
"jdk.graal.compiler.libgraal.loader"
],
"distDependencies" : [
"sdk:NATIVEIMAGE",
"sdk:NATIVEIMAGE_LIBGRAAL",
"GRAAL",
],
Expand All @@ -699,10 +698,12 @@
"distDependencies": [
"GRAAL",
"GRAAL_MANAGEMENT",
"sdk:NATIVEIMAGE",
"sdk:NATIVEIMAGE_LIBGRAAL",
"sdk:COLLECTIONS",
"sdk:JNIUTILS",
"sdk:NATIVEIMAGE",
"sdk:NATIVEBRIDGE"
"sdk:NATIVEBRIDGE",
"truffle:TRUFFLE_COMPILER"
],
"maven": False,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.module.ModuleDescriptor;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Path;
import java.util.ArrayList;
Expand All @@ -37,13 +40,18 @@
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import jdk.graal.compiler.core.common.NativeImageSupport;
import jdk.graal.compiler.hotspot.CompilerConfig;
import org.graalvm.collections.EconomicMap;
import org.graalvm.jniutils.NativeBridgeSupport;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
Expand All @@ -54,6 +62,7 @@
import org.graalvm.nativeimage.libgraal.LibGraalLoader;

import jdk.graal.compiler.core.common.Fields;
import jdk.graal.compiler.core.common.LibGraalSupport.HostedOnly;
import jdk.graal.compiler.core.common.spi.ForeignCallSignature;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.graph.Edges;
Expand Down Expand Up @@ -140,6 +149,12 @@ private static Path readLibgraalJavaHome(ClassLoader cl) {

@Override
public void afterRegistration(AfterRegistrationAccess access) {
// Check that NativeImageSupport.inBuildtimeCode() and ImageInfo.inImageBuildtimeCode()
// agree on the system property key and value they rely on.
GraalError.guarantee(ImageInfo.PROPERTY_IMAGE_CODE_KEY.equals("org.graalvm.nativeimage.imagecode") &&
ImageInfo.PROPERTY_IMAGE_CODE_VALUE_BUILDTIME.equals("buildtime"),
"%s is out of sync with %s", NativeImageSupport.class, ImageInfo.class);

ImageSingletons.add(NativeBridgeSupport.class, new LibGraalNativeBridgeSupport());

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

/**
* List of reached elements annotated by {@link HostedOnly}.
*/
private final List<Object> reachedHostedOnlyElements = new ArrayList<>();

record HostedOnlyElementCallback(Object element, List<Object> reached) implements Consumer<DuringAnalysisAccess> {
@Override
public void accept(DuringAnalysisAccess duringAnalysisAccess) {
reached.add(element);
}
}

private void registerHostedOnlyElements(BeforeAnalysisAccess access, AnnotatedElement... elements) {
for (AnnotatedElement element : elements) {
if (element.getAnnotation(HostedOnly.class) != null) {
access.registerReachabilityHandler(new HostedOnlyElementCallback(element, reachedHostedOnlyElements), element);
}
}
}

@SuppressWarnings("unchecked")
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
beforeAnalysisAccess = access;
new FieldOffsetsTransformer().register(access);

for (String className : libgraalLoader.getClassModuleMap().keySet()) {
try {
Class<?> c = Class.forName(className, false, (ClassLoader) libgraalLoader);
registerHostedOnlyElements(access, c);
registerHostedOnlyElements(access, c.getDeclaredMethods());
registerHostedOnlyElements(access, c.getDeclaredFields());
registerHostedOnlyElements(access, c.getDeclaredConstructors());
} catch (ClassNotFoundException e) {
throw new GraalError(e);
}
}

/* Contains static fields that depend on HotSpotJVMCIRuntime */
RuntimeClassInitialization.initializeAtRunTime(HotSpotModifiers.class);
RuntimeClassInitialization.initializeAtRunTime(lookupClass("jdk.vm.ci.hotspot.HotSpotCompiledCodeStream"));
Expand Down Expand Up @@ -379,6 +426,40 @@ private void doLegacyJVMCIInitialization() {
@Override
public void afterAnalysis(AfterAnalysisAccess access) {
optionCollector.afterAnalysis(access);

if (!reachedHostedOnlyElements.isEmpty()) {
Map<String, Set<String>> suggestions = new TreeMap<>();
for (var e : reachedHostedOnlyElements) {
String name;
String value;
switch (e) {
case Method m -> {
name = "Method";
value = m.getDeclaringClass().getName() + "." + m.getName();
}
case Constructor<?> c -> {
name = "Method";
Class<?> dc = c.getDeclaringClass();
value = dc.getName() + "." + dc.getSimpleName();
}
case Field f -> {
name = "Field";
value = f.getDeclaringClass().getName() + "." + f.getName();
}
case Class<?> c -> {
name = "Type";
value = c.getName();
}
case null, default -> throw new GraalError("Unexpected element: ", e);
}
suggestions.computeIfAbsent(name, k -> new TreeSet<>()).add(value);
}
var sep = System.lineSeparator() + " ";
var s = suggestions.entrySet().stream().map(e -> e.getValue().stream().map(v -> "-H:AbortOn" + e.getKey() + "Reachable" + "=" + v).collect(Collectors.joining(sep))).collect(
Collectors.joining(sep));
String anno = HostedOnly.class.getSimpleName();
throw new IllegalArgumentException("@" + anno + " annotated elements reached. Add following Native Image flags to see why they are reachable:" + sep + s);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sample output:

[libjvmcicompiler.dylib:71997] Internal exception: com.oracle.svm.core.util.UserError$UserException: Feature defined by jdk.graal.compiler.libgraal.LibGraalFeature unexpectedly failed with a(n) java.lang.IllegalArgumentException. Please report this problem to the authors of jdk.graal.compiler.libgraal.LibGraalFeature.
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.UserError.abort(UserError.java:97)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.FeatureHandler.handleFeatureError(FeatureHandler.java:306)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.FeatureHandler.forEachFeature(FeatureHandler.java:95)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:857)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:571)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGenerator.run(NativeImageGenerator.java:537)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.buildImage(NativeImageGeneratorRunner.java:545)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.build(NativeImageGeneratorRunner.java:731)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.start(NativeImageGeneratorRunner.java:151)
[libjvmcicompiler.dylib:71997] 	at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:99)
[libjvmcicompiler.dylib:71997] Caused by: java.lang.IllegalArgumentException: @HostedOnly annotated elements reached. Add following Native Image flags to see why they are reachable:
[libjvmcicompiler.dylib:71997]   -H:AbortOnFieldReachable=jdk.graal.compiler.graph.NodeFlood.totalMarkedCount
[libjvmcicompiler.dylib:71997]   -H:AbortOnFieldReachable=jdk.graal.compiler.graph.NodeFlood.worklist
[libjvmcicompiler.dylib:71997]   -H:AbortOnMethodReachable=jdk.graal.compiler.graph.NodeInputList.NodeInputList
[libjvmcicompiler.dylib:71997]   -H:AbortOnMethodReachable=jdk.graal.compiler.graph.NodeSourcePosition.trim
[libjvmcicompiler.dylib:71997]   -H:AbortOnTypeReachable=jdk.graal.compiler.graph.SuccessorEdges

}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,17 @@

import java.util.List;

import jdk.graal.compiler.hotspot.HotSpotGraalCompilerFactory;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.java.LoadFieldNode;
import jdk.graal.compiler.nodes.spi.CoreProviders;
import jdk.graal.compiler.phases.VerifyPhase;
import jdk.graal.compiler.serviceprovider.GraalServices;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.services.Services;
import org.graalvm.nativeimage.ImageInfo;

/**
* Ensures that the only code directly accessing
* {@link jdk.vm.ci.services.Services#IS_IN_NATIVE_IMAGE} is in
* {@link jdk.graal.compiler.serviceprovider.GraalServices}. All other code must use one of the
* following methods:
* <ul>
* <li>{@link GraalServices#isInLibgraal()}</li>
* <li>{@link ImageInfo#inImageCode()}</li>
* <li>{@link ImageInfo#inImageBuildtimeCode()}</li>
* <li>{@link ImageInfo#inImageRuntimeCode()}</li>
* </ul>
* Ensures that no code accesses {@link jdk.vm.ci.services.Services#IS_IN_NATIVE_IMAGE}.
*/
public class VerifyLibGraalContextChecks extends VerifyPhase<CoreProviders> {

Expand All @@ -57,16 +45,6 @@ public boolean checkContract() {
return false;
}

static boolean isAllowedToAccess(ResolvedJavaMethod method) {
if (method.getDeclaringClass().toJavaName().equals(GraalServices.class.getName())) {
return method.getName().equals("isBuildingLibgraal") || method.getName().equals("isInLibgraal");
}
if (method.getDeclaringClass().toJavaName().equals(HotSpotGraalCompilerFactory.class.getName())) {
return method.getName().equals("createCompiler");
}
return false;
}

@Override
protected void verify(StructuredGraph graph, CoreProviders context) {

Expand All @@ -78,13 +56,11 @@ protected void verify(StructuredGraph graph, CoreProviders context) {
ResolvedJavaField field = load.field();
if (field.getDeclaringClass().toJavaName().equals(Services.class.getName())) {
if (field.getName().equals("IS_IN_NATIVE_IMAGE")) {
if (!isAllowedToAccess(graph.method())) {
throw new VerificationError("reading %s in %s is prohibited - use %s.isInLibgraal() instead",
field.format("%H.%n"),
graph.method().format("%H.%n(%p)"),
GraalServices.class.getName());
throw new VerificationError("reading %s in %s is prohibited - use %s.isInLibgraal() instead",
field.format("%H.%n"),
graph.method().format("%H.%n(%p)"),
GraalServices.class.getName());

}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
package jdk.graal.compiler.core.test;

import jdk.graal.compiler.core.common.NativeImageSupport;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.java.MethodCallTargetNode;
import jdk.graal.compiler.nodes.spi.CoreProviders;
Expand Down Expand Up @@ -89,6 +90,9 @@ protected void verify(StructuredGraph graph, CoreProviders context) {
} else if (holderQualified.equals("jdk.graal.compiler.hotspot.HotSpotReplacementsImpl") && caller.getName().equals("registerSnippet")) {
// We allow opening snippet registration in jargraal unit tests.
return;
} else if (holderQualified.equals(NativeImageSupport.class.getName())) {
// Called as part of initializing GraalServices
return;
}
for (MethodCallTargetNode t : graph.getNodes(MethodCallTargetNode.TYPE)) {
ResolvedJavaMethod callee = t.targetMethod();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2025, 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 jdk.graal.compiler.hotspot.test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.graalvm.nativeimage.ImageInfo;
import org.junit.Test;

import jdk.graal.compiler.core.GraalCompiler;
import jdk.graal.compiler.core.test.GraalCompilerTest;
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.serviceprovider.GraalServices;
import jdk.graal.compiler.test.SubprocessUtil;
import jdk.graal.compiler.test.SubprocessUtil.Subprocess;

/**
* Tests that {@code jdk.graal.compiler} does not have unwanted dependencies such as
* {@code org.graalvm.nativeimage}.
*/
public class GraalModuleDependenciesTest extends GraalCompilerTest {

static Path getJavaExe() {
Path javaHome = Path.of(System.getProperty("java.home"));
boolean isWindows = GraalServices.getSavedProperty("os.name").contains("Windows");
Path javaExe = javaHome.resolve(Path.of("bin", isWindows ? "java.exe" : "java"));
if (!Files.isExecutable(javaExe)) {
throw new GraalError("Java launcher %s does not exist or is not executable", javaExe);
}
return javaExe;
}

private static Subprocess run(String... command) throws InterruptedException, IOException {
Subprocess proc = SubprocessUtil.java(List.of(command));
if (proc.exitCode != 0) {
fail("Non-zero exit code:%n%s", proc.preserveArgfile());
}
return proc;
}

private static String removeVersionSuffix(String moduleName) {
int at = moduleName.indexOf('@');
if (at == -1) {
return moduleName;
}
return moduleName.substring(0, at);
}

@Test
public void test() throws InterruptedException, IOException {
String javaExe = getJavaExe().toString();
Subprocess proc = run(javaExe,
"--limit-modules=jdk.graal.compiler",
"--list-modules");

String graal = GraalCompiler.class.getModule().getName();
String nativeImage = ImageInfo.class.getModule().getName();
Set<String> moduleNames = proc.output.stream().map(GraalModuleDependenciesTest::removeVersionSuffix).collect(Collectors.toSet());
if (!moduleNames.contains(graal)) {
fail("Missing Graal (%s):%n%s", graal, proc.preserveArgfile());
}
if (moduleNames.contains(nativeImage)) {
fail("Native Image API (%s) should not be a dependency of Graal (%s):%n%s", nativeImage, graal, proc.preserveArgfile());
}

proc = run(javaExe,
"--limit-modules=jdk.graal.compiler",
"-XX:+EagerJVMCI",
"-Djdk.graal.ShowConfiguration=info",
"--version");
Pattern expect = Pattern.compile("^Using .* loaded from class files");
if (proc.output.stream().noneMatch(line -> expect.matcher(line).find())) {
fail("Did not find line matching %s%n%s", expect, proc.preserveArgfile());
}
}
}
Loading
Loading