Skip to content

[GR-65048] Allow language interpreter to run on z/OS and s390x architecture. #11390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2025
Merged
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
3 changes: 3 additions & 0 deletions sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

This changelog summarizes major changes between GraalVM SDK versions. The main focus is on APIs exported by GraalVM SDK.

## Version 26.0.0
* GR-65048: GR-65048: Introduced the `-Dpolyglot.engine.allowUnsupportedPlatform=true` system property to enable Truffle to run on unsupported platforms. If this property is enabled then the failure will be suppressed. Please see follow-up errors and warnings for instructions on how to continue. Note that using an unsupported platform will also force the fallback runtime without runtime optimization.

## Version 25.0.0
* GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens.
* GR-51664 Improved `PolyglotException#toString` and `PolyglotException#printStackTrace`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ static Map<String, String> readOptionsFromSystemProperties(Map<String, String> o
for (Object systemKey : properties.keySet()) {
String key = (String) systemKey;
if ("polyglot.engine.AllowExperimentalOptions".equals(key) || key.equals("polyglot.engine.resourcePath") || key.startsWith("polyglot.engine.resourcePath.") ||
key.equals("polyglot.engine.userResourceCache")) {
key.equals("polyglot.engine.userResourceCache") || key.equals("polyglot.engine.allowUnsupportedPlatform")) {
continue;
}
if (key.startsWith(systemPropertyPrefix)) {
Expand Down
3 changes: 3 additions & 0 deletions truffle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

This changelog summarizes major changes between Truffle versions relevant to languages implementors building upon the Truffle framework. The main focus is on APIs exported by Truffle.

## Version 26.0
* GR-65048: Introduced `InternalResource.OS.UNSUPPORTED` and `InternalResource.CPUArchitecture.UNSUPPORTED` to represent unsupported platforms. Execution on unsupported platforms must be explicitly enabled using the system property `-Dpolyglot.engine.allowUnsupportedPlatform=true`. If this property is not set, calls to `OS.getCurrent()` or `CPUArchitecture.getCurrent()` will throw an `IllegalStateException` when running on an unsupported platform. `InternalResource` implementations should handle the unsupported platform and describe possible steps in the error message on how to proceed.

## Version 25.0
* GR-31495 Added ability to specify language and instrument specific options using `Source.Builder.option(String, String)`. Languages may describe available source options by implementing `TruffleLanguage.getSourceOptionDescriptors()` and `TruffleInstrument.getSourceOptionDescriptors()` respectively.
* GR-61493 Added `RootNode.prepareForCall` which allows root nodes to prepare themselves for use as a call target (or to validate whether they can be used as a call target).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.Formatter;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -74,6 +75,7 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -87,6 +89,8 @@
import javax.management.remote.JMXServiceURL;

import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.options.OptionDescriptor;
import org.graalvm.options.OptionDescriptors;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
Expand Down Expand Up @@ -188,11 +192,11 @@ private static Method findTestMethod(Class<?> testClass) {
}

private static Subprocess execute(Method testMethod, boolean failOnNonZeroExitCode, List<String> prefixVMOptions,
List<String> postfixVmOptions, Duration timeout, Consumer<ProcessHandle> onStart) throws IOException, InterruptedException {
List<String> postfixVmOptions, boolean removeOptimizedRuntimeOptions, Duration timeout, Consumer<ProcessHandle> onStart) throws IOException, InterruptedException {
String enclosingElement = testMethod.getDeclaringClass().getName();
String testName = testMethod.getName();
Subprocess subprocess = javaHelper(
configure(getVmArgs(), prefixVMOptions, postfixVmOptions),
configure(getVmArgs(), prefixVMOptions, postfixVmOptions, removeOptimizedRuntimeOptions),
null, null,
List.of("com.oracle.mxtool.junit.MxJUnitWrapper", String.format("%s#%s", enclosingElement, testName)),
timeout, onStart);
Expand All @@ -202,8 +206,9 @@ private static Subprocess execute(Method testMethod, boolean failOnNonZeroExitCo
return subprocess;
}

private static List<String> configure(List<String> vmArgs, List<String> prefixVMOptions, List<String> postfixVmOptions) {
private static List<String> configure(List<String> vmArgs, List<String> prefixVMOptions, List<String> postfixVmOptions, boolean removeOptimizedRuntimeOptions) {
List<String> newVmArgs = new ArrayList<>();
Predicate<String> optimizedRuntimeFilter = removeOptimizedRuntimeOptions ? new OptimizedRuntimeOptionsFilter() : (s) -> true;
newVmArgs.addAll(vmArgs.stream().filter(vmArg -> {
for (String toRemove : getForbiddenVmOptions()) {
if (vmArg.startsWith(toRemove)) {
Expand All @@ -221,14 +226,14 @@ private static List<String> configure(List<String> vmArgs, List<String> prefixVM
}
}
return true;
}).collect(Collectors.toList()));
}).filter(optimizedRuntimeFilter).toList());
for (String additionalVmOption : prefixVMOptions) {
if (!additionalVmOption.startsWith(TO_REMOVE_PREFIX)) {
if (!additionalVmOption.startsWith(TO_REMOVE_PREFIX) && optimizedRuntimeFilter.test(additionalVmOption)) {
newVmArgs.add(1, additionalVmOption);
}
}
for (String additionalVmOption : postfixVmOptions) {
if (!additionalVmOption.startsWith(TO_REMOVE_PREFIX)) {
if (!additionalVmOption.startsWith(TO_REMOVE_PREFIX) && optimizedRuntimeFilter.test(additionalVmOption)) {
newVmArgs.add(additionalVmOption);
}
}
Expand Down Expand Up @@ -466,6 +471,7 @@ public static final class Builder {
private Duration timeout;
private Consumer<Subprocess> onExit;
private Consumer<ProcessHandle> onStart;
private boolean removeOptimizedRuntimeOptions;

private Builder(Class<?> testClass, Runnable run) {
this.testClass = testClass;
Expand Down Expand Up @@ -496,6 +502,17 @@ public Builder postfixVmOption(String... options) {
return this;
}

/**
* Removes all {@code OptimizedRuntimeOptions} from the command line.
* <p>
* This method is useful in tests that require fallback to the default Truffle runtime,
* which does not support optimized runtime options.
*/
public Builder removeOptimizedRuntimeOptions(boolean value) {
removeOptimizedRuntimeOptions = value;
return this;
}

/**
* Disables assertions in {@code forClass}.
*/
Expand Down Expand Up @@ -535,7 +552,7 @@ public void run() throws IOException, InterruptedException {
if (isSubprocess()) {
runnable.run();
} else {
Subprocess process = execute(findTestMethod(testClass), failOnNonZeroExit, prefixVmArgs, postfixVmArgs, timeout, onStart);
Subprocess process = execute(findTestMethod(testClass), failOnNonZeroExit, prefixVmArgs, postfixVmArgs, removeOptimizedRuntimeOptions, timeout, onStart);
if (onExit != null) {
try {
onExit.accept(process);
Expand Down Expand Up @@ -1175,4 +1192,34 @@ private void skipWhite() throws IOException {
}
}
}

private static final class OptimizedRuntimeOptionsFilter implements Predicate<String> {

private final Set<String> toRemove;

OptimizedRuntimeOptionsFilter() {
Set<String> optionNames;
try {
Class<?> options = Class.forName("com.oracle.truffle.runtime.OptimizedRuntimeOptions");
OptionDescriptors descriptors = (OptionDescriptors) ReflectionUtils.invokeStatic(options, "getDescriptors");
optionNames = new HashSet<>();
for (OptionDescriptor descriptor : descriptors) {
optionNames.add(String.format("-Dpolyglot.%s", descriptor.getName()));
}
} catch (ClassNotFoundException e) {
optionNames = Set.of();
}
toRemove = optionNames;
}

@Override
public boolean test(String s) {
int index = s.lastIndexOf("=");
if (index > 0) {
String key = s.substring(0, index);
return !toRemove.contains(key);
}
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand All @@ -72,10 +73,13 @@
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
import com.oracle.truffle.api.test.OSUtils;
import com.oracle.truffle.api.test.ReflectionUtils;
import com.oracle.truffle.api.test.SubprocessTestUtils;
import com.oracle.truffle.api.test.common.TestUtils;
import com.oracle.truffle.tck.tests.TruffleTestAssumptions;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.PolyglotException;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
Expand All @@ -91,6 +95,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static com.oracle.truffle.api.test.polyglot.AbstractPolyglotTest.assertFails;
import static org.junit.Assume.assumeFalse;

public class InternalResourceTest {

Expand Down Expand Up @@ -1308,6 +1313,83 @@ protected Object execute(RootNode node, Env env, Object[] contextArguments, Obje
}
}

@Test
public void testUnsupportedPlatformDisabled() throws IOException, InterruptedException {
assumeFalse(ImageInfo.inImageCode());
SubprocessTestUtils.newBuilder(InternalResourceTest.class, () -> {
var prevProperties = setUnsupportedArchitecture();
try {
AbstractPolyglotTest.assertFails(() -> Context.create(), PolyglotException.class, (e) -> {
assertEquals("java.lang.IllegalStateException: Unsupported operating system: 'z/os'. " +
"If you want to continue using this unsupported platform, set the system property '-Dpolyglot.engine.allowUnsupportedPlatform=true'. " +
"Note that unsupported platforms require additional command line options to be functional.", e.getMessage());
});
} finally {
restoreArchitecture(prevProperties);
}
}).removeOptimizedRuntimeOptions(true).run();
}

@Test
public void testUnsupportedPlatformEnabled() throws IOException, InterruptedException {
assumeFalse(ImageInfo.inImageCode());
SubprocessTestUtils.newBuilder(InternalResourceTest.class, () -> {
var prevProperties = setUnsupportedArchitecture();
try {
AbstractPolyglotTest.assertFails(() -> Context.create(), PolyglotException.class, (e) -> {
assertEquals("java.lang.IllegalStateException: Truffle is running on an unsupported platform. " +
"On unsupported platforms, you must explicitly set the default cache directory " +
"using the system property '-Dpolyglot.engine.userResourceCache=<path_to_cache_folder>'.", e.getMessage());
});
} finally {
restoreArchitecture(prevProperties);
}
}).removeOptimizedRuntimeOptions(true).prefixVmOption("-Dpolyglot.engine.allowUnsupportedPlatform=true").run();
}

@Test
public void testUnsupportedPlatformEnabledWithExplicitCacheFolder() throws IOException, InterruptedException {
assumeFalse(ImageInfo.inImageCode());
Path tmp = SubprocessTestUtils.isSubprocess() ? null : Files.createTempDirectory("test_cache_root").toAbsolutePath();
try {
SubprocessTestUtils.newBuilder(InternalResourceTest.class, () -> {
var prevProperties = setUnsupportedArchitecture();
try {
Context context = Context.create();
context.close();
} finally {
restoreArchitecture(prevProperties);
}
}).//
prefixVmOption("-Dpolyglot.engine.allowUnsupportedPlatform=true").//
prefixVmOption("-Dpolyglot.engine.userResourceCache=" + tmp).//
removeOptimizedRuntimeOptions(true).//
onExit((subprocess) -> {
assertTrue(subprocess.output.stream().anyMatch((l) -> l.contains("Truffle is running on an unsupported platform where the TruffleAttach library is unavailable.")));
}).//
run();
} finally {
if (tmp != null) {
delete(tmp);
}
}
}

private static Map<String, String> setUnsupportedArchitecture() {
Map<String, String> prev = new HashMap<>();
prev.put("os.name", System.getProperty("os.name"));
prev.put("os.arch", System.getProperty("os.arch"));
System.setProperty("os.name", "z/os");
System.setProperty("os.arch", "s390x");
return prev;
}

private static void restoreArchitecture(Map<String, String> properties) {
for (var entry : properties.entrySet()) {
System.setProperty(entry.getKey(), entry.getValue());
}
}

private static String findLine(String pattern, String[] lines) {
Pattern p = Pattern.compile(pattern);
for (String line : lines) {
Expand Down
4 changes: 3 additions & 1 deletion truffle/src/com.oracle.truffle.api/snapshot.sigtest
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ CLSS public final static !enum com.oracle.truffle.api.InternalResource$CPUArchit
outer com.oracle.truffle.api.InternalResource
fld public final static com.oracle.truffle.api.InternalResource$CPUArchitecture AARCH64
fld public final static com.oracle.truffle.api.InternalResource$CPUArchitecture AMD64
fld public final static com.oracle.truffle.api.InternalResource$CPUArchitecture UNSUPPORTED
meth public java.lang.String toString()
meth public static com.oracle.truffle.api.InternalResource$CPUArchitecture getCurrent()
meth public static com.oracle.truffle.api.InternalResource$CPUArchitecture valueOf(java.lang.String)
Expand Down Expand Up @@ -213,13 +214,14 @@ CLSS public final static !enum com.oracle.truffle.api.InternalResource$OS
outer com.oracle.truffle.api.InternalResource
fld public final static com.oracle.truffle.api.InternalResource$OS DARWIN
fld public final static com.oracle.truffle.api.InternalResource$OS LINUX
fld public final static com.oracle.truffle.api.InternalResource$OS UNSUPPORTED
fld public final static com.oracle.truffle.api.InternalResource$OS WINDOWS
meth public java.lang.String toString()
meth public static com.oracle.truffle.api.InternalResource$OS getCurrent()
meth public static com.oracle.truffle.api.InternalResource$OS valueOf(java.lang.String)
meth public static com.oracle.truffle.api.InternalResource$OS[] values()
supr java.lang.Enum<com.oracle.truffle.api.InternalResource$OS>
hfds id
hfds PROPERTY_ALLOW_UNSUPPORTED_PLATFORM,allowsUnsupportedPlatformValue,id

CLSS public com.oracle.truffle.api.OptimizationFailedException
cons public init(java.lang.Throwable,com.oracle.truffle.api.RootCallTarget)
Expand Down
Loading
Loading