Value の扱い方¶
YanmaJS では JavaScript の値をすべて Value という型で表現します。このページでは Value の作り方・変換・型判定について説明します。
Value とは¶
Value は NaN-boxing によって実装された opaque な型で、実体は u64 です。数値・真偽値・null/undefined・オブジェクトへのポインタなどをすべて 1 語(8 バイト)に詰め込んでいます。
u64 であるため、Zig の == でそのまま比較できます(ただしオブジェクトの場合は「同一の値かどうか」の比較になり、JavaScript の === と同じ意味にはなるものの、NaN同士の比較などプリミティブの扱いには注意してください)。
const a = yanmajs.makeInt(ctx, 42);
const b = yanmajs.makeInt(ctx, 42);
// a == b は true(小さい整数は同じビットパターンにエンコードされる)
多くの make* 系関数はヒープ上にオブジェクトを確保するため、GC のルートに繋がっていない状態で返ってきます。すぐに setGlobal/setProperty/setIndex などでルートへアタッチするか、Context.protect/HandleScope.pin で保護してください。
Value を作る¶
| 関数 | 説明 |
|---|---|
yanmajs.makeString(ctx, s: []const u8) !Value |
UTF-8 文字列から JavaScript の string を作成 |
yanmajs.makeInt(ctx, v: i64) Value |
整数値を作成(i32 の範囲外は内部的に float 表現になります) |
yanmajs.makeFloat(v: f64) Value |
浮動小数点数を作成 |
yanmajs.makeBool(v: bool) Value |
真偽値を作成 |
yanmajs.makeNull() Value |
null を作成 |
yanmajs.makeUndefined() Value |
undefined を作成 |
yanmajs.makeSymbol(ctx, description: []const u8) !Value |
Symbol を作成 |
yanmajs.makeBigInt(ctx, v: i64) !Value |
BigInt を作成 |
const s = try yanmajs.makeString(ctx, "hello");
const n = yanmajs.makeInt(ctx, 42);
const f = yanmajs.makeFloat(3.14);
const b = yanmajs.makeBool(true);
const nul = yanmajs.makeNull();
const undef = yanmajs.makeUndefined();
const sym = try yanmajs.makeSymbol(ctx, "mySymbol");
const bi = try yanmajs.makeBigInt(ctx, 42);
makeString と astral 文字の正規化¶
JavaScript の文字列は仕様上 UTF-16 のコード単位列として扱われます。makeString はホストから渡された UTF-8 文字列を受け取りますが、U+10000 以上の astral 文字(4 バイトの UTF-8 シーケンス)が含まれる場合は自動的に CESU-8(サロゲートペアをそれぞれ 3 バイトの UTF-8 として符号化した形式)へ正規化します。これにより、astral 文字を含む文字列でも .length やインデックスアクセスが仕様どおり(UTF-16 コード単位ベース)の挙動になります。
// "😀" は astral 文字(U+1F600)。内部的には CESU-8 = サロゲートペアとして
// 格納され、JS 側から見た .length は 2 になる(UTF-16 の仕様どおり)。
const s = try yanmajs.makeString(ctx, "😀");
try ctx.setGlobal("s", s);
const len = try ctx.eval("s.length"); // 2
Value を変換する¶
| 関数 | 説明 |
|---|---|
yanmajs.toString(ctx, v: Value) ![]const u8 |
文字列表現に変換。呼び出し側が解放(free)する責任を持つ |
yanmajs.toInt(v: Value) i64 |
整数に変換 |
yanmajs.toFloat(v: Value) f64 |
浮動小数点数に変換 |
yanmajs.toBool(v: Value) bool |
真偽値に変換(truthy/falsy 判定) |
yanmajs.toBigInt(v: Value) ?i64 |
BigInt の場合のみ値を取り出す。BigInt でなければ null |
const result = try ctx.eval("'hello' + ' world'");
const s = try yanmajs.toString(ctx, result);
defer ctx.allocator.free(s); // 忘れずに解放する
toString はオブジェクトが文字列型の stack own プロパティを持つ場合はその値をそのまま返し、なければ仕様準拠の ToString にフォールバックします。エンジン生成エラーの stack は "<name>: <message>" に続けて呼び出しフレームごとの行を含む複数行の文字列になります。
型を判定する¶
利用可能な判定関数は以下のとおりです。
isNull,isUndefined,isBool,isNumber,isStringisArray,isObject,isFunctionisPromise,isMap,isSet,isDate,isRegexisTypedArray,isDataView,isArrayBufferisGenerator,isProxy,isWeakRefisSymbol,isBigInt
JSON¶
JSON.parse/JSON.stringify 相当の機能をホスト側から直接呼び出せます。内部的にはグローバルの JSON オブジェクトを経由して実装されています。
const val = try yanmajs.parseJSON(ctx, "{\"x\": 42}");
try std.testing.expect(yanmajs.isObject(val));
const json = try yanmajs.stringifyJSON(ctx, val);
defer ctx.allocator.free(json); // stringifyJSON も呼び出し側が解放する
オブジェクト・配列の読み取り¶
Value がオブジェクト/配列の場合、以下のヘルパーでプロパティや要素に直接アクセスできます(いずれも対象外の値には undefined/0 を返す安全な実装です)。
const obj = ctx.getGlobal("obj");
const x = yanmajs.getProperty(obj, "x");
const arr = ctx.getGlobal("arr");
const len = yanmajs.getLength(arr);
const first = yanmajs.getIndex(arr, 0);
オブジェクト・配列の新規作成や書き込みについては オブジェクトと配列 の makeObject/makeArray/setProperty/setIndex を参照してください。