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