ぷろじぇくと、みすじら。

ChakraCoreをビルドしてC#から使うはじめの一歩

Created at:

ついにChakraCoreが公開されたので、早速ビルドしてC#からとりあえずハローしてみます。

ChakraCore is 何

とその前にChakraCoreのおさらいです。

まずChakraCoreはInternet ExplorerやEdgeで使われているJavaScriptエンジン ChakraのWindows固有の機能を外したライブラリです。要するにV8とかJavaScriptCoreみたいなものです。

オープンソースになった後のロードマップが公開され、いろいろ書いてあります。

ChakraCoreのメリット

従来のChakraはWindowsまたはInternet Explorerと共にバージョンが管理されていました。
つまり現在、ChakraにはInternet Explorer 9, 10, 11(Windows Vista/7/2008/2008 R2)、Windows 8.1、10、10 Version 1511というバリエーションがあることになり、
それをアプリケーションに組み込んで利用したい場合にバージョンの差異が発生することになります。

そこでChakraCoreはWindowsやブラウザから分離したJavaScriptのエンジンとなったので、
アプリケーションに独立して組み込むことができるようになり、環境ごとのバージョンの差異に悩まされないで済むようになります。

そもそも従来のWindowsではWindows Scripting Hostを経由してエンジンを組み込むことができましたが、APIもCOMベースでJavaScriptのエンジンを使うということ以外を考慮していたりして使いづらく、エンジンも古いのでモダンなJavaScriptを動かすことができなかったという話もあります。

ChakraCoreをビルドする

2016年1月14日現在、WindowsサポートのみなのでGitHubのリポジトリをcloneし、BuildディレクトリにあるChakraCore.slnをVisual Studio 2015で開いてビルドすればよいです。

と、簡単そうな気がしたのですが日本語環境(CP932がデフォルト環境)ではソースコードがUnicodeじゃないよというWarning(C4819)が出て、WarningがErrorとして扱われてビルドが失敗します。幸い数は少ないのでBOM付きUTF-8で適当に保存しなおしましょう。

ビルドすると Build\VcBuild\bin\x64_debug などに ChakraCore.dll が生成されます。

ChakraCoreをC#から使う

ChakraCoreを無事ビルドできたらC#からChakraCoreを使ってみます。

ChakraCoreはJSRT API(JavaScript Runtime API)というAPIを持っているのでそれを利用してChakraCoreの機能を呼び出します。ちなみにJSRTはChakraCoreよりも以前からWindows 10以降(またはInternet Explorer 11以降)ではChakraを利用する手段として提供されています。

詳しくはMSDNのJavaScript ランタイムのホスト処理に書いてあります。

ただしJSRTは.NET Framework向けのAPIではないアンマネージドなAPIなため、P/Invokeでの呼び出しを行う必要があります。
幸いにもGitHubにChakra-Samples@Microsoftというリポジトリがあり、JSRTのC#のバインディングがすでに作られているのでこれを使います(自分で書こうとするとだいぶ大変です)。

というわけでChakra-Samplesをcloneやダウンロードしておきます。Chakra-SamplesにはHello WorldやHostingサンプルがあるのでまあそれでいいという話もあるのですが、最小限の構成を作ってみることにします。

準備

まず初めに適当にコンソールアプリケーションのプロジェクトを作ります。

プロジェクトを作ったら先ほどビルドしたChakraCore.dllをプロジェクトに追加し、プロパティでビルド時にコピーされるように設定します。

ちなみに普通にプロジェクトを作るとPrefer 32bitということで32bitプロセスとして起動されるのでx86版DLLをコピーしておきましょう。

次にChakra-SamplesからChakraCore Samples\JSRT Hosting Samples\C#\ChakraCoreHostにあるHostingフォルダをプロジェクトにコピーします。これがJSRTのバインディングです。

プロジェクト構成

ChakraCoreを実行してみる

というわけでまずはランタイム(=実行エンジン)を作り、実行用のコンテキストを作って実行に備えます。

// ランタイムを作る
JavaScriptRuntime runtime;
Native.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null, out runtime);

// 実行コンテキストを作る
var context = runtime.CreateContext();

// 現在のスレッドの実行コンテキストをセットする
Native.JsSetCurrentContext(context);

次に実際にJavaScriptのコードを実行します。

// 実行するスクリプト
var script = @"
    class Greeter {
        hello() { return 'コンニチハ!'; }
    }

    new Greeter().hello();
";

// スクリプトのソースの位置を記録するためのコンテキスト
var currentSourceContext = JavaScriptSourceContext.FromIntPtr(IntPtr.Zero);
// スクリプトを実行する
JavaScriptValue result;
Native.JsRunScript(script, currentSourceContext++, "", out result);

JsRunScriptメソッドはランタイムもコンテキストも渡さず、現在のスレッドのコンテキストから良しなに実行するので少し不思議な感じがしますね。

正常に実行できれば戻り値がresult変数に格納されます。戻り値はJavaScriptの値となっているのでそこからさらにCLRオブジェクトへ変換します。ToStringメソッドがお手軽に変換してくれるので任せましょう。

// 戻り値をJavaScriptの値からCLRのStringに変換する
// ちなみにConvertToStringメソッドはJavaScriptのStringなので注意。
var resultString = result.ToString(); // Native.JsStringToPointer + Marshal.PtrToStringUni

// 出力
Console.WriteLine(resultString);

最後はお片付けです。

// 後片付け
Native.JsSetCurrentContext(JavaScriptContext.Invalid);
runtime.Dispose(); // IDisposableなのでusingでもいい

というわけでここまでのコードをまとめるとこんな感じ。

実行すると「コンニチハ!」という出力が出てくるかと思います。割とお手軽ですね。

なお実行してBadImageFormatExceptionAn attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)と言われたらそれはx86/x64のDLLを間違えている可能性があります。

まとめ

もちろんエラーハンドリングや.NETからJavaScript側へオブジェクトの公開などもありますがとりあえず今回はここまでです。

意外と簡単に動かせるようになっているのでアプリケーションに組み込み用途にはいいかなと思うものの、ホストとChakraCoreとのやり取りが増えると割と高度なことを必要とするのでちょっと難易度高いかもという感じもします。