Skip to content

Commit e17d792

Browse files
committed
Added common Truffle version support.
1 parent 1627836 commit e17d792

File tree

5 files changed

+357
-138
lines changed

5 files changed

+357
-138
lines changed

truffle/src/com.oracle.truffle.runtime/src/META-INF/native-image/org.graalvm.truffle/truffle-runtime/native-image.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ ForceOnModulePath = org.graalvm.truffle.runtime
22
Args = --macro:truffle-svm \
33
--features=com.oracle.svm.truffle.TruffleFeature \
44
--features=com.oracle.svm.truffle.TruffleJFRFeature \
5+
--features=com.oracle.truffle.runtime.VersionCheckFeature \
56
-H:MaxRuntimeCompileMethods=2500
67

78
JavaArgs = -Dtruffle.TruffleRuntime=com.oracle.svm.truffle.api.SubstrateTruffleRuntime \

truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/OptimizedTruffleRuntime.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@
7171

7272
import org.graalvm.collections.EconomicMap;
7373
import org.graalvm.collections.UnmodifiableEconomicMap;
74-
import org.graalvm.home.Version;
7574
import org.graalvm.nativeimage.ImageInfo;
7675
import org.graalvm.options.OptionCategory;
7776
import org.graalvm.options.OptionDescriptor;
@@ -168,13 +167,6 @@
168167
public abstract class OptimizedTruffleRuntime implements TruffleRuntime, TruffleCompilerRuntime {
169168

170169
private static final int JAVA_SPECIFICATION_VERSION = Runtime.version().feature();
171-
/**
172-
* When modifying the version values defined below, ensure that the corresponding version fields
173-
* in {@code TruffleBaseFeature} are also updated accordingly to maintain consistency.
174-
*/
175-
public static final int MIN_JDK_VERSION = 25;
176-
public static final int MAX_JDK_VERSION = 29;
177-
public static final Version NEXT_VERSION_UPDATE = Version.create(29, 1);
178170

179171
/**
180172
* Used only to reset state for native image compilation.
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
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+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.runtime;
42+
43+
import com.oracle.truffle.api.CompilerDirectives;
44+
import com.oracle.truffle.compiler.TruffleCompilationSupport;
45+
import com.oracle.truffle.polyglot.PolyglotImpl;
46+
import org.graalvm.home.Version;
47+
48+
import java.io.BufferedReader;
49+
import java.io.IOException;
50+
import java.io.InputStream;
51+
import java.io.InputStreamReader;
52+
import java.lang.reflect.Field;
53+
import java.lang.reflect.Method;
54+
import java.nio.charset.StandardCharsets;
55+
56+
/**
57+
* Provides support for verifying compatibility between the Truffle API, Truffle compiler, and
58+
* Truffle SubstrateVM feature versions.
59+
*/
60+
public final class VersionCheck {
61+
62+
private static final int MIN_JDK_VERSION = 21;
63+
private static final int MAX_JDK_VERSION = 29;
64+
private static final Version MIN_COMPILER_VERSION = Version.create(23, 1, 2);
65+
private static final Version NEXT_VERSION_UPDATE = Version.create(29, 1);
66+
67+
private VersionCheck() {
68+
}
69+
70+
/**
71+
* Verifies that the Truffle feature is compatible with the current Truffle runtime version.
72+
*
73+
* @return a string describing the incompatibility, or {@code null} if the check passes.
74+
*/
75+
static String checkSVMVersion() {
76+
if (isVersionCheck()) {
77+
Version truffleAPIVersion = getTruffleVersion();
78+
Version truffleMajorMinorVersion = stripUpdateVersion(truffleAPIVersion);
79+
Version truffleSVMVersion = getSVMFeatureVersion();
80+
Version truffleSVMMajorMinorVersion = stripUpdateVersion(truffleSVMVersion);
81+
if (truffleSVMVersion.compareTo(NEXT_VERSION_UPDATE) >= 0) {
82+
throw new AssertionError("MIN_COMPILER_VERSION, MIN_JDK_VERSION and MAX_JDK_VERSION must be updated!");
83+
} else if (truffleSVMMajorMinorVersion.compareTo(truffleMajorMinorVersion) > 0) {
84+
// no forward compatibility
85+
return formatVersionWarningMessage("""
86+
Your Java runtime '%s' with native-image feature version '%s' is incompatible with polyglot version '%s'.
87+
Update the org.graalvm.polyglot versions to at least '%s' to resolve this.
88+
""", Runtime.version(), truffleSVMVersion, truffleAPIVersion, truffleSVMVersion);
89+
} else if (Runtime.version().feature() < MIN_JDK_VERSION) {
90+
return formatVersionWarningMessage("""
91+
Your Java runtime '%s' with native-image feature version '%s' is incompatible with polyglot version '%s'.
92+
The Java runtime version must be greater or equal to JDK '%d'.
93+
Update your Java runtime to resolve this.
94+
""", Runtime.version(), truffleSVMVersion, truffleAPIVersion, MIN_JDK_VERSION);
95+
} else if (truffleSVMVersion.compareTo(MIN_COMPILER_VERSION) < 0) {
96+
return formatVersionWarningMessage("""
97+
Your Java runtime '%s' with compiler version '%s' is incompatible with polyglot version '%s'.
98+
Update the Java runtime to the latest update release of JDK '%d'.
99+
""", Runtime.version(), truffleSVMVersion, truffleAPIVersion, Runtime.version().feature());
100+
}
101+
}
102+
return null;
103+
}
104+
105+
/**
106+
* Verifies that the LibGraal compiler is compatible with the current Truffle runtime version.
107+
*
108+
* @return a string describing the incompatibility, or {@code null} if the check passes.
109+
*/
110+
public static String checkLibGraalCompilerVersion(TruffleCompilationSupport compilationSupport) {
111+
if (isVersionCheck()) {
112+
Version truffleVersion = getTruffleVersion();
113+
if (truffleVersion.compareTo(NEXT_VERSION_UPDATE) >= 0) {
114+
throw new AssertionError("MIN_COMPILER_VERSION, MIN_JDK_VERSION and MAX_JDK_VERSION must be updated!");
115+
}
116+
Version truffleMajorMinorVersion = stripUpdateVersion(truffleVersion);
117+
Version compilerVersion = getCompilerVersion(compilationSupport);
118+
Version compilerMajorMinorVersion = stripUpdateVersion(compilerVersion);
119+
int jdkFeatureVersion = Runtime.version().feature();
120+
if (jdkFeatureVersion < MIN_JDK_VERSION || jdkFeatureVersion >= MAX_JDK_VERSION) {
121+
return formatVersionWarningMessage("""
122+
Your Java runtime '%s' with compiler version '%s' is incompatible with polyglot version '%s'.
123+
The Java runtime version must be greater or equal to JDK '%d' and smaller than JDK '%d'.
124+
Update your Java runtime to resolve this.
125+
""", Runtime.version(), compilerVersion, truffleVersion, MIN_JDK_VERSION, MAX_JDK_VERSION);
126+
} else if (compilerMajorMinorVersion.compareTo(truffleMajorMinorVersion) > 0) {
127+
/*
128+
* Forward compatibility is supported only for minor updates, not for major
129+
* releases.
130+
*/
131+
return formatVersionWarningMessage("""
132+
Your Java runtime '%s' with compiler version '%s' is incompatible with polyglot version '%s'.
133+
Update the org.graalvm.polyglot versions to at least '%s' to resolve this.
134+
""", Runtime.version(), compilerVersion, truffleVersion, compilerVersion);
135+
} else if (compilerVersion.compareTo(MIN_COMPILER_VERSION) < 0) {
136+
return formatVersionWarningMessage("""
137+
Your Java runtime '%s' with compiler version '%s' is incompatible with polyglot version '%s'.
138+
Update the Java runtime to the latest update release of JDK '%d'.
139+
""", Runtime.version(), compilerVersion, truffleVersion, jdkFeatureVersion);
140+
}
141+
}
142+
return null;
143+
}
144+
145+
/**
146+
* Verifies that the JarGraal compiler is compatible with the current Truffle runtime version.
147+
*
148+
* @return a string describing the incompatibility, or {@code null} if the check passes.
149+
*/
150+
public static String checkJarGraalCompilerVersion(TruffleCompilationSupport compilationSupport) {
151+
if (isVersionCheck()) {
152+
String jvmciVersionCheckError = verifyJVMCIVersion(compilationSupport.getClass());
153+
if (jvmciVersionCheckError != null) {
154+
return jvmciVersionCheckError;
155+
}
156+
Version truffleVersion = getTruffleVersion();
157+
Version truffleMajorMinorVersion = stripUpdateVersion(truffleVersion);
158+
Version compilerVersion = getCompilerVersion(compilationSupport);
159+
Version compilerMajorMinorVersion = stripUpdateVersion(compilerVersion);
160+
if (!compilerMajorMinorVersion.equals(truffleMajorMinorVersion)) {
161+
return formatVersionWarningMessage("""
162+
The Graal compiler version '%s' is incompatible with polyglot version '%s'.
163+
Update the compiler version to '%s' to resolve this.
164+
""", compilerVersion, truffleVersion, truffleVersion);
165+
}
166+
}
167+
return null;
168+
}
169+
170+
/**
171+
* Determines whether version checks are currently enabled.
172+
*/
173+
private static boolean isVersionCheck() {
174+
return !Boolean.getBoolean("polyglotimpl.DisableVersionChecks");
175+
}
176+
177+
/**
178+
* Reads reflectively the org.graalvm.truffle module version. The method uses reflection to
179+
* access the {@code PolyglotImpl#TRUFFLE_VERSION} field because the Truffle API may be of a
180+
* version earlier than graalvm-23.1.2 where the field does not exist.
181+
*
182+
* @return the Truffle API version or 23.1.1 if the {@code PolyglotImpl#TRUFFLE_VERSION} field
183+
* does not exist.
184+
*/
185+
private static Version getTruffleVersion() {
186+
try {
187+
Field versionField = PolyglotImpl.class.getDeclaredField("TRUFFLE_VERSION");
188+
versionField.setAccessible(true);
189+
return Version.parse((String) versionField.get(null));
190+
} catch (NoSuchFieldException nf) {
191+
return Version.create(23, 1, 1);
192+
} catch (ReflectiveOperationException e) {
193+
throw new InternalError(e);
194+
}
195+
}
196+
197+
/**
198+
* Retrieves the compiler version from the provided {@link TruffleCompilationSupport} instance
199+
* using reflection. If the method is unavailable, a fallback version of 23.1.1 is returned.
200+
*/
201+
private static Version getCompilerVersion(TruffleCompilationSupport compilationSupport) {
202+
/*
203+
* The TruffleCompilationSupport is present in both the maven artifact
204+
* org.graalvm.truffle/truffle-compiler and the JDK org.graalvm.truffle.compiler module. The
205+
* JDK version of TruffleCompilationSupport may be outdated and lack the getCompilerVersion
206+
* method. To address this, we use reflection.
207+
*/
208+
String compilerVersionString = null;
209+
try {
210+
Method getCompilerVersion = compilationSupport.getClass().getMethod("getCompilerVersion");
211+
compilerVersionString = (String) getCompilerVersion.invoke(compilationSupport);
212+
} catch (NoSuchMethodException noMethod) {
213+
// pass with compilerVersionString set to null
214+
} catch (ReflectiveOperationException e) {
215+
throw new InternalError(e);
216+
}
217+
return compilerVersionString != null ? Version.parse(compilerVersionString) : Version.create(23, 1, 1);
218+
}
219+
220+
/**
221+
* Triggers verification of JVMCI.
222+
*/
223+
private static String verifyJVMCIVersion(Class<?> hotspotCompilationSupport) {
224+
/*
225+
* The TruffleCompilationSupport is present in both the maven artifact
226+
* org.graalvm.truffle/truffle-compiler and the JDK org.graalvm.truffle.compiler module. The
227+
* JDK version of TruffleCompilationSupport may be outdated and lack the verifyJVMCIVersion
228+
* method. To address this, we use reflection.
229+
*/
230+
String errorMessage = null;
231+
try {
232+
Method verifyJVMCIVersion = hotspotCompilationSupport.getDeclaredMethod("verifyJVMCIVersion");
233+
errorMessage = (String) verifyJVMCIVersion.invoke(null);
234+
} catch (NoSuchMethodException noMethod) {
235+
// pass with result set to true
236+
} catch (ReflectiveOperationException e) {
237+
throw new InternalError(e);
238+
}
239+
return errorMessage;
240+
}
241+
242+
/**
243+
* Reads the version of the Truffle feature.
244+
*/
245+
private static Version getSVMFeatureVersion() {
246+
InputStream in = VersionCheckFeature.class.getClassLoader().getResourceAsStream("META-INF/graalvm/org.graalvm.truffle.runtime.svm/version");
247+
if (in == null) {
248+
throw CompilerDirectives.shouldNotReachHere("Truffle native image feature must have a version file.");
249+
}
250+
try (BufferedReader r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
251+
return Version.parse(r.readLine());
252+
} catch (IOException ioe) {
253+
throw CompilerDirectives.shouldNotReachHere(ioe);
254+
}
255+
}
256+
257+
private static Version stripUpdateVersion(Version version) {
258+
int major = version.getComponent(0);
259+
int minor = version.getComponent(1);
260+
if (major == 0 && minor == 0) {
261+
/*
262+
* Version represents a pure snapshot version without any numeric component.
263+
*/
264+
return version;
265+
} else {
266+
return Version.create(major, minor);
267+
}
268+
}
269+
270+
private static String formatVersionWarningMessage(String errorFormat, Object... args) {
271+
StringBuilder errorMessage = new StringBuilder("Version check failed.\n");
272+
errorMessage.append(String.format(errorFormat, args));
273+
errorMessage.append("""
274+
To disable this version check the '-Dpolyglotimpl.DisableVersionChecks=true' system property can be used.
275+
It is not recommended to disable version checks.
276+
""");
277+
return errorMessage.toString();
278+
}
279+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.runtime;
42+
43+
import org.graalvm.nativeimage.hosted.Feature;
44+
45+
import java.io.PrintStream;
46+
47+
public final class VersionCheckFeature implements Feature {
48+
49+
@Override
50+
public String getURL() {
51+
return "https://github.com/oracle/graal/tree/master/truffle/src/com.oracle.truffle.runtime/src/com/oracle/truffle/runtime/VersionCheckFeature.java";
52+
}
53+
54+
@Override
55+
public String getDescription() {
56+
return "Ensures compatibility between the Truffle optimized runtime and the native-image builder";
57+
}
58+
59+
@Override
60+
public void afterRegistration(AfterRegistrationAccess access) {
61+
String result = VersionCheck.checkSVMVersion();
62+
if (result != null) {
63+
// GR-67329: Exceptions thrown by features do not include their error messages in the
64+
// native-image output
65+
PrintStream out = System.err;
66+
out.printf("[%s] %s", getClass().getName(), result);
67+
throw new IllegalStateException(result);
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)