ポリグロット・プログラミング

GraalVMでは、ユーザーは、Truffle言語実装フレームワーク(以降、Truffle)を使用して、ある言語から別の言語に値をシームレスに渡すポリグロット・アプリケーションを記述できます。

Truffleは、抽象構文ツリーを自己変更するためのインタプリタとしてプログラミング言語実装をビルドするためのJavaライブラリです。Truffleを使用して言語インタプリタを記述すると、言語のJust-in-TimeコンパイラとしてGraalコンパイラが自動的に使用されます。このフレームワークにアクセスできることにより、たとえば、RubyアプリケーションをJavaアプリケーションと同じJVMで実行できます。また、JVMベースのホスト言語とゲスト言語が、直接相互運用し、同じメモリー領域でデータを受け渡すこともできます。

Truffleを使用して実装された言語で他言語のポリグロット値を提供するために、いわゆるポリグロット相互運用性プロトコルが開発されました。この相互運用性プロトコルは、すべての言語が実装し、他言語のポリグロット値に使用する一連の標準化されたメッセージで構成されています。このプロトコルにより、GraalVMは、それぞれの言語が相互を認識していなくても、あらゆる組合せの言語間の相互運用性をサポートできます。詳細は、『High-Performance Cross-Language Interoperability in a Multi-Language Runtime』を参照してください。

この項全体にわたって、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

ポリグロット・オプション

スループットまたは起動が向上するように言語エンジンを構成できます。

言語ランチャへのオプションの受渡し

それぞれの言語ランチャは、いわゆるポリグロット・オプションのセットで拡張されています。ポリグロット・オプションを使用すると、どの言語ランチャのユーザーも、GraalVMでサポートされている(Truffle言語実装フレームワークを使用して実装された)他の言語のオプションにアクセスできます。形式は--<languageID>.<property>=<value>です。たとえば、Rランチャは、--js.atomics=true JavaScriptオプションもサポートします。

languageIDに使用できる値は次のとおりです:

使用可能なオプションを確認するには、--help:languagesを使用します。

ポリグロット・ツールのオプションも、--<toolID>.<property>=<value>という形式で同様に機能します。

<toolID>に使用できる値は次のとおりです:

使用可能なオプションを確認するには、--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

ノート: システム・プロパティは、ポリグロット・コンテキストの作成時に1回読み取られます。それ以降に変更しても影響はありません。