多語言程式設計

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 會收集最大量的設定檔資訊,並使用最大數量的最佳化來編譯。此模式會導致應用程式啟動速度較慢,但輸送量較佳。如果未另外指定,此模式會使用編譯器組態 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 開發人員工具進行偵錯
  • 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

注意:建立多語言內容時,系統屬性會讀取一次。後續變更沒有任何作用。

與我們聯繫