IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

JavaScriptCore (Safari の JavaScript エンジン) を使って C 言語から JavaScript を実行する

はじめに

JavaScriptCore (WebKit/SafariJavaScript エンジン) を C 言語に組込む方法を調べてみました。
解説はソースコード中のコメントを見てください。

まず

以下のコードを hoge.c とかいう名前で保存します。

#include <JavaScriptCore/JavaScriptCore.h>

static JSValueRef jsGlobalPrint(
    JSContextRef        ctx,
    JSObjectRef         jobj,
    JSObjectRef         jobjThis,
    size_t              argLen,
    const JSObjectRef   args[],
    JSValueRef*         jobjExp);

int main(int argc, char** argv) {

    if (argc == 1) exit(0);

    // ---- グローバルの環境の準備 ----
    // グローバル実行コンテキストというものを作る
    JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);
    // グローバル実行コンテキストが持つグローバルオブジェクトを取得する
    // ブラウザでいうところの window オブジェクトのようなもの
    JSObjectRef jobjGlobal = JSContextGetGlobalObject(ctx);

    // ---- print 関数を作る ----
    // JavaScript で扱える文字列を作る
    JSStringRef jstrPrint = JSStringCreateWithUTF8CString("print");
    // C の関数を JS の関数オブジェクトにする
    JSObjectRef jfuncPrint = JSObjectMakeFunctionWithCallback(ctx, jstrPrint, (JSObjectCallAsFunctionCallback)jsGlobalPrint);
    // グローバルオブジェクトのプロパティとして追加
    JSObjectSetProperty(ctx, jobjGlobal, jstrPrint, jfuncPrint, kJSPropertyAttributeNone, NULL);
    // 文字列のリファレンスカウンタをデクリメント
    JSStringRelease(jstrPrint);

    // JavaScript のソースを JS の文字列にする
    JSStringRef jstrSource = JSStringCreateWithUTF8CString(argv[1]);
    // 実行、 this を NULL とするとグローバルオブジェクトが this になるらしい
    JSEvaluateScript(ctx, jstrSource, NULL, NULL, 0, NULL); // 訂正: 0 ではなく 1 が正しいです。(開始行数、詳しくは次の記事を見てください><)
    // 文字列のリファレンスカウンタをデクリメント
    JSStringRelease(jstrSource);

    // 解放してもいいよ
    JSGlobalContextRelease(ctx);
    // 解放するよ
    JSGarbageCollect(ctx);
    return 0;
}

static JSValueRef jsGlobalPrint(
    JSContextRef        ctx,
    JSObjectRef         jobj,
    JSObjectRef         jobjThis,
    size_t              argLen,
    const JSObjectRef   args[],
    JSValueRef*         jobjExp) {

    if (argLen) {
        // 第一引数を JS の文字列としてコピー
        JSStringRef     jstrArg = JSValueToStringCopy(ctx, args[0], jobjExp);
        // 長さを取得
        size_t          len     = JSStringGetMaximumUTF8CStringSize(jstrArg);
        // 領域の確保
        char*           szArg   =  (char*)malloc(len);
        // 領域に UTF8 をコピー
        JSStringGetUTF8CString(jstrArg, szArg, len);
        // 出力
        puts(szArg);
        // 文字列のリファレンスカウンタをデクリメント
        JSStringRelease(jstrArg);
        // 解放
        free(szArg);
    }

    // undefined を作って返す
    return JSValueMakeUndefined(ctx);
}

コンパイル

Mac だと、以下のように -framework で JavaScriptCore を指定すればコンパイルできます。

$ gcc hoge.c -framework JavaScriptCore
$ 

簡単ですね!
JavaScriptCore って普通の Mac OS X の環境でも入ってるんだろうか><誰か教えて><
Safari が動いている = JavaScriptCore.framework が使えるという認識で良い?)
Windows や他の Unix でのやり方はわかりません><

a.out を実行

$ ./a.out "var data = {3:'さん',6:'ろく',9:'きゅう'}; for (var i = 1; i <= 10; i++) print(i%3?i:data[i])"
1
2
さん
4
5
ろく
7
8
きゅう
10
$ 

おおおおお。ちゃんと動いてますね!

以下のエントリに続きます

http://d.hatena.ne.jp/amachang/20080610/1213109852