- 适用于 JDK 23 的 GraalVM(最新版本)
- 适用于 JDK 24 的 GraalVM(抢先体验版)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发版本
多语言编程
GraalVM 允许用户编写多语言应用程序,这些应用程序通过 Truffle 语言实现框架(以下简称“Truffle”)无缝地将值从一种语言传递到另一种语言。
Truffle 是一个 Java 库,用于构建编程语言实现,作为自修改抽象语法树的解释器。使用 Truffle 编写语言解释器时,它将自动使用 Graal 编译器作为该语言的即时编译器。通过访问此框架,例如 Ruby 应用程序可以在与 Java 应用程序相同的 JVM 上运行。此外,主机 JVM 基语言和客语言可以彼此直接交互,并在同一内存空间中来回传递数据。
为了在使用 Truffle 实现的语言中提供外来多语言值,已经开发了所谓的“多语言互操作协议”。此互操作协议包含一组标准化消息,每种语言都实现并使用这些消息来处理外来多语言值。该协议允许 GraalVM 支持任何语言组合之间的互操作性,而无需它们相互了解。有关更多详细信息,请参阅 多语言运行时中的高性能跨语言互操作性 论文。
在本节中,您将学习如何使用 GraalVM 多语言 API 组合多种语言。
运行多语言应用程序 #
以下示例旨在帮助您开始使用基本的 多语言应用程序。选择您“开始语言”的章节,然后选择您“目标语言”的选项卡。
以下示例预计在 JVM 或本地独立发行版中都能正常运行。对于使用 Java 作为目标语言且访问 Java 数组以外的类的本地启动器和本地可执行文件,需要重新编译映像并提供 反射配置文件。
注意:要使用 LLVM 作为目标语言启动应用程序,请确保预先编译以下提供的“polyglot.c”文件。
从 JavaScript / Node.js 开始 #
创建“polyglot.js”文件
// BEGIN-SNIPPET
var array = Polyglot.eval("R", "c(1,2,42,4)")
console.log(array[2]);
// END-SNIPPET
// BEGIN-SNIPPET
var array = Polyglot.eval("ruby", "[1,2,42,4]")
console.log(array[2]);
// END-SNIPPET
// BEGIN-SNIPPET
var array = Polyglot.eval("python", "[1,2,42,4]")
console.log(array[2]);
// END-SNIPPET
// BEGIN-SNIPPET
var array = new (Java.type("int[]"))(4);
array[2] = 42;
console.log(array[2])
// END-SNIPPET
// BEGIN-SNIPPET
var cpart = Polyglot.evalFile("llvm", "polyglot");
cpart.main()
// END-SNIPPET
运行
js polyglot.js
42
node polyglot.js
42
开始语言 R #
创建“polyglot.R”文件
# BEGIN-SNIPPET
array <- eval.polyglot("js", "[1,2,42,4]")
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
array <- eval.polyglot("ruby", "[1,2,42,4]")
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
array <- eval.polyglot("python", "[1,2,42,4]")
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
array <- new("int[]", 4)
array[3L] <- 42
print(array[3L])
# END-SNIPPET
# BEGIN-SNIPPET
cpart <- eval.polyglot("llvm", path="polyglot")
cpart$main()
# END-SNIPPET
运行
Rscript polyglot.R
[1] 42
开始语言 Ruby #
创建“polyglot.rb”文件
# BEGIN-SNIPPET
array = Polyglot.eval('js', '[1,2,42,4]')
puts array[2]
# END-SNIPPET
# BEGIN-SNIPPET
array = Polyglot.eval('R', 'c(1L,2L,42L,4L)')
puts array[2]
# END-SNIPPET
# BEGIN-SNIPPET
array = Polyglot.eval('python', '[1,2,42,4]')
puts array[2]
# END-SNIPPET
# BEGIN-SNIPPET
array = Java.type('int[]').new(4)
array[2] = 42
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
cpart = Polyglot.eval_file('llvm', 'polyglot')
cpart.main()
# END-SNIPPET
运行
ruby polyglot.rb
42
开始语言 Python #
创建“polyglot.py”文件
# BEGIN-SNIPPET
import polyglot
array = polyglot.eval(language="js", string="[1,2,42,4]")
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import polyglot
array = polyglot.eval(language="R", string="c(1L,2L,42L,4L)")
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import polyglot
array = polyglot.eval(language="ruby", string="[1,2,42,4]")
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import java
array = java.type("int[]")(4)
array[2] = 42
print(array[2])
# END-SNIPPET
# BEGIN-SNIPPET
import polyglot
cpart = polyglot.eval(language="llvm", path="polyglot")
cpart.main()
# END-SNIPPET
运行
graalpy polyglot.py
42
开始语言 Java #
创建“Polyglot.java”文件
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context polyglot = Context.create();
Value array = polyglot.eval("js", "[1,2,42,4]");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context polyglot = Context.newBuilder().
allowAllAccess(true).build();
Value array = polyglot.eval("R", "c(1,2,42,4)");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context polyglot = Context.newBuilder().
allowAllAccess(true).build();
Value array = polyglot.eval("ruby", "[1,2,42,4]");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) {
Context context = Context.newBuilder().allowIO(true).build();
Value array = context.eval("python", "[1,2,42,4]");
int result = array.getArrayElement(2).asInt();
System.out.println(result);
}
}
// END-SNIPPET
// BEGIN-SNIPPET
import java.io.*;
import org.graalvm.polyglot.*;
class Polyglot {
public static void main(String[] args) throws IOException {
Context polyglot = Context.newBuilder().
allowAllAccess(true).build();
File file = new File("polyglot");
Source source = Source.newBuilder("llvm", file).build();
Value cpart = polyglot.eval(source);
cpart.execute();
}
}
// END-SNIPPET
运行
javac Polyglot.java
java Polyglot
42
开始语言 C #
创建“polyglot.c”文件
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("js", "[1,2,42,4]");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("R", "c(1,2,42,4)");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("ruby", "[1,2,42,4]");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *array = polyglot_eval("python", "[1,2,42,4]");
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
// BEGIN-SNIPPET
#include <stdio.h>
#include <graalvm/llvm/polyglot.h>
int main() {
void *arrayType = polyglot_java_type("int[]");
void *array = polyglot_new_instance(arrayType, 4);
polyglot_set_array_element(array, 2, 42);
int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
printf("%d\n", element);
return element;
}
// END-SNIPPET
示例 C 代码必须使用 LLVM 前端(如“clang”)编译为 LLVM 位代码。用户可以使用来自预构建的 与 GraalVM LLVM 运行时一起提供的 LLVM 工具链 的“clang”。
export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)
运行
$LLVM_TOOLCHAIN/clang polyglot.c -lgraalvm-llvm -o polyglot
lli polyglot
42
多语言选项 #
您可以配置语言引擎以提高吞吐量或启动速度。
- “
--engine.Mode=default
”配置引擎的执行模式。执行模式会自动调整多语言引擎以适应延迟或吞吐量。- “
throughput
”会收集最大数量的分析信息,并使用最大数量的优化进行编译。此模式会导致应用程序启动速度变慢,但吞吐量更高。如果未另行指定,此模式将使用“community
”或“enterprise
”编译器配置。 - “
default
”使用平衡的引擎配置。如果未另行指定,此模式将使用“community
”或“enterprise
”编译器配置。 - “
latency
”仅收集最少的分析信息,并尽可能快地编译,但生成的代码优化程度较低。此模式会导致应用程序启动速度更快,但吞吐量不太理想。如果未另行指定,此模式将使用“economy
”编译器配置。
- “
传递选项到语言启动器 #
每个语言启动器都已扩展了一组所谓的“多语言选项”。多语言选项允许任何语言启动器的用户访问 GraalVM 支持的其他语言的选项(使用 Truffle 语言实现框架实现)。格式为:--<languageID>.<property>=<value>
。例如,“R”启动器还支持“--js.atomics=true
”JavaScript 选项。
“languageID
”的允许值为
- “
js
”:JavaScript 选项 - “
python
”:Python 选项 - “
r
”:R 选项 - “
ruby
”:Ruby 选项 - “
llvm
”:LLVM 选项
使用“--help:languages
”查找可用的选项。
多语言工具的选项以相同的方式工作,格式为:--<toolID>.<property>=<value>
。
“<toolID>
”的允许值为
- “
inspect
”:允许使用 Chrome 开发者工具进行调试 - “
cpusampler
”:收集有关 CPU 使用情况的数据 - “
cputracer
”:捕获有关 CPU 使用情况的跟踪信息 - “
memtracer
”:捕获有关内存使用情况的跟踪信息
使用“--help:tools
”查找可用的选项。
以编程方式传递选项 #
也可以使用 Java 多语言 API 以编程方式传递选项。
创建一个名为“OptionsTest.java”的文件
import org.graalvm.polyglot.*;
class OptionsTest {
public static void main(String[] args) {
Context polyglot = Context.newBuilder()
.allowExperimentalOptions(true)
.option("js.shared-array-buffer", "true")
.build();
// the use of shared array buffer requires the 'js.shared-array-buffer' option to be 'true'
polyglot.eval("js", "new SharedArrayBuffer(1024)");
}
}
运行
javac OptionsTest.java
java OptionsTest
注意:工具选项可以以相同的方式传递。在创建上下文后无法修改选项。
使用 JVM 参数传递选项 #
每个多语言选项也可以作为 Java 系统属性传递。每个可用选项都会转换为带有“polyglot.
”前缀的系统属性。例如,“-Dpolyglot.js.strict=true
”将为在 JVM 中运行的所有 JavaScript 代码设置严格解释的默认值。以编程方式设置的选项优先于 Java 系统属性。对于语言,可以使用以下格式:-Dpolyglot.<languageID>.<property>=<value>
,而对于工具,可以使用:-Dpolyglot.<toolID>.<property>=<value>
。
创建一个名为“SystemPropertiesTest.java”的文件
import org.graalvm.polyglot.*;
class SystemPropertiesTest {
public static void main(String[] args) {
Context polyglot = Context.newBuilder()
.allowExperimentalOptions(true)
.build();
// the use of shared array buffer requires the 'js.shared-array-buffer' option to be 'true'
polyglot.eval("js", "new SharedArrayBuffer(1024)");
}
}
运行
javac SystemPropertiesTest.java
java -Dpolyglot.js.strict=true SystemPropertiesTest
注意:在创建多语言上下文时会读取一次系统属性。后续更改无效。
相关文档 #
- 从 嵌入语言文档 中了解更多关于客语言和 Java 主机语言互操作性的信息