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

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を使用して複数の言語を組み合せる方法を学習します。

ポリグロット・アプリケーションの実行

次に示す各例は、基本的なポリグロット・アプリケーションの使用を開始することを目的としています。開始言語の項を選択した後、ターゲット言語のタブを選択します。

始める前に、必ずGraalVMを設定してください。

下に示す各例は、次の方法で機能します:

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 --jvm polyglot.js
42
node --polyglot --jvm 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 --jvm 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 --jvm 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 --jvm 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ビットコードにコンパイルする必要があります。ユーザーは、ビルド済のLLVMツールチェーン・サポートをインストールすることにより、GraalVMに付属しているclangを使用できます:

gu install llvm-toolchain
export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)

次を実行します:

$LLVM_TOOLCHAIN/clang polyglot.c -lgraalvm-llvm -o polyglot
lli --polyglot polyglot
42

polyglotランチャ

ポリグロット・アプリケーションでは、多くの場合、アプリケーションのプライマリ言語を決定できません。そのため、polyglotと呼ばれる試験段階の新しいランチャがGraalVMに追加されました。現時点では、このランチャは、プライマリ言語を選択しなくてもJavaScript、RubyおよびRのコードを実行します。polyglotランチャでは--polyglotオプションは必要ありません。デフォルトで有効になっています。

前述の例を使用してポリグロット・アプリケーションを実行する方法を次に示します:

polyglot --jvm polyglot.js polyglot.R polyglot.rb

ポリグロット・シェルと呼ばれる、複数言語用の試験段階の基本シェルも用意しました。これは、Truffleフレームワークで実装された言語の双方向性をすばやくテストする場合に便利です。この起動方法は次のとおりです:

polyglot --jvm --shell

オプションの言語パックをすべてGraalVMコア・インストールにインストールした場合、ポリグロット・シェルは次のようになります:

GraalVM MultiLanguage Shell 22.2.0
Copyright (c) 2013-2021, Oracle and/or its affiliates
  Java version 22.2.0
  JavaScript version 22.2.0
  Python version 3.8.5
  R version 4.0.3
  Ruby version 3.0.3
Usage:
  Use Ctrl+n to switch language and Ctrl+d to exit.
  Enter -usage to get a list of available commands.
js>

ノート: polyglotランチャおよびポリグロット・シェルは、GraalVMの試験段階の機能です。

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

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

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

それぞれの言語ランチャは、いわゆるポリグロット・オプションのセットで拡張されています。ポリグロット・オプションを使用すると、どの言語ランチャのユーザーも、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回読み取られます。それ以降に変更しても影響はありません。