Truffle言語をGraalVMで動かす

Truffleを使って実装した言語をGraalVMで動かす方法、なかなか難しい。
まとめると、言語実装のクラスパスはtruffle.class.path.appendで追加して、言語利用側は普通のクラスパス、ということでした。


ということで、言語実装をlangに、起動プログラムをlauncherに置く感じにします。

sample
├lang
│  └TruffleSample.java
└launcher
    └Launcher.java


言語実装はこんな感じに。指定した文字列にhelloを付け加えるだけの言語です。

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.RootNode;
import org.graalvm.polyglot.Context;

@TruffleLanguage.Registration(name = "Minimum", id = "mini",
        defaultMimeType = TruffleSample.MIME, characterMimeTypes = TruffleSample.MIME)
public class TruffleSample extends TruffleLanguage<Object>{
    static final String MIME = "application/x-mini";

    @Override
    protected CallTarget parse(ParsingRequest request) throws Exception {
        String text = request.getSource().getCharacters().toString();
        return Truffle.getRuntime().createCallTarget(new RootNode(this){
            @Override
            public Object execute(VirtualFrame frame) {
                return "Hello " + text;
            }
        });
    }
    
    @Override
    protected Object createContext(Env env) {
        return new Object();
    }

    @Override
    protected boolean isObjectOfLanguage(Object object) {
        return false;
    }
}


こんな感じでコンパイル。$GHにGraalVMのパスが設定されてるとします。truffle-api.jarとtruffle-dsl-processor.jarをクラスパスに追加する必要があります。通常のJDKコンパイルする場合にはgraal-sdk.jarも必要になりますが、GraalVMには含まれているので不要です。

sample$ cd lang
lang$ $GH/bin/javac -cp $GH/jre/lib/truffle/truffle-api.jar:$GH/jre/lib/truffle/truffle-dsl-processor.jar TruffleSample.java


そうするとlanguageというファイルもできています。

lang$ cat language
#Generated by com.oracle.truffle.dsl.processor.LanguageRegistrationProcessor
#Fri Jan 04 15:43:28 GMT 2019
language1.characterMimeType.0=application/x-mini
language1.className=TruffleSample
language1.defaultMimeType=application/x-mini
language1.id=mini
language1.implementationName=
language1.interactive=true
language1.internal=false
language1.name=Minimum
language1.version=inherit


このファイルはMETA-INF/truffleに入ってる必要があります。mavenでjarを作ると適切なフォルダに作成されるのですが、javacでコンパイルするとそうならないので、自力で移動します。

lang$ mkdir META-INF
lang$ mkdir META-INF/truffle
lang$ mv language META-INF/truffle


起動用のプログラムはこんな感じ。

import org.graalvm.polyglot.Context;
public class Launcher {
    public static void main(String... args) {
        Context ctx = Context.create("mini");
        System.out.println(ctx.eval("mini", "test"));
    }
}


org.graalvm.polyglotパッケージのあるgraal-sdk.jarはGraalVMに含まれてるので、クラスパスを追加する必要はありません。

lang$ cd ../launcher
launcher$ $GH/bin/javac Launcher.java


そしたら上のフォルダに移動して実行。-Dtruffle.class.path.appendで言語のパス、-cpで起動プログラムのパスを指定しています。

launcher$ cd ..
sample$ $GH/bin/java -Dtruffle.class.path.append=lang -cp launcher Launcher
Hello test


動きました。