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

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

Truffleは、自己変更抽象構文木のインタプリタとしてプログラミング言語の実装を構築するためのJavaライブラリです。Truffleで言語インタプリタを作成すると、言語のジャストインタイムコンパイラとしてGraalコンパイラが自動的に使用されます。このフレームワークにアクセスすることにより、たとえば、RubyアプリケーションはJavaアプリケーションと同じJVMで実行できます。また、ホストJVMベースの言語とゲスト言語は、同じメモリスペースで直接相互運用してデータをやり取りできます。

Truffleで実装された言語で外部ポリグロット値を提供するために、いわゆるポリグロット相互運用プロトコルが開発されました。この相互運用プロトコルは、すべての言語が実装し、外部ポリグロット値に使用する標準化されたメッセージのセットで構成されています。このプロトコルにより、GraalVMは、言語が互いを認識する必要なしに、任意の言語の組み合わせ間の相互運用性をサポートできます。詳細については、「マルチランタイムにおける高性能クロス言語相互運用性」の論文を参照してください。

このセクションでは、GraalVM Polyglot 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は、最大量のプロファイリング情報を収集し、最大の最適化数を使用してコンパイルします。このモードでは、アプリケーションの起動が遅くなりますが、スループットが向上します。このモードでは、特に指定されていない限り、コンパイラ構成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 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

注:システムプロパティは、ポリグロットコンテキストの作成時に1回読み取られます。後続の変更は影響しません。

お問い合わせください