Skip to main content

atmos/os_lib/js/
builtins.rs

1//! JS 標準ライブラリ(実用コア)。
2//!
3//! グローバル関数・console・Math・JSON と、文字列/配列/オブジェクトのメソッドを
4//! ネイティブ関数として提供する。メソッドは `this`(レシーバ)を受け取る。
5
6use alloc::format;
7use alloc::rc::Rc;
8use alloc::string::String;
9use alloc::string::ToString;
10use alloc::vec;
11use alloc::vec::Vec;
12use core::cell::RefCell;
13use core::sync::atomic::{AtomicU64, Ordering};
14
15use super::interp::{Interp, Scope};
16use super::value::*;
17
18/// `window.` 経由でも読めるようにするグローバル名。
19///
20/// ブラウザではグローバルと `window` のプロパティは同じ物を指す。
21/// この処理系は別々に持っているので、写して差を埋める。
22const WINDOW_MIRRORED_GLOBALS: &[&str] = &[
23    "navigator", "screen", "location", "document", "history",
24    "localStorage", "sessionStorage", "performance", "console",
25    "JSON", "Math", "Date", "Promise", "Array", "Object", "String",
26    "Number", "Boolean", "RegExp", "Error", "Map", "Set", "Symbol",
27    "XMLHttpRequest", "URL", "URLSearchParams",
28];
29
30/// `Array.prototype` に並べるメソッド名。
31///
32/// 実体は `props_methods::object_get` が返す同じ関数を指す。
33const ARRAY_PROTO_METHODS: &[&str] = &[
34    "push", "pop", "shift", "unshift", "slice", "splice", "concat",
35    "join", "reverse", "sort", "indexOf", "lastIndexOf", "includes",
36    "find", "findIndex", "findLast", "findLastIndex", "filter", "map",
37    "forEach", "reduce", "reduceRight", "some", "every", "flat",
38    "flatMap", "fill", "copyWithin", "at", "keys", "values", "entries",
39    "toString",
40];
41
42/// グローバルスコープへ組込みを登録。
43/// `String.prototype` に並べるメソッド名。
44///
45/// 実体は `props_methods::string_get` が返す同じ関数を指す。
46/// ここに無いものはプロトタイプ経由では読めないが、
47/// 実体(`"x".foo()`)からは従来どおり解決できる。
48const STRING_PROTO_METHODS: &[&str] = &[
49    "charAt", "charCodeAt", "codePointAt", "localeCompare", "indexOf",
50    "lastIndexOf", "includes", "startsWith", "endsWith", "slice",
51    "substring", "substr", "toUpperCase", "toLowerCase", "trim",
52    "trimStart", "trimEnd", "split", "replace", "replaceAll", "match",
53    "matchAll", "search", "repeat", "concat", "padStart", "padEnd",
54    "at", "toString", "valueOf", "normalize",
55];
56
57pub fn install(global: &Rc<RefCell<Scope>>) {
58    let mut g = global.borrow_mut();
59
60    // console
61    let console = Obj::plain();
62    {
63        let mut c = console.borrow_mut();
64        c.props.insert("log".into(), nv("console.log", console_log));
65        c.props
66            .insert("error".into(), nv("console.error", console_log));
67        c.props
68            .insert("warn".into(), nv("console.warn", console_log));
69        c.props
70            .insert("info".into(), nv("console.info", console_log));
71        c.props
72            .insert("debug".into(), nv("console.debug", console_log));
73        c.props
74            .insert("trace".into(), nv("console.trace", console_log));
75        c.props.insert("dir".into(), nv("console.dir", console_log));
76        c.props
77            .insert("table".into(), nv("console.table", console_log));
78        c.props
79            .insert("group".into(), nv("console.group", console_log));
80        c.props
81            .insert("groupEnd".into(), nv("console.groupEnd", dom_noop));
82        c.props.insert(
83            "groupCollapsed".into(),
84            nv("console.groupCollapsed", console_log),
85        );
86        c.props
87            .insert("count".into(), nv("console.count", dom_noop));
88        c.props
89            .insert("assert".into(), nv("console.assert", console_log));
90        c.props.insert("time".into(), nv("console.time", dom_noop));
91        c.props
92            .insert("timeEnd".into(), nv("console.timeEnd", dom_noop));
93    }
94    g.vars.insert("console".into(), Value::Object(console));
95
96    // Math
97    let math = Obj::plain();
98    {
99        let mut m = math.borrow_mut();
100        m.props
101            .insert("PI".into(), Value::Number(core::f64::consts::PI));
102        m.props
103            .insert("E".into(), Value::Number(core::f64::consts::E));
104        m.props.insert("floor".into(), nv("Math.floor", math_floor));
105        m.props.insert("ceil".into(), nv("Math.ceil", math_ceil));
106        m.props.insert("round".into(), nv("Math.round", math_round));
107        m.props.insert("trunc".into(), nv("Math.trunc", math_trunc));
108        m.props.insert("abs".into(), nv("Math.abs", math_abs));
109        m.props.insert("sign".into(), nv("Math.sign", math_sign));
110        m.props.insert("sqrt".into(), nv("Math.sqrt", math_sqrt));
111        m.props.insert("pow".into(), nv("Math.pow", math_pow));
112        m.props.insert("min".into(), nv("Math.min", math_min));
113        m.props.insert("max".into(), nv("Math.max", math_max));
114        m.props
115            .insert("random".into(), nv("Math.random", math_random));
116        m.props.insert("sin".into(), nv("Math.sin", math_sin));
117        m.props.insert("cos".into(), nv("Math.cos", math_cos));
118        m.props.insert("tan".into(), nv("Math.tan", math_tan));
119        m.props.insert("asin".into(), nv("Math.asin", math_asin));
120        m.props.insert("acos".into(), nv("Math.acos", math_acos));
121        m.props.insert("atan".into(), nv("Math.atan", math_atan));
122        m.props.insert("atan2".into(), nv("Math.atan2", math_atan2));
123        m.props.insert("log".into(), nv("Math.log", math_log));
124        m.props.insert("log2".into(), nv("Math.log2", math_log2));
125        m.props.insert("log10".into(), nv("Math.log10", math_log10));
126        m.props.insert("cbrt".into(), nv("Math.cbrt", math_cbrt));
127        m.props.insert("hypot".into(), nv("Math.hypot", math_hypot));
128        m.props.insert("exp".into(), nv("Math.exp", math_exp));
129        m.props.insert("fround".into(), nv("Math.fround", math_fround));
130        m.props.insert("f16round".into(), nv("Math.f16round", math_f16round));
131        m.props.insert("clamp".into(), nv("Math.clamp", math_clamp));
132        m.props.insert("sumPrecise".into(), nv("Math.sumPrecise", math_sum_precise));
133        m.props.insert("clz32".into(), nv("Math.clz32", math_clz32));
134        m.props.insert("imul".into(), nv("Math.imul", math_imul));
135        m.props.insert("sinh".into(), nv("Math.sinh", math_sinh));
136        m.props.insert("cosh".into(), nv("Math.cosh", math_cosh));
137        m.props.insert("tanh".into(), nv("Math.tanh", math_tanh));
138        m.props.insert("asinh".into(), nv("Math.asinh", math_asinh));
139        m.props.insert("acosh".into(), nv("Math.acosh", math_acosh));
140        m.props.insert("atanh".into(), nv("Math.atanh", math_atanh));
141        m.props.insert("expm1".into(), nv("Math.expm1", math_expm1));
142        m.props.insert("log1p".into(), nv("Math.log1p", math_log1p));
143        // `Math.LN2`/`LN10`/`LOG2E`/`LOG10E`/`SQRT2`/`SQRT1_2`(ES1 以来の定数群)が
144        // `PI`/`E` 以外一つも登録されておらず丸ごと未対応だった。
145        m.props.insert("LN2".into(), Value::Number(core::f64::consts::LN_2));
146        m.props.insert("LN10".into(), Value::Number(core::f64::consts::LN_10));
147        m.props.insert("LOG2E".into(), Value::Number(core::f64::consts::LOG2_E));
148        m.props.insert("LOG10E".into(), Value::Number(core::f64::consts::LOG10_E));
149        m.props.insert("SQRT2".into(), Value::Number(core::f64::consts::SQRT_2));
150        m.props.insert("SQRT1_2".into(), Value::Number(1.0 / core::f64::consts::SQRT_2));
151        m.props
152            .insert("PI".into(), Value::Number(core::f64::consts::PI));
153        m.props
154            .insert("E".into(), Value::Number(core::f64::consts::E));
155    }
156    g.vars.insert("Math".into(), Value::Object(math));
157
158    // `Intl`(`Intl.NumberFormat`/`Intl.DateTimeFormat`/`Intl.Collator` 等)が
159    // グローバルオブジェクトに丸ごと存在しなかった。ロケール辞書・タイムゾーン
160    // データベースを伴う本格実装は本キャンペーンの規模を超えるため、既に実装
161    // 済みの `Number.prototype.toLocaleString`(既定の3桁カンマ区切り)を
162    // `.format()` から呼べるようにする最小限の `Intl.NumberFormat` のみを追加し、
163    // `new Intl.NumberFormat().format(n)` という最頻出イディオムだけを救う
164    // (`DateTimeFormat`/`Collator`/ロケール・オプション引数は非対応のまま)。
165    g.vars.insert("Intl".into(), create_intl_object());
166
167    // `CSS.escape(str)`(CSSOM。`document.querySelector('#' + CSS.escape(userId))`
168    // という定番イディオムで使われる)が `CSS` 名前空間自体丸ごと未対応だった。
169    // `CSS.supports()`(`@supports` の条件式評価を実装した際に新設した
170    // `css::supports_condition_matches` をそのまま再利用できるようになったため
171    // 追加。かつては「CSS エンジンとの結合が必要で改修規模が異なる」として
172    // 対象外にしていたが、その結合自体が既に完了済みとなった)。
173    {
174        let css_ns = Obj::plain();
175        css_ns
176            .borrow_mut()
177            .props
178            .insert("escape".into(), nv("CSS.escape", css_escape_static));
179        css_ns
180            .borrow_mut()
181            .props
182            .insert("supports".into(), nv("CSS.supports", css_supports_static));
183        // `CSS.number()`/`.percent()`/`.px()`/`.em()`/`.rem()`/`.deg()`
184        // (CSS Typed OM のうち値+単位オブジェクトを構築するだけの
185        // ファクトリ関数群。丸ごと未対応だった。詳細は`regexp.rs`の
186        // `make_css_unit_value`参照。2026-07-18 発見・実装)。
187        {
188            let mut b = css_ns.borrow_mut();
189            b.props.insert("number".into(), nv("CSS.number", css_number_static));
190            b.props.insert("percent".into(), nv("CSS.percent", css_percent_static));
191            b.props.insert("px".into(), nv("CSS.px", css_px_static));
192            b.props.insert("em".into(), nv("CSS.em", css_em_static));
193            b.props.insert("rem".into(), nv("CSS.rem", css_rem_static));
194            b.props.insert("deg".into(), nv("CSS.deg", css_deg_static));
195        }
196        g.vars.insert("CSS".into(), Value::Object(css_ns));
197    }
198
199    // JSON
200    let json = Obj::plain();
201    {
202        let mut j = json.borrow_mut();
203        j.props.insert("parse".into(), nv("JSON.parse", json_parse));
204        j.props
205            .insert("stringify".into(), nv("JSON.stringify", json_stringify));
206        // `JSON.rawJSON`/`JSON.isRawJSON`(ES2025)が丸ごと未対応だった。
207        // 2026-07-18 発見・実装。
208        j.props
209            .insert("rawJSON".into(), nv("JSON.rawJSON", json_raw_json));
210        j.props
211            .insert("isRawJSON".into(), nv("JSON.isRawJSON", json_is_raw_json));
212    }
213    g.vars.insert("JSON".into(), Value::Object(json));
214
215    // Object(コンストラクタ兼名前空間)
216    let object_ctor = Obj::native("Object", object_ctor);
217    object_ctor
218        .borrow_mut()
219        .props
220        .insert("keys".into(), nv("Object.keys", object_keys));
221    object_ctor
222        .borrow_mut()
223        .props
224        .insert("values".into(), nv("Object.values", object_values));
225    object_ctor
226        .borrow_mut()
227        .props
228        .insert("entries".into(), nv("Object.entries", object_entries));
229    object_ctor
230        .borrow_mut()
231        .props
232        .insert("assign".into(), nv("Object.assign", object_assign));
233    object_ctor.borrow_mut().props.insert(
234        "fromEntries".into(),
235        nv("Object.fromEntries", object_from_entries),
236    );
237    object_ctor.borrow_mut().props.insert(
238        "getOwnPropertyNames".into(),
239        nv("Object.getOwnPropertyNames", object_keys),
240    );
241    object_ctor
242        .borrow_mut()
243        .props
244        .insert("freeze".into(), nv("Object.freeze", object_freeze));
245    object_ctor.borrow_mut().props.insert(
246        "isFrozen".into(),
247        nv("Object.isFrozen", object_is_frozen),
248    );
249    object_ctor
250        .borrow_mut()
251        .props
252        .insert("seal".into(), nv("Object.seal", object_seal));
253    object_ctor
254        .borrow_mut()
255        .props
256        .insert("isSealed".into(), nv("Object.isSealed", object_is_sealed));
257    object_ctor.borrow_mut().props.insert(
258        "preventExtensions".into(),
259        nv("Object.preventExtensions", object_prevent_extensions),
260    );
261    object_ctor.borrow_mut().props.insert(
262        "isExtensible".into(),
263        nv("Object.isExtensible", object_is_extensible),
264    );
265    // `Object.is(a, b)`(ES2015、SameValue)が丸ごと欠落していた。`===` と違い
266    // `NaN` 同士は等しいと判定し、`Object.is(0, -0)` は `false`(`===`/SameValueZero とは
267    // ここだけ異なる、符号付きゼロを区別する点が特徴)。
268    object_ctor
269        .borrow_mut()
270        .props
271        .insert("is".into(), nv("Object.is", object_is));
272    object_ctor
273        .borrow_mut()
274        .props
275        .insert("create".into(), nv("Object.create", object_create));
276    object_ctor.borrow_mut().props.insert(
277        "defineProperty".into(),
278        nv("Object.defineProperty", object_define_property),
279    );
280    object_ctor.borrow_mut().props.insert(
281        "defineProperties".into(),
282        nv("Object.defineProperties", object_define_properties),
283    );
284    object_ctor.borrow_mut().props.insert(
285        "getOwnPropertyDescriptor".into(),
286        nv(
287            "Object.getOwnPropertyDescriptor",
288            object_get_own_property_descriptor,
289        ),
290    );
291    object_ctor.borrow_mut().props.insert(
292        "getOwnPropertyDescriptors".into(),
293        nv(
294            "Object.getOwnPropertyDescriptors",
295            object_get_own_property_descriptors,
296        ),
297    );
298    object_ctor
299        .borrow_mut()
300        .props
301        .insert("hasOwn".into(), nv("Object.hasOwn", object_has_own_static));
302    object_ctor
303        .borrow_mut()
304        .props
305        .insert("groupBy".into(), nv("Object.groupBy", object_group_by));
306    object_ctor.borrow_mut().props.insert(
307        "getPrototypeOf".into(),
308        nv("Object.getPrototypeOf", object_get_prototype_of),
309    );
310    object_ctor.borrow_mut().props.insert(
311        "setPrototypeOf".into(),
312        nv("Object.setPrototypeOf", object_set_prototype_of),
313    );
314    // `Object.prototype`(丸ごと未対応だった。`Object.prototype.toString.call(x)` という
315    // `lodash` 等で広く使われる型判定イディオムは、`Object` に `.prototype` が全く無いと
316    // 呼び出しようが無いため、`obj_to_string()` 自体を直しても実質使えない状態だった)。
317    // この処理系の「組込み Array/Object は実際の `.prototype` を持たない簡略実装」という
318    // 既存方針の中で、`Object.prototype` だけは最低限アクセス可能にする。
319    let object_proto = Obj::plain();
320    object_proto
321        .borrow_mut()
322        .props
323        .insert("toString".into(), nv("toString", obj_to_string));
324    object_ctor
325        .borrow_mut()
326        .props
327        .insert("prototype".into(), Value::Object(object_proto.clone()));
328    g.vars.insert("Object".into(), Value::Object(object_ctor));
329
330    // Array
331    let array_ctor = Obj::native("Array", array_ctor);
332    let array_proto = Obj::plain();
333    let unscopables_obj = Obj::plain();
334    {
335        let mut u = unscopables_obj.borrow_mut();
336        for key in [
337            "copyWithin", "entries", "fill", "find", "findIndex",
338            "findLast", "findLastIndex", "flat", "flatMap", "includes",
339            "keys", "values", "at",
340        ] {
341            u.props.insert(key.into(), Value::Bool(true));
342        }
343    }
344    array_proto
345        .borrow_mut()
346        .props
347        .insert("Symbol(Symbol.unscopables)".into(), Value::Object(unscopables_obj));
348    // 【2026-09-05】`Array.prototype` はオブジェクトとしては在ったが
349    // **メソッドが 1 つも入っていなかった**(`Symbol.unscopables` のみ)。
350    //
351    // 配列メソッドは実体からの動的解決(`props_methods::object_get`)だけで
352    // 賄っており、`Array.prototype.slice` のように**プロトタイプから直接
353    // 読む**形が `undefined` になっていた。
354    // jQuery 1.8.2 の先頭が
355    // `j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf`
356    // と読むため、続く `k.call(...)` で
357    // 「Cannot read properties of undefined (reading 'call')」になっていた。
358    //
359    // `String.prototype` と同じく、同じ解決口を使って並べる。
360    // 実体側と同じ関数を指すので挙動は一致し、二重実装にならない。
361    {
362        let probe = Obj::array(alloc::vec::Vec::new());
363        let mut ap = array_proto.borrow_mut();
364        for key in ARRAY_PROTO_METHODS {
365            let v = super::builtins::props_methods::object_get(&probe, key);
366            if !matches!(v, Value::Undefined) {
367                ap.props.insert(alloc::string::String::from(*key), v);
368            }
369        }
370    }
371    array_ctor
372        .borrow_mut()
373        .props
374        .insert("prototype".into(), Value::Object(array_proto));
375    array_ctor
376        .borrow_mut()
377        .props
378        .insert("isArray".into(), nv("Array.isArray", array_is_array));
379    array_ctor
380        .borrow_mut()
381        .props
382        .insert("from".into(), nv("Array.from", array_from));
383    array_ctor
384        .borrow_mut()
385        .props
386        .insert("of".into(), nv("Array.of", array_of));
387    array_ctor
388        .borrow_mut()
389        .props
390        .insert("fromAsync".into(), nv("Array.fromAsync", array_from_async));
391    g.vars.insert("Array".into(), Value::Object(array_ctor));
392
393    // 単純な型変換コンストラクタ
394    let string_ctor_v = nv("String", string_ctor);
395    if let Value::Object(sc) = &string_ctor_v {
396        let mut b = sc.borrow_mut();
397        b.props.insert(
398            "fromCharCode".into(),
399            nv("String.fromCharCode", string_from_char_code),
400        );
401        b.props.insert(
402            "fromCodePoint".into(),
403            nv("String.fromCodePoint", string_from_code_point),
404        );
405        b.props.insert("raw".into(), nv("String.raw", string_raw));
406        // 【2026-09-05】`String.prototype` が丸ごと存在しなかった。
407        //
408        // 文字列のメソッドは実体(プリミティブ)からの動的解決
409        // (`props_methods::string_get`)だけで賄っており、
410        // `String.prototype.trim` のように**プロトタイプから直接読む**
411        // 形が `undefined` になっていた。
412        // jQuery 1.8.2 の先頭が
413        // `o=String.prototype.trim` を読むため、**本体が実行時に落ち**、
414        // 続く `$` も未定義になっていた(実測)。
415        //
416        // 同じ解決口を使ってメソッドを並べる。実体側と同じ関数を指すので
417        // 挙動は一致し、二重実装にならない。
418        let string_proto = Obj::plain();
419        {
420            let mut sp = string_proto.borrow_mut();
421            for key in STRING_PROTO_METHODS {
422                let v = super::builtins::props_methods::string_get("", key);
423                if !matches!(v, Value::Undefined) {
424                    sp.props.insert(alloc::string::String::from(*key), v);
425                }
426            }
427        }
428        b.props
429            .insert("prototype".into(), Value::Object(string_proto));
430    }
431    g.vars.insert("String".into(), string_ctor_v);
432    // `Number.parseFloat`/`Number.parseInt`(ES2015)は仕様上グローバル `parseFloat`/
433    // `parseInt` と「同一の関数オブジェクト」(`Number.parseFloat === parseFloat` が
434    // `true`)でなければならないが、以前はそれぞれ別々に `nv(...)` していたため
435    // 別オブジェクトになり `===` が `false` になっていた(`.name` も本来どちらも
436    // `"parseFloat"`/`"parseInt"` であるべきところ `"Number.parseFloat"` 等になっていた)。
437    // 1回だけ構築して両方の束縛先で同じ `Value`(同じ `Rc`)を共有する。
438    let parse_float_v = nv("parseFloat", global_parse_float);
439    let parse_int_v = nv("parseInt", global_parse_int);
440    let number_ctor_v = nv("Number", number_ctor);
441    if let Value::Object(nc) = &number_ctor_v {
442        let mut b = nc.borrow_mut();
443        b.props.insert(
444            "isInteger".into(),
445            nv("Number.isInteger", number_is_integer),
446        );
447        b.props.insert(
448            "isSafeInteger".into(),
449            nv("Number.isSafeInteger", number_is_safe_integer),
450        );
451        b.props.insert(
452            "isFinite".into(),
453            nv("Number.isFinite", number_is_finite_static),
454        );
455        b.props
456            .insert("isNaN".into(), nv("Number.isNaN", number_is_nan_static));
457        b.props.insert("parseFloat".into(), parse_float_v.clone());
458        b.props.insert("parseInt".into(), parse_int_v.clone());
459        b.props
460            .insert("MAX_SAFE_INTEGER".into(), Value::Number(9007199254740991.0));
461        b.props.insert(
462            "MIN_SAFE_INTEGER".into(),
463            Value::Number(-9007199254740991.0),
464        );
465        b.props
466            .insert("EPSILON".into(), Value::Number(f64::EPSILON));
467        b.props.insert("MAX_VALUE".into(), Value::Number(f64::MAX));
468        // `Number.MIN_VALUE`(0 に最も近い正の表現可能な値 = 最小の非正規化数
469        // `5e-324`。ES1 以来の定数)と `Number.NaN`(グローバル `NaN` とは別に
470        // `Number` 名前空間側にも仕様上存在する)が丸ごと未登録だった。
471        // `f64::MIN_POSITIVE`(Rust 標準の「最小の正規化数」約 `2.2e-308`)とは
472        // 別物なので注意(仕様の `Number.MIN_VALUE` は非正規化数まで含めた最小値)。
473        // `MIN_SAFE_INTEGER`(安全な整数の下限、大きな負の値)とも紛らわしいが
474        // 別物。
475        b.props
476            .insert("MIN_VALUE".into(), Value::Number(f64::from_bits(1)));
477        b.props.insert("NaN".into(), Value::Number(f64::NAN));
478        b.props
479            .insert("POSITIVE_INFINITY".into(), Value::Number(f64::INFINITY));
480        b.props
481            .insert("NEGATIVE_INFINITY".into(), Value::Number(f64::NEG_INFINITY));
482    }
483    g.vars.insert("Number".into(), number_ctor_v);
484    g.vars.insert("Boolean".into(), nv("Boolean", boolean_ctor));
485
486    // BigInt コンストラクタ + 静的メソッド asIntN / asUintN。
487    let bigint_ctor_v = nv("BigInt", bigint_ctor);
488    if let Value::Object(bc) = &bigint_ctor_v {
489        let mut b = bc.borrow_mut();
490        b.props
491            .insert("asIntN".into(), nv("BigInt.asIntN", bigint_as_int_n));
492        b.props
493            .insert("asUintN".into(), nv("BigInt.asUintN", bigint_as_uint_n));
494    }
495    g.vars.insert("BigInt".into(), bigint_ctor_v);
496
497    // グローバル関数
498    g.vars.insert("parseInt".into(), parse_int_v);
499    g.vars.insert("parseFloat".into(), parse_float_v);
500    g.vars.insert("isNaN".into(), nv("isNaN", global_is_nan));
501    g.vars
502        .insert("isFinite".into(), nv("isFinite", global_is_finite));
503    // `eval`(ES1 以来。文字列以外の引数はそのまま返す仕様)と `Function` コンストラクタ
504    // (ES1。動的にソースからコードを生成する定番イディオムの2大対象)が丸ごと欠落して
505    // いたのを実装。この処理系は「常にグローバルスコープで実行する」簡略実装(indirect
506    // eval と同じセマンティクス。direct eval が呼び出し元のレキシカルスコープを見る挙動は
507    // 対象外)。
508    g.vars.insert("eval".into(), nv("eval", global_eval));
509    // 【2026-09-26】`Function.prototype` が存在しなかった(`String.prototype` 等と同じ欠落)。
510    // 関数のメソッド(call/apply/bind/toString)は実体からの動的解決だけで賄っており、
511    // `Function.prototype.toString.call(f)` のように**プロトタイプから直接読む**形が
512    // undefined になっていた。OpenStreetMap 埋め込み(Leaflet 同梱の JS)が起動時にこれを読み、
513    // 「Cannot read properties of undefined (reading 'toString')」で止まっていた。
514    // 同じ解決口から取ったメソッドを並べる(実体側と同じ関数を指すので二重実装にならない)。
515    let function_ctor_v = nv("Function", function_ctor);
516    if let Value::Object(fc) = &function_ctor_v {
517        let function_proto = Obj::plain();
518        {
519            let mut fp = function_proto.borrow_mut();
520            for key in ["call", "apply", "bind"] {
521                let v = super::builtins::props_methods::function_method(key);
522                if !matches!(v, Value::Undefined) {
523                    fp.props.insert(alloc::string::String::from(key), v);
524                }
525            }
526            fp.props.insert(
527                "toString".into(),
528                nv("toString", super::builtins::props_methods::fn_to_string),
529            );
530        }
531        fc.borrow_mut()
532            .props
533            .insert("prototype".into(), Value::Object(function_proto));
534    }
535    g.vars.insert("Function".into(), function_ctor_v);
536
537    // location(window/document/global で共有する単一オブジェクト。
538    // 値は base_url 設定時に update_location が埋める。install 時は空)。
539    let location = Obj::plain();
540    {
541        let mut l = location.borrow_mut();
542        for k in [
543            "href", "protocol", "username", "password", "host", "hostname", "port", "origin",
544            "pathname", "search", "hash",
545        ] {
546            l.props.insert(String::from(k), Value::str(""));
547        }
548        l.props
549            .insert("assign".into(), nv("location.assign", location_assign));
550        l.props
551            .insert("replace".into(), nv("location.replace", location_replace));
552        l.props
553            .insert("reload".into(), nv("location.reload", location_reload));
554        l.props.insert(
555            "toString".into(),
556            nv("location.toString", location_to_string),
557        );
558        // ホスト連携用マーカー。set_property がこのオブジェクトを location と判定し、
559        // href/hash/pathname/search 代入をナビゲーション要求に変換する。
560        l.props.insert("_is_location".into(), Value::Bool(true));
561    }
562
563    // history(SPA ルーティング。pushState/replaceState で location を書換え(再読込なし))。
564    let history = Obj::plain();
565    {
566        let mut h = history.borrow_mut();
567        h.props.insert("length".into(), Value::Number(1.0));
568        h.props.insert("state".into(), Value::Null);
569        h.props
570            .insert("scrollRestoration".into(), Value::str("auto"));
571        h.props.insert(
572            "pushState".into(),
573            nv("history.pushState", history_push_state),
574        );
575        h.props.insert(
576            "replaceState".into(),
577            nv("history.replaceState", history_replace_state),
578        );
579        h.props
580            .insert("back".into(), nv("history.back", history_back));
581        h.props
582            .insert("forward".into(), nv("history.forward", history_forward));
583        h.props.insert("go".into(), nv("history.go", history_go));
584    }
585
586    // document(DOM ホストオブジェクト)
587    let document = Obj::host("document");
588    {
589        let mut d = document.borrow_mut();
590        d.props.insert(
591            "getElementById".into(),
592            nv("getElementById", document_get_element_by_id),
593        );
594        d.props.insert(
595            "querySelector".into(),
596            nv("querySelector", document_query_selector),
597        );
598        d.props.insert(
599            "querySelectorAll".into(),
600            nv("querySelectorAll", document_query_selector_all),
601        );
602        // `document.elementFromPoint(x,y)`/`.elementsFromPoint(x,y)`(座標ヒット
603        // テストの定番 API)が丸ごと未対応だった。この処理系に z-index を考慮した
604        // 合成順スタックは無いため、レイアウト確定後の `rects`(`getBoundingClientRect`
605        // と同じ発生源)から座標を含む矩形を集め、最も面積が小さい(=最も深く
606        // ネストした)要素を「最前面」の近似として返す簡略実装。
607        d.props.insert(
608            "elementFromPoint".into(),
609            nv("elementFromPoint", document_element_from_point),
610        );
611        d.props.insert(
612            "elementsFromPoint".into(),
613            nv("elementsFromPoint", document_elements_from_point),
614        );
615        d.props.insert(
616            "createElement".into(),
617            nv("document.createElement", document_create_element),
618        );
619        d.props.insert(
620            "createElementNS".into(),
621            nv("document.createElementNS", document_create_element_ns),
622        );
623        d.props.insert(
624            "createTextNode".into(),
625            nv("document.createTextNode", document_create_text_node),
626        );
627        d.props.insert(
628            "createComment".into(),
629            nv("document.createComment", document_create_comment),
630        );
631        // `document.createDocumentFragment()` が丸ごと未対応だった。
632        d.props.insert(
633            "createDocumentFragment".into(),
634            nv("document.createDocumentFragment", document_create_document_fragment),
635        );
636        d.props
637            .insert("location".into(), Value::Object(location.clone()));
638        d.props.insert(
639            "getElementsByClassName".into(),
640            nv(
641                "getElementsByClassName",
642                document_get_elements_by_class_name,
643            ),
644        );
645        d.props.insert(
646            "getElementsByTagName".into(),
647            nv("getElementsByTagName", document_get_elements_by_tag_name),
648        );
649        d.props.insert(
650            "getElementsByTagNameNS".into(),
651            nv("getElementsByTagNameNS", document_get_elements_by_tag_name_ns),
652        );
653        d.props.insert(
654            "getElementsByName".into(),
655            nv("getElementsByName", document_get_elements_by_name),
656        );
657        d.props.insert("title".into(), Value::str(""));
658        d.props.insert("activeElement".into(), Value::Null);
659        // `document`にも`EventTarget`風の汎用リスナ格納`_listeners`を持たせる
660        // (`click`/`keydown`等、`window`と共有しない汎用イベント種別の
661        // `addEventListener`受け皿。2026-07-16 発見・実装。詳細は
662        // `document_add_event_listener`のコメント参照)。
663        d.props.insert("_listeners".into(), Value::Object(Obj::plain()));
664        d.props.insert("readyState".into(), Value::str("complete"));
665        // `document.designMode`(`"on"`/`"off"`。HTML5、丸ごと未対応だった)。
666        // `element.contentEditable` と同じ理由でこの処理系にはキャレット/テキスト
667        // 編集機構自体が無いため、実際のページ全体編集可能化は行わず状態の読み書き
668        // のみを提供する簡略実装(既定 `"off"`)。
669        d.props.insert("designMode".into(), Value::str("off"));
670        // Fullscreen API(丸ごと未対応だった)。実際の画面占有切り替えは行わず
671        // 状態のみを追跡する簡略実装(`fullscreenchange` イベントは対象外)。
672        d.props.insert("fullscreenElement".into(), Value::Null);
673        d.props.insert("fullscreenEnabled".into(), Value::Bool(true));
674        d.props.insert(
675            "exitFullscreen".into(),
676            nv("document.exitFullscreen", document_exit_fullscreen),
677        );
678        // Pointer Lock API(丸ごと未対応だった)。Fullscreen API と同じ
679        // 「状態のみ追跡する」簡略方針(`pointerlockchange` イベントは対象外)。
680        d.props.insert("pointerLockElement".into(), Value::Null);
681        d.props.insert(
682            "exitPointerLock".into(),
683            nv("document.exitPointerLock", document_exit_pointer_lock),
684        );
685        // Picture-in-Picture API(丸ごと未対応だった)。Fullscreen/Pointer Lock
686        // API と同じ「状態のみ追跡する」簡略方針
687        // (`enterpictureinpicture`/`leavepictureinpicture` イベントは対象外)。
688        d.props.insert("pictureInPictureElement".into(), Value::Null);
689        d.props.insert("pictureInPictureEnabled".into(), Value::Bool(true));
690        d.props.insert(
691            "exitPictureInPicture".into(),
692            nv("document.exitPictureInPicture", document_exit_picture_in_picture),
693        );
694        // Storage Access API(`document.hasStorageAccess()`/
695        // `.requestStorageAccess()`。丸ごと未対応だった。サードパーティ
696        // iframe からの Cookie アクセス許可を確認/要求する定番パターン)。
697        // この処理系にはサードパーティ Cookie 分離/ブロック機構自体が無く
698        // ストレージは常にアクセス可能なため、`true`/成功を返すのは楽観視の
699        // 偽装ではなく実態に即した誠実な近似。
700        d.props.insert(
701            "hasStorageAccess".into(),
702            nv("document.hasStorageAccess", document_has_storage_access),
703        );
704        d.props.insert(
705            "requestStorageAccess".into(),
706            nv("document.requestStorageAccess", document_request_storage_access),
707        );
708        // `document.addEventListener('DOMContentLoaded'|'load', fn)` が単なる no-op で
709        // リスナが一切発火しない黙殺バグだった(`window.addEventListener` 側は既に
710        // 同じイベントを即時発火する実装があったが、`document` 側は同じ関数を配線
711        // し忘れていた非対称な状態。`document.addEventListener('DOMContentLoaded', ...)`
712        // は `window` 版よりもむしろ広く使われる定番パターン)。`document_add_event_
713        // listener`(`window_api.rs`)が固定種別は `window` と共有し、それ以外
714        // (`click`/`keydown`等の汎用種別)は `document` 自身の `_listeners` へ
715        // 振り分ける(2026-07-16、`document.addEventListener('click', ...)` の
716        // イベント委譲パターン対応で拡張)。
717        d.props.insert(
718            "addEventListener".into(),
719            nv("document.addEventListener", document_add_event_listener),
720        );
721        // `document.removeEventListener`(丸ごと未対応だった。`addEventListener`
722        // は `window` 版を再利用する形で配線済みだったが、対になる
723        // `removeEventListener` 自体が登録されておらず「関数ではない」
724        // `TypeError` になっていた)。
725        d.props.insert(
726            "removeEventListener".into(),
727            nv("document.removeEventListener", document_remove_event_listener),
728        );
729        // `document.dispatchEvent`(丸ごと未対応だった。`addEventListener`/
730        // `removeEventListener` と同じ兄弟ギャップ)。
731        d.props.insert(
732            "dispatchEvent".into(),
733            nv("document.dispatchEvent", document_dispatch_event),
734        );
735        d.props
736            .insert("write".into(), nv("document.write", dom_noop));
737        d.props.insert(
738            "createRange".into(),
739            nv("document.createRange", document_create_range),
740        );
741        // `document.createTreeWalker(root, whatToShow?, filter?)`(丸ごと
742        // 未対応だった。DOM 部分木を走査する定番パターン)。
743        d.props.insert(
744            "createTreeWalker".into(),
745            nv("document.createTreeWalker", document_create_tree_walker),
746        );
747        // `document.createNodeIterator(root, whatToShow?, filter?)`(丸ごと
748        // 未対応だった)。`TreeWalker` とは異なり内部状態が「参照ノード+
749        // 前後どちら側を指しているか」のポインタモデルで、`nextNode()`/
750        // `previousNode()` を交互に呼ぶと同じノードへ一旦戻る仕様上の挙動がある。
751        d.props.insert(
752            "createNodeIterator".into(),
753            nv("document.createNodeIterator", document_create_node_iterator),
754        );
755        // `document.importNode(externalNode, deep)`/`document.adoptNode(node)`
756        // (丸ごと未対応だった。`<template>` の中身を取り込む定番パターン
757        // `document.importNode(template.content, true)` 等で使われる)。
758        d.props.insert(
759            "importNode".into(),
760            nv("document.importNode", doc_import_node),
761        );
762        d.props.insert(
763            "adoptNode".into(),
764            nv("document.adoptNode", doc_adopt_node),
765        );
766        // `document.createAttribute(name)`(丸ごと未対応だった。`getAttributeNode`
767        // と対になる `Attr` ノード新規作成パターン)。
768        d.props.insert(
769            "createAttribute".into(),
770            nv("document.createAttribute", document_create_attribute),
771        );
772        d.props.insert(
773            "getSelection".into(),
774            nv("document.getSelection", document_get_selection),
775        );
776        d.props.insert(
777            "execCommand".into(),
778            nv("document.execCommand", document_exec_command),
779        );
780        // `document.getAnimations()`(丸ごと未対応だった。`element.
781        // getAnimations()` と同じく実行中アニメーションを一切保持していない
782        // ため常に空配列を返す簡略実装)。
783        d.props.insert(
784            "getAnimations".into(),
785            nv("document.getAnimations", |_, _, _| Ok(Value::Object(Obj::array(alloc::vec![])))),
786        );
787        d.props.insert(
788            "hasFocus".into(),
789            nv("document.hasFocus", |_, _, _| Ok(Value::Bool(true))),
790        );
791        // `document.visibilityState`/`.hidden`(Page Visibility API。丸ごと
792        // 未対応だった。`visibilitychange`イベント自体は前サイクルで登録経路
793        // のみ実装済みだったが、対になる状態プロパティが欠けていた)。この
794        // OS にはタブ/ウィンドウのバックグラウンド化という概念自体が無く
795        // GUI は常時フォアグラウンドのため、`document.hasFocus()`と同じく
796        // 常に「可視」を返す誠実な簡略実装。
797        d.props.insert("visibilityState".into(), Value::str("visible"));
798        d.props.insert("hidden".into(), Value::Bool(false));
799        // `document.characterSet`/`.compatMode`/`.contentType`/`.doctype`
800        // (丸ごと未対応だった。2026-07-16 発見)。この処理系は常に UTF-8
801        // でパースし `<!DOCTYPE html>` 前提の標準準拠モードのみをサポート
802        // するため、`characterSet`/`compatMode`/`contentType`は固定値の
803        // 誠実な簡略実装。`doctype`(`DocumentType` ノード)はパース時に
804        // 一切モデル化していない(HTML パーサが `<!DOCTYPE ...>` を読み
805        // 飛ばすのみでノード化しない)ため、「DocumentType ノードが存在
806        // しない文書」を表す仕様どおりの `null` を返す。
807        d.props.insert("characterSet".into(), Value::str("UTF-8"));
808        d.props.insert("compatMode".into(), Value::str("CSS1Compat"));
809        d.props.insert("contentType".into(), Value::str("text/html"));
810        d.props.insert("doctype".into(), Value::Null);
811        // `document.currentScript`(丸ごと未対応だった。2026-07-16 発見)。
812        // 仕様上「同期実行中の非モジュールクラシックスクリプトの実行中のみ
813        // 対応する `<script>` 要素、それ以外(イベントハンドラ・タイマー・
814        // 実行完了後)は常に `null`」だが、この処理系には「現在実行中の
815        // script 要素」を追跡する仕組みが無い(`<script>` はパース時に一括
816        // 逐次評価されるのみで、実行スタックとノードの紐付けを持たない)
817        // ため、`getAnimations()`と同方針の「状態追跡なし」の誠実な簡略
818        // 実装として常に `null` を返す。
819        d.props.insert("currentScript".into(), Value::Null);
820        // `document.implementation`(`DOMImplementation`。丸ごと未対応
821        // だった。2026-07-16 発見)。`hasFeature()` は仕様上「常に `true`
822        // を返すレガシーな no-op」(実際の機能判定は行わない)と明記されて
823        // いるため引数を無視して固定 `true` を返す誠実な実装。
824        // `createDocumentType()` は他ノードから独立した単体オブジェクトを
825        // 組み立てるだけで済むため対応。一方 `createDocument()`/
826        // `createHTMLDocument()` はこの処理系が単一のグローバル `document`
827        // シングルトンしか持たず独立した2つ目の文書ツリーを表現できない
828        // ため、`customElements`/`<canvas>` と同格の大規模改修が必要と
829        // 判断し本サイクルでは対象外とする(詳細は TODO.md 参照)。
830        let implementation = Obj::plain();
831        {
832            let mut im = implementation.borrow_mut();
833            im.props.insert(
834                "hasFeature".into(),
835                nv("DOMImplementation.hasFeature", |_, _, _| Ok(Value::Bool(true))),
836            );
837            im.props.insert(
838                "createDocumentType".into(),
839                nv("DOMImplementation.createDocumentType", dom_implementation_create_document_type),
840            );
841            im.props.insert(
842                "createHTMLDocument".into(),
843                nv("DOMImplementation.createHTMLDocument", dom_implementation_create_html_document),
844            );
845            im.props.insert(
846                "createDocument".into(),
847                nv("DOMImplementation.createDocument", dom_implementation_create_document),
848            );
849        }
850        d.props
851            .insert("implementation".into(), Value::Object(implementation));
852    }
853    /// `document.implementation.createDocumentType(qualifiedName, publicId,
854    /// systemId)`(丸ごと未対応だった。2026-07-16 発見)。この処理系には
855    /// `DocumentType` 専用の内部表現が無いため、`element.attributes`/
856    /// `getAttributeNode` と同じ「読み取り専用簡略実装」方針で、`{name,
857    /// publicId, systemId}` の平プレーンオブジェクトを返す(どのノードにも
858    /// 接続されず `document.doctype` へも反映されない独立した値)。
859    fn dom_implementation_create_document_type(
860        _it: &mut Interp,
861        _t: Value,
862        a: &[Value],
863    ) -> Result<Value, Value> {
864        let obj = Obj::plain();
865        {
866            let mut b = obj.borrow_mut();
867            b.props.insert("nodeType".into(), Value::Number(10.0));
868            b.props.insert("name".into(), Value::str(arg(a, 0).to_js_string()));
869            b.props.insert("publicId".into(), Value::str(arg(a, 1).to_js_string()));
870            b.props.insert("systemId".into(), Value::str(arg(a, 2).to_js_string()));
871        }
872        Ok(Value::Object(obj))
873    }
874    fn dom_implementation_create_html_document(
875        _it: &mut Interp,
876        _t: Value,
877        a: &[Value],
878    ) -> Result<Value, Value> {
879        let title = if !a.is_empty() && !matches!(a[0], Value::Undefined) {
880            a[0].to_js_string()
881        } else {
882            String::new()
883        };
884        let doc = Obj::plain();
885        {
886            let mut b = doc.borrow_mut();
887            b.props.insert("nodeType".into(), Value::Number(9.0));
888            b.props.insert("nodeName".into(), Value::str("#document"));
889            b.props.insert("title".into(), Value::str(&title));
890            b.props.insert("body".into(), Value::Null);
891            b.props.insert("head".into(), Value::Null);
892            b.props.insert("documentElement".into(), Value::Null);
893            b.props.insert("createElement".into(), nv("createElement", document_create_element));
894        }
895        Ok(Value::Object(doc))
896    }
897    fn dom_implementation_create_document(
898        _it: &mut Interp,
899        _t: Value,
900        a: &[Value],
901    ) -> Result<Value, Value> {
902        let ns = arg(a, 0);
903        let qname = arg(a, 1);
904        let doc = Obj::plain();
905        {
906            let mut b = doc.borrow_mut();
907            b.props.insert("nodeType".into(), Value::Number(9.0));
908            b.props.insert("nodeName".into(), Value::str("#document"));
909            b.props.insert("namespaceURI".into(), ns);
910            b.props.insert("qualifiedName".into(), qname);
911            b.props.insert("createElement".into(), nv("createElement", document_create_element));
912        }
913        Ok(Value::Object(doc))
914    }
915    g.vars
916        .insert("document".into(), Value::Object(document.clone()));
917
918    // window(最小限。document への参照と no-op を提供)
919    let window = Obj::plain();
920    let screen_obj = Obj::plain();
921    {
922        let mut w = window.borrow_mut();
923        w.props.insert("document".into(), Value::Object(document));
924        w.props
925            .insert("location".into(), Value::Object(location.clone()));
926        w.props
927            .insert("history".into(), Value::Object(history.clone()));
928        w.props.insert(
929            "addEventListener".into(),
930            nv("window.addEventListener", window_add_event_listener),
931        );
932        w.props.insert(
933            "removeEventListener".into(),
934            nv("window.removeEventListener", window_remove_event_listener),
935        );
936        w.props.insert(
937            "dispatchEvent".into(),
938            nv("window.dispatchEvent", window_dispatch_event),
939        );
940        // `window.print()`(丸ごと未対応だった)。実際の印刷パイプライン自体が
941        // 無いため、`focus`/`blur`/`scrollIntoView` と同じ無害な no-op とする。
942        w.props.insert("print".into(), nv("window.print", dom_noop));
943        w.props.insert("scrollY".into(), Value::Number(0.0));
944        w.props.insert("pageYOffset".into(), Value::Number(0.0));
945        w.props.insert("scrollX".into(), Value::Number(0.0));
946        w.props.insert("pageXOffset".into(), Value::Number(0.0));
947        w.props
948            .insert("scrollTo".into(), nv("scrollTo", window_scroll_to));
949        w.props
950            .insert("scroll".into(), nv("scroll", window_scroll_to));
951        // `window.scrollBy(x, y)`(丸ごと未対応だった。`window.`修飾無しの
952        // bare `scrollBy`〔グローバル関数〕は登録されていたが、`window.
953        // scrollBy(...)`とプロパティ経由でアクセスする形は登録が漏れており
954        // `TypeError: not a function`になっていた。`scrollTo`/`scroll`は
955        // 両方の形が登録済みだったのに`scrollBy`だけこの非対称漏れが
956        // あった。2026-07-16 発見・実装)。
957        w.props
958            .insert("scrollBy".into(), nv("window.scrollBy", window_scroll_by));
959        w.props
960            .insert("matchMedia".into(), nv("matchMedia", window_match_media));
961        // `window.postMessage(data, targetOrigin)`(丸ごと未対応だった)。
962        // 別ウィンドウ/フレーム間通信の仕様上の非同期性・`targetOrigin` に
963        // よる送信先絞り込みは実装せず、同一 `window` 上の 'message' リスナへ
964        // 同期的に配送する簡略実装(この処理系にはフレーム分離が無いため
965        // 実用上大きな差は生じない)。
966        w.props
967            .insert("postMessage".into(), nv("postMessage", window_post_message));
968        w.props.insert("alert".into(), nv("alert", console_log));
969        // `window.confirm`/`window.prompt`(丸ごと未対応だった。`alert`は
970        // 既に`console_log`で代替されていたのに、対になるこの2つだけ
971        // グローバルにも`window`にも一切登録が無く「関数ではない」
972        // `TypeError`になっていた。2026-07-16 発見・実装)。
973        w.props
974            .insert("confirm".into(), nv("window.confirm", window_confirm));
975        w.props
976            .insert("prompt".into(), nv("window.prompt", window_prompt));
977        // `window.open`/`.close`/`.focus`/`.blur`(丸ごと未対応だった。同じ
978        // 監査で発見。呼び出すと`TypeError`になっていた。2026-07-16
979        // 発見・実装)。
980        w.props
981            .insert("open".into(), nv("window.open", window_open));
982        w.props
983            .insert("close".into(), nv("window.close", window_close));
984        // `window.stop()`(丸ごと未対応だった。詳細は`window_stop`参照。
985        // 2026-07-17 発見・実装)。
986        w.props
987            .insert("stop".into(), nv("window.stop", window_stop));
988        w.props
989            .insert("focus".into(), nv("window.focus", window_focus));
990        w.props
991            .insert("blur".into(), nv("window.blur", window_focus));
992        // `window.getSelection()`(丸ごと未対応だった。仕様上こちらが本来の
993        // 正規の場所で、`document.getSelection()`はレガシーな別名。この
994        // 処理系は逆に`document.getSelection`だけを実装しており、より
995        // 一般的に使われる`window.getSelection().toString()`等のイディオムが
996        // `TypeError`になっていた。2026-07-16 発見・実装。同じ`document_get_
997        // selection`実装を再利用)。
998        w.props.insert(
999            "getSelection".into(),
1000            nv("window.getSelection", document_get_selection),
1001        );
1002        // `window.self`/`.top`/`.parent`/`.frames`/`.name`(丸ごと未対応
1003        // だった。bare `self`/`top`/`parent`/`frames`〔`g.vars`〕と同じ
1004        // 理由。この処理系には独立JSレルムを持つiframeが無く常に単一
1005        // フレームのため、仕様どおり自分自身を指す。`.name`はプロパティ
1006        // として読み書き可能な単なる文字列で既定は空文字列)。
1007        w.props.insert("self".into(), Value::Object(window.clone()));
1008        w.props.insert("top".into(), Value::Object(window.clone()));
1009        w.props.insert("parent".into(), Value::Object(window.clone()));
1010        w.props.insert("frames".into(), Value::Object(window.clone()));
1011        w.props.insert("name".into(), Value::str(""));
1012        w.props.insert(
1013            "getComputedStyle".into(),
1014            nv("getComputedStyle", get_computed_style),
1015        );
1016        w.props.insert(
1017            "localStorage".into(),
1018            Value::Object(Obj::host("storage:local")),
1019        );
1020        w.props.insert(
1021            "sessionStorage".into(),
1022            Value::Object(Obj::host("storage:session")),
1023        );
1024        // viewport dimensions(VIEWPORT_HINT がセットされていれば使う)
1025        let vw = super::super::layout::VIEWPORT_HINT_W.load(core::sync::atomic::Ordering::Relaxed);
1026        let vh = super::super::layout::VIEWPORT_HINT_H.load(core::sync::atomic::Ordering::Relaxed);
1027        let vw_f = if vw > 0 { vw as f64 } else { 1024.0 };
1028        let vh_f = if vh > 0 { vh as f64 } else { 600.0 };
1029        w.props.insert("innerWidth".into(), Value::Number(vw_f));
1030        w.props.insert("innerHeight".into(), Value::Number(vh_f));
1031        w.props.insert("outerWidth".into(), Value::Number(vw_f));
1032        w.props.insert("outerHeight".into(), Value::Number(vh_f));
1033        w.props
1034            .insert("devicePixelRatio".into(), Value::Number(1.0));
1035        // (`window.scrollBy` は上の方で `window_scroll_by` へ正しく登録済み。
1036        // ここに以前あった `nv("scrollBy", window_scroll_to)` という重複
1037        // 登録は、後勝ちの `props.insert` で正しい登録を静かに上書きし
1038        // `window.scrollBy(dx,dy)` が実質 `scrollTo` と誤動作するバグ
1039        // だった。2026-07-16 発見・削除)。
1040        // `window.screen`(丸ごと未対応だった。`screen.width`/`.height`/
1041        // `.availWidth`/`.availHeight`/`.colorDepth`/`.pixelDepth`/
1042        // `.orientation.type` というレスポンシブ分岐の定番パターンで参照される
1043        // 標準オブジェクト。タスクバー等が無いためウインドウ全体を占有する
1044        // ビューポート寸法と `avail*` を同一値にする簡略実装)。
1045        {
1046            let mut s = screen_obj.borrow_mut();
1047            s.props.insert("width".into(), Value::Number(vw_f));
1048            s.props.insert("height".into(), Value::Number(vh_f));
1049            s.props.insert("availWidth".into(), Value::Number(vw_f));
1050            s.props.insert("availHeight".into(), Value::Number(vh_f));
1051            s.props.insert("colorDepth".into(), Value::Number(24.0));
1052            s.props.insert("pixelDepth".into(), Value::Number(24.0));
1053            let orientation = Obj::plain();
1054            let otype = if vw_f >= vh_f { "landscape-primary" } else { "portrait-primary" };
1055            orientation.borrow_mut().props.insert("type".into(), Value::str(otype));
1056            orientation.borrow_mut().props.insert("angle".into(), Value::Number(0.0));
1057            // `screen.orientation.lock()`/`.unlock()`(丸ごと未対応だった)。
1058            // 実際にディスプレイの向きを固定する機構は無いため、`fullscreenElement`
1059            // 等と同じ「状態のみ追跡する」簡略実装とする(`lock()` は要求された
1060            // 向きへ `type` を書き換えるだけ、`unlock()` は現在のビューポート寸法
1061            // から求めた自然な向きへ戻す)。
1062            orientation
1063                .borrow_mut()
1064                .props
1065                .insert("lock".into(), nv("screen.orientation.lock", screen_orientation_lock));
1066            orientation.borrow_mut().props.insert(
1067                "unlock".into(),
1068                nv("screen.orientation.unlock", screen_orientation_unlock),
1069            );
1070            s.props.insert("orientation".into(), Value::Object(orientation));
1071        }
1072        w.props.insert("screen".into(), Value::Object(screen_obj.clone()));
1073    }
1074    // `window.cookieStore`(Cookie Store API。丸ごと未対応だった。従来の
1075    // `document.cookie` 文字列パースに代わる非同期・構造化された定番の
1076    // 読み書き方式)。既存の `COOKIES`(`document.cookie` の実データ)を
1077    // そのまま共有するため、両方の API 経由での読み書きが相互に反映される。
1078    let cookie_store = Obj::plain();
1079    {
1080        let mut cs = cookie_store.borrow_mut();
1081        cs.props.insert("get".into(), nv("cookieStore.get", cookie_store_get));
1082        cs.props.insert("getAll".into(), nv("cookieStore.getAll", cookie_store_get_all));
1083        cs.props.insert("set".into(), nv("cookieStore.set", cookie_store_set));
1084        cs.props.insert("delete".into(), nv("cookieStore.delete", cookie_store_delete));
1085    }
1086    window
1087        .borrow_mut()
1088        .props
1089        .insert("cookieStore".into(), Value::Object(cookie_store.clone()));
1090    g.vars.insert("window".into(), Value::Object(window));
1091    g.vars.insert("screen".into(), Value::Object(screen_obj));
1092    g.vars.insert("cookieStore".into(), Value::Object(cookie_store));
1093    g.vars.insert(
1094        "innerWidth".into(),
1095        Value::Number({
1096            let v =
1097                super::super::layout::VIEWPORT_HINT_W.load(core::sync::atomic::Ordering::Relaxed);
1098            if v > 0 {
1099                v as f64
1100            } else {
1101                1024.0
1102            }
1103        }),
1104    );
1105    g.vars.insert(
1106        "innerHeight".into(),
1107        Value::Number({
1108            let v =
1109                super::super::layout::VIEWPORT_HINT_H.load(core::sync::atomic::Ordering::Relaxed);
1110            if v > 0 {
1111                v as f64
1112            } else {
1113                600.0
1114            }
1115        }),
1116    );
1117    g.vars.insert(
1118        "getComputedStyle".into(),
1119        nv("getComputedStyle", get_computed_style),
1120    );
1121    g.vars.insert(
1122        "localStorage".into(),
1123        Value::Object(Obj::host("storage:local")),
1124    );
1125    g.vars.insert(
1126        "sessionStorage".into(),
1127        Value::Object(Obj::host("storage:session")),
1128    );
1129
1130    // URL / URLSearchParams / URLPattern
1131    g.vars.insert(
1132        "URLSearchParams".into(),
1133        nv("URLSearchParams", url_search_params_ctor),
1134    );
1135    g.vars.insert(
1136        "URLPattern".into(),
1137        nv("URLPattern", url_pattern_ctor),
1138    );
1139    g.vars
1140        .insert("FormData".into(), nv("FormData", form_data_ctor));
1141    // `URL.canParse(url, base?)`(ES2023/WHATWG。`new URL()` を try/catch で
1142    // 包む定番の存在確認イディオムを1メソッドで完結させる)が丸ごと未対応
1143    // だった。
1144    // `URL.createObjectURL(blob)`/`.revokeObjectURL(url)`(`Blob`/`File` を
1145    // 一意な `blob:` URL へ変換する定番パターン)が丸ごと未対応だった。
1146    {
1147        let url_ctor_val = nv("URL", url_ctor);
1148        if let Value::Object(o) = &url_ctor_val {
1149            let mut b = o.borrow_mut();
1150            b.props.insert("canParse".into(), nv("URL.canParse", url_can_parse));
1151            // `URL.parse(url, base?)`(ES2024/WHATWG。丸ごと未対応だった。詳細は
1152            // url_parse 参照)。
1153            b.props.insert("parse".into(), nv("URL.parse", url_parse));
1154            b.props.insert(
1155                "createObjectURL".into(),
1156                nv("URL.createObjectURL", url_create_object_url),
1157            );
1158            b.props.insert(
1159                "revokeObjectURL".into(),
1160                nv("URL.revokeObjectURL", url_revoke_object_url),
1161            );
1162        }
1163        g.vars.insert("URL".into(), url_ctor_val);
1164    }
1165
1166    // XMLSerializer(丸ごと未対応だった。`new XMLSerializer().serializeToString(node)`
1167    // という定番パターン。既存の `outerHTML` ゲッターと同じ
1168    // `DomBridge::get_outer_html` を再利用する。詳細は xml_serializer_ctor 参照)。
1169    g.vars
1170        .insert("XMLSerializer".into(), nv("XMLSerializer", xml_serializer_ctor));
1171
1172    // URI エンコード
1173    g.vars.insert(
1174        "encodeURIComponent".into(),
1175        nv("encodeURIComponent", js_encode_uri_component),
1176    );
1177    g.vars.insert(
1178        "decodeURIComponent".into(),
1179        nv("decodeURIComponent", js_decode_uri_component),
1180    );
1181    g.vars
1182        .insert("encodeURI".into(), nv("encodeURI", js_encode_uri));
1183    g.vars
1184        .insert("decodeURI".into(), nv("decodeURI", js_decode_uri));
1185    // `escape`/`unescape`(Annex B.2.1)が丸ごと未対応だった。`encodeURI` 系より
1186    // 古い遺産だが、実務コードになお残る(詳細は各関数の実装コメント参照)。
1187    g.vars.insert("escape".into(), nv("escape", js_escape));
1188    g.vars.insert("unescape".into(), nv("unescape", js_unescape));
1189    // `new ClipboardItem({mimeType: blob})`(丸ごと未対応だった。
1190    // `navigator.clipboard.write()` に渡す定番パターン)。
1191    g.vars.insert("ClipboardItem".into(), nv("ClipboardItem", clipboard_item_ctor));
1192    // `new EyeDropper()`(EyeDropper API。丸ごと未対応だった。`.open()` で
1193    // 画面上の色を拾うカラーピッカー UI を開く定番パターン)。この処理系には
1194    // 実際のピッカー UI が無くいかなる色も本当には拾えないため、
1195    // `navigator.share` と同じ「できないことを楽観視しない」方針で、
1196    // ユーザーがピッカーをキャンセルした場合と同じ仕様どおりの `AbortError`
1197    // で常に拒否する。
1198    g.vars.insert("EyeDropper".into(), nv("EyeDropper", eye_dropper_ctor));
1199
1200    // base64
1201    g.vars.insert("btoa".into(), nv("btoa", btoa));
1202    g.vars.insert("atob".into(), nv("atob", atob));
1203
1204    // navigator(最小限)
1205    let navigator = Obj::plain();
1206    {
1207        let mut n = navigator.borrow_mut();
1208        n.props.insert(
1209            "userAgent".into(),
1210            Value::str("AtmOS/1.0 (Raspberry Pi; aarch64) AtmOSBrowser/1.0"),
1211        );
1212        n.props.insert("platform".into(), Value::str("AtmOS"));
1213        n.props.insert("language".into(), Value::str("ja-JP"));
1214        n.props.insert("onLine".into(), Value::Bool(true));
1215        n.props.insert("vendor".into(), Value::str("Sugimura Lab"));
1216        // `navigator.cookieEnabled`/`.hardwareConcurrency`/`.maxTouchPoints`/
1217        // `.languages`/`.webdriver`(丸ごと未対応だった。いずれも
1218        // `if (!navigator.cookieEnabled) {...}` 等のフィーチャー検出で
1219        // 参照される定番プロパティ)。`hardwareConcurrency` は QEMU 起動時の
1220        // `-smp 4`(RPi3B+ の実コア数)に合わせた固定値、`maxTouchPoints` は
1221        // タッチスクリーンが無いため 0、`webdriver` は自動操作されていない
1222        // ため `false` の固定値。
1223        n.props.insert("cookieEnabled".into(), Value::Bool(true));
1224        n.props
1225            .insert("hardwareConcurrency".into(), Value::Number(4.0));
1226        n.props.insert("maxTouchPoints".into(), Value::Number(0.0));
1227        n.props.insert(
1228            "languages".into(),
1229            Value::Object(Obj::array(alloc::vec![Value::str("ja-JP")])),
1230        );
1231        n.props.insert("webdriver".into(), Value::Bool(false));
1232        // `navigator.userAgentData`(Client Hints API。丸ごと未対応
1233        // だった。`navigator.userAgent`文字列パースに代わる構造化版の
1234        // 定番フィーチャー検出パターン。2026-07-17 発見・実装)。既存の
1235        // `userAgent`文字列("AtmOSBrowser/1.0")/`platform`("AtmOS")と
1236        // 整合する固定値を返す誠実な簡略実装。
1237        let ua_data = Obj::plain();
1238        {
1239            let mut u = ua_data.borrow_mut();
1240            let brand_entry = Obj::plain();
1241            brand_entry.borrow_mut().props.insert("brand".into(), Value::str("AtmOSBrowser"));
1242            brand_entry.borrow_mut().props.insert("version".into(), Value::str("1"));
1243            u.props.insert(
1244                "brands".into(),
1245                Value::Object(Obj::array(alloc::vec![Value::Object(brand_entry)])),
1246            );
1247            u.props.insert("mobile".into(), Value::Bool(false));
1248            u.props.insert("platform".into(), Value::str("AtmOS"));
1249            u.props.insert(
1250                "getHighEntropyValues".into(),
1251                nv("getHighEntropyValues", navigator_get_high_entropy_values),
1252            );
1253        }
1254        n.props.insert("userAgentData".into(), Value::Object(ua_data));
1255    }
1256    // `navigator.clipboard`(丸ごと未対応だった。`navigator.clipboard.writeText(...)`/
1257    // `readText()` というコピー&ペースト UI の定番パターン)。この OS には実際の
1258    // OS 全体で共有されるクリップボードが無いため、単純なプロセス内の文字列保持
1259    // (`_clipboard` prop)で近似する。
1260    let clipboard = Obj::plain();
1261    {
1262        let mut c = clipboard.borrow_mut();
1263        c.props.insert("_clipboard".into(), Value::str(""));
1264        c.props.insert("writeText".into(), nv("writeText", clipboard_write_text));
1265        c.props.insert("readText".into(), nv("readText", clipboard_read_text));
1266        // `navigator.clipboard.write(items)`/`.read()`(`ClipboardItem` 配列版。
1267        // 丸ごと未対応だった。`text/plain` エントリのみ対応する簡略実装で、
1268        // `writeText`/`readText` と同じ `_clipboard` prop を共有する)。
1269        c.props.insert("write".into(), nv("write", navigator_clipboard_write));
1270        c.props.insert("read".into(), nv("read", navigator_clipboard_read));
1271    }
1272    navigator
1273        .borrow_mut()
1274        .props
1275        .insert("clipboard".into(), Value::Object(clipboard));
1276    // `navigator.storage`(StorageManager API。`.estimate()`/`.persist()`/
1277    // `.persisted()`が丸ごと未対応だった。2026-07-17 発見)。この処理系
1278    // には実際のディスク使用量計測・永続化権限の概念が無い(SylFSへの
1279    // 書き込みは常に永続化される)ため、`usage: 0`(未計測の誠実な簡略値)・
1280    // `quota`は固定の代表値、`persist`/`persisted`は常に`true`を返す
1281    // 簡略実装。
1282    let storage_mgr = Obj::plain();
1283    {
1284        let mut s = storage_mgr.borrow_mut();
1285        s.props.insert("estimate".into(), nv("estimate", navigator_storage_estimate));
1286        s.props.insert("persist".into(), nv("persist", navigator_storage_persist));
1287        s.props.insert("persisted".into(), nv("persisted", navigator_storage_persist));
1288    }
1289    navigator
1290        .borrow_mut()
1291        .props
1292        .insert("storage".into(), Value::Object(storage_mgr));
1293    // `navigator.sendBeacon(url, data)`(丸ごと未対応だった。ページ離脱時の
1294    // 計測データ送信に使われる定番パターン。詳細は navigator_send_beacon 参照)。
1295    navigator
1296        .borrow_mut()
1297        .props
1298        .insert("sendBeacon".into(), nv("sendBeacon", navigator_send_beacon));
1299    // `navigator.geolocation`(丸ごと未対応だった。`getCurrentPosition(success,
1300    // error, options)`/`watchPosition(...)`/`clearWatch(id)` という位置情報
1301    // 取得の定番パターン)。この OS には実際の位置情報ハードウェア/OS権限機構が
1302    // 一切無いため、常に `errorCallback` を `PERMISSION_DENIED`(実ブラウザで
1303    // 権限拒否時に返る値と同じコード)で同期的に呼ぶ簡略実装とする(`clipboard`
1304    // のような部分的な近似すら成立しない、この処理系に無い機能そのものであるため)。
1305    let geolocation = Obj::plain();
1306    {
1307        let mut geo = geolocation.borrow_mut();
1308        geo.props.insert(
1309            "getCurrentPosition".into(),
1310            nv("getCurrentPosition", geolocation_get_current_position),
1311        );
1312        geo.props
1313            .insert("watchPosition".into(), nv("watchPosition", geolocation_watch_position));
1314        geo.props
1315            .insert("clearWatch".into(), nv("clearWatch", geolocation_clear_watch));
1316    }
1317    navigator
1318        .borrow_mut()
1319        .props
1320        .insert("geolocation".into(), Value::Object(geolocation));
1321    // `navigator.permissions.query({name})`(丸ごと未対応だった。`if
1322    // ((await navigator.permissions.query({name:'geolocation'})).state ===
1323    // 'granted') {...}` という定番の機能可否事前判定パターン)。この処理系には
1324    // 実際の権限プロンプト UI が無いため、各 API の実装状況と整合する固定値を
1325    // 返す簡略実装とする(`geolocation` は常に `PERMISSION_DENIED` を返す実装
1326    // と整合させ `denied`、`Notification.permission` が常に `granted` と
1327    // 整合する `notifications` は `granted`、その他は `prompt`)。
1328    let permissions = Obj::plain();
1329    permissions
1330        .borrow_mut()
1331        .props
1332        .insert("query".into(), nv("permissions.query", permissions_query));
1333    navigator
1334        .borrow_mut()
1335        .props
1336        .insert("permissions".into(), Value::Object(permissions));
1337    // `navigator.wakeLock.request(type)`(Screen Wake Lock API。丸ごと未対応
1338    // だった。`await navigator.wakeLock.request('screen')` でスリープ抑制を
1339    // 要求する定番パターン)。この処理系には実際の電源管理機構が無いため、
1340    // `WakeLockSentinel` 相当の `{released, type, release()}` を状態のみ
1341    // 追跡する簡略実装で返す(`release` イベントは対象外)。
1342    let wake_lock = Obj::plain();
1343    wake_lock
1344        .borrow_mut()
1345        .props
1346        .insert("request".into(), nv("wakeLock.request", wake_lock_request));
1347    navigator
1348        .borrow_mut()
1349        .props
1350        .insert("wakeLock".into(), Value::Object(wake_lock));
1351    // `navigator.share(data)`/`navigator.canShare(data)`(Web Share API。
1352    // 丸ごと未対応だった。`await navigator.share({title, url})` でネイティブ
1353    // 共有シートを開く定番パターン)。この処理系には実際の共有先 UI が無い
1354    // ため、`navigator.geolocation` が実位置情報を取得できない以上
1355    // `PERMISSION_DENIED` を返すのと同じ「できないことを楽観視しない」方針で、
1356    // `canShare` は常に `false`、`share` は仕様どおり `AbortError` で拒否する
1357    // (何も共有できていないのに成功を偽装しない)。
1358    navigator
1359        .borrow_mut()
1360        .props
1361        .insert("canShare".into(), nv("navigator.canShare", navigator_can_share));
1362    navigator
1363        .borrow_mut()
1364        .props
1365        .insert("share".into(), nv("navigator.share", navigator_share));
1366    // `navigator.vibrate(pattern)`(Vibration API。丸ごと未対応だった)。
1367    // 仕様上この戻り値は「振動リクエストを受理したか」のみを表し、実際に
1368    // 振動モーターが存在するかとは無関係(ハードウェアの無い環境でも `true`
1369    // を返すのが仕様どおりの正しい挙動)なので、`geolocation` とは異なり
1370    // 常に `true` を返す簡略実装で問題ない。
1371    navigator
1372        .borrow_mut()
1373        .props
1374        .insert("vibrate".into(), nv("navigator.vibrate", navigator_vibrate));
1375    // `navigator.getBattery()`(Battery Status API。丸ごと未対応だった)。
1376    // このOSのターゲット(Raspberry Pi)はバッテリー非搭載で常時 AC 電源
1377    // 動作のため、「常時満充電で給電中」という値は楽観視の偽装ではなく
1378    // 実態に即した誠実な近似(`charging:true`/`level:1`/`chargingTime:0`/
1379    // `dischargingTime:Infinity`)。`levelchange` 等のイベントは対象外。
1380    navigator
1381        .borrow_mut()
1382        .props
1383        .insert("getBattery".into(), nv("navigator.getBattery", navigator_get_battery));
1384    // `navigator.connection`(Network Information API。丸ごと未対応
1385    // だった。`if (navigator.connection.saveData) {...}` という低速回線向け
1386    // 最適化分岐の定番パターン)。この処理系には実際の帯域/往復遅延の計測
1387    // 機構が無いため、`saveData:false`(節約モードを要求されていないと
1388    // 仮定する既定値)と一般的なブロードバンド相当の代表値を返す簡略実装
1389    // (`change` イベントは対象外)。
1390    let connection = Obj::plain();
1391    {
1392        let mut c = connection.borrow_mut();
1393        c.props.insert("type".into(), Value::str("ethernet"));
1394        c.props.insert("effectiveType".into(), Value::str("4g"));
1395        c.props.insert("downlink".into(), Value::Number(10.0));
1396        c.props.insert("rtt".into(), Value::Number(50.0));
1397        c.props.insert("saveData".into(), Value::Bool(false));
1398        c.props
1399            .insert("addEventListener".into(), nv("addEventListener", dom_noop));
1400        c.props
1401            .insert("removeEventListener".into(), nv("removeEventListener", dom_noop));
1402    }
1403    navigator
1404        .borrow_mut()
1405        .props
1406        .insert("connection".into(), Value::Object(connection));
1407    // `navigator.locks`(Web Locks API。丸ごと未対応だった)。この処理系には
1408    // 複数タブ/複数ワーカー間の実際の競合排他機構が無い(単一ドキュメント・
1409    // 単一スレッドの JS 実行モデル)ため、`request(name, callback)` は
1410    // コールバックを即座に同期呼び出しし、その戻り値で解決する Promise を
1411    // 返す簡略実装(`options` 引数中の `signal`/`mode`/`ifAvailable`/
1412    // `steal` は対象外)。`query()` は保持中/待機中とも常に空。
1413    let locks = Obj::plain();
1414    {
1415        let mut l = locks.borrow_mut();
1416        l.props.insert("request".into(), nv("navigator.locks.request", navigator_locks_request));
1417        l.props.insert("query".into(), nv("navigator.locks.query", navigator_locks_query));
1418    }
1419    navigator.borrow_mut().props.insert("locks".into(), Value::Object(locks));
1420    g.vars.insert("navigator".into(), Value::Object(navigator));
1421
1422    // performance.now(疑似単調増加)
1423    let performance = Obj::plain();
1424    performance
1425        .borrow_mut()
1426        .props
1427        .insert("now".into(), nv("performance.now", performance_now));
1428    // `performance.timeOrigin`(丸ごと未対応だった。`performance.now()`が
1429    // 相対時刻の基準とする実時刻を表す。ページ読込=プロセス起動という
1430    // この処理系の簡略化と整合させ、グローバル初期化時点の実時刻を
1431    // 固定値として使う)。
1432    performance
1433        .borrow_mut()
1434        .props
1435        .insert("timeOrigin".into(), Value::Number(epoch_ms_now()));
1436    // Performance Timeline / User Timing API(`mark`/`measure`/
1437    // `getEntriesByType`/`getEntriesByName`/`clearMarks`/`clearMeasures`)が
1438    // `now()` 以外丸ごと未対応だった(プロファイリングの定番イディオム
1439    // `performance.mark('start'); ...; performance.measure('x','start')` が
1440    // 軒並み TypeError)。
1441    {
1442        let mut p = performance.borrow_mut();
1443        p.props.insert("_entries".into(), Value::Object(Obj::array(alloc::vec::Vec::new())));
1444        p.props.insert("mark".into(), nv("mark", perf_mark));
1445        p.props.insert("measure".into(), nv("measure", perf_measure));
1446        // `performance.getEntries()`(フィルタ無しの全件取得。丸ごと未対応
1447        // だった。`getEntriesByType`/`getEntriesByName` は既に対応済み
1448        // だったが、素の `getEntries()` 自体が欠けていた)。
1449        p.props.insert("getEntries".into(), nv("getEntries", perf_get_entries));
1450        p.props.insert(
1451            "getEntriesByType".into(),
1452            nv("getEntriesByType", perf_get_entries_by_type),
1453        );
1454        p.props.insert(
1455            "getEntriesByName".into(),
1456            nv("getEntriesByName", perf_get_entries_by_name),
1457        );
1458        p.props.insert("clearMarks".into(), nv("clearMarks", perf_clear_marks));
1459        p.props
1460            .insert("clearMeasures".into(), nv("clearMeasures", perf_clear_measures));
1461        // `PerformanceObserver` が監視対象を登録する先(丸ごと未対応だった。
1462        // 詳細は performance_observer_ctor 参照)。
1463        p.props
1464            .insert("_observers".into(), Value::Object(Obj::array(alloc::vec::Vec::new())));
1465    }
1466    g.vars
1467        .insert("performance".into(), Value::Object(performance));
1468    let performance_observer_ctor_val = nv("PerformanceObserver", performance_observer_ctor);
1469    // `PerformanceObserver.supportedEntryTypes`(静的プロパティ。丸ごと未対応
1470    // だった)。この処理系のパフォーマンスタイムラインが実際に生成する
1471    // エントリ種別は `mark`/`measure` のみのため、誠実にその2種のみを返す
1472    // (`navigation`/`resource`等、実装していない種別は含めない)。
1473    if let Value::Object(po) = &performance_observer_ctor_val {
1474        po.borrow_mut().props.insert(
1475            "supportedEntryTypes".into(),
1476            Value::Object(Obj::array(alloc::vec![Value::str("mark"), Value::str("measure")])),
1477        );
1478    }
1479    g.vars
1480        .insert("PerformanceObserver".into(), performance_observer_ctor_val);
1481
1482    // ArrayBuffer / TypedArray 全種別(簡易実装。内部表現は通常の
1483    // ObjKind::Array(Vec<Value::Number>) を流用し、buffer は別インスタンスの独立コピー
1484    // — 真の共有メモリビューではない。BigInt64Array/BigUint64Array は対象外)。
1485    let ab_ctor = nv("ArrayBuffer", arraybuffer_ctor);
1486    if let Value::Object(o) = &ab_ctor {
1487        o.borrow_mut()
1488            .props
1489            .insert("isView".into(), nv("isView", arraybuffer_is_view));
1490    }
1491    g.vars.insert("ArrayBuffer".into(), ab_ctor);
1492    let mut register_typed_array = |name: &str, ctor: NativeFn, from_fn: NativeFn| {
1493        let f = nv(name, ctor);
1494        if let Value::Object(o) = &f {
1495            o.borrow_mut()
1496                .props
1497                .insert("from".into(), nv("from", from_fn));
1498            let proto = Obj::plain();
1499            proto.borrow_mut().proto = Some(object_proto.clone());
1500            o.borrow_mut()
1501                .props
1502                .insert("prototype".into(), Value::Object(proto.clone()));
1503            proto
1504                .borrow_mut()
1505                .props
1506                .insert("constructor".into(), f.clone());
1507        }
1508        g.vars.insert(String::from(name), f);
1509    };
1510    register_typed_array("Uint8Array", uint8array_ctor, uint8array_from);
1511    register_typed_array("Int8Array", int8array_ctor, int8array_from);
1512    register_typed_array(
1513        "Uint8ClampedArray",
1514        uint8clampedarray_ctor,
1515        uint8clampedarray_from,
1516    );
1517    register_typed_array("Int16Array", int16array_ctor, int16array_from);
1518    register_typed_array("Uint16Array", uint16array_ctor, uint16array_from);
1519    register_typed_array("Int32Array", int32array_ctor, int32array_from);
1520    register_typed_array("Uint32Array", uint32array_ctor, uint32array_from);
1521    register_typed_array("Float16Array", float16array_ctor, float16array_from);
1522    register_typed_array("Float32Array", float32array_ctor, float32array_from);
1523    register_typed_array("Float64Array", float64array_ctor, float64array_from);
1524    // `Uint8Array.fromBase64`/`fromHex`(ES2024/2025 静的メソッド)。
1525    if let Some(Value::Object(u8a)) = g.vars.get("Uint8Array").cloned() {
1526        u8a.borrow_mut()
1527            .props
1528            .insert("fromBase64".into(), nv("fromBase64", ta_from_base64));
1529        u8a.borrow_mut()
1530            .props
1531            .insert("fromHex".into(), nv("fromHex", ta_from_hex));
1532    }
1533    // DataView(ES2015)。丸ごと未対応で `new DataView(buf)` が
1534    // `DataView is not defined` になっていた。ArrayBuffer が実バイト列を持たない
1535    // 独立コピー簡易実装なのと同様、内部にバイト列(`ObjKind::Array` 流用)を
1536    // 独自に確保する(真の共有メモリビューではない)。
1537    g.vars.insert("DataView".into(), nv("DataView", dataview_ctor));
1538    // Blob(File API の基礎コンテナ。丸ごと未対応だった。File/FileReader/実 I/O は
1539    // この OS には実ファイル選択 UI が存在しないため対象外のまま、メモリ上のバイト列
1540    // コンテナとしての Blob 単体のみ対応する)。
1541    g.vars.insert("Blob".into(), nv("Blob", blob_ctor));
1542    // `File`(`Blob` を継承し `name`/`lastModified` を追加するファイル API の
1543    // 基礎コンストラクタ)が丸ごと未対応だった。`Blob` は既に対応済みだったため、
1544    // `new File(['x'], 'a.txt', {type, lastModified})` という定番パターンが
1545    // `File is not defined` になっていた。
1546    g.vars.insert("File".into(), nv("File", file_ctor));
1547    // `new Option(text?, value?, defaultSelected?, selected?)`(HTML5。
1548    // `document.createElement('option')` + `.text`/`.value`/`.selected` の
1549    // 個別代入を1呼び出しに縮める定番ショートハンド)が丸ごと未対応だった
1550    // (`Option is not defined`)。`defaultSelected` は `selected` 属性の
1551    // 初期値として、`selected` は同じ意味の別名として扱う簡略方針(本物の
1552    // `defaultSelected`/`selected` プロパティ分離は `input.defaultValue` と
1553    // 同種の既知の未対応)。
1554    g.vars.insert("Option".into(), nv("Option", option_ctor));
1555    // `FileReader`(`Blob`/`File` の内容を非同期に読み出す定番の従来型 API。
1556    // `blob.text()`/`.arrayBuffer()` という Promise 版は既に対応済みだったが、
1557    // `reader.readAsText(blob)` + `reader.onload = ...` という古典的な
1558    // イベントベースの書き方自体が丸ごと未対応だった)が丸ごと未対応だった。
1559    // 実際の非同期 I/O が無いこの処理系では読み出しは同期的に完了するため、
1560    // 完了後すぐに `onload`/`onloadend` を呼ぶ簡略実装とする。
1561    g.vars
1562        .insert("FileReader".into(), nv("FileReader", file_reader_ctor));
1563
1564    // Notification(丸ごと未対応だった)。この OS には通知 UI 自体が無いため、
1565    // `element.requestFullscreen()`/`document.execCommand` と同じ「実際の見た目の
1566    // 変化は伴わないが JS 側の契約(プロパティ/Promise の形)は満たす」簡略実装
1567    // とする。許可拒否ダイアログが存在しないため常に `"granted"`。詳細は
1568    // notification_ctor 参照。
1569    {
1570        let notification_ctor_val = nv("Notification", notification_ctor);
1571        if let Value::Object(o) = &notification_ctor_val {
1572            let mut b = o.borrow_mut();
1573            b.props.insert("permission".into(), Value::str("granted"));
1574            b.props.insert(
1575                "requestPermission".into(),
1576                nv("requestPermission", notification_request_permission),
1577            );
1578        }
1579        g.vars.insert("Notification".into(), notification_ctor_val);
1580    }
1581
1582    // TextEncoder / TextDecoder
1583    g.vars
1584        .insert("TextEncoder".into(), nv("TextEncoder", text_encoder_ctor));
1585    g.vars
1586        .insert("TextDecoder".into(), nv("TextDecoder", text_decoder_ctor));
1587
1588    // DOMParser(DOM Parsing and Serialization API。HTML文字列からDOMツリーを生成)
1589    g.vars
1590        .insert("DOMParser".into(), nv("DOMParser", dom_parser_ctor));
1591
1592    // crypto
1593    let crypto = Obj::plain();
1594    {
1595        let mut c = crypto.borrow_mut();
1596        c.props
1597            .insert("randomUUID".into(), nv("randomUUID", crypto_random_uuid));
1598        c.props.insert(
1599            "getRandomValues".into(),
1600            nv("getRandomValues", crypto_get_random_values),
1601        );
1602        // `crypto.subtle`(SubtleCrypto)が丸ごと未対応だった。`digest('SHA-256',
1603        // data)`のみの部分実装(詳細はbuiltins/crypto_subtle.rsのモジュール
1604        // doc参照。2026-07-18 発見・実装)。
1605        let subtle = Obj::plain();
1606        subtle
1607            .borrow_mut()
1608            .props
1609            .insert("digest".into(), nv("digest", subtle_digest));
1610        c.props.insert("subtle".into(), Value::Object(subtle));
1611    }
1612    g.vars.insert("crypto".into(), Value::Object(crypto));
1613
1614    // structuredClone
1615    g.vars.insert(
1616        "structuredClone".into(),
1617        nv("structuredClone", structured_clone),
1618    );
1619
1620    // AbortController(fetch のキャンセル等。signal.aborted を立てる最小実装)
1621    g.vars.insert(
1622        "AbortController".into(),
1623        nv("AbortController", abort_controller_ctor),
1624    );
1625    // `AbortSignal`(ES2022。`AbortSignal.timeout(ms)`/`.abort(reason)` という
1626    // fetch タイムアウト実装の定番イディオムで使われる静的ファクトリ)が、
1627    // `AbortController` 経由でしか signal を得られず丸ごと未対応だった。
1628    // `new AbortSignal()` は仕様上直接構築不可のため、静的メソッドのみを持つ
1629    // ただのオブジェクトとして登録する。
1630    {
1631        let abort_signal_ns = Obj::plain();
1632        abort_signal_ns
1633            .borrow_mut()
1634            .props
1635            .insert("timeout".into(), nv("AbortSignal.timeout", abort_signal_timeout));
1636        abort_signal_ns
1637            .borrow_mut()
1638            .props
1639            .insert("abort".into(), nv("AbortSignal.abort", abort_signal_abort_static));
1640        abort_signal_ns
1641            .borrow_mut()
1642            .props
1643            .insert("any".into(), nv("AbortSignal.any", abort_signal_any));
1644        g.vars
1645            .insert("AbortSignal".into(), Value::Object(abort_signal_ns));
1646    }
1647    // EventTarget(丸ごと未対応だった。DOM 要素以外で独自のイベント発行/購読の
1648    // 基盤として使う定番パターン。他の EventTarget 風オブジェクト[XHR/AbortSignal]と
1649    // 違い、型ごとに複数リスナを保持する仕様どおりの実装)。
1650    g.vars
1651        .insert("EventTarget".into(), nv("EventTarget", event_target_ctor));
1652    // MessageChannel/MessagePort(丸ごと未対応だった。`new MessageChannel()` の
1653    // `.port1`/`.port2` 双方向チャネルで Worker 間通信を模す定番パターン。
1654    // 詳細は message_channel_ctor 参照)。
1655    g.vars
1656        .insert("MessageChannel".into(), nv("MessageChannel", message_channel_ctor));
1657    // BroadcastChannel(丸ごと未対応だった。`new BroadcastChannel(name)` で同名の
1658    // 他インスタンス全てへブロードキャストする定番パターン。詳細は
1659    // broadcast_channel_ctor 参照)。
1660    g.vars
1661        .insert("BroadcastChannel".into(), nv("BroadcastChannel", broadcast_channel_ctor));
1662    g.vars.insert("CSSStyleSheet".into(), Value::Object(Obj::plain()));
1663    g.vars.insert("CSSStyleRule".into(), Value::Object(Obj::plain()));
1664    // DOM コンストラクタ階層 (Node -> Element -> HTMLElement -> HTMLXxxElement)
1665    // 仕様どおり TypeError("Illegal constructor") を投げる NativeFunction コンストラクタと
1666    // 正しい prototype 継承チェーンを構築し、`el instanceof HTMLInputElement` や `el instanceof Node` を可能にする。
1667    {
1668        let make_dom_cls = |name: &str, parent_proto: Option<&ObjRef>| -> (Value, ObjRef) {
1669            // `new Node()`/`new HTMLElement()`等(仕様上どれも直接構築不可の
1670            // 抽象/イリーガルコンストラクタ)は`TypeError`を投げるべきだが、
1671            // 以前は`it.error()`(`Error.prototype`に繋がる汎用エラー)を
1672            // 使っていたため、メッセージ文字列こそ"TypeError: ..."だが
1673            // `instanceof TypeError`は満たさないバグだった(2026-07-18
1674            // 発見・修正)。
1675            let ctor = Obj::native(name, |it, _, _| Err(it.type_error("Illegal constructor")));
1676            let proto = Obj::plain();
1677            if let Some(parent) = parent_proto {
1678                proto.borrow_mut().proto = Some(parent.clone());
1679            }
1680            proto
1681                .borrow_mut()
1682                .props
1683                .insert("constructor".into(), Value::Object(ctor.clone()));
1684            ctor.borrow_mut()
1685                .props
1686                .insert("prototype".into(), Value::Object(proto.clone()));
1687            (Value::Object(ctor), proto)
1688        };
1689
1690        let (node_ctor, node_proto) = make_dom_cls("Node", None);
1691        let (element_ctor, element_proto) = make_dom_cls("Element", Some(&node_proto));
1692        let (html_element_ctor, html_element_proto) = make_dom_cls("HTMLElement", Some(&element_proto));
1693
1694        let consts: &[(&str, f64)] = &[
1695            ("ELEMENT_NODE", 1.0),
1696            ("ATTRIBUTE_NODE", 2.0),
1697            ("TEXT_NODE", 3.0),
1698            ("CDATA_SECTION_NODE", 4.0),
1699            ("PROCESSING_INSTRUCTION_NODE", 7.0),
1700            ("COMMENT_NODE", 8.0),
1701            ("DOCUMENT_NODE", 9.0),
1702            ("DOCUMENT_TYPE_NODE", 10.0),
1703            ("DOCUMENT_FRAGMENT_NODE", 11.0),
1704            ("DOCUMENT_POSITION_DISCONNECTED", 1.0),
1705            ("DOCUMENT_POSITION_PRECEDING", 2.0),
1706            ("DOCUMENT_POSITION_FOLLOWING", 4.0),
1707            ("DOCUMENT_POSITION_CONTAINS", 8.0),
1708            ("DOCUMENT_POSITION_CONTAINED_BY", 16.0),
1709            ("DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", 32.0),
1710        ];
1711        if let Value::Object(c) = &node_ctor {
1712            for (name, val) in consts {
1713                c.borrow_mut()
1714                    .props
1715                    .insert(String::from(*name), Value::Number(*val));
1716            }
1717        }
1718
1719        g.vars.insert("Node".into(), node_ctor);
1720        g.vars.insert("Element".into(), element_ctor);
1721        g.vars.insert("HTMLElement".into(), html_element_ctor);
1722
1723        let (nodelist_ctor, nodelist_proto) = make_dom_cls("NodeList", None);
1724        let (htmlcollection_ctor, _) = make_dom_cls("HTMLCollection", None);
1725        let (radionodelist_ctor, _) = make_dom_cls("RadioNodeList", Some(&nodelist_proto));
1726        g.vars.insert("NodeList".into(), nodelist_ctor);
1727        g.vars.insert("HTMLCollection".into(), htmlcollection_ctor);
1728        g.vars.insert("RadioNodeList".into(), radionodelist_ctor);
1729
1730        let html_classes = &[
1731            "HTMLInputElement",
1732            "HTMLFormElement",
1733            "HTMLAnchorElement",
1734            "HTMLImageElement",
1735            "HTMLButtonElement",
1736            "HTMLSelectElement",
1737            "HTMLOptionElement",
1738            "HTMLTextAreaElement",
1739            "HTMLDivElement",
1740            "HTMLSpanElement",
1741            "HTMLScriptElement",
1742            "HTMLStyleElement",
1743            "HTMLIFrameElement",
1744            "HTMLCanvasElement",
1745            "HTMLParagraphElement",
1746            "HTMLHeadingElement",
1747            "HTMLTableElement",
1748            "HTMLTableRowElement",
1749            "HTMLTableCellElement",
1750            "HTMLUListElement",
1751            "HTMLLIElement",
1752            "HTMLTemplateElement",
1753            "HTMLUnknownElement",
1754        ];
1755
1756        for cls_name in html_classes {
1757            let (ctor, _) = make_dom_cls(cls_name, Some(&html_element_proto));
1758            if *cls_name == "HTMLScriptElement" {
1759                if let Value::Object(c) = &ctor {
1760                    c.borrow_mut().props.insert(
1761                        String::from("supports"),
1762                        nv("HTMLScriptElement.supports", |_: &mut Interp, _: Value, a: &[Value]| {
1763                            let ty = a.first().map(|v| v.to_js_string()).unwrap_or_default();
1764                            Ok(Value::Bool(matches!(ty.as_str(), "classic" | "module")))
1765                        }),
1766                    );
1767                }
1768            }
1769            g.vars.insert((*cls_name).into(), ctor);
1770        }
1771    }
1772
1773    // IntersectionObserver(DOM Ready 後にコールバックを全要素 visible で発火)
1774    g.vars.insert(
1775        "IntersectionObserver".into(),
1776        nv("IntersectionObserver", intersection_observer_ctor),
1777    );
1778
1779    // ResizeObserver(observe 時に即時コールバック発火)
1780    g.vars.insert(
1781        "ResizeObserver".into(),
1782        nv("ResizeObserver", resize_observer_ctor),
1783    );
1784
1785    // MutationObserver(observe/disconnect no-op として登録)
1786    g.vars.insert(
1787        "MutationObserver".into(),
1788        nv("MutationObserver", mutation_observer_ctor),
1789    );
1790
1791    // CustomEvent コンストラクタ
1792    g.vars
1793        .insert("CustomEvent".into(), nv("CustomEvent", custom_event_ctor));
1794
1795    // Event コンストラクタ(new Event('foo', {bubbles, cancelable}))
1796    // `Event.NONE`/`.CAPTURING_PHASE`/`.AT_TARGET`/`.BUBBLING_PHASE`
1797    // (`event.eventPhase === Event.AT_TARGET` という `Node.ELEMENT_NODE` と
1798    // 同型の定番パターンで使われる標準定数)が丸ごと未対応だった。
1799    {
1800        let event_ctor_val = nv("Event", event_ctor);
1801        if let Value::Object(o) = &event_ctor_val {
1802            let phase_consts: &[(&str, f64)] = &[
1803                ("NONE", 0.0),
1804                ("CAPTURING_PHASE", 1.0),
1805                ("AT_TARGET", 2.0),
1806                ("BUBBLING_PHASE", 3.0),
1807            ];
1808            for (name, val) in phase_consts {
1809                o.borrow_mut()
1810                    .props
1811                    .insert(String::from(*name), Value::Number(*val));
1812            }
1813        }
1814        g.vars.insert("Event".into(), event_ctor_val);
1815    }
1816
1817    // KeyboardEvent/MouseEvent コンストラクタ(丸ごと未対応だった。
1818    // `el.dispatchEvent(new KeyboardEvent('keydown', {key:'a'}))`/
1819    // `new MouseEvent('click', {clientX, clientY})` というシミュレーション
1820    // イベント構築の定番パターン。詳細は各 ctor 参照)。
1821    g.vars
1822        .insert("KeyboardEvent".into(), nv("KeyboardEvent", keyboard_event_ctor));
1823    g.vars
1824        .insert("MouseEvent".into(), nv("MouseEvent", mouse_event_ctor));
1825    // WheelEvent/FocusEvent コンストラクタ(丸ごと未対応だった。詳細は
1826    // 各 ctor 参照)。
1827    g.vars
1828        .insert("WheelEvent".into(), nv("WheelEvent", wheel_event_ctor));
1829    g.vars
1830        .insert("FocusEvent".into(), nv("FocusEvent", focus_event_ctor));
1831    // Touch/TouchEvent コンストラクタ(丸ごと未対応だった。詳細は各 ctor 参照)。
1832    g.vars.insert("Touch".into(), nv("Touch", touch_ctor));
1833    g.vars
1834        .insert("TouchEvent".into(), nv("TouchEvent", touch_event_ctor));
1835    // `DOMRect`/`DOMRectReadOnly`コンストラクタ(丸ごと未対応だった。
1836    // Geometry Interfaces Module Level 1 仕様準拠。2026-07-21 拡張)。
1837    let dom_rect_fn = nv("DOMRect", dom_rect_ctor);
1838    if let Value::Object(ref o) = dom_rect_fn {
1839        o.borrow_mut().props.insert("fromRect".into(), nv("fromRect", dom_rect_from_rect));
1840    }
1841    g.vars.insert("DOMRect".into(), dom_rect_fn.clone());
1842    g.vars.insert("DOMRectReadOnly".into(), dom_rect_fn);
1843
1844    // `DOMPoint`/`DOMPointReadOnly`コンストラクタ(丸ごと未対応だった。
1845    // Geometry Interfaces Module Level 1 仕様準拠。2026-07-21 拡張)。
1846    let dom_point_fn = nv("DOMPoint", dom_point_ctor);
1847    if let Value::Object(ref o) = dom_point_fn {
1848        o.borrow_mut().props.insert("fromPoint".into(), nv("fromPoint", dom_point_from_point));
1849    }
1850    g.vars.insert("DOMPoint".into(), dom_point_fn.clone());
1851    g.vars.insert("DOMPointReadOnly".into(), dom_point_fn);
1852    // `DOMMatrix`/`DOMMatrixReadOnly`/`DOMQuad`コンストラクタ(丸ごと未対応だった。
1853    // Geometry Interfaces Module Level 1 仕様準拠。2026-07-21 実装)。
1854    let dom_matrix_fn = nv("DOMMatrix", dom_matrix_ctor);
1855    if let Value::Object(ref o) = dom_matrix_fn {
1856        o.borrow_mut().props.insert("fromMatrix".into(), nv("fromMatrix", |it, _, a| dom_matrix_ctor(it, Value::Undefined, a)));
1857        o.borrow_mut().props.insert("fromFloat32Array".into(), nv("fromFloat32Array", |it, _, a| dom_matrix_ctor(it, Value::Undefined, a)));
1858        o.borrow_mut().props.insert("fromFloat64Array".into(), nv("fromFloat64Array", |it, _, a| dom_matrix_ctor(it, Value::Undefined, a)));
1859    }
1860    g.vars.insert("DOMMatrix".into(), dom_matrix_fn.clone());
1861    g.vars.insert("DOMMatrixReadOnly".into(), dom_matrix_fn);
1862
1863    let dom_quad_fn = nv("DOMQuad", dom_quad_ctor);
1864    if let Value::Object(ref o) = dom_quad_fn {
1865        o.borrow_mut().props.insert("fromRect".into(), nv("fromRect", |it, _, a| {
1866            let r = arg(a, 0);
1867            let rx = it.get_property(&r, "x").unwrap_or(Value::Number(0.0)).to_number();
1868            let ry = it.get_property(&r, "y").unwrap_or(Value::Number(0.0)).to_number();
1869            let rw = it.get_property(&r, "width").unwrap_or(Value::Number(0.0)).to_number();
1870            let rh = it.get_property(&r, "height").unwrap_or(Value::Number(0.0)).to_number();
1871            let p1 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx), Value::Number(ry)])?;
1872            let p2 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx + rw), Value::Number(ry)])?;
1873            let p3 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx + rw), Value::Number(ry + rh)])?;
1874            let p4 = dom_point_ctor(it, Value::Undefined, &[Value::Number(rx), Value::Number(ry + rh)])?;
1875            dom_quad_ctor(it, Value::Undefined, &[p1, p2, p3, p4])
1876        }));
1877    }
1878    g.vars.insert("DOMQuad".into(), dom_quad_fn);
1879    g.vars.insert("Path2D".into(), nv("Path2D", dom_path2d_ctor));
1880
1881    // HashChangeEvent/PageTransitionEvent コンストラクタ(丸ごと未対応
1882    // だった。詳細は各 ctor 参照)。
1883    g.vars.insert(
1884        "HashChangeEvent".into(),
1885        nv("HashChangeEvent", hash_change_event_ctor),
1886    );
1887    g.vars.insert(
1888        "PageTransitionEvent".into(),
1889        nv("PageTransitionEvent", page_transition_event_ctor),
1890    );
1891    // PopStateEvent コンストラクタ(丸ごと未対応だった。詳細は
1892    // `pop_state_event_ctor` 参照。2026-07-17 発見・実装)。
1893    g.vars.insert(
1894        "PopStateEvent".into(),
1895        nv("PopStateEvent", pop_state_event_ctor),
1896    );
1897    // PointerEvent/InputEvent コンストラクタ(丸ごと未対応だった。詳細は
1898    // 各 ctor 参照)。
1899    g.vars
1900        .insert("PointerEvent".into(), nv("PointerEvent", pointer_event_ctor));
1901    g.vars
1902        .insert("InputEvent".into(), nv("InputEvent", input_event_ctor));
1903    // ProgressEvent/StorageEvent コンストラクタ(丸ごと未対応だった。詳細は
1904    // 各 ctor 参照)。
1905    g.vars
1906        .insert("ProgressEvent".into(), nv("ProgressEvent", progress_event_ctor));
1907    g.vars
1908        .insert("StorageEvent".into(), nv("StorageEvent", storage_event_ctor));
1909    // CommandEvent/ToggleEvent/FormDataEvent コンストラクタ(WHATWG HTML Standard 準拠)
1910    g.vars
1911        .insert("CommandEvent".into(), nv("CommandEvent", command_event_ctor));
1912    g.vars
1913        .insert("ToggleEvent".into(), nv("ToggleEvent", toggle_event_ctor));
1914    g.vars
1915        .insert("FormDataEvent".into(), nv("FormDataEvent", form_data_event_ctor));
1916
1917    // CompositionEvent/ClipboardEvent コンストラクタ(丸ごと未対応だった。
1918    // 詳細は各 ctor 参照)。
1919    g.vars.insert(
1920        "CompositionEvent".into(),
1921        nv("CompositionEvent", composition_event_ctor),
1922    );
1923    g.vars
1924        .insert("ClipboardEvent".into(), nv("ClipboardEvent", clipboard_event_ctor));
1925    // AnimationEvent/TransitionEvent/DragEvent/SubmitEvent コンストラクタ
1926    // (丸ごと未対応だった。詳細は各 ctor 参照)。
1927    g.vars
1928        .insert("AnimationEvent".into(), nv("AnimationEvent", animation_event_ctor));
1929    g.vars
1930        .insert("TransitionEvent".into(), nv("TransitionEvent", transition_event_ctor));
1931    g.vars.insert("DragEvent".into(), nv("DragEvent", drag_event_ctor));
1932    g.vars
1933        .insert("SubmitEvent".into(), nv("SubmitEvent", submit_event_ctor));
1934    // MessageEvent/ErrorEvent コンストラクタ(丸ごと未対応だった。詳細は
1935    // 各 ctor 参照)。
1936    g.vars
1937        .insert("MessageEvent".into(), nv("MessageEvent", message_event_ctor));
1938    g.vars.insert("ErrorEvent".into(), nv("ErrorEvent", error_event_ctor));
1939
1940    // window.scrollY / pageYOffset / scrollTo / scrollX
1941    g.vars.insert("scrollY".into(), Value::Number(0.0));
1942    g.vars.insert("pageYOffset".into(), Value::Number(0.0));
1943    g.vars.insert("scrollX".into(), Value::Number(0.0));
1944    g.vars.insert("pageXOffset".into(), Value::Number(0.0));
1945    g.vars
1946        .insert("scrollTo".into(), nv("scrollTo", window_scroll_to));
1947    g.vars
1948        .insert("scroll".into(), nv("scroll", window_scroll_to));
1949    // `window.scrollBy(x, y)`(丸ごと未対応だった。`dom_noop`のまま何もせず
1950    // 相対スクロールが一切機能しなかった。2026-07-16 発見・実装)。
1951    g.vars
1952        .insert("scrollBy".into(), nv("scrollBy", window_scroll_by));
1953    g.vars
1954        .insert("matchMedia".into(), nv("matchMedia", window_match_media));
1955
1956    // window.load イベント(addEventListener('load', cb) → cb を即発火)
1957    // 実装は window の addEventListener で load を即時処理。
1958    g.vars.insert("location".into(), Value::Object(location));
1959    g.vars.insert("history".into(), Value::Object(history));
1960    g.vars.insert("alert".into(), nv("alert", console_log));
1961    // `confirm`/`prompt`(bare修飾無し形。丸ごと未対応だった。理由は
1962    // `window.confirm`/`.prompt`と同じ。2026-07-16 発見・実装)。
1963    g.vars
1964        .insert("confirm".into(), nv("confirm", window_confirm));
1965    g.vars.insert("prompt".into(), nv("prompt", window_prompt));
1966    // `open`/`close`/`focus`/`blur`(bare修飾無し形。丸ごと未対応だった。
1967    // 理由は`window.open`等と同じ。2026-07-16 発見・実装)。
1968    g.vars.insert("open".into(), nv("open", window_open));
1969    g.vars.insert("close".into(), nv("close", window_close));
1970    // `stop`(bare修飾無し形。丸ごと未対応だった。2026-07-17 発見・実装)。
1971    g.vars.insert("stop".into(), nv("stop", window_stop));
1972    g.vars.insert("focus".into(), nv("focus", window_focus));
1973    g.vars.insert("blur".into(), nv("blur", window_focus));
1974    // `getSelection`(bare修飾無し形。丸ごと未対応だった。理由は
1975    // `window.getSelection`と同じ。2026-07-16 発見・実装)。
1976    g.vars
1977        .insert("getSelection".into(), nv("getSelection", document_get_selection));
1978
1979    // Map / Set コンストラクタ
1980    let map_ctor_v = nv("Map", map_ctor);
1981    if let Value::Object(mc) = &map_ctor_v {
1982        mc.borrow_mut()
1983            .props
1984            .insert("groupBy".into(), nv("Map.groupBy", map_group_by));
1985    }
1986    g.vars.insert("Map".into(), map_ctor_v);
1987    g.vars.insert("Set".into(), nv("Set", set_ctor));
1988
1989    // Promise コンストラクタ + 静的メソッド
1990    let promise_ctor_v = nv("Promise", promise_ctor);
1991    if let Value::Object(pc) = &promise_ctor_v {
1992        pc.borrow_mut().props.insert(
1993            "resolve".into(),
1994            nv("Promise.resolve", promise_resolve_static),
1995        );
1996        pc.borrow_mut()
1997            .props
1998            .insert("reject".into(), nv("Promise.reject", promise_reject_static));
1999        pc.borrow_mut()
2000            .props
2001            .insert("all".into(), nv("Promise.all", promise_all_static));
2002        pc.borrow_mut().props.insert(
2003            "allSettled".into(),
2004            nv("Promise.allSettled", promise_all_settled_static),
2005        );
2006        pc.borrow_mut()
2007            .props
2008            .insert("race".into(), nv("Promise.race", promise_race_static));
2009        pc.borrow_mut()
2010            .props
2011            .insert("any".into(), nv("Promise.any", promise_any_static));
2012        pc.borrow_mut().props.insert(
2013            "withResolvers".into(),
2014            nv("Promise.withResolvers", promise_with_resolvers_static),
2015        );
2016        pc.borrow_mut()
2017            .props
2018            .insert("try".into(), nv("Promise.try", promise_try_static));
2019    }
2020    g.vars.insert("Promise".into(), promise_ctor_v);
2021
2022    // Proxy コンストラクタ
2023    g.vars.insert("Proxy".into(), nv("Proxy", proxy_ctor));
2024
2025    // Reflect グローバルオブジェクト
2026    let reflect = Obj::plain();
2027    {
2028        let mut r = reflect.borrow_mut();
2029        r.props.insert("get".into(), nv("Reflect.get", reflect_get));
2030        r.props.insert("set".into(), nv("Reflect.set", reflect_set));
2031        r.props.insert("has".into(), nv("Reflect.has", reflect_has));
2032        r.props.insert(
2033            "deleteProperty".into(),
2034            nv("Reflect.deleteProperty", reflect_delete_property),
2035        );
2036        r.props
2037            .insert("ownKeys".into(), nv("Reflect.ownKeys", reflect_own_keys));
2038        r.props.insert(
2039            "getPrototypeOf".into(),
2040            nv("Reflect.getPrototypeOf", reflect_get_prototype_of),
2041        );
2042        r.props.insert(
2043            "defineProperty".into(),
2044            nv("Reflect.defineProperty", reflect_define_property),
2045        );
2046        r.props
2047            .insert("apply".into(), nv("Reflect.apply", reflect_apply));
2048        r.props.insert(
2049            "construct".into(),
2050            nv("Reflect.construct", reflect_construct),
2051        );
2052        // `setPrototypeOf`/`isExtensible`/`preventExtensions`/`getOwnPropertyDescriptor` が
2053        // 丸ごと欠落していた(`Object` 側の対応する静的メソッドと引数の形が同じなので
2054        // そのまま流用する)。
2055        r.props.insert(
2056            "setPrototypeOf".into(),
2057            nv("Reflect.setPrototypeOf", object_set_prototype_of),
2058        );
2059        r.props.insert(
2060            "isExtensible".into(),
2061            nv("Reflect.isExtensible", object_is_extensible),
2062        );
2063        r.props.insert(
2064            "preventExtensions".into(),
2065            nv("Reflect.preventExtensions", object_prevent_extensions),
2066        );
2067        r.props.insert(
2068            "getOwnPropertyDescriptor".into(),
2069            nv(
2070                "Reflect.getOwnPropertyDescriptor",
2071                object_get_own_property_descriptor,
2072            ),
2073        );
2074    }
2075    g.vars.insert("Reflect".into(), Value::Object(reflect));
2076
2077    // Iterator(ES2025)。コンストラクタ自体は呼び出し不可扱いとし、静的メソッド
2078    // `Iterator.from(iterable)` のみを提供する簡略実装。
2079    let iterator_ctor = nv("Iterator", |_: &mut Interp, _: Value, _: &[Value]| {
2080        Ok(Value::Undefined)
2081    });
2082    if let Value::Object(ic) = &iterator_ctor {
2083        ic.borrow_mut()
2084            .props
2085            .insert("from".into(), nv("Iterator.from", iterator_from));
2086        ic.borrow_mut()
2087            .props
2088            .insert("range".into(), nv("Iterator.range", iterator_range));
2089        ic.borrow_mut()
2090            .props
2091            .insert("concat".into(), nv("Iterator.concat", iterator_concat_static));
2092    }
2093    g.vars.insert("Iterator".into(), iterator_ctor);
2094
2095    // タイマー(イベントループのマクロタスク)。遅延は無視し、登録順に実行する。
2096    g.vars
2097        .insert("setTimeout".into(), nv("setTimeout", set_timeout));
2098    g.vars
2099        .insert("setInterval".into(), nv("setInterval", set_interval));
2100    g.vars
2101        .insert("clearTimeout".into(), nv("clearTimeout", clear_timeout));
2102    g.vars
2103        .insert("clearInterval".into(), nv("clearInterval", clear_interval));
2104    g.vars.insert(
2105        "queueMicrotask".into(),
2106        nv("queueMicrotask", queue_microtask),
2107    );
2108    // `reportError(error)`(丸ごと未対応だった。未捕捉例外と同じ経路で
2109    // `window.onerror`/`window.addEventListener('error', fn)` へ報告する
2110    // 標準グローバル関数。詳細は report_error 参照)。
2111    g.vars
2112        .insert("reportError".into(), nv("reportError", report_error));
2113    g.vars.insert(
2114        "requestAnimationFrame".into(),
2115        nv("requestAnimationFrame", request_animation_frame),
2116    );
2117    g.vars.insert(
2118        "cancelAnimationFrame".into(),
2119        nv("cancelAnimationFrame", clear_timeout),
2120    );
2121    g.vars.insert(
2122        "requestIdleCallback".into(),
2123        nv("requestIdleCallback", request_idle_callback),
2124    );
2125    g.vars.insert(
2126        "cancelIdleCallback".into(),
2127        nv("cancelIdleCallback", dom_noop),
2128    );
2129
2130    // Date。`RegExp`/`Map`/`Set` と同じ設計で、ネイティブコンストラクタが
2131    // `construct_object` が用意する `this` を無視して `ObjKind::DateObj` を積んだ
2132    // 新規オブジェクトを直接返す(メソッドは `.proto` 経由ではなく、`get_property` の
2133    // `ObjKind` 特殊扱いで解決される。`instanceof Date` は組込み Array/Object と同様に
2134    // false になる既知の簡略化)。
2135    let date_ctor_obj = Obj::native("Date", date_ctor);
2136    date_ctor_obj
2137        .borrow_mut()
2138        .props
2139        .insert("now".into(), nv("Date.now", date_now_static));
2140    date_ctor_obj
2141        .borrow_mut()
2142        .props
2143        .insert("parse".into(), nv("Date.parse", date_parse_static));
2144    // `Date.now`/`Date.parse` は登録済みだったが、3つ揃いのはずの `Date.UTC`
2145    // (年月日等の成分から epoch ミリ秒を直接得る静的ファクトリ。`new Date(...)`
2146    // の多引数コンストラクタと違い、これ自体は Date インスタンスではなく数値を
2147    // 返す)が丸ごと未対応だった。
2148    date_ctor_obj
2149        .borrow_mut()
2150        .props
2151        .insert("UTC".into(), nv("Date.UTC", date_utc_static));
2152    g.vars.insert("Date".into(), Value::Object(date_ctor_obj));
2153
2154    // Error 階層。各サブタイプの `.prototype` は `Error.prototype` を proto に持つため、
2155    // `new TypeError() instanceof Error` 等が正しく true になる(OrdinaryHasInstance 相当の
2156    // `instanceof` 実装と対で意味を持つ。詳細は spec/walkthrough.md 2026-07-07 参照)。
2157    let error_proto = Obj::plain();
2158    error_proto
2159        .borrow_mut()
2160        .props
2161        .insert("name".into(), Value::str("Error"));
2162    error_proto
2163        .borrow_mut()
2164        .props
2165        .insert("message".into(), Value::str(""));
2166    let error_ctor_obj = Obj::native("Error", error_ctor);
2167    error_ctor_obj
2168        .borrow_mut()
2169        .props
2170        .insert("prototype".into(), Value::Object(error_proto.clone()));
2171    error_proto
2172        .borrow_mut()
2173        .props
2174        .insert("constructor".into(), Value::Object(error_ctor_obj.clone()));
2175    error_ctor_obj
2176        .borrow_mut()
2177        .props
2178        .insert("isError".into(), nv("Error.isError", error_is_error));
2179    g.vars.insert("Error".into(), Value::Object(error_ctor_obj));
2180
2181    fn make_error_subtype(
2182        name: &str,
2183        error_proto: &ObjRef,
2184        ctor_fn: NativeFn,
2185    ) -> Value {
2186        let proto = Obj::plain();
2187        proto.borrow_mut().proto = Some(error_proto.clone());
2188        proto
2189            .borrow_mut()
2190            .props
2191            .insert("name".into(), Value::str(name));
2192        let ctor_obj = Obj::native(name, ctor_fn);
2193        ctor_obj
2194            .borrow_mut()
2195            .props
2196            .insert("prototype".into(), Value::Object(proto.clone()));
2197        proto
2198            .borrow_mut()
2199            .props
2200            .insert("constructor".into(), Value::Object(ctor_obj.clone()));
2201        Value::Object(ctor_obj)
2202    }
2203    g.vars.insert(
2204        "TypeError".into(),
2205        make_error_subtype("TypeError", &error_proto, type_error_ctor),
2206    );
2207    g.vars.insert(
2208        "RangeError".into(),
2209        make_error_subtype("RangeError", &error_proto, range_error_ctor),
2210    );
2211    g.vars.insert(
2212        "SyntaxError".into(),
2213        make_error_subtype("SyntaxError", &error_proto, syntax_error_ctor),
2214    );
2215    g.vars.insert(
2216        "SuppressedError".into(),
2217        make_error_subtype("SuppressedError", &error_proto, suppressed_error_ctor),
2218    );
2219    g.vars.insert(
2220        "AggregateError".into(),
2221        make_error_subtype("AggregateError", &error_proto, aggregate_error_ctor),
2222    );
2223
2224    // `DOMException`(丸ごと未対応だった。Web API 全般が送出する標準例外型。
2225    // `AbortController.abort()` 等の内部エラーはこれまで `name`/`message` のみ
2226    // 持つ簡易オブジェクトで代用していたが、`new DOMException(message, name)`
2227    // というユーザーコードからの直接構築や、レガシーな `.code` 数値定数
2228    // (`err.code === DOMException.NOT_FOUND_ERR` 等)に対応する本物の
2229    // グローバルコンストラクタ自体が存在しなかった。詳細は
2230    // dom_exception_ctor/make_dom_exception 参照)。
2231    {
2232        let dom_exception_ctor_val = nv("DOMException", dom_exception_ctor);
2233        if let Value::Object(o) = &dom_exception_ctor_val {
2234            let mut b = o.borrow_mut();
2235            let legacy_codes: &[(&str, f64)] = &[
2236                ("INDEX_SIZE_ERR", 1.0),
2237                ("HIERARCHY_REQUEST_ERR", 3.0),
2238                ("WRONG_DOCUMENT_ERR", 4.0),
2239                ("INVALID_CHARACTER_ERR", 5.0),
2240                ("NO_MODIFICATION_ALLOWED_ERR", 7.0),
2241                ("NOT_FOUND_ERR", 8.0),
2242                ("NOT_SUPPORTED_ERR", 9.0),
2243                ("INUSE_ATTRIBUTE_ERR", 10.0),
2244                ("INVALID_STATE_ERR", 11.0),
2245                ("SYNTAX_ERR", 12.0),
2246                ("INVALID_MODIFICATION_ERR", 13.0),
2247                ("NAMESPACE_ERR", 14.0),
2248                ("INVALID_ACCESS_ERR", 15.0),
2249                ("SECURITY_ERR", 18.0),
2250                ("NETWORK_ERR", 19.0),
2251                ("ABORT_ERR", 20.0),
2252                ("TIMEOUT_ERR", 23.0),
2253                ("DATA_CLONE_ERR", 25.0),
2254            ];
2255            for (name, code) in legacy_codes {
2256                b.props.insert(String::from(*name), Value::Number(*code));
2257            }
2258        }
2259        g.vars.insert("DOMException".into(), dom_exception_ctor_val);
2260    }
2261
2262    // FinalizationRegistry(GC コールバック。AtmOS は GC 非搭載のため register は no-op)
2263    g.vars.insert(
2264        "FinalizationRegistry".into(),
2265        nv("FinalizationRegistry", finalization_registry_ctor),
2266    );
2267
2268    // fetch(同期HTTP GET を Promise/Response 互換で。data: URL も対応)。
2269    g.vars.insert("fetch".into(), nv("fetch", fetch));
2270    g.vars.insert("Request".into(), nv("Request", request_ctor));
2271    g.vars.insert("Headers".into(), nv("Headers", headers_ctor));
2272    // `Response` コンストラクタ自体が丸ごと未対応だった(これまで `fetch()`
2273    // 内部の `make_response`/`make_response_with_url` からしか生成できず、
2274    // Service Worker やテストコードでの `new Response(JSON.stringify(x),
2275    // {status:200})`/`Response.json(x)` という定番パターンが使えなかった)。
2276    {
2277        let response_ctor_val = nv("Response", response_ctor);
2278        if let Value::Object(o) = &response_ctor_val {
2279            let mut b = o.borrow_mut();
2280            b.props.insert("json".into(), nv("Response.json", response_json_static));
2281            b.props.insert("error".into(), nv("Response.error", response_error_static));
2282            b.props
2283                .insert("redirect".into(), nv("Response.redirect", response_redirect_static));
2284        }
2285        g.vars.insert("Response".into(), response_ctor_val);
2286    }
2287    // XMLHttpRequest(同期取得 + onload/onreadystatechange 発火)。
2288    g.vars
2289        .insert("XMLHttpRequest".into(), nv("XMLHttpRequest", xhr_ctor));
2290
2291    // RegExp コンストラクタ
2292    let regexp_ctor_v = nv("RegExp", regexp_ctor);
2293    if let Value::Object(rc) = &regexp_ctor_v {
2294        rc.borrow_mut()
2295            .props
2296            .insert("escape".into(), nv("RegExp.escape", regexp_escape_static));
2297    }
2298    g.vars.insert("RegExp".into(), regexp_ctor_v);
2299
2300    // WeakMap / WeakSet / WeakRef(Rc ベース近似実装: キーはオブジェクト参照で識別)
2301    g.vars.insert("WeakMap".into(), nv("WeakMap", weakmap_ctor));
2302    g.vars.insert("WeakSet".into(), nv("WeakSet", weakset_ctor));
2303    g.vars.insert("WeakRef".into(), nv("WeakRef", weakref_ctor));
2304
2305    // Symbol(グローバル `Symbol()` 関数: 文字列ラベルを付与した一意値を返す)
2306    {
2307        let sym_fn = nv("Symbol", symbol_fn);
2308        if let Value::Object(sf) = &sym_fn {
2309            sf.borrow_mut().props.insert("for".into(), nv("Symbol.for", symbol_for));
2310            sf.borrow_mut().props.insert("keyFor".into(), nv("Symbol.keyFor", symbol_key_for));
2311            sf.borrow_mut().props.insert("iterator".into(), Value::str("Symbol(Symbol.iterator)"));
2312            sf.borrow_mut().props.insert("toPrimitive".into(), Value::str("Symbol(Symbol.toPrimitive)"));
2313            sf.borrow_mut().props.insert("hasInstance".into(), Value::str("Symbol(Symbol.hasInstance)"));
2314            sf.borrow_mut().props.insert("toStringTag".into(), Value::str("Symbol(Symbol.toStringTag)"));
2315            sf.borrow_mut().props.insert("asyncIterator".into(), Value::str("Symbol(Symbol.asyncIterator)"));
2316            // `Array.prototype.concat` が参照する well-known symbol。定数自体が丸ごと
2317            // 未登録だったため `Symbol.isConcatSpreadable` が常に `undefined` になり、
2318            // `arr[Symbol.isConcatSpreadable] = ...` が実質 `arr["undefined"] = ...` という
2319            // 無関係なプロパティ代入に化けていた。
2320            sf.borrow_mut()
2321                .props
2322                .insert("isConcatSpreadable".into(), Value::str("Symbol(Symbol.isConcatSpreadable)"));
2323            // `Symbol.species`/`unscopables`/`match`/`replace`/`search`/`split` も同じ理由
2324            // (定数自体が未登録だと `obj[Symbol.species] = ...` 等が `obj["undefined"] = ...`
2325            // という無関係なプロパティ代入に化ける)で追加登録する。派生コンストラクタ選択
2326            // (`species`)やカスタムマッチャープロトコル(`match`/`replace`/`search`/`split`。
2327            // `String.prototype` 側のメソッドはこれらを見ずに常に組み込み `RegExp` 実装へ
2328            // 委譲する簡略実装のまま)、`with` 文の `unscopables` フィルタは非対応のままだが、
2329            // 定数自体は仕様どおり存在させ、キー衝突による静かな破壊だけは防ぐ。
2330            for name in [
2331                "species",
2332                "unscopables",
2333                "match",
2334                "replace",
2335                "search",
2336                "split",
2337            ] {
2338                sf.borrow_mut()
2339                    .props
2340                    .insert(String::from(name), Value::str(format!("Symbol(Symbol.{})", name)));
2341            }
2342            // Explicit Resource Management(`using`/`await using` 宣言、ES2023→ES2026ベースライン)。
2343            sf.borrow_mut().props.insert("dispose".into(), Value::str("Symbol(Symbol.dispose)"));
2344            sf.borrow_mut().props.insert("asyncDispose".into(), Value::str("Symbol(Symbol.asyncDispose)"));
2345        }
2346        g.vars.insert("Symbol".into(), sym_fn);
2347
2348        // 各組み込みコンストラクタ関数への Symbol(Symbol.species) アクセサ配線。
2349        // `Symbol.species`は仕様上ゲッターアクセサ(`this`を返すだけ)であり、
2350        // 呼び出し可能な関数値をそのまま`props`にデータプロパティとして
2351        // 差し込んでしまうと、`Array[Symbol.species]`はそのゲッター関数
2352        // オブジェクト自体を返してしまい(呼び出されない)、`=== Array`が
2353        // 常に`false`になるバグだった(2026-07-18 発見・修正)。他の
2354        // アクセサ登録箇所と同じ`accessors`マップへ`Accessor{get,set}`として
2355        // 登録する必要がある。
2356        use super::value::Accessor;
2357        let species_fn = nv("get [Symbol.species]", symbol_species_getter);
2358        for ctor_name in [
2359            "Array", "RegExp", "Promise", "Map", "Set", "ArrayBuffer",
2360            "Uint8Array", "Int8Array", "Uint8ClampedArray", "Int16Array",
2361            "Uint16Array", "Int32Array", "Uint32Array", "Float16Array", "Float32Array",
2362            "Float64Array", "BigInt64Array", "BigUint64Array",
2363        ] {
2364            if let Some(Value::Object(ctor_obj)) = g.vars.get(ctor_name).cloned() {
2365                ctor_obj.borrow_mut().accessors.insert(
2366                    "Symbol(Symbol.species)".into(),
2367                    Accessor { get: Some(species_fn.clone()), set: None },
2368                );
2369            }
2370        }
2371    }
2372
2373    // 定数
2374    g.vars.insert("NaN".into(), Value::Number(f64::NAN));
2375    g.vars
2376        .insert("Infinity".into(), Value::Number(f64::INFINITY));
2377    g.vars.insert("undefined".into(), Value::Undefined);
2378    // globalThis は window オブジェクトを指す(location/history/fetch 等にアクセス可能)。
2379    let global_this = g.vars.get("window").cloned().unwrap_or(Value::Undefined);
2380    g.vars.insert("globalThis".into(), global_this);
2381    // `self`/`top`/`parent`/`frames`(丸ごと未対応だった。`globalThis`は
2382    // 実装済みだったのに、同じ「windowオブジェクト自身を指す別名」仲間の
2383    // うちこれら4つだけ登録が漏れていた。2026-07-16 発見・実装)。
2384    // `self`はワーカー/メインスレッド両対応コードで`typeof self !==
2385    // 'undefined'`という定番の環境判定に使われる。`top`/`parent`/`frames`は
2386    // 本来フレーム階層を表すが、この処理系にはiframeの独立JSレルムが無く
2387    // 常に単一フレームのため、仕様上「フレームが無い最上位ウィンドウでは
2388    // top/parent/framesはいずれも自分自身」という規定どおり`window`自身を
2389    // 指す(`window.top === window`という定番のフレーム内判定イディオムが
2390    // 正しく`true`になる)。
2391    // 【2026-09-05】グローバルにはあるのに `window.` 経由で読めないものを塞ぐ。
2392    //
2393    // `window` は個別に用意したプロパティしか持たず、`navigator` などは
2394    // 別のグローバル変数として登録されているだけだった。
2395    // ブラウザでは同じ物を両方から読めるので、`window.navigator` が
2396    // `undefined` になる。jQuery 1.8.2 は
2397    // `p.uaMatch(a.navigator.userAgent)`(`a` は window)と書くため、
2398    // ここで初期化が止まっていた。
2399    //
2400    // 既に `window` に入っているものは上書きしない(個別に用意した実装を
2401    // 壊さないため)。
2402    if let Some(Value::Object(w)) = g.vars.get("window").cloned() {
2403        for name in WINDOW_MIRRORED_GLOBALS {
2404            if w.borrow().props.contains_key(*name) {
2405                continue;
2406            }
2407            if let Some(v) = g.vars.get(*name).cloned() {
2408                w.borrow_mut()
2409                    .props
2410                    .insert(alloc::string::String::from(*name), v);
2411            }
2412        }
2413    }
2414    let window_ref = g.vars.get("window").cloned().unwrap_or(Value::Undefined);
2415    g.vars.insert("self".into(), window_ref.clone());
2416    g.vars.insert("top".into(), window_ref.clone());
2417    g.vars.insert("parent".into(), window_ref.clone());
2418    g.vars.insert("frames".into(), window_ref);
2419}
2420
2421
2422// ===== 分割サブモジュール(2026-07-16 リファクタ フェーズ2) =====
2423// builtins.rs が肥大化したため、install() 以降の各セクションを builtins/ 配下へ
2424// 機械的に切り出した。ロジックは不変。`builtins::<name>` の外部参照パスを保つため
2425// ここで pub(crate) use で全て再エクスポートする。詳細は walkthrough.md 参照。
2426mod dom;
2427mod console_math_json;
2428mod object_array;
2429mod props_methods;
2430mod collections;
2431mod promise;
2432mod web_forms;
2433mod encoding;
2434mod extra_methods;
2435mod typed_array;
2436mod text_clone;
2437mod extra_static;
2438mod dom_document;
2439mod observers;
2440mod events;
2441mod window_api;
2442mod static_classlist;
2443mod timers_gen;
2444mod weak_symbol;
2445mod regexp;
2446mod date;
2447mod errors_misc;
2448mod intl;
2449mod crypto_subtle;
2450
2451pub(crate) use dom::*;
2452pub(crate) use console_math_json::*;
2453pub(crate) use object_array::*;
2454pub(crate) use props_methods::*;
2455pub(crate) use collections::*;
2456pub(crate) use promise::*;
2457pub(crate) use web_forms::*;
2458pub(crate) use encoding::*;
2459pub(crate) use extra_methods::*;
2460pub(crate) use typed_array::*;
2461pub(crate) use text_clone::*;
2462pub(crate) use extra_static::*;
2463pub(crate) use dom_document::*;
2464pub(crate) use observers::*;
2465pub(crate) use events::*;
2466pub(crate) use window_api::*;
2467pub(crate) use static_classlist::*;
2468pub(crate) use timers_gen::*;
2469pub(crate) use weak_symbol::*;
2470pub(crate) use regexp::*;
2471pub(crate) use date::*;
2472pub(crate) use errors_misc::*;
2473pub(crate) use intl::*;
2474pub(crate) use crypto_subtle::*;