Skip to content

error

YanmaJS のエラー処理は、Zig の error union による制御フローと、JavaScript 例外値を保持するpending exceptionの2系統を組み合わせて扱います。


設計方針

  • スクリプト評価系の API(eval/runScript/evalModule/callFunction)は Zig の EvalError!Value を返します。JavaScript 側で例外が投げられ、捕捉されずにトップレベルまで伝播した場合は error.UncaughtException になります
  • 例外の中身(JavaScript の Value としての例外オブジェクト)は戻り値のエラーセットとは別に、Context.getPendingException() で取得します
  • ホスト関数(HostFn/HostMethod)が Zig のエラーを返した場合は、自動的に JavaScript の Error オブジェクトに変換されて投げられます

EvalError

pub const EvalError = error{
    OutOfMemory,
    StackOverflow,
    ArityMismatch,
    CallStackOverflow,
    TryStackOverflow,
    UncaughtException,
    JumpTooLarge,
    NotCallable,
    RuntimeError,
};
バリアント 発生条件
OutOfMemory アロケータからのメモリ確保に失敗した(initWithMemoryLimit で設定したメモリ上限超過を含む)
StackOverflow 値スタック(VM の評価スタック)が上限を超えた
ArityMismatch 関数呼び出しの引数数がバイトコード上の期待値と一致しない(内部的な整合性エラー)
CallStackOverflow 関数呼び出しのネストが深すぎる(無限再帰など)
TryStackOverflow try/catch/finally のネストが深すぎる
UncaughtException JavaScript コードが投げた例外が捕捉されずトップレベルまで到達した。Context.getPendingException() で例外値を取得できる
JumpTooLarge バイトコードの分岐オフセットが表現範囲を超えた(内部的な整合性エラー、通常は発生しない)
NotCallable 予約バリアント。現状はどの経路からも返されない(呼び出し不能な値の呼び出しは TypeError "value is not a function" を pending exception にセットした RuntimeError になる)
RuntimeError 上記以外の実行時エラー全般。Context.requestInterrupt() による割り込みもここに含まれる

eval/compileScript/runScript/evalModule/callFunction はいずれも内部的に anyerrormapEvalErrorEvalError へ写像して返します。mapEvalError にないバリアントは RuntimeError に丸められます。


Pending Exception パターン

UncaughtException(または割り込みによる RuntimeError)が返った際、実際の JavaScript 例外オブジェクトはエラー値そのものではなく Context 内部の pending_throw スロットに格納されます。

Context.getPendingException(ctx: *Context) ?Value

呼び出すと保留中の例外値を返し、同時にスロットをクリアします(一度取り出すと null になります)。典型的な使い方は次の通りです。

const result = ctx.eval("throw new TypeError('bad')");
if (result) |_| {} else |err| {
    if (err == error.UncaughtException) {
        const ex = ctx.getPendingException().?;
        const msg = try yanmajs.toString(ctx, ex);
        defer ctx.allocator.free(msg);
        std.debug.print("uncaught: {s}\n", .{msg}); // "TypeError: bad"
    }
}

Context.throwValue/throwError/throwTypeError などでホスト側から例外を設定した場合も同じスロットに書き込まれるため、同じ API で取得できます。


throw 系メソッド

HostFn/HostMethod の実装内、あるいはホスト API 呼び出しの中で JavaScript 例外を発生させるためのメソッド群です。いずれも Context の pending exception スロットに例外値を設定するだけで、呼び出し自体は即座に返ります(Zig の制御フローを中断しません)。ホスト関数側で例外を投げた後は、通常そのまま return して呼び出し元(VM)に処理を戻します。

Context.throwValue(ctx: *Context, val: Value) void
Context.throwError(ctx: *Context, name: []const u8, message: []const u8) void
Context.throwTypeError(ctx: *Context, message: []const u8) void
Context.throwRangeError(ctx: *Context, message: []const u8) void
Context.throwReferenceError(ctx: *Context, message: []const u8) void
Context.throwSyntaxError(ctx: *Context, message: []const u8) void
メソッド 説明
throwValue 任意の Value をそのまま例外値として設定する。エラーオブジェクト以外(文字列や数値など)を投げたい場合に使う
throwError name(コンストラクタ名。"TypeError" や独自の "CustomError" など)と message から Error 系オブジェクトを生成して設定する
throwTypeError / throwRangeError / throwReferenceError / throwSyntaxError それぞれ対応する組み込みエラー型で throwError を呼ぶ便利ラッパー

例外オブジェクトの生成自体に失敗した場合(メモリ確保失敗など)、throwError 系は静かに何もしません(pending exception は設定されないまま)。


HostFn / HostMethod のエラー変換規則

HostFn/HostMethod はいずれも anyerror!Value を返します。VM がこれをディスパッチする際の規則は次の通りです。

  • 戻り値が成功(Value を返す)であれば、そのままスクリプト側の呼び出し結果になる
  • 戻り値がエラーの場合、@errorName(err)(Zig のエラー名文字列。例: "OutOfMemory")を message として Error(name = "Error")オブジェクトを生成し、pending exception に設定した上で undefined を返す
  • ホスト関数内で既に Context.throwTypeError などによって pending exception を設定していても、関数自体が Zig のエラーで抜けた場合は上記の Error上書きされる点に注意してください。特定の種類の例外(TypeError など)を投げたい場合は、Zig のエラーを返すのではなく ctx.throwXxx(...) を呼んだ上で通常の戻り値(例えば makeUndefined())を返してください
const validateFn: HostFn = struct {
    fn call(c: *Context, args: []const Value) !Value {
        if (args.len == 0 or !yanmajs.isNumber(args[0])) {
            c.throwTypeError("argument must be a number"); // 具体的なエラー型を明示
            return yanmajs.makeUndefined(); // Zig のエラーを返さない
        }
        return args[0];
    }
}.call;

上記のように「ctx.throwXxx を呼んでから正常値として返す」パターンが、狙った種類の JavaScript 例外を投げる正しい方法です。単に return error.SomeZigError; すると "Error: SomeZigError" という汎用エラーになります。