Insightのアプリケーションへの組込み

InsightのJavaへの組込み

GraalVM言語(Truffleフレームワークで実装された言語。つまり、JavaScript、Python、Ruby、R)は、ポリグロット・コンテキストAPIを介してカスタムJavaアプリケーションに組み込むことができます。GraalVM Insightも、同じAPIを介して制御できます。たとえば:

final Engine engine = context.getEngine();
Instrument instrument = engine.getInstruments().get("insight");
Function<Source, AutoCloseable> access = instrument.lookup(Function.class);
AutoCloseable handle = access.apply(agentSrc);

ContextEngineを取得し、insightインストゥルメントを要求します。次に、GraalVM Insightスクリプトを使用してSourceを作成し、インストゥルメンテーション・ハンドルの取得時に適用します。不要になったときにすべてのスクリプトのインストゥルメンテーションを無効にするには、handle.close()を使用します。

内部スクリプトを無視

多くの場合、動的言語で記述された特定のコードを特権コードとして扱う必要があります。OSの概念やアプリケーションのその他の機能への様々なバインディングを想像してみましょう。このようなスクリプトは、ブラックボックス化したままで、GraalVM Insightのインストゥルメンテーション機能から非表示にしておくことをお薦めします。

特権スクリプトを非表示にするには、内部としてマークします。デフォルトでは、GraalVM Insightでは内部スクリプトは無視され、処理されません。

Insightスクリプトの機能の拡張

GraalVM InsightをJavaアプリケーションに組み込む場合、評価対象のInsightスクリプトで追加のオブジェクトを使用できるようになります。たとえば:

@TruffleInstrument.Registration(
    id = "meaningOfWorld", name = "Meaning Of World", version = "demo",
    services = { Insight.SymbolProvider.class }
)
public final class MeaningOfWorldInstrument extends TruffleInstrument {
    @Override
    protected void onCreate(Env env) {
        Map<String, Integer> symbols = Collections.singletonMap("meaning", 42);
        Insight.SymbolProvider provider = () -> symbols;
        env.registerService(provider);
    }
}

前のJavaコードでは、評価されるすべてのInsightスクリプトに新しいシンボルmeaningを登録するインストゥルメントが作成されます。次に、各スクリプトでこれを参照し、たとえばメソッド呼出し回数を制限するために使用できます:

insight.on('enter', (ctx, frames) => { if (--meaning <= 0) throw 'Stop!' }, { roots : true });

単純な値および複雑なオブジェクトを公開できます。詳細は、javadocを参照してください。インストゥルメンテーションは、プログラム実行の多くの側面を変更することが可能で、セキュリティ・サンドボックスの対象にはなりません。

InsightのNode.jsへの組込み

Insightのマニュアルでは、nodeでGraalVM Insightを使用する例を多く示しています。ただし、そのほとんどはコマンドライン・オプション--insightに依存しており、ツールの動的な性質による利点はありません。次の例は、管理サーバーを作成する方法を示しています。

このコードをadminserver.jsに保存します:

function initialize(insight, require) {
    const http = require("http");
    const srv = http.createServer((req, res) => {
        let method = req.method;
        if (method === 'POST') {
            var data = '';
            req.on('data', (chunk) => {
                data += chunk.toString();
            });
            req.on('end', () => {
                const fn = new Function('insight', data);
                try {
                    fn(insight);
                    res.write('GraalVM Insight hook activated\n');
                } finally {
                    res.end();
                }
            });
        }
    });
    srv.listen(9999, () => console.log("Admin ready at 9999"));
}


let waitForRequire = function (event) {
  if (typeof process === 'object' && process.mainModule && process.mainModule.require) {
    insight.off('source', waitForRequire);
    initialize(insight, process.mainModule.require.bind(process.mainModule));
  }
};

insight.on('source', waitForRequire, { roots: true });

プログラムは、ポート9999でHTTPサーバーを開き、後でいつでも適用される受信スクリプトをリスニングします。アプリケーションを起動します:

node --insight=adminserver.js yourapp.js
Admin ready at 9999

実行中に、管理ポートに接続します。任意のGraalVM Insightスクリプトを送信します。たとえば、次のスクリプトは、process.exitをコールするユーザーを確認します:

curl --data \
'insight.on("enter", (ctx, frame) => { console.log(new Error("call to exit").stack); }, \
{ roots: true, rootNameFilter: "exit" });' \
-X POST http://localhost:9999/

独自のadminserver.jsを記述する場合は、セキュリティに注意してください。アプリケーションに任意のフックを適用できるのは、認可されたユーザーのみです。管理サーバー・ポートを誰にでも開かないでください。

次に読むもの

Insightの詳細および使用例については、Insightのマニュアルを参照してください。これは、必須のHelloWorldの例から始まり、より難しいタスクのデモを行います。