多语言编程

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 代码必须使用诸如 clang 之类的 LLVM 前端编译为 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(吞吐量)模式会收集最大量的性能分析信息,并使用最大数量的优化进行编译。此模式会导致应用程序启动较慢但吞吐量更高。除非另有指定,否则此模式使用 communityenterprise 编译器配置。
    • default(默认)模式使用均衡的引擎配置。除非另有指定,否则此模式使用 communityenterprise 编译器配置。
    • 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 DevTools 进行调试
  • 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

注意:系统属性在多语言上下文创建时读取一次。随后的更改无效。

联系我们