多语言编程

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 主机语言互操作性的信息

与我们联系