Skip to main content

atmos/os_lib/js/
selftest.rs

1//! JS エンジン自己テスト(mod.rs から分割。2026-07-16 リファクタ フェーズ4)。
2//! ロジック不変。`super::*` で mod.rs の JsRuntime/eval/Value 等を取り込む。
3use super::*;
4
5/// JS エンジンの自己テスト。各ケースを評価し、最後の式の文字列化が期待値と一致するか確認。
6/// 起動時に呼ばれ `JS_SELFTEST: PASS n/n` をシリアルへ出す(CI スモークの判定マーカー)。
7pub fn selftest() -> (usize, usize) {
8    let cases: &[(&str, &str)] = &[
9        // 演算子・優先順位
10        ("1 + 2 * 3", "7"),
11        ("(1 + 2) * 3", "9"),
12        ("2 ** 10", "1024"),
13        ("7 % 3", "1"),
14        ("10 / 4", "2.5"),
15        ("true && false || true", "true"),
16        ("null ?? 'x'", "x"),
17        ("1 < 2 ? 'a' : 'b'", "a"),
18        // テンプレートリテラル `${}` 補間(ES2015)。以前はレキサーが `${...}` を
19        // 単なる生文字列として保持するだけで実際の補間評価が行われず(Phase 4 として
20        // 未実装のまま放置されていた)、自己テストも一件も存在しない状態だった。
21        ("var name='World'; `Hello, ${name}!`", "Hello, World!"),
22        ("`1+2=${1+2}`", "1+2=3"),
23        ("var a=1,b=2; `${a}-${b}-${a+b}`", "1-2-3"),
24        ("`no interpolation here`", "no interpolation here"),
25        ("``", ""),
26        ("`${'a'+'b'}${'c'}`", "abc"),
27        // 補間式の中に文字列リテラル(波括弧に見える文字を含む可能性がある)や
28        // オブジェクトリテラル(ネストした `{}`)があっても深さ計算が壊れないこと。
29        ("`x=${'{not a brace}'}`", "x={not a brace}"),
30        ("`obj=${JSON.stringify({a:1})}`", "obj={\"a\":1}"),
31        // 補間式内でのネストしたテンプレートリテラル(二重ネストではない単純な形)。
32        ("`outer ${`inner`}`", "outer inner"),
33        // 文字列
34        ("'a' + 'b' + 'c'", "abc"),
35        ("'Hello'.toUpperCase()", "HELLO"),
36        ("'a,b,c'.split(',').length", "3"),
37        ("'  hi  '.trim()", "hi"),
38        ("'abc'.slice(1)", "bc"),
39        // 変数・制御構文
40        ("var s = 0; for (var i = 1; i <= 5; i++) s += i; s", "15"),
41        ("let n = 0; while (n < 3) n++; n", "3"),
42        ("let x = 10; if (x > 5) { x = 1 } else { x = 2 } x", "1"),
43        // ラベル付き break/continue(ES3以降の基礎機能。以前は AST に Label 相当の
44        // variant 自体が存在せず完全に未サポートだった)。
45        (
46            "var out=''; outer: for(var i=0;i<3;i++){ for(var j=0;j<3;j++){ if(j===1) continue outer; out+=i+''+j; } } out",
47            "001020",
48        ),
49        (
50            "var found=null; outer: for(var i=0;i<3;i++){ for(var j=0;j<3;j++){ if(i===1&&j===1){ found=i+''+j; break outer; } } } found",
51            "11",
52        ),
53        // ラベル無しの break/continue は従来通り最も内側のループのみに作用する
54        // (ラベル対応追加による回帰が無いことの確認)。
55        (
56            "var out=''; for(var i=0;i<3;i++){ for(var j=0;j<3;j++){ if(j===1) break; out+=i+''+j; } } out",
57            "001020",
58        ),
59        // 三項演算子の `:` とラベル構文の衝突が無いこと(`x ? 1 : 2` の `:` は
60        // ラベル判定の対象にならない)。
61        ("var x=5; x > 0 ? 1 : 2", "1"),
62        // `**` の右結合性(`2**3**2` は `2**(3**2)`=512。左結合だと `(2**3)**2`=64になり誤り)。
63        ("2 ** 3 ** 2", "512"),
64        // 論理代入演算子(ES2021): 値としての結果。
65        ("var a=0; a ||= 5; a", "5"),
66        ("var a=1; a ||= 5; a", "1"),
67        ("var a=1; a &&= 5; a", "5"),
68        ("var a=0; a &&= 5; a", "0"),
69        ("var a=null; a ??= 5; a", "5"),
70        ("var a=0; a ??= 5; a", "0"),
71        // 論理代入演算子は仕様上の短絡評価が必須: 条件を満たさない場合、右辺の副作用は
72        // 一切実行されてはならない(以前は関数の先頭で右辺を無条件に評価してから分岐して
73        // いたため、代入されない場合でも副作用が毎回発生してしまう仕様違反バグがあった)。
74        (
75            "var calls=0; function rhs(){calls++; return 9;} var a=1; a ||= rhs(); calls",
76            "0",
77        ),
78        (
79            "var calls=0; function rhs(){calls++; return 9;} var a=0; a &&= rhs(); calls",
80            "0",
81        ),
82        (
83            "var calls=0; function rhs(){calls++; return 9;} var a=5; a ??= rhs(); calls",
84            "0",
85        ),
86        // 条件を満たす場合は右辺が(1回だけ)評価される。
87        (
88            "var calls=0; function rhs(){calls++; return 9;} var a=0; a ||= rhs(); calls+','+a",
89            "1,9",
90        ),
91        // 関数・クロージャ・再帰
92        ("function add(a, b) { return a + b } add(3, 4)", "7"),
93        ("let f = (a, b) => a + b; f(5, 6)", "11"),
94        ("function fib(n){ return n < 2 ? n : fib(n-1) + fib(n-2) } fib(10)", "55"),
95        ("let mk = () => { let c = 0; return () => ++c }; let g = mk(); g(); g(); g()", "3"),
96        // 配列
97        ("[1,2,3].map(x => x * 2).join(',')", "2,4,6"),
98        ("[1,2,3,4].filter(x => x % 2 === 0).join(',')", "2,4"),
99        ("[1,2,3,4].reduce((s, x) => s + x, 0)", "10"),
100        ("[3,1,2].sort((a,b) => a - b).join('')", "123"),
101        ("let a = [1]; a.push(2); a.push(3); a.length", "3"),
102        // オブジェクト
103        ("var o = {a:1, b:2}; o.a + o.b", "3"),
104        ("var o = {}; o['k'] = 9; o.k", "9"),
105        ("Object.keys({x:1, y:2, z:3}).length", "3"),
106        // typeof / 例外
107        ("typeof undefinedVar", "undefined"),
108        ("typeof 42", "number"),
109        ("try { throw 'boom' } catch (e) { e }", "boom"),
110        // JSON
111        ("JSON.stringify({x:[1,2], y:'z'})", "{\"x\":[1,2],\"y\":\"z\"}"),
112        ("JSON.parse('{\"n\":5}').n", "5"),
113        // Math
114        ("Math.max(3, 7, 2)", "7"),
115        ("Math.floor(3.9)", "3"),
116        ("Math.abs(-5)", "5"),
117        // switch(フォールスルー・default 含む)
118        ("function f(x){ switch(x){ case 1: return 'one'; case 2: return 'two'; default: return 'other' } } f(2)", "two"),
119        ("function f(x){ switch(x){ case 1: return 'one'; default: return 'd' } } f(9)", "d"),
120        ("var r=''; switch(1){ case 1: r+='a'; case 2: r+='b'; break; case 3: r+='c' } r", "ab"),
121        // スプレッド/レスト
122        ("var a=[1,2]; var b=[...a,3,4]; b.join(',')", "1,2,3,4"),
123        ("function sum(...xs){ return xs.reduce((s,x)=>s+x,0) } sum(1,2,3,4)", "10"),
124        ("function add(a,b,c){ return a+b+c } add(...[1,2,3])", "6"),
125        ("var o={a:1}; var p={...o, b:2}; p.a + p.b", "3"),
126        ("Math.max(...[5,2,9,1])", "9"),
127        ("function tail(first, ...rest){ return rest.join('-') } tail(1,2,3,4)", "2-3-4"),
128        // Map
129        ("var m=new Map(); m.set('a',1).set('b',2); m.get('a') + m.size", "3"),
130        ("var m=new Map([['x',10],['y',20]]); m.has('y') + ',' + m.get('y')", "true,20"),
131        ("var m=new Map(); m.set(1,'one'); m.delete(1); m.has(1)", "false"),
132        // `new Map(iterable)` の各要素が非オブジェクト(ペアでない)場合は仕様どおり
133        // TypeError を投げる必要があるが、以前は黙って {undefined: undefined} という
134        // 意味のない1エントリだけの Map が静かに作られていた。
135        (
136            "try { new Map([1,2,3]); 'no throw'; } catch(e) { 'threw'; }",
137            "threw",
138        ),
139        (
140            "try { new Map(['ab']); 'no throw'; } catch(e) { 'threw'; }",
141            "threw",
142        ),
143        // Set
144        ("var s=new Set([1,2,2,3,3,3]); s.size", "3"),
145        ("var s=new Set(); s.add(5).add(5).add(6); s.has(5) + ',' + s.size", "true,2"),
146        ("var s=new Set([1,2,3]); var t=0; s.forEach(v=>t+=v); t", "6"),
147        ("[...new Set([1,1,2,3,3])].join(',')", "1,2,3"),
148        // 分割代入(オブジェクト)
149        ("var {a, b} = {a:1, b:2}; a + b", "3"),
150        ("var {a: x, b: y} = {a:10, b:20}; x + y", "30"),
151        ("var {a, c = 9} = {a:1}; a + c", "10"),
152        ("var {a, ...rest} = {a:1, b:2, c:3}; rest.b + rest.c", "5"),
153        ("var {p: {q}} = {p:{q:42}}; q", "42"),
154        // 分割代入(配列)
155        ("var [a, b] = [1, 2]; a + b", "3"),
156        ("var [a, , c] = [1, 2, 3]; a + c", "4"),
157        ("var [a, ...rest] = [1, 2, 3, 4]; rest.join('-')", "2-3-4"),
158        ("var [a = 5, b = 6] = [1]; a + b", "7"),
159        ("var [[a], [b]] = [[1],[2]]; a + b", "3"),
160        ("var t=0; for (const [k,v] of [['a',1],['b',2]]) t += v; t", "3"),
161        // デフォルト引数
162        ("function f(a, b = 10) { return a + b } f(5)", "15"),
163        ("function f(a, b = 10) { return a + b } f(5, 7)", "12"),
164        // 引数の分割代入
165        ("function f({x, y}) { return x * y } f({x:3, y:4})", "12"),
166        ("function f([a, b]) { return a - b } f([9, 2])", "7"),
167        // 算出キー
168        ("var k = 'dyn'; var o = {[k]: 99}; o.dyn", "99"),
169        ("var o = {['a' + 'b']: 1}; o.ab", "1"),
170        // class(基本)
171        ("class A { constructor(x){ this.x = x } get(){ return this.x } } new A(7).get()", "7"),
172        ("class A { constructor(){ this.n = 0 } inc(){ this.n++; return this.n } } var a=new A(); a.inc(); a.inc()", "2"),
173        ("class P { area(){ return 0 } } class C extends P { area(){ return 5 } } new C().area()", "5"),
174        // class(継承・super)
175        ("class A { constructor(x){ this.x = x } } class B extends A { constructor(x,y){ super(x); this.y = y } sum(){ return this.x + this.y } } new B(3,4).sum()", "7"),
176        ("class A { greet(){ return 'A' } } class B extends A { greet(){ return super.greet() + 'B' } } new B().greet()", "AB"),
177        ("class A { constructor(){ this.v = 1 } } class B extends A {} new B().v", "1"),
178        // class field 宣言(ES2022。コンストラクタ本体の先頭で this.x = init される)
179        ("class C { x = 5; } new C().x", "5"),
180        ("class C { x; } typeof new C().x", "undefined"),
181        ("class C { x = 1; y = this.x + 1; } var c = new C(); c.x + ',' + c.y", "1,2"),
182        (
183            "class C { x = 1; constructor(){ this.x = this.x + 10 } } new C().x",
184            "11",
185        ),
186        (
187            "class A { x = 1; } class B extends A { y = 2; } var b = new B(); b.x + ',' + b.y",
188            "1,2",
189        ),
190        // private field(#x)。'#' が識別子の一部として扱われ、通常のプロパティアクセス
191        // 経路をそのまま通る(真の隠蔽ではないが、構文としては正しく動作する)。
192        (
193            "class Counter { #n = 0; inc(){ this.#n++; return this.#n } } var c = new Counter(); c.inc(); c.inc()",
194            "2",
195        ),
196        (
197            "class C { #secret = 'hidden'; reveal(){ return this.#secret } } new C().reveal()",
198            "hidden",
199        ),
200        // private メソッド(#method())
201        (
202            "class C { #double(n){ return n * 2 } calc(n){ return this.#double(n) } } new C().calc(21)",
203            "42",
204        ),
205        // プライベートフィールドのブランドチェック `#x in obj`(ES2022)が丸ごと
206        // 未対応で、`#x is not defined` の例外に必ずなっていた。
207        (
208            "class C { #x = 1; static has(o){ return #x in o; } } C.has(new C()) + ',' + C.has({})",
209            "true,false",
210        ),
211        // static field
212        ("class C { static count = 7; } C.count", "7"),
213        // static メソッド
214        ("class M { static twice(n){ return n * 2 } } M.twice(21)", "42"),
215        // メソッドからメソッド呼び出し
216        ("class C { a(){ return 2 } b(){ return this.a() * 10 } } new C().b()", "20"),
217        // Promise / async / await(await はマイクロタスクを駆動して同期解決)
218        ("await Promise.resolve(42)", "42"),
219        // **重要**: 汎用 thenable(本物の Promise ではないが呼び出し可能な `.then` を
220        // 持つオブジェクト。古いライブラリの独自 Promise 実装等でよく使われる)の
221        // 同化が丸ごと未対応で、`await`/`Promise.resolve` がオブジェクト自体を
222        // 履行値にしてしまうバグだった。
223        (
224            "await Promise.resolve({ then(resolve){ resolve(99); } })",
225            "99",
226        ),
227        (
228            "await { then(resolve){ resolve(7); } }",
229            "7",
230        ),
231        (
232            "try { await { then(resolve, reject){ reject('bad'); } } } catch(e) { e }",
233            "bad",
234        ),
235        // thenable の連鎖(thenable の then が別の thenable で resolve する)も
236        // 再帰的に同化されること。
237        (
238            "await { then(resolve){ resolve({ then(r){ r(5); } }); } }",
239            "5",
240        ),
241        // **重要**: thenable 検出が own プロパティしか見ていなかったため、
242        // `then` メソッドが `.prototype` 側にある class インスタンスの thenable
243        // を取りこぼすバグだった(自己検証で発見)。
244        (
245            "class MyThenable { then(resolve){ resolve(42); } } await new MyThenable()",
246            "42",
247        ),
248        (
249            "class MyThenable { then(_, reject){ reject('bad'); } } try { await new MyThenable() } catch(e) { e }",
250            "bad",
251        ),
252        ("await Promise.resolve(1).then(v => v + 1)", "2"),
253        ("await Promise.resolve(1).then(v => v + 1).then(v => v * 10)", "20"),
254        ("await Promise.reject('e').catch(e => 'caught:' + e)", "caught:e"),
255        ("async function f(){ return 7 } await f()", "7"),
256        ("async function f(){ let x = await Promise.resolve(10); return x * 2 } await f()", "20"),
257        ("async function f(){ try { await Promise.reject('x') } catch(e){ return 'C' + e } } await f()", "Cx"),
258        ("await (async () => 5)()", "5"),
259        ("await new Promise((res) => res(99))", "99"),
260        ("await new Promise((res, rej) => rej('bad')).catch(e => 'got:' + e)", "got:bad"),
261        ("await Promise.all([Promise.resolve(1), Promise.resolve(2), 3]).then(a => a.join(','))", "1,2,3"),
262        // **重要**: `Promise.all` の配列要素に汎用 thenable(本物の Promise ではないが
263        // 呼び出し可能な `.then` を持つオブジェクト)が混ざっていると、`Promise.resolve`/
264        // `await` 単体では既に同化対応済みだったのに、要素がそのままオブジェクトとして
265        // 混入してしまう非対称なバグだった。
266        (
267            "await Promise.all([1, { then(r){ r(2); } }, Promise.resolve(3)]).then(a => a.join(','))",
268            "1,2,3",
269        ),
270        (
271            "await Promise.all([{ then(resolve, reject){ reject('bad'); } }]).catch(e => 'caught:' + e)",
272            "caught:bad",
273        ),
274        ("typeof Promise.resolve(1)", "object"),
275        ("var log=''; await Promise.resolve(1).finally(() => log = 'F'); log", "F"),
276        // `finally` は元の値/理由をそのまま次へ渡す(コールバックの戻り値は無視)。
277        ("await Promise.resolve(1).finally(() => 2)", "1"),
278        // `finally` のコールバックが例外を投げた場合、それが settle を上書きすべき
279        // (以前は副作用としてだけ実行し戻り値/例外を完全に無視していたバグ)。
280        (
281            "await Promise.resolve(1).finally(() => { throw 'x'; }).catch(e => 'caught:' + e)",
282            "caught:x",
283        ),
284        // `finally` のコールバックが reject する Promise を返した場合も同様。
285        (
286            "await Promise.resolve(1).finally(() => Promise.reject('y')).catch(e => 'caught:' + e)",
287            "caught:y",
288        ),
289        // reject 経路でも `finally` はコールバック実行後、元の reason で reject し続ける。
290        (
291            "await Promise.reject('orig').finally(() => 'ignored').catch(e => 'caught:' + e)",
292            "caught:orig",
293        ),
294        ("async function sq(n){ return n*n } var s=0; for (const n of [1,2,3]) s += await sq(n); s", "14"),
295        // generator(遅延評価 / replay 駆動)
296        ("function* g(){ yield 1; yield 2; yield 3 } [...g()].join(',')", "1,2,3"),
297        ("function* g(){ yield* [1,2]; yield 3 } [...g()].join(',')", "1,2,3"),
298        ("function* g(){ for (let i=0;i<3;i++) yield i*i } [...g()].join(',')", "0,1,4"),
299        ("function* g(){ yield 1; yield 2; yield 3 } var s=0; for (const v of g()) s += v; s", "6"),
300        ("function* g(){ yield 'a'; yield 'b' } var it=g(); it.next().value + it.next().value + it.next().done", "abtrue"),
301        ("function* g(){ yield 1; return 99; } var it=g(); it.next(); it.next().done", "true"),
302        // 無限 generator を有限個だけ消費(真の遅延評価。eager だと無限ループ)
303        ("function* nat(){ let i=0; while(true) yield i++; } var it=nat(); it.next().value + ',' + it.next().value + ',' + it.next().value", "0,1,2"),
304        ("function* nat(){ let i=1; while(true) yield i++; } var it=nat(); var s=0; for(let k=0;k<5;k++) s += it.next().value; s", "15"),
305        // .next(v) による双方向の値受け渡し
306        ("function* g(){ var a = yield 1; var b = yield a + 10; return a + b; } var it=g(); it.next(); var r1=it.next(5); var r2=it.next(100); r1.value + ',' + r2.value", "15,105"),
307        ("function* g(){ var x = yield 'first'; yield 'got:' + x; } var it=g(); it.next(); it.next('hello').value", "got:hello"),
308        // yield* で別の generator へ委譲(generator 同士のネスト)
309        ("function* inner(){ yield 1; yield 2; } function* outer(){ yield 0; yield* inner(); yield 3; } [...outer()].join(',')", "0,1,2,3"),
310        // **重要**: `async function*`(非同期 generator。ES2018)の `.next()` が
311        // 仕様上必ず Promise を返すべきところ、`is_generator` の分岐が `is_async` を
312        // 完全に無視しており、普通の同期 generator と同じ `{value,done}` プレーン
313        // オブジェクトを直接返してしまっていたバグ。
314        (
315            "async function* g(){ yield 1; yield 2; } typeof g().next().then",
316            "function",
317        ),
318        (
319            "async function* g(){ yield 1; yield 2; } var it = g(); await it.next().then(r => r.value + ',' + r.done)",
320            "1,false",
321        ),
322        (
323            "async function* g(){ yield await Promise.resolve(42); } var it = g(); await it.next().then(r => r.value)",
324            "42",
325        ),
326        ("function* g(){ yield* 'abc'; } [...g()].join('-')", "a-b-c"),
327        // return() で早期終了
328        ("function* g(){ yield 1; yield 2; yield 3; } var it=g(); it.next(); var r=it.return(99); r.value + ',' + r.done + ',' + it.next().done", "99,true,true"),
329        // return() と try...finally の連携(return() 時に finally が実行される)
330        ("var log=''; function* g(){ try { yield 1; } finally { log+='f'; } } var it=g(); it.next(); var r=it.return(99); r.value + ',' + r.done + ',' + log", "99,true,f"),
331        // finally の中で return がある場合はそちらが優先される
332        ("function* g(){ try { yield 1; } finally { return 42; } } var it=g(); it.next(); it.return(99).value", "42"),
333        // finally の中で yield がある場合はそこで一時停止し、次の再開で強制Return値で完了する。
334        // value/done は正しいが、下の replay 特性(368行目)どおり finally 冒頭の
335        // `log+='f'` は forced-return 契機の replay と次の .next() の replay の
336        // 計2回実行されるため 'f' が2つになる(2026-07-14、期待値の計算ミスを
337        // QEMU JS_SELFTEST FAIL で発見・修正。本体側の finally 実行ロジックは正しい)。
338        ("var log=''; function* g(){ try { yield 1; } finally { log+='f'; yield 2; log+='end'; } } var it=g(); it.next(); var r1=it.return(99); var r2=it.next(); r1.value+','+r1.done+','+r2.value+','+r2.done+','+log", "2,false,99,true,ffend"),
339        // replay 方式の帰結: resume のたびに本体を先頭から再実行するため、外部副作用は
340        // 複数回発生する(yield される値・.next(v) の受け渡しは正しい)。この再実行特性を
341        // 明示的に検証する(2 回目の next で log+='a' が再実行され 'aab' になる)。
342        ("var log=''; function* g(){ log+='a'; yield 1; log+='b'; yield 2; } var it=g(); it.next(); it.next(); log", "aab"),
343        // Array.from が generator を消費できる(+ mapFn)
344        ("function* g(){ yield 1; yield 2; yield 3; } Array.from(g()).join(',')", "1,2,3"),
345        ("function* g(){ yield 1; yield 2; yield 3; } Array.from(g(), x => x * 10).join(',')", "10,20,30"),
346        // try/finally を跨ぐ yield(中断中は finally を実行せず、resume で継続)
347        ("function* g(){ try { yield 1; yield 2; } finally { } yield 3; } [...g()].join(',')", "1,2,3"),
348        // Symbol.match/replace/search/split のオブジェクト委譲
349        ("var custom = { [Symbol.match](s) { return s === 'hello' ? 'yes' : 'no'; } }; 'hello'.match(custom) + ',' + 'world'.match(custom)", "yes,no"),
350        ("var custom = { [Symbol.replace](s, r) { return s + '-' + r; } }; 'abc'.replace(custom, '123')", "abc-123"),
351        ("var custom = { [Symbol.search](s) { return s.length; } }; 'abc'.search(custom)", "3"),
352        ("var custom = { [Symbol.split](s, lim) { return [s.length, lim]; } }; 'abc'.split(custom, 5).join(',')", "3,5"),
353        // RegExp.prototype[Symbol.*] 直接呼び出し & RegExp 委譲動作
354        ("typeof /a/[Symbol.match]", "function"),
355        ("/b/[Symbol.match]('abc').join(',')", "b"),
356        ("/b/[Symbol.replace]('abc', 'X')", "aXc"),
357        ("/b/[Symbol.search]('abc')", "1"),
358        ("/b/[Symbol.split]('abc').join(',')", "a,c"),
359        // Symbol.species 静的アクセサ
360        ("Array[Symbol.species] === Array", "true"),
361        ("RegExp[Symbol.species] === RegExp", "true"),
362        ("Promise[Symbol.species] === Promise", "true"),
363        // ArraySpeciesCreate (Symbol.species による派生生成)
364        ("class SubArr extends Array {}; const sa = new SubArr(1, 2, 3); sa.map(x => x*2) instanceof SubArr", "true"),
365        ("class SubArr extends Array {}; const sa = new SubArr(1, 2, 3); sa.filter(x => x > 1) instanceof SubArr", "true"),
366        ("class SubArr extends Array {}; const sa = new SubArr(1, 2, 3); sa.slice(1) instanceof SubArr", "true"),
367        ("class SubArr extends Array {}; const sa = new SubArr(1, 2, 3); sa.concat([4]) instanceof SubArr", "true"),
368        ("class SubArr extends Array {}; const sa = new SubArr(1, 2, 3); sa.splice(1, 1) instanceof SubArr", "true"),
369        ("class SubArr extends Array {}; const sa = new SubArr(1, 2, 3); sa.flatMap(x => [x]) instanceof SubArr", "true"),
370        ("class CustomArr extends Array { static get [Symbol.species]() { return Array; } }; const ca = new CustomArr(1, 2); !(ca.map(x => x) instanceof CustomArr) && ca.map(x => x) instanceof Array", "true"),
371        // Array.prototype[Symbol.unscopables]
372        ("Array.prototype[Symbol.unscopables].includes", "true"),
373        ("Array.prototype[Symbol.unscopables].at", "true"),
374        // setTimeout(マクロタスク。Promise を介して await で駆動)
375        ("var x=0; await new Promise(r => setTimeout(() => { x = 5; r(); }, 0)); x", "5"),
376        ("var done=false; await new Promise(r => setTimeout(() => { done = true; r(); }, 0)); done", "true"),
377        ("await new Promise(res => setTimeout(() => res(7), 0))", "7"),
378        // requestAnimationFrame(マクロタスク。timestamp 引数 + cancelAnimationFrame)
379        ("typeof requestAnimationFrame", "function"),
380        ("var x=0; await new Promise(r => requestAnimationFrame(() => { x = 5; r(); })); x", "5"),
381        ("var ty=''; await new Promise(r => requestAnimationFrame(t => { ty = typeof t; r(); })); ty", "number"),
382        ("var L=''; requestAnimationFrame(() => L+='a'); var id=requestAnimationFrame(() => L+='b'); cancelAnimationFrame(id); await new Promise(r => requestAnimationFrame(() => r())); L", "a"),
383        // localStorage / sessionStorage(静的マップ共有のため各テスト先頭で clear)
384        ("localStorage.clear(); localStorage.setItem('a','1'); localStorage.getItem('a')", "1"),
385        ("localStorage.clear(); localStorage.getItem('missing')", "null"),
386        ("localStorage.clear(); localStorage.setItem('x','9'); localStorage.length", "1"),
387        ("localStorage.clear(); localStorage.setItem('a','1'); localStorage.removeItem('a'); localStorage.getItem('a')", "null"),
388        ("localStorage.clear(); localStorage.foo='bar'; localStorage.getItem('foo')", "bar"),
389        ("sessionStorage.clear(); sessionStorage.setItem('s','2'); sessionStorage.getItem('s')", "2"),
390        // `delete localStorage.foo` も同じ種類の黙殺バグだった(`removeItem` と同義のはずが
391        // Host プロキシが `.props` を持たないため何も起きていなかった)。
392        (
393            "localStorage.clear(); localStorage.foo='bar'; delete localStorage.foo; localStorage.getItem('foo')",
394            "null",
395        ),
396        // `Object.keys/values/entries(localStorage)` と `for...in localStorage` が実データ
397        // (専用マップ側にある) を一切見ておらず常に空になっていたバグの修正確認。
398        (
399            "localStorage.clear(); localStorage.setItem('a','1'); localStorage.setItem('b','2'); Object.keys(localStorage).sort().join(',')",
400            "a,b",
401        ),
402        (
403            "localStorage.clear(); localStorage.setItem('a','1'); localStorage.setItem('b','2'); Object.values(localStorage).sort().join(',')",
404            "1,2",
405        ),
406        (
407            "localStorage.clear(); localStorage.setItem('a','1'); Object.entries(localStorage)[0].join(':')",
408            "a:1",
409        ),
410        (
411            "localStorage.clear(); localStorage.setItem('a','1'); localStorage.setItem('b','2'); var out=[]; for (var k in localStorage) out.push(k); out.sort().join(',')",
412            "a,b",
413        ),
414        (
415            "localStorage.clear(); localStorage.setItem('a','1'); Object.assign({}, localStorage).a",
416            "1",
417        ),
418        // `Object.keys/values/entries(el.dataset)` と `for...in el.dataset` も同種の
419        // バグで実データ(DOM 要素側の `data-*` 属性)を一切見ておらず常に空になって
420        // いた(`localStorage`/`sessionStorage` で修正済みのバグと同型)。
421        (
422            "var e=document.createElement('div'); e.dataset.userId='42'; e.dataset.fooBar='x'; Object.keys(e.dataset).sort().join(',')",
423            "fooBar,userId",
424        ),
425        (
426            "var e=document.createElement('div'); e.dataset.a='1'; e.dataset.b='2'; Object.values(e.dataset).sort().join(',')",
427            "1,2",
428        ),
429        (
430            "var e=document.createElement('div'); e.dataset.a='1'; Object.entries(e.dataset)[0].join(':')",
431            "a:1",
432        ),
433        (
434            "var e=document.createElement('div'); e.dataset.a='1'; e.dataset.b='2'; var out=[]; for (var k in e.dataset) out.push(k); out.sort().join(',')",
435            "a,b",
436        ),
437        (
438            "var e=document.createElement('div'); e.dataset.a='1'; Object.assign({}, e.dataset).a",
439            "1",
440        ),
441        // タグ付きテンプレート `` tag`...${e}...` ``(ES2015)が丸ごと未対応で、`Template`
442        // トークンが式の直後に来ると静かにパースが崩れるバグだった。基本形・複数補間・
443        // `this` 束縛(メソッド呼出形)・組込み `String.raw` を確認。
444        (
445            "function tag(s, ...v){ return s.join('|') + '::' + v.join(','); } tag`a${1}b${2}c`",
446            "a|b|c::1,2",
447        ),
448        (
449            "var o={ tag(s){ return this===o ? s[0] : 'bad'; } }; o.tag`hi`",
450            "hi",
451        ),
452        ("String.raw`a\\nb`", "a\\nb"),
453        ("String.raw`x${1+1}y`", "x2y"),
454        // `strings.raw` はエスケープ未解決のまま渡る(cooked とは別)ことの確認。
455        (
456            "function tag(s){ return s[0] + '|' + s.raw[0]; } tag`a\\nb`",
457            "a\nb|a\\nb",
458        ),
459        // `Object.is(a, b)`(ES2015、SameValue)が丸ごと欠落していた。`===` と違い
460        // `NaN` 同士は等しく、`+0`/`-0` は区別する。
461        ("Object.is(NaN, NaN)", "true"),
462        ("NaN === NaN", "false"),
463        ("Object.is(0, -0)", "false"),
464        ("0 === -0", "true"),
465        ("Object.is(1, 1)", "true"),
466        ("Object.is('a', 'a')", "true"),
467        ("Object.is({}, {})", "false"),
468        // `Reflect.setPrototypeOf`/`isExtensible`/`preventExtensions`/
469        // `getOwnPropertyDescriptor` が丸ごと欠落していた。
470        (
471            "var o={}; var p={x:1}; Reflect.setPrototypeOf(o,p); o.x",
472            "1",
473        ),
474        ("var o={}; Reflect.isExtensible(o)", "true"),
475        (
476            "var o={}; Reflect.preventExtensions(o); Reflect.isExtensible(o)",
477            "false",
478        ),
479        (
480            "Reflect.getOwnPropertyDescriptor({x:1}, 'x').value",
481            "1",
482        ),
483        // `Array.prototype.fill(value, start, end)` が第2/第3引数を完全に無視し、
484        // 常に配列全体を上書きしていた。
485        ("[1,2,3,4,5].fill(0,1,3).join(',')", "1,0,0,4,5"),
486        ("[1,2,3,4,5].fill(9).join(',')", "9,9,9,9,9"),
487        ("[1,2,3,4,5].fill(0,-2).join(',')", "1,2,3,0,0"),
488        ("[1,2,3].fill(0,1).join(',')", "1,0,0"),
489        // `Array.prototype.indexOf`/`includes` の `fromIndex`(第2引数)が完全に無視され、
490        // 常に先頭から探索していた。
491        ("[1,2,3,2,1].indexOf(2, 2)", "3"),
492        ("[1,2,3].indexOf(1, 1)", "-1"),
493        ("[1,2,3].indexOf(2, -2)", "1"),
494        ("[1,2,3].includes(1, 1)", "false"),
495        ("[1,2,3].includes(2, 1)", "true"),
496        // `lastIndexOf` にも同じ `fromIndex` 無視バグがあった。
497        ("[1,2,1,2,1].lastIndexOf(2, 2)", "1"),
498        ("[1,2,3].lastIndexOf(3, 1)", "-1"),
499        ("[1,2,3].lastIndexOf(1, -1)", "0"),
500        ("[1,2,3,2].lastIndexOf(2)", "3"),
501        // `fromIndex` に NaN を渡すと仕様上 `ToIntegerOrInfinity(NaN)=0` として扱われ
502        // index 0 のみを見るはずだが、以前は無条件に -1 を返していた。
503        ("[5,1,2].lastIndexOf(5, NaN)", "0"),
504        ("[5,1,2].lastIndexOf(1, NaN)", "-1"),
505        // URLSearchParams
506        ("new URLSearchParams('a=1&b=2').get('a')", "1"),
507        ("new URLSearchParams('?a=1&b=2').get('b')", "2"),
508        ("new URLSearchParams('a=1&a=2').getAll('a').join(',')", "1,2"),
509        ("new URLSearchParams('a=1').has('a') + ',' + new URLSearchParams('a=1').has('z')", "true,false"),
510        ("var p=new URLSearchParams('a=1'); p.append('b','2'); p.toString()", "a=1&b=2"),
511        ("var p=new URLSearchParams('a=1&b=2'); p.delete('a'); p.toString()", "b=2"),
512        ("var p=new URLSearchParams('a=1'); p.set('a','9'); p.get('a')", "9"),
513        // `new URLSearchParams(init)` の init がオブジェクトの場合(sequence of
514        // pairs / record / 既存インスタンス)が丸ごと壊れていたバグ(`to_js_string`
515        // 丸投げで常に `"[object Object]"` 相当に落ちていた)。
516        ("new URLSearchParams([['a','1'],['b','2']]).toString()", "a=1&b=2"),
517        ("new URLSearchParams({a:'1',b:'2'}).toString()", "a=1&b=2"),
518        (
519            "var p1=new URLSearchParams('a=1&b=2'); new URLSearchParams(p1).toString()",
520            "a=1&b=2",
521        ),
522        // URLSearchParams: %XX / + デコード(form-urlencoded)
523        ("new URLSearchParams('a=hello%20world').get('a')", "hello world"),
524        ("new URLSearchParams('a=hello+world').get('a')", "hello world"),
525        ("var p=new URLSearchParams(); p.set('a','hello world'); p.toString()", "a=hello+world"),
526        ("var p=new URLSearchParams(); p.set('a','a&b=c'); p.get('a')", "a&b=c"),
527        // `entries`/`keys`/`values`/`forEach` が `URLSearchParams` に丸ごと欠落していた
528        // (`FormData` は既にあったが、内部表現が同じなのに移植されていなかった)。
529        (
530            "[...new URLSearchParams('a=1&b=2').entries()].map(e=>e[0]+':'+e[1]).join(',')",
531            "a:1,b:2",
532        ),
533        ("new URLSearchParams('a=1&b=2').keys().join(',')", "a,b"),
534        ("new URLSearchParams('a=1&b=2').values().join(',')", "1,2"),
535        (
536            "var s=''; new URLSearchParams('a=1&b=2').forEach(function(v,k){ s+=k+'='+v+';'; }); s",
537            "a=1;b=2;",
538        ),
539        // `Symbol.iterator` 未登録で `for (const [k,v] of params)`(`entries()` と同義の
540        // 既定イテレーション)が丸ごと非対応だったバグ。`FormData` にも同じ修正を適用。
541        (
542            "var out=[]; for (const [k,v] of new URLSearchParams('a=1&b=2')) out.push(k+':'+v); out.join(',')",
543            "a:1,b:2",
544        ),
545        (
546            "var fd=new FormData(); fd.set('x','9'); var out=[]; for (const [k,v] of fd) out.push(k+':'+v); out.join(',')",
547            "x:9",
548        ),
549        // URLPattern API (HTML Standard)
550        ("new URLPattern('/users/:id').test('/users/123')", "true"),
551        ("new URLPattern({ pathname: '/about' }).test('/about')", "true"),
552        ("new URLPattern('/users/:id').exec('/users/123').pathname.input", "/users/123"),
553        ("new URLPattern('/admin/*').test('/users/123')", "false"),
554        // encodeURIComponent / decodeURIComponent
555        ("encodeURIComponent('hello world')", "hello%20world"),
556        ("encodeURIComponent('a=1&b=2')", "a%3D1%26b%3D2"),
557        ("decodeURIComponent('hello%20world')", "hello world"),
558        ("decodeURIComponent(encodeURIComponent('日本語'))", "日本語"),
559        ("encodeURIComponent(\"!'()*\")", "!'()*"),
560        // encodeURI / decodeURI(予約文字は残す)
561        ("encodeURI('https://example.com/a b?x=1&y=2')", "https://example.com/a%20b?x=1&y=2"),
562        ("decodeURI('https://example.com/a%20b')", "https://example.com/a b"),
563        // `escape`/`unescape`(Annex B.2.1)が丸ごと未対応だった。
564        ("escape('a b?c')", "a%20b%3Fc"),
565        ("unescape(escape('a b?c'))", "a b?c"),
566        // ASCII 範囲外は `%uXXXX` 形式になる。
567        ("escape('日')", "%u65E5"),
568        ("unescape('%u65E5')", "日"),
569        // Annex B.2.3 の `String.prototype` HTML ラッパーメソッド群が丸ごと未対応だった。
570        ("'hi'.bold()", "<b>hi</b>"),
571        ("'hi'.italics()", "<i>hi</i>"),
572        ("'hi'.big()", "<big>hi</big>"),
573        ("'hi'.small()", "<small>hi</small>"),
574        ("'hi'.strike()", "<strike>hi</strike>"),
575        ("'hi'.sub()", "<sub>hi</sub>"),
576        ("'hi'.sup()", "<sup>hi</sup>"),
577        ("'hi'.fixed()", "<tt>hi</tt>"),
578        ("'hi'.blink()", "<blink>hi</blink>"),
579        ("'hi'.anchor('top')", "<a name=\"top\">hi</a>"),
580        ("'hi'.link('x.html')", "<a href=\"x.html\">hi</a>"),
581        ("'hi'.fontcolor('red')", "<font color=\"red\">hi</font>"),
582        ("'hi'.fontsize(3)", "<font size=\"3\">hi</font>"),
583        // 属性値の `\"` は `&quot;` へエスケープする(仕様どおり)。
584        ("'hi'.anchor('a\"b')", "<a name=\"a&quot;b\">hi</a>"),
585        // `trimLeft`/`trimRight`(Annex B.2.2。`trimStart`/`trimEnd` の別名)が
586        // 丸ごと未対応だった。
587        ("'  hi  '.trimLeft()", "hi  "),
588        ("'  hi  '.trimRight()", "  hi"),
589        // FormData (空生成 + API)
590        ("var f=new FormData(); f.append('a','1'); f.append('b','2'); f.get('a')", "1"),
591        ("var f=new FormData(); f.append('a','1'); f.append('a','2'); f.getAll('a').join(',')", "1,2"),
592        ("var f=new FormData(); f.append('a','1'); f.append('b','2'); f.toString()", "a=1&b=2"),
593        ("var f=new FormData(); f.append('a','1'); f.set('a','9'); f.get('a')", "9"),
594        ("var f=new FormData(); f.append('a','1'); f.has('a')+','+f.has('z')", "true,false"),
595        ("var f=new FormData(); f.append('a','1'); f.append('b','2'); f.entries().map(function(e){return e[0]+'='+e[1]}).join('&')", "a=1&b=2"),
596        ("var f=new FormData(); f.append('a','1'); f.append('b','2'); f.keys().join(',')", "a,b"),
597        ("var f=new FormData(); f.append('a','1'); f.append('b','2'); var s=''; f.forEach(function(v,k){s+=k+':'+v+';'}); s", "a:1;b:2;"),
598        // URL
599        ("new URL('https://e.com/p?x=1#h').pathname", "/p"),
600        ("new URL('https://e.com/p?x=1').searchParams.get('x')", "1"),
601        // `new URL(url, base)` の第2引数 base が丸ごと無視され、相対URL解決という
602        // URL API 最も基本的な使い方が機能しなかったバグ(既存の `resolve_url`
603        // ヘルパは `fetch`/XHR では既に使われていたが `URL` コンストラクタには
604        // 配線されていなかった。`.`/`..` セグメントの解決は非対応の簡略実装
605        // という既存の制約はそのまま)。
606        ("new URL('/a/b', 'https://e.com/x/y').href", "https://e.com/a/b"),
607        ("new URL('c', 'https://e.com/a/b').href", "https://e.com/a/c"),
608        // `user:pass@host` 形式のユーザー情報(userinfo)が丸ごと未対応で、
609        // `hostname`(引いては `host`/`port`/`origin`)にそのまま混入し
610        // 壊れた値になるバグだった(`user` が `hostname` の一部に化ける)。
611        ("new URL('https://user:pass@example.com/p').hostname", "example.com"),
612        ("new URL('https://user:pass@example.com/p').username", "user"),
613        ("new URL('https://user:pass@example.com/p').password", "pass"),
614        ("new URL('https://user:pass@example.com/p').origin", "https://example.com"),
615        // ユーザー情報無しの通常URLでは既定値が空文字列のまま(回帰確認)。
616        ("new URL('https://example.com/p').username", ""),
617        // `url.href = '...'`(URL全体を再パースして全コンポーネントを書き換える
618        // 定番の再代入パターン)が丸ごと未対応で、セッターが無いため代入が
619        // 黙って無視されていた。
620        (
621            "var u=new URL('https://a.com/x'); u.href='https://b.com/y?z=1'; u.hostname",
622            "b.com",
623        ),
624        (
625            "var u=new URL('https://a.com/x'); u.href='https://b.com/y?z=1'; u.pathname",
626            "/y",
627        ),
628        (
629            "var u=new URL('https://a.com/x'); u.href='https://b.com/y?z=1'; u.searchParams.get('z')",
630            "1",
631        ),
632        (
633            // オブジェクト自体は再代入せず同じインスタンスを書き換えるため、
634            // 元の変数参照からも新しい値が見える。
635            "var u=new URL('https://a.com/x'); var u2=u; u.href='https://b.com/y'; u2.href",
636            "https://b.com/y",
637        ),
638        // `url.pathname`/`.search`/`.hash`の個別再代入が丸ごと未対応だった。
639        // 特に`.search`は`url.toString()`/`.href`が`searchParams`だけを見て
640        // `search`プロパティ自体は読まないため、代入しても何も反映されない
641        // という`.href`丸ごと差し替えとは別種の静かな不整合があった。
642        (
643            "var u=new URL('https://a.com/x'); u.pathname='y'; u.href",
644            "https://a.com/y",
645        ),
646        (
647            "var u=new URL('https://a.com/x'); u.search='z=1'; u.href",
648            "https://a.com/x?z=1",
649        ),
650        (
651            "var u=new URL('https://a.com/x'); u.search='z=1'; u.searchParams.get('z')",
652            "1",
653        ),
654        (
655            "var u=new URL('https://a.com/x?old=1'); u.search='new=2'; u.href",
656            "https://a.com/x?new=2",
657        ),
658        (
659            "var u=new URL('https://a.com/x'); u.hash='frag'; u.href",
660            "https://a.com/x#frag",
661        ),
662        (
663            // 空文字列への代入はコンポーネントを除去する(クエリ/フラグメント無しに戻る)。
664            "var u=new URL('https://a.com/x?y=1#z'); u.search=''; u.hash=''; u.href",
665            "https://a.com/x",
666        ),
667        // `url.protocol`/`.host`/`.hostname`/`.port`の個別再代入も丸ごと
668        // 未対応だった。これら4つは互いに依存する派生値(`host`=`hostname`+
669        // `:`+`port`、`origin`=`protocol`+`//`+`host`)を持つため、どれを
670        // 代入しても`host`/`origin`を再計算し直す必要がある。
671        (
672            "var u=new URL('https://a.com:8080/x'); u.hostname='b.com'; u.href",
673            "https://b.com:8080/x",
674        ),
675        (
676            "var u=new URL('https://a.com:8080/x'); u.hostname='b.com'; u.origin",
677            "https://b.com:8080",
678        ),
679        (
680            "var u=new URL('https://a.com/x'); u.port='9090'; u.host",
681            "a.com:9090",
682        ),
683        (
684            "var u=new URL('https://a.com:8080/x'); u.host='c.com:7070'; u.hostname + ',' + u.port",
685            "c.com,7070",
686        ),
687        (
688            "var u=new URL('http://a.com/x'); u.protocol='https'; u.href",
689            "https://a.com/x",
690        ),
691        // クエリのみ/フラグメントのみの相対参照(`?x=2`/`#frag`)が、他の
692        // 相対パス解決と同じ「ディレクトリ相対」ロジックに落ちてベースの
693        // パスの最後のセグメントごと消えるバグだった。
694        ("new URL('?x=2', 'https://e.com/a/b?x=1').href", "https://e.com/a/b?x=2"),
695        ("new URL('#frag', 'https://e.com/a/b?x=1').href", "https://e.com/a/b?x=1#frag"),
696        // プロトコル相対URL(`//host/path`)が、ベースのホスト配下の相対パスと
697        // して誤解決されるバグだった(`//cdn.example.com/x.js` は別ホストへの
698        // 参照であり、ベースホスト配下のパスにしてはいけない)。
699        ("new URL('//cdn.example.com/x.js', 'https://e.com/a/b').href", "https://cdn.example.com/x.js"),
700        // `URL.canParse(url, base?)`(ES2023/WHATWG)が丸ごと未対応だった。
701        ("URL.canParse('https://example.com')", "true"),
702        ("URL.canParse('not a url')", "false"),
703        ("URL.canParse('/path', 'https://example.com')", "true"),
704        ("URL.canParse('/path')", "false"),
705        // `URL.parse(url, base?)`(ES2024/WHATWG)が丸ごと未対応だった。
706        // `try { new URL(x) } catch { null }` の1メソッド版。
707        ("URL.parse('https://example.com/p').pathname", "/p"),
708        ("URL.parse('not a url') === null", "true"),
709        ("URL.parse('/path', 'https://example.com').host", "example.com"),
710        ("URL.parse('/path') === null", "true"),
711        // `URL.createObjectURL(blob)`/`.revokeObjectURL(url)` が丸ごと未対応だった。
712        ("URL.createObjectURL(new Blob(['x'])).startsWith('blob:')", "true"),
713        // 呼び出す毎に一意な URL を発行する。
714        (
715            "URL.createObjectURL(new Blob(['x'])) === URL.createObjectURL(new Blob(['x']))",
716            "false",
717        ),
718        ("typeof URL.revokeObjectURL('blob:x')", "undefined"),
719        // `URLSearchParams.prototype.size`(ES2023)が丸ごと未対応だった。
720        ("new URLSearchParams('a=1&b=2').size", "2"),
721        (
722            "var p=new URLSearchParams('a=1'); p.append('b','2'); p.size",
723            "2",
724        ),
725        (
726            "var p=new URLSearchParams('a=1&b=2'); p.delete('a'); p.size",
727            "1",
728        ),
729        ("new URLSearchParams('').size", "0"),
730        // `has(name, value)`/`delete(name, value)`(ES2023 の第2引数)が丸ごと
731        // 無視され、値を問わず同名エントリを判定/削除してしまうバグだった。
732        ("new URLSearchParams('a=1&a=2').has('a', '1')", "true"),
733        ("new URLSearchParams('a=1&a=2').has('a', '9')", "false"),
734        (
735            "var p=new URLSearchParams('a=1&a=2'); p.delete('a','1'); p.getAll('a').join(',')",
736            "2",
737        ),
738        (
739            "var p=new URLSearchParams('a=1&a=2'); p.delete('a'); p.getAll('a').join(',')",
740            "",
741        ),
742        // `URLSearchParams.prototype.sort()`(ES2020)が丸ごと未対応だった。
743        (
744            "var p=new URLSearchParams('c=3&a=1&b=2'); p.sort(); p.toString()",
745            "a=1&b=2&c=3",
746        ),
747        (
748            "var p=new URLSearchParams('b=2&a=1&a=0'); p.sort(); p.getAll('a').join(',')",
749            "1,0",
750        ),
751        // `url.toString()`(暗黙の文字列化 `fetch(url)` 等でも使われる)が `href` の
752        // 構築時点スナップショットのままで、`searchParams` の変更を反映しないバグ。
753        (
754            "var u=new URL('https://e.com/p?x=1'); u.searchParams.set('x','9'); u.toString()",
755            "https://e.com/p?x=9",
756        ),
757        (
758            "var u=new URL('https://e.com/p'); u.searchParams.append('a','1'); u.searchParams.append('b','2'); u.toString()",
759            "https://e.com/p?a=1&b=2",
760        ),
761        (
762            "var u=new URL('https://e.com/p?x=1#h'); u.searchParams.delete('x'); u.toString()",
763            "https://e.com/p#h",
764        ),
765        // `URL.prototype.toJSON`(仕様上 `toString()` と同じ href を返すだけの
766        // エイリアス)が丸ごと未対応で、`JSON.stringify(url)` がプロパティ丸ごとの
767        // JSON になってしまうバグだった。
768        (
769            "JSON.stringify({u: new URL('https://e.com/p?x=1')})",
770            "{\"u\":\"https://e.com/p?x=1\"}",
771        ),
772        // base64
773        ("btoa('hello')", "aGVsbG8="),
774        ("atob('aGVsbG8=')", "hello"),
775        ("atob(btoa('AtmOS roundtrip'))", "AtmOS roundtrip"),
776        // navigator / performance
777        ("navigator.platform", "AtmOS"),
778        ("typeof navigator.userAgent", "string"),
779        ("navigator.onLine", "true"),
780        // `navigator.cookieEnabled`/`.hardwareConcurrency`/`.maxTouchPoints`/
781        // `.languages`/`.webdriver`(丸ごと未対応だった)。
782        ("navigator.cookieEnabled", "true"),
783        ("navigator.hardwareConcurrency", "4"),
784        ("navigator.maxTouchPoints", "0"),
785        ("navigator.languages[0]", "ja-JP"),
786        ("navigator.webdriver", "false"),
787        // `navigator.userAgentData`(Client Hints API。丸ごと未対応だった。
788        // `navigator.userAgent`文字列パースに代わる構造化版の定番フィー
789        // チャー検出パターン。2026-07-17 発見・実装)。
790        (
791            "navigator.userAgentData.brands[0].brand + ',' + navigator.userAgentData.mobile + ',' + \
792             navigator.userAgentData.platform",
793            "AtmOSBrowser,false,AtmOS",
794        ),
795        (
796            "await navigator.userAgentData.getHighEntropyValues(['architecture','bitness']).then(v => \
797             v.architecture + ',' + v.bitness + ',' + v.platform)",
798            "arm,64,AtmOS",
799        ),
800        // `navigator.storage`(StorageManager API。`.estimate()`/`.persist()`/
801        // `.persisted()`が丸ごと未対応だった。2026-07-17 発見・実装。実際の
802        // ディスク使用量計測機構が無いため`usage:0`の誠実な簡略値、
803        // `persist`/`persisted`は常に`true`)。
804        (
805            "await navigator.storage.estimate().then(e => typeof e.usage + ',' + typeof e.quota)",
806            "number,number",
807        ),
808        ("await navigator.storage.persist()", "true"),
809        ("await navigator.storage.persisted()", "true"),
810        ("typeof performance.now()", "number"),
811        // Performance Timeline / User Timing API(`mark`/`measure`/
812        // `getEntriesByType`/`getEntriesByName`/`clearMarks`/`clearMeasures`)
813        // が `now()` 以外丸ごと未対応だった。
814        ("performance.mark('a').entryType", "mark"),
815        (
816            "performance.mark('s1'); performance.mark('e1'); \
817             performance.measure('m1','s1','e1').entryType",
818            "measure",
819        ),
820        // `performance.timeOrigin`(丸ごと未対応だった。2026-07-16 発見・
821        // 実装)。自己テストはブート初期段階(壁時計がまだ NTP 同期前)に
822        // 実行されるため `epoch_ms_now()` が `0` を返しうる。数値型である
823        // ことのみ検証し、正の値は断定しない。
824        ("typeof performance.timeOrigin", "number"),
825        ("performance.timeOrigin >= 0", "true"),
826        // `performance.getEntries()`(フィルタ無し全件取得。丸ごと未対応
827        // だった。2026-07-16 発見・実装)。
828        (
829            "performance.mark('ge1'); performance.getEntries().some(e => e.name === 'ge1')",
830            "true",
831        ),
832        ("performance.mark('a'); performance.getEntriesByType('mark').length > 0", "true"),
833        ("performance.mark('a'); performance.getEntriesByName('a', 'mark').length", "1"),
834        (
835            "performance.mark('to-clear'); performance.clearMarks('to-clear'); \
836             performance.getEntriesByName('to-clear').length",
837            "0",
838        ),
839        (
840            "performance.mark('sX'); performance.mark('eX'); \
841             performance.measure('mX','sX','eX'); performance.clearMeasures(); \
842             performance.getEntriesByType('measure').length",
843            "0",
844        ),
845        // `PerformanceObserver`(丸ごと未対応だった。`observe({entryTypes})`
846        // で監視中の種別に一致する `mark`/`measure` を同期通知する)。
847        (
848            "let got; new PerformanceObserver((list) => { got = list.getEntries()[0].name; }) \
849             .observe({entryTypes:['mark']}); performance.mark('obs1'); got",
850            "obs1",
851        ),
852        (
853            "let n = 0; new PerformanceObserver((list) => { n += list.getEntries().length; }) \
854             .observe({entryTypes:['measure']}); performance.mark('s2'); \
855             performance.mark('e2'); performance.measure('m2','s2','e2'); n",
856            "1",
857        ),
858        (
859            "let hit = false; let ob = new PerformanceObserver(() => { hit = true; }); \
860             ob.observe({entryTypes:['mark']}); ob.disconnect(); performance.mark('after-disconnect'); hit",
861            "false",
862        ),
863        // `PerformanceObserver.supportedEntryTypes`(丸ごと未対応だった。
864        // 2026-07-16 発見・実装)。
865        (
866            "PerformanceObserver.supportedEntryTypes.join(',')",
867            "mark,measure",
868        ),
869        // `observe({type: '...', buffered: true})`(単一種別+既存エントリ
870        // 即時配信の新形式)が丸ごと未対応だった(`entryTypes`しか読んで
871        // おらず`type`単体では一切発火しない黙殺バグだった。2026-07-17
872        // 発見・実装)。
873        (
874            "let got; new PerformanceObserver((list) => { got = list.getEntries()[0].name; }) \
875             .observe({type:'mark'}); performance.mark('obs2'); got",
876            "obs2",
877        ),
878        (
879            "performance.mark('pre-existing'); let got=null; \
880             new PerformanceObserver((list) => { got = list.getEntries()[0].name; }) \
881             .observe({type:'mark', buffered:true}); got",
882            "pre-existing",
883        ),
884        (
885            "let n=0; new PerformanceObserver((list) => { n += list.getEntries().length; }) \
886             .observe({type:'mark'}); n",
887            "0",
888        ),
889        // MessageChannel/MessagePort(丸ごと未対応だった。`.port1`/`.port2` 間の
890        // 双方向メッセージング)。
891        (
892            "let mc = new MessageChannel(); let got; \
893             mc.port2.onmessage = e => { got = e.data; }; \
894             mc.port1.postMessage('hi'); got",
895            "hi",
896        ),
897        (
898            "let mc2 = new MessageChannel(); let n = 0; \
899             mc2.port2.addEventListener('message', e => { n += e.data; }); \
900             mc2.port1.postMessage(1); mc2.port1.postMessage(2); n",
901            "3",
902        ),
903        (
904            "let mc3 = new MessageChannel(); let hit = false; \
905             mc3.port2.onmessage = () => { hit = true; }; \
906             mc3.port2.close(); mc3.port1.postMessage('x'); hit",
907            "false",
908        ),
909        // BroadcastChannel(丸ごと未対応だった。同名インスタンス全体への配送、
910        // 自分自身への配送除外、他名チャネルへの非配送を確認)。
911        (
912            "let a1 = new BroadcastChannel('ch'); let b1 = new BroadcastChannel('ch'); \
913             let got; b1.onmessage = e => { got = e.data; }; \
914             a1.postMessage('hi'); got",
915            "hi",
916        ),
917        (
918            "let a2 = new BroadcastChannel('ch2'); let selfHit = false; \
919             a2.onmessage = () => { selfHit = true; }; \
920             a2.postMessage('x'); selfHit",
921            "false",
922        ),
923        (
924            "let a3 = new BroadcastChannel('chX'); let b3 = new BroadcastChannel('chY'); \
925             let hit = false; b3.onmessage = () => { hit = true; }; \
926             a3.postMessage('x'); hit",
927            "false",
928        ),
929        // `window.screen`(丸ごと未対応だった。`screen.width`/`.height`/
930        // `.availWidth`/`.availHeight`/`.colorDepth`/`.orientation.type` という
931        // レスポンシブ分岐の定番パターン)。
932        ("typeof screen.width", "number"),
933        ("screen.width === window.innerWidth", "true"),
934        ("screen.availHeight === screen.height", "true"),
935        ("screen.colorDepth", "24"),
936        ("screen.orientation.type", "landscape-primary"),
937        ("window.screen === screen", "true"),
938        // `screen.orientation.lock()`/`.unlock()`(丸ごと未対応だった。実際に
939        // 画面の向きを固定する機構は無いため状態のみ追跡する簡略実装)。
940        (
941            "typeof screen.orientation.lock('portrait-primary').then",
942            "function",
943        ),
944        (
945            "screen.orientation.lock('portrait-primary'); screen.orientation.type",
946            "portrait-primary",
947        ),
948        (
949            "screen.orientation.lock('portrait-primary'); screen.orientation.unlock(); \
950             screen.orientation.type",
951            "landscape-primary",
952        ),
953        // `reportError()`(丸ごと未対応だった。`window.onerror`/
954        // `window.addEventListener('error', fn)` へ未捕捉例外と同じ経路で
955        // 通知する標準グローバル関数)。
956        (
957            "let got; window.onerror = (msg) => { got = msg; }; \
958             reportError(new Error('boom')); got",
959            "boom",
960        ),
961        (
962            "let ev; window.addEventListener('error', e => { ev = e; }); \
963             reportError(new Error('e2')); ev.error.message",
964            "e2",
965        ),
966        ("typeof reportError('plain string')", "undefined"),
967        // `window.confirm`/`window.prompt`(丸ごと未対応だった。`alert`は
968        // 既に`console_log`で代替されていたのに、対になるこの2つだけ
969        // グローバルにも`window`にも未登録で`TypeError`になっていた。
970        // ブロッキングUIが無いこの処理系では常に「OK」相当を返す簡略実装。
971        // 2026-07-16 発見・実装)。
972        ("confirm('are you sure?')", "true"),
973        ("window.confirm('are you sure?')", "true"),
974        ("prompt('name?', 'bob')", "bob"),
975        ("window.prompt('name?')", "null"),
976        // `window.open`/`.close`/`.focus`/`.blur`(丸ごと未対応だった。同じ
977        // 監査で発見。呼び出すと`TypeError`になっていた。この処理系には
978        // 複数ウィンドウ機構が無いため`open`はポップアップブロック相当の
979        // `null`、他は無害なno-op。2026-07-16 発見・実装)。
980        ("typeof open", "function"),
981        ("open('https://example.com')", "null"),
982        ("window.open('https://example.com', '_blank')", "null"),
983        ("typeof close", "function"),
984        ("close(); window.close(); 'ok'", "ok"),
985        // `window.stop()`(丸ごと未対応だった。ページ読み込み中止ボタン
986        // 相当の定番API。2026-07-17 発見・実装。`close`/`.print`と同じ
987        // 無害なno-op)。
988        ("typeof stop", "function"),
989        ("stop(); window.stop(); 'ok'", "ok"),
990        ("typeof focus", "function"),
991        ("focus(); window.focus(); blur(); window.blur(); 'ok'", "ok"),
992        // `window.getSelection()`(丸ごと未対応だった。`document.
993        // getSelection()`は既に実装済みだったが、仕様上こちらが本来の
994        // 正規の場所で、より一般的に使われる`window.getSelection().
995        // toString()`等のイディオムが`TypeError`になっていた。
996        // 2026-07-16 発見・実装。同じ実装を再利用するため戻り値も
997        // `document.getSelection()`と同一の空文字列)。
998        ("typeof window.getSelection", "function"),
999        ("typeof getSelection", "function"),
1000        ("window.getSelection().toString()", ""),
1001        // `self`/`top`/`parent`/`frames`(丸ごと未対応だった。`globalThis`
1002        // は既に実装済みだったのに、同じ「windowを指す別名」仲間のこの4つ
1003        // だけ登録が漏れていた。この処理系には独立JSレルムを持つiframeが
1004        // 無く常に単一フレームのため、仕様どおり自分自身を指す。
1005        // 2026-07-16 発見・実装)。
1006        ("self === window", "true"),
1007        ("window.top === window", "true"),
1008        ("parent === window", "true"),
1009        ("window.frames === window", "true"),
1010        ("typeof window.name", "string"),
1011        // `iframe.contentWindow`/`.contentDocument`(同じ「フレームが無い
1012        // ので自分自身を指す」規定。丸ごと未対応だった。2026-07-17
1013        // 発見・実装)。
1014        (
1015            "document.createElement('iframe').contentWindow === window",
1016            "true",
1017        ),
1018        (
1019            "document.createElement('iframe').contentDocument === document",
1020            "true",
1021        ),
1022        (
1023            "document.createElement('div').contentWindow",
1024            "undefined",
1025        ),
1026        // `object.contentDocument`(丸ごと未対応だった。`HTMLObjectElement`
1027        // は`contentWindow`を持たず`contentDocument`のみが仕様上の対象。
1028        // 2026-07-17 発見・実装)。
1029        (
1030            "document.createElement('object').contentDocument === document",
1031            "true",
1032        ),
1033        (
1034            "document.createElement('object').contentWindow",
1035            "undefined",
1036        ),
1037        // `navigator.sendBeacon(url, data)`(丸ごと未対応だった。ページ離脱時の
1038        // 計測データ送信に使われる定番パターン。既存の `fetch()` POST経路を
1039        // 再利用して送信し常に `true` を返す)。
1040        ("typeof navigator.sendBeacon", "function"),
1041        ("navigator.sendBeacon('data:,x')", "true"),
1042        ("navigator.sendBeacon('data:,x', 'payload')", "true"),
1043        // `XMLSerializer`(丸ごと未対応だった。`.serializeToString(node)` は
1044        // 既存の `outerHTML` ゲッターと同じ `get_outer_html` を再利用する)。
1045        (
1046            "var e=document.createElement('div'); e.setAttribute('id','x'); \
1047             e.setAttribute('class','a b'); e.innerHTML='hi2'; \
1048             new XMLSerializer().serializeToString(e) === e.outerHTML",
1049            "true",
1050        ),
1051        (
1052            "var e=document.createElement('span'); e.textContent='z'; \
1053             new XMLSerializer().serializeToString(e) === e.outerHTML",
1054            "true",
1055        ),
1056        ("typeof new XMLSerializer().serializeToString", "function"),
1057        // `navigator.clipboard`(丸ごと未対応だった。`writeText`/`readText` という
1058        // コピー&ペースト UI の定番パターン)。実クリップボードは無いため単純な
1059        // プロセス内文字列保持で近似する。
1060        (
1061            "await navigator.clipboard.writeText('hello'); await navigator.clipboard.readText()",
1062            "hello",
1063        ),
1064        ("typeof navigator.clipboard.writeText('x')", "object"),
1065        // Array: at / flatMap / findLast / findLastIndex
1066        ("[10,20,30].at(-1)", "30"),
1067        ("[10,20,30].at(0)", "10"),
1068        ("[1,2,3].flatMap(x => [x, x*10]).join(',')", "1,10,2,20,3,30"),
1069        ("[1,2,3,4].findLast(x => x % 2 === 1)", "3"),
1070        ("[1,2,3,4].findLastIndex(x => x % 2 === 0)", "3"),
1071        // String: at
1072        ("'hello'.at(-1)", "o"),
1073        ("'hello'.at(1)", "e"),
1074        // 通常の呼び出しは影響を受けないことも確認。
1075        ("'hello world'.includes('world')", "true"),
1076        ("'hello'.startsWith('he')", "true"),
1077        ("'hello'.endsWith('lo')", "true"),
1078        // **重要**: `includes`/`startsWith`/`endsWith` は仕様上、第1引数に
1079        // `RegExp` を渡すと `TypeError` を投げる必要がある(うっかり正規表現を
1080        // 渡した誤用に対する意図的なガード)が、丸ごと未対応で常に無言で
1081        // リテラル文字列 `"/x/"` として検索してしまっていた。
1082        (
1083            "try { 'x'.includes(/x/); 'no-throw' } catch(e) { 'threw' }",
1084            "threw",
1085        ),
1086        (
1087            "try { 'x'.startsWith(/x/); 'no-throw' } catch(e) { 'threw' }",
1088            "threw",
1089        ),
1090        (
1091            "try { 'x'.endsWith(/x/); 'no-throw' } catch(e) { 'threw' }",
1092            "threw",
1093        ),
1094        // Promise: allSettled / race / any
1095        ("(await Promise.allSettled([Promise.resolve(1), Promise.reject('e')])).map(r => r.status).join(',')", "fulfilled,rejected"),
1096        ("await Promise.race([Promise.resolve('first'), Promise.resolve('second')])", "first"),
1097        ("await Promise.any([Promise.reject('x'), Promise.resolve('ok')])", "ok"),
1098        // `allSettled`/`race`/`any` も `Promise.all` と同じく配列要素の汎用 thenable を
1099        // 同化しない非対称なバグだった(共通ヘルパ `resolve_maybe_thenable` で解消)。
1100        (
1101            "(await Promise.allSettled([{ then(r){ r(1); } }, { then(_,rj){ rj('e'); } }])).map(r => r.status).join(',')",
1102            "fulfilled,rejected",
1103        ),
1104        (
1105            "await Promise.race([{ then(r){ r('first'); } }, Promise.resolve('second')])",
1106            "first",
1107        ),
1108        (
1109            "await Promise.any([{ then(_,rj){ rj('x'); } }, { then(r){ r('ok'); } }])",
1110            "ok",
1111        ),
1112        // TextEncoder / TextDecoder
1113        ("new TextEncoder().encode('Hi').join(',')", "72,105"),
1114        ("new TextDecoder().decode([72,105])", "Hi"),
1115        ("new TextDecoder().decode(new TextEncoder().encode('AtmOS'))", "AtmOS"),
1116        // `new TextDecoder(label)` が丸ごとラベル引数を無視し常に `utf-8` 決め打ち
1117        // だったバグ(`utf-16le`/`utf-16be` データを渡すと静かに文字化けしていた)。
1118        ("new TextDecoder('utf-16le').encoding", "utf-16le"),
1119        ("new TextDecoder('utf-16be').encoding", "utf-16be"),
1120        // 'H'=0x48 'i'=0x69 を UTF-16LE(下位バイト→上位バイト)で並べたバイト列。
1121        ("new TextDecoder('utf-16le').decode([0x48,0x00,0x69,0x00])", "Hi"),
1122        ("new TextDecoder('utf-16be').decode([0x00,0x48,0x00,0x69])", "Hi"),
1123        ("new TextDecoder('utf-16').encoding", "utf-16le"),
1124        // `decode(chunk, {stream: true})`(丸ごと未対応だった。チャンク境界で
1125        // マルチバイト文字が分断された場合の定番パターン。2026-07-14 発見・
1126        // 実装)。'€'(U+20AC)の UTF-8 表現 [0xE2,0x82,0xAC] を先頭2バイトと
1127        // 残り1バイトに分割して渡す。
1128        (
1129            "var d=new TextDecoder(); d.decode(new Uint8Array([0xE2,0x82]), {stream:true}) + \
1130             d.decode(new Uint8Array([0xAC]))",
1131            "€",
1132        ),
1133        // stream:true 無し(既定 false)で不完全なバイト列を渡すと、従来どおり
1134        // 即座に置換文字化される(持ち越されない)。
1135        ("new TextDecoder().decode(new Uint8Array([0xE2,0x82]))", "\u{FFFD}"),
1136        // crypto
1137        ("crypto.randomUUID().length", "36"),
1138        ("crypto.randomUUID().charAt(14)", "4"),
1139        ("crypto.getRandomValues([0,0,0]).length", "3"),
1140        // `crypto.getRandomValues` が種別を無視して常に `& 0xff`(0-255)で埋めており、
1141        // 8bit より広い型(`Uint16Array` 等)では値域のごく一部しか使わないバグだった。
1142        // 十分な試行数の中に 255 を超える値が1つでも現れれば、下位8bitマスクではなく
1143        // 実際に16bit幅を使っている証拠になる。
1144        (
1145            "crypto.getRandomValues(new Uint16Array(200)).some(v => v > 255)",
1146            "true",
1147        ),
1148        // `Uint8Array` は元々の挙動(0-255 の範囲内)を維持する。
1149        (
1150            "crypto.getRandomValues(new Uint8Array(50)).every(v => v >= 0 && v <= 255)",
1151            "true",
1152        ),
1153        // structuredClone(深いコピー: 元を変えても複製は不変)
1154        ("var o={a:{b:1}}; var c=structuredClone(o); o.a.b=9; c.a.b", "1"),
1155        ("structuredClone([1,[2,3]])[1][0]", "2"),
1156        // structuredClone(Error) — 汎用の plain object 複製に落ちると proto が
1157        // 引き継がれず `instanceof Error` を失っていたバグ(2026-07-14 発見・修正。
1158        // message/name/stack は元々コピーされていたが、プロトタイプ連鎖が
1159        // 途切れていた)。サブタイプ(TypeError 等)も正しいプロトタイプで
1160        // 複製されることを確認。
1161        (
1162            "var c=structuredClone(new TypeError('x')); c instanceof Error && c instanceof TypeError",
1163            "true",
1164        ),
1165        ("structuredClone(new Error('boom')).message", "boom"),
1166        ("structuredClone(new TypeError('x')).stack", "TypeError: x"),
1167        // `structuredClone(function)`が仕様上の`DataCloneError`を投げず、
1168        // 汎用plain object複製に落ちて「呼出不能な壊れたオブジェクト」に
1169        // 静かに化けていたバグ(`deep_clone_value`を`Result`化して修正。
1170        // 2026-07-17 発見・実装)。
1171        (
1172            "try { structuredClone(function(){}); 'no-throw' } catch(e) { e.name }",
1173            "DataCloneError",
1174        ),
1175        (
1176            "try { structuredClone({fn: () => 1}); 'no-throw' } catch(e) { e.name }",
1177            "DataCloneError",
1178        ),
1179        ("structuredClone({a: 1, b: [2, 3]}).a", "1"),
1180        // 上記`DataCloneError`修正の副次効果として、`URLSearchParams`/
1181        // `FormData`/`Headers`(メソッド群が`.props`上の生の関数値として
1182        // 実装されている「疑似クラス」オブジェクト)を`structuredClone`
1183        // すると、以前は関数プロパティごと汎用複製されて壊れたオブジェクト
1184        // (メソッド呼び出し不能)に静かに化けていたのが、今回の修正で
1185        // 正しく`DataCloneError`を投げるようになったことを確認・記録する
1186        // (2026-07-17)。
1187        (
1188            "try { structuredClone(new URLSearchParams('a=1')); 'no-throw' } catch(e) { e.name }",
1189            "DataCloneError",
1190        ),
1191        (
1192            "try { structuredClone(new FormData()); 'no-throw' } catch(e) { e.name }",
1193            "DataCloneError",
1194        ),
1195        (
1196            "try { structuredClone(new Headers()); 'no-throw' } catch(e) { e.name }",
1197            "DataCloneError",
1198        ),
1199        // `structuredClone(Promise)`も仕様上構造化複製不可(`DataCloneError`)
1200        // だが未対応で、`.then`/`.catch`が消えた呼出不能な壊れたオブジェクト
1201        // に静かに化けていた(丸ごと未対応だった。2026-07-17 発見・実装)。
1202        (
1203            "try { structuredClone(Promise.resolve(1)); 'no-throw' } catch(e) { e.name }",
1204            "DataCloneError",
1205        ),
1206        // `WeakMap`/`WeakSet`も`URLSearchParams`/`FormData`/`Headers`と同じ
1207        // 「メソッド群が`.props`上の生の関数値」パターンの疑似クラスで、
1208        // 仕様上も構造化複製不可(`DataCloneError`)。前サイクルまでの
1209        // `deep_clone_value`関数検出修正の副次効果で既に正しく動作している
1210        // ことを確認・記録する(2026-07-17)。
1211        (
1212            "try { structuredClone(new WeakMap()); 'no-throw' } catch(e) { e.name }",
1213            "DataCloneError",
1214        ),
1215        (
1216            "try { structuredClone(new WeakSet()); 'no-throw' } catch(e) { e.name }",
1217            "DataCloneError",
1218        ),
1219        // `AbortController`/`AbortSignal`/`MessagePort`/`MessageChannel`も
1220        // 同じ「メソッド群が`.props`上の生の関数値」パターンの疑似クラス
1221        // で、仕様上も構造化複製不可。前サイクルまでの`deep_clone_value`
1222        // 関数検出修正の副次効果で正しく動作していることを確認・記録し、
1223        // `DataCloneError`検証の横断監査を完了する(2026-07-17)。
1224        (
1225            "try { structuredClone(new AbortController()); 'no-throw' } catch(e) { e.name }",
1226            "DataCloneError",
1227        ),
1228        (
1229            "try { structuredClone(new AbortController().signal); 'no-throw' } catch(e) { e.name }",
1230            "DataCloneError",
1231        ),
1232        (
1233            "try { structuredClone(new MessageChannel()); 'no-throw' } catch(e) { e.name }",
1234            "DataCloneError",
1235        ),
1236        (
1237            "try { structuredClone(new MessageChannel().port1); 'no-throw' } catch(e) { e.name }",
1238            "DataCloneError",
1239        ),
1240        // structuredClone(Map/Set) — 中身(エントリ/要素)まで正しく複製され、元を変えても
1241        // 複製に影響しないこと(以前は ObjKind::MapObj/SetObj 自体が複製されず消えていたバグ)。
1242        (
1243            "var m=new Map([['a',1],['b',2]]); var c=structuredClone(m); m.set('a',99); c.get('a')+','+c.get('b')+','+c.size",
1244            "1,2,2",
1245        ),
1246        (
1247            "var s=new Set([1,2,3]); var c=structuredClone(s); s.add(4); c.has(4)+','+c.size",
1248            "false,3",
1249        ),
1250        // structuredClone(TypedArray) — TypedArray も内部表現は ObjKind::Array 流用
1251        // (`_ta_kind` タグで区別)なため、素の配列複製経路に落ちると型情報
1252        // (`_ta_kind`/`BYTES_PER_ELEMENT`/`set`/`slice`/`fill`)が丸ごと失われる
1253        // バグだった(Date/RegExp/Map/Set と同種)。
1254        ("var a=new Int8Array([200,-100]); var c=structuredClone(a); c.join(',')", "-56,-100"),
1255        (
1256            "var a=new Int8Array(1); var c=structuredClone(a); c.BYTES_PER_ELEMENT",
1257            "1",
1258        ),
1259        ("var a=new Int8Array(1); var c=structuredClone(a); c[0]=200; c[0]", "-56"),
1260        (
1261            "var a=new Int8Array([1,2]); var c=structuredClone(a); a[0]=9; c[0]",
1262            "1",
1263        ),
1264        // structuredClone(buf, {transfer:[buf]}) — 第2引数 transfer が丸ごと
1265        // 無視されており、転送指定した ArrayBuffer が detached にならなかった
1266        // バグ。
1267        (
1268            "var b=new ArrayBuffer(4); var c=structuredClone(b,{transfer:[b]}); b.detached+','+c.byteLength",
1269            "true,4",
1270        ),
1271        // structuredClone(Blob/DataView) — どちらも `Obj::plain()` + 隠しプロパティで
1272        // 実バイト列を持つ実装のため、汎用の plain object 複製に落ちると
1273        // `slice`/`text`/`getUint8` 等のネイティブ関数プロパティが呼出不能な壊れた
1274        // オブジェクトに複製されてしまうバグだった(TypedArray と同種)。
1275        (
1276            "var b=new Blob(['hi']); var c=structuredClone(b); await c.text()",
1277            "hi",
1278        ),
1279        ("var b=new Blob(['hi']); var c=structuredClone(b); c.size", "2"),
1280        (
1281            "var dv=new DataView(new ArrayBuffer(1)); dv.setUint8(0,65); var c=structuredClone(dv); c.getUint8(0)",
1282            "65",
1283        ),
1284        // structuredClone(ArrayBuffer) — `slice` がネイティブ関数プロパティのため
1285        // 同種のバグで複製後は呼出不能になっていた。
1286        (
1287            "var buf=new ArrayBuffer(4); var c=structuredClone(buf); c.byteLength",
1288            "4",
1289        ),
1290        (
1291            "var buf=new ArrayBuffer(4); var c=structuredClone(buf); typeof c.slice",
1292            "function",
1293        ),
1294        // structuredClone(Proxy) — 実データが `.props` ではなく `ObjKind::Proxy`
1295        // 側にあるため、汎用の plain object 複製に落ちると常に空の `{}` になる
1296        // バグだった(`Date`/`Map`/`Set` 等と同種)。仕様上は本来複製不可
1297        // (`DataCloneError`)だが、他の Proxy 透過性修正と同じ「target へ
1298        // フォワード」方針で target を複製する簡略実装とした。
1299        (
1300            "var p=new Proxy({a:1,b:2},{}); var c=structuredClone(p); c.a+','+c.b",
1301            "1,2",
1302        ),
1303        (
1304            "var t={a:1}; var p=new Proxy(t,{}); var c=structuredClone(p); t.a=99; c.a",
1305            "1",
1306        ),
1307        (
1308            "var buf = await new Blob(['A']).arrayBuffer(); var c=structuredClone(buf); new DataView(c).getUint8(0)",
1309            "65",
1310        ),
1311        // Object: fromEntries / getOwnPropertyNames / freeze
1312        ("Object.fromEntries([['a',1],['b',2]]).b", "2"),
1313        ("Object.getOwnPropertyNames({x:1,y:2}).join(',')", "x,y"),
1314        ("var o=Object.freeze({a:1}); o.a", "1"),
1315        // Number 静的
1316        ("Number.isInteger(5)", "true"),
1317        ("Number.isInteger(5.5)", "false"),
1318        ("Number.isInteger('5')", "false"),
1319        ("Number.isSafeInteger(9007199254740991)", "true"),
1320        ("Number.MAX_SAFE_INTEGER", "9007199254740991"),
1321        ("Number.isNaN(NaN) + ',' + Number.isNaN(5)", "true,false"),
1322        // `Number.MIN_VALUE`/`Number.NaN`(ES1 以来の定数)が丸ごと未登録だった。
1323        // `MIN_VALUE` は「0に最も近い正の値」(非正規化数まで含む)であり
1324        // `MIN_SAFE_INTEGER`(大きな負の整数)とは別物であることに注意。
1325        ("Number.MIN_VALUE > 0", "true"),
1326        ("Number.MIN_VALUE < Number.EPSILON", "true"),
1327        ("Number.isNaN(Number.NaN)", "true"),
1328        // Math 追加
1329        ("Math.hypot(3,4)", "5"),
1330        ("Math.log2(8)", "3"),
1331        ("Math.round(Math.log10(1000))", "3"),
1332        ("Math.round(Math.cbrt(27))", "3"),
1333        ("Math.atan2(0,5)", "0"),
1334        // Math 追加2(ES2015。fround/clz32/imul/sinh系/expm1/log1p/定数群が丸ごと未対応だった)。
1335        ("Math.fround(1.5)", "1.5"),
1336        ("Math.fround(1.1) !== 1.1", "true"),
1337        ("Math.f16round(1.5)", "1.5"),
1338        ("Math.f16round(1.337) !== 1.337", "true"),
1339        ("Math.sumPrecise([1, 2, 3])", "6"),
1340        ("Math.sumPrecise([0.1, 0.2])", "0.30000000000000004"),
1341        ("Math.sumPrecise([1, Infinity])", "Infinity"),
1342        ("isNaN(Math.sumPrecise([Infinity, -Infinity]))", "true"),
1343        ("isNaN(Math.sumPrecise([1, NaN]))", "true"),
1344        ("new Headers({'Set-Cookie':'a=1'}).getSetCookie().join(',')", "a=1"),
1345        ("var h=new Headers(); h.append('Set-Cookie','a=1'); h.append('Set-Cookie','b=2'); h.getSetCookie().join('; ')", "a=1; b=2"),
1346        ("Math.clz32(1)", "31"),
1347        ("Math.clz32(0)", "32"),
1348        ("Math.clz32(1000)", "22"),
1349        ("Math.imul(3,4)", "12"),
1350        ("Math.imul(0xffffffff, 5)", "-5"),
1351        ("Math.round(Math.sinh(0))", "0"),
1352        ("Math.cosh(0)", "1"),
1353        ("Math.tanh(0)", "0"),
1354        ("Math.asinh(0)", "0"),
1355        ("Math.acosh(1)", "0"),
1356        ("Math.atanh(0)", "0"),
1357        ("Math.round(Math.expm1(0))", "0"),
1358        ("Math.log1p(0)", "0"),
1359        ("Math.round(Math.LN2*1000)", "693"),
1360        ("Math.round(Math.LN10*1000)", "2303"),
1361        ("Math.round(Math.SQRT2*1000)", "1414"),
1362        ("Math.round(Math.SQRT1_2*1000)", "707"),
1363        // AbortController
1364        ("var c=new AbortController(); var b=c.signal.aborted; c.abort(); b+','+c.signal.aborted", "false,true"),
1365        // `signal.addEventListener('abort', fn)` が丸ごと no-op で、`abort()` を呼んでも
1366        // 登録済みリスナが一切発火しないバグだった。`signal.reason` も未対応だった。
1367        (
1368            "var c=new AbortController(); var fired=false; c.signal.addEventListener('abort', function(){fired=true;}); c.abort(); fired",
1369            "true",
1370        ),
1371        ("var c=new AbortController(); c.abort(); c.signal.reason.name", "AbortError"),
1372        ("var c=new AbortController(); c.abort('custom'); c.signal.reason", "custom"),
1373        // 二重 abort() は2回目以降が黙殺される(リスナが2回発火しない)。
1374        (
1375            "var c=new AbortController(); var n=0; c.signal.addEventListener('abort', function(){n++;}); c.abort(); c.abort(); n",
1376            "1",
1377        ),
1378        // `AbortSignal.abort(reason)`(ES2022)が、`AbortController` 経由でしか
1379        // signal を得られず丸ごと未対応だった。
1380        ("AbortSignal.abort().aborted", "true"),
1381        ("AbortSignal.abort('why').reason", "why"),
1382        ("AbortSignal.abort().reason.name", "AbortError"),
1383        // `DOMException`(丸ごと未対応だった。`new DOMException(message, name)`
1384        // というユーザーコードからの直接構築、レガシー `.code` 数値定数)。
1385        ("new DOMException('boom', 'NotFoundError').message", "boom"),
1386        ("new DOMException('boom', 'NotFoundError').name", "NotFoundError"),
1387        ("new DOMException('boom', 'NotFoundError').code", "8"),
1388        ("DOMException.NOT_FOUND_ERR", "8"),
1389        ("new DOMException().name", "Error"),
1390        ("new DOMException('x', 'CustomError').code", "0"),
1391        // 既存の AbortError も同じ DOMException 経路で構築されるようになった
1392        // (`.code` が正しく `20`=`ABORT_ERR` になることを確認)。
1393        ("AbortSignal.abort().reason.code", "20"),
1394        // `signal.removeEventListener('abort', fn)` が `addEventListener` は対応済み
1395        // なのに対をなす削除側だけ無条件 no-op のままだったバグ。
1396        (
1397            "var c=new AbortController(); var n=0; var fn=function(){n++;}; c.signal.addEventListener('abort', fn); c.signal.removeEventListener('abort', fn); c.abort(); n",
1398            "0",
1399        ),
1400        (
1401            "var c=new AbortController(); var n=0; c.signal.addEventListener('abort', function(){n++;}); c.signal.removeEventListener('abort', function(){}); c.abort(); n",
1402            "1",
1403        ),
1404        // `AbortSignal.any(iterable)`(ES2024)— 複数の中断条件を1つの signal にまとめる。
1405        (
1406            "var c1=new AbortController(); var c2=new AbortController(); var s=AbortSignal.any([c1.signal, c2.signal]); var before=s.aborted; c2.abort('two'); before+','+s.aborted+','+s.reason",
1407            "false,true,two",
1408        ),
1409        (
1410            "var c1=new AbortController(); c1.abort('already'); AbortSignal.any([c1.signal]).reason",
1411            "already",
1412        ),
1413        // `signal.throwIfAborted()` が丸ごと未対応だった。中断前は no-op。
1414        ("var c=new AbortController(); c.signal.throwIfAborted()", "undefined"),
1415        // 中断後は `reason` を投げる。
1416        (
1417            "var c=new AbortController(); c.abort('boom'); \
1418             var caught; try { c.signal.throwIfAborted(); } catch (e) { caught = e; } caught",
1419            "boom",
1420        ),
1421        // `EventTarget` が丸ごと未対応だった(DOM 要素以外で独自のイベント発行/購読の
1422        // 基盤として使う定番パターン)。他の EventTarget 風オブジェクトと違い型ごとに
1423        // 複数リスナを保持する。
1424        (
1425            "var t=new EventTarget(); var log=''; t.addEventListener('x', ()=>log+='a'); t.addEventListener('x', ()=>log+='b'); t.dispatchEvent(new Event('x')); log",
1426            "ab",
1427        ),
1428        (
1429            "var t=new EventTarget(); var n=0; var fn=()=>n++; t.addEventListener('x', fn); t.addEventListener('x', fn); t.dispatchEvent(new Event('x')); n",
1430            "1",
1431        ),
1432        (
1433            "var t=new EventTarget(); var n=0; var fn=()=>n++; t.addEventListener('x', fn); t.removeEventListener('x', fn); t.dispatchEvent(new Event('x')); n",
1434            "0",
1435        ),
1436        // 同一関数を capture/bubble 両方で登録すると重複扱いされず2件とも
1437        // 保持される(仕様上 `(listener, capture)` の組で区別すべきところ、
1438        // 以前は `capture` を見ずに `cb` だけで重複判定していたため2回目の
1439        // 登録が黙って無視されるバグだった。`document`のcapture対応
1440        // 〔2026-07-16〕で顕在化・同日発見・修正)。
1441        (
1442            "var t=new EventTarget(); var n=0; var fn=()=>n++; t.addEventListener('x', fn, true); t.addEventListener('x', fn, false); t.dispatchEvent(new Event('x')); n",
1443            "2",
1444        ),
1445        // `removeEventListener(type, fn)`(capture省略=既定false)はcapture
1446        // 登録済みの同一関数を巻き込んで消してはいけない。
1447        (
1448            "var t=new EventTarget(); var n=0; var fn=()=>n++; t.addEventListener('x', fn, true); t.addEventListener('x', fn, false); t.removeEventListener('x', fn); t.dispatchEvent(new Event('x')); n",
1449            "1",
1450        ),
1451        // `addEventListener(type, fn, {signal})`(丸ごと未対応だった。
1452        // `AbortController` でリスナを一括解除する定番パターン。2026-07-14
1453        // 発見・実装)。abort 後は発火しなくなる。
1454        (
1455            "var t=new EventTarget(); var c=new AbortController(); var n=0; \
1456             t.addEventListener('x', ()=>n++, {signal: c.signal}); \
1457             t.dispatchEvent(new Event('x')); c.abort(); t.dispatchEvent(new Event('x')); n",
1458            "1",
1459        ),
1460        // 登録前に既に abort 済みの signal を渡した場合は登録自体が行われない。
1461        (
1462            "var t=new EventTarget(); var c=new AbortController(); c.abort(); var n=0; \
1463             t.addEventListener('x', ()=>n++, {signal: c.signal}); \
1464             t.dispatchEvent(new Event('x')); n",
1465            "0",
1466        ),
1467        // DOM 要素側(`Element.prototype.addEventListener`)でも同じく対応。
1468        (
1469            "var el=document.createElement('div'); var c=new AbortController(); var n=0; \
1470             el.addEventListener('click', ()=>n++, {signal: c.signal}); \
1471             el.click(); c.abort(); el.click(); n",
1472            "1",
1473        ),
1474        // `event.preventDefault()` が丸ごと no-op で `defaultPrevented` を一切
1475        // 追跡せず、`dispatchEvent()` の戻り値が常に `true` になるバグだった。
1476        (
1477            "var t=new EventTarget(); t.addEventListener('x', e => e.preventDefault()); t.dispatchEvent(new Event('x', {cancelable:true}))",
1478            "false",
1479        ),
1480        (
1481            "var t=new EventTarget(); t.addEventListener('x', e => e.preventDefault()); t.dispatchEvent(new Event('x'))",
1482            "true",
1483        ),
1484        // `event.isTrusted` が丸ごと未対応だった。スクリプトから発火した
1485        // イベントは仕様上常に `false`。
1486        (
1487            "var t=new EventTarget(); var r; t.addEventListener('x', e => { r = e.isTrusted; }); t.dispatchEvent(new Event('x')); r",
1488            "false",
1489        ),
1490        // `event.composed`(丸ごと未対応だった。`Event`/`CustomEvent`両方の
1491        // コンストラクタで`options.composed`が一切読まれず常に`undefined`に
1492        // なっていた。2026-07-16 発見・実装)。
1493        ("new Event('x', {composed: true}).composed", "true"),
1494        ("new Event('x').composed", "false"),
1495        (
1496            "new CustomEvent('x', {detail: 1, composed: true}).composed",
1497            "true",
1498        ),
1499        // `EventTarget` のイベントに `stopImmediatePropagation` が丸ごと
1500        // 未対応で、呼び出すと TypeError になっていた(そもそも呼び出しようが
1501        // ないため残りのリスナーを止める効果も一切なかった)。
1502        (
1503            "var t=new EventTarget(); var log=''; \
1504             t.addEventListener('x', e => { log+='a'; e.stopImmediatePropagation(); }); \
1505             t.addEventListener('x', () => { log+='b'; }); \
1506             t.dispatchEvent(new Event('x')); log",
1507            "a",
1508        ),
1509        // `EventTarget.addEventListener` の第3引数 `{once:true}` が丸ごと
1510        // 読まれておらず、一度発火しても解除されず何度でも呼ばれ続けていた。
1511        (
1512            "var t=new EventTarget(); var n=0; t.addEventListener('x', ()=>n++, {once:true}); \
1513             t.dispatchEvent(new Event('x')); t.dispatchEvent(new Event('x')); n",
1514            "1",
1515        ),
1516        // once 指定でも removeEventListener で明示的に解除できる。
1517        (
1518            "var t=new EventTarget(); var n=0; var fn=()=>n++; t.addEventListener('x', fn, {once:true}); \
1519             t.removeEventListener('x', fn); t.dispatchEvent(new Event('x')); n",
1520            "0",
1521        ),
1522        // `EventTarget` 発の `Event`/`CustomEvent` に `composedPath()` が丸ごと
1523        // 未対応で、呼び出すと TypeError になっていた(DOM 要素側は既に対応済み)。
1524        (
1525            "var t=new EventTarget(); var p; t.addEventListener('x', e => { p = e.composedPath(); }); \
1526             t.dispatchEvent(new Event('x')); p.length === 1 && p[0] === t",
1527            "true",
1528        ),
1529        // 発火前(target 未設定)は composedPath() が空配列を返す。
1530        ("new CustomEvent('x').composedPath().length", "0"),
1531        // `KeyboardEvent`/`MouseEvent`(丸ごと未対応だった。`el.dispatchEvent
1532        // (new KeyboardEvent('keydown', {key:'a'}))` というシミュレーション
1533        // イベント構築の定番パターン)。
1534        ("new KeyboardEvent('keydown', {key:'a', code:'KeyA'}).key", "a"),
1535        ("new KeyboardEvent('keydown', {key:'a', code:'KeyA'}).code", "KeyA"),
1536        (
1537            "var t=new EventTarget(); var got; \
1538             t.addEventListener('keydown', e => { got = e.ctrlKey + ':' + e.key; }); \
1539             t.dispatchEvent(new KeyboardEvent('keydown', {key:'Enter', ctrlKey:true})); got",
1540            "true:Enter",
1541        ),
1542        ("new MouseEvent('click', {clientX:10, clientY:20}).clientX", "10"),
1543        (
1544            "var t=new EventTarget(); var got; \
1545             t.addEventListener('click', e => { got = e.clientY + ':' + e.button; }); \
1546             t.dispatchEvent(new MouseEvent('click', {clientY:5, button:2})); got",
1547            "5:2",
1548        ),
1549        ("new MouseEvent('click').bubbles", "false"),
1550        ("new MouseEvent('click', {bubbles:true}).bubbles", "true"),
1551        // `WheelEvent`/`FocusEvent`(丸ごと未対応だった。`MouseEvent` を継承
1552        // する `WheelEvent` は座標系プロパティも合わせて持つべき仕様)。
1553        ("new WheelEvent('wheel', {deltaY:100}).deltaY", "100"),
1554        ("new WheelEvent('wheel', {deltaY:1, clientX:5}).clientX", "5"),
1555        ("new WheelEvent('wheel').deltaMode", "0"),
1556        ("new FocusEvent('focusout', {relatedTarget: null}).relatedTarget === null", "true"),
1557        (
1558            "var t=new EventTarget(); var r=new EventTarget(); var got; \
1559             t.addEventListener('focusout', e => { got = e.relatedTarget; }); \
1560             t.dispatchEvent(new FocusEvent('focusout', {relatedTarget: r})); got === r",
1561            "true",
1562        ),
1563        // `Touch`/`TouchEvent`(丸ごと未対応だった。この処理系にはタッチ
1564        // スクリーン入力機構自体が無いが、コンストラクタでのシミュレーション
1565        // パターンには対応する。2026-07-15 発見・実装)。
1566        ("new Touch({identifier:1, clientX:10, clientY:20}).clientX", "10"),
1567        ("new Touch({}).force", "1"),
1568        (
1569            "var t=new Touch({identifier:1}); \
1570             new TouchEvent('touchstart', {touches:[t], changedTouches:[t]}).touches[0] === t",
1571            "true",
1572        ),
1573        // `CommandEvent`/`ToggleEvent`/`FormDataEvent`(WHATWG HTML Standard / Invoker Commands API)
1574        ("new CommandEvent('command', {command:'show-modal'}).command", "show-modal"),
1575        ("new CommandEvent('command', {source:null}).source === null", "true"),
1576        ("new ToggleEvent('toggle', {oldState:'closed', newState:'open'}).newState", "open"),
1577        ("new ToggleEvent('toggle').oldState", "closed"),
1578        ("new FormDataEvent('formdata', {formData:null}).formData === null", "true"),
1579
1580        ("new TouchEvent('touchend').changedTouches.length", "0"),
1581        // `HashChangeEvent`/`PageTransitionEvent`(丸ごと未対応だった。
1582        // 2026-07-15 発見・実装)。
1583        (
1584            "new HashChangeEvent('hashchange', {oldURL:'a', newURL:'b'}).oldURL + ',' + \
1585             new HashChangeEvent('hashchange', {oldURL:'a', newURL:'b'}).newURL",
1586            "a,b",
1587        ),
1588        (
1589            "new HashChangeEvent('hashchange').type",
1590            "hashchange",
1591        ),
1592        (
1593            "new PageTransitionEvent('pageshow', {persisted:true}).persisted",
1594            "true",
1595        ),
1596        // `PopStateEvent`(丸ごと未対応だった。実ナビゲーション経由の発火は
1597        // 既に`state`付きで動いていたが、手動シミュレーション用の専用
1598        // コンストラクタが無かった。2026-07-17 発見・実装)。
1599        (
1600            "new PopStateEvent('popstate', {state:{a:1}}).state.a",
1601            "1",
1602        ),
1603        ("new PopStateEvent('popstate').state", "null"),
1604        (
1605            "var got=null; window.addEventListener('popstate', e => got = e.state); \
1606             window.dispatchEvent(new PopStateEvent('popstate', {state:'s'})); got",
1607            "s",
1608        ),
1609        // `DOMRect`/`DOMRectReadOnly`コンストラクタ(丸ごと未対応だった。
1610        // `getBoundingClientRect()`と同じ形状で手動構築する定番パターン。
1611        // 2026-07-17 発見・実装)。
1612        (
1613            "var r=new DOMRect(10,20,30,40); \
1614             [r.x,r.y,r.width,r.height,r.left,r.top,r.right,r.bottom].join(',')",
1615            "10,20,30,40,10,20,40,60",
1616        ),
1617        ("new DOMRect().width", "0"),
1618        ("new DOMRectReadOnly(1,2,3,4).right", "4"),
1619        ("JSON.stringify(new DOMRect(1,2,3,4).toJSON()).includes('\"width\":3')", "true"),
1620        // `DOMPoint`/`DOMPointReadOnly`コンストラクタおよび DOMMatrix/DOMQuad (Geometry Interfaces)
1621        (
1622            "var p=new DOMPoint(1,2); p.x+','+p.y+','+p.z+','+p.w",
1623            "1,2,0,1",
1624        ),
1625        ("new DOMPoint(1,2,3,4).w", "4"),
1626        ("new DOMPointReadOnly().x", "0"),
1627        ("JSON.stringify(new DOMPoint(1,2).toJSON()).includes('\"y\":2')", "true"),
1628        ("new DOMPoint(10, 20).matrixTransform(new DOMMatrix().translate(5, 5)).x", "15"),
1629        ("new DOMMatrix().isIdentity", "true"),
1630        ("new DOMMatrix().is2D", "true"),
1631        ("new DOMMatrix([1,0,0,1,10,20]).toString()", "matrix(1, 0, 0, 1, 10, 20)"),
1632        ("new DOMMatrix().translate(10, 20).e", "10"),
1633        ("new DOMMatrix().scale(2, 3).d", "3"),
1634        ("DOMMatrix.fromFloat32Array(new Float32Array([1,0,0,1,10,20])).e", "10"),
1635        ("new DOMMatrix().rotate(90).b", "1"),
1636        ("new DOMMatrix([1,0,0,1,10,20]).invert().e", "-10"),
1637        ("new DOMMatrix().translateSelf(10, 20).e", "10"),
1638        ("new DOMMatrix().rotateSelf(90).b", "1"),
1639        ("new DOMMatrix().scaleSelf(2, 3).d", "3"),
1640        ("new DOMMatrix([1,0,0,1,10,20]).invertSelf().e", "-10"),
1641        ("DOMQuad.fromRect(new DOMRect(10, 20, 30, 40)).getBounds().width", "30"),
1642        ("DOMRect.fromRect({x: 5, y: 10, width: 15, height: 20}).width", "15"),
1643        ("DOMPoint.fromPoint({x: 3, y: 4, z: 5, w: 1}).z", "5"),
1644        ("'가'.normalize('NFD').normalize('NFC')", "가"),
1645
1646        // `window.dispatchEvent(event)`(丸ごと未対応だった。前サイクルで
1647        // 発見した既知ギャップを本サイクルで解消。2026-07-15 発見・実装)。
1648        (
1649            "var got=null; window.addEventListener('hashchange', e => got = e.newURL); \
1650             window.dispatchEvent(new HashChangeEvent('hashchange', {newURL:'x'})); got",
1651            "x",
1652        ),
1653        (
1654            "window.dispatchEvent(new Event('unknown-type'))",
1655            "true",
1656        ),
1657        // `window.dispatchEvent()` は `addEventListener` 登録分だけでなく
1658        // `window.on<type>`(IDL属性スタイル)も合わせて呼び出す(丸ごと
1659        // 未対応だった。2026-07-15 発見・実装)。
1660        (
1661            "var n=0; window.onhashchange = () => n++; \
1662             window.dispatchEvent(new HashChangeEvent('hashchange')); n",
1663            "1",
1664        ),
1665        (
1666            "var order=[]; window.onresize = () => order.push('on'); \
1667             window.addEventListener('resize', () => order.push('listener')); \
1668             window.dispatchEvent(new Event('resize')); order.join(',')",
1669            "on,listener",
1670        ),
1671        // `window.removeEventListener()`(丸ごと未対応だった。以前は無害な
1672        // no-op のままで、`AbortSignal` 経由以外ではリスナが二度と解除
1673        // できなかった。2026-07-15 発見・実装)。
1674        (
1675            "var n=0; var cb = () => n++; window.addEventListener('resize', cb); \
1676             window.removeEventListener('resize', cb); \
1677             window.dispatchEvent(new Event('resize')); n",
1678            "0",
1679        ),
1680        (
1681            "var log=[]; var cb1 = () => log.push('1'); var cb2 = () => log.push('2'); \
1682             window.addEventListener('resize', cb1); window.addEventListener('resize', cb2); \
1683             window.removeEventListener('resize', cb1); \
1684             window.dispatchEvent(new Event('resize')); log.join(',')",
1685            "2",
1686        ),
1687        // `document.removeEventListener()`(丸ごと未対応だった。`addEventListener`
1688        // は `window` 版を再利用していたが、対になる `removeEventListener`
1689        // 自体が未登録で「関数ではない」`TypeError` になっていた。2026-07-15
1690        // 発見・実装)。
1691        (
1692            "typeof document.removeEventListener",
1693            "function",
1694        ),
1695        (
1696            "var n=0; var cb = () => n++; document.addEventListener('hashchange', cb); \
1697             document.removeEventListener('hashchange', cb); \
1698             window.dispatchEvent(new Event('hashchange')); n",
1699            "0",
1700        ),
1701        // `document.dispatchEvent`(丸ごと未対応だった。`addEventListener`/
1702        // `removeEventListener`と同じ兄弟ギャップ。2026-07-15 発見・実装)。
1703        (
1704            "var got=null; document.addEventListener('hashchange', e => got = e.newURL); \
1705             document.dispatchEvent(new HashChangeEvent('hashchange', {newURL:'y'})); got",
1706            "y",
1707        ),
1708        // `window.dispatchEvent()`/`document.dispatchEvent()` の戻り値が仕様
1709        // どおり `!defaultPrevented` になること(以前は無条件 `true` を返し、
1710        // リスナが `preventDefault()` を呼んでも常に `true` だった。要素用
1711        // `dom_dispatch_event`/`event_target_dispatch_event` は正しく実装済みで
1712        // window/document 経路にだけ同型バグが残っていた。2026-07-16 発見・修正)。
1713        (
1714            "window.addEventListener('resize', e => e.preventDefault()); \
1715             window.dispatchEvent(new Event('resize', {cancelable:true}))",
1716            "false",
1717        ),
1718        (
1719            "document.addEventListener('scroll', e => e.preventDefault()); \
1720             document.dispatchEvent(new Event('scroll', {cancelable:true}))",
1721            "false",
1722        ),
1723        // `preventDefault()` を呼ばなければ従来どおり `true` を返す。
1724        (
1725            "window.addEventListener('popstate', () => {}); \
1726             window.dispatchEvent(new Event('popstate'))",
1727            "true",
1728        ),
1729        // dispatch 中に `event.target` が dispatch 対象へ設定されること(以前は
1730        // window/document 経路では一切設定されず `undefined` だった。2026-07-16
1731        // 発見・修正)。
1732        (
1733            "var t='none'; window.addEventListener('message', e => t = (e.target === window)); \
1734             window.dispatchEvent(new Event('message')); t",
1735            "true",
1736        ),
1737        // `window.dispatchEvent(new ErrorEvent(...))` 経由でも `window.onerror`
1738        // がレガシー5引数シグネチャ `(message, source, lineno, colno, error)` で
1739        // 呼ばれること(以前は Event オブジェクトを1引数でそのまま渡していて
1740        // `reportError()`〔既に正しい5引数呼び出し〕と経路によって呼び出し形が
1741        // 食い違っていた。2026-07-16 発見・修正)。
1742        (
1743            "var got=''; window.onerror = (msg, src, line, col) => { got = msg+'|'+src+'|'+line+'|'+col; }; \
1744             window.dispatchEvent(new ErrorEvent('error', {message:'boom', filename:'a.js', lineno:3, colno:7})); got",
1745            "boom|a.js|3|7",
1746        ),
1747        // `online`/`offline`(丸ごと未対応だった。`addEventListener('online',
1748        // ...)` が登録経路自体無く黙って何もしなかった。2026-07-16 発見・実装)。
1749        (
1750            "var n=0; window.addEventListener('online', () => n++); \
1751             window.dispatchEvent(new Event('online')); n",
1752            "1",
1753        ),
1754        (
1755            "var n=0; var cb = () => n++; window.addEventListener('offline', cb); \
1756             window.removeEventListener('offline', cb); \
1757             window.dispatchEvent(new Event('offline')); n",
1758            "0",
1759        ),
1760        // `beforeunload`/`unload`/`pageshow`/`pagehide`/`visibilitychange`/
1761        // `languagechange`(丸ごと未対応だった。2026-07-16 発見・実装)。
1762        (
1763            "var log=[]; \
1764             window.addEventListener('beforeunload', () => log.push('bu')); \
1765             window.addEventListener('unload', () => log.push('u')); \
1766             window.addEventListener('pageshow', () => log.push('ps')); \
1767             window.addEventListener('pagehide', () => log.push('ph')); \
1768             document.addEventListener('visibilitychange', () => log.push('vc')); \
1769             window.addEventListener('languagechange', () => log.push('lc')); \
1770             window.dispatchEvent(new Event('beforeunload')); \
1771             window.dispatchEvent(new Event('unload')); \
1772             window.dispatchEvent(new Event('pageshow')); \
1773             window.dispatchEvent(new Event('pagehide')); \
1774             document.dispatchEvent(new Event('visibilitychange')); \
1775             window.dispatchEvent(new Event('languagechange')); \
1776             log.join(',')",
1777            "bu,u,ps,ph,vc,lc",
1778        ),
1779        // `document.visibilityState`/`.hidden`(丸ごと未対応だった。2026-07-16
1780        // 発見・実装)。
1781        ("document.visibilityState", "visible"),
1782        ("document.hidden", "false"),
1783        // `document.characterSet`/`.compatMode`/`.contentType`/`.doctype`
1784        // (丸ごと未対応だった。2026-07-16 発見・実装)。
1785        ("document.characterSet", "UTF-8"),
1786        ("document.compatMode", "CSS1Compat"),
1787        ("document.contentType", "text/html"),
1788        ("document.doctype", "null"),
1789        // `document.currentScript`(丸ごと未対応だった。2026-07-16 発見・
1790        // 実装。実行中スクリプト要素の追跡機構が無いため常に `null` を返す
1791        // 誠実な簡略実装)。
1792        ("document.currentScript", "null"),
1793        // `document.implementation`(`DOMImplementation`。丸ごと未対応
1794        // だった。2026-07-16 発見・実装。`hasFeature()`は仕様どおり常に
1795        // `true`を返すレガシー no-op、`createDocumentType()`は独立した
1796        // 読み取り専用簡略オブジェクトを返す)。
1797        ("document.implementation.hasFeature('x','1.0')", "true"),
1798        (
1799            "var dt=document.implementation.createDocumentType('html',' ',''); dt.name+','+dt.nodeType",
1800            "html,10",
1801        ),
1802        (
1803            "var doc=document.implementation.createHTMLDocument('MyDoc'); doc.title+','+doc.nodeType",
1804            "MyDoc,9",
1805        ),
1806        (
1807            "var doc=document.implementation.createDocument('http://www.w3.org/1999/xhtml','html'); doc.qualifiedName+','+doc.nodeType",
1808            "html,9",
1809        ),
1810        // `storage` イベント(丸ごと未対応だった。`StorageEvent` コンストラクタ
1811        // 自体は既に対応済みだったが登録経路が無かった。2026-07-16 発見・実装)。
1812        (
1813            "var got=null; window.addEventListener('storage', e => got = e.key); \
1814             window.dispatchEvent(new StorageEvent('storage', {key:'k'})); got",
1815            "k",
1816        ),
1817        // `document.addEventListener('readystatechange', ...)`(丸ごと未対応
1818        // だった。`document.readyState` は常に `"complete"` のため
1819        // `load`/`DOMContentLoaded` と同じ即時発火。2026-07-16 発見・実装)。
1820        (
1821            "var n=0; document.addEventListener('readystatechange', () => n++); n",
1822            "1",
1823        ),
1824        // `window.print()`/`beforeprint`/`afterprint`(丸ごと未対応だった。
1825        // 2026-07-16 発見・実装)。
1826        ("window.print(); true", "true"),
1827        (
1828            "var log=[]; \
1829             window.addEventListener('beforeprint', () => log.push('bp')); \
1830             window.addEventListener('afterprint', () => log.push('ap')); \
1831             window.dispatchEvent(new Event('beforeprint')); \
1832             window.dispatchEvent(new Event('afterprint')); \
1833             log.join(',')",
1834            "bp,ap",
1835        ),
1836        // `PointerEvent`/`InputEvent`(丸ごと未対応だった。`PointerEvent` は
1837        // `MouseEvent` を継承するため座標系プロパティも合わせて持つべき仕様)。
1838        ("new PointerEvent('pointerdown', {pointerId:7, pointerType:'touch'}).pointerId", "7"),
1839        ("new PointerEvent('pointerdown', {pointerType:'touch', clientX:9}).clientX", "9"),
1840        ("new PointerEvent('pointerdown').isPrimary", "false"),
1841        ("new PointerEvent('pointerdown', {isPrimary:true}).width", "1"),
1842        ("new InputEvent('input', {data:'a', inputType:'insertText'}).data", "a"),
1843        ("new InputEvent('input', {data:'a', inputType:'insertText'}).inputType", "insertText"),
1844        ("new InputEvent('beforeinput').isComposing", "false"),
1845        // `setPointerCapture`/`releasePointerCapture`/`hasPointerCapture`
1846        // (Pointer Events。丸ごと未対応だった)。
1847        (
1848            "var e=document.createElement('div'); e.hasPointerCapture(1)",
1849            "false",
1850        ),
1851        (
1852            "var e=document.createElement('div'); e.setPointerCapture(1); e.hasPointerCapture(1)",
1853            "true",
1854        ),
1855        (
1856            "var e=document.createElement('div'); e.setPointerCapture(1); \
1857             e.releasePointerCapture(1); e.hasPointerCapture(1)",
1858            "false",
1859        ),
1860        // 別 pointerId には影響しない。
1861        (
1862            "var e=document.createElement('div'); e.setPointerCapture(1); e.hasPointerCapture(2)",
1863            "false",
1864        ),
1865        // `ProgressEvent`/`StorageEvent`(丸ごと未対応だった)。
1866        ("new ProgressEvent('progress', {loaded:50, total:100}).loaded", "50"),
1867        ("new ProgressEvent('progress', {lengthComputable:true}).lengthComputable", "true"),
1868        ("new ProgressEvent('progress').total", "0"),
1869        ("new StorageEvent('storage', {key:'k', newValue:'v'}).key", "k"),
1870        ("new StorageEvent('storage', {key:'k', newValue:'v'}).newValue", "v"),
1871        ("new StorageEvent('storage').oldValue === null", "true"),
1872        // `CompositionEvent`/`ClipboardEvent`(丸ごと未対応だった)。
1873        ("new CompositionEvent('compositionupdate', {data:'あ'}).data", "あ"),
1874        ("new CompositionEvent('compositionstart').data", ""),
1875        (
1876            "var t=new EventTarget(); var got; \
1877             t.addEventListener('paste', e => { got = e.clipboardData; }); \
1878             t.dispatchEvent(new ClipboardEvent('paste', {clipboardData:'X'})); got",
1879            "X",
1880        ),
1881        ("new ClipboardEvent('copy').clipboardData === null", "true"),
1882        // `AnimationEvent`/`TransitionEvent`/`DragEvent`/`SubmitEvent`(丸ごと
1883        // 未対応だった。`DragEvent` は `MouseEvent` を継承するため座標系
1884        // プロパティも合わせて持つべき仕様)。
1885        ("new AnimationEvent('animationend', {animationName:'spin'}).animationName", "spin"),
1886        ("new AnimationEvent('animationend').elapsedTime", "0"),
1887        ("new TransitionEvent('transitionend', {propertyName:'opacity'}).propertyName", "opacity"),
1888        ("new DragEvent('drop', {dataTransfer:'X'}).dataTransfer", "X"),
1889        ("new DragEvent('drop', {clientX:3}).clientX", "3"),
1890        ("new SubmitEvent('submit', {submitter:'BTN'}).submitter", "BTN"),
1891        ("new SubmitEvent('submit').submitter === null", "true"),
1892        // `MessageEvent`/`ErrorEvent`(丸ごと未対応だった。`window.
1893        // postMessage()`/`reportError()` の内部イベント形と同じ形をユーザー
1894        // コードから直接構築できる)。
1895        ("new MessageEvent('message', {data:'hi', origin:'https://a.com'}).data", "hi"),
1896        ("new MessageEvent('message', {data:'hi', origin:'https://a.com'}).origin", "https://a.com"),
1897        ("new MessageEvent('message').ports.length", "0"),
1898        ("new ErrorEvent('error', {message:'boom', lineno:5}).message", "boom"),
1899        ("new ErrorEvent('error', {message:'boom', lineno:5}).lineno", "5"),
1900        ("new ErrorEvent('error').error === null", "true"),
1901        // `EventTarget` 発のイベントで `currentTarget`/`eventPhase` が丸ごと
1902        // 未設定で常に undefined だった(DOM 要素側は既に対応済み)。
1903        (
1904            "var t=new EventTarget(); var ct, ph; \
1905             t.addEventListener('x', e => { ct = e.currentTarget; ph = e.eventPhase; }); \
1906             t.dispatchEvent(new Event('x')); ct === t && ph === 2",
1907            "true",
1908        ),
1909        // 発火完了後は currentTarget が null、eventPhase が 0 に戻る。
1910        (
1911            "var t=new EventTarget(); var e=new Event('x'); t.dispatchEvent(e); \
1912             e.currentTarget === null && e.eventPhase",
1913            "0",
1914        ),
1915        // `Event.timeStamp` がこのエンジン全体に丸ごと未対応で常に undefined
1916        // だった(DOM要素・EventTarget どちらの経路でも欠落していた)。
1917        ("typeof new Event('x').timeStamp", "number"),
1918        (
1919            "var b=document.createElement('button'); var ts; \
1920             b.addEventListener('click', e => { ts = e.timeStamp; }); \
1921             b.dispatchEvent({type:'click'}); typeof ts",
1922            "number",
1923        ),
1924        // レガシー DOM Level 0 の `returnValue`/`cancelBubble`/`srcElement` が
1925        // 丸ごと未対応だった。`e.returnValue = false` は `preventDefault()` と
1926        // 等価であるべき。
1927        (
1928            "var t=new EventTarget(); var r; \
1929             t.addEventListener('x', e => { e.returnValue = false; }); \
1930             r = t.dispatchEvent(new Event('x', {cancelable:true})); r",
1931            "false",
1932        ),
1933        // `e.cancelBubble = true` は `stopPropagation()` と等価であるべき。
1934        (
1935            "var b=document.createElement('button'); var p=document.createElement('div'); \
1936             p.appendChild(b); var s=''; \
1937             p.addEventListener('click', ()=>s+='P'); \
1938             b.addEventListener('click', e => { s+='B'; e.cancelBubble = true; }); \
1939             b.dispatchEvent({type:'click', bubbles:true}); s",
1940            "B",
1941        ),
1942        // `srcElement` は `target` と同じ値の別名。
1943        (
1944            "var t=new EventTarget(); var se; \
1945             t.addEventListener('x', e => { se = e.srcElement; }); \
1946             t.dispatchEvent(new Event('x')); se === t",
1947            "true",
1948        ),
1949        // `Event.NONE`/`.CAPTURING_PHASE`/`.AT_TARGET`/`.BUBBLING_PHASE`
1950        // (`Node.ELEMENT_NODE`と同型の標準定数)が丸ごと未対応だった。
1951        (
1952            "Event.NONE + ',' + Event.CAPTURING_PHASE + ',' + Event.AT_TARGET + ',' + Event.BUBBLING_PHASE",
1953            "0,1,2,3",
1954        ),
1955        (
1956            "var t=new EventTarget(); var ph; \
1957             t.addEventListener('x', e => { ph = e.eventPhase === Event.AT_TARGET; }); \
1958             t.dispatchEvent(new Event('x')); ph",
1959            "true",
1960        ),
1961        // `form.requestSubmit()`/`form.submit()`(HTML5)が丸ごと未対応だった。
1962        // `requestSubmit()` は `submit` イベントを発火する。
1963        (
1964            "var f=document.createElement('form'); var n=0; \
1965             f.addEventListener('submit', ()=>n++); f.requestSubmit(); n",
1966            "1",
1967        ),
1968        // `preventDefault()` で中止可能。
1969        (
1970            "var f=document.createElement('form'); \
1971             f.addEventListener('submit', e=>e.preventDefault()); \
1972             typeof f.requestSubmit()",
1973            "undefined",
1974        ),
1975        // `submit()` は無害な no-op(実際のフォーム送信/ページ遷移パイプラインが
1976        // 無いため)で、少なくとも例外を投げない。
1977        ("var f=document.createElement('form'); f.submit(); true", "true"),
1978        // `input.showPicker()`(丸ごと未対応だった。ネイティブピッカー UI 自体が
1979        // 無いため `submit()` と同じ無害な no-op。2026-07-15 発見・実装)。
1980        (
1981            "var i=document.createElement('input'); i.type='date'; i.showPicker(); true",
1982            "true",
1983        ),
1984        // `input.stepUp(n?)`/`.stepDown(n?)`(HTML5)が丸ごと未対応だった。
1985        (
1986            "var i=document.createElement('input'); i.type='number'; i.value='5'; \
1987             i.stepUp(); i.value",
1988            "6",
1989        ),
1990        ("var i=document.createElement('input'); i.type='number'; i.value='5'; i.stepDown(3); i.value", "2"),
1991        // `step` 属性を刻み幅として使う。
1992        (
1993            "var i=document.createElement('input'); i.type='number'; i.value='0'; i.step='0.5'; \
1994             i.stepUp(); i.value",
1995            "0.5",
1996        ),
1997        // `max` があればクランプする。
1998        (
1999            "var i=document.createElement('input'); i.type='number'; i.value='9'; i.max='10'; \
2000             i.stepUp(5); i.value",
2001            "10",
2002        ),
2003        // String / Array 静的・console・globalThis
2004        ("String.fromCharCode(72,105)", "Hi"),
2005        ("String.fromCodePoint(65)", "A"),
2006        ("Array.of(1,2,3).join(',')", "1,2,3"),
2007        // Array イテレータ系 + reduceRight
2008        ("[...[10,20].keys()].join(',')", "0,1"),
2009        ("[...[10,20].values()].join(',')", "10,20"),
2010        // entries() は本物の Array Iterator(.map 等の Array メソッドは持たない。仕様どおり)
2011        // を返すため、配列メソッドを使うにはまず spread/Array.from で配列化する必要がある。
2012        ("[...[5,6].entries()].map(e => e[0]+':'+e[1]).join(',')", "0:5,1:6"),
2013        ("[1,2,3].reduceRight((a,b) => a+'-'+b)", "3-2-1"),
2014        ("[1,2,3,4].reduceRight((a,b) => a+b, 0)", "10"),
2015        // document.title 読み書き
2016        ("document.title = 'AtmOS'; document.title", "AtmOS"),
2017        ("document.readyState", "complete"),
2018        // `document.compatMode`/`.characterSet`/`.charset`/`.contentType`/
2019        // `.scrollingElement`(HTML5。丸ごと未対応だった)。
2020        ("document.compatMode", "CSS1Compat"),
2021        ("document.characterSet", "UTF-8"),
2022        ("document.charset", "UTF-8"),
2023        ("document.contentType", "text/html"),
2024        ("document.scrollingElement === document.documentElement", "true"),
2025        // `document.children`/`.firstElementChild`/`.lastElementChild`/
2026        // `.childElementCount`(`ParentNode`ミックスインの`Document`側配線
2027        // 漏れ。丸ごと未対応だった。2026-07-17 発見・実装。この配列は
2028        // bare `JsRuntime::new()` で `<html>` 自体が未構築のため、実際の
2029        // 検証は `dom_cases`(HTML フィクスチャ付き)側で行う)。
2030        ("document.firstElementChild === document.documentElement", "true"),
2031        ("document.lastElementChild === document.documentElement", "true"),
2032        ("document.children.length", "0"),
2033        ("document.childElementCount", "0"),
2034        // `document.referrer`/`.lastModified`/`.domain`(HTML5。丸ごと未対応
2035        // だった)。
2036        ("document.referrer", ""),
2037        (
2038            "/^\\d{2}\\/\\d{2}\\/\\d{4} \\d{2}:\\d{2}:\\d{2}$/.test(document.lastModified)",
2039            "true",
2040        ),
2041        ("typeof document.domain", "string"),
2042        // `navigator.geolocation`(丸ごと未対応だった。実ハードウェアが無いため
2043        // 常に `PERMISSION_DENIED` で `error` コールバックを同期的に呼ぶ簡略実装)。
2044        ("typeof navigator.geolocation.getCurrentPosition", "function"),
2045        (
2046            "var code; navigator.geolocation.getCurrentPosition(() => {}, e => { code = e.code; }); \
2047             code === 1",
2048            "true",
2049        ),
2050        (
2051            "typeof navigator.geolocation.watchPosition(() => {}, () => {})",
2052            "number",
2053        ),
2054        ("typeof navigator.geolocation.clearWatch(1)", "undefined"),
2055        // `navigator.wakeLock.request(type)`(Screen Wake Lock API。丸ごと
2056        // 未対応だった。実際の電源管理機構は無いため状態のみ追跡する簡略実装)。
2057        (
2058            "(await navigator.wakeLock.request('screen')).released",
2059            "false",
2060        ),
2061        ("(await navigator.wakeLock.request('screen')).type", "screen"),
2062        (
2063            "var s = await navigator.wakeLock.request('screen'); await s.release(); s.released",
2064            "true",
2065        ),
2066        // `navigator.share(data)`/`navigator.canShare(data)`(Web Share API。
2067        // 丸ごと未対応だった。実際の共有先 UI が無いため `canShare` は常に
2068        // `false`、`share` は仕様どおり `AbortError` で拒否する)。
2069        ("navigator.canShare({title:'x'})", "false"),
2070        (
2071            "try { await navigator.share({title:'x'}); 'resolved' } \
2072             catch(e) { e.name }",
2073            "AbortError",
2074        ),
2075        // `new EyeDropper().open()`(丸ごと未対応だった。実際のピッカー UI が
2076        // 無いため、ユーザーがキャンセルした場合と同じ `AbortError` で拒否)。
2077        (
2078            "try { await new EyeDropper().open(); 'resolved' } \
2079             catch(e) { e.name }",
2080            "AbortError",
2081        ),
2082        // `navigator.vibrate(pattern)`(Vibration API。丸ごと未対応だった。
2083        // 戻り値はリクエスト受理の可否のみを表しハードウェアの有無とは
2084        // 無関係なため、常に `true`)。
2085        ("navigator.vibrate(200)", "true"),
2086        ("navigator.vibrate([100,50,100])", "true"),
2087        // `navigator.getBattery()`(Battery Status API。丸ごと未対応だった。
2088        // バッテリー非搭載・常時 AC 電源動作というこの OS のターゲット
2089        // 実態に即した「常時満充電で給電中」を返す簡略実装)。
2090        ("(await navigator.getBattery()).charging", "true"),
2091        ("(await navigator.getBattery()).level", "1"),
2092        ("(await navigator.getBattery()).chargingTime", "0"),
2093        // `navigator.connection`(Network Information API。丸ごと未対応
2094        // だった。実測機構は無いため一般的なブロードバンド相当の代表値を返す
2095        // 簡略実装)。
2096        ("navigator.connection.saveData", "false"),
2097        ("navigator.connection.effectiveType", "4g"),
2098        ("typeof navigator.connection.downlink", "number"),
2099        // `navigator.locks.request(name, callback)`(Web Locks API。丸ごと
2100        // 未対応だった。複数タブ間の実際の排他機構が無いため、コールバックを
2101        // 即座に同期呼び出しし戻り値で解決する簡略実装。2026-07-15 発見・実装)。
2102        ("await navigator.locks.request('x', lock => lock.name)", "x"),
2103        ("await navigator.locks.request('x', lock => lock.mode)", "exclusive"),
2104        (
2105            "var ran=false; await navigator.locks.request('x', () => { ran=true; }); ran",
2106            "true",
2107        ),
2108        ("(await navigator.locks.query()).held.length", "0"),
2109        // Storage Access API(`document.hasStorageAccess()`/
2110        // `.requestStorageAccess()`。丸ごと未対応だった。サードパーティ
2111        // Cookie 分離機構自体が無いため常に成功する簡略実装)。
2112        ("await document.hasStorageAccess()", "true"),
2113        ("typeof await document.requestStorageAccess()", "undefined"),
2114        // `navigator.clipboard.write(items)`/`.read()`(`ClipboardItem` 配列版。
2115        // 丸ごと未対応だった。`text/plain` エントリのみ対応する簡略実装で、
2116        // `writeText`/`readText` と同じ内部状態を共有する)。
2117        (
2118            "await navigator.clipboard.write([new ClipboardItem({'text/plain': \
2119             new Blob(['hello-clip'], {type:'text/plain'})})]); \
2120             await navigator.clipboard.readText()",
2121            "hello-clip",
2122        ),
2123        (
2124            "await navigator.clipboard.writeText('via-text'); \
2125             (await navigator.clipboard.read())[0].types[0]",
2126            "text/plain",
2127        ),
2128        (
2129            "await navigator.clipboard.writeText('via-text2'); \
2130             var items = await navigator.clipboard.read(); \
2131             var blob = await items[0].getType('text/plain'); await blob.text()",
2132            "via-text2",
2133        ),
2134        // `navigator.permissions.query({name})`(丸ごと未対応だった。`geolocation`
2135        // は常に `PERMISSION_DENIED` を返す実装と、`notifications` は
2136        // `Notification.permission === 'granted'` と、それぞれ整合させる)。
2137        (
2138            "(await navigator.permissions.query({name:'geolocation'})).state",
2139            "denied",
2140        ),
2141        (
2142            "(await navigator.permissions.query({name:'notifications'})).state",
2143            "granted",
2144        ),
2145        (
2146            "(await navigator.permissions.query({name:'camera'})).state",
2147            "denied",
2148        ),
2149        // IntersectionObserver(コールバック即時発火・isIntersecting=true)
2150        ("var seen=false; var io=new IntersectionObserver(entries => { seen=entries[0].isIntersecting; }); io.observe(document.body); seen", "true"),
2151        // `IntersectionObserverEntry` の `boundingClientRect`/`intersectionRect`/
2152        // `rootBounds`/`time`(丸ごと未対応だった。2026-07-16 発見・実装)。
2153        (
2154            "var e=null; var io=new IntersectionObserver(entries => { e=entries[0]; }); \
2155             io.observe(document.body); \
2156             typeof e.boundingClientRect.width + ',' + \
2157             (e.intersectionRect === e.boundingClientRect ? 'same' : 'diff') + ',' + \
2158             (e.rootBounds === null) + ',' + (typeof e.time)",
2159            "number,same,true,number",
2160        ),
2161        // `IntersectionObserver`のコンストラクタ第2引数`options`
2162        // (`root`/`rootMargin`/`threshold`)が丸ごと無視されており、対応
2163        // する`.root`/`.rootMargin`/`.thresholds`読み取り専用プロパティも
2164        // 存在しなかった(丸ごと未対応だった。2026-07-17 発見・実装)。
2165        (
2166            "var io=new IntersectionObserver(()=>{}); \
2167             (io.root===null)+','+io.rootMargin+','+io.thresholds.join(',')",
2168            "true,0px 0px 0px 0px,0",
2169        ),
2170        (
2171            "var r=document.body; \
2172             var io=new IntersectionObserver(()=>{}, {root:r, rootMargin:'10px', threshold:0.5}); \
2173             (io.root===r)+','+io.rootMargin+','+io.thresholds.join(',')",
2174            "true,10px,0.5",
2175        ),
2176        (
2177            "var io=new IntersectionObserver(()=>{}, {threshold:[0,0.25,1]}); io.thresholds.join(',')",
2178            "0,0.25,1",
2179        ),
2180        // `ResizeObserverEntry.contentRect` が常に 0/0/0/0 固定だったバグと、
2181        // `borderBoxSize`/`contentBoxSize`(丸ごと未対応だった)を修正・追加。
2182        // 2026-07-16 発見・実装。
2183        (
2184            "var e=null; var ro=new ResizeObserver(entries => { e=entries[0]; }); \
2185             ro.observe(document.createElement('div')); \
2186             typeof e.contentRect.width + ',' + \
2187             Array.isArray(e.borderBoxSize) + ',' + \
2188             typeof e.borderBoxSize[0].inlineSize + ',' + \
2189             Array.isArray(e.contentBoxSize) + ',' + \
2190             typeof e.contentBoxSize[0].blockSize",
2191            "number,true,number,true,number",
2192        ),
2193        // `ResizeObserverEntry.devicePixelContentBoxSize`が丸ごと未対応
2194        // だった(`borderBoxSize`/`contentBoxSize`のみ対応済みで漏れて
2195        // いた。2026-07-17 発見・実装。`devicePixelRatio`が常に`1.0`固定
2196        // のため数値上は`contentBoxSize`と同一になる簡略実装)。
2197        (
2198            "var e=null; var ro=new ResizeObserver(entries => { e=entries[0]; }); \
2199             ro.observe(document.createElement('div')); \
2200             Array.isArray(e.devicePixelContentBoxSize) + ',' + \
2201             typeof e.devicePixelContentBoxSize[0].inlineSize",
2202            "true,number",
2203        ),
2204        // window.matchMedia(max-width 判定)
2205        ("window.matchMedia('(max-width: 9999px)').matches", "true"),
2206        ("window.matchMedia('(max-width: 0px)').matches", "false"),
2207        ("window.matchMedia('(min-width: 1px)').matches", "true"),
2208        // `window.matchMedia()` が `max-width`/`min-width` しか判定できず、CSS 側の
2209        // `@media` 評価(`prefers-color-scheme` 等)と食い違う非対称なバグだった。
2210        // 同じ判定ロジックへ統一した後の確認(既定は light テーマ)。
2211        ("window.matchMedia('(prefers-color-scheme: light)').matches", "true"),
2212        ("window.matchMedia('(prefers-color-scheme: dark)').matches", "false"),
2213        // window.scrollY / scrollTo
2214        ("typeof scrollY", "number"),
2215        ("scrollTo({top:100}); scrollY", "0"),
2216        // window.addEventListener load → 即時コールバック
2217        ("var loaded=false; window.addEventListener('load', () => { loaded=true; }); loaded", "true"),
2218        // `document.addEventListener('DOMContentLoaded', fn)` が丸ごと no-op だった
2219        // バグ(`window` 版は既に即時発火していたが `document` 側の配線が漏れていた)。
2220        // `window` 版よりもむしろよく使われる定番パターン。
2221        (
2222            "var ready=false; document.addEventListener('DOMContentLoaded', () => { ready=true; }); ready",
2223            "true",
2224        ),
2225        // `input.checked`(チェックボックス/ラジオボタンの選択状態)が JS プロパティとして
2226        // 丸ごと未実装で、`checkbox.checked`/`checkbox.checked = true` が常に
2227        // `undefined`/黙殺になっていた。
2228        (
2229            "var c=document.createElement('input'); c.type='checkbox'; c.checked",
2230            "false",
2231        ),
2232        (
2233            "var c=document.createElement('input'); c.type='checkbox'; c.checked=true; c.checked",
2234            "true",
2235        ),
2236        (
2237            "var c=document.createElement('input'); c.type='checkbox'; c.checked=true; c.checked=false; c.checked",
2238            "false",
2239        ),
2240        // `input.files`(`<input type="file">`のFileList。丸ごと未対応
2241        // だった。2026-07-17 発見・実装。実際のファイルピッカーUIが無い
2242        // ため常に空配列を返す誠実な簡略実装)。
2243        (
2244            "var f=document.createElement('input'); f.type='file'; f.files.length",
2245            "0",
2246        ),
2247        (
2248            "var f=document.createElement('input'); f.type='text'; f.files",
2249            "undefined",
2250        ),
2251        // `input[type=file].value`はセキュリティ上重要な仕様(`value`content
2252        // 属性は常に無視/未反映で、選択済みファイルが無ければ常に空文字列を
2253        // 返すべき)だが、以前は汎用の属性直結フォールバックに落ちており、
2254        // `<input type="file" value="...">`の属性値がそのまま漏れる
2255        // 静かなバグだった。
2256        (
2257            "var f=document.createElement('input'); f.type='file'; \
2258             f.setAttribute('value','c:\\\\secret.txt'); f.value",
2259            "",
2260        ),
2261        (
2262            "var f=document.createElement('input'); f.type='file'; f.value='x'; f.value",
2263            "",
2264        ),
2265        // `a`/`area`要素のURL分解プロパティ(`URLUtils`ミックスイン。
2266        // `.protocol`/`.host`/`.hostname`/`.port`/`.pathname`/`.search`/
2267        // `.hash`/`.origin`)が丸ごと未対応だった(`location`/`URL`は
2268        // 既に対応済みだったが`<a>`側への配線漏れ。2026-07-17 発見・実装)。
2269        (
2270            "var a=document.createElement('a'); a.href='https://sub.example.org:8080/x/y?z=1#top'; \
2271             [a.protocol,a.host,a.hostname,a.port,a.pathname,a.search,a.hash,a.origin].join('|')",
2272            "https:|sub.example.org:8080|sub.example.org|8080|/x/y|?z=1|#top|https://sub.example.org:8080",
2273        ),
2274        (
2275            "var a=document.createElement('area'); a.href='http://x.com/'; a.protocol",
2276            "http:",
2277        ),
2278        // `checkbox.indeterminate`(不確定表示状態。HTML 属性としては反映されない
2279        // JS 専用プロパティ)が丸ごと未対応だった。
2280        (
2281            "var c=document.createElement('input'); c.type='checkbox'; c.indeterminate",
2282            "false",
2283        ),
2284        (
2285            "var c=document.createElement('input'); c.type='checkbox'; c.indeterminate=true; c.indeterminate",
2286            "true",
2287        ),
2288        (
2289            "var c=document.createElement('input'); c.type='checkbox'; c.indeterminate=true; c.hasAttribute('indeterminate')",
2290            "false",
2291        ),
2292        // `input`/`textarea.selectionStart`/`.selectionEnd`/
2293        // `.selectionDirection`/`textarea.textLength`(丸ごと未対応
2294        // だった。2026-07-17 発見・実装。実際のキャレット機構が無いため
2295        // 状態追跡のみの簡略実装。未設定時は仕様どおり`0`/`"none"`)。
2296        (
2297            "var i=document.createElement('input'); i.selectionStart + ',' + i.selectionEnd + ',' + i.selectionDirection",
2298            "0,0,none",
2299        ),
2300        (
2301            "var i=document.createElement('input'); i.selectionStart=2; i.selectionEnd=5; i.selectionDirection='forward'; \
2302             i.selectionStart + ',' + i.selectionEnd + ',' + i.selectionDirection",
2303            "2,5,forward",
2304        ),
2305        (
2306            "var t=document.createElement('textarea'); t.value='hello'; t.textLength",
2307            "5",
2308        ),
2309        // `setSelectionRange`/`setRangeText`(丸ごと未対応だった。
2310        // `selectionStart`等と対になる操作メソッド。2026-07-17 発見・
2311        // 実装)。
2312        (
2313            "var i=document.createElement('input'); i.setSelectionRange(1,3,'forward'); \
2314             i.selectionStart + ',' + i.selectionEnd + ',' + i.selectionDirection",
2315            "1,3,forward",
2316        ),
2317        (
2318            "var i=document.createElement('input'); i.value='hello world'; \
2319             i.setRangeText('there', 6, 11); i.value",
2320            "hello there",
2321        ),
2322        (
2323            "var i=document.createElement('input'); i.value='hello world'; \
2324             i.setRangeText('there', 6, 11, 'select'); \
2325             i.value + '|' + i.selectionStart + ',' + i.selectionEnd",
2326            "hello there|6,11",
2327        ),
2328        (
2329            "var i=document.createElement('input'); i.value='hello world'; \
2330             i.selectionStart=0; i.selectionEnd=5; i.setRangeText('hi'); i.value",
2331            "hi world",
2332        ),
2333        // `checkbox.click()` は仕様上、リスナ実行前に自身の `checked` を切り替える。
2334        (
2335            "var c=document.createElement('input'); c.type='checkbox'; c.click(); c.checked",
2336            "true",
2337        ),
2338        (
2339            "var c=document.createElement('input'); c.type='checkbox'; c.click(); c.click(); c.checked",
2340            "false",
2341        ),
2342        (
2343            "var r=document.createElement('input'); r.type='radio'; r.click(); r.checked",
2344            "true",
2345        ),
2346        // `node.ownerDocument`(`element.ownerDocument.createElement(...)` という
2347        // 定番イディオムで使われる)が丸ごと未対応だった。
2348        (
2349            "document.createElement('div').ownerDocument === document",
2350            "true",
2351        ),
2352        (
2353            "typeof document.createElement('div').ownerDocument.createElement",
2354            "function",
2355        ),
2356        // **重要**: ラジオボタンの最も基本的な仕様上の挙動(同じ `name` を持つグループ内で
2357        // 1つだけが `checked` になる排他選択)が以前は非対応の簡略化として明示的に
2358        // 見送られていた。`element.click()` と `.checked = true` 代入の両方で効くこと。
2359        (
2360            "var r1=document.createElement('input'); r1.type='radio'; r1.name='g'; \
2361             var r2=document.createElement('input'); r2.type='radio'; r2.name='g'; \
2362             r1.click(); r2.click(); r1.checked + ',' + r2.checked",
2363            "false,true",
2364        ),
2365        (
2366            "var r1=document.createElement('input'); r1.type='radio'; r1.name='g2'; \
2367             var r2=document.createElement('input'); r2.type='radio'; r2.name='g2'; \
2368             r1.checked=true; r2.checked=true; r1.checked + ',' + r2.checked",
2369            "false,true",
2370        ),
2371        // 異なる `name` を持つ radio 同士は互いに影響しない。
2372        (
2373            "var r1=document.createElement('input'); r1.type='radio'; r1.name='ga'; \
2374             var r2=document.createElement('input'); r2.type='radio'; r2.name='gb'; \
2375             r1.click(); r2.click(); r1.checked + ',' + r2.checked",
2376            "true,true",
2377        ),
2378        // `input.disabled`/`option.selected`/`input.placeholder`/`input.name` も
2379        // `checked`/`type` と同じ理由で JS プロパティとして丸ごと未実装だった。
2380        (
2381            "var b=document.createElement('button'); b.disabled=true; b.disabled",
2382            "true",
2383        ),
2384        (
2385            "var b=document.createElement('button'); b.disabled=true; b.disabled=false; b.disabled",
2386            "false",
2387        ),
2388        (
2389            "var o=document.createElement('option'); o.selected=true; o.selected",
2390            "true",
2391        ),
2392        // `option.text`(`textContent` の別名。`new Option(...)`/`option.text = '...'`
2393        // という定番パターン)が丸ごと未対応だった。
2394        (
2395            "var o=document.createElement('option'); o.text='Label'; o.text",
2396            "Label",
2397        ),
2398        (
2399            "var o=document.createElement('option'); o.text='Label'; o.textContent",
2400            "Label",
2401        ),
2402        // `option`/`optgroup.label`(丸ごと未対応だった。`option.label`は
2403        // 仕様上`label`属性が無ければテキスト内容にフォールバックする。
2404        // 2026-07-17 発見・実装)。
2405        (
2406            "var o=document.createElement('option'); o.textContent='hi'; o.label",
2407            "hi",
2408        ),
2409        (
2410            "var o=document.createElement('option'); o.textContent='hi'; o.label='explicit'; o.label",
2411            "explicit",
2412        ),
2413        (
2414            "var g=document.createElement('optgroup'); g.textContent='hi'; g.label",
2415            "",
2416        ),
2417        (
2418            "var g=document.createElement('optgroup'); g.label='Group A'; g.label",
2419            "Group A",
2420        ),
2421        // `input.required`/`input.readOnly`/`input.multiple`/`img.src`/`img.alt`/
2422        // `a.href` も同じ理由で JS プロパティとして丸ごと未実装だった。
2423        (
2424            "var i=document.createElement('input'); i.required=true; i.required",
2425            "true",
2426        ),
2427        (
2428            "var i=document.createElement('input'); i.readOnly=true; i.readOnly",
2429            "true",
2430        ),
2431        (
2432            "var s=document.createElement('select'); s.multiple=true; s.multiple",
2433            "true",
2434        ),
2435        (
2436            "var img=document.createElement('img'); img.src='a.png'; img.alt='pic'; img.src+','+img.alt",
2437            "a.png,pic",
2438        ),
2439        (
2440            "var a=document.createElement('a'); a.href='/x'; a.href",
2441            "/x",
2442        ),
2443        // `label.htmlFor`/`form.action`/`form.method`/`input.min`/`max`/`step`/
2444        // `maxLength`/`a.target`/`rel`/`download` も同種の未実装だった。
2445        (
2446            "var l=document.createElement('label'); l.htmlFor='name'; l.htmlFor",
2447            "name",
2448        ),
2449        (
2450            "var f=document.createElement('form'); f.action='/submit'; f.method='post'; f.action+','+f.method",
2451            "/submit,post",
2452        ),
2453        // `form.method`(`method`属性が無いか既知の列挙値以外なら既定
2454        // `"get"`にフォールバックする。`fieldset.type`等と同じバグ
2455        // パターンで丸ごと未対応だった。2026-07-17 発見・実装)。
2456        ("document.createElement('form').method", "get"),
2457        (
2458            "var f=document.createElement('form'); f.setAttribute('method','DIALOG'); f.method",
2459            "dialog",
2460        ),
2461        (
2462            "var f=document.createElement('form'); f.setAttribute('method','bogus'); f.method",
2463            "get",
2464        ),
2465        // `form.enctype`/`.encoding`(丸ごと未対応だった。`encoding`は
2466        // `enctype`のレガシー別名で同じ属性を指す。既知の列挙値3種以外
2467        // なら既定`"application/x-www-form-urlencoded"`にフォールバック
2468        // する。2026-07-17 発見・実装)。
2469        (
2470            "document.createElement('form').enctype",
2471            "application/x-www-form-urlencoded",
2472        ),
2473        (
2474            "var f=document.createElement('form'); f.enctype='multipart/form-data'; f.enctype+','+f.encoding",
2475            "multipart/form-data,multipart/form-data",
2476        ),
2477        (
2478            "var f=document.createElement('form'); f.encoding='text/plain'; f.enctype",
2479            "text/plain",
2480        ),
2481        // `form.acceptCharset`(`accept-charset`属性のcamelCase IDL
2482        // プロパティ版。`htmlFor`と同じパターンだが丸ごと未対応だった。
2483        // 2026-07-17 発見・実装)。
2484        (
2485            "var f=document.createElement('form'); f.acceptCharset='UTF-8'; f.getAttribute('accept-charset')",
2486            "UTF-8",
2487        ),
2488        (
2489            "var f=document.createElement('form'); f.setAttribute('accept-charset','UTF-8'); f.acceptCharset",
2490            "UTF-8",
2491        ),
2492        // `button`/`input`の「送信オーバーライド」IDLプロパティ(`formAction`/
2493        // `formEnctype`/`formMethod`/`formNoValidate`/`formTarget`)が丸ごと
2494        // 未対応だった。`form.action`/`.method`/`.enctype`と同じ既定値
2495        // フォールバックパターンを踏襲する。2026-07-17 発見・実装)。
2496        (
2497            "var b=document.createElement('button'); b.formAction='/x'; b.getAttribute('formaction')",
2498            "/x",
2499        ),
2500        (
2501            "document.createElement('input').formEnctype",
2502            "application/x-www-form-urlencoded",
2503        ),
2504        (
2505            "var b=document.createElement('button'); b.formEnctype='multipart/form-data'; b.formEnctype",
2506            "multipart/form-data",
2507        ),
2508        ("document.createElement('button').formMethod", "get"),
2509        (
2510            "var i=document.createElement('input'); i.formMethod='post'; i.formMethod",
2511            "post",
2512        ),
2513        (
2514            "document.createElement('button').formNoValidate",
2515            "false",
2516        ),
2517        (
2518            "var b=document.createElement('button'); b.formNoValidate=true; b.getAttribute('formnovalidate')",
2519            "formnovalidate",
2520        ),
2521        (
2522            "var i=document.createElement('input'); i.formTarget='_blank'; i.formTarget",
2523            "_blank",
2524        ),
2525        (
2526            "var i=document.createElement('input'); i.defaultValue='hello'; i.defaultValue+','+i.getAttribute('value')",
2527            "hello,hello",
2528        ),
2529        (
2530            "var i=document.createElement('input'); i.defaultChecked=true; i.defaultChecked+','+i.hasAttribute('checked')",
2531            "true,true",
2532        ),
2533        (
2534            "var o=document.createElement('option'); o.defaultSelected=true; o.defaultSelected+','+o.hasAttribute('selected')",
2535            "true,true",
2536        ),
2537        // `img.loading`/`.decoding`(既知の列挙値以外なら仕様上の既定値
2538        // `"auto"`にフォールバックする必要があるが、`fieldset.type`等と
2539        // 同じバグパターンで空文字列になっていた。2026-07-17 発見・実装)。
2540        ("document.createElement('img').loading", "auto"),
2541        (
2542            "var i=document.createElement('img'); i.loading='lazy'; i.loading",
2543            "lazy",
2544        ),
2545        (
2546            "var i=document.createElement('img'); i.setAttribute('loading','bogus'); i.loading",
2547            "auto",
2548        ),
2549        ("document.createElement('img').decoding", "auto"),
2550        (
2551            "var i=document.createElement('img'); i.decoding='sync'; i.decoding",
2552            "sync",
2553        ),
2554        (
2555            "var i=document.createElement('img'); i.setAttribute('decoding','bogus'); i.decoding",
2556            "auto",
2557        ),
2558        // `audio`/`video.preload`(欠落時既定値`"metadata"`、不正値既定値
2559        // `"auto"`。`fieldset.type`等と同じバグパターンで空文字列になって
2560        // いた。2026-07-17 発見・実装)。
2561        ("document.createElement('audio').preload", "metadata"),
2562        (
2563            "var v=document.createElement('video'); v.preload='none'; v.preload",
2564            "none",
2565        ),
2566        (
2567            "var v=document.createElement('video'); v.setAttribute('preload','bogus'); v.preload",
2568            "auto",
2569        ),
2570        // `textarea.wrap`(`"soft"`/`"hard"`。既知値以外・欠落時は仕様上の
2571        // 既定値`"soft"`にフォールバックする必要があるが、丸ごと未対応
2572        // だった。2026-07-17 発見・実装)。
2573        ("document.createElement('textarea').wrap", "soft"),
2574        (
2575            "var t=document.createElement('textarea'); t.wrap='hard'; t.wrap",
2576            "hard",
2577        ),
2578        (
2579            "var t=document.createElement('textarea'); t.setAttribute('wrap','bogus'); t.wrap",
2580            "soft",
2581        ),
2582        // `track.kind`(欠落時既定`"subtitles"`/不正値既定`"metadata"`の
2583        // 2段階フォールバック。プロパティ自体が丸ごと未対応だった。
2584        // 2026-07-17 発見・実装)。
2585        ("document.createElement('track').kind", "subtitles"),
2586        (
2587            "var t=document.createElement('track'); t.kind='captions'; t.kind",
2588            "captions",
2589        ),
2590        (
2591            "var t=document.createElement('track'); t.setAttribute('kind','bogus'); t.kind",
2592            "metadata",
2593        ),
2594        // `track.label`/`.srclang`(単純な文字列反映)と`track.default`
2595        // (ブール型)が丸ごと未対応だった。あわせて`option`/`optgroup`/
2596        // `track`共通の`label`セッターが丸ごと存在しなかった重複バグも
2597        // 解消。2026-07-18 発見・実装)。
2598        (
2599            "var t=document.createElement('track'); t.label='English'; t.getAttribute('label')",
2600            "English",
2601        ),
2602        (
2603            "var t=document.createElement('track'); t.srclang='en'; t.srclang",
2604            "en",
2605        ),
2606        ("document.createElement('track').default", "false"),
2607        (
2608            "var t=document.createElement('track'); t.default=true; t.getAttribute('default')",
2609            "default",
2610        ),
2611        // `link.as`(`rel="preload"`/`"prefetch"`の取得先種別ヒント。単純な
2612        // 文字列反映だが丸ごと未対応だった。2026-07-18 発見・実装)。
2613        (
2614            "var l=document.createElement('link'); l.as='script'; l.getAttribute('as')",
2615            "script",
2616        ),
2617        (
2618            "var l=document.createElement('link'); l.setAttribute('as','style'); l.as",
2619            "style",
2620        ),
2621        // `video.videoWidth`/`.videoHeight`(実デコードパイプラインが
2622        // 無いため常に`0`)と`audio`/`video.seeking`(実際の非同期シーク
2623        // 処理が無いため常に`false`)が丸ごと未対応だった。2026-07-18
2624        // 発見・実装)。
2625        (
2626            "document.createElement('video').videoWidth+','+document.createElement('video').videoHeight",
2627            "0,0",
2628        ),
2629        ("document.createElement('video').seeking", "false"),
2630        ("document.createElement('audio').seeking", "false"),
2631        // `select.size`(欠落・不正値時の既定値`0`)と`input.size`(欠落・
2632        // 不正値時の既定値`20`)が丸ごと未対応だった。2026-07-18 発見・
2633        // 実装)。
2634        ("document.createElement('select').size", "0"),
2635        (
2636            "var s=document.createElement('select'); s.size=5; s.size",
2637            "5",
2638        ),
2639        ("document.createElement('input').size", "20"),
2640        (
2641            "var i=document.createElement('input'); i.size=10; i.size",
2642            "10",
2643        ),
2644        (
2645            "var i=document.createElement('input'); i.setAttribute('size','0'); i.size",
2646            "20",
2647        ),
2648        // `textarea.rows`/`.cols`(欠落・不正値時の既定値`2`/`20`)が
2649        // 丸ごと未対応だった。2026-07-18 発見・実装)。
2650        ("document.createElement('textarea').rows", "2"),
2651        (
2652            "var t=document.createElement('textarea'); t.rows=8; t.rows",
2653            "8",
2654        ),
2655        ("document.createElement('textarea').cols", "20"),
2656        (
2657            "var t=document.createElement('textarea'); t.cols=40; t.cols",
2658            "40",
2659        ),
2660        (
2661            "var t=document.createElement('textarea'); t.setAttribute('rows','0'); t.rows",
2662            "2",
2663        ),
2664        // `dialog.requestClose(returnValue?)`(`.close()`と違い、閉じる
2665        // 直前に取消可能な`cancel`イベントを発火する。丸ごと未対応
2666        // だった。2026-07-18 発見・実装)。
2667        (
2668            "var d=document.createElement('dialog'); d.open=true; d.requestClose('x'); \
2669             d.open+','+d.returnValue",
2670            "false,x",
2671        ),
2672        (
2673            "var d=document.createElement('dialog'); d.open=true; \
2674             d.addEventListener('cancel', e=>e.preventDefault()); d.requestClose(); d.open",
2675            "true",
2676        ),
2677        (
2678            "var d=document.createElement('dialog'); d.open=true; var fired=false; \
2679             d.addEventListener('close', ()=>fired=true); d.requestClose(); fired",
2680            "true",
2681        ),
2682        // `progress.position`(廃止予定だが仕様に残る読み取り専用IDL。
2683        // `value`未設定なら不確定状態`-1`、それ以外は`value/max`。丸ごと
2684        // 未対応だった。2026-07-18 発見・実装)。
2685        ("document.createElement('progress').position", "-1"),
2686        (
2687            "var p=document.createElement('progress'); p.max=10; p.value=5; p.position",
2688            "0.5",
2689        ),
2690        (
2691            "var p=document.createElement('progress'); p.value=1; p.position",
2692            "1",
2693        ),
2694        // `output.htmlFor`(`for`属性を空白区切りトークンとして扱う
2695        // `DOMTokenList`。`label.htmlFor`(単一文字列)とは異なる意味。
2696        // `relList`/`sandbox`と同じ読み取り専用トークン配列の簡略実装。
2697        // 丸ごと未対応だった。2026-07-18 発見・実装)。
2698        (
2699            "var o=document.createElement('output'); o.htmlFor='a b c'; \
2700             o.htmlFor.length+','+o.htmlFor[1]",
2701            "3,b",
2702        ),
2703        (
2704            "var o=document.createElement('output'); o.setAttribute('for','x y'); \
2705             Array.from(o.htmlFor).join(',')",
2706            "x,y",
2707        ),
2708        ("document.createElement('output').htmlFor.length", "0"),
2709        // `input[type=checkbox/radio].value`(`value`属性が無い場合の仕様上
2710        // の既定値は`""`ではなく`"on"`。フォーム送信ロジック
2711        // 〔`collect_form_data`〕は既に正しくこの既定値を使っていたが、
2712        // JSプロパティの`.value`読み出し側だけ空文字列になる不整合が
2713        // あった。2026-07-18 発見・実装)。
2714        (
2715            "var c=document.createElement('input'); c.type='checkbox'; c.value",
2716            "on",
2717        ),
2718        (
2719            "var r=document.createElement('input'); r.type='radio'; r.value",
2720            "on",
2721        ),
2722        (
2723            "var c=document.createElement('input'); c.type='checkbox'; c.value='yes'; c.value",
2724            "yes",
2725        ),
2726        (
2727            "document.createElement('input').value",
2728            "",
2729        ),
2730        // `input[type=color].value`(`value`属性が無いか7文字の`#rrggbb`
2731        // 小文字16進形式でない不正値の場合の既定値は`"#000000"`。
2732        // `checkbox`/`radio`の`"on"`と同じバグパターンで空文字列に
2733        // なっていた。2026-07-18 発見・実装)。
2734        (
2735            "var c=document.createElement('input'); c.type='color'; c.value",
2736            "#000000",
2737        ),
2738        (
2739            "var c=document.createElement('input'); c.type='color'; c.value='#ff00aa'; c.value",
2740            "#ff00aa",
2741        ),
2742        // `input[type=color].value = v`の書き込み側sanitizationが丸ごと
2743        // 未対応だった。ゲッター側は「既に小文字16進形式か」しか見ておらず、
2744        // セッター側で大文字→小文字への正規化が行われていなかったため、
2745        // 仕様上は妥当な色(大文字16進)である`'#FF00AA'`を代入しても
2746        // 小文字化されずゲッターの検証に弾かれ`#000000`になってしまう
2747        // 食い違いがあった(2026-07-18 発見・修正)。
2748        (
2749            "var c=document.createElement('input'); c.type='color'; c.value='#FF00AA'; c.value",
2750            "#ff00aa",
2751        ),
2752        (
2753            // 16進形式として無効な値(色名等)は仕様どおり`#000000`へ正規化される。
2754            "var c=document.createElement('input'); c.type='color'; c.value='red'; c.value",
2755            "#000000",
2756        ),
2757        // `input[type=range].value`(`value`属性が無いか不正値の場合の
2758        // 既定値は`min`と`max`の中点。`min`/`max`省略時は既定`0`/`100`
2759        // のため無指定時は`50`。`checkbox`/`color`と同じバグパターンで
2760        // 空文字列になっていた。2026-07-18 発見・実装)。
2761        (
2762            "var r=document.createElement('input'); r.type='range'; r.value",
2763            "50",
2764        ),
2765        (
2766            "var r=document.createElement('input'); r.type='range'; r.min='0'; r.max='10'; r.value",
2767            "5",
2768        ),
2769        (
2770            "var r=document.createElement('input'); r.type='range'; r.value='7'; r.value",
2771            "7",
2772        ),
2773        (
2774            "var r=document.createElement('input'); r.type='range'; r.min='5'; r.max='2'; r.value",
2775            "5",
2776        ),
2777        // `input[type=range].value`の仕様上のvalue sanitization algorithmは
2778        // 数値変換できない不正値の既定値フォールバックだけでなく`min`/`max`
2779        // へのクランプも含むが、パース自体には成功する範囲外の値
2780        // (`max=10`で`value=999`等)はクランプされずそのまま漏れていた。
2781        (
2782            "var r=document.createElement('input'); r.type='range'; r.min='0'; r.max='10'; r.value='999'; r.value",
2783            "10",
2784        ),
2785        (
2786            "var r=document.createElement('input'); r.type='range'; r.min='0'; r.max='10'; r.value='-5'; r.value",
2787            "0",
2788        ),
2789        (
2790            // `setAttribute`経由(IDLセッターを介さない直接の属性書き込み)でも
2791            // 同じくクランプされる。
2792            "var r=document.createElement('input'); r.type='range'; r.min='0'; r.max='10'; \
2793             r.setAttribute('value','999'); r.value",
2794            "10",
2795        ),
2796        // `table.tHead`/`.tFoot`/`.caption`のセッター(読み出し側は既に
2797        // 実装済みだったが、対になる書き込み側が丸ごと未対応だった。
2798        // 代入すると既存の同名セクションを除去し、新しい要素を正しい
2799        // 位置へ挿入する。2026-07-18 発見・実装)。
2800        (
2801            "var t=document.createElement('table'); var h=document.createElement('thead'); \
2802             t.tHead=h; t.tHead===h",
2803            "true",
2804        ),
2805        (
2806            "var t=document.createElement('table'); var h1=document.createElement('thead'); \
2807             var h2=document.createElement('thead'); t.tHead=h1; t.tHead=h2; \
2808             (t.tHead===h2)+','+t.children.length",
2809            "true,1",
2810        ),
2811        (
2812            "var t=document.createElement('table'); var h=document.createElement('thead'); \
2813             t.tHead=h; t.tHead=null; t.tHead",
2814            "null",
2815        ),
2816        (
2817            "var t=document.createElement('table'); var c=document.createElement('caption'); \
2818             var b=document.createElement('tbody'); t.appendChild(b); t.caption=c; \
2819             t.firstChild===c",
2820            "true",
2821        ),
2822        // `select.add(element, before)`(`select.options.add`と同じ意味の
2823        // 便利メソッド。丸ごと未対応だった。2026-07-18 発見・実装)。
2824        (
2825            "var s=document.createElement('select'); var o=document.createElement('option'); \
2826             s.add(o); s.options.length+','+(s.options[0]===o)",
2827            "1,true",
2828        ),
2829        (
2830            "var s=document.createElement('select'); var o1=document.createElement('option'); \
2831             var o2=document.createElement('option'); s.add(o1); s.add(o2, o1); \
2832             s.options[0]===o2",
2833            "true",
2834        ),
2835        // `audio`/`video.played`(`TimeRanges`。`buffered`/`.seekable`と
2836        // 同じ理由で常に空。丸ごと未対応だった。2026-07-18 発見・実装)。
2837        ("document.createElement('video').played.length", "0"),
2838        ("document.createElement('audio').played.length", "0"),
2839        // `element.role`/`.ariaLabel`等(`ARIAMixin`。`role`/`aria-*`属性の
2840        // IDLプロパティ版)が丸ごと未対応だった。`aria`接頭辞後の
2841        // PascalCase部分を全て小文字化して`aria-`と連結する命名規則を
2842        // 汎用アームで一括対応。2026-07-18 発見・実装)。
2843        (
2844            "var d=document.createElement('div'); d.role='button'; d.getAttribute('role')",
2845            "button",
2846        ),
2847        (
2848            "var d=document.createElement('div'); d.setAttribute('role','tab'); d.role",
2849            "tab",
2850        ),
2851        (
2852            "var d=document.createElement('div'); d.ariaLabel='close'; d.getAttribute('aria-label')",
2853            "close",
2854        ),
2855        (
2856            "var d=document.createElement('div'); d.setAttribute('aria-hidden','true'); d.ariaHidden",
2857            "true",
2858        ),
2859        (
2860            "var d=document.createElement('div'); d.ariaValueNow='5'; d.getAttribute('aria-valuenow')",
2861            "5",
2862        ),
2863        (
2864            "var d=document.createElement('div'); d.ariaLabel='x'; d.ariaLabel=null; \
2865             d.getAttribute('aria-label')",
2866            "null",
2867        ),
2868        ("document.createElement('div').ariaLabel", "null"),
2869        // `element.outerText`(読み出しは`innerText`と同じだが、書き込みは
2870        // 自身の中身ではなく自身「そのもの」をテキストノードへ置き換える
2871        // 点が違う。丸ごと未対応だった。2026-07-18 発見・実装)。
2872        (
2873            "var d=document.createElement('div'); d.textContent='hi'; d.outerText",
2874            "hi",
2875        ),
2876        (
2877            "var p=document.createElement('div'); var c=document.createElement('span'); \
2878             c.textContent='x'; p.appendChild(c); c.outerText='y'; p.textContent",
2879            "y",
2880        ),
2881        (
2882            "var p=document.createElement('div'); var c=document.createElement('span'); \
2883             p.appendChild(c); c.outerText='z'; p.children.length",
2884            "0",
2885        ),
2886        // `element.part`(CSS Shadow Parts。`relList`/`sandbox`と同じ
2887        // 読み取り専用トークン配列の簡略実装。丸ごと未対応だった。
2888        // 2026-07-18 発見・実装)。
2889        (
2890            "var d=document.createElement('div'); d.setAttribute('part','a b'); \
2891             d.part.length+','+d.part[1]",
2892            "2,b",
2893        ),
2894        ("document.createElement('div').part.length", "0"),
2895        // `link.sizes`は`img`/`source.sizes`(単純なDOMString)とは異なり
2896        // 仕様上`DOMTokenList`。`part`/`relList`と同じ読み取り専用トークン
2897        // 配列の簡略実装で区別する。以前は同名の文字列反映アームに
2898        // 一括で落ちて型が誤っていた。2026-07-18 発見・実装)。
2899        (
2900            "var l=document.createElement('link'); l.setAttribute('sizes','16x16 32x32'); \
2901             l.sizes.length+','+l.sizes[1]",
2902            "2,32x32",
2903        ),
2904        (
2905            "var i=document.createElement('img'); i.sizes='(min-width: 600px) 50vw, 100vw'; i.sizes",
2906            "(min-width: 600px) 50vw, 100vw",
2907        ),
2908        // `input.accept`(`type="file"`が許可するファイル種別フィルタ。
2909        // 単純な文字列反映だが丸ごと未対応だった。2026-07-18 発見・実装)。
2910        (
2911            "var i=document.createElement('input'); i.accept='image/*'; i.getAttribute('accept')",
2912            "image/*",
2913        ),
2914        (
2915            "var i=document.createElement('input'); i.setAttribute('accept','.pdf,.doc'); i.accept",
2916            ".pdf,.doc",
2917        ),
2918        // `script.charset`(廃止予定だが仕様に残る単純な文字列反映。
2919        // ゲッターが`meta`タグ限定のままで他タグでは未対応だった。
2920        // 2026-07-18 発見・実装)。
2921        (
2922            "var s=document.createElement('script'); s.charset='utf-8'; s.getAttribute('charset')",
2923            "utf-8",
2924        ),
2925        (
2926            "var s=document.createElement('script'); s.setAttribute('charset','shift-jis'); s.charset",
2927            "shift-jis",
2928        ),
2929        // `a`/`area.protocol`/`.host`/`.hostname`/`.port`/`.pathname`/
2930        // `.search`/`.hash`のセッター(`URLUtils`ミックスイン。ゲッターは
2931        // 既に実装済みだったが対になるセッターが丸ごと未対応だった。
2932        // 2026-07-18 発見・実装)。
2933        (
2934            "var a=document.createElement('a'); a.href='https://x.com/p'; a.hostname='y.com'; a.href",
2935            "https://y.com/p",
2936        ),
2937        (
2938            "var a=document.createElement('a'); a.href='https://x.com/p'; a.protocol='http'; a.protocol",
2939            "http:",
2940        ),
2941        (
2942            "var a=document.createElement('a'); a.href='https://x.com/p'; a.pathname='q'; a.pathname",
2943            "/q",
2944        ),
2945        (
2946            "var a=document.createElement('a'); a.href='https://x.com/p'; a.search='x=1'; a.search",
2947            "?x=1",
2948        ),
2949        (
2950            "var a=document.createElement('a'); a.href='https://x.com/p'; a.hash='frag'; a.hash",
2951            "#frag",
2952        ),
2953        (
2954            "var a=document.createElement('a'); a.href='https://x.com:80/p'; a.host='y.com:90'; \
2955             a.hostname+','+a.port",
2956            "y.com,90",
2957        ),
2958        (
2959            "var a=document.createElement('a'); a.href='https://x.com/p'; a.username='u'; \
2960             a.password='pw'; a.href",
2961            "https://u:pw@x.com/p",
2962        ),
2963        // `element.willValidate`がタグを一切見ておらず、`<output>`/`<div>`
2964        // のような本来常に`false`であるべき非フォームコントロールでも
2965        // `true`を返してしまうバグだった。2026-07-18 発見・修正)。
2966        ("document.createElement('output').willValidate", "false"),
2967        ("document.createElement('div').willValidate", "false"),
2968        ("document.createElement('fieldset').willValidate", "false"),
2969        ("document.createElement('input').willValidate", "true"),
2970        (
2971            "var b=document.createElement('button'); b.disabled=true; b.willValidate",
2972            "false",
2973        ),
2974        // `validity_flags`(`.validity`が内部で使う共通ロジック)が
2975        // `willValidate`と同根の「タグを見ない」バグを持っていた。
2976        // `<output required>`のような本来検証対象外の要素へ`required`
2977        // 属性を付けても`valueMissing`が立たないことを確認。2026-07-18
2978        // 発見・修正)。
2979        (
2980            "var o=document.createElement('output'); o.setAttribute('required','required'); \
2981             o.validity.valid",
2982            "true",
2983        ),
2984        (
2985            "var d=document.createElement('div'); d.setAttribute('required','required'); \
2986             d.validity.valueMissing",
2987            "false",
2988        ),
2989        (
2990            "var i=document.createElement('input'); i.setAttribute('required','required'); \
2991             i.validity.valueMissing",
2992            "true",
2993        ),
2994        // `validate_field`(`element.checkValidity()`が直接呼ぶ経路)も
2995        // 同根の「タグを見ない」バグを持っていた。`<output required>`へ
2996        // `checkValidity()`を直接呼んでも常に`true`のままであることを
2997        // 確認。`form.checkValidity()`側は既に`input`/`textarea`/
2998        // `select`へ絞り込み済みのため実害が無かった経路との違いに
2999        // 注意。2026-07-18 発見・修正)。
3000        (
3001            "var o=document.createElement('output'); o.setAttribute('required','required'); \
3002             o.checkValidity()",
3003            "true",
3004        ),
3005        (
3006            "var i=document.createElement('input'); i.setAttribute('required','required'); \
3007             i.checkValidity()",
3008            "false",
3009        ),
3010        // `video.disableRemotePlayback`(`disablePictureInPicture`と同じ
3011        // 単純なブール属性反映だが丸ごと未対応だった。2026-07-18
3012        // 発見・実装)。
3013        (
3014            "document.createElement('video').disableRemotePlayback",
3015            "false",
3016        ),
3017        (
3018            "var v=document.createElement('video'); v.disableRemotePlayback=true; \
3019             v.getAttribute('disableremoteplayback')",
3020            "disableremoteplayback",
3021        ),
3022        // `link`/`script.blocking`(`part`/`relList`/`sandbox`と同じ読み
3023        // 取り専用トークン配列。丸ごと未対応だった。2026-07-18 発見・
3024        // 実装)。
3025        (
3026            "var l=document.createElement('link'); l.setAttribute('blocking','render'); \
3027             l.blocking.length+','+l.blocking[0]",
3028            "1,render",
3029        ),
3030        ("document.createElement('script').blocking.length", "0"),
3031        // `output.value`/`.defaultValue`(`<output>`は`value`content属性を
3032        // 持たず、未書込み時は`.textContent`にフォールバックする。
3033        // `textarea.value`と同じパターンだが丸ごと未対応だった。
3034        // 2026-07-18 発見・実装)。
3035        (
3036            "var o=document.createElement('output'); o.textContent='42'; o.value",
3037            "42",
3038        ),
3039        (
3040            "var o=document.createElement('output'); o.textContent='42'; o.value='7'; o.value",
3041            "7",
3042        ),
3043        (
3044            "var o=document.createElement('output'); o.textContent='42'; o.value='7'; \
3045             o.textContent",
3046            "42",
3047        ),
3048        // `video.getVideoPlaybackQuality()`(丸ごと未対応だった。実
3049        // デコードパイプラインが無いため全カウンタ`0`の誠実な簡略実装。
3050        // 2026-07-18 発見・実装)。
3051        (
3052            "var q=document.createElement('video').getVideoPlaybackQuality(); \
3053             q.totalVideoFrames+','+q.droppedVideoFrames+','+q.corruptedVideoFrames",
3054            "0,0,0",
3055        ),
3056        // `element.namespaceURI`/`.localName`/`.prefix`(丸ごと未対応
3057        // だった。XML名前空間を一切モデル化していないため常にHTML
3058        // 名前空間定数/タグ名そのもの/`null`を返す誠実な簡略実装。
3059        // 2026-07-18 発見・実装)。
3060        (
3061            "document.createElement('div').namespaceURI",
3062            "http://www.w3.org/1999/xhtml",
3063        ),
3064        ("document.createElement('SPAN').localName", "span"),
3065        ("document.createElement('div').prefix", "null"),
3066        (
3067            "document.createTextNode('x').namespaceURI",
3068            "null",
3069        ),
3070        (
3071            "document.createTextNode('x').localName",
3072            "null",
3073        ),
3074        // `HTMLScriptElement.supports(type)`(静的メソッド。丸ごと未対応
3075        // だった。`classic`/`module`は実サポート済みで`true`、
3076        // `importmap`はインポートマップ自体が未実装のため`false`)。
3077        (
3078            "HTMLScriptElement.supports('classic')+','+HTMLScriptElement.supports('module')",
3079            "true,true",
3080        ),
3081        (
3082            "document.createElement('div').ariaActiveDescendantElement",
3083            "null",
3084        ),
3085        (
3086            "(function(){var a=document.createElement('div');a.setAttribute('aria-activedescendant','nope');return a.ariaActiveDescendantElement;})()",
3087            "null",
3088        ),
3089        // `element.getHTML()`/`.setHTMLUnsafe()`(`innerHTML`ゲッター/
3090        // セッターの明示的メソッド版。丸ごと未対応だった。この処理系には
3091        // Trusted Types自体が無いため`innerHTML`と可視的な違いは無い
3092        // 簡略実装。2026-07-18 発見・実装)。
3093        (
3094            "var d=document.createElement('div'); d.innerHTML='<b>x</b>'; d.getHTML()",
3095            "<b>x</b>",
3096        ),
3097        (
3098            "var d=document.createElement('div'); d.setHTMLUnsafe('<i>y</i>'); d.innerHTML",
3099            "<i>y</i>",
3100        ),
3101        // `CSS.number()`/`.percent()`/`.px()`/`.em()`/`.rem()`/`.deg()`
3102        // (CSS Typed OMの値+単位ファクトリ関数群。丸ごと未対応だった。
3103        // 2026-07-18 発見・実装)。
3104        ("CSS.px(5).value+','+CSS.px(5).unit+','+CSS.px(5).toString()", "5,px,5px"),
3105        ("CSS.percent(50).toString()", "50%"),
3106        ("CSS.number(3).toString()", "3"),
3107        ("CSS.em(1.5).toString()", "1.5em"),
3108        ("CSS.rem(2).toString()", "2rem"),
3109        ("CSS.deg(90).toString()", "90deg"),
3110        ("HTMLScriptElement.supports('importmap')", "false"),
3111        ("HTMLScriptElement.supports('bogus')", "false"),
3112        // `link.imageSrcset`/`.imageSizes`(`rel="preload" as="image"`用の
3113        // レスポンシブ画像プリロードヒント。`link.as`と同じ単純な文字列
3114        // 反映だが丸ごと未対応だった。2026-07-18 発見・実装)。
3115        (
3116            "(function(){var l=document.createElement('link');l.imageSrcset='a.jpg 1x, b.jpg 2x';l.imageSizes='100vw';return l.imageSrcset+'|'+l.imageSizes;})()",
3117            "a.jpg 1x, b.jpg 2x|100vw",
3118        ),
3119        (
3120            "document.createElement('link').imageSrcset+'|'+document.createElement('link').imageSizes",
3121            "|",
3122        ),
3123        // `iframe.credentialless`(COEP credentialless。`allowFullscreen`と
3124        // 同じ単純なブール属性反映だが丸ごと未対応だった。2026-07-18
3125        // 発見・実装)。
3126        (
3127            "(function(){var f=document.createElement('iframe');var before=f.credentialless;f.credentialless=true;var after=f.credentialless;f.credentialless=false;return before+','+after+','+f.credentialless;})()",
3128            "false,true,false",
3129        ),
3130        // `area.coords`/`.shape`(`shape`は既知値以外・欠落時は仕様上の
3131        // 既定値`"rect"`にフォールバックし、レガシー別名`"circ"`/
3132        // `"polygon"`も正規化する。プロパティ自体が丸ごと未対応だった。
3133        // 2026-07-17 発見・実装)。
3134        (
3135            "var a=document.createElement('area'); a.coords='0,0,10,10'; a.coords",
3136            "0,0,10,10",
3137        ),
3138        ("document.createElement('area').shape", "rect"),
3139        (
3140            "var a=document.createElement('area'); a.shape='circle'; a.shape",
3141            "circle",
3142        ),
3143        (
3144            "var a=document.createElement('area'); a.setAttribute('shape','circ'); a.shape",
3145            "circle",
3146        ),
3147        (
3148            "var a=document.createElement('area'); a.setAttribute('shape','polygon'); a.shape",
3149            "poly",
3150        ),
3151        // `ol.type`/`.start`/`.reversed`(`type`は既知値以外・欠落時は
3152        // 仕様上の既定値`"1"`にフォールバックする。`start`は欠落・不正値
3153        // 時の既定値`1`。どちらも丸ごと未対応だった。2026-07-17 発見・
3154        // 実装)。
3155        ("document.createElement('ol').type", "1"),
3156        (
3157            "var o=document.createElement('ol'); o.type='A'; o.type",
3158            "A",
3159        ),
3160        (
3161            "var o=document.createElement('ol'); o.setAttribute('type','bogus'); o.type",
3162            "1",
3163        ),
3164        ("document.createElement('ol').start", "1"),
3165        (
3166            "var o=document.createElement('ol'); o.start=5; o.start",
3167            "5",
3168        ),
3169        (
3170            "document.createElement('ol').reversed",
3171            "false",
3172        ),
3173        (
3174            "var o=document.createElement('ol'); o.reversed=true; o.getAttribute('reversed')",
3175            "reversed",
3176        ),
3177        // `meter.low`/`.high`/`.optimum`(`min`/`max`と同じく丸ごと未対応
3178        // だった。既定値はそれぞれ`min`/`max`/`min`と`max`の中点。
3179        // 2026-07-17 発見・実装)。
3180        (
3181            "var m=document.createElement('meter'); m.min='2'; m.max='10'; m.low+','+m.high+','+m.optimum",
3182            "2,10,6",
3183        ),
3184        (
3185            "var m=document.createElement('meter'); m.max='10'; m.low='4'; m.low",
3186            "4",
3187        ),
3188        (
3189            "var m=document.createElement('meter'); m.max='10'; m.high='20'; m.high",
3190            "10",
3191        ),
3192        // `td`/`th.colSpan`/`.rowSpan`(欠落・不正値時の既定値`1`、
3193        // `colSpan`は最大`1000`、`rowSpan`は最大`65534`にクランプ。
3194        // どちらも丸ごと未対応だった。2026-07-17 発見・実装)。
3195        ("document.createElement('td').colSpan", "1"),
3196        (
3197            "var c=document.createElement('td'); c.colSpan=3; c.colSpan",
3198            "3",
3199        ),
3200        (
3201            "var c=document.createElement('td'); c.setAttribute('colspan','9999'); c.colSpan",
3202            "1000",
3203        ),
3204        ("document.createElement('th').rowSpan", "1"),
3205        (
3206            "var c=document.createElement('th'); c.rowSpan=0; c.rowSpan",
3207            "0",
3208        ),
3209        // `td`/`th.headers`(単純な文字列反映)と`th.scope`(既知値以外・
3210        // 欠落時は仕様上の既定値`""`にフォールバック)、`th.abbr`(単純な
3211        // 文字列反映)が丸ごと未対応だった。2026-07-17 発見・実装)。
3212        (
3213            "var c=document.createElement('td'); c.headers='h1 h2'; c.headers",
3214            "h1 h2",
3215        ),
3216        ("document.createElement('th').scope", ""),
3217        (
3218            "var t=document.createElement('th'); t.scope='col'; t.scope",
3219            "col",
3220        ),
3221        (
3222            "var t=document.createElement('th'); t.setAttribute('scope','bogus'); t.scope",
3223            "",
3224        ),
3225        (
3226            "var t=document.createElement('th'); t.abbr='Description'; t.abbr",
3227            "Description",
3228        ),
3229        // `col`/`colgroup.span`(欠落・不正値時の既定値`1`、最大`1000`に
3230        // クランプ。丸ごと未対応だった。2026-07-17 発見・実装)。
3231        ("document.createElement('col').span", "1"),
3232        (
3233            "var c=document.createElement('colgroup'); c.span=3; c.span",
3234            "3",
3235        ),
3236        (
3237            "var c=document.createElement('col'); c.setAttribute('span','9999'); c.span",
3238            "1000",
3239        ),
3240        // `object.data`(`href`/`src`/`action`と同じくURL属性を反映し、
3241        // 現在のページURL基準の絶対URLを返す。テキストノードの
3242        // `CharacterData.data`と同名で衝突するためタグ限定で扱う必要が
3243        // あったが、プロパティ自体が丸ごと未対応だった。2026-07-17
3244        // 発見・実装)。
3245        (
3246            "var o=document.createElement('object'); o.data='/x'; o.getAttribute('data')",
3247            "/x",
3248        ),
3249        (
3250            "var t=document.createTextNode('hi'); t.data",
3251            "hi",
3252        ),
3253        // `meta.httpEquiv`/`.content`/`.charset`(`httpEquiv`は`http-equiv`
3254        // 属性の camelCase IDL プロパティ版で`htmlFor`と同じマッピング
3255        // パターン、`content`/`.charset`は単純な属性反映。3つとも丸ごと
3256        // 未対応だった。2026-07-17 発見・実装)。
3257        (
3258            "var m=document.createElement('meta'); m.httpEquiv='refresh'; m.getAttribute('http-equiv')",
3259            "refresh",
3260        ),
3261        (
3262            "var m=document.createElement('meta'); m.setAttribute('http-equiv','refresh'); m.httpEquiv",
3263            "refresh",
3264        ),
3265        (
3266            "var m=document.createElement('meta'); m.content='width=device-width'; m.content",
3267            "width=device-width",
3268        ),
3269        (
3270            "var m=document.createElement('meta'); m.charset='utf-8'; m.charset",
3271            "utf-8",
3272        ),
3273        // `blockquote`/`q`/`ins`/`del.cite`(単純な文字列反映)と
3274        // `ins`/`del`/`time.dateTime`(`datetime`属性のcamelCase IDL
3275        // プロパティ版)が丸ごと未対応だった。2026-07-17 発見・実装)。
3276        (
3277            "var b=document.createElement('blockquote'); b.cite='https://example.com'; b.getAttribute('cite')",
3278            "https://example.com",
3279        ),
3280        (
3281            "var t=document.createElement('time'); t.dateTime='2026-07-17'; t.getAttribute('datetime')",
3282            "2026-07-17",
3283        ),
3284        (
3285            "var i=document.createElement('ins'); i.setAttribute('datetime','2026-07-17'); i.dateTime",
3286            "2026-07-17",
3287        ),
3288        // `video.disablePictureInPicture`/`iframe.allowFullscreen`(単純な
3289        // ブール属性反映だが丸ごと未対応だった。2026-07-17 発見・実装)。
3290        (
3291            "document.createElement('video').disablePictureInPicture",
3292            "false",
3293        ),
3294        (
3295            "var v=document.createElement('video'); v.disablePictureInPicture=true; v.getAttribute('disablepictureinpicture')",
3296            "disablepictureinpicture",
3297        ),
3298        (
3299            "var f=document.createElement('iframe'); f.allowFullscreen=true; f.getAttribute('allowfullscreen')",
3300            "allowfullscreen",
3301        ),
3302        // `canvas.width`/`.height`(欠落・不正値時の既定値`300`/`150`)と
3303        // `img`/`video.width`/`.height`(既定値`0`)が丸ごと未対応
3304        // だった。2026-07-17 発見・実装)。
3305        (
3306            "document.createElement('canvas').width+','+document.createElement('canvas').height",
3307            "300,150",
3308        ),
3309        (
3310            "var c=document.createElement('canvas'); c.width=640; c.height=480; c.width+','+c.height",
3311            "640,480",
3312        ),
3313        (
3314            "document.createElement('img').width+','+document.createElement('img').height",
3315            "0,0",
3316        ),
3317        (
3318            "var v=document.createElement('video'); v.width=320; v.width",
3319            "320",
3320        ),
3321        // `embed`/`object`/`iframe.width`/`.height`(`DOMString`型の単純な
3322        // 文字列反映。`canvas`/`img`/`video`の`unsigned long`型とは異なり
3323        // 数値クランプ・既定値フォールバックが無い。丸ごと未対応
3324        // だった。2026-07-17 発見・実装)。
3325        (
3326            "var e=document.createElement('embed'); e.width='50%'; e.width",
3327            "50%",
3328        ),
3329        (
3330            "var o=document.createElement('object'); o.height='300'; o.height",
3331            "300",
3332        ),
3333        ("document.createElement('iframe').width", ""),
3334        (
3335            "var f=document.createElement('iframe'); f.setAttribute('width','640'); f.width",
3336            "640",
3337        ),
3338        // `input`/`textarea.defaultValue`(`value`属性/初期テキストを
3339        // 反映)と`input.defaultChecked`/`option.defaultSelected`(`checked`/
3340        // `selected`属性の有無を反映するブール型)が丸ごと未対応
3341        // だった。2026-07-17 発見・実装)。
3342        (
3343            "var i=document.createElement('input'); i.defaultValue='hi'; i.getAttribute('value')",
3344            "hi",
3345        ),
3346        (
3347            "var i=document.createElement('input'); i.setAttribute('value','preset'); i.defaultValue",
3348            "preset",
3349        ),
3350        (
3351            "var i=document.createElement('input'); i.setAttribute('value','preset'); i.value='changed'; i.defaultValue",
3352            "preset",
3353        ),
3354        (
3355            "var i=document.createElement('input'); i.setAttribute('value','preset'); i.value='changed'; i.value",
3356            "changed",
3357        ),
3358        (
3359            "document.createElement('input').defaultChecked",
3360            "false",
3361        ),
3362        (
3363            "var i=document.createElement('input'); i.defaultChecked=true; i.getAttribute('checked')",
3364            "checked",
3365        ),
3366        (
3367            "var i=document.createElement('input'); i.defaultChecked=true; i.checked=false; i.defaultChecked",
3368            "true",
3369        ),
3370        (
3371            "document.createElement('option').defaultSelected",
3372            "false",
3373        ),
3374        (
3375            "var o=document.createElement('option'); o.defaultSelected=true; o.getAttribute('selected')",
3376            "selected",
3377        ),
3378        // `output.type`(仕様上、属性値に関わらず常に固定文字列
3379        // `"output"`を返す読み取り専用IDLプロパティ。`<output>`要素
3380        // 自体が丸ごと未対応だった。2026-07-17 発見・実装)。
3381        ("document.createElement('output').type", "output"),
3382        (
3383            "var o=document.createElement('output'); o.setAttribute('type','bogus'); o.type",
3384            "output",
3385        ),
3386        // `iframe.srcdoc`(単純な文字列反映だが丸ごと未対応だった。
3387        // 2026-07-17 発見・実装)。
3388        (
3389            "var f=document.createElement('iframe'); f.srcdoc='<p>hi</p>'; f.getAttribute('srcdoc')",
3390            "<p>hi</p>",
3391        ),
3392        (
3393            "var f=document.createElement('iframe'); f.setAttribute('srcdoc','<b>x</b>'); f.srcdoc",
3394            "<b>x</b>",
3395        ),
3396        // `li.value`(順序リストの番号上書き用`long`型IDLプロパティ。
3397        // 欠落・不正値時の既定値は`0`。丸ごと未対応だった。汎用の`value`
3398        // アームだと数値ではなく文字列になってしまう衝突があった。
3399        // 2026-07-17 発見・実装)。
3400        ("document.createElement('li').value", "0"),
3401        (
3402            "var l=document.createElement('li'); l.value=5; l.value",
3403            "5",
3404        ),
3405        (
3406            "var l=document.createElement('li'); l.setAttribute('value','bogus'); l.value",
3407            "0",
3408        ),
3409        // `input.pattern`(`placeholder`と同じ単純な文字列反映だが丸ごと
3410        // 未対応だった。2026-07-17 発見・実装)。
3411        (
3412            "var i=document.createElement('input'); i.pattern='[0-9]+'; i.getAttribute('pattern')",
3413            "[0-9]+",
3414        ),
3415        (
3416            "var i=document.createElement('input'); i.setAttribute('pattern','[a-z]+'); i.pattern",
3417            "[a-z]+",
3418        ),
3419        // `input.capture`(`type="file"`のカメラ/マイク取り込みヒント。
3420        // 単純な文字列反映だが丸ごと未対応だった。2026-07-17 発見・実装)。
3421        (
3422            "var i=document.createElement('input'); i.capture='environment'; i.getAttribute('capture')",
3423            "environment",
3424        ),
3425        (
3426            "var i=document.createElement('input'); i.setAttribute('capture','user'); i.capture",
3427            "user",
3428        ),
3429        // `input.autocomplete`(単純な文字列反映)と`element.autofocus`
3430        // (ブール型。実際のフォーカス自動移動機構は無いため属性値の
3431        // 反映のみ)が丸ごと未対応だった。2026-07-18 発見・実装)。
3432        (
3433            "var i=document.createElement('input'); i.autocomplete='email'; i.getAttribute('autocomplete')",
3434            "email",
3435        ),
3436        (
3437            "var i=document.createElement('input'); i.setAttribute('autocomplete','off'); i.autocomplete",
3438            "off",
3439        ),
3440        (
3441            "document.createElement('input').autofocus",
3442            "false",
3443        ),
3444        (
3445            "var i=document.createElement('input'); i.autofocus=true; i.getAttribute('autofocus')",
3446            "autofocus",
3447        ),
3448        (
3449            "var i=document.createElement('input'); i.min='0'; i.max='10'; i.step='2'; i.min+','+i.max+','+i.step",
3450            "0,10,2",
3451        ),
3452        (
3453            "var i=document.createElement('input'); i.maxLength='5'; i.maxLength",
3454            "5",
3455        ),
3456        // `maxLength`/`minLength`はWebIDL上`long`(数値型)のIDL属性で、無指定時
3457        // の既定値は`-1`だが、以前は`maxLength`が文字列型(属性が無ければ空
3458        // 文字列)で返っており、`minLength`はゲッター自体が丸ごと未対応だった
3459        // (`min`/`max`/`step`は`DOMString`型が正しいIDL定義のため対象外)。
3460        (
3461            "typeof document.createElement('input').maxLength",
3462            "number",
3463        ),
3464        (
3465            "document.createElement('input').maxLength",
3466            "-1",
3467        ),
3468        (
3469            "var i=document.createElement('input'); i.minLength=3; typeof i.minLength + ',' + i.minLength",
3470            "number,3",
3471        ),
3472        (
3473            "document.createElement('textarea').minLength",
3474            "-1",
3475        ),
3476        (
3477            "var a=document.createElement('a'); a.target='_blank'; a.rel='noopener'; a.download='f.txt'; a.target+','+a.rel+','+a.download",
3478            "_blank,noopener,f.txt",
3479        ),
3480        (
3481            "var i=document.createElement('input'); i.placeholder='enter name'; i.placeholder",
3482            "enter name",
3483        ),
3484        (
3485            "var d=document.createElement('details'); d.open",
3486            "false",
3487        ),
3488        (
3489            "var d=document.createElement('details'); d.open=true; d.open",
3490            "true",
3491        ),
3492        (
3493            "var d=document.createElement('details'); d.setAttribute('open',''); d.open",
3494            "true",
3495        ),
3496        (
3497            "var i=document.createElement('input'); i.name='email'; i.name",
3498            "email",
3499        ),
3500        // `select.value`/`select.selectedIndex` が丸ごと未対応だった。`<select>` は
3501        // 自身に `value` 属性を持たず、子の `<option selected>` が選択状態を持つため、
3502        // 汎用の属性直結 getter では常に空文字列になっていた。
3503        (
3504            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.value",
3505            "a",
3506        ),
3507        (
3508            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.value='b'; s.value",
3509            "b",
3510        ),
3511        (
3512            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.value='b'; s.selectedIndex",
3513            "1",
3514        ),
3515        (
3516            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.selectedIndex=1; s.value",
3517            "b",
3518        ),
3519        // `option.index`(`select.selectedIndex`と対になる、`<option>`側の0始まり
3520        // 位置を返すIDL属性)が丸ごと未対応だった。
3521        (
3522            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.options[1].index",
3523            "1",
3524        ),
3525        (
3526            "document.createElement('option').index",
3527            "0",
3528        ),
3529        // `select.length`(`select.options.length`と同じ値を返すIDL属性、および
3530        // 末尾optionを切り捨てる/空optionで埋めるセッター)が丸ごと未対応だった。
3531        (
3532            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.length",
3533            "2",
3534        ),
3535        (
3536            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.length=1; s.options.length + ',' + s.value",
3537            "1,a",
3538        ),
3539        (
3540            "var s=document.createElement('select'); s.length=2; s.options.length",
3541            "2",
3542        ),
3543        // `select.remove(index)`(`HTMLSelectElement`固有のオーバーロード。
3544        // 指定インデックスのoptionを削除する)が、同名の`ChildNode.remove()`
3545        // (引数無しで自身を親から削除する)と混同され、`select.remove(1)`が
3546        // 常にselect自身を親ごと削除してしまうバグだった。
3547        (
3548            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option><option value='b'>B</option>\"; s.remove(0); s.options.length + ',' + s.value",
3549            "1,b",
3550        ),
3551        (
3552            // 引数無しの `.remove()` は従来どおり自身を親から削除する
3553            // (ChildNode.remove()と同じ挙動を維持)。
3554            "var d=document.createElement('div'); var s=document.createElement('select'); d.appendChild(s); s.remove(); d.children.length",
3555            "0",
3556        ),
3557        (
3558            // 負のインデックスはno-op(`as usize`の飽和変換で誤って先頭が
3559            // 削除されるバグを防いだ確認)。
3560            "var s=document.createElement('select'); s.innerHTML=\"<option value='a'>A</option>\"; s.remove(-1); s.options.length",
3561            "1",
3562        ),
3563        // `textarea.value` も同種のバグだった(`<textarea>` の初期値は本来テキスト
3564        // ノードの内容で表現されるべきだが、`.value` 書込み後の読み出し往復は
3565        // 最低限このとおり動くことを確認する)。
3566        (
3567            "var t=document.createElement('textarea'); t.value='hi'; t.value",
3568            "hi",
3569        ),
3570        ("typeof console.table", "function"),
3571        ("typeof globalThis", "object"),
3572        ("typeof globalThis.document", "object"),
3573        // fetch(data: URL でネット非依存にフルチェーン検証: Promise→Response→text()/json())
3574        ("typeof fetch", "function"),
3575        ("(await fetch('data:,hello')).status", "200"),
3576        ("(await fetch('data:,hello')).ok", "true"),
3577        ("await (await fetch('data:,hello world')).text()", "hello world"),
3578        ("await fetch('data:text/plain,abc').then(r => r.text())", "abc"),
3579        ("(await (await fetch('data:application/json,{\"a\":7,\"b\":[1,2]}')).json()).a", "7"),
3580        ("(await (await fetch('data:application/json,{\"a\":7,\"b\":[1,2]}')).json()).b.length", "2"),
3581        // `Response.prototype.blob()`(丸ごと未対応で `blob is not defined` だった)。
3582        ("(await (await fetch('data:,hello')).blob()).size", "5"),
3583        ("await (await (await fetch('data:,hello')).blob()).text()", "hello"),
3584        // `Response.prototype.arrayBuffer()` も同種の欠落だった。実バイト列付きの
3585        // ArrayBuffer で解決するため、そこから作った DataView で正しく読み戻せる。
3586        (
3587            "var buf = await (await fetch('data:,A')).arrayBuffer(); new DataView(buf).getUint8(0)",
3588            "65",
3589        ),
3590        ("await fetch('ftp://x/y').then(() => 'ok', e => 'err:' + (e.message || e))", "err:fetch: only absolute http(s) and data URLs are supported"),
3591        // `Response.prototype.statusText` が丸ごと未対応で常に `undefined` だった
3592        // (エラーログの定番パターン `console.log(response.status, response.statusText)`
3593        // で参照される)。
3594        ("(await fetch('data:,hi')).statusText", "OK"),
3595        // `Response.prototype.clone()`(`response.clone().json()` のように本文を
3596        // 2回読みたい場合に使う定番パターン)が丸ごと未対応で `clone is not a
3597        // function` だった。
3598        (
3599            "var r = await fetch('data:,hello'); var c = r.clone(); (await c.text()) + ',' + (await r.text())",
3600            "hello,hello",
3601        ),
3602        ("(await fetch('data:,x')).clone().status", "200"),
3603        // `Response.prototype.url`(リダイレクト後の最終 URL 確認等で使われる
3604        // 定番プロパティ)が丸ごと未対応で常に `undefined` だった。
3605        (
3606            "(await fetch('data:,hi')).url",
3607            "data:,hi",
3608        ),
3609        // `Response` コンストラクタ自体が丸ごと未対応だった(`fetch()` 内部からしか
3610        // 生成できず、`new Response(...)` が呼び出すと TypeError になっていた)。
3611        ("new Response('hi').status", "200"),
3612        ("await new Response('hi', {status: 201}).text()", "hi"),
3613        (
3614            "(await new Response(JSON.stringify({a:1}), {headers:{'content-type':'application/json'}}).json()).a",
3615            "1",
3616        ),
3617        // `Response.json(data, init)`(ES2022 static factory)。
3618        ("(await Response.json({a:5}).json()).a", "5"),
3619        ("Response.json({}).headers.get('content-type')", "application/json"),
3620        // `Response.error()`。
3621        ("Response.error().ok", "false"),
3622        ("Response.error().type", "error"),
3623        // `Response.redirect(url, status?)`。
3624        ("Response.redirect('https://e.com/x').status", "302"),
3625        ("Response.redirect('https://e.com/x', 301).headers.get('location')", "https://e.com/x"),
3626        (
3627            "(await fetch('data:,x')).clone().url",
3628            "data:,x",
3629        ),
3630        // `fetch(url, {signal})` が丸ごと未対応で、既に aborted な signal を渡しても
3631        // 常にリクエストを実行してしまっていた。
3632        (
3633            "await fetch('data:,hi', {signal: AbortSignal.abort('why')}).then(() => 'no-throw', e => 'caught:' + e)",
3634            "caught:why",
3635        ),
3636        (
3637            "await fetch('data:,hi', {signal: new AbortController().signal}).then(r => r.status)",
3638            "200",
3639        ),
3640        // `new Request(url, options)` が丸ごと未対応で `Request is not defined`
3641        // だった。`fetch(request)` という定番パターンも合わせて確認する。
3642        ("new Request('data:,hi').url", "data:,hi"),
3643        ("new Request('data:,hi', {method:'POST'}).method", "POST"),
3644        // `request.headers` が仕様上常に本物の `Headers` インスタンスであるべき
3645        // ところ、生の値をそのまま格納するだけで `.get(...)` が壊れていた。
3646        (
3647            "new Request('data:,hi', {headers:{'X-Foo':'bar'}}).headers.get('x-foo')",
3648            "bar",
3649        ),
3650        ("typeof new Request('data:,hi').headers.get", "function"),
3651        // `response.type`/`.redirected` が丸ごと未対応で常に `undefined` だった。
3652        ("(await fetch('data:,hi')).type", "basic"),
3653        ("(await fetch('data:,hi')).redirected", "false"),
3654        ("await fetch(new Request('data:,hello')).then(r => r.text())", "hello"),
3655        (
3656            "await fetch(new Request('data:,x'), {method:'GET'}).then(r => r.status)",
3657            "200",
3658        ),
3659        // `Headers`(Fetch API の get/set/has/append/delete 付き専用クラス)が
3660        // 丸ごと未対応だった。ヘッダ名は大小無視で正規化される。
3661        ("new Headers({'Content-Type':'text/plain'}).get('content-type')", "text/plain"),
3662        // `[[k,v], ...]` 配列形式の init(`make_headers` の `is_array` 分岐。
3663        // 2026-07-14、clippy `single_match` 修正時に既存テストで一度もこの
3664        // 分岐が通っていなかったことに気づき追加)。
3665        ("new Headers([['A','1'],['b','2']]).get('a')", "1"),
3666        ("new Headers().has('x')", "false"),
3667        (
3668            "var h=new Headers(); h.set('X-Foo','1'); h.has('x-foo')",
3669            "true",
3670        ),
3671        (
3672            "var h=new Headers(); h.append('a','1'); h.append('a','2'); h.get('a')",
3673            "1, 2",
3674        ),
3675        (
3676            "var h=new Headers({a:'1'}); h.delete('a'); h.has('a')",
3677            "false",
3678        ),
3679        (
3680            "var h=new Headers({a:'1',b:'2'}); var s=''; h.forEach(function(v,k){s+=k+'='+v+';'}); s",
3681            "a=1;b=2;",
3682        ),
3683        // `entries`/`keys`/`values`/`Symbol.iterator`(`for (const [k,v] of headers)`
3684        // という定番イディオムを含む)が丸ごと未対応だった。
3685        (
3686            "Array.from(new Headers({b:'2',a:'1'}).entries()).map(function(e){return e[0]+'='+e[1]}).join(',')",
3687            "a=1,b=2",
3688        ),
3689        ("Array.from(new Headers({a:'1',b:'2'}).keys()).join(',')", "a,b"),
3690        ("Array.from(new Headers({a:'1',b:'2'}).values()).join(',')", "1,2"),
3691        (
3692            "var s=''; for (var e of new Headers({a:'1',b:'2'})) { s += e[0]+e[1]; } s",
3693            "a1b2",
3694        ),
3695        // `response.headers` が丸ごと欠落しておらず、本物の `Headers`(メソッド持ち)
3696        // が返ること。この処理系はレスポンスヘッダを捕捉していないため中身は
3697        // 常に空になる。
3698        (
3699            "typeof (await fetch('data:,hi')).headers.get",
3700            "function",
3701        ),
3702        // `xhr.getResponseHeader(name)` が丸ごと未対応で「関数ではない」の
3703        // TypeError になっていた。
3704        (
3705            "var x=new XMLHttpRequest(); x.open('GET','data:,hi'); x.send(); x.getResponseHeader('x') === null",
3706            "true",
3707        ),
3708        // XMLHttpRequest(同期。data: でオフライン検証)
3709        ("typeof XMLHttpRequest", "function"),
3710        ("var x=new XMLHttpRequest(); x.open('GET','data:,hi'); x.send(); x.responseText", "hi"),
3711        ("var x=new XMLHttpRequest(); x.open('GET','data:,hi'); x.send(); x.status", "200"),
3712        ("var x=new XMLHttpRequest(); x.open('GET','data:,hi'); x.send(); x.readyState", "4"),
3713        // `xhr.statusText` がコンストラクタで空文字列初期化されたきり `send()` 完了後も
3714        // 一切更新されないバグだった(`Response.prototype.statusText` と同種)。
3715        ("var x=new XMLHttpRequest(); x.open('GET','data:,hi'); x.send(); x.statusText", "OK"),
3716        ("var g=''; var x=new XMLHttpRequest(); x.onload=function(){g=x.responseText}; x.open('GET','data:,YO'); x.send(); g", "YO"),
3717        ("var g=''; var x=new XMLHttpRequest(); x.addEventListener('load',function(){g='L'+x.status}); x.open('GET','data:,q'); x.send(); g", "L200"),
3718        // `xhr.removeEventListener` が丸ごと未対応で、呼び出すと「関数ではない」の
3719        // TypeError になっていた。
3720        (
3721            "var g=''; var x=new XMLHttpRequest(); var fn=function(){g='L'}; x.addEventListener('load',fn); x.removeEventListener('load',fn); x.open('GET','data:,q'); x.send(); g",
3722            "",
3723        ),
3724        (
3725            "typeof new XMLHttpRequest().removeEventListener",
3726            "function",
3727        ),
3728        ("var x=new XMLHttpRequest(); x.open('GET','data:application/json,{\"n\":5}'); x.send(); JSON.parse(x.responseText).n", "5"),
3729        // fetch/XHR の POST オプション配線(data: は method 非依存で本文を返す=経路確認)
3730        ("await (await fetch('data:,hi', {method:'POST', body:'x'})).text()", "hi"),
3731        ("(await fetch('data:,ok', {method:'POST', headers:{'Content-Type':'application/json'}, body:'{}'})).status", "200"),
3732        ("var x=new XMLHttpRequest(); x.open('POST','data:,ok'); x.setRequestHeader('Content-Type','application/json'); x.send('{\"a\":1}'); x.responseText", "ok"),
3733        ("var x=new XMLHttpRequest(); x.open('POST','data:,z'); x.send(); x.status", "200"),
3734        // RegExp
3735        ("/\\d+/.test('abc123')", "true"),
3736        ("/^\\d+$/.test('abc')", "false"),
3737        ("'2026-06-15'.match(/(\\d+)-(\\d+)-(\\d+)/)[2]", "06"),
3738        ("'hello world'.replace(/o/g, '0')", "hell0 w0rld"),
3739        ("'a1b2c3'.replace(/\\d/g, m => '[' + m + ']')", "a[1]b[2]c[3]"),
3740        ("'one,two;three four'.split(/[,; ]/).join('|')", "one|two|three|four"),
3741        ("'foobarbaz'.match(/a/g).length", "2"),
3742        ("new RegExp('[A-Z]+').test('hello WORLD')", "true"),
3743        ("'Hello'.replace(/(\\w)(\\w+)/, '$2$1')", "elloH"),
3744        ("'price: $42'.match(/\\$(\\d+)/)[1]", "42"),
3745        ("/cat/i.test('CAT')", "true"),
3746        ("'a,b,,c'.split(/,/).length", "4"),
3747        // RegExp `y`(sticky)フラグ(ES2015): lastIndex の位置でのみマッチを試みる
3748        // (`g` と違い前方探索しない)。dotAll/sticky ゲッタープロパティも合わせて検証。
3749        ("/x/y.sticky", "true"),
3750        ("/x/g.sticky", "false"),
3751        ("/./s.dotAll", "true"),
3752        ("/./.dotAll", "false"),
3753        (
3754            "var re=/foo/y; re.lastIndex=3; re.test('xxxfoo')",
3755            "true",
3756        ),
3757        (
3758            "var re=/foo/y; re.lastIndex=2; re.test('xxxfoo')",
3759            "false",
3760        ),
3761        (
3762            "var re=/foo/y; var a=re.exec('foofoo').index; var b=re.exec('foofoo').index; a+','+b",
3763            "0,3",
3764        ),
3765        // RegExp `d`(hasIndices)フラグ(ES2022): exec() 結果に各キャプチャの
3766        // [start,end) 文字インデックスを持つ indices 配列を追加する。
3767        ("/x/d.hasIndices", "true"),
3768        ("/x/.hasIndices", "false"),
3769        (
3770            "var m=/(foo)(bar)/d.exec('xxfoobar'); m.indices[0].join(',')",
3771            "2,8",
3772        ),
3773        (
3774            "var m=/(foo)(bar)/d.exec('xxfoobar'); m.indices[1].join(',')+'/'+m.indices[2].join(',')",
3775            "2,5/5,8",
3776        ),
3777        (
3778            "var m=/(foo)(baz)?/d.exec('foo'); typeof m.indices[2]",
3779            "undefined",
3780        ),
3781        ("/x/.exec('x').indices", "undefined"),
3782        // setInterval: 発火確認は同一 eval 内では行えない(コールバックは
3783        // event_loop の次 tick で発火するため、登録直後は n=0 が正しい)。
3784        // 複数回発火の検証は下の「2段階評価テスト」で行う。
3785        // clearInterval: 登録直後にクリアすれば未発火のまま n=0。
3786        ("var n=0; var id=setInterval(function(){n++;}, 10); clearInterval(id); n", "0"),
3787        // window.innerWidth / innerHeight
3788        ("typeof window.innerWidth", "number"),
3789        ("typeof window.innerHeight", "number"),
3790        // XMLHttpRequest 任意メソッド(PUT/DELETE/PATCH)。data: URL は同期・無ネットワークで
3791        // メソッド非依存に 200+ボディを返すため、do_http_request→web_request 経路が
3792        // GET/POST 以外でも例外なく通ることを決定論的に検証できる。
3793        ("var x=new XMLHttpRequest(); x.open('PUT','data:text/plain,HELLO'); x.send('body'); x.status", "200"),
3794        ("var x=new XMLHttpRequest(); x.open('PUT','data:text/plain,HELLO'); x.send('body'); x.responseText", "HELLO"),
3795        ("var x=new XMLHttpRequest(); x.open('DELETE','data:text/plain,GONE'); x.send(); x.status", "200"),
3796        ("var x=new XMLHttpRequest(); x.open('DELETE','data:text/plain,GONE'); x.send(); x.responseText", "GONE"),
3797        ("var x=new XMLHttpRequest(); x.open('PATCH','data:text/plain,Pdata'); x.send('d'); x.responseText", "Pdata"),
3798        // 小文字メソッドも web_request 側で大文字正規化され通ること。
3799        ("var x=new XMLHttpRequest(); x.open('put','data:text/plain,low'); x.send('b'); x.responseText", "low"),
3800        // history.back/forward/go: ナビ要求を history._pending_nav(相対移動量)に積む。
3801        // 実ナビはホスト側 process_pending_nav が回収・実行するため、ここでは積算値を検証。
3802        ("history.back(); history._pending_nav", "-1"),
3803        ("history.forward(); history._pending_nav", "1"),
3804        ("history.go(-2); history._pending_nav", "-2"),
3805        ("history.back(); history.back(); history._pending_nav", "-2"),
3806        ("history.go(0); history._pending_nav || 0", "0"),
3807        // popstate リスナ登録は window の隠し配列へ蓄積される(発火はホスト主導)。
3808        ("window.addEventListener('popstate', function(){}); window._popstate_listeners.length", "1"),
3809        // scroll リスナも window の隠し配列 _scroll_listeners へ蓄積される(発火はホスト主導)。
3810        ("window.addEventListener('scroll', function(){}); window._scroll_listeners.length", "1"),
3811        ("window.addEventListener('scroll', function(){}); window.addEventListener('scroll', function(){}); window._scroll_listeners.length", "2"),
3812        // window.scrollY 初期値は 0(ホストが fire_scroll で更新する)。
3813        ("window.scrollY", "0"),
3814        // `window.postMessage()`(丸ごと未対応だった。フレーム分離が無いため
3815        // 同一 window 上の 'message' リスナへ同期配送する簡略実装)。
3816        (
3817            "var got=null; window.addEventListener('message', function(e){ got=e.data; }); \
3818             window.postMessage({x:1}); got.x",
3819            "1",
3820        ),
3821        (
3822            "var t=null; window.addEventListener('message', function(e){ t=e.type; }); \
3823             window.postMessage('hi'); t",
3824            "message",
3825        ),
3826        // `window.addEventListener(type, fn, {signal})`(丸ごと未対応だった。
3827        // `AbortController` でリスナを一括解除する定番パターン。2026-07-14
3828        // 発見・実装)。abort 後は発火しなくなる。
3829        (
3830            "var c=new AbortController(); var n=0; \
3831             window.addEventListener('message', function(){n++;}, {signal: c.signal}); \
3832             window.postMessage('a'); c.abort(); window.postMessage('b'); n",
3833            "1",
3834        ),
3835        // 登録前に既に abort 済みの signal を渡した場合は登録自体が行われない。
3836        (
3837            "var c=new AbortController(); c.abort(); var n=0; \
3838             window.addEventListener('message', function(){n++;}, {signal: c.signal}); \
3839             window.postMessage('a'); n",
3840            "0",
3841        ),
3842        // ---- Proxy / Reflect ----
3843        // get トラップ: プロパティ読み取りを横取りする。
3844        ("var p=new Proxy({}, {get:function(t,k){return 'G:'+k;}}); p.foo", "G:foo"),
3845        // get トラップ未定義なら target へフォワード。
3846        ("var p=new Proxy({a:42}, {}); p.a", "42"),
3847        // set トラップ: 書き込みを横取りして target に別キーで格納。
3848        ("var log=''; var p=new Proxy({}, {set:function(t,k,v){t['_'+k]=v;return true;}}); p.x=9; p._x", "9"),
3849        // set トラップ未定義なら target に直接書く。
3850        ("var p=new Proxy({}, {}); p.y=7; p.y", "7"),
3851        // has トラップ: in 演算子を横取り。
3852        ("var p=new Proxy({}, {has:function(t,k){return k==='magic';}}); ('magic' in p)", "true"),
3853        ("var p=new Proxy({}, {has:function(t,k){return k==='magic';}}); ('other' in p)", "false"),
3854        // has トラップ未定義なら target の存在判定にフォワード。
3855        ("var p=new Proxy({z:1}, {}); ('z' in p)", "true"),
3856        ("var p=new Proxy({z:1}, {}); ('w' in p)", "false"),
3857        // deleteProperty トラップ: `delete p.prop` を横取り。以前はここが未対応で、
3858        // トラップ呼び出しはおろか target へのフォワードすら行われず常に何も削除せず
3859        // `true` を返すだけの黙殺バグだった。
3860        (
3861            "var log=''; var p=new Proxy({a:1}, {deleteProperty:function(t,k){log+=k; delete t[k]; return true;}}); delete p.a; log + ':' + ('a' in p)",
3862            "a:false",
3863        ),
3864        // deleteProperty トラップ未定義なら target への delete にフォワードする。
3865        ("var p=new Proxy({a:1}, {}); delete p.a; ('a' in p)", "false"),
3866        // `Index`(`delete p['a']`)経由でも同じくフォワードされる。
3867        ("var p=new Proxy({a:1}, {}); delete p['a']; ('a' in p)", "false"),
3868        // `Object.defineProperty`/`Object.getOwnPropertyDescriptor` も Proxy 自身の
3869        // 意味の無い `.props` に書き込む/読み取るだけで target に一切反映・反射されない
3870        // バグだった。target フォワードで対応(trap 呼び出しまでは非対応)。
3871        (
3872            "var t={}; var p=new Proxy(t, {}); Object.defineProperty(p, 'x', {value:5}); t.x",
3873            "5",
3874        ),
3875        (
3876            "var p=new Proxy({x:5}, {}); Object.getOwnPropertyDescriptor(p, 'x').value",
3877            "5",
3878        ),
3879        // apply トラップ: 関数呼び出しを横取り。
3880        ("var f=function(a,b){return a+b;}; var p=new Proxy(f, {apply:function(t,thiz,args){return args[0]*args[1];}}); p(3,4)", "12"),
3881        // apply トラップ未定義なら target を普通に呼ぶ。
3882        ("var f=function(a,b){return a+b;}; var p=new Proxy(f, {}); p(3,4)", "7"),
3883        // construct トラップ: new を横取り。
3884        ("function C(){} var p=new Proxy(C, {construct:function(t,args){return {made:args[0]};}}); (new p(99)).made", "99"),
3885        // construct トラップ未定義なら target を new する。
3886        ("function C(x){this.v=x;} var p=new Proxy(C, {}); (new p(5)).v", "5"),
3887        // Reflect.get / set
3888        ("var o={a:1}; Reflect.set(o,'a',8); Reflect.get(o,'a')", "8"),
3889        // Reflect.has
3890        ("Reflect.has({k:1},'k')", "true"),
3891        ("Reflect.has({k:1},'x')", "false"),
3892        // Reflect.deleteProperty
3893        ("var o={a:1,b:2}; Reflect.deleteProperty(o,'a'); ('a' in o)", "false"),
3894        // Reflect.ownKeys
3895        ("Reflect.ownKeys({a:1,b:2,c:3}).length", "3"),
3896        // Reflect.defineProperty
3897        ("var o={}; Reflect.defineProperty(o,'x',{value:42}); o.x", "42"),
3898        // Reflect.apply
3899        ("Reflect.apply(function(a,b){return a-b;}, null, [10,3])", "7"),
3900        // Reflect.apply/construct の argumentsList は仕様上 array-like 全般
3901        // (実配列に限らない)を受け付けるべきだが、以前は `&mut Interp` を持たない
3902        // `iterable_values` を使っており、素の array-like を渡すと引数が消えていた。
3903        (
3904            "Reflect.apply(function(a,b){return a-b;}, null, {0:10,1:3,length:2})",
3905            "7",
3906        ),
3907        // Reflect.construct
3908        ("function P(x){this.x=x;} Reflect.construct(P,[77]).x", "77"),
3909        (
3910            "function P(x){this.x=x;} Reflect.construct(P,{0:77,length:1}).x",
3911            "77",
3912        ),
3913        // Reflect.get がプロトタイプ連鎖を辿る。
3914        ("var base={greet:'hi'}; var o=Object.create(base); Reflect.get(o,'greet')", "hi"),
3915        // Proxy + Reflect 併用: get をログしつつ target にフォワード。
3916        ("var p=new Proxy({n:5}, {get:function(t,k){return Reflect.get(t,k)*2;}}); p.n", "10"),
3917        // 配列を包む Proxy の get(数値インデックス)。
3918        ("var p=new Proxy([10,20,30], {get:function(t,k){return Reflect.get(t,k);}}); p[1]", "20"),
3919        // ネストした Proxy(二重ラップ)。
3920        ("var inner=new Proxy({v:1},{get:function(t,k){return 100;}}); var outer=new Proxy(inner,{}); outer.v", "100"),
3921        // WeakMap
3922        ("var wm=new WeakMap(); var k={}; wm.set(k,42); wm.get(k)", "42"),
3923        ("var wm=new WeakMap(); var k={}; wm.set(k,1); wm.has(k)", "true"),
3924        ("var wm=new WeakMap(); var k={}; wm.set(k,1); wm.delete(k); wm.has(k)", "false"),
3925        // WeakSet
3926        ("var ws=new WeakSet(); var o={}; ws.add(o); ws.has(o)", "true"),
3927        ("var ws=new WeakSet(); var o={}; ws.add(o); ws.delete(o); ws.has(o)", "false"),
3928        // `WeakMap`/`WeakSet` は仕様上オブジェクト以外のキー/値を拒否し `TypeError` を
3929        // 投げるべきだが、以前は型を一切検証せず静かに成功していたバグ。
3930        (
3931            "try { new WeakMap().set(1,'x'); 'no-throw' } catch(e) { 'caught' }",
3932            "caught",
3933        ),
3934        (
3935            "try { new WeakSet().add('x'); 'no-throw' } catch(e) { 'caught' }",
3936            "caught",
3937        ),
3938        // `new WeakMap(iterable)`/`new WeakSet(iterable)` の初期化引数が `Map` と違い
3939        // 完全に無視されていた(常に空になるバグ)。
3940        ("var k1={}; var wm=new WeakMap([[k1,1]]); wm.get(k1)", "1"),
3941        ("var o1={}; var ws=new WeakSet([o1]); ws.has(o1)", "true"),
3942        // WeakRef
3943        ("var o={v:7}; var wr=new WeakRef(o); wr.deref().v", "7"),
3944        // Symbol 関数(AtmOS では文字列近似)
3945        ("typeof Symbol('x')", "string"),
3946        ("Symbol('a') === Symbol('a')", "false"),
3947        ("Symbol.for('x') === Symbol.for('x')", "true"),
3948        // `Symbol.keyFor`(`Symbol.for` の逆引き)が丸ごと未対応だった。
3949        ("Symbol.keyFor(Symbol.for('mykey'))", "mykey"),
3950        ("typeof Symbol.keyFor(Symbol('local'))", "undefined"),
3951        ("typeof Symbol.iterator", "string"),
3952        // MutationObserver — typeof で存在確認。
3953        ("typeof MutationObserver", "function"),
3954        // MutationObserver — コンストラクタが呼べる。
3955        ("var mo = new MutationObserver(function(){}); typeof mo.observe", "function"),
3956        // MutationObserver — disconnect と takeRecords が存在する。
3957        ("var mo = new MutationObserver(function(){}); typeof mo.disconnect + ',' + typeof mo.takeRecords", "function,function"),
3958        // MutationObserver — takeRecords は初期状態で空配列。
3959        ("var mo = new MutationObserver(function(){}); mo.takeRecords().length", "0"),
3960        // MutationObserver — attributeOldValue が丸ごと未対応で oldValue が常に
3961        // null 固定だったバグ(以前は old_value 自体を捕捉していなかった)。
3962        (
3963            "var el=document.createElement('div'); el.setAttribute('foo','bar'); \
3964             var mo=new MutationObserver(function(){}); \
3965             mo.observe(el,{attributes:true,attributeOldValue:true}); \
3966             el.setAttribute('foo','baz'); var r=mo.takeRecords(); \
3967             r.length+','+r[0].attributeName+','+r[0].oldValue",
3968            "1,foo,bar",
3969        ),
3970        // MutationObserver — attributeFilter が丸ごと未対応で、フィルタ対象外の
3971        // 属性変化まで常に通知されてしまっていたバグ。
3972        (
3973            "var el=document.createElement('div'); \
3974             var mo=new MutationObserver(function(){}); \
3975             mo.observe(el,{attributes:true,attributeFilter:['foo']}); \
3976             el.setAttribute('bar','x'); el.setAttribute('foo','y'); \
3977             var r=mo.takeRecords(); r.length+','+r[0].attributeName",
3978            "1,foo",
3979        ),
3980        // MutationObserver — removeAttribute() が非対称に通知漏れしていたバグ
3981        // (setAttribute() は既に notify_attribute を呼んでいたが removeAttribute()
3982        // 側だけこの呼び出し自体が丸ごと欠けていた)。
3983        (
3984            "var el=document.createElement('div'); el.setAttribute('foo','bar'); \
3985             var mo=new MutationObserver(function(){}); \
3986             mo.observe(el,{attributes:true,attributeOldValue:true}); \
3987             el.removeAttribute('foo'); var r=mo.takeRecords(); \
3988             r.length+','+r[0].attributeName+','+r[0].oldValue",
3989            "1,foo,bar",
3990        ),
3991        // MutationObserver — classList.add()/.remove() が「class」属性を書き換える
3992        // にもかかわらず notify_attribute を一切呼んでおらず通知漏れしていたバグ
3993        // (setAttribute('class',...)/removeAttribute() 経由の変更は通知されるのに
3994        // classList 経由だけ抜けていた非対称ペア)。
3995        (
3996            "var el=document.createElement('div'); el.className='a'; \
3997             var mo=new MutationObserver(function(){}); \
3998             mo.observe(el,{attributes:true,attributeOldValue:true}); \
3999             el.classList.add('b'); var r=mo.takeRecords(); \
4000             r.length+','+r[0].attributeName+','+r[0].oldValue",
4001            "1,class,a",
4002        ),
4003        (
4004            "var el=document.createElement('div'); el.className='a b'; \
4005             var mo=new MutationObserver(function(){}); \
4006             mo.observe(el,{attributes:true,attributeOldValue:true}); \
4007             el.classList.remove('b'); var r=mo.takeRecords(); \
4008             r.length+','+r[0].attributeName+','+r[0].oldValue",
4009            "1,class,a b",
4010        ),
4011        // MutationObserver — style.xxx=/.cssText= が「style」属性を書き換える
4012        // にもかかわらず notify_attribute を一切呼んでいなかったバグ(classList と
4013        // 同型の非対称ペア)。
4014        (
4015            "var el=document.createElement('div'); el.style.color='red'; \
4016             var mo=new MutationObserver(function(){}); \
4017             mo.observe(el,{attributes:true,attributeOldValue:true}); \
4018             el.style.color='blue'; var r=mo.takeRecords(); \
4019             r.length+','+r[0].attributeName+','+r[0].oldValue",
4020            "1,style,color: red",
4021        ),
4022        // MutationObserver — 仕様上`attributeOldValue`/`attributeFilter`が
4023        // 指定され`attributes`自体が省略された場合は`attributes`を暗黙的に
4024        // `true`とみなすべきところ、この暗黙有効化ロジックが丸ごと無く
4025        // `observe(el,{attributeOldValue:true})`という定番の省略記法
4026        // (`attributes:true`を明示せずとも動くのが仕様)が黙って何も
4027        // 監視しないバグだった。2026-07-17 発見・実装。
4028        (
4029            "var el=document.createElement('div'); el.setAttribute('data-x','1'); \
4030             var mo=new MutationObserver(function(){}); \
4031             mo.observe(el,{attributeOldValue:true}); \
4032             el.setAttribute('data-x','2'); var r=mo.takeRecords(); \
4033             r.length+','+r[0].type",
4034            "1,attributes",
4035        ),
4036        // `attributeFilter`のみ指定(`attributes`省略)でも同様に暗黙有効化される。
4037        (
4038            "var el=document.createElement('div'); \
4039             var mo=new MutationObserver(function(){}); \
4040             mo.observe(el,{attributeFilter:['data-x']}); \
4041             el.setAttribute('data-x','1'); var r=mo.takeRecords(); \
4042             r.length",
4043            "1",
4044        ),
4045        // `characterDataOldValue`のみ指定(`characterData`省略)でも同様。
4046        (
4047            "var el=document.createElement('div'); el.textContent='hi'; \
4048             var mo=new MutationObserver(function(){}); \
4049             mo.observe(el,{characterDataOldValue:true, subtree:true}); \
4050             el.textContent='bye'; var r=mo.takeRecords(); \
4051             r.length+','+r[0].type+','+r[0].oldValue",
4052            "1,characterData,hi",
4053        ),
4054        // `attributes:false`を明示している場合は暗黙有効化しない(明示指定を
4055        // 尊重する。`attributes_specified.is_none()`条件の裏側の確認)。
4056        (
4057            "var el=document.createElement('div'); el.setAttribute('data-x','1'); \
4058             var mo=new MutationObserver(function(){}); \
4059             mo.observe(el,{attributes:false, attributeOldValue:true}); \
4060             el.setAttribute('data-x','2'); var r=mo.takeRecords(); \
4061             r.length",
4062            "0",
4063        ),
4064        // MutationObserver — el.innerHTML=... が childList の変化を一切通知して
4065        // いなかったバグ(appendChild/removeChild/insertBefore/replaceChild は
4066        // 既に notify_child_list を呼んでいたのに、最も頻繁に使われる innerHTML
4067        // 代入だけがこの呼び出しを欠いていた)。
4068        (
4069            "var el=document.createElement('div'); \
4070             var mo=new MutationObserver(function(){}); \
4071             mo.observe(el,{childList:true}); \
4072             el.innerHTML='<span>hi</span>'; var r=mo.takeRecords(); \
4073             r.length+','+r[0].type+','+r[0].addedNodes.length",
4074            "1,childList,1",
4075        ),
4076        // MutationObserver — 子が1件も無い要素への textContent=... 代入
4077        // (新規テキストノードを1件追加する経路)も childList を通知していなかった。
4078        (
4079            "var el=document.createElement('div'); \
4080             var mo=new MutationObserver(function(){}); \
4081             mo.observe(el,{childList:true}); \
4082             el.textContent='hi'; var r=mo.takeRecords(); \
4083             r.length+','+r[0].type+','+r[0].addedNodes.length",
4084            "1,childList,1",
4085        ),
4086        // MutationObserver — `characterData`オプションが丸ごと未対応だった
4087        // (`childList`/`attributes`は既に対応済みで`characterData`だけ抜けて
4088        // いた兄弟ギャップ。テキストノードが既に存在する要素への
4089        // `textContent`再代入は`characterData`変化として通知されるべき
4090        // ところ、対応する通知経路自体が存在せず`observe(el,
4091        // {characterData:true})`を指定してもコールバックが一切呼ばれ
4092        // なかった。2026-07-17 発見・実装)。
4093        (
4094            "var el=document.createElement('div'); el.textContent='hi'; \
4095             var mo=new MutationObserver(function(){}); \
4096             mo.observe(el,{characterData:true, subtree:true}); \
4097             el.textContent='bye'; var r=mo.takeRecords(); \
4098             r.length+','+r[0].type",
4099            "1,characterData",
4100        ),
4101        // `characterDataOldValue:true`で変更前のテキストを取得できる。
4102        (
4103            "var el=document.createElement('div'); el.textContent='hi'; \
4104             var mo=new MutationObserver(function(){}); \
4105             mo.observe(el,{characterData:true, characterDataOldValue:true, subtree:true}); \
4106             el.textContent='bye'; var r=mo.takeRecords(); \
4107             r[0].oldValue",
4108            "hi",
4109        ),
4110        // 値が実質変化していない代入(同じ文字列への再代入)は通知しない。
4111        (
4112            "var el=document.createElement('div'); el.textContent='hi'; \
4113             var mo=new MutationObserver(function(){}); \
4114             mo.observe(el,{characterData:true, subtree:true}); \
4115             el.textContent='hi'; var r=mo.takeRecords(); r.length",
4116            "0",
4117        ),
4118        // `characterData`未指定(既定false)ならテキスト変化を通知しない
4119        // (`childList:true`のみ指定している場合、テキストノードの中身が
4120        // 変わっただけでは子ノードの追加/削除ではないため無関係)。
4121        (
4122            "var el=document.createElement('div'); el.textContent='hi'; \
4123             var mo=new MutationObserver(function(){}); \
4124             mo.observe(el,{childList:true}); \
4125             el.textContent='bye'; var r=mo.takeRecords(); r.length",
4126            "0",
4127        ),
4128        // Object.defineProperty — データ記述子。
4129        ("var o={}; Object.defineProperty(o,'x',{value:42,writable:true}); o.x", "42"),
4130        // Object.defineProperty — getter。
4131        ("var o={_v:7}; Object.defineProperty(o,'v',{get:function(){return this._v*2;}}); o.v", "14"),
4132        // Object.defineProperty — setter + getter。
4133        ("var o={_x:0}; Object.defineProperty(o,'x',{get:function(){return this._x;},set:function(v){this._x=v+1;}}); o.x=9; o.x", "10"),
4134        // Object.defineProperty — setterのみ(getterなし→undefined)。
4135        ("var o={_s:''}; Object.defineProperty(o,'s',{set:function(v){this._s=v;}}); o.s='hi'; o._s", "hi"),
4136        // `__defineGetter__`/`__defineSetter__`/`__lookupGetter__`/
4137        // `__lookupSetter__`(Annex B.3.1)が丸ごと未対応だった。
4138        ("var o={_v:7}; o.__defineGetter__('v', function(){return this._v*2;}); o.v", "14"),
4139        ("var o={_x:0}; o.__defineSetter__('x', function(v){this._x=v+1;}); o.x=9; o._x", "10"),
4140        // getter/setter を別々に定義しても互いを上書きしない。
4141        (
4142            "var o={_x:1}; o.__defineGetter__('x', function(){return this._x;}); \
4143             o.__defineSetter__('x', function(v){this._x=v;}); o.x=5; o.x",
4144            "5",
4145        ),
4146        ("var o={}; o.__defineGetter__('v', function(){return 1;}); typeof o.__lookupGetter__('v')", "function"),
4147        ("var o={}; typeof o.__lookupGetter__('nope')", "undefined"),
4148        // Object.defineProperties — 複数。
4149        ("var o={}; Object.defineProperties(o,{a:{value:1},b:{value:2}}); o.a+o.b", "3"),
4150        // getter で動的値を返す。
4151        ("var o={n:0}; Object.defineProperty(o,'next',{get:function(){return ++this.n;}}); o.next; o.next; o.next", "3"),
4152        // Object.getOwnPropertyDescriptor — データ記述子。
4153        ("var o={x:5}; Object.getOwnPropertyDescriptor(o,'x').value", "5"),
4154        // Object.getOwnPropertyDescriptor — アクセサ記述子。
4155        ("var o={}; Object.defineProperty(o,'v',{get:function(){return 99;}}); typeof Object.getOwnPropertyDescriptor(o,'v').get", "function"),
4156        // Object.getOwnPropertyDescriptor — 存在しないキー→undefined。
4157        ("Object.getOwnPropertyDescriptor({x:1},'y')", "undefined"),
4158        // Object.getOwnPropertyDescriptors — 全記述子。
4159        ("var o={a:1}; Object.defineProperty(o,'b',{get:function(){return 2;}}); Object.getOwnPropertyDescriptors(o).a.value + Object.getOwnPropertyDescriptors(o).b.get()", "3"),
4160        // getter/setter リテラル構文(ES2015。Object.defineProperty 経由でのみ可能だった
4161        // アクセサ定義を、オブジェクトリテラル/クラス本体で直接書けるようにする)。
4162        // オブジェクトリテラルの getter。
4163        ("var o={_v:7, get v(){return this._v*2;}}; o.v", "14"),
4164        // オブジェクトリテラルの getter+setter。
4165        ("var o={_x:0, get x(){return this._x;}, set x(v){this._x=v+1;}}; o.x=9; o.x", "10"),
4166        // "get"/"set" という名前そのもののプロパティ/メソッドは従来通り動作する
4167        // (2トークン先読みでアクセサと誤認しないこと)。
4168        ("var o={get: 5}; o.get", "5"),
4169        ("var o={get(){return 7;}}; o.get()", "7"),
4170        // クラスの getter。
4171        (
4172            "class Circle { constructor(r){this.r=r;} get area(){return Math.round(Math.PI*this.r*this.r);} } new Circle(2).area",
4173            "13",
4174        ),
4175        // クラスの getter+setter(private フィールド風の下線プレフィックス)。
4176        (
4177            "class Box { constructor(){this._w=0;} get w(){return this._w;} set w(v){this._w=v<0?0:v;} } var b=new Box(); b.w=-5; b.w",
4178            "0",
4179        ),
4180        // static getter。
4181        (
4182            "class Config { static get version(){return '1.0';} } Config.version",
4183            "1.0",
4184        ),
4185        // Array.prototype.splice — 要素削除と返値。
4186        ("var a=[1,2,3,4]; var r=a.splice(1,2); r.join(',')+'/'+a.join(',')", "2,3/1,4"),
4187        // splice — 挿入。
4188        ("var a=[1,4]; a.splice(1,0,2,3); a.join(',')", "1,2,3,4"),
4189        // lastIndexOf。
4190        ("[1,2,3,2,1].lastIndexOf(2)", "3"),
4191        // copyWithin。
4192        ("[1,2,3,4,5].copyWithin(0,3).join(',')", "4,5,3,4,5"),
4193        // copyWithin — 負のインデックス(末尾からのオフセット)。
4194        ("[1,2,3,4,5].copyWithin(-2,0).join(',')", "1,2,3,1,2"),
4195        // copyWithin — end 引数で範囲を限定。
4196        ("[1,2,3,4,5].copyWithin(0,3,4).join(',')", "4,2,3,4,5"),
4197        // toSorted — 元配列を変更しない。
4198        ("var a=[3,1,2]; var s=a.toSorted(); a[0]+'/'+s.join(',')", "3/1,2,3"),
4199        // sort — undefined は比較関数に渡さず常に末尾に送る(SortCompare の特別扱い)。
4200        ("[undefined,3,undefined,1,2].sort((a,b)=>a-b).join(',')", "1,2,3,,"),
4201        ("[undefined,3,1,2].sort().join(',')", "1,2,3,"),
4202        ("[undefined,3,1,2].toSorted((a,b)=>a-b).join(',')", "1,2,3,"),
4203        // 配列の数値変換(ToPrimitive→ToNumber)。以前は Object 全般と同じく常に NaN だった。
4204        ("[5]*2", "10"),
4205        ("[]+1", "1"),
4206        ("['5']-2", "3"),
4207        ("[1,2]*1", "NaN"),
4208        // `Value::to_number` にも同種の Proxy 素通しバグがあり、`Array` は
4209        // 特別扱いされているのに `Proxy` で包むと常に `NaN` になっていた
4210        // (`Array.isArray`/`to_js_string` 等は既に透過性修正済みだった)。
4211        ("new Proxy([5],{})*2", "10"),
4212        ("+new Proxy([5],{})", "5"),
4213        // 緩やか等価(==)で Object(配列)と Number/String を比較。以前はどの分岐にも
4214        // 該当せず常に false になっていた。
4215        ("[5]==5", "true"),
4216        ("[5]=='5'", "true"),
4217        ("''==[]", "true"),
4218        ("[1,2]==5", "false"),
4219        // Number("0x..")/("0o..")/("0b..") 整数リテラル変換。以前は NaN 固定だった。
4220        ("Number('0x1F')", "31"),
4221        ("Number('0o17')", "15"),
4222        ("Number('0b101')", "5"),
4223        ("+'0xff'", "255"),
4224        // String.prototype.normalize — 丸ごと欠落していた。Unicode 分解/合成テーブルが無い
4225        // ため恒等変換の簡略実装だが、少なくとも呼び出せて有効な form は通ることを確認。
4226        ("'abc'.normalize()", "abc"),
4227        ("'abc'.normalize('NFD')", "abc"),
4228        ("(function(){ try { 'x'.normalize('bogus'); return 'no-throw'; } catch(e) { return 'threw'; } })()", "threw"),
4229        // 西欧言語の分音符付きラテン文字に限定した部分実装の NFC/NFD 検証
4230        ("'e\\u0301'.normalize('NFC') === '\\u00e9'", "true"),
4231        ("'\\u00e9'.normalize('NFD') === 'e\\u0301'", "true"),
4232        ("'\\u00e9'.normalize('NFC') === '\\u00e9'", "true"),
4233        // オプショナルチェイニング `a?.[b]`(computed member)。以前は Index に optional
4234        // フラグ自体が存在せず、常に通常のプロパティアクセスとして評価され null/undefined
4235        // で TypeError になっていた(`a?.b` の識別子版は動いていたが `[]` 版だけ壊れていた)。
4236        ("var a=null; a?.['x']", "undefined"),
4237        ("var a=undefined; a?.[0]", "undefined"),
4238        ("var a=[1,2,3]; a?.[1]", "2"),
4239        ("var f=null; f?.['x'](1,2)", "undefined"),
4240        // オプショナルチェイン全体の短絡伝播。以前は直近1段しか短絡せず `a?.b.c` は
4241        // `a` が null/undefined のとき後続の `.c` アクセスで TypeError になっていた。
4242        ("var a=null; a?.b.c", "undefined"),
4243        ("var a=undefined; a?.b.c.d", "undefined"),
4244        ("var a=null; a?.b[0].c", "undefined"),
4245        ("var a=null; a?.b()", "undefined"),
4246        ("var o={b:{c:5}}; o?.b.c", "5"),
4247        // for...in がプロトタイプ連鎖上の継承プロパティを列挙しないバグ。以前は自身の
4248        // プロパティのみだった。
4249        ("(function(){ function Base(){} Base.prototype.x=1; function D(){this.y=2;} D.prototype=Object.create(Base.prototype); var d=new D(); var ks=[]; for(var k in d) ks.push(k); return ks.sort().join(','); })()", "constructor,x,y"),
4250        // 自身のプロパティがプロトタイプ側の同名キーを覆い隠す(重複しない)ことも確認。
4251        ("(function(){ function Base(){} Base.prototype.x=1; function D(){this.x=99;} D.prototype=Object.create(Base.prototype); var d=new D(); var ks=[]; for(var k in d) ks.push(k); return ks.sort().join(','); })()", "constructor,x"),
4252        // `in` 演算子・for-in が accessors(getter/setter 限定プロパティ)専用マップを
4253        // 見ておらず「無い」扱いになっていたバグ。
4254        ("'x' in {get x(){return 1;}}", "true"),
4255        ("(function(){ var o={get x(){return 1;}}; var ks=[]; for(var k in o) ks.push(k); return ks.join(','); })()", "x"),
4256        // Object.keys/values/entries/assign も同じ accessors 見落としバグがあった。
4257        ("Object.keys({get x(){return 1;}}).join(',')", "x"),
4258        ("Object.values({get x(){return 42;}}).join(',')", "42"),
4259        ("JSON.stringify(Object.entries({get x(){return 7;}}))", "[[\"x\",7]]"),
4260        ("Object.assign({}, {get x(){return 9;}}).x", "9"),
4261        // JSON.stringify も同じ accessors 見落としバグがあった。
4262        ("JSON.stringify({get x(){return 5;}})", "{\"x\":5}"),
4263        // オブジェクトスプレッド {...src} も同じ accessors 見落としバグがあった。
4264        ("({...{get x(){return 3;}}}).x", "3"),
4265        ("var s={get x(){return 1;}}; var o={...s, y:2}; o.x+','+o.y", "1,2"),
4266        // Number.prototype.toExponential/toPrecision/valueOf — 丸ごと欠落していた。
4267        ("(150).toExponential(2)", "1.50e+2"),
4268        ("(0.0012345).toExponential(3)", "1.235e-3"),
4269        ("(123.456).toPrecision(5)", "123.46"),
4270        ("(0.0001234).toPrecision(2)", "0.00012"),
4271        ("(123456).toPrecision(2)", "1.2e+5"),
4272        ("(5).valueOf()", "5"),
4273        // `Number.prototype.toFixed(fractionDigits)` は仕様上 `fractionDigits` が
4274        // 0〜100 の範囲外なら RangeError を投げる必要があるが、以前は範囲外/非有限値を
4275        // 黙って0にクランプするだけで上限チェックが無く、`(1).toFixed(500)` のような
4276        // 呼び出しが `10^500`(Infinity)経由で壊れた出力になり得た。
4277        ("(1).toFixed(0)", "1"),
4278        ("(1.5).toFixed(100).length", "102"),
4279        (
4280            "try { (1).toFixed(-1); 'no-throw' } catch(e) { 'threw' }",
4281            "threw",
4282        ),
4283        (
4284            "try { (1).toFixed(101); 'no-throw' } catch(e) { 'threw' }",
4285            "threw",
4286        ),
4287        ("(NaN).toFixed(2)", "NaN"),
4288        ("(Infinity).toFixed(2)", "Infinity"),
4289        // Array.prototype.toLocaleString / Number.prototype.toLocaleString — 丸ごと欠落。
4290        ("[1,2,3].toLocaleString()", "1,2,3"),
4291        ("[1,null,undefined,2].toLocaleString()", "1,,,2"),
4292        // Number.prototype.toLocaleString は `toString()` の別名でしかなく、実際のブラウザなら
4293        // `Intl` 非搭載でも入る3桁区切りのカンマが欠落していた(2026-07-09に修正)。
4294        ("(1234.5).toLocaleString()", "1,234.5"),
4295        ("(1234567).toLocaleString()", "1,234,567"),
4296        ("(123).toLocaleString()", "123"),
4297        ("(-1234567.89).toLocaleString()", "-1,234,567.89"),
4298        // `Intl` グローバルが丸ごと未対応だった。ロケール/オプション引数は無視するが、
4299        // `new Intl.NumberFormat().format(n)` という最頻出イディオムだけは救う最小実装。
4300        ("new Intl.NumberFormat().format(1234567)", "1,234,567"),
4301        ("new Intl.NumberFormat('en-US').format(-1234.5)", "-1,234.5"),
4302        ("typeof Intl.NumberFormat().format", "function"),
4303        ("(0).toLocaleString()", "0"),
4304        ("(NaN).toLocaleString()", "NaN"),
4305        ("(Infinity).toLocaleString()", "Infinity"),
4306        // Boolean プリミティブへのプロパティアクセスが一律 undefined になり、メソッド呼出
4307        // 自体が丸ごと欠落していたバグ(プリミティブ中 Boolean だけメソッドが皆無だった)。
4308        ("true.toString()", "true"),
4309        ("false.toString()", "false"),
4310        ("true.valueOf()", "true"),
4311        ("(false).valueOf() === false", "true"),
4312        // 関数の name/length が丸ごと欠落していたバグ。
4313        ("function foo(a,b){} foo.name", "foo"),
4314        ("function foo(a,b){} foo.length", "2"),
4315        ("function foo(a,b=1,...c){} foo.length", "1"),
4316        ("(function(){}).length", "0"),
4317        ("Math.max.length", "0"),
4318        // `Function.prototype.bind()` が返す関数の `.name`/`.length` が丸ごと未対応で
4319        // 常に `undefined` になっていた。
4320        // 【2026-09-26】`Function.prototype` が存在しなかった(OpenStreetMap 埋め込みで発見)。
4321        ("typeof Function.prototype", "object"),
4322        ("typeof Function.prototype.toString", "function"),
4323        ("function foo(){} Function.prototype.toString.call(foo).includes('foo')", "true"),
4324        ("function add(a,b){return a+b} Function.prototype.call.call(add, null, 2, 3)", "5"),
4325        ("var of = 7; of + 1", "8"),
4326        ("0x10000000000000000 === 18446744073709551616", "true"),
4327        ("function foo(a,b,c){} foo.bind(null).name", "bound foo"),
4328        ("function foo(a,b,c){} foo.bind(null).length", "3"),
4329        ("function foo(a,b,c){} foo.bind(null,1).length", "2"),
4330        ("function foo(a,b,c){} foo.bind(null,1,2,3,4).length", "0"),
4331        // 束縛済み関数の文字列化(`String(fn.bind(...))`)も専用ケースが無く
4332        // `[object Object]` に落ちるバグだった。
4333        (
4334            "function foo(){} String(foo.bind(null)).includes('native code')",
4335            "true",
4336        ),
4337        // `Object.prototype.toString.call(x)` が `this` の実際の種別を一切見ず
4338        // 常に `[object Object]` を返すバグだった(`lodash` 等で広く使われる
4339        // 型判定イディオムが配列/Map/Set/Date/RegExp/関数のいずれに対しても
4340        // 機能しなくなる、実用上かなり影響の大きいバグ)。
4341        ("Object.prototype.toString.call([1,2])", "[object Array]"),
4342        ("Object.prototype.toString.call({})", "[object Object]"),
4343        ("Object.prototype.toString.call(new Date())", "[object Date]"),
4344        ("Object.prototype.toString.call(/a/)", "[object RegExp]"),
4345        ("Object.prototype.toString.call(new Map())", "[object Map]"),
4346        ("Object.prototype.toString.call(new Set())", "[object Set]"),
4347        ("Object.prototype.toString.call(function(){})", "[object Function]"),
4348        ("Object.prototype.toString.call(null)", "[object Null]"),
4349        ("Object.prototype.toString.call(undefined)", "[object Undefined]"),
4350        // `Symbol.toStringTag`(クラスでカスタムタグを定義する仕組み)も一切考慮して
4351        // いなかった。データプロパティ/アクセサ(getter)どちらの定義方法も動くこと。
4352        (
4353            "class Foo { get [Symbol.toStringTag](){ return 'Foo'; } } Object.prototype.toString.call(new Foo())",
4354            "[object Foo]",
4355        ),
4356        (
4357            "var o={}; o['Symbol(Symbol.toStringTag)']='Bar'; Object.prototype.toString.call(o)",
4358            "[object Bar]",
4359        ),
4360        // NamedEvaluation(ES2015): `const f = function(){}`/`const f = () => {}` の
4361        // ような無名関数式を単純な識別子へ代入すると、その識別子名を `.name` として
4362        // 継承する仕様が丸ごと未対応で、常に空文字列のままだった。
4363        ("var f = function(){}; f.name", "f"),
4364        ("var g = () => {}; g.name", "g"),
4365        // 既に名前を持つ関数式(`function foo(){}`)は変数名で上書きされない。
4366        ("var h = function foo(){}; h.name", "foo"),
4367        // object literal のプロパティ値としての無名関数式も同様にキー名を継承する。
4368        ("({bar: function(){}}).bar.name", "bar"),
4369        ("({baz: () => {}}).baz.name", "baz"),
4370        // メソッド短縮記法は元々キー名を持つため、この継承の影響を受けない
4371        // (上書きではなく「無名の場合のみ設定」であることの確認)。
4372        ("({qux(){}}).qux.name", "qux"),
4373        // structuredClone(Date/RegExp) — 実データが props に無く plain object 化に落ちて
4374        // 消えてしまうバグ(Map/Set と同種)。
4375        ("var d=new Date(2020,0,1); var c=structuredClone(d); c.getTime()===d.getTime() && c!==d", "true"),
4376        ("var r=/abc/gi; var c=structuredClone(r); c.source+','+c.flags+','+(c!==r)", "abc,gi,true"),
4377        // 分割代入の rest ({...rest}) も同じ accessors 見落としバグがあった。
4378        ("var o={get x(){return 1;}, y:2}; var {y, ...rest} = o; rest.x+','+rest.y", "1,undefined"),
4379        // hasOwnProperty/Object.hasOwn が accessors と配列インデックス/length を
4380        // 見落とすバグ。
4381        ("({get x(){return 1;}}).hasOwnProperty('x')", "true"),
4382        ("[1,2,3].hasOwnProperty(0)", "true"),
4383        ("[1,2,3].hasOwnProperty('length')", "true"),
4384        ("[1,2,3].hasOwnProperty(5)", "false"),
4385        ("Object.hasOwn({get x(){return 1;}}, 'x')", "true"),
4386        ("({}).toLocaleString()", "[object Object]"),
4387        ("[{}, {}].toLocaleString()", "[object Object],[object Object]"),
4388        // Object.getOwnPropertyDescriptor(s) が配列の数値インデックス/length を見落とし
4389        // 常に undefined になるバグ。
4390        ("Object.getOwnPropertyDescriptor([1,2,3], '0').value", "1"),
4391        ("Object.getOwnPropertyDescriptor([1,2,3], 'length').value", "3"),
4392        ("Object.keys(Object.getOwnPropertyDescriptors([1,2])).sort().join(',')", "0,1,length"),
4393        // Object.defineProperty が配列の数値インデックス/length に書き込んでも
4394        // 実データ(ObjKind::Array)側に反映されないバグ。
4395        ("var a=[1,2,3]; Object.defineProperty(a,'0',{value:99}); a[0]", "99"),
4396        ("var a=[1,2]; Object.defineProperty(a,'5',{value:9}); a.length+','+a[5]", "6,9"),
4397        ("var a=[1,2,3]; Object.defineProperty(a,'length',{value:1}); a.join(',')", "1"),
4398        // Array/Map/Set の Symbol.iterator が丸ごと未登録で、明示呼出し
4399        // (`arr[Symbol.iterator]()`)が「関数ではない」になっていたバグ。for...of は
4400        // 内部高速経路のため気づかれなかった。
4401        ("[...[1,2,3][Symbol.iterator]()].join(',')", "1,2,3"),
4402        ("var m=new Map([['a',1]]); [...m[Symbol.iterator]()][0].join(',')", "a,1"),
4403        ("[...new Set([1,2])[Symbol.iterator]()].join(',')", "1,2"),
4404        // entries/keys/values が本物の Iterator(`.next()`/`Symbol.iterator` 持ち)ではなく
4405        // 単なる配列を返しており、明示的なイテレータプロトコル駆動
4406        // (`const it = arr.values(); it.next()`) が「next が存在しない」で壊れていたバグ。
4407        ("var it=[10,20].values(); it.next().value+','+it.next().value+','+it.next().done", "10,20,true"),
4408        ("var it=[1,2].entries(); it.next().value.join(',')", "0,1"),
4409        ("var it=[1,2].keys(); it.next().value+','+it.next().value", "0,1"),
4410        ("var it=new Map([['a',1]]).entries(); it.next().value.join(',')", "a,1"),
4411        ("var it=new Set([5,6]).values(); it.next().value+','+it.next().value", "5,6"),
4412        ("var it=[1].values(); it[Symbol.iterator]()===it", "true"),
4413        // String.prototype[Symbol.iterator] が丸ごと欠落していたバグ。
4414        ("[...'ab'[Symbol.iterator]()].join(',')", "a,b"),
4415        ("var it='xy'[Symbol.iterator](); it.next().value+it.next().value+it.next().done", "xytrue"),
4416        // 上記の Iterator 化に伴う回帰: Array.from/new Map/new Set が新しい軽量イテレータ
4417        // オブジェクト(entries/keys/values の戻り値)を「iterable」として認識できず
4418        // 空になっていたバグ(本イテレータ導入時に発見・即修正)。
4419        ("Array.from([10,20].values()).join(',')", "10,20"),
4420        ("Array.from([1,2].entries()).map(e=>e.join(':')).join(',')", "0:1,1:2"),
4421        ("var m=new Map(new Map([['a',1]]).entries()); m.get('a')", "1"),
4422        ("var s=new Set([1,2,2,3].values()); [...s].join(',')", "1,2,3"),
4423        // イテレータの内部実装詳細(_items/_pos)が Object.keys/JSON.stringify/spread から
4424        // 見えてしまう漏れ(make_iterator 導入時に発見・即修正)。
4425        ("Object.keys([1,2].values()).length", "0"),
4426        ("JSON.stringify([1,2].values())", "{}"),
4427        ("Object.keys({...[1,2].values()}).length", "0"),
4428        // AggregateError(ES2021)が丸ごと欠落していた。Promise.any が全滅した際に
4429        // 最後の reject 理由しか見えず、`.errors` を持つ AggregateError も
4430        // 投げられていなかった。
4431        ("new AggregateError([1,2],'m').errors.join(',')", "1,2"),
4432        ("new AggregateError([],'m') instanceof Error", "true"),
4433        (
4434            "await Promise.any([Promise.reject('a'),Promise.reject('b')]).catch(e => e.name+':'+e.errors.join(','))",
4435            "AggregateError:a,b",
4436        ),
4437        // Date の日時文字列パース(ISO 8601)が丸ごと非対応だった(`new Date("2024-01-15")`
4438        // が Invalid Date になっていた)。
4439        ("new Date('2024-01-15').getFullYear()+'-'+(new Date('2024-01-15').getMonth()+1)+'-'+new Date('2024-01-15').getDate()", "2024-1-15"),
4440        ("Date.parse('2024-01-15T00:00:00Z')", "1705276800000"),
4441        ("Date.parse('2024-01-15T02:00:00+02:00')", "1705276800000"),
4442        // コロン無しタイムゾーンオフセット(`+0900` 形式。ISO 8601/ECMA-262 双方で正当)
4443        ("Date.parse('2024-01-15T09:00:00+0900')", "1705276800000"),
4444        ("Date.parse('2024-01-15T00:00:00+00')", "1705276800000"),
4445        ("new Date('2024-01-15T00:00:00.500Z').getTime()", "1705276800500"),
4446        ("isNaN(Date.parse('not a date'))", "true"),
4447        // RFC 2822 形式(HTTP日付ヘッダ等の定番書式)が丸ごと非対応で、ISO 8601以外の
4448        // 文字列は常に NaN になっていたバグ。曜日名の有無・タイムゾーンオフセット双方を
4449        // 確認する。
4450        ("Date.parse('Mon, 15 Jan 2024 00:00:00 GMT')", "1705276800000"),
4451        ("Date.parse('15 Jan 2024 00:00:00 GMT')", "1705276800000"),
4452        ("Date.parse('Mon, 15 Jan 2024 02:00:00 +0200')", "1705276800000"),
4453        // `Date.now`/`Date.parse` は登録済みだったが `Date.UTC` が丸ごと未対応だった。
4454        ("Date.UTC(2024, 0, 15) === Date.parse('2024-01-15T00:00:00Z')", "true"),
4455        ("new Date(Date.UTC(2024,0,15,10,30,0)).getUTCHours()", "10"),
4456        // 省略引数の既定値(day のみ1、他は0)。
4457        ("new Date(Date.UTC(2024,0)).getDate()", "1"),
4458        ("new Date('Mon, 15 Jan 2024 00:00:00 GMT').getFullYear()", "2024"),
4459        // RegExp 名前付きキャプチャグループ (?<name>...)(ES2018)が丸ごと非対応で
4460        // `(?<`(lookbehind と誤認され `:` フォールバックと同じ経路)を通ると壊れた
4461        // パース結果になり、`match.groups` は常に undefined だった。
4462        ("'2024-01-15'.match(/(?<year>\\d+)-(?<month>\\d+)-(?<day>\\d+)/).groups.year", "2024"),
4463        ("'2024-01-15'.match(/(?<year>\\d+)-(?<month>\\d+)-(?<day>\\d+)/).groups.month", "01"),
4464        ("/(?<a>x)(y)/.exec('xy')[2]", "y"),
4465        ("/(x)/.exec('x').groups", "undefined"),
4466        ("[...'a1 b2'.matchAll(/(?<letter>[a-z])(?<num>\\d)/g)].map(m=>m.groups.letter+m.groups.num).join(',')", "a1,b2"),
4467        // replace() の $<name> 置換パターン(ES2018)が丸ごと非対応だった。合わせて
4468        // $`(一致より前)/$'(一致より後)も未対応だった。
4469        ("'2024-01-15'.replace(/(?<y>\\d+)-(?<m>\\d+)-(?<d>\\d+)/, '$<d>/$<m>/$<y>')", "15/01/2024"),
4470        ("'abc'.replace(/b/, \"[$`|$']\")", "a[a|c]c"),
4471        ("'x'.replace(/(?<a>x)/, (m,a,off,str,groups)=>groups.a+'!')", "x!"),
4472        // RegExp の \uXXXX/\u{X...}/\xXX エスケープが丸ごと未対応で、`A` が文字コード
4473        // 指定ではなく `u0041`(5文字のリテラル)として誤解釈されていたバグ。
4474        ("/\\u0041/.test('A')", "true"),
4475        ("/\\u{1F600}/u.test('\\u{1F600}')", "true"),
4476        ("/\\x41/.test('A')", "true"),
4477        ("'A1'.match(/\\u0041\\d/)[0]", "A1"),
4478        // 文字クラス内の \uXXXX/\xXX(範囲の端点含む)も同じバグがあった。
4479        ("/[\\u0041-\\u005A]/.test('M')", "true"),
4480        ("/[\\x41-\\x5A]/.test('Z')", "true"),
4481        ("/[\\u0041-\\u005A]/.test('m')", "false"),
4482        // String.prototype.split(regexp) がキャプチャグループを結果に含めないバグ
4483        // (仕様上、区切りに使った正規表現のキャプチャは結果配列に挿入される)。
4484        ("'a1b2c'.split(/(\\d)/).join('|')", "a|1|b|2|c"),
4485        ("'axb'.split(/(x)|(y)/).join(',')", "a,x,,b"),
4486        ("'abc'.split(/b/).join(',')", "a,c"),
4487        // クラス/オブジェクトリテラルの async/generator メソッド短縮記法が丸ごと
4488        // 未認識で、`*`/`async` に遭遇するとキー解析がその場で失敗し、メンバ自体が
4489        // 消えるだけでなく後続メンバの解析まで壊れる重大バグだった。
4490        ("class C { *gen(){ yield 1; yield 2; } } [...new C().gen()].join(',')", "1,2"),
4491        ("await (new (class C { async f(){ return 42; } })()).f()", "42"),
4492        ("class C { *gen(){ yield 'a'; } method(){ return 'm'; } } var c=new C(); [...c.gen()].join(',')+c.method()", "am"),
4493        ("[...{ *gen(){ yield 1; yield 2; } }.gen()].join(',')", "1,2"),
4494        ("await { async f(){ return 'ok'; } }.f()", "ok"),
4495        // static + async/generator の組み合わせ、および算出メソッド名との組み合わせ。
4496        ("await (class C { static async f(){ return 7; } }).f()", "7"),
4497        ("class C { static *gen(){ yield 9; } } [...C.gen()].join(',')", "9"),
4498        ("class C { *[Symbol.iterator](){ yield 3; } } [...new C()].join(',')", "3"),
4499        ("({ async *gen(){}, method(){ return 'ok'; } }).method()", "ok"),
4500        // 分割代入の算出プロパティ名 `{[expr]: target}` が丸ごと未認識で、`[` に遭遇すると
4501        // パターン解析全体が壊れる(後続プロパティも巻き添え)バグだった。
4502        ("var k='x'; var {[k]: v} = {x: 42}; v", "42"),
4503        ("var k='a'; var {[k]: v, y} = {a: 1, y: 2}; v+','+y", "1,2"),
4504        ("var k='n'; function f({[k]: val}){ return val; } f({n: 7})", "7"),
4505        // 直前の async/generator 修飾子検出の実装ミス("async"/"*" を常に修飾子として
4506        // 消費してしまい、"async" という名前の通常プロパティ/フィールド自体が壊れる
4507        // 新規回帰)を発見・即修正。
4508        ("({ async: 1, b: 2 }).async + ({ async: 1, b: 2 }).b", "3"),
4509        ("class C { async = 5; b = 6; } var c = new C(); c.async + c.b", "11"),
4510        // "static" の修飾子誤認識も同じバグ族(`static`/`async`/`*` 全てに共通の
4511        // 「次が `(` でなければ無条件で修飾子」という緩すぎる判定)だった。
4512        ("class C { static = 5; b = 6; } var c = new C(); c.static + c.b", "11"),
4513        ("class C { static x = 1; static method(){ return 2; } } C.x + C.method()", "3"),
4514        // get/set の算出プロパティ名 `get [expr](){}` がアクセサとして認識されず、
4515        // "get"/"set" という名前の孤立フィールド+無関係なメソッドに分解される
4516        // バグだった。
4517        ("var k='x'; var o={ get [k](){ return 42; } }; o.x", "42"),
4518        ("var k='y'; var o={y:0, set [k](v){ this._y=v; } }; o.y=5; o._y", "5"),
4519        ("var k='z'; class C { get [k](){ return 9; } } new C().z", "9"),
4520        // for(let i=...) の反復ごとの束縛(per-iteration binding)が丸ごと未対応で、
4521        // ループ内で作ったクロージャが全て同じ最終値を捕捉してしまうバグだった
4522        // (`var` は対象外で仕様どおり単一束縛のまま)。
4523        ("var a=[]; for(let i=0;i<3;i++){ a.push(()=>i); } a.map(f=>f()).join(',')", "0,1,2"),
4524        ("var a=[]; for(var i=0;i<3;i++){ a.push(()=>i); } a.map(f=>f()).join(',')", "3,3,3"),
4525        ("var a=[]; for(let i=0;i<3;i++){ if(i===1) continue; a.push(()=>i); } a.map(f=>f()).join(',')", "0,2"),
4526        // for-of/for-in の let/const も同じ反復ごとの束縛が必要(var は対象外)。
4527        ("var a=[]; for(const x of [1,2,3]){ a.push(()=>x); } a.map(f=>f()).join(',')", "1,2,3"),
4528        ("var a=[]; for(var x of [1,2,3]){ a.push(()=>x); } a.map(f=>f()).join(',')", "3,3,3"),
4529        ("var a=[]; for(let k in {a:1,b:2}){ a.push(()=>k); } a.map(f=>f()).join(',')", "a,b"),
4530        // 配列プロトタイプメソッドのほぼ全てが使う共通ヘルパ this_items/this_objref が
4531        // Proxy を素通しできず、Proxy でラップした配列へメソッド呼出し
4532        // (`.map()`/`.push()` 等)すると常に空扱いになるバグだった(for...of/スプレッドは
4533        // 別経路で既に対応済みだったが、メソッド呼出し経由は漏れていた)。
4534        ("new Proxy([1,2,3],{}).map(x=>x*2).join(',')", "2,4,6"),
4535        ("var p=new Proxy([1,2],{}); p.push(3); p.join(',')", "1,2,3"),
4536        ("new Proxy([5,6,7],{}).indexOf(6)", "1"),
4537        // `String(proxy)`/文字列連結/テンプレートリテラル補間が使う
4538        // `Value::to_js_string` にも同種の Proxy 素通しバグがあり、`ObjKind::Proxy`
4539        // 専用ケースが無く汎用の "[object Object]" に落ちていた。
4540        ("String(new Proxy([1,2,3], {}))", "1,2,3"),
4541        ("'' + new Proxy([7,8], {})", "7,8"),
4542        // Map/Set の全メソッドが使う with_map/with_set にも同じ Proxy 素通しバグがあった。
4543        ("var p=new Proxy(new Map([['a',1]]),{}); p.get('a')", "1"),
4544        ("var p=new Proxy(new Set([1,2]),{}); p.add(3); [...p].join(',')", "1,2,3"),
4545        // WeakSet / Promise / Generator の共通ヘルパにも同じ Proxy 素通しバグがあった。
4546        ("var o={}; var p=new Proxy(new WeakSet([o]),{}); p.has(o)", "true"),
4547        ("await new Proxy(Promise.resolve(5), {}).then(x=>x*2)", "10"),
4548        ("function* g(){ yield 1; yield 2; } var p=new Proxy(g(),{}); [...p].join(',')", "1,2"),
4549        // String.fromCharCode/fromCodePoint が同じ実装を共有しており、fromCharCode の
4550        // サロゲートペア結合(絵文字1文字を表す ES1 以来の定番イディオム)が壊れていた。
4551        ("String.fromCharCode(72,105)", "Hi"),
4552        ("String.fromCharCode(0xD83D,0xDE00)", "\u{1F600}"),
4553        ("String.fromCodePoint(0x1F600)", "\u{1F600}"),
4554        ("String.fromCodePoint(72,105)", "Hi"),
4555        // Object.create(proto, propertiesObject) の第2引数が丸ごと無視されていたバグ。
4556        ("Object.create(null, {x:{value:5}}).x", "5"),
4557        ("var o=Object.create(null, {y:{value:9}, z:{value:1}}); o.y + o.z", "10"),
4558        // JSON.stringify の toJSON() フックが Date 専用の特別扱いのみで、任意のオブジェクトに
4559        // 定義できる汎用フックとして機能していなかった。
4560        ("JSON.stringify({toJSON(){ return 42; }})", "42"),
4561        ("JSON.stringify({a: {toJSON(){ return 'x'; }}})", "{\"a\":\"x\"}"),
4562        ("JSON.stringify([{toJSON(){ return 1; }}, 2])", "[1,2]"),
4563        // 数値セパレータ `_`(ES2021)が丸ごと未対応で、`1_000` の `_` の時点で数値
4564        // リテラルが打ち切られ、残りが別トークン(識別子)として構文解析を静かに
4565        // 壊すバグだった。
4566        ("1_000_000", "1000000"),
4567        ("0x1_000", "4096"),
4568        ("1_000.5_5", "1000.55"),
4569        // `0o`(8進)/`0b`(2進)リテラルがソースコード上で丸ごと未対応で、`0` だけ
4570        // 数値トークンとして打ち切られ `o17`/`b101` が別の識別子トークンになり
4571        // 構文解析が静かに壊れるバグだった(16進 `0x` は既存で対応済みだったのと非対称)。
4572        ("0o17", "15"),
4573        ("0b101", "5"),
4574        ("0o17 + 0b101", "20"),
4575        ("0b101n + 2n", "7"),
4576        // 文字列リテラル中の行継続(`\` の直後の改行)が仕様上「何も追加しない」はずが、
4577        // 改行文字そのものを値に混入させていたバグ。
4578        ("'abc\\\ndef'", "abcdef"),
4579        ("'abc\\\r\ndef'.length", "6"),
4580        // toReversed — 元配列を変更しない。
4581        ("var a=[1,2,3]; var r=a.toReversed(); a[0]+'/'+r.join(',')", "1/3,2,1"),
4582        // toSpliced — 元配列を変更しない。
4583        ("var a=[1,2,3]; var s=a.toSpliced(1,1,9); a.join(',')+'/'+s.join(',')", "1,2,3/1,9,3"),
4584        // toSpliced — deleteCount 省略時は末尾まで削除するが、明示的な `undefined` は
4585        // 仕様上 `ToIntegerOrInfinity(undefined)` = 0(省略とは異なる)扱いになる。
4586        ("[1,2,3].toSpliced(1).join(',')", "1"),
4587        ("[1,2,3].toSpliced(1, undefined).join(',')", "1,2,3"),
4588        // Array.prototype.concat — `Symbol.isConcatSpreadable` が丸ごと未対応で、
4589        // 常に `ObjKind::Array` かどうかだけで展開の有無を決めていたバグ。
4590        // 配列に `[Symbol.isConcatSpreadable]=false` を明示すると展開されず単一要素になる。
4591        (
4592            "var a=[1,2]; a[Symbol.isConcatSpreadable]=false; [0].concat(a).length",
4593            "2",
4594        ),
4595        // 非配列の array-like に `Symbol.isConcatSpreadable=true` を明示すると
4596        // `length`+添字プロパティから展開される。
4597        (
4598            "var o={0:'x',1:'y',length:2}; o[Symbol.isConcatSpreadable]=true; [0].concat(o).join(',')",
4599            "0,x,y",
4600        ),
4601        // フラグ未指定時は従来どおり配列のみ自動展開、非配列は単一要素のまま。
4602        ("[1].concat([2,3],4).join(',')", "1,2,3,4"),
4603        // `Array.prototype.join` は仕様上ジェネリックメソッド(`this` の `length` +
4604        // 添字プロパティだけを見る)だが、以前は `ObjKind::Array` 以外を無条件で空扱いし
4605        // 配列インスタンスから取り出した `join` を array-like へ `.call()` しても
4606        // 常に `""` になっていた(この処理系はメソッド解決を `ObjKind::Array` の
4607        // プロパティ取得時にのみ行う簡略実装のため、実際に呼び出し可能な関数値を
4608        // 得るには `Array.prototype.join` ではなく実配列インスタンスから取り出す)。
4609        (
4610            "var f=[].join; f.call({0:'a',1:'b',length:2}, '-')",
4611            "a-b",
4612        ),
4613        // `push`/`pop`/`shift`/`unshift` は仕様上ミューテートするメソッドの中でも
4614        // 特にジェネリック(`length` を介して任意の array-like を書き換える)
4615        // ことが要求される定番のイディオム(`Array.prototype.push.call(arguments, x)` 等)。
4616        (
4617            "var o={length:0}; var f=[].push; f.call(o,'a','b')+','+o.length+','+o[0]+','+o[1]",
4618            "2,2,a,b",
4619        ),
4620        (
4621            "var o={0:'a',1:'b',length:2}; var f=[].pop; f.call(o)+','+o.length",
4622            "b,1",
4623        ),
4624        (
4625            "var o={0:'a',1:'b',length:2}; var f=[].shift; f.call(o)+','+o.length+','+o[0]",
4626            "a,1,b",
4627        ),
4628        (
4629            "var o={0:'a',length:1}; var f=[].unshift; f.call(o,'x','y')+','+o.length+','+o[0]+','+o[1]+','+o[2]",
4630            "3,3,x,y,a",
4631        ),
4632        // 通常の配列に対する挙動には回帰なし。
4633        ("[1,2,3].join('-')", "1-2-3"),
4634        // `arguments.callee`(Annex B。無名関数の自己再帰呼び出しイディオム)が
4635        // 丸ごと未対応だった。
4636        (
4637            "var fact = function(n){ return n <= 1 ? 1 : n * arguments.callee(n - 1); }; fact(5)",
4638            "120",
4639        ),
4640        ("var g = function(){ return arguments.callee === g; }; g()", "true"),
4641        // 名前付き関数式の自己参照束縛(`(function f(){ ...f... })()` で `f` を
4642        // 本体内から参照できる仕様どおりの挙動)が丸ごと未対応だった。
4643        ("(function f(n){ return n<=1?1:n*f(n-1); })(5)", "120"),
4644        ("(function f(){ return typeof f; })()", "function"),
4645        // 外側スコープからは関数式自身の名前が見えない(仕様どおり)。
4646        ("(function f(){})(); typeof f", "undefined"),
4647        // `Array.prototype.slice` も同じジェネリックメソッド未対応バグがあった
4648        // (`join`/`concat` と同型。`this_items` が `ObjKind::Array` 以外を無条件で
4649        // 空 `Vec` 扱いしていたため array-like への適用が常に `[]` になっていた)。
4650        (
4651            "var s=[].slice; s.call({0:'a',1:'b',length:2}, 0, 1).join(',')",
4652            "a",
4653        ),
4654        (
4655            "var s=[].slice; s.call({0:'a',1:'b',2:'c',length:3}, 1).join(',')",
4656            "b,c",
4657        ),
4658        // 通常の配列に対する挙動には回帰なし。
4659        ("[1,2,3,4].slice(1,3).join(',')", "2,3"),
4660        // `forEach`/`map`/`filter`/`indexOf`/`includes` にも同型のジェネリックメソッド
4661        // 未対応バグがあった(`join`/`slice`/`concat` で修正済みの続き)。array-like
4662        // (`arguments`/`NodeList` 等への `.call()` の定番イディオム)への適用が
4663        // 「何も反復しない/常に見つからない」結果になっていた。
4664        (
4665            "var out=''; var fe=[].forEach; fe.call({0:'a',1:'b',length:2}, function(v){ out+=v }); out",
4666            "ab",
4667        ),
4668        (
4669            "var m=[].map; m.call({0:1,1:2,length:2}, function(v){ return v*2 }).join(',')",
4670            "2,4",
4671        ),
4672        (
4673            "var fl=[].filter; fl.call({0:1,1:2,2:3,length:3}, function(v){ return v>1 }).join(',')",
4674            "2,3",
4675        ),
4676        (
4677            "var io=[].indexOf; io.call({0:'a',1:'b',length:2}, 'b')",
4678            "1",
4679        ),
4680        (
4681            "var inc=[].includes; inc.call({0:'a',1:'b',length:2}, 'b')",
4682            "true",
4683        ),
4684        // `reduce`/`find`/`findIndex`/`some`/`every` にも同型のジェネリックメソッド
4685        // 未対応バグがあった(`forEach`/`map`/`filter`/`indexOf`/`includes` の続き)。
4686        (
4687            "var r=[].reduce; r.call({0:1,1:2,2:3,length:3}, function(a,v){ return a+v }, 0)",
4688            "6",
4689        ),
4690        (
4691            "var fd=[].find; fd.call({0:1,1:2,2:3,length:3}, function(v){ return v>1 })",
4692            "2",
4693        ),
4694        (
4695            "var fi=[].findIndex; fi.call({0:1,1:2,2:3,length:3}, function(v){ return v>1 })",
4696            "1",
4697        ),
4698        (
4699            "var sm=[].some; sm.call({0:1,1:2,length:2}, function(v){ return v>1 })",
4700            "true",
4701        ),
4702        (
4703            "var ev=[].every; ev.call({0:1,1:2,length:2}, function(v){ return v>0 })",
4704            "true",
4705        ),
4706        (
4707            "var at=[].at; at.call({0:1,1:2,length:2}, -1)",
4708            "2",
4709        ),
4710        (
4711            "var fl=[].findLast; fl.call({0:1,1:2,length:2}, function(v){ return v<2 })",
4712            "1",
4713        ),
4714        (
4715            "var fli=[].findLastIndex; fli.call({0:1,1:2,length:2}, function(v){ return v<2 })",
4716            "0",
4717        ),
4718        (
4719            "var fm=[].flatMap; fm.call({0:1,1:2,length:2}, function(v){ return v*2 }).join(',')",
4720            "2,4",
4721        ),
4722        (
4723            "var rr=[].reduceRight; rr.call({0:1,1:2,length:2}, function(a,v){ return a+v })",
4724            "3",
4725        ),
4726        (
4727            "var lio=[].lastIndexOf; lio.call({0:1,1:1,length:2}, 1)",
4728            "1",
4729        ),
4730        (
4731            "var fla=[].flat; fla.call({0:1,1:[2,3],length:2}).join(',')",
4732            "1,2,3",
4733        ),
4734        (
4735            "var en=[].entries; var itr=en.call({0:1,1:2,length:2}); itr.next().value.join(',')",
4736            "0,1",
4737        ),
4738        (
4739            "var ky=[].keys; var itr=ky.call({0:1,1:2,length:2}); itr.next().value+','+itr.next().value",
4740            "0,1",
4741        ),
4742        (
4743            "var vl=[].values; var itr=vl.call({0:1,1:2,length:2}); itr.next().value+','+itr.next().value",
4744            "1,2",
4745        ),
4746        (
4747            "var tls=[].toLocaleString; tls.call({0:1,1:2,length:2})",
4748            "1,2",
4749        ),
4750        (
4751            "var o={0:1,1:2,length:2}; o[Symbol.isConcatSpreadable]=true; [].concat.call(o, 3).join(',')",
4752            "1,2,3",
4753        ),
4754        (
4755            "var o={0:1,1:2,length:2}; [].concat.call(o, 3).length",
4756            "2",
4757        ),
4758        (
4759            "var ts=[].toSorted; ts.call({0:3,1:1,length:2}).join(',')",
4760            "1,3",
4761        ),
4762        (
4763            "var tr=[].toReversed; tr.call({0:1,1:2,length:2}).join(',')",
4764            "2,1",
4765        ),
4766        (
4767            "var tsp=[].toSpliced; tsp.call({0:1,1:2,length:2}, 1, 0, 9).join(',')",
4768            "1,9,2",
4769        ),
4770        (
4771            "var w=[].with; w.call({0:1,1:2,length:2}, 0, 9).join(',')",
4772            "9,2",
4773        ),
4774        // 通常の配列に対する挙動には回帰なし。
4775        ("[1,2,3].map(function(v){return v*2}).join(',')", "2,4,6"),
4776        // with — 元配列を変更しない。
4777        ("var a=[1,2,3]; var b=a.with(1,9); a[1]+'/'+b[1]", "2/9"),
4778        // TypeError コンストラクタ。
4779        ("(function(){ try { throw new TypeError('bad'); } catch(e) { return e.name+':'+e.message; } })()", "TypeError:bad"),
4780        // RangeError コンストラクタ。
4781        ("(function(){ try { throw new RangeError('oor'); } catch(e) { return e.name; } })()", "RangeError"),
4782        // FinalizationRegistry — register は no-op (object が取れる)。
4783        ("typeof new FinalizationRegistry(function(){}).register", "function"),
4784        // requestIdleCallback — コールバックが呼ばれ、didTimeout が false。
4785        ("var ok=false; requestIdleCallback(function(d){ ok = !d.didTimeout && d.timeRemaining()>0; }); ok", "true"),
4786        // Object.hasOwn — あり/なし。
4787        ("Object.hasOwn({x:1},'x')+','+Object.hasOwn({x:1},'y')", "true,false"),
4788        // Object.groupBy — 偶奇グループ。
4789        ("var g=Object.groupBy([1,2,3,4],function(n){return n%2===0?'e':'o'}); g.e.join(',')+'/'+g.o.join(',')", "2,4/1,3"),
4790        // Map.groupBy(ES2024)— Object.groupBy と違いキーを文字列化せず任意の値のまま使える。
4791        ("var m=Map.groupBy([1,2,3,4],function(n){return n%2===0?'e':'o'}); m.get('e').join(',')+'/'+m.get('o').join(',')", "2,4/1,3"),
4792        (
4793            "var kEven={}; var kOdd={}; var m=Map.groupBy([1,2,3],function(n){return n%2===0?kEven:kOdd}); m.get(kEven).join(',')+'/'+m.get(kOdd).join(',')",
4794            "2/1,3",
4795        ),
4796        ("Map.groupBy([1,2,3],function(n){return n}).size", "3"),
4797        // Object.groupBy/Map.groupBy は仕様上任意の iterable を受け付ける(配列限定ではない)。
4798        // 以前は実配列以外は無条件で空扱いになり Set/Map/ジェネレータが常に空グループになるバグだった。
4799        (
4800            "var g=Object.groupBy(new Set([1,2,3,4]),function(n){return n%2===0?'e':'o'}); g.e.join(',')+'/'+g.o.join(',')",
4801            "2,4/1,3",
4802        ),
4803        (
4804            "function* gen(){yield 1;yield 2;yield 3;} var g=Object.groupBy(gen(),function(n){return n%2===0?'e':'o'}); g.e.join(',')+'/'+g.o.join(',')",
4805            "2/1,3",
4806        ),
4807        (
4808            "var m=Map.groupBy(new Set([1,2,3,4]),function(n){return n%2===0?'e':'o'}); m.get('e').join(',')+'/'+m.get('o').join(',')",
4809            "2,4/1,3",
4810        ),
4811        // Map/Set/WeakMap/WeakSet コンストラクタ・Promise.all 系・Object.fromEntries も
4812        // 同じく `Interp` 不要の純関数 `iterable_values` を使っており、Generator を渡すと
4813        // 常に空になる同型のバグがあった(`it.iter_to_vec` に統一して解消)。
4814        (
4815            "function* gen(){yield [1,'a'];yield [2,'b'];} var m=new Map(gen()); m.get(1)+','+m.get(2)",
4816            "a,b",
4817        ),
4818        (
4819            "function* gen(){yield 1;yield 2;yield 2;} [...new Set(gen())].join(',')",
4820            "1,2",
4821        ),
4822        (
4823            "function* gen(){yield ['a',1];} Object.fromEntries(gen()).a",
4824            "1",
4825        ),
4826        (
4827            "function* gen(){yield Promise.resolve(1);yield Promise.resolve(2);} await Promise.all(gen()).then(a => a.join(','))",
4828            "1,2",
4829        ),
4830        // Uint8Array — 長さ指定・要素代入・length。
4831        ("var a=new Uint8Array(3); a[0]=10; a[1]=20; a.length+','+a[0]+','+a[1]", "3,10,20"),
4832        // Uint8Array — 配列/イテラブルから構築。
4833        ("Array.from(new Uint8Array([1,2,3])).join(',')", "1,2,3"),
4834        // Uint8Array.from — 静的メソッド。
4835        ("Uint8Array.from([4,5,6]).join(',')", "4,5,6"),
4836        // Uint8Array コンストラクタは Generator も受け付けるべき(仕様上どの iterable でも可)。
4837        (
4838            "function* gen(){yield 1;yield 2;yield 3;} new Uint8Array(gen()).join(',')",
4839            "1,2,3",
4840        ),
4841        // AggregateError も同様に Generator を errors として受け付ける。
4842        (
4843            "function* gen(){yield 'a';yield 'b';} new AggregateError(gen(), 'msg').errors.join(',')",
4844            "a,b",
4845        ),
4846        // Uint8Array — 値のラップ(256 は mod 256 で 0、-1 は 255、300 は 44)。
4847        ("var a=new Uint8Array([256,-1,300]); a.join(',')", "0,255,44"),
4848        // TypedArray コンストラクタに負の length を渡すと仕様上 RangeError(`ToIndex` が
4849        // `integer >= 0` を要求)だが、以前は `.max(0.0)` で黙って0にクランプしていた。
4850        (
4851            "try { new Uint8Array(-1); 'no-throw' } catch(e) { 'threw' }",
4852            "threw",
4853        ),
4854        (
4855            "try { new Float64Array(-3); 'no-throw' } catch(e) { 'threw' }",
4856            "threw",
4857        ),
4858        // `typedArray.set(array, offset)`は仕様上`offset + array.length`が
4859        // 対象の長さを超えるとRangeErrorだが、以前は範囲外の書き込みを
4860        // 黙って無視するだけで、`ta.set([1,2,3], hugeOffset)`が例外にならず
4861        // 無害なno-opとして成立してしまっていた(TypedArrayコンストラクタの
4862        // 負lengthバグと同じ「範囲外を黙って受け流す」パターン)。
4863        (
4864            "try { new Uint8Array(3).set([1,2,3], 5); 'no-throw' } catch(e) { 'threw' }",
4865            "threw",
4866        ),
4867        (
4868            "try { new Uint8Array(3).set([1,2], 2); 'no-throw' } catch(e) { 'threw' }",
4869            "threw",
4870        ),
4871        (
4872            // 境界ちょうど(offset + length === target.length)は範囲内なので例外にならない。
4873            "var a=new Uint8Array(3); a.set([9,9], 1); a.join(',')",
4874            "0,9,9",
4875        ),
4876        // Uint8Array.set — offset 付き上書き。
4877        ("var a=new Uint8Array(4); a.set([9,9],1); a.join(',')", "0,9,9,0"),
4878        // Uint8Array.set — 仕様上 array-like(非配列プレーンオブジェクト)も受け付けるべき。
4879        (
4880            "var a=new Uint8Array(3); a.set({0:5,1:6,length:2}); a.join(',')",
4881            "5,6,0",
4882        ),
4883        // Uint8Array.slice — 独立コピーを返す。
4884        ("var a=new Uint8Array([1,2,3,4,5]); var s=a.slice(1,3); s.join(',')+'/'+a.length", "2,3/5"),
4885        // Uint8Array.fill — 破壊的に埋めて this を返す。
4886        ("var a=new Uint8Array(4); a.fill(7,1,3); a.join(',')", "0,7,7,0"),
4887        // Uint8Array — byteLength / BYTES_PER_ELEMENT / buffer.byteLength。
4888        ("var a=new Uint8Array(5); a.byteLength+','+a.BYTES_PER_ELEMENT+','+a.buffer.byteLength", "5,1,5"),
4889        // ArrayBuffer — byteLength と、そこから Uint8Array を構築。
4890        ("var b=new ArrayBuffer(8); new Uint8Array(b).length+','+b.byteLength", "8,8"),
4891        // ArrayBuffer.prototype.resize/transfer/resizable/maxByteLength(ES2024)。
4892        // 以前は丸ごと未対応で、非 resizable なバッファしか作れなかった。
4893        (
4894            "var b=new ArrayBuffer(4,{maxByteLength:16}); b.resizable+','+b.maxByteLength+','+b.byteLength",
4895            "true,16,4",
4896        ),
4897        ("var b=new ArrayBuffer(4); b.resizable+','+b.maxByteLength", "false,4"),
4898        // resize は byteLength をその場で伸縮させる(新規領域はゼロ埋め)。
4899        (
4900            "var b=new ArrayBuffer(4,{maxByteLength:16}); b.resize(8); var u=new Uint8Array(b); b.byteLength+','+u.length",
4901            "8,8",
4902        ),
4903        // maxByteLength を超える resize は RangeError 相当の例外。
4904        (
4905            "var b=new ArrayBuffer(4,{maxByteLength:8}); try { b.resize(9); 'no throw'; } catch(e) { 'threw'; }",
4906            "threw",
4907        ),
4908        // 非 resizable なバッファへの resize は TypeError 相当の例外。
4909        (
4910            "var b=new ArrayBuffer(4); try { b.resize(2); 'no throw'; } catch(e) { 'threw'; }",
4911            "threw",
4912        ),
4913        // transfer は同じ byteLength の新バッファを返し、元のバッファを detach する(byteLength→0)。
4914        // 注: この処理系の ArrayBuffer/TypedArray は実メモリを共有しない独立コピー簡易実装
4915        // (DataView 等と同じ既存の割り切り)のため、TypedArray 経由で書いた値の引き継ぎまでは検証しない。
4916        (
4917            "var b=new ArrayBuffer(4); var b2=b.transfer(); b.byteLength+','+b2.byteLength",
4918            "0,4",
4919        ),
4920        // detach 後の再 transfer/resize は例外。
4921        (
4922            "var b=new ArrayBuffer(4); b.transfer(); try { b.transfer(); 'no throw'; } catch(e) { 'threw'; }",
4923            "threw",
4924        ),
4925        // transferToFixedLength は常に非 resizable な結果を返す。
4926        (
4927            "var b=new ArrayBuffer(4,{maxByteLength:8}); var b2=b.transferToFixedLength(); b2.resizable",
4928            "false",
4929        ),
4930        // `structuredClone(resizableArrayBuffer)`が`resizable`/`maxByteLength`
4931        // を引き継がず非resizableな複製に化けていたバグ(丸ごと未対応
4932        // だった。2026-07-17 発見・実装)。
4933        (
4934            "var b=new ArrayBuffer(4,{maxByteLength:16}); var c=structuredClone(b); \
4935             c.resizable+','+c.maxByteLength+','+c.byteLength",
4936            "true,16,4",
4937        ),
4938        (
4939            "var b=new ArrayBuffer(4,{maxByteLength:16}); var c=structuredClone(b); \
4940             c.resize(8); c.byteLength",
4941            "8",
4942        ),
4943        // Uint8Array.prototype.toBase64/toHex + 静的 fromBase64/fromHex(ES2024/2025)。
4944        ("new Uint8Array([72,101,108,108,111]).toBase64()", "SGVsbG8="),
4945        ("Array.from(Uint8Array.fromBase64('SGVsbG8=')).join(',')", "72,101,108,108,111"),
4946        ("new Uint8Array([222,173,190,239]).toHex()", "deadbeef"),
4947        ("Array.from(Uint8Array.fromHex('deadbeef')).join(',')", "222,173,190,239"),
4948        // 往復(バイト列 -> base64/hex -> バイト列)が元に戻ることを確認。
4949        (
4950            "var a=new Uint8Array([1,2,3,255,0]); Array.from(Uint8Array.fromBase64(a.toBase64())).join(',')",
4951            "1,2,3,255,0",
4952        ),
4953        (
4954            "var a=new Uint8Array([1,2,3,255,0]); Array.from(Uint8Array.fromHex(a.toHex())).join(',')",
4955            "1,2,3,255,0",
4956        ),
4957        // fromHex: 不正な16進文字列(奇数長)は例外を投げる。
4958        (
4959            "try { Uint8Array.fromHex('abc'); 'no-throw' } catch(e) { 'caught:' + e.name }",
4960            "caught:SyntaxError",
4961        ),
4962        // setFromBase64/setFromHex(ES2024/2025)— 既存バッファへインプレース書き込み。
4963        (
4964            "var a=new Uint8Array(5); var r=a.setFromBase64('SGVsbG8='); Array.from(a).join(',')+'|'+r.read+'|'+r.written",
4965            "72,101,108,108,111|8|5",
4966        ),
4967        (
4968            "var a=new Uint8Array(4); var r=a.setFromHex('deadbeef'); Array.from(a).join(',')+'|'+r.read+'|'+r.written",
4969            "222,173,190,239|8|4",
4970        ),
4971        (
4972            "try { new Uint8Array(1).setFromHex('zz'); 'no-throw' } catch(e) { 'caught:' + e.name }",
4973            "caught:SyntaxError",
4974        ),
4975        // TextEncoder — Uint8Array を返し、UTF-8 バイト値が正しい。
4976        ("var e=new TextEncoder().encode('AB'); e.length+','+e[0]+','+e[1]", "2,65,66"),
4977        // TextEncoder→TextDecoder 往復。
4978        ("new TextDecoder().decode(new TextEncoder().encode('hello'))", "hello"),
4979        // `TextEncoder.prototype.encodeInto(source, destination)` が丸ごと
4980        // 未対応だった(`encode()` は既存だが、この性能向けバリアントが欠けていた)。
4981        (
4982            "var buf=new Uint8Array(5); var r=new TextEncoder().encodeInto('Hi', buf); \
4983             r.read+','+r.written+','+buf[0]+','+buf[1]",
4984            "2,2,72,105",
4985        ),
4986        // 宛先バッファが足りない場合はコードポイント境界で打ち切る(マルチバイト
4987        // 文字を途中で分割しない)。
4988        (
4989            "var buf=new Uint8Array(1); var r=new TextEncoder().encodeInto('AB', buf); \
4990             r.read+','+r.written",
4991            "1,1",
4992        ),
4993        // Int8Array — 符号付きラップ(200 は -56、-100 はそのまま)。
4994        ("var a=new Int8Array([200,-100]); a.join(',')", "-56,-100"),
4995        // Uint8ClampedArray — 飽和(クランプ、ラップではない)。
4996        ("var a=new Uint8ClampedArray([300,-50,128.6]); a.join(',')", "255,0,129"),
4997        // ToUint8Clamp は同点を偶数丸め(banker's rounding)する仕様
4998        // (Math.round の四捨五入とは異なる)。
4999        ("var a=new Uint8ClampedArray([0.5,1.5,2.5,3.5]); a.join(',')", "0,2,2,4"),
5000        // Int16Array / Uint16Array — 幅16bitのラップ。
5001        ("var a=new Int16Array([40000]); a[0]", "-25536"),
5002        // 索引代入 `ta[i]=v` はコンストラクタ/`set`/`fill` と違って型変換
5003        // (ラップ/クランプ)が一切効かず生の数値がそのまま入るバグだった。
5004        ("var a=new Int8Array(1); a[0]=200; a[0]", "-56"),
5005        ("var a=new Uint8ClampedArray(1); a[0]=300; a[0]", "255"),
5006        ("var a=new Uint8ClampedArray(1); a[0]=-50; a[0]", "0"),
5007        ("var a=new Int16Array(1); a[0]=40000; a[0]", "-25536"),
5008        ("var a=new Uint16Array([70000]); a[0]", "4464"),
5009        // Int32Array / Uint32Array — 幅32bitのラップ。
5010        ("var a=new Int32Array([4294967295]); a[0]", "-1"),
5011        ("var a=new Uint32Array([-1]); a[0]", "4294967295"),
5012        // Float32Array — 単精度への丸め込み(倍精度のままではない)。
5013        ("var a=new Float32Array([0.1]); a[0] !== 0.1", "true"),
5014        // Float16Array / DataView getFloat16 / setFloat16 (ES2025)。
5015        ("var a=new Float16Array([1.5]); a[0] === 1.5", "true"),
5016        ("var dv=new DataView(new ArrayBuffer(2)); dv.setFloat16(0, 2.5); dv.getFloat16(0)", "2.5"),
5017        // Float64Array — 倍精度はそのまま保持。
5018        ("var a=new Float64Array([0.1]); a[0] === 0.1", "true"),
5019        // 各 TypedArray の BYTES_PER_ELEMENT。
5020        ("new Int16Array(1).BYTES_PER_ELEMENT+','+new Float64Array(1).BYTES_PER_ELEMENT", "2,8"),
5021        // TypedArray.from(静的)と set/slice/fill の型別クロスチェック。
5022        ("Int32Array.from([1,2,3]).join(',')", "1,2,3"),
5023        ("var a=new Uint16Array(3); a.set([1,2],1); a.join(',')", "0,1,2"),
5024        ("var a=new Int8Array([1,2,3,4]); a.slice(1,3).join(',')", "2,3"),
5025        ("var a=new Uint32Array(3); a.fill(5); a.join(',')", "5,5,5"),
5026        // `toSorted`/`toReversed`/`toSpliced`/`with`(ES2023)が TypedArray に対しては
5027        // 常に普通の配列を返すバグだった(`structuredClone(TypedArray)` と同種)。
5028        // 型変換(ラップ)も維持されることを確認する。
5029        (
5030            "var a=new Int8Array([200,3,1]); var b=a.toSorted(); b.join(',')+','+b.BYTES_PER_ELEMENT",
5031            "-56,1,3,1",
5032        ),
5033        (
5034            "var a=new Int8Array([1,2,200]); var b=a.toReversed(); b.join(',')+','+b.BYTES_PER_ELEMENT",
5035            "-56,2,1,1",
5036        ),
5037        (
5038            "var a=new Int8Array([1,2,3]); var b=a.with(1,200); b.join(',')+','+b.BYTES_PER_ELEMENT",
5039            "1,-56,3,1",
5040        ),
5041        // `map`/`filter` も同じ理由で TypedArray に対しては常に普通の配列を返す
5042        // バグだった。callback の戻り値が有効域外でも型ごとの変換(ラップ)が働く。
5043        (
5044            "var a=new Int8Array([1,2,3]); var b=a.map(x => x + 200); b.join(',')+','+b.BYTES_PER_ELEMENT",
5045            "-55,-54,-53,1",
5046        ),
5047        (
5048            "var a=new Int8Array([1,2,3,4]); var b=a.filter(x => x % 2 === 0); b.join(',')+','+b.BYTES_PER_ELEMENT",
5049            "2,4,1",
5050        ),
5051        // DataView(ES2015)が丸ごと未対応で `new DataView(buf)` が
5052        // `DataView is not defined` になっていた。
5053        (
5054            "var dv=new DataView(new ArrayBuffer(4)); dv.setUint8(0,255); dv.getUint8(0)",
5055            "255",
5056        ),
5057        // デフォルトはビッグエンディアン。
5058        (
5059            "var dv=new DataView(new ArrayBuffer(4)); dv.setUint32(0,0x01020304); dv.getUint8(0)+','+dv.getUint8(3)",
5060            "1,4",
5061        ),
5062        // littleEndian=true を渡すとバイト順が反転する。
5063        (
5064            "var dv=new DataView(new ArrayBuffer(4)); dv.setUint32(0,0x01020304,true); dv.getUint8(0)+','+dv.getUint8(3)",
5065            "4,1",
5066        ),
5067        // 符号付き/符号無しの往復(16bit)。
5068        ("var dv=new DataView(new ArrayBuffer(2)); dv.setInt16(0,-1); dv.getUint16(0)", "65535"),
5069        // Float64 の往復(丸めなし)。
5070        ("var dv=new DataView(new ArrayBuffer(8)); dv.setFloat64(0,3.5); dv.getFloat64(0)", "3.5"),
5071        // 範囲外アクセスは例外。
5072        (
5073            "try { new DataView(new ArrayBuffer(1)).getUint32(0); 'no throw'; } catch(e) { 'threw'; }",
5074            "threw",
5075        ),
5076        // Blob(File API の基礎コンテナ)が丸ごと未対応で `new Blob([...])` が
5077        // `Blob is not defined` になっていた。
5078        ("new Blob(['hello', ' ', 'world']).size", "11"),
5079        ("new Blob(['abc'], {type:'text/plain'}).type", "text/plain"),
5080        ("await new Blob(['hello world']).text()", "hello world"),
5081        ("await new Blob(['hello world']).slice(0,5).text()", "hello"),
5082        // `Blob.arrayBuffer()` が返す ArrayBuffer は実バイト列を持ち、そこから作った
5083        // DataView で正しく読み戻せる(`Blob`→`ArrayBuffer`→`DataView` の実用連携)。
5084        (
5085            "var buf = await new Blob(['A']).arrayBuffer(); new DataView(buf).getUint8(0)",
5086            "65",
5087        ),
5088        // `Blob.prototype.bytes()`(ES2024。丸ごと未対応だった。`await blob.
5089        // arrayBuffer()`→`new Uint8Array(...)` の2手間を1メソッドで済ませる。
5090        // 2026-07-15 発見・実装)。
5091        // 注: `instanceof Uint8Array` では検証しない — 調査の結果、この処理系の
5092        // TypedArray コンストラクタ群は `.prototype` を一切持たず `instanceof` が
5093        // 常に `false` を返す既存の別バグと判明(`Array`/`Map` 等 TypedArray 以外の
5094        // 組み込み型も含め instanceof 自体の自己テストがコードベースに1件も無く、
5095        // 広範な既知の未検証領域)。`BYTES_PER_ELEMENT`(Uint8Array 固有の値)で
5096        // 型を検証し、この既存バグを新規追跡項目として TODO.md へ記録した。
5097        (
5098            "var u = await new Blob(['AB']).bytes(); u.BYTES_PER_ELEMENT",
5099            "1",
5100        ),
5101        ("var u = await new Blob(['AB']).bytes(); u[0]+','+u[1]", "65,66"),
5102        // `File`(`Blob` を継承し `name`/`lastModified` を追加する File API の
5103        // 基礎コンストラクタ)が丸ごと未対応で `File is not defined` になっていた。
5104        ("new File(['hi'], 'a.txt').name", "a.txt"),
5105        ("new File(['hi'], 'a.txt', {type:'text/plain'}).type", "text/plain"),
5106        ("new File(['hello'], 'a.txt').size", "5"),
5107        ("await new File(['hello'], 'a.txt').text()", "hello"),
5108        // `Blob` 由来のメソッド(`slice`)もそのまま継承する。
5109        ("await new File(['hello world'], 'a.txt').slice(0,5).text()", "hello"),
5110        // `lastModified` は明示指定を尊重する。
5111        ("new File(['x'], 'a.txt', {lastModified: 12345}).lastModified", "12345"),
5112        // `File.webkitRelativePath`(丸ごと未対応だった。実際のディレクトリ
5113        // 選択UIが無いため常に空文字列の誠実な簡略実装。2026-07-17 発見・
5114        // 実装)。
5115        ("new File(['x'], 'a.txt').webkitRelativePath", ""),
5116        // `structuredClone(File)`が`Blob`分岐に落ちて`name`/`lastModified`
5117        // が失われ、素の`Blob`へ格下げされていたバグ(丸ごと未対応
5118        // だった。2026-07-17 発見・実装)。
5119        (
5120            "var c=structuredClone(new File(['hi'], 'a.txt', {lastModified: 12345})); \
5121             c.name + ',' + c.lastModified",
5122            "a.txt,12345",
5123        ),
5124        (
5125            "await structuredClone(new File(['hello'], 'a.txt')).text()",
5126            "hello",
5127        ),
5128        // 素の`Blob`(`name`無し)は引き続き`name`を持たない`Blob`のまま
5129        // 複製される(回帰確認)。
5130        ("structuredClone(new Blob(['x'])).name", "undefined"),
5131        // `FileReader` が丸ごと未対応だった(`blob.text()` 等の Promise 版は
5132        // 既に対応済みだったが、古典的なイベントベースの `readAsText`+`onload`
5133        // という書き方自体が丸ごと存在しなかった)。
5134        (
5135            "var r=new FileReader(); var out; r.onload=function(e){ out=e.target.result; }; \
5136             r.readAsText(new Blob(['hello'])); out",
5137            "hello",
5138        ),
5139        (
5140            "var r=new FileReader(); r.readAsText(new Blob(['x'])); r.readyState",
5141            "2",
5142        ),
5143        (
5144            "var r=new FileReader(); var buf; r.onload=function(e){ buf=e.target.result; }; \
5145             r.readAsArrayBuffer(new Blob(['A'])); new DataView(buf).getUint8(0)",
5146            "65",
5147        ),
5148        (
5149            "var r=new FileReader(); var url; r.onload=function(e){ url=e.target.result; }; \
5150             r.readAsDataURL(new Blob(['hi'], {type:'text/plain'})); url",
5151            "data:text/plain;base64,aGk=",
5152        ),
5153        // `addEventListener('load', ...)` 経由でも `onload` と同じく発火する
5154        // (`on<type>` 単一リスナ方式を共有しているため)。
5155        (
5156            "var r=new FileReader(); var fired=false; r.addEventListener('load', ()=>fired=true); \
5157             r.readAsText(new Blob(['x'])); fired",
5158            "true",
5159        ),
5160        // `Notification`(丸ごと未対応だった。通知 UI 自体が無いこの OS では
5161        // 実際の表示は行わず JS 側の契約のみ満たす簡略実装)。
5162        ("Notification.permission", "granted"),
5163        ("await Notification.requestPermission()", "granted"),
5164        (
5165            "var n = new Notification('hi', {body: 'world'}); n.title + ':' + n.body",
5166            "hi:world",
5167        ),
5168        ("typeof new Notification('x').close", "function"),
5169        // `data`/`requireInteraction`/`silent`/`dir`/`lang`(丸ごと未対応
5170        // だった。`title`/`body`/`icon`/`tag`は既に対応済みだったが、同じ
5171        // コンストラクタoptionsの残りが漏れていた。2026-07-17 発見・実装)。
5172        (
5173            "var n = new Notification('x', {data: {id: 5}, requireInteraction: true, silent: true}); \
5174             n.data.id + ',' + n.requireInteraction + ',' + n.silent",
5175            "5,true,true",
5176        ),
5177        ("new Notification('x').dir", "auto"),
5178        ("new Notification('x').data", "null"),
5179        ("typeof new Notification('x').removeEventListener", "function"),
5180        // Promise.withResolvers(ES2024)
5181        (
5182            "var {promise, resolve} = Promise.withResolvers(); resolve(5); await promise",
5183            "5",
5184        ),
5185        (
5186            "var {promise, reject} = Promise.withResolvers(); reject('e'); await promise.catch(e => 'caught:' + e)",
5187            "caught:e",
5188        ),
5189        // Promise.try(ES2025): 同期の戻り値/例外/Promise 戻り値をすべて統一的に扱う
5190        ("await Promise.try(() => 42)", "42"),
5191        (
5192            "await Promise.try(() => { throw 'boom' }).catch(e => 'caught:' + e)",
5193            "caught:boom",
5194        ),
5195        ("await Promise.try(() => Promise.resolve('ok'))", "ok"),
5196        ("await Promise.try((a, b) => a + b, 1, 2)", "3"),
5197        // String.prototype.isWellFormed / toWellFormed(ES2024)
5198        ("'abc'.isWellFormed()", "true"),
5199        ("'abc'.toWellFormed()", "abc"),
5200        // using 宣言(Explicit Resource Management、ES2023→ES2026ベースライン)
5201        (
5202            "var log=''; { using r = {[Symbol.dispose]: () => log+='d'}; log+='a' } log+='b'; log",
5203            "adb",
5204        ),
5205        // 複数 using は宣言の逆順で dispose される(LIFO)。
5206        (
5207            "var log=''; { using a = {[Symbol.dispose]: () => log+='A'}; using b = {[Symbol.dispose]: () => log+='B'} } log",
5208            "BA",
5209        ),
5210        // return で早期脱出しても dispose は呼ばれる(戻り値確定後に外側の log へ副作用)。
5211        (
5212            "var log=''; function f(){ { using r = {[Symbol.dispose]: () => log+='d'}; return 'a' } } var res=f(); log+res",
5213            "da",
5214        ),
5215        // await using は Symbol.asyncDispose の戻り値を await する。
5216        (
5217            "async function f(){ var log=''; { await using r = {[Symbol.asyncDispose]: async () => { log+='d' }}; log+='a' } return log } await f()",
5218            "ad",
5219        ),
5220        // Array.fromAsync(ES2024/2025): 同期イテラブル + 各要素の Promise を await。
5221        (
5222            "(await Array.fromAsync([Promise.resolve(1), 2, Promise.resolve(3)])).join(',')",
5223            "1,2,3",
5224        ),
5225        // mapFn 付き。
5226        ("(await Array.fromAsync([1,2,3], x => x * 10)).join(',')", "10,20,30"),
5227        // 複数 using の dispose がいずれも例外を投げると SuppressedError へ集約される
5228        // (LIFO: 後から宣言した b が先に dispose される → error=A(最後に投げた), suppressed=B)。
5229        (
5230            "var caught=null; try { { using a = {[Symbol.dispose]: () => { throw 'A' }}; using b = {[Symbol.dispose]: () => { throw 'B' }} } } catch(e) { caught = e } caught.name + ':' + caught.error + ':' + caught.suppressed",
5231            "SuppressedError:A:B",
5232        ),
5233        // for await...of: 同期イテラブル + 各要素の Promise を await するフォールバック。
5234        (
5235            "async function f(){ var out=''; for await (const x of [Promise.resolve(1), 2, Promise.resolve(3)]) { out += x } return out } await f()",
5236            "123",
5237        ),
5238        // for await...of: Symbol.asyncIterator を実装した独自オブジェクトを手動駆動。
5239        (
5240            "async function f(){ var obj={}; obj[Symbol.asyncIterator]=function(){ var i=0; return {next:function(){ i++; if (i<=3) { return Promise.resolve({value:i,done:false}); } return Promise.resolve({value:undefined,done:true}); }}; }; var out=''; for await (const x of obj){ out += x } return out } await f()",
5241            "123",
5242        ),
5243        // async generator インスタンスは Symbol.asyncIterator を公開すべき
5244        // (Generator.prototype[Symbol.asyncIterator] は自分自身を返す仕様)。
5245        // 以前は generator_method に asyncIterator/iterator の登録が無く、for await が
5246        // 常に eager-drain フォールバックへ落ちていた(値は一致するが例外伝播が違った)。
5247        (
5248            "async function* g(){ yield 1; yield 2; yield 3; } typeof g()[Symbol.asyncIterator]",
5249            "function",
5250        ),
5251        (
5252            "async function* g(){ yield 1; yield 2; yield 3; } var it=g(); it[Symbol.asyncIterator]() === it",
5253            "true",
5254        ),
5255        (
5256            "async function* g(){ yield 1; yield 2; yield 3; } async function f(){ var out=''; for await (const x of g()) { out += x } return out } await f()",
5257            "123",
5258        ),
5259        // 通常の(非 async)generator は Symbol.asyncIterator を公開してはいけない
5260        // (for await のフォールバックが正しく同期経路を通ることの確認)。
5261        (
5262            "function* g(){ yield 1; } typeof g()[Symbol.asyncIterator]",
5263            "undefined",
5264        ),
5265        // async generator が途中で例外を投げた場合、for await はそれを正しく伝播する
5266        // (eager-drain フォールバックはこれを黙って握り潰していた)。
5267        (
5268            "async function* g(){ yield 1; throw 'boom'; } async function f(){ var out=''; try { for await (const x of g()) { out += x } } catch(e) { out += ':' + e } return out } await f()",
5269            "1:boom",
5270        ),
5271        // 非宣言形式の for-of(x は既存変数): 配列を反復。
5272        ("var x; var out=''; for (x of [1,2,3]) { out += x } out", "123"),
5273        // 非宣言形式は既存の外側変数へ代入する(ループ後もその値が残る)。
5274        ("var x=0; for (x of [1,2,3]) {} x", "3"),
5275        // 非宣言形式の for-in(k は既存変数)。
5276        ("var k; var out=''; for (k in {a:1,b:2}) { out += k } out", "ab"),
5277        // 非宣言形式 + 配列分割代入 for-of。
5278        (
5279            "var a,b; var out=''; for ([a,b] of [[1,2],[3,4]]) { out += a+','+b+';' } out",
5280            "1,2;3,4;",
5281        ),
5282        // 非宣言形式 + オブジェクト分割代入 for-of。
5283        (
5284            "var a,b; var out=''; for ({a,b} of [{a:1,b:2},{a:3,b:4}]) { out += a+','+b+';' } out",
5285            "1,2;3,4;",
5286        ),
5287        // 非宣言形式 + メンバー式 for-of(obj.prop)。
5288        ("var obj={p:0}; for (obj.p of [1,2,3]) {} obj.p", "3"),
5289        // 非宣言形式 + 添字式 for-of(arr[0])。
5290        ("var arr=[0]; for (arr[0] of [7,8,9]) {} arr[0]", "9"),
5291        // New Set Methods(ES2024/2025)。仕様上第一引数は `Set` に限らず任意の
5292        // set-like(iterable)を受け付けるべきだが、以前は `&mut Interp` を持たない
5293        // `iterable_values` にフォールバックしており Generator を渡すと常に空扱いに
5294        // なるバグだった(`iterable_values`/`this_items` 系の横展開監査で見落とし)。
5295        (
5296            "function* g(){yield 3;yield 4;} [...new Set([1,2,3]).union(g())].sort().join(',')",
5297            "1,2,3,4",
5298        ),
5299        (
5300            "function* g(){yield 2;yield 3;} [...new Set([1,2,3]).intersection(g())].sort().join(',')",
5301            "2,3",
5302        ),
5303        ("[...new Set([1,2,3]).union(new Set([3,4,5]))].sort().join(',')", "1,2,3,4,5"),
5304        ("[...new Set([1,2,3]).intersection(new Set([2,3,4]))].sort().join(',')", "2,3"),
5305        ("[...new Set([1,2,3]).difference(new Set([2,3]))].join(',')", "1"),
5306        (
5307            "[...new Set([1,2,3]).symmetricDifference(new Set([2,3,4]))].sort().join(',')",
5308            "1,4",
5309        ),
5310        ("new Set([1,2]).isSubsetOf(new Set([1,2,3]))", "true"),
5311        ("new Set([1,2,3]).isSupersetOf(new Set([1,2]))", "true"),
5312        ("new Set([1,2]).isDisjointFrom(new Set([3,4]))", "true"),
5313        ("new Set([1,2]).isDisjointFrom(new Set([2,3]))", "false"),
5314        // Iterator helpers(ES2025): generator に対する map/filter/take/drop/flatMap/toArray/
5315        // forEach/some/every/find/reduce。無限イテレータには非対応の簡略実装(要素を全展開してから処理)。
5316        ("function* g(){ yield 1; yield 2; yield 3; } g().map(x=>x*2).join(',')", "2,4,6"),
5317        (
5318            "function* g(){ yield 1; yield 2; yield 3; yield 4; } g().filter(x=>x%2===0).join(',')",
5319            "2,4",
5320        ),
5321        (
5322            "function* g(){ yield 1; yield 2; yield 3; yield 4; yield 5; } g().take(2).join(',')",
5323            "1,2",
5324        ),
5325        (
5326            "function* g(){ yield 1; yield 2; yield 3; yield 4; yield 5; } g().drop(3).join(',')",
5327            "4,5",
5328        ),
5329        (
5330            "function* g(){ yield 1; yield 2; } g().flatMap(x=>[x,x*10]).join(',')",
5331            "1,10,2,20",
5332        ),
5333        ("function* g(){ yield 1; yield 2; } g().toArray().join(',')", "1,2"),
5334        (
5335            "function* g(){ yield 1; yield 2; yield 3; } var s=0; g().forEach(x=>s+=x); s",
5336            "6",
5337        ),
5338        ("function* g(){ yield 1; yield 2; yield 3; } g().some(x=>x>2)", "true"),
5339        ("function* g(){ yield 1; yield 2; yield 3; } g().every(x=>x>0)", "true"),
5340        ("function* g(){ yield 1; yield 2; yield 3; } g().find(x=>x>1)", "2"),
5341        (
5342            "function* g(){ yield 1; yield 2; yield 3; } g().reduce((a,x)=>a+x,0)",
5343            "6",
5344        ),
5345        // Iterator.from(ES2025): 任意のイテラブル(配列/Set/Generator)を Iterator helpers
5346        // 一式にアクセスできる形でラップする。map/filter 等は Array.prototype 経由、
5347        // take/drop/toArray は Iterator helpers 経由(Array に無いメソッドのため)。
5348        ("Iterator.from([1,2,3]).map(x=>x*2).join(',')", "2,4,6"),
5349        ("Iterator.from([1,2,3,4,5]).take(2).join(',')", "1,2"),
5350        ("Iterator.from([1,2,3,4,5]).drop(3).join(',')", "4,5"),
5351        ("Iterator.from(new Set([1,2,3])).toArray().join(',')", "1,2,3"),
5352        (
5353            "function* g(){ yield 1; yield 2; yield 3; } Iterator.from(g()).take(2).join(',')",
5354            "1,2",
5355        ),
5356        // Iterator.concat(ES2025 Standard): 複数のイテラブル(配列、Set、Generator等)をシームレスに結合する。
5357        ("Iterator.concat([1,2], new Set([3,4]), [5]).toArray().join(',')", "1,2,3,4,5"),
5358        ("Iterator.concat(['a','b'], ['c']).take(2).toArray().join(',')", "a,b"),
5359        // `instanceof`(ES1 以来の基礎演算子)が `BinaryOp::InstanceOf => Value::Bool(false)`
5360        // という未実装のプレースホルダのまま放置され、常に false を返す重大バグだった。
5361        // OrdinaryHasInstance 相当(`r.prototype` を起点に `l` のプロトタイプ連鎖を辿る)を実装。
5362        ("function Foo(){} new Foo() instanceof Foo", "true"),
5363        ("function Foo(){} function Bar(){} new Foo() instanceof Bar", "false"),
5364        ("class Animal{} class Dog extends Animal{} new Dog() instanceof Animal", "true"),
5365        ("class Animal{} class Dog extends Animal{} new Dog() instanceof Dog", "true"),
5366        ("1 instanceof Object", "false"),
5367        // 組み込みの `Object`/`Array` コンストラクタは(他のビルトインメソッド解決と同様に
5368        // ObjKind による特殊扱いで実装されており)実際の `.prototype` オブジェクトを
5369        // 持たないため、リテラルとの `instanceof` は常に false になる簡略実装の既知の限界。
5370        // ユーザー定義の function/class コンストラクタ(実際に `.prototype` を持つ)が
5371        // 主要なユースケースであり、そちらは上のテストの通り正しく動作する。
5372        ("({}) instanceof Object", "false"),
5373        ("[] instanceof Array", "false"),
5374        // グローバルな `Error` コンストラクタ自体が長期間欠落しており(TypeError/RangeError/
5375        // SyntaxError というサブタイプだけが存在し基底の `Error` が無い状態だった)、
5376        // `new Error("msg")` は「Error is not defined」で ReferenceError になっていた。
5377        // 合わせて各サブタイプの `.prototype` を `Error.prototype` を proto に持つ形へ
5378        // 再構成し、`instanceof` の実装と対で `new TypeError() instanceof Error` 等が
5379        // 正しく true になるようにした。
5380        ("new Error('boom').message", "boom"),
5381        ("new Error('boom').name", "Error"),
5382        ("new Error('boom') instanceof Error", "true"),
5383        // `Error.prototype.stack`(丸ごと未対応だった)。実際のコールフレーム
5384        // 一覧は再現できないため、V8 の1行目と同じ `"name: message"` 形式の
5385        // 簡略実装(詳細は `builtins::build_error_stack` 参照)。
5386        ("new Error('boom').stack", "Error: boom"),
5387        ("new TypeError('x').stack", "TypeError: x"),
5388        ("typeof new Error().stack === 'string' && new Error().stack.length > 0", "true"),
5389        (
5390            "(function(){ try { null.foo; } catch(e) { return typeof e.stack; } })()",
5391            "string",
5392        ),
5393        ("new TypeError('x') instanceof Error", "true"),
5394        ("new TypeError('x') instanceof TypeError", "true"),
5395        ("new RangeError('x') instanceof TypeError", "false"),
5396        ("new Uint8Array(1) instanceof Uint8Array", "true"),
5397        ("new Int8Array(1) instanceof Int8Array", "true"),
5398        ("new Uint8Array(1) instanceof Int8Array", "false"),
5399        ("Object.getPrototypeOf(new Uint8Array(1)) === Uint8Array.prototype", "true"),
5400        ("Uint8Array.prototype.constructor === Uint8Array", "true"),
5401        (
5402            "try { null.x } catch(e) { e instanceof Error }",
5403            "true",
5404        ),
5405        // ユーザー定義のカスタムエラークラス(`class MyError extends Error`)は、`super(msg)`
5406        // が到達するネイティブ `Error` コンストラクタが `this`(サブクラスのインスタンス。
5407        // proto は MyError.prototype → Error.prototype と連鎖)へ直接 message を書き込む
5408        // ことに依存しており、今回の一連の修正で自然に動くようになった実用パターン。
5409        (
5410            "class MyError extends Error { constructor(m){ super(m); this.name='MyError'; } } var e = new MyError('bad'); e.message + '/' + e.name",
5411            "bad/MyError",
5412        ),
5413        (
5414            "class MyError extends Error { constructor(m){ super(m); } } new MyError('x') instanceof Error",
5415            "true",
5416        ),
5417        (
5418            "class MyError extends Error { constructor(m){ super(m); } } new MyError('x') instanceof MyError",
5419            "true",
5420        ),
5421        // **重要**: `Symbol.hasInstance`(ES2015。`class Foo { static
5422        // [Symbol.hasInstance](x){...} }` によるカスタム `instanceof` 判定)が丸ごと
5423        // 未対応で、常にプロトタイプ連鎖の照合に決め打ちされていた。
5424        (
5425            "class Even { static [Symbol.hasInstance](n){ return typeof n === 'number' && n % 2 === 0; } } (4 instanceof Even) + ',' + (3 instanceof Even)",
5426            "true,false",
5427        ),
5428        (
5429            "class AlwaysTrue { static [Symbol.hasInstance](x){ return true; } } ({} instanceof AlwaysTrue)",
5430            "true",
5431        ),
5432        // `Error.isError(value)`(ES2025)が丸ごと欠落していた。`instanceof Error` と違い
5433        // `Symbol.hasInstance`/`Error.prototype` の上書きに影響されず組込み proto を直接見る。
5434        ("Error.isError(new Error('x'))", "true"),
5435        ("Error.isError(new TypeError('x'))", "true"),
5436        (
5437            "class MyError extends Error { constructor(m){ super(m); } } Error.isError(new MyError('x'))",
5438            "true",
5439        ),
5440        ("Error.isError({message:'x'})", "false"),
5441        ("Error.isError(42)", "false"),
5442        ("Error.isError(undefined)", "false"),
5443        // `new Error(msg, {cause})`(ES2022)が丸ごと未対応で、`err.cause` が常に
5444        // `undefined` になっていた(例外を包んで再送出する定番イディオムで使われる)。
5445        ("new Error('wrap', {cause: 'orig'}).cause", "orig"),
5446        ("typeof new Error('x').cause", "undefined"),
5447        ("new TypeError('t', {cause: 42}).cause", "42"),
5448        (
5449            "try { throw new Error('inner') } catch(e) { new Error('outer', {cause: e}).cause.message }",
5450            "inner",
5451        ),
5452        // `RegExp.escape(str)`(ES2025)が丸ごと未対応だった。ユーザー入力を
5453        // `new RegExp(...)` へそのまま渡す前に構文文字を無害化する定番イディオム。
5454        ("RegExp.escape('a.b*c')", "a\\.b\\*c"),
5455        ("new RegExp(RegExp.escape('1+1=2')).test('1+1=2')", "true"),
5456        (
5457            "new RegExp('^' + RegExp.escape('a.b') + '$').test('aXb')",
5458            "false",
5459        ),
5460        // `RegExp.prototype.compile()`(レガシーだが仕様に残るメソッド。丸ごと
5461        // 未対応だった)。`this` を新しいオブジェクトを作らずその場で差し替える。
5462        ("var r=/a/; r.compile('b'); r.test('b')", "true"),
5463        ("var r=/a/i; r.compile('a'); r.flags", ""),
5464        ("var r=/a/; r.compile('a','gi'); r.flags", "gi"),
5465        ("var r=/a/g; r.lastIndex=3; r.compile('b'); r.lastIndex", "0"),
5466        ("var r=/x/; var r2=r.compile('y'); r===r2", "true"),
5467        // `CSS.escape(str)`(CSSOM。丸ごと未対応だった。`CSS` 名前空間自体が
5468        // 存在しなかった)。
5469        ("CSS.escape('a.b')", "a\\.b"),
5470        ("CSS.escape('.foo#bar')", "\\.foo\\#bar"),
5471        ("CSS.escape('123')", "\\31 23"),
5472        ("CSS.escape('-1')", "-\\31 "),
5473        ("CSS.escape('-')", "\\-"),
5474        ("CSS.escape('abc')", "abc"),
5475        // `CSS.supports(conditionText)`/`CSS.supports(property, value)`
5476        // (CSSOM。丸ごと未対応だった。`@supports` の条件式評価をそのまま
5477        // 再利用するため、`not`/`and`/`or` の論理結合も正しく評価される)。
5478        ("CSS.supports('(display: grid)')", "true"),
5479        ("CSS.supports('display', 'grid')", "true"),
5480        ("CSS.supports('not (display: grid)')", "false"),
5481        (
5482            "CSS.supports('(display: grid) and (gap: 1rem)')",
5483            "true",
5484        ),
5485        (
5486            "CSS.supports('not (display: grid) or (gap: 1rem)')",
5487            "true",
5488        ),
5489        // `<audio>`/`<video>` の `.play()`/`.pause()`/`.load()`/`.canPlayType()`
5490        // (HTMLMediaElement。丸ごと未対応だった)。
5491        (
5492            "typeof document.createElement('audio').play().then",
5493            "function",
5494        ),
5495        ("typeof document.createElement('video').pause()", "undefined"),
5496        ("typeof document.createElement('audio').load()", "undefined"),
5497        ("document.createElement('video').canPlayType('video/mp4')", ""),
5498        // ARIA 反映 IDL 属性(`.role`/`.ariaLabel`/`.ariaChecked` 等)が丸ごと
5499        // 未対応だった。
5500        ("document.createElement('div').role", "null"),
5501        (
5502            "var e=document.createElement('div'); e.role='button'; e.getAttribute('role')",
5503            "button",
5504        ),
5505        (
5506            "var e=document.createElement('div'); e.setAttribute('aria-label','x'); e.ariaLabel",
5507            "x",
5508        ),
5509        (
5510            "var e=document.createElement('div'); e.ariaExpanded='true'; \
5511             e.getAttribute('aria-expanded')",
5512            "true",
5513        ),
5514        (
5515            "var e=document.createElement('div'); e.ariaLabel='x'; e.ariaLabel=null; \
5516             e.hasAttribute('aria-label')",
5517            "false",
5518        ),
5519        // 表・リスト系の残りの ARIA 反映 IDL 属性(丸ごと未対応だった。
5520        // 2026-07-15 発見・実装)。
5521        (
5522            "var e=document.createElement('div'); e.ariaColCount='3'; \
5523             e.getAttribute('aria-colcount')",
5524            "3",
5525        ),
5526        (
5527            "var e=document.createElement('div'); e.setAttribute('aria-rowindex','2'); \
5528             e.ariaRowIndex",
5529            "2",
5530        ),
5531        (
5532            "var e=document.createElement('div'); e.ariaSetSize='5'; e.ariaPosInSet='2'; \
5533             e.ariaSetSize + ',' + e.ariaPosInSet",
5534            "5,2",
5535        ),
5536        (
5537            "var e=document.createElement('div'); e.ariaLevel='1'; e.getAttribute('aria-level')",
5538            "1",
5539        ),
5540        // `String.prototype.repeat(count)` が仕様上必須の「負値/+Infinity で RangeError」
5541        // を投げず黙って空文字列に丸めていたバグ。
5542        ("'ab'.repeat(3)", "ababab"),
5543        ("'x'.repeat(0)", ""),
5544        (
5545            "try { 'x'.repeat(-1); 'no-throw' } catch(e) { 'threw' }",
5546            "threw",
5547        ),
5548        (
5549            "try { 'x'.repeat(Infinity); 'no-throw' } catch(e) { 'threw' }",
5550            "threw",
5551        ),
5552        // `eval`/`Function`(ES1 の最基礎機能の2つ)が丸ごと欠落していた
5553        // (`eval is not a function`/`Function is not defined`)。
5554        ("eval('1 + 2 * 3')", "7"),
5555        ("eval(42)", "42"),
5556        ("var x = 10; eval('x = x + 5'); x", "15"),
5557        ("new Function('a', 'b', 'return a + b')(3, 4)", "7"),
5558        ("new Function('return 1 + 1')()", "2"),
5559        (
5560            "typeof new Function('a', 'return a * 2')",
5561            "function",
5562        ),
5563        // Iterator Helpers(ES2025)が丸ごと未対応だった。`arr.values()` 等が返す軽量
5564        // イテレータに `map`/`filter`/`take`/`drop`/`toArray`/`forEach`/`reduce` を追加。
5565        ("[1,2,3].values().map(x => x * 2).toArray().join(',')", "2,4,6"),
5566        ("[1,2,3,4].values().filter(x => x % 2 === 0).toArray().join(',')", "2,4"),
5567        ("[1,2,3,4,5].values().take(2).toArray().join(',')", "1,2"),
5568        ("[1,2,3,4,5].values().drop(3).toArray().join(',')", "4,5"),
5569        (
5570            "[1,2,3].values().map(x => x + 1).filter(x => x > 2).toArray().join(',')",
5571            "3,4",
5572        ),
5573        (
5574            "var s=''; [1,2,3].values().forEach(x => s += x); s",
5575            "123",
5576        ),
5577        ("[1,2,3,4].values().reduce((a,b) => a + b)", "10"),
5578        ("[1,2,3].values().reduce((a,b) => a + b, 10)", "16"),
5579        // `some`/`every`/`find`(同じ ES2025 Iterator Helpers 提案に含まれる残り3つ
5580        // が丸ごと未対応だった。`map`/`filter`/`take`/`drop`/`toArray`/`forEach`/
5581        // `reduce` は既に対応済み)。
5582        ("[1,2,3].values().some(x => x > 2)", "true"),
5583        ("[1,2,3].values().some(x => x > 5)", "false"),
5584        ("[1,2,3].values().every(x => x > 0)", "true"),
5585        ("[1,2,3].values().every(x => x > 1)", "false"),
5586        ("[1,2,3,4].values().find(x => x % 2 === 0)", "2"),
5587        ("typeof [1,2,3].values().find(x => x > 5)", "undefined"),
5588        // `flatMap`(ES2025 Iterator Helpers 提案の最後の1つが丸ごと未対応だった)。
5589        (
5590            "[1,2,3].values().flatMap(x => [x, x * 10]).toArray().join(',')",
5591            "1,10,2,20,3,30",
5592        ),
5593        // `Iterator.prototype.flatMap` のマッパー戻り値が配列以外の iterable(Generator 等)
5594        // でも展開されるべき(以前は実配列以外を無条件で空扱いする同型のバグがあった)。
5595        (
5596            "function* dup(x){ yield x; yield x*10; } [1,2].values().flatMap(x => dup(x)).toArray().join(',')",
5597            "1,10,2,20",
5598        ),
5599        // `Object.getPrototypeOf`/`Object.setPrototypeOf`(ES5/ES6。`Reflect` 版は既に
5600        // あったが、実用上はるかに一般的なこちらの静的メソッドが欠落していた)。
5601        (
5602            "function Foo(){} Object.getPrototypeOf(new Foo()) === Foo.prototype",
5603            "true",
5604        ),
5605        (
5606            "var a={}; var b={x:1}; Object.setPrototypeOf(a,b); a.x",
5607            "1",
5608        ),
5609        // `Object.setPrototypeOf` は循環参照を検証しないため、循環したプロトタイプ
5610        // 連鎖を持つオブジェクトへのプロパティ読み書き/`in`/`for...in` が無限ループ
5611        // (QEMU 起動ハング)になり得たバグ。
5612        (
5613            "var a={}; Object.setPrototypeOf(a,a); a.nope",
5614            "undefined",
5615        ),
5616        (
5617            "var a={}; var b={}; Object.setPrototypeOf(a,b); Object.setPrototypeOf(b,a); a.nope",
5618            "undefined",
5619        ),
5620        (
5621            "var a={}; Object.setPrototypeOf(a,a); ('nope' in a)",
5622            "false",
5623        ),
5624        (
5625            "var a={}; Object.setPrototypeOf(a,a); a.x=1; a.x",
5626            "1",
5627        ),
5628        (
5629            "var a={}; Object.setPrototypeOf(a,a); var ks=[]; for (var k in a) { ks.push(k); } ks.length",
5630            "0",
5631        ),
5632        // `obj.__proto__`(Annex B のレガシーアクセサ。`Object.getPrototypeOf`/
5633        // `setPrototypeOf` は既に対応済みだったが、実務コードで極めて広く使われる
5634        // こちらの糖衣構文自体が丸ごと未対応だった)。
5635        ("var a={}; var b={x:1}; a.__proto__ = b; a.x", "1"),
5636        ("var b={x:1}; var a=Object.create(b); a.__proto__ === b", "true"),
5637        ("({}).__proto__ === null", "true"),
5638        ("var a={}; a.__proto__ = null; a.__proto__", "null"),
5639        // オブジェクトリテラル内の `__proto__: value`(算出キーでない場合)は
5640        // 通常のプロパティではなく [[Prototype]] を設定する特別構文(Annex B.3.1)。
5641        ("var p={x:5}; var o={__proto__: p}; o.x", "5"),
5642        ("var p={x:5}; var o={__proto__: p}; Object.keys(o).includes('__proto__')", "false"),
5643        // 算出キー `{['__proto__']: v}` は通常の own プロパティのまま(特別扱いしない)。
5644        ("var o={['__proto__']: 5}; o.__proto__", "5"),
5645        // `Object.prototype.isPrototypeOf`/`propertyIsEnumerable`。
5646        ("function Foo(){} Foo.prototype.isPrototypeOf(new Foo())", "true"),
5647        ("({}).isPrototypeOf({})", "false"),
5648        ("({x:1}).propertyIsEnumerable('x')", "true"),
5649        ("({x:1}).propertyIsEnumerable('y')", "false"),
5650        // `Function.prototype.call`/`apply`/`bind`(ES3/ES5)が丸ごと欠落していた
5651        // (`Reflect.apply` はあったが、はるかに一般的なこちらのインスタンスメソッドが無かった)。
5652        ("function f(a,b){ return this.x+a+b; } f.call({x:10}, 1, 2)", "13"),
5653        ("function f(a,b){ return this.x+a+b; } f.apply({x:10}, [1,2])", "13"),
5654        // `Function.prototype.apply` の第2引数も `Reflect.apply` と同じく array-like
5655        // 全般(非配列)を受け付けるべきだが、以前は `iterable_values` を使っており
5656        // 素の array-like を渡すと引数が消える同型のバグだった。
5657        (
5658            "function f(a,b){ return this.x+a+b; } f.apply({x:10}, {0:1,1:2,length:2})",
5659            "13",
5660        ),
5661        (
5662            "function f(a,b){ return this.x+a+b; } var g = f.bind({x:10}, 1); g(2)",
5663            "13",
5664        ),
5665        (
5666            "function f(){ return this.x; } var g = f.bind({x:5}); g.call({x:99})",
5667            "5",
5668        ),
5669        // `new.target`(ES6 MetaProperty)が未実装だった。`new` 経由の呼び出しでは
5670        // コンストラクタ自身、通常呼び出しでは undefined になる。
5671        // `new Foo()` はコンストラクタが(オブジェクトではなく)真偽値を return しても
5672        // 仕様どおり無視され `this`(構築済みインスタンス)を返すため、`new.target` の値は
5673        // 外側の変数に代入して確認する。
5674        (
5675            "var r; function Foo(){ r = new.target === Foo; } new Foo(); r",
5676            "true",
5677        ),
5678        ("function Foo(){ return new.target === Foo; } Foo()", "false"),
5679        (
5680            "function Foo(){ return typeof new.target; } Foo()",
5681            "undefined",
5682        ),
5683        (
5684            "class Foo { constructor(){ this.isNew = new.target === Foo; } } new Foo().isNew",
5685            "true",
5686        ),
5687        // アロー関数は `this` と同様に `new.target` もレキシカルに周囲から継承する
5688        // (アロー自身は `new` できず独自のフレームを持たないため)。
5689        (
5690            "var r; function Foo(){ var f = () => { r = new.target === Foo; }; f(); } new Foo(); r",
5691            "true",
5692        ),
5693        // `catch` 節の分割代入パターン(ES2015)。以前は `catch_param` が単なる識別子
5694        // 文字列で、パターン(`{message}` 等)を受け付けられなかった。
5695        (
5696            "try { throw {message:'boom', code:42}; } catch({message, code}) { message + '/' + code; }",
5697            "boom/42",
5698        ),
5699        (
5700            "try { throw [1,2,3]; } catch([a,,c]) { a + '/' + c; }",
5701            "1/3",
5702        ),
5703        ("try { throw 'x'; } catch(e) { e; }", "x"),
5704        // クラスの算出メソッド名 `[expr](){}` が未対応だった(パーサがキーとして
5705        // 識別子/文字列/数値/`[`を含む未知トークンを読み飛ばすだけで、`[Symbol.iterator]`
5706        // のようなよくあるパターンが正しくパースできなかった)。
5707        (
5708            "class C { ['foo' + 'bar'](){ return 42; } } new C().foobar()",
5709            "42",
5710        ),
5711        // 上の算出メソッド名対応と組み合わせ、`for...of` がユーザー定義の
5712        // `[Symbol.iterator]()` を実装したカスタムイテラブル(class)を正しく駆動できる
5713        // ようにした(従来は Array/Set/Map/Generator/文字列以外は「オブジェクト自身の
5714        // プロパティ値」を無条件に列挙するだけで、Symbol.iterator 自体を一切見ていなかった)。
5715        (
5716            "class Range { constructor(n){ this.n = n; } [Symbol.iterator](){ var i=0, n=this.n; return { next(){ return i<n ? {value:i++, done:false} : {value:undefined, done:true}; } }; } } var s=''; for (const x of new Range(4)) { s += x; } s",
5717            "0123",
5718        ),
5719        (
5720            "class Range { constructor(n){ this.n = n; } [Symbol.iterator](){ var i=0, n=this.n; return { next(){ return i<n ? {value:i++, done:false} : {value:undefined, done:true}; } }; } } [...new Range(3)].join(',')",
5721            "0,1,2",
5722        ),
5723        // `[Symbol.iterator]` 等の偽装 Symbol キーが `Object.keys`/`values`/`entries`/
5724        // `for...in`/`JSON.stringify` に漏れて列挙されてしまうバグの検証
5725        // (`Object.assign`/スプレッド構文は仕様どおり対象のままなので混同しないこと)。
5726        (
5727            "var o={a:1}; o[Symbol.iterator]=function(){}; Object.keys(o).join(',')",
5728            "a",
5729        ),
5730        (
5731            "var o={a:1}; o[Symbol.iterator]=function(){}; Object.values(o).join(',')",
5732            "1",
5733        ),
5734        (
5735            "var o={a:1}; o[Symbol.iterator]=function(){}; JSON.stringify(o)",
5736            "{\"a\":1}",
5737        ),
5738        (
5739            "var o={a:1}; o[Symbol.iterator]=function(){}; var k=''; for (const x in o) { k+=x; } k",
5740            "a",
5741        ),
5742        // `Date` が丸ごと未実装だった(`performance.now()` はあったが実時刻を持たない
5743        // 単純カウンタで、`Date` 自体はグローバルに存在しなかった)。実時刻は NTP 同期済みの
5744        // カーネル壁時計(`kernel::timer::get_unix_time()`)を利用する。
5745        (
5746            "new Date(2024, 0, 15, 10, 30, 45, 500).toISOString()",
5747            "2024-01-15T10:30:45.500Z",
5748        ),
5749        ("new Date(0).toISOString()", "1970-01-01T00:00:00.000Z"),
5750        // `toDateString`/`toTimeString`/`toLocaleDateString`/`toLocaleTimeString` は
5751        // 以前すべて `toString`(フルISO日時文字列)にエイリアスされており、日付のみ/
5752        // 時刻のみを返すべきところに他方の情報が混入していた。
5753        (
5754            "new Date(2024, 0, 15, 10, 30, 45, 500).toDateString()",
5755            "2024-01-15",
5756        ),
5757        (
5758            "new Date(2024, 0, 15, 10, 30, 45, 500).toTimeString()",
5759            "10:30:45",
5760        ),
5761        (
5762            "new Date(2024, 0, 15).toLocaleDateString()",
5763            "2024-01-15",
5764        ),
5765        ("new Date(2024, 0, 15).toLocaleTimeString()", "00:00:00"),
5766        ("new Date(2024, 0, 1).getFullYear()", "2024"),
5767        ("new Date(2024, 5, 15).getMonth()", "5"),
5768        ("new Date(2024, 0, 1).getDay()", "1"), // 2024-01-01 は月曜日
5769        // getTimezoneOffset()(丸ごと未対応だった)。UTC を常にローカルとみなす簡略実装のため常に0。
5770        ("typeof new Date().getTimezoneOffset()", "number"),
5771        ("new Date().getTimezoneOffset()", "0"),
5772        // `Date.prototype.set*` が `setTime` も含めて1つも実装されておらず、getter
5773        // しか無い丸ごと未対応バグだった(`setFullYear`/`setDate` 等の定番書き換え
5774        // イディオムが軒並み `undefined is not a function` になっていた)。
5775        (
5776            "var d=new Date(2024,0,15); d.setFullYear(2025); d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()",
5777            "2025-1-15",
5778        ),
5779        // `getYear`/`setYear`(Annex B.2.4/B.2.5。2桁年時代の遺産)が丸ごと未対応だった。
5780        ("new Date(2024,0,1).getYear()", "124"),
5781        // `setYear(y)` は `0<=y<=99` なら `1900+y` を、それ以外は `y` をそのまま
5782        // フルイヤーとして使う仕様どおりの奇妙な後方互換ロジック。
5783        ("var d=new Date(2024,0,1); d.setYear(5); d.getFullYear()", "1905"),
5784        ("var d=new Date(2024,0,1); d.setYear(2030); d.getFullYear()", "2030"),
5785        (
5786            "var d=new Date(2024,0,15); d.setMonth(5); d.getMonth()",
5787            "5",
5788        ),
5789        (
5790            "var d=new Date(2024,0,15); d.setDate(d.getDate()+1); d.getDate()",
5791            "16",
5792        ),
5793        // setDate は月境界を仕様どおり繰り上げる。
5794        (
5795            "var d=new Date(2024,0,31); d.setDate(d.getDate()+1); (d.getMonth()+1)+'-'+d.getDate()",
5796            "2-1",
5797        ),
5798        (
5799            "var d=new Date(2024,0,15,10,20,30); d.setHours(23); d.getHours()+':'+d.getMinutes()+':'+d.getSeconds()",
5800            "23:20:30",
5801        ),
5802        (
5803            "var d=new Date(2024,0,15,10,20,30); d.setMinutes(5); d.getMinutes()",
5804            "5",
5805        ),
5806        (
5807            "var d=new Date(2024,0,15,10,20,30); d.setSeconds(59); d.getSeconds()",
5808            "59",
5809        ),
5810        (
5811            "var d=new Date(2024,0,15); d.setMilliseconds(123); d.getMilliseconds()",
5812            "123",
5813        ),
5814        (
5815            "var d=new Date(0); d.setTime(5000); d.getTime()",
5816            "5000",
5817        ),
5818        // set* の戻り値は仕様どおり新しい getTime() と同じ。
5819        (
5820            "var d=new Date(0); var r=d.setFullYear(2000); r === d.getTime()",
5821            "true",
5822        ),
5823        // `Date` は `to_number()` に `ObjKind::DateObj` 特殊扱いを追加したことで、
5824        // `.getTime()` を明示せずとも算術/比較演算がそのまま動く
5825        // (この処理系はオブジェクト全般の `valueOf`/`Symbol.toPrimitive` は汎用サポートしない
5826        // ため、これは `Date` だけの特殊扱い)。
5827        ("new Date(1000) - new Date(0)", "1000"),
5828        ("new Date(0) < new Date(1000)", "true"),
5829        ("+new Date(500)", "500"),
5830        ("typeof Date.now()", "number"),
5831        // `delete`(ES1 以来の基礎演算子)が AST/パーサ/インタプリタのどこにも存在せず、
5832        // 丸ごと未実装だった(`delete` トークン自体はレキサーが認識するのに、パーサが
5833        // どの式としても解釈しない状態)。
5834        ("var o = {x:1}; delete o.x; 'x' in o", "false"),
5835        ("var o = {x:1, y:2}; delete o.x; o.y", "2"),
5836        ("var k='x'; var o = {x:1}; delete o[k]; 'x' in o", "false"),
5837        ("var a=[1,2,3]; delete a[1]; a[1]", "undefined"),
5838        ("var a=[1,2,3]; delete a[1]; a.length", "3"),
5839        ("delete 5", "true"),
5840        // カンマ演算子 — 以前はパーサが先行する式を単に上書きして AST から消してしまい、
5841        // その式の副作用(関数呼び出し・代入等)が一切実行されない重大なバグだった。
5842        ("var s=''; (s+='a', s+='b', s+='c'); s", "abc"),
5843        ("var x=(1,2,3); x", "3"),
5844        (
5845            "var s=''; for (var i=0,j=10; i<3; i++,j--) { s += i+':'+j+' '; } s",
5846            "0:10 1:9 2:8 ",
5847        ),
5848        ("var a; var b; (a=1, b=a+1); a+','+b", "1,2"),
5849        // `generator.throw(err)`(ES2015)が丸ごと未実装だった(`next`/`return` はあったが
5850        // `throw` だけが欠落)。中断中の yield 位置に例外を注入する。
5851        (
5852            "function* g(){ try { yield 1; yield 2; } catch(e) { yield 'caught:'+e; } } var it=g(); it.next(); it.throw('boom').value",
5853            "caught:boom",
5854        ),
5855        (
5856            "function* g(){ yield 1; yield 2; } var it=g(); it.next(); var r; try { it.throw('x'); } catch(e) { r = 'propagated:'+e; } r",
5857            "propagated:x",
5858        ),
5859        // `String.prototype.codePointAt`(ES2015)/`localeCompare`(ES3)が丸ごと未実装だった。
5860        ("'A'.codePointAt(0)", "65"),
5861        ("'ABC'.codePointAt(5)", "undefined"),
5862        ("'a'.localeCompare('b')", "-1"),
5863        ("'b'.localeCompare('a')", "1"),
5864        ("'a'.localeCompare('a')", "0"),
5865        // `String.prototype.substr`(ES3 由来の Annex B 非推奨メソッド)が `slice`/
5866        // `substring` はあったのに欠落していた。古いコードで依然として広く使われる。
5867        ("'Hello World'.substr(6)", "World"),
5868        ("'Hello World'.substr(0, 5)", "Hello"),
5869        ("'Hello World'.substr(-5)", "World"),
5870        ("'Hello World'.substr(-5, 3)", "Wor"),
5871        // `static { ... }`(ES2022 静的初期化ブロック)が未実装だった。パーサはメソッド名
5872        // として識別子/文字列/数値/`[computed]` しか認識せず、`{` が来ると読み飛ばして
5873        // 続行する未実装フォールバックに落ちていた。
5874        (
5875            "class C { static x; static { C.x = 1 + 2; } } C.x",
5876            "3",
5877        ),
5878        (
5879            "class C { static x = 10; static { C.x += 5; } } C.x",
5880            "15",
5881        ),
5882        // 複数の static ブロックは宣言順に実行される。
5883        (
5884            "class C { static log=''; static { C.log+='a'; } static { C.log+='b'; } } C.log",
5885            "ab",
5886        ),
5887        // `Array.prototype.flat(depth)` が `depth` 引数を完全に無視し、常に1段しか
5888        // 展開しないバグだった。`.flat(Infinity)`(深いネスト解除の定番イディオム)も
5889        // 動作していなかった。
5890        ("[1,[2,3]].flat().join(',')", "1,2,3"),
5891        ("[1,[2,[3,[4]]]].flat(2).join(',')", "1,2,3,4"),
5892        ("[1,[2,[3,[4,[5]]]]].flat(Infinity).join(',')", "1,2,3,4,5"),
5893        // `.flat(Infinity)` は循環参照する配列(`a.push(a)`)に対しては祖先追跡
5894        // ガードが無いと無限再帰になり、この no_std 環境ではスタック
5895        // オーバーフロー(クラッシュ/ハング)に直結し得るバグだった(2026-07-13。
5896        // `Value::to_js_string` で修正済みの循環参照系と同種)。クラッシュ/
5897        // ハングせず完走することを確認する(既に祖先に現れた配列はそれ以上
5898        // 展開されずそのまま結果に含まれるため、要素数は打ち切り位置に依存)。
5899        ("var a=[1]; a.push(a); a.flat(Infinity); 'ok'", "ok"),
5900        // depth=0 は展開しない(トップレベルの要素数がそのまま length に残ることで確認)。
5901        ("[1,[2,[3]]].flat(0).length", "2"),
5902        // `Object.freeze()` が明示的な no-op(同一オブジェクトを返すだけ)だった。
5903        // `Object.isFrozen()` 自体も丸ごと欠落していた。
5904        ("var o={x:1}; Object.freeze(o); o.x=2; o.x", "1"),
5905        ("var o={x:1}; Object.freeze(o); o.y=2; 'y' in o", "false"),
5906        ("var o={x:1}; Object.freeze(o); delete o.x; o.x", "1"),
5907        ("var o={x:1}; Object.isFrozen(o)", "false"),
5908        ("var o={x:1}; Object.freeze(o); Object.isFrozen(o)", "true"),
5909        ("Object.isFrozen(5)", "true"),
5910        // `Object.seal`/`isSealed`/`preventExtensions`/`isExtensible` も丸ごと欠落していた。
5911        ("var o={x:1}; Object.seal(o); o.x=2; o.x", "2"),
5912        ("var o={x:1}; Object.seal(o); o.y=2; 'y' in o", "false"),
5913        ("var o={x:1}; Object.seal(o); delete o.x; 'x' in o", "true"),
5914        ("var o={x:1}; Object.isSealed(o)", "false"),
5915        ("var o={x:1}; Object.seal(o); Object.isSealed(o)", "true"),
5916        (
5917            "var o={x:1}; Object.preventExtensions(o); o.y=2; 'y' in o",
5918            "false",
5919        ),
5920        (
5921            "var o={x:1}; Object.preventExtensions(o); o.x=5; delete o.x; 'x' in o",
5922            "false",
5923        ),
5924        ("var o={x:1}; Object.isExtensible(o)", "true"),
5925        (
5926            "var o={x:1}; Object.preventExtensions(o); Object.isExtensible(o)",
5927            "false",
5928        ),
5929        // `Array.isArray` は仕様上 Proxy を透過して target を見る必要があるが、
5930        // 以前は Proxy でラップした配列が false 判定になっていた。
5931        ("Array.isArray(new Proxy([1,2], {}))", "true"),
5932        ("Array.isArray(new Proxy({}, {}))", "false"),
5933        // TypedArray も内部表現は ObjKind::Array を流用するため、`_ta_kind` タグを
5934        // 見ずに判定すると `Array.isArray(new Int8Array(...))` が仕様に反して
5935        // `true` になるバグだった(TypedArray は Array exotic object ではない)。
5936        ("Array.isArray(new Int8Array([1,2]))", "false"),
5937        ("Array.isArray([1,2])", "true"),
5938        // `Object.keys`/`values`/`entries` は Proxy を素通しできず(Proxy 自身は props を
5939        // 持たないため)常に空を返すバグだった。target フォワードのみの Proxy では
5940        // 正しく動くようにした(ownKeys トラップの呼び出しまでは非対応)。
5941        ("Object.keys(new Proxy({a:1,b:2}, {})).sort().join(',')", "a,b"),
5942        ("Object.values(new Proxy({a:1,b:2}, {})).sort().join(',')", "1,2"),
5943        (
5944            "Object.entries(new Proxy({a:1}, {})).map(e=>e[0]+':'+e[1]).join(',')",
5945            "a:1",
5946        ),
5947        // `JSON.stringify` も同じ Proxy 透過漏れで、Proxy を渡すと常に `{}` になっていた。
5948        ("JSON.stringify(new Proxy({a:1,b:2}, {}))", "{\"a\":1,\"b\":2}"),
5949        // `for...of`/スプレッドも同じ Proxy 透過漏れで、target フォワードのみの Proxy が
5950        // 配列/Set/Map を包んでいても何も反復しないバグだった(`iterate_values()` が自身の
5951        // `ObjKind` だけを見て Proxy を Array 等として認識できていなかった)。
5952        (
5953            "var s=''; for (const x of new Proxy([1,2,3], {})) { s += x; } s",
5954            "123",
5955        ),
5956        ("[...new Proxy([1,2,3], {})].join(',')", "1,2,3"),
5957        // `Object.assign` も同じ系統のバグを2つ抱えていた: (1) ソースが Proxy だと
5958        // 何もコピーされない、(2) ソースが配列だと要素が `ObjKind::Array` 側にあり
5959        // `props` には無いため、これも何もコピーされない。
5960        (
5961            "var t = Object.assign({}, new Proxy({a:1,b:2}, {})); t.a+','+t.b",
5962            "1,2",
5963        ),
5964        (
5965            "var t = Object.assign({}, [10,20,30]); t[0]+','+t[1]+','+t[2]",
5966            "10,20,30",
5967        ),
5968        // `Reflect.deleteProperty` は `delete` 演算子と同じ凍結/封印済みオブジェクト保護を
5969        // 適用すべきだが、以前は一切チェックせず常に削除・成功させていた(非対称だった)。
5970        (
5971            "var o={x:1}; Object.freeze(o); Reflect.deleteProperty(o,'x')",
5972            "false",
5973        ),
5974        ("var o={x:1}; Reflect.deleteProperty(o,'x')", "true"),
5975        // `Reflect.set` も同様に、以前は常に `true` を返しており、凍結済みオブジェクトへの
5976        // 書込みが黙殺されていても呼び出し元からは成功したように見えていた。
5977        (
5978            "var o={x:1}; Object.freeze(o); Reflect.set(o,'x',2)",
5979            "false",
5980        ),
5981        ("var o={x:1}; Reflect.set(o,'x',2); o.x", "2"),
5982        // `Array.prototype.includes` は SameValueZero で比較すべきところ `===` を使っており、
5983        // `[NaN].includes(NaN)` が false になるバグだった(`indexOf` は `===` のままで正しい)。
5984        ("[NaN].includes(NaN)", "true"),
5985        ("[NaN].indexOf(NaN)", "-1"),
5986        ("[1,2,NaN].includes(NaN)", "true"),
5987        ("[1,2,3].includes(2)", "true"),
5988        ("[1,2,3].includes(5)", "false"),
5989        // 高階配列メソッドの第2引数 `thisArg` が `map`/`filter`/`forEach`/`find` 系
5990        // すべてで無視されていた(コールバックの `this` を常に `undefined` 決め打ちしていた)。
5991        (
5992            "var ctx={mul:10}; [1,2,3].map(function(x){return x*this.mul;}, ctx).join(',')",
5993            "10,20,30",
5994        ),
5995        (
5996            "var ctx={min:2}; [1,2,3].filter(function(x){return x>this.min;}, ctx).join(',')",
5997            "3",
5998        ),
5999        (
6000            "var ctx={sum:0}; [1,2,3].forEach(function(x){this.sum+=x;}, ctx); ctx.sum",
6001            "6",
6002        ),
6003        (
6004            "var ctx={target:2}; [1,2,3].find(function(x){return x===this.target;}, ctx)",
6005            "2",
6006        ),
6007        (
6008            "var ctx={target:5}; [1,2,3].some(function(x){return x===this.target;}, ctx)",
6009            "false",
6010        ),
6011        (
6012            "var ctx={min:0}; [1,2,3].every(function(x){return x>this.min;}, ctx)",
6013            "true",
6014        ),
6015        // 同じ `thisArg` 無視バグが `Map.prototype.forEach`/`Set.prototype.forEach`/
6016        // `Array.from` の第3引数にもあった。
6017        (
6018            "var ctx={sum:0}; var m=new Map([['a',1],['b',2]]); m.forEach(function(v){this.sum+=v;}, ctx); ctx.sum",
6019            "3",
6020        ),
6021        (
6022            "var ctx={sum:0}; var s=new Set([1,2,3]); s.forEach(function(v){this.sum+=v;}, ctx); ctx.sum",
6023            "6",
6024        ),
6025        (
6026            "var ctx={mul:2}; Array.from([1,2,3], function(x){return x*this.mul;}, ctx).join(',')",
6027            "2,4,6",
6028        ),
6029        // `JSON.stringify` の第3引数 `space`(インデント整形。デバッグ出力の定番
6030        // イディオム `JSON.stringify(obj, null, 2)`)が完全に無視されていた。
6031        (
6032            "JSON.stringify({a:1,b:2}, null, 2)",
6033            "{\n  \"a\": 1,\n  \"b\": 2\n}",
6034        ),
6035        ("JSON.stringify([1,2], null, 2)", "[\n  1,\n  2\n]"),
6036        ("JSON.stringify({}, null, 2)", "{}"),
6037        ("JSON.stringify([], null, 2)", "[]"),
6038        ("JSON.stringify({a:1}, null, '\\t')", "{\n\t\"a\": 1\n}"),
6039        // space 省略時は従来どおりコンパクト出力のまま。
6040        ("JSON.stringify({a:1,b:2})", "{\"a\":1,\"b\":2}"),
6041        // `JSON.stringify` は BigInt を直列化できず本来 TypeError を投げるべきだが、
6042        // 以前は静かに省略していた(`undefined`/関数と同じ扱いになっていたバグ)。
6043        (
6044            "try { JSON.stringify(10n); 'no-throw' } catch(e) { 'caught:' + e.name }",
6045            "caught:Error",
6046        ),
6047        (
6048            "try { JSON.stringify({a:10n}); 'no-throw' } catch(e) { 'caught' }",
6049            "caught",
6050        ),
6051        // `replacer` 引数(キーの絞り込み用配列 / 値差し替え関数)も同様に丸ごと未対応だった。
6052        ("JSON.stringify({a:1,b:2,c:3}, ['a','c'])", "{\"a\":1,\"c\":3}"),
6053        (
6054            "JSON.stringify({a:1,b:2}, function(k,v){ return typeof v === 'number' ? v*10 : v; })",
6055            "{\"a\":10,\"b\":20}",
6056        ),
6057        // replacer 配列はオブジェクトのキー絞り込みのみに適用され、配列要素自体は
6058        // 絞り込まれない(仕様どおり)。
6059        ("JSON.stringify([1,2,3], ['a'])", "[1,2,3]"),
6060        // `JSON.parse` の第2引数 `reviver`(キーをボトムアップで巡る変換関数)も
6061        // 同様に丸ごと未対応だった。
6062        (
6063            "JSON.parse('{\"a\":1,\"b\":2}', function(k,v){ return typeof v === 'number' ? v*10 : v; }).a",
6064            "10",
6065        ),
6066        (
6067            "var r = JSON.parse('{\"a\":1,\"b\":2}', function(k,v){ return k==='b' ? undefined : v; }); 'b' in r",
6068            "false",
6069        ),
6070        (
6071            "JSON.parse('[1,2,3]', function(k,v){ return typeof v === 'number' ? v+1 : v; }).join(',')",
6072            "2,3,4",
6073        ),
6074        ("JSON.parse('{\"a\":1}').a", "1"),
6075        // `String.prototype.replaceAll` が RegExp パターン(`g` フラグ必須)・関数
6076        // リプレーサのどちらも未対応だった(RegExp を渡すと文字列表現 "/pat/flags" として
6077        // 扱われ無変換になっていた)。
6078        ("'aaa'.replaceAll(/a/g, 'b')", "bbb"),
6079        (
6080            "try { 'aaa'.replaceAll(/a/, 'b'); 'no-throw' } catch(e) { 'threw' }",
6081            "threw",
6082        ),
6083        (
6084            "'a1b2'.replaceAll(/[0-9]/g, function(m){ return '['+m+']'; })",
6085            "a[1]b[2]",
6086        ),
6087        ("'aXaXa'.replaceAll('X', function(){ return '-'; })", "a-a-a"),
6088        ("'abc'.replaceAll('x', 'y')", "abc"),
6089        // 同一の `var` 文の中で、前の宣言子を後の初期化子が参照できるか。
6090        // jQuery 1.8.2 の先頭が
6091        // `var ...,h=[],p="1.8.2",d=h.concat,...,w=p.trim,...` という形で、
6092        // ここが順に評価されないと `p` が undefined になり
6093        // 「Cannot read properties of undefined (reading 'trim')」で落ちる。
6094        ("var a1 = 1, b1 = a1 + 1; b1", "2"),
6095        ("var s1 = 'xy', t1 = s1.length; t1", "2"),
6096        ("var h1 = [], p1 = '1.8.2', w1 = p1.trim; typeof w1", "function"),
6097        ("var q1 = [], r1 = q1.push; typeof r1", "function"),
6098        // `String.prototype` が丸ごと無く、プロトタイプから直接読む形が
6099        // undefined になっていた。jQuery 1.8.2 の `o=String.prototype.trim`
6100        // で本体が実行時に落ちていた。
6101        ("typeof String.prototype", "object"),
6102        ("typeof String.prototype.trim", "function"),
6103        ("typeof String.prototype.slice", "function"),
6104        ("String.prototype.trim.call('  x  ')", "x"),
6105        ("String.prototype.toUpperCase.call('ab')", "AB"),
6106        // 実体からの解決は従来どおり。
6107        ("'  y '.trim()", "y"),
6108        // 素のオブジェクト/配列/関数から、プロトタイプ経由で
6109        // メソッドを**値として**読めるか。jQuery は
6110        // `core_toString = class2type.toString`(`class2type = {}`)のように
6111        // 読んでから `.call(...)` する。読めないと
6112        // 「Cannot read properties of undefined (reading 'call')」になる。
6113        ("typeof ({}).toString", "function"),
6114        ("typeof ({}).hasOwnProperty", "function"),
6115        ("typeof [].slice", "function"),
6116        ("typeof [].concat", "function"),
6117        ("typeof (function(){}).call", "function"),
6118        ("typeof (function(){}).apply", "function"),
6119        ("({}).toString.call([])", "[object Array]"),
6120        // `Array.prototype` はオブジェクトとしては在ったがメソッドが空だった。
6121        // jQuery 1.8.2 の
6122        // `j=Array.prototype.push,k=Array.prototype.slice,l=Array.prototype.indexOf`
6123        // で undefined になり、続く `k.call(...)` で落ちていた。
6124        ("typeof Array.prototype.push", "function"),
6125        ("typeof Array.prototype.slice", "function"),
6126        ("typeof Array.prototype.indexOf", "function"),
6127        ("Array.prototype.slice.call([1,2,3], 1).join(',')", "2,3"),
6128        ("Array.prototype.indexOf.call([4,5,6], 5)", "1"),
6129        // `Object.prototype` 側も jQuery が読む 2 つを確かめる。
6130        ("typeof Object.prototype.toString", "function"),
6131        ("typeof Object.prototype.hasOwnProperty", "function"),
6132        // jQuery 1.8.2 の機能検出は、**切り離された**要素へ innerHTML を入れ、
6133        // そこから要素を取り出して `style` を触る。この一連が通らないと
6134        // 「Cannot read properties of undefined (reading 'style')」になる。
6135        ("var d = document.createElement('div'); typeof d", "object"),
6136        ("var d2 = document.createElement('div'); typeof d2.style", "object"),
6137        (
6138            "var d3 = document.createElement('div'); d3.innerHTML = \"<a href='/a'>a</a>\";              d3.getElementsByTagName('a').length",
6139            "1",
6140        ),
6141        (
6142            "var d4 = document.createElement('div'); d4.innerHTML = \"<a href='/a'>a</a>\";              typeof d4.getElementsByTagName('a')[0]",
6143            "object",
6144        ),
6145        (
6146            "var d5 = document.createElement('div'); d5.innerHTML = \"<a href='/a'>a</a>\";              typeof d5.getElementsByTagName('a')[0].style",
6147            "object",
6148        ),
6149        (
6150            "var d6 = document.createElement('div');              d6.innerHTML = '<table></table><a href=\"/a\">a</a>';              d6.getElementsByTagName('*').length > 0",
6151            "true",
6152        ),
6153        (
6154            "var d7 = document.createElement('div'); d7.style.cssText = 'top:1px';              typeof d7.style.cssText",
6155            "string",
6156        ),
6157        (
6158            "var d8 = document.createElement('div'); d8.setAttribute('className','t');              d8.getAttribute('className')",
6159            "t",
6160        ),
6161        // jQuery 1.8.2 の機能検出が実際に入れる HTML。先頭の空白 2 つ・
6162        // 自己閉じの `<link/>`・`<table></table>` が `<a>` の前に来る。
6163        // ここで `<a>` が取れないと `d.style` で落ちる。
6164        // どの要素が原因かを分けて確かめる。
6165        (
6166            "var e1 = document.createElement('div'); e1.innerHTML = '<link/><a href=\"/a\">a</a>';              e1.getElementsByTagName('a').length",
6167            "1",
6168        ),
6169        (
6170            "var e2 = document.createElement('div');              e2.innerHTML = '<table></table><a href=\"/a\">a</a>';              e2.getElementsByTagName('a').length",
6171            "1",
6172        ),
6173        (
6174            "var e3 = document.createElement('div'); e3.innerHTML = '  <a href=\"/a\">a</a>';              e3.getElementsByTagName('a').length",
6175            "1",
6176        ),
6177        (
6178            "var e4 = document.createElement('div');              e4.innerHTML = '<input type=\"checkbox\"/><a href=\"/a\">a</a>';              e4.getElementsByTagName('a').length",
6179            "1",
6180        ),
6181        // jQuery が入れる文字列そのもの。
6182        (
6183            "var e5 = document.createElement('div');              e5.innerHTML = '  <link/><table></table><a href=\"/a\">a</a><input type=\"checkbox\"/>';              e5.getElementsByTagName('a').length",
6184            "1",
6185        ),
6186        (
6187            "var e6 = document.createElement('div');              e6.innerHTML = '  <link/><table></table><a href=\"/a\">a</a><input type=\"checkbox\"/>';              e6.getElementsByTagName('*').length > 0",
6188            "true",
6189        ),
6190        // jQuery は IIFE の中で `e = a.document`(`a = window`)として
6191        // **`window.document` 経由**で触る。グローバルの `document` を
6192        // 直接使う経路とは別なので、そちらも確かめる。
6193        ("typeof window.document", "object"),
6194        // グローバルにあるのに `window.` 経由で読めない漏れ。
6195        // jQuery 1.8.2 は `a.navigator.userAgent`(`a` は window)と書く。
6196        ("typeof window.navigator", "object"),
6197        ("typeof window.navigator.userAgent", "string"),
6198        ("window.navigator === navigator", "true"),
6199        ("typeof window.location", "object"),
6200        ("typeof window.JSON", "object"),
6201        ("typeof window.Math", "object"),
6202        ("typeof window.XMLHttpRequest", "function"),
6203        // `window.x = ...` の後に素の `x` で読めること。
6204        // jQuery 1.8.2 は最後に `a.jQuery = a.$ = p`(`a` は window)で
6205        // 自分を公開するので、これが無いと **jQuery が完走しても
6206        // `$ is not defined`** になる。
6207        ("window.myGlobal1 = 42; myGlobal1", "42"),
6208        ("window.myFn1 = function(){ return 'z'; }; myFn1()", "z"),
6209        ("window.mySelf1 = window; typeof mySelf1.document", "object"),
6210        // スコープの変数が優先される(window 側で影を作らない)。
6211        ("window.shadow1 = 'w'; var shadow1 = 'v'; shadow1", "v"),
6212        // `Object.prototype.toString` はプリミティブの種別を返す。
6213        // 全部 `[object Object]` だと、jQuery の `jQuery.extend(true, ...)` が
6214        // 文字列を「素のオブジェクト」と誤判定して `{}` に置き換える
6215        // (実際に `s.type`("GET")が壊れ `$.getJSON` が落ちていた)。
6216        ("Object.prototype.toString.call('x')", "[object String]"),
6217        ("Object.prototype.toString.call(1)", "[object Number]"),
6218        ("Object.prototype.toString.call(true)", "[object Boolean]"),
6219        ("Object.prototype.toString.call(null)", "[object Null]"),
6220        ("Object.prototype.toString.call(undefined)", "[object Undefined]"),
6221        ("Object.prototype.toString.call({})", "[object Object]"),
6222        ("Object.prototype.toString.call([])", "[object Array]"),
6223        ("Object.prototype.toString.call(function(){})", "[object Function]"),
6224        ("window.document === document", "true"),
6225        ("typeof window.document.createElement", "function"),
6226        (
6227            "var w1 = window.document.createElement('div'); typeof w1.style",
6228            "object",
6229        ),
6230        (
6231            "var w2 = window.document.createElement('div');              w2.innerHTML = '  <link/><table></table><a href=\"/a\">a</a><input type=\"checkbox\"/>';              typeof w2.getElementsByTagName('a')[0]",
6232            "object",
6233        ),
6234        // 関数の引数として渡した window から辿る形(jQuery の IIFE と同じ)。
6235        (
6236            "(function(a){ var e = a.document, n = e.createElement('div');              n.setAttribute('className','t');              n.innerHTML = '  <link/><table></table><a href=\"/a\">a</a><input type=\"checkbox\"/>';              var d = n.getElementsByTagName('a')[0]; return typeof d; })(window)",
6237            "object",
6238        ),
6239        // `replaceAll`/`matchAll` が独自の `[Symbol.replace]`/`[Symbol.matchAll]` へ
6240        // 委譲していなかった(`replace`/`match`/`search`/`split` は委譲済みだった)。
6241        // 【順序】`g` フラグの検査が**先**、委譲はその後。逆にすると
6242        // 非 g の RegExp が委譲側へ流れて TypeError が出なくなる。
6243        (
6244            "var o = {}; o[Symbol.replace] = function(s, r){ return 'X' + s + r; };              'ab'.replaceAll(o, 'Z')",
6245            "XabZ",
6246        ),
6247        (
6248            "var o = {}; o[Symbol.matchAll] = function(s){ return ['m:' + s]; };              'ab'.matchAll(o)[0]",
6249            "m:ab",
6250        ),
6251        // 委譲を足しても、非 g の RegExp は従来どおり TypeError のまま。
6252        (
6253            "try { 'aaa'.matchAll(/a/); 'no-throw' } catch(e) { 'threw' }",
6254            "threw",
6255        ),
6256        // `String.prototype.split` の第2引数 `limit` が完全に無視されていた。
6257        ("'a,b,c,d'.split(',', 2).join('|')", "a|b"),
6258        ("'a,b,c'.split(',', 0).length", "0"),
6259        ("'a1b2c3'.split(/[0-9]/, 2).join('|')", "a|b"),
6260        ("'a,b,c'.split(',').join('|')", "a|b|c"),
6261        // `matchAll` は `g` フラグ必須(無ければ TypeError)だが、以前は無条件に
6262        // 全件マッチを返していた。
6263        (
6264            "try { 'aa'.matchAll(/a/); 'no-throw' } catch(e) { 'threw' }",
6265            "threw",
6266        ),
6267        (
6268            "[...'a1b2'.matchAll(/[0-9]/g)].map(m=>m[0]).join(',')",
6269            "1,2",
6270        ),
6271        // `structuredClone` は循環参照(自己参照)を扱えず無限再帰(この no_std 環境では
6272        // クラッシュ/ハングになり得る)に陥るバグだった。
6273        (
6274            "var a={x:1}; a.self=a; var c=structuredClone(a); c.self===c",
6275            "true",
6276        ),
6277        ("var a={x:1}; a.self=a; var c=structuredClone(a); c.x", "1"),
6278        // 同一オブジェクトへの複数参照は複製後も共有構造として保たれる(仕様どおり)。
6279        (
6280            "var shared={v:1}; var o={a:shared,b:shared}; var c=structuredClone(o); c.a===c.b",
6281            "true",
6282        ),
6283        // `JSON.stringify` にも同じ「循環参照で無限再帰」バグがあった。仕様どおり
6284        // TypeError を投げるようにした(同一オブジェクトへの非循環な複数参照=
6285        // 構造共有は問題なく直列化できることも確認)。
6286        (
6287            "try { var a={}; a.self=a; JSON.stringify(a); 'no-throw' } catch(e) { 'threw' }",
6288            "threw",
6289        ),
6290        // 深いネスト(30段超)でもスタックオーバーフローせずTypeErrorをスローするかの保護テスト
6291        (
6292            "var cur={}; for(var i=0;i<40;i++){ cur={child:cur}; } try { JSON.stringify(cur); 'no-throw' } catch(e) { 'depth-exceeded' }",
6293            "depth-exceeded",
6294        ),
6295        (
6296            "var shared={v:1}; JSON.stringify({a:shared,b:shared})",
6297            "{\"a\":{\"v\":1},\"b\":{\"v\":1}}",
6298        ),
6299        // `console.log` の表示整形(`display_value`)にも同じ「循環参照で無限再帰」
6300        // バグがあった。`console.log(a)`(`a.self=a`)は特に日常的にありうる操作のため、
6301        // 一番踏みやすいケースだった(クラッシュ/ハングせず完走することを確認)。
6302        (
6303            "var a={}; a.self=a; console.log(a); 'ok'",
6304            "ok",
6305        ),
6306        (
6307            "var a=[1,2]; a.push(a); console.log(a); 'ok'",
6308            "ok",
6309        ),
6310        // `console.log` の表示整形(`display_value_inner`)が `Proxy` を
6311        // `Array`/`Plain` 以外への素通しフォールバックに落としており、
6312        // `[ 1, 2, 3 ]` のような角括弧付き表示にならず素の `"1,2,3"` に
6313        // なっていた(`Value::to_js_string` の Proxy 素通しバグ修正と同時に
6314        // 発見。target へ委譲して同じ表示ロジックへ再帰するよう修正)。
6315        (
6316            "console.log(new Proxy([1,2,3], {})); 'ok'",
6317            "ok",
6318        ),
6319        // `Value::to_js_string`(`Array.prototype.join`/`toString`・テンプレート
6320        // リテラル補間・暗黙の文字列連結が共通で使う変換)にも同じ「循環参照で
6321        // 無限再帰」バグが残っていた(`structuredClone`/`JSON.stringify`/
6322        // `console.log` は既に修正済みだったが、この経路だけ未点検だった)。
6323        // `a.push(a); a.join()` はクラッシュ/ハングせず完走し、循環要素は
6324        // 仕様どおり空文字列として扱われることを確認する。
6325        ("var a=[1]; a.push(a); a.join()", "1,1,"),
6326        ("var a=[1]; a.push(a); String(a); 'ok'", "ok"),
6327        ("var a=[1]; a.push(a); `${a}`; 'ok'", "ok"),
6328        // `Number.prototype.toString(radix)` が `radix` 引数を完全に無視し常に10進表示
6329        // だった(`n.toString(16)` 等の定番イディオムが動かなかった)。
6330        ("(255).toString(16)", "ff"),
6331        ("(8).toString(2)", "1000"),
6332        ("(255).toString()", "255"),
6333        ("(-255).toString(16)", "-ff"),
6334        ("(0).toString(2)", "0"),
6335        (
6336            "try { (1).toString(1); 'no-throw' } catch(e) { 'threw' }",
6337            "threw",
6338        ),
6339        // `parseInt` が `radix` 未指定時に `0x` 接頭辞を自動検出せず、明示的に
6340        // `radix=16` を渡しても `0x` を読み飛ばさないバグだった。
6341        ("parseInt('0xFF')", "255"),
6342        ("parseInt('0xFF', 16)", "255"),
6343        ("parseInt('FF', 16)", "255"),
6344        ("parseInt('10')", "10"),
6345        ("parseInt('-0x10')", "-16"),
6346        ("parseInt('10', 2)", "2"),
6347        // `Number.parseFloat`/`Number.parseInt`(ES2015)は仕様上グローバル
6348        // `parseFloat`/`parseInt` と同一の関数オブジェクトでなければならないが、
6349        // 以前は別々に構築されており `===` が `false` になっていた。
6350        ("Number.parseFloat === parseFloat", "true"),
6351        ("Number.parseInt === parseInt", "true"),
6352        ("Number.parseFloat.name", "parseFloat"),
6353        ("Number.parseInt.name", "parseInt"),
6354        // `JSON.stringify` は `Date` の `toJSON`(ISO 文字列)相当を特殊扱いする。
6355        // 以前は Date インスタンスが(`toJSON` を汎用的にチェックしないため)
6356        // プロパティなしの `{}` として直列化されてしまっていた。
6357        (
6358            "JSON.stringify(new Date(0))",
6359            "\"1970-01-01T00:00:00.000Z\"",
6360        ),
6361        (
6362            "JSON.stringify({d: new Date(0)})",
6363            "{\"d\":\"1970-01-01T00:00:00.000Z\"}",
6364        ),
6365        // 文字列化(テンプレートリテラル補間・暗黙の文字列連結)でも `[object Object]` ではなく
6366        // ISO 形式が出るようにした(`Value::to_js_string()` に `ObjKind::DateObj` 分岐を追加)。
6367        ("'' + new Date(0)", "1970-01-01T00:00:00.000Z"),
6368        ("`date=${new Date(0)}`", "date=1970-01-01T00:00:00.000Z"),
6369        // Object.defineProperty / Property attributes (writable, configurable, enumerable)
6370        (
6371            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); o.x = 2; o.x",
6372            "1",
6373        ),
6374        (
6375            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); delete o.x",
6376            "false",
6377        ),
6378        (
6379            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); Object.keys(o).length",
6380            "0",
6381        ),
6382        (
6383            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); var keys=[]; for(var k in o){ keys.push(k); } keys.length",
6384            "0",
6385        ),
6386        (
6387            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); JSON.stringify(o)",
6388            "{}",
6389        ),
6390        (
6391            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); ({...o}).x",
6392            "undefined",
6393        ),
6394        (
6395            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); Object.assign({}, o).x",
6396            "undefined",
6397        ),
6398        (
6399            "var o={}; Object.defineProperty(o, 'x', {value: 1, writable: false, configurable: false, enumerable: false}); Reflect.deleteProperty(o, 'x')",
6400            "false",
6401        ),
6402        // `DataView.prototype.getBigInt64`/`.getBigUint64`/`.setBigInt64`/
6403        // `.setBigUint64`(ES2020)が丸ごと未対応だった。64bit整数はNumberの
6404        // 安全整数範囲(2^53)を超えうるためBigIntで往復する必要がある。
6405        // 2026-07-18 発見・実装)。
6406        (
6407            "var b=new ArrayBuffer(8); var v=new DataView(b); v.setBigInt64(0, 123456789012345n); v.getBigInt64(0).toString()",
6408            "123456789012345",
6409        ),
6410        (
6411            "var b=new ArrayBuffer(8); var v=new DataView(b); v.setBigInt64(0, -1n); v.getBigInt64(0).toString()",
6412            "-1",
6413        ),
6414        (
6415            "var b=new ArrayBuffer(8); var v=new DataView(b); v.setBigUint64(0, -1n); v.getBigUint64(0).toString()",
6416            "18446744073709551615",
6417        ),
6418        (
6419            "var b=new ArrayBuffer(8); var v=new DataView(b); v.setBigInt64(0, 1n, true); v.getBigInt64(0, true).toString()",
6420            "1",
6421        ),
6422        // `JSON.rawJSON`/`JSON.isRawJSON`(ES2025)が丸ごと未対応だった。
6423        // Numberの精度で表現できない巨大な整数をJSON文字列へ精度損失無しで
6424        // 埋め込むための機能。2026-07-18 発見・実装)。
6425        (
6426            "JSON.stringify({n: JSON.rawJSON('123456789012345678901234567890')})",
6427            "{\"n\":123456789012345678901234567890}",
6428        ),
6429        ("JSON.isRawJSON(JSON.rawJSON('42'))", "true"),
6430        ("JSON.isRawJSON({rawJSON: '42'})", "false"),
6431        ("JSON.isRawJSON(42)", "false"),
6432        (
6433            "(function(){ try { JSON.rawJSON(' 1'); return 'no-throw'; } catch(e) { return e instanceof Error; } })()",
6434            "true",
6435        ),
6436        (
6437            "(function(){ try { JSON.rawJSON('not json'); return 'no-throw'; } catch(e) { return e instanceof Error; } })()",
6438            "true",
6439        ),
6440        // `button.command`/`.commandForElement`(Invoker Commands API)が
6441        // 丸ごと未対応だった。`popoverTargetAction`/`.popoverTargetElement`と
6442        // 全く同じ反映・idref解決パターンで実装(クリック時の実際の
6443        // `CommandEvent`発火/組込み動作は本サイクルでは未対応。
6444        // 2026-07-18 発見・実装)。
6445        (
6446            "var b=document.createElement('button'); b.command='show-modal'; b.command",
6447            "show-modal",
6448        ),
6449        (
6450            "document.createElement('button').commandForElement",
6451            "null",
6452        ),
6453        // `commandForElement`セッターの往復(`b.commandForElement = 要素`→
6454        // `commandfor`属性へidを反映)。この方向はDOMツリーへの接続を
6455        // 要さない(`popoverTargetElement`と同じ、対象要素のidを直接
6456        // 読むだけの実装)ため、`document.getElementById`のアタッチ要件
6457        // (共有テストフィクスチャ`wrap`が本テストスイート内の別テストで
6458        // 未接続化され得る既知の不安定要素)に依存しない自己完結した形に
6459        // している。逆方向(属性→要素解決)は`popoverTargetElement`と
6460        // 全く同じ`get_element_by_id`経路を再利用しているため別途検証済み。
6461        (
6462            "var t=document.createElement('div'); t.setAttribute('id','cfe-target'); var b=document.createElement('button'); b.commandForElement=t; b.getAttribute('commandfor')",
6463            "cfe-target",
6464        ),
6465        // プレーンオブジェクトのプロパティ列挙順序(ECMA-262
6466        // `OrdinaryOwnPropertyKeys`)が丸ごと未対応だった(`Obj.props`が
6467        // `BTreeMap`のため常に文字列の辞書式順序になっていた、
6468        // architecture-level の既知ギャップ)。`BTreeMap`→`IndexMap`移行
6469        // (挿入順保持。2026-07-18完了)に続けて、`Object.keys`/`.values`/
6470        // `.entries`へ「正整数キー→数値昇順」「残り→挿入順」の2段階
6471        // 並べ替え(`spec_key_order`)を配線し、仕様どおりの列挙順序を
6472        // 完成させた。2026-07-21 に `for...in`/`JSON.stringify`/スプレッド構文/
6473        // `Object.assign` 等すべての列挙箇所への同ロジック横展開を完了。
6474        (
6475            "Object.keys({y:1,x:2,a:3}).join(',')",
6476            "y,x,a",
6477        ),
6478        (
6479            "var o={}; delete o.x; o.b=1; o.a=2; o.c=3; delete o.a; o.a=4; Object.keys(o).join(',')",
6480            "b,c,a",
6481        ),
6482        (
6483            "Object.keys({2:'b',1:'a',10:'c'}).join(',')",
6484            "1,2,10",
6485        ),
6486        (
6487            "Object.keys({10:'c',y:1,2:'b',x:2}).join(',')",
6488            "2,10,y,x",
6489        ),
6490        (
6491            "Object.values({10:'c',y:1,2:'b',x:2}).join(',')",
6492            "b,c,1,2",
6493        ),
6494        (
6495            "var res=[]; for(var k in {10:'c',y:1,2:'b',x:2}){ res.push(k); } res.join(',')",
6496            "2,10,y,x",
6497        ),
6498        (
6499            "JSON.stringify({10:'c',y:1,2:'b',x:2})",
6500            "{\"2\":\"b\",\"10\":\"c\",\"y\":1,\"x\":2}",
6501        ),
6502        (
6503            "Object.keys({...{10:'c',y:1,2:'b',x:2}}).join(',')",
6504            "2,10,y,x",
6505        ),
6506        (
6507            "Object.keys(Object.assign({}, {10:'c',y:1,2:'b',x:2})).join(',')",
6508            "2,10,y,x",
6509        ),
6510        (
6511            "Object.entries({2:'b',y:1,1:'a'}).map(e=>e.join(':')).join(',')",
6512            "1:a,2:b,y:1",
6513        ),
6514        // `01`(先頭ゼロ)は仕様上「配列インデックス」ではなく通常の文字列
6515        // キー扱いのため数値昇順の対象外(挿入順のまま)。
6516        (
6517            "Object.keys({'01':'x', 1:'y'}).join(',')",
6518            "1,01",
6519        ),
6520        // ECMAScript Internationalization API (Intl)
6521        (
6522            "typeof Intl",
6523            "object",
6524        ),
6525        (
6526            "Intl.getCanonicalLocales('ja-JP').join(',')",
6527            "ja-JP",
6528        ),
6529        // `ja-JP`も`en-US`と同じ3桁カンマ区切り書式を使う言語のため`"123,456"`が
6530        // 正しい期待値(この処理系はロケール別の書式データベースを持たず全て
6531        // 同じ簡略書式を使うため、ロケール文字列自体は解析結果に影響しない)。
6532        // 元は書式化ロジック実装前の無加工出力`"123456"`をそのまま期待値に
6533        // していた誤り(2026-07-18 発見・修正)。
6534        (
6535            "new Intl.NumberFormat('ja-JP').format(123456)",
6536            "123,456",
6537        ),
6538        (
6539            "new Intl.DateTimeFormat('ja-JP').resolvedOptions().locale",
6540            "ja-JP",
6541        ),
6542        (
6543            "new Intl.Collator().compare('a', 'b')",
6544            "-1",
6545        ),
6546        (
6547            "new Intl.PluralRules().select(1)",
6548            "one",
6549        ),
6550        (
6551            "new Intl.RelativeTimeFormat().format(-1, 'day')",
6552            "1 days ago",
6553        ),
6554        (
6555            "new Intl.ListFormat().format(['a', 'b'])",
6556            "a, b",
6557        ),
6558        (
6559            "new Intl.DisplayNames(['en'], {type: 'language'}).of('ja')",
6560            "ja",
6561        ),
6562        // RegExp u/v flags & Unicode property escapes
6563        (
6564            "new RegExp('a', 'u').unicode",
6565            "true",
6566        ),
6567        (
6568            "new RegExp('a', 'v').unicodeSets",
6569            "true",
6570        ),
6571        (
6572            "/\\p{Letter}/u.test('A')",
6573            "true",
6574        ),
6575        (
6576            "/\\p{Number}/u.test('9')",
6577            "true",
6578        ),
6579        // Generator.prototype.return(v) & try...finally
6580        (
6581            "var finRan = false; function* g() { try { yield 1; } finally { finRan = true; } } var it = g(); it.next(); it.return(42); finRan",
6582            "true",
6583        ),
6584        // `Object.create(null)`/`Object.groupBy()`/`Object.setPrototypeOf(obj,null)`が
6585        // 実際には`Object.prototype`のメソッド(`hasOwnProperty`/`toString`等)を
6586        // 引き続き持ってしまうバグ(`proto: None`だけでは「明示的にnull
6587        // プロトタイプ」なのか「単に通常オブジェクトでチェーンが尽きた」のか
6588        // 区別できず、`get_property`の共通メソッドフォールバックが無条件に
6589        // 適用されていた)。`null_proto`フラグを新設して修正(2026-07-18
6590        // 発見・実装)。
6591        (
6592            "typeof Object.create(null).hasOwnProperty",
6593            "undefined",
6594        ),
6595        (
6596            "typeof Object.create(null).toString",
6597            "undefined",
6598        ),
6599        (
6600            "Object.getPrototypeOf(Object.create(null))",
6601            "null",
6602        ),
6603        // 通常のオブジェクトリテラルは引き続き`Object.prototype`のメソッドを
6604        // 継承する(`null_proto`の導入で既存の通常オブジェクトの挙動を
6605        // 壊していないことの回帰確認)。
6606        (
6607            "typeof {}.hasOwnProperty",
6608            "function",
6609        ),
6610        (
6611            "typeof Object.groupBy([1,2,3], x=>x%2).hasOwnProperty",
6612            "undefined",
6613        ),
6614        (
6615            "var o={}; Object.setPrototypeOf(o, null); typeof o.toString",
6616            "undefined",
6617        ),
6618        // `setPrototypeOf`で実オブジェクトへ戻せば`null_proto`が解除され、
6619        // 継承したメソッドが再び見えることの確認。
6620        (
6621            "var o={}; Object.setPrototypeOf(o, null); Object.setPrototypeOf(o, Object.prototype); typeof o.hasOwnProperty",
6622            "function",
6623        ),
6624        // `crypto.subtle.digest('SHA-256', data)`が丸ごと未対応だった
6625        // (`Float16Array`/`oklch()`と同格の「1ビットの誤りが静かに間違った
6626        // 結果を生み続けるリスク」と判断し従来は着手見送りだったが、SHA-256は
6627        // NIST/RFC公開の既知の正解値で完全一致検証できる純粋なビット演算
6628        // アルゴリズムのため浮動小数点近似とは性質が異なり、リスク評価を
6629        // 見直し実装した。以下はFIPS 180-4/一般に広く知られる既知の正解値
6630        // (Known Answer Test)。2026-07-18 発見・実装)。
6631        (
6632            "await crypto.subtle.digest('SHA-256', new TextEncoder().encode('')).then(buf => Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''))",
6633            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
6634        ),
6635        (
6636            "await crypto.subtle.digest('SHA-256', new TextEncoder().encode('abc')).then(buf => Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''))",
6637            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
6638        ),
6639        (
6640            "await crypto.subtle.digest('SHA-256', new TextEncoder().encode('The quick brown fox jumps over the lazy dog')).then(buf => Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''))",
6641            "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592",
6642        ),
6643        // `algorithm`引数を`{name: "SHA-256"}`形式(実ブラウザでも有効な
6644        // もう一方の呼び出し形式)で渡しても同じ結果になることの確認。
6645        (
6646            "await crypto.subtle.digest({name: 'SHA-256'}, new TextEncoder().encode('abc')).then(buf => Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''))",
6647            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
6648        ),
6649        // 未対応アルゴリズム名は仕様どおりPromiseのreject(例外)で通知される。
6650        (
6651            "await crypto.subtle.digest('SHA-1', new TextEncoder().encode('x')).then(() => 'resolved', () => 'rejected')",
6652            "rejected",
6653        ),
6654        // `TextDecoder.decode(arrayBuffer)`(TypedArrayビューではなく生の
6655        // `ArrayBuffer`を直接渡す、仕様上有効な呼び出し形式)が常に空文字列を
6656        // 返すバグだった(`crypto.subtle.digest`実装の検証中に発見した
6657        // `new Uint8Array(arrayBuffer)`のゼロ埋めバグと同系統の「生の
6658        // ArrayBufferから実データを読む経路」の欠落)。2026-07-18 発見・実装。
6659        (
6660            "new TextDecoder().decode(new TextEncoder().encode('hello').buffer)",
6661            "hello",
6662        ),
6663        (
6664            "new TextDecoder().decode(new Uint8Array([65,116,109,79,83]).buffer)",
6665            "AtmOS",
6666        ),
6667        // `DataView` を `TextDecoder.decode` や `crypto.subtle.digest` や
6668        // `new Uint8Array(dataView)` に渡した際、`_dv_bytes` が参照されず
6669        // 全ゼロ埋めになるバグを修正(2026-07-21 発見・実装)。
6670        (
6671            "new TextDecoder().decode(new DataView(new TextEncoder().encode('AtmOS').buffer))",
6672            "AtmOS",
6673        ),
6674        (
6675            "await crypto.subtle.digest('SHA-256', new DataView(new TextEncoder().encode('abc').buffer)).then(buf => Array.from(new Uint8Array(buf)).map(b=>b.toString(16).padStart(2,'0')).join(''))",
6676            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
6677        ),
6678        (
6679            "new Uint8Array(new DataView(new Uint8Array([65,66,67]).buffer)).join('')",
6680            "656667",
6681        ),
6682        // `String.prototype.normalize()` 日本語ひらがな・カタカナの濁点・半濁点の分解(NFD)・合成(NFC)
6683        (
6684            "'が'.normalize('NFD') === 'か\\u3099'",
6685            "true",
6686        ),
6687        (
6688            "'か\\u3099'.normalize('NFC')",
6689            "が",
6690        ),
6691        (
6692            "'パ'.normalize('NFD') === 'ハ\\u309A'",
6693            "true",
6694        ),
6695        (
6696            "'ハ\\u309A'.normalize('NFC')",
6697            "パ",
6698        ),
6699        // `structuredClone` 極端な深いネストの保護(DataCloneError 例外の返却)。
6700        // 元は閾値1000・ネスト1100段でテストしていたが、これ自体が
6701        // ブート時点の64KB初期コアスタックを枯渇させ、QEMU起動が丸ごと
6702        // 無応答になる重大な回帰を引き起こしていた(実測で深さ80段でも
6703        // 無応答、50段は安全と確認)。`deep_clone_value`の閾値を30へ
6704        // 引き下げ、ネスト段数も安全に確認できた範囲内の40へ縮小する
6705        // (2026-07-18 発見・修正)。
6706        (
6707            "var a = []; var cur = a; for (var i = 0; i < 40; i++) { var n = []; cur.push(n); cur = n; } try { structuredClone(a); 'ok'; } catch (e) { e.name; }",
6708            "DataCloneError",
6709        ),
6710        // `structuredClone` フラットな大配列(要素数>30のオブジェクト配列)が
6711        // 累積オブジェクト数(seen.len())ではなく再帰深さ(depth)で判定され
6712        // 正しく複製されることの検証(seen.len() ガード時代の過剰エラーバグ防止)。
6713        (
6714            "var a = []; for (var i = 0; i < 50; i++) { a.push({v: i}); } var c = structuredClone(a); c.length === 50 && c[49].v === 49",
6715            "true",
6716        ),
6717        // `JSON.parse`(`os_lib::json::parse`のRust側相互再帰パーサ)にも
6718        // `structuredClone`と同種のRustネイティブ再帰スタックオーバーフロー
6719        // リスクがあったため、同じ閾値30の深さガードを新設(2026-07-21)。
6720        // 20階層のネストは閾値内で安全にパースできる。
6721        (
6722            "(function(){ var s = '['.repeat(20) + '0' + ']'.repeat(20); try { JSON.parse(s); return 'ok'; } catch (e) { return 'error'; } })()",
6723            "ok",
6724        ),
6725        // GlobalEventHandlers (onchange / onclick 等) の属性・プロパティ統合テスト
6726        (
6727            "var d=document.createElement('div'); var r='none'; d.onchange=function(){r='ok';}; d.dispatchEvent(new Event('change')); r",
6728            "ok",
6729        ),
6730        // outerHTML セッターのテスト
6731        (
6732            "var p=document.createElement('div'); p.innerHTML='<span id=x>old</span>'; var s=p.children[0]; s.outerHTML='<b id=y>new</b>'; p.children[0].tagName+','+p.children[0].id",
6733            "B,y",
6734        ),
6735    ];
6736    let mut passed = 0;
6737    let mut total = cases.len();
6738    for (src, expect) in cases {
6739        let mut rt = JsRuntime::new();
6740        match rt.eval(src) {
6741            Ok(v) if &v.to_js_string() == expect => passed += 1,
6742            Ok(v) => crate::println!(
6743                "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
6744                src,
6745                v.to_js_string(),
6746                expect
6747            ),
6748            Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
6749        }
6750    }
6751
6752    // setInterval / clearInterval の2段階評価テスト
6753    {
6754        total += 1;
6755        let mut rt = JsRuntime::new();
6756        let _ = rt.eval("var n=0; var id=setInterval(function(){n++;}, 10);");
6757        match rt.eval("n > 0") {
6758            Ok(v) if v.to_js_string() == "true" => passed += 1,
6759            Ok(v) => crate::println!(
6760                "JS_SELFTEST FAIL: `setInterval` => n > 0 is `{}` (want true)",
6761                v.to_js_string()
6762            ),
6763            Err(e) => crate::println!("JS_SELFTEST ERR:  `setInterval` => {}", e),
6764        }
6765    }
6766    {
6767        total += 1;
6768        let mut rt = JsRuntime::new();
6769        let _ = rt.eval("var n=0; var id=setInterval(function(){n++;}, 10); clearInterval(id);");
6770        match rt.eval("n") {
6771            Ok(v) if v.to_js_string() == "0" => passed += 1,
6772            Ok(v) => crate::println!(
6773                "JS_SELFTEST FAIL: `clearInterval` => n is `{}` (want 0)",
6774                v.to_js_string()
6775            ),
6776            Err(e) => crate::println!("JS_SELFTEST ERR:  `clearInterval` => {}", e),
6777        }
6778    }
6779    // `setInterval`の発火回数上限(以前は8回。実サイト www.sugi-lab.net の
6780    // 統計カウンターアニメーション`increment=target/100`が最終値の8%程度で
6781    // 頭打ちになり正しい数値へ到達できないバグだった。2026-07-22発見・修正で
6782    // 200回へ引き上げ)の回帰防止テスト。target=50, increment=0.5として、
6783    // 100回の発火で正確にtargetへ到達し`clearInterval`されることを検証する
6784    // (8回上限のままなら`n`は50に到達せず4のまま止まる)。
6785    {
6786        total += 1;
6787        let mut rt = JsRuntime::new();
6788        let _ = rt.eval(
6789            "var n=0; var target=50; var inc=target/100; \
6790             var id=setInterval(function(){ \
6791                 n+=inc; \
6792                 if (n>=target) { n=target; clearInterval(id); } \
6793             }, 1);",
6794        );
6795        match rt.eval("n") {
6796            Ok(v) if v.to_js_string() == "50" => passed += 1,
6797            Ok(v) => crate::println!(
6798                "JS_SELFTEST FAIL: setInterval 統計カウンター模擬 => n is `{}` (want 50)",
6799                v.to_js_string()
6800            ),
6801            Err(e) => crate::println!("JS_SELFTEST ERR:  setInterval 統計カウンター模擬 => {}", e),
6802        }
6803    }
6804    // `AbortSignal.timeout(ms)`(ES2022)の2段階評価テスト(setTimeout 経由で
6805    // マクロタスクキューが解ける必要があるため setInterval と同じ2段階構成)。
6806    {
6807        total += 1;
6808        let mut rt = JsRuntime::new();
6809        let _ = rt.eval(
6810            "var fired=false; var s=AbortSignal.timeout(0); s.addEventListener('abort', function(){fired=true;});",
6811        );
6812        // `s.reason.name`(丸ごと未対応だった。以前は既定の `AbortError` の
6813        // ままで仕様上の `TimeoutError`ではなかったバグ。2026-07-16 発見・
6814        // 実装)も合わせて検証する。
6815        match rt.eval("fired + ',' + s.reason.name") {
6816            Ok(v) if v.to_js_string() == "true,TimeoutError" => passed += 1,
6817            Ok(v) => crate::println!(
6818                "JS_SELFTEST FAIL: `AbortSignal.timeout` => `{}` (want `true,TimeoutError`)",
6819                v.to_js_string()
6820            ),
6821            Err(e) => crate::println!("JS_SELFTEST ERR:  `AbortSignal.timeout` => {}", e),
6822        }
6823    }
6824
6825    // DOM 連携(Phase 3)。小さな DOM を構築してから評価する。
6826    let dom_html = "<div id='out'>0</div><p class='msg'>hi</p><button id='b' class='x'>go</button>";
6827    let dom_cases: &[(&str, &str)] = &[
6828        ("document.getElementById('out').textContent", "0"),
6829        ("var e=document.getElementById('out'); e.textContent='42'; e.textContent", "42"),
6830        ("document.getElementById('out').tagName", "DIV"),
6831        ("document.querySelector('.msg').textContent", "hi"),
6832        ("document.querySelectorAll('div').length", "1"),
6833        ("document.getElementById('b').classList.contains('x')", "true"),
6834        ("var e=document.getElementById('b'); e.classList.add('y'); e.classList.contains('y')", "true"),
6835        ("var e=document.getElementById('b'); e.classList.toggle('x'); e.classList.contains('x')", "false"),
6836        ("var e=document.getElementById('out'); e.style.color='red'; e.style.color", "red"),
6837        ("document.getElementById('missing') === null", "true"),
6838        ("document.getElementById('b').getAttribute('id')", "b"),
6839        // `document.children`/`.childElementCount`(`ParentNode`ミックスイン
6840        // の`Document`側配線漏れ。丸ごと未対応だった。bare `cases`配列では
6841        // `<html>`未構築のため、実際にHTMLフィクスチャがあるこちらで検証。
6842        // 2026-07-17 発見・実装)。
6843        ("document.children.length", "1"),
6844        ("document.children[0] === document.documentElement", "true"),
6845        ("document.childElementCount", "1"),
6846        // addEventListener + クリック発火(ブリッジ経由でディスパッチ)
6847        ("var n=0; document.getElementById('b').addEventListener('click', () => n++); n", "0"),
6848        // 動的ノード(createElement / appendChild / innerHTML / remove)
6849        ("document.createElement('DIV').tagName", "DIV"),
6850        ("var d=document.createElement('div'); d.textContent='hi'; d.textContent", "hi"),
6851        // textContent/innerHTML は WebIDL 上 nullable (DOMString?) につき null/undefined 代入は空文字列になる
6852        ("var d=document.createElement('div'); d.textContent='hi'; d.textContent=null; d.textContent===''", "true"),
6853        ("var d=document.createElement('div'); d.innerHTML='<b>x</b>'; d.innerHTML=undefined; d.innerHTML===''", "true"),
6854        ("var c=document.getElementById('out'); var s=document.createElement('span'); s.textContent='X'; c.appendChild(s); c.textContent", "0X"),
6855        ("var c=document.getElementById('out'); c.innerHTML='<b>Y</b>'; c.textContent", "Y"),
6856        ("var c=document.getElementById('out'); var i=document.createElement('i'); c.appendChild(i); document.querySelector('i') === null", "false"),
6857        ("var c=document.getElementById('out'); c.innerHTML='<span class=z>1</span><span class=z>2</span>'; document.querySelectorAll('.z').length", "2"),
6858        ("var c=document.getElementById('out'); var t=document.createTextNode('TT'); c.appendChild(t); c.textContent", "0TT"),
6859        ("var c=document.getElementById('out'); c.appendChild(document.createElement('em')); document.querySelector('em').remove(); document.querySelector('em') === null", "true"),
6860        // `ariaActiveDescendantElement`(ARIA Element Reflection。
6861        // `aria-activedescendant`のidref文字列ではなく実際のElement参照を
6862        // 返す/設定する。`popoverTargetElement`と同じidref解決パターン。
6863        // 丸ごと未対応だった。既にDOMツリーに接続されたフィクスチャ要素
6864        // (`get_element_by_id`は接続済みノードしか見ない)が必要なため、
6865        // bare `cases`配列ではなくこちらのfixture付き配列で検証。
6866        // 2026-07-18 発見・実装)。
6867        (
6868            "var a=document.createElement('div'); var b=document.getElementById('out'); a.ariaActiveDescendantElement=b; a.getAttribute('aria-activedescendant')+'|'+(a.ariaActiveDescendantElement===b)",
6869            "out|true",
6870        ),
6871        // `ariaLabelledByElements`/`ariaDescribedByElements`等(ARIA Element
6872        // Reflection、複数形)が丸ごと未対応だった。空白区切りidref属性を
6873        // 実際のElement参照の配列として読み書きする。単数形の
6874        // `ariaActiveDescendantElement`と同じ理由で接続済みfixture要素が
6875        // 必要。2026-07-18 発見・実装)。
6876        (
6877            "var a=document.createElement('div'); var b1=document.getElementById('out'); var b2=document.getElementById('b'); a.ariaLabelledByElements=[b1,b2]; a.getAttribute('aria-labelledby')+'|'+a.ariaLabelledByElements.length+'|'+(a.ariaLabelledByElements[0]===b1)+'|'+(a.ariaLabelledByElements[1]===b2)",
6878            "out b|2|true|true",
6879        ),
6880        (
6881            "document.createElement('div').ariaDescribedByElements.length",
6882            "0",
6883        ),
6884        // `parent.moveBefore(movedNode, referenceNode)`(丸ごと未対応
6885        // だった。この処理系には保持すべき「生きた状態」自体が無いため、
6886        // `insertBefore`と同じdetach+挿入の簡略実装で仕様どおり動作する。
6887        // 2026-07-18 発見・実装)。
6888        (
6889            "var p1=document.createElement('div'); var p2=document.createElement('div'); var m=document.createElement('span'); p1.appendChild(m); var r=document.createElement('b'); p2.appendChild(r); p2.moveBefore(m, r); m.parentNode===p2 && p2.firstChild===m && p1.children.length",
6890            "0",
6891        ),
6892        (
6893            "var p=document.createElement('div'); var a=document.createElement('i'); var b=document.createElement('b'); p.appendChild(a); p.appendChild(b); p.moveBefore(b, a); p.firstChild===b",
6894            "true",
6895        ),
6896        (
6897            "var p1=document.createElement('div'); var p2=document.createElement('div'); var m=document.createElement('span'); var other=document.createElement('b'); p1.appendChild(m); p2.appendChild(other); try { p1.moveBefore(m, other); 'no-throw'; } catch(e) { e.name; }",
6898            "NotFoundError",
6899        ),
6900    ];
6901    total += dom_cases.len();
6902    for (src, expect) in dom_cases {
6903        let mut rt = JsRuntime::new();
6904        rt.dom
6905            .borrow_mut()
6906            .build_from(&crate::os_lib::dom::parse_html(dom_html));
6907        match rt.eval(src) {
6908            Ok(v) if &v.to_js_string() == expect => passed += 1,
6909            Ok(v) => crate::println!(
6910                "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
6911                src,
6912                v.to_js_string(),
6913                expect
6914            ),
6915            Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
6916        }
6917    }
6918
6919    // 要素 DOM API(hasAttribute/removeAttribute/matches/closest/element querySelector/ナビ)。
6920    let dom2_html = "<div id='wrap' class='container'><p id='t' class='a b' data-k='v'>x</p><span id='s'>y</span></div>";
6921    let dom2_cases: &[(&str, &str)] = &[
6922        ("document.getElementById('t').hasAttribute('class')", "true"),
6923        ("document.getElementById('t').hasAttribute('nope')", "false"),
6924        ("document.getElementById('t').getAttribute('data-k')", "v"),
6925        ("var e=document.getElementById('t'); e.removeAttribute('class'); e.hasAttribute('class')", "false"),
6926        ("document.getElementById('t').matches('.a')", "true"),
6927        ("document.getElementById('t').matches('.z')", "false"),
6928        ("document.getElementById('t').matches('p')", "true"),
6929        ("document.getElementById('t').closest('.container').id", "wrap"),
6930        ("document.getElementById('t').closest('#wrap').id", "wrap"),
6931        ("document.getElementById('t').closest('p').id", "t"),
6932        ("document.getElementById('t').closest('.nope') === null", "true"),
6933        ("document.getElementById('wrap').querySelector('.a').id", "t"),
6934        ("document.getElementById('wrap').querySelector('span').id", "s"),
6935        ("document.getElementById('wrap').querySelectorAll('*').length", "2"),
6936        // コンパウンドセレクタ(タグ+クラス、以前は文字列全体をタグ名として比較しており
6937        // 常に false になっていた)。
6938        ("document.getElementById('t').matches('p.a')", "true"),
6939        ("document.getElementById('t').matches('p.z')", "false"),
6940        ("document.getElementById('t').matches('p.a.b')", "true"),
6941        // 属性セレクタ(以前は丸ごと未対応で常に false になっていた)。
6942        ("document.getElementById('t').matches('[data-k]')", "true"),
6943        ("document.getElementById('t').matches('[data-k=v]')", "true"),
6944        ("document.getElementById('t').matches('[data-k=x]')", "false"),
6945        ("document.getElementById('t').matches('[data-k^=v]')", "true"),
6946        // 結合子(子孫/子、以前は丸ごと未対応で常に false になっていた)。
6947        ("document.getElementById('t').matches('#wrap p')", "true"),
6948        ("document.getElementById('t').matches('#wrap > p')", "true"),
6949        ("document.getElementById('t').matches('#wrap > span')", "false"),
6950        // 一般兄弟結合子(`p ~ span`)。
6951        ("document.getElementById('s').matches('p ~ span')", "true"),
6952        ("document.getElementById('s').matches('span ~ span')", "false"),
6953        // querySelector(All) でも同じコンパウンド/属性セレクタが機能する。
6954        ("document.getElementById('wrap').querySelector('p.a').id", "t"),
6955        ("document.getElementById('wrap').querySelectorAll('[data-k]').length", "1"),
6956        // `:not()` 疑似クラス。
6957        ("document.getElementById('t').matches(':not(span)')", "true"),
6958        ("document.getElementById('t').matches(':not(p)')", "false"),
6959        ("document.getElementById('t').matches('p:not(.z)')", "true"),
6960        ("document.getElementById('t').matches('p:not(.a)')", "false"),
6961        ("document.getElementById('wrap').querySelectorAll(':not(span)').length", "1"),
6962        // `:scope` 疑似クラス(Selectors Level 4。丸ごと未対応だった)。
6963        // `element.querySelector(All)`/`matches`/`closest` における `:scope` は
6964        // 呼び出し元の要素自身を指す。
6965        ("document.getElementById('wrap').querySelectorAll(':scope > p').length", "1"),
6966        ("document.getElementById('wrap').querySelectorAll(':scope > p')[0].id", "t"),
6967        ("document.getElementById('wrap').querySelector(':scope > span').id", "s"),
6968        ("document.getElementById('wrap').querySelectorAll(':scope > .nope').length", "0"),
6969        ("document.getElementById('t').matches(':scope')", "true"),
6970        ("document.getElementById('t').closest(':scope').id", "t"),
6971        // 位置系疑似クラス(`:first-child`/`:last-child`/`:only-child`/`:nth-child()`)。
6972        ("document.getElementById('t').matches(':first-child')", "true"),
6973        ("document.getElementById('t').matches(':last-child')", "false"),
6974        ("document.getElementById('s').matches(':last-child')", "true"),
6975        ("document.getElementById('t').matches(':only-child')", "false"),
6976        ("document.getElementById('t').matches(':nth-child(1)')", "true"),
6977        ("document.getElementById('s').matches(':nth-child(2)')", "true"),
6978        ("document.getElementById('t').matches(':nth-child(odd)')", "true"),
6979        ("document.getElementById('s').matches(':nth-child(odd)')", "false"),
6980        ("document.getElementById('s').matches(':nth-child(2n)')", "true"),
6981        // `:nth-last-child()`(末尾から数える版。丸ごと未対応だった)。
6982        // #t/#s は2要素中1番目/2番目 → 末尾からは2番目/1番目。
6983        ("document.getElementById('s').matches(':nth-last-child(1)')", "true"),
6984        ("document.getElementById('t').matches(':nth-last-child(1)')", "false"),
6985        ("document.getElementById('t').matches(':nth-last-child(2)')", "true"),
6986        ("document.getElementById('s').matches(':nth-last-child(odd)')", "true"),
6987        // `:first-of-type`/`:last-of-type`/`:only-of-type`/`:nth-of-type()`(タグ名が
6988        // 同じ兄弟のみを数える点以外は `:nth-child()` 系と同じ規則)。各テストケースは
6989        // `document.getElementById` の「同一 id が複数あれば最初のものを返す」挙動と
6990        // 衝突しないよう、テストごとに固有の id を使う(以前は全ケースで同じ
6991        // p1/s1/p2/p3 を使い回しており、2回目以降は常に最初に追加した要素を
6992        // 参照してしまっていた)。
6993        (
6994            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot1a'>a</p><span id='ot1b'>b</span><p id='ot1c'>c</p><p id='ot1d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot1a').matches(':first-of-type')",
6995            "true",
6996        ),
6997        (
6998            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot2a'>a</p><span id='ot2b'>b</span><p id='ot2c'>c</p><p id='ot2d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot2d').matches(':last-of-type')",
6999            "true",
7000        ),
7001        (
7002            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot3a'>a</p><span id='ot3b'>b</span><p id='ot3c'>c</p><p id='ot3d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot3c').matches(':last-of-type')",
7003            "false",
7004        ),
7005        (
7006            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot4a'>a</p><span id='ot4b'>b</span><p id='ot4c'>c</p><p id='ot4d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot4b').matches(':only-of-type')",
7007            "true",
7008        ),
7009        (
7010            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot5a'>a</p><span id='ot5b'>b</span><p id='ot5c'>c</p><p id='ot5d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot5a').matches(':only-of-type')",
7011            "false",
7012        ),
7013        (
7014            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot6a'>a</p><span id='ot6b'>b</span><p id='ot6c'>c</p><p id='ot6d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot6c').matches(':nth-of-type(2)')",
7015            "true",
7016        ),
7017        (
7018            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot7a'>a</p><span id='ot7b'>b</span><p id='ot7c'>c</p><p id='ot7d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot7a').matches(':nth-of-type(2)')",
7019            "false",
7020        ),
7021        // `:nth-last-of-type()`(末尾から数える版。丸ごと未対応だった)。
7022        // p 要素は a/c/d の3つ → 末尾からは d=1番目, c=2番目, a=3番目。
7023        (
7024            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot8a'>a</p><span id='ot8b'>b</span><p id='ot8c'>c</p><p id='ot8d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot8d').matches(':nth-last-of-type(1)')",
7025            "true",
7026        ),
7027        (
7028            "var w=document.createElement('div'); w.innerHTML=\"<p id='ot9a'>a</p><span id='ot9b'>b</span><p id='ot9c'>c</p><p id='ot9d'>d</p>\"; document.getElementById('wrap').appendChild(w); document.getElementById('ot9a').matches(':nth-last-of-type(3)')",
7029            "true",
7030        ),
7031        // `:is()`/`:where()`(コンパウンドセレクタのリストのみ対応。結合子を含む
7032        // 複雑なセレクタは非対応)。
7033        ("document.getElementById('t').matches(':is(.z, .a)')", "true"),
7034        ("document.getElementById('t').matches(':is(.z, .y)')", "false"),
7035        ("document.getElementById('t').matches(':where(span, p.a)')", "true"),
7036        ("document.getElementById('s').matches(':is(span)')", "true"),
7037        ("document.getElementById('t').parentElement.id", "wrap"),
7038        ("document.getElementById('wrap').children.length", "2"),
7039        ("document.getElementById('wrap').childElementCount", "2"),
7040        // `node.hasChildNodes()`(丸ごと未対応だった。2026-07-15 発見・実装)。
7041        ("document.getElementById('wrap').hasChildNodes()", "true"),
7042        ("document.createElement('div').hasChildNodes()", "false"),
7043        // `document.createElementNS(ns, tag)`(丸ごと未対応だった。SVG 要素を
7044        // 動的生成する標準的な定番パターン。2026-07-15 発見・実装)。
7045        // `*AttributeNS` と同じく名前空間自体はモデル化せず非 NS 版へ委譲する
7046        // 簡略実装のため、生成される要素は通常の `createElement` と同じ。
7047        (
7048            "document.createElementNS('http://www.w3.org/2000/svg', 'circle').tagName",
7049            "CIRCLE",
7050        ),
7051        (
7052            "var e=document.createElementNS('http://www.w3.org/2000/svg', 'rect'); \
7053             e.setAttribute('width', '10'); e.getAttribute('width')",
7054            "10",
7055        ),
7056        ("document.getElementById('wrap').firstElementChild.id", "t"),
7057        ("document.getElementById('wrap').lastElementChild.id", "s"),
7058        ("document.getElementById('t').nextElementSibling.id", "s"),
7059        ("document.getElementById('s').previousElementSibling.id", "t"),
7060        ("document.getElementById('wrap').getAttributeNames().join(',')", "id,class"),
7061        // `element.attributes`(DOM 標準の `NamedNodeMap`。丸ごと未対応だった。
7062        // `{name,value}` オブジェクトの配列という簡略表現)。
7063        ("document.getElementById('wrap').attributes.length", "2"),
7064        ("document.getElementById('wrap').attributes[0].name", "id"),
7065        ("document.getElementById('wrap').attributes[0].value", "wrap"),
7066        // `getAttributeNode`/`setAttributeNode`/`removeAttributeNode`
7067        // (丸ごと未対応だった。2026-07-14 発見・実装)。
7068        ("document.getElementById('wrap').getAttributeNode('id').value", "wrap"),
7069        ("document.getElementById('wrap').getAttributeNode('missing')", "null"),
7070        (
7071            "var e=document.getElementById('wrap'); \
7072             e.setAttributeNode({name:'data-x', value:'1'}); e.getAttribute('data-x')",
7073            "1",
7074        ),
7075        (
7076            "var e=document.getElementById('wrap'); var a=e.getAttributeNode('id'); \
7077             e.removeAttributeNode(a); e.hasAttribute('id')",
7078            "false",
7079        ),
7080        // `document.createAttribute(name)`(丸ごと未対応だった。`Attr` ノードを
7081        // 新規に作ってから `setAttributeNode()` で取り付ける定番パターン。
7082        // 2026-07-15 発見・実装)。仕様どおり名前は小文字化される。
7083        ("document.createAttribute('DATA-X').name", "data-x"),
7084        ("document.createAttribute('data-x').value", ""),
7085        ("document.createAttribute('data-x').ownerElement", "null"),
7086        (
7087            "var e=document.getElementById('wrap'); var a=document.createAttribute('data-y'); \
7088             a.value='42'; e.setAttributeNode(a); e.getAttribute('data-y')",
7089            "42",
7090        ),
7091        // Fullscreen API(`element.requestFullscreen()`/`document.exitFullscreen()`/
7092        // `document.fullscreenElement`。丸ごと未対応だった。実際の画面占有切替は
7093        // 行わず状態のみ追跡する簡略実装)。
7094        ("document.fullscreenElement", "null"),
7095        ("document.fullscreenEnabled", "true"),
7096        (
7097            "var e=document.getElementById('wrap'); e.requestFullscreen(); document.fullscreenElement===e",
7098            "true",
7099        ),
7100        (
7101            "var e=document.getElementById('wrap'); e.requestFullscreen(); document.exitFullscreen(); document.fullscreenElement",
7102            "null",
7103        ),
7104        // Pointer Lock API(`element.requestPointerLock()`/`document.exitPointerLock()`/
7105        // `document.pointerLockElement`。丸ごと未対応だった。Fullscreen API と
7106        // 同じ「状態のみ追跡する」簡略実装)。
7107        ("document.pointerLockElement", "null"),
7108        (
7109            "var e=document.getElementById('wrap'); e.requestPointerLock(); document.pointerLockElement===e",
7110            "true",
7111        ),
7112        (
7113            "var e=document.getElementById('wrap'); e.requestPointerLock(); document.exitPointerLock(); document.pointerLockElement",
7114            "null",
7115        ),
7116        // Picture-in-Picture API(`element.requestPictureInPicture()`/
7117        // `document.exitPictureInPicture()`/`document.pictureInPictureElement`。
7118        // 丸ごと未対応だった。Fullscreen/Pointer Lock API と同じ
7119        // 「状態のみ追跡する」簡略実装)。
7120        ("document.pictureInPictureElement", "null"),
7121        ("document.pictureInPictureEnabled", "true"),
7122        (
7123            "var e=document.getElementById('wrap'); e.requestPictureInPicture(); \
7124             document.pictureInPictureElement===e",
7125            "true",
7126        ),
7127        (
7128            "var e=document.getElementById('wrap'); e.requestPictureInPicture(); \
7129             document.exitPictureInPicture(); document.pictureInPictureElement",
7130            "null",
7131        ),
7132        // `document.createTreeWalker(root)`(丸ごと未対応だった。DOM 部分木を
7133        // 走査する定番パターン。`wrap` の子は `#t`(p)/`#s`(span) の2要素)。
7134        (
7135            "document.createTreeWalker(document.getElementById('wrap')).currentNode.id",
7136            "wrap",
7137        ),
7138        (
7139            "var tw=document.createTreeWalker(document.getElementById('wrap')); \
7140             tw.firstChild(); tw.currentNode.id",
7141            "t",
7142        ),
7143        (
7144            "var tw=document.createTreeWalker(document.getElementById('wrap')); \
7145             tw.firstChild(); tw.nextSibling(); tw.currentNode.id",
7146            "s",
7147        ),
7148        (
7149            "var tw=document.createTreeWalker(document.getElementById('wrap')); \
7150             tw.firstChild(); tw.nextSibling(); tw.parentNode(); tw.currentNode.id",
7151            "wrap",
7152        ),
7153        (
7154            "var tw=document.createTreeWalker(document.getElementById('wrap')); \
7155             tw.nextNode(); tw.currentNode.id",
7156            "t",
7157        ),
7158        (
7159            "var tw=document.createTreeWalker(document.getElementById('wrap')); \
7160             var r=tw.parentNode(); r === null && tw.currentNode.id === 'wrap'",
7161            "true",
7162        ),
7163        // `document.createNodeIterator(root)`(丸ごと未対応だった。`TreeWalker` とは
7164        // 異なる「参照ノード+前後どちら側を指しているか」のポインタモデル)。
7165        (
7166            "document.createNodeIterator(document.getElementById('wrap')).nextNode().id",
7167            "wrap",
7168        ),
7169        (
7170            "var ni=document.createNodeIterator(document.getElementById('wrap')); \
7171             ni.nextNode(); ni.nextNode().id",
7172            "t",
7173        ),
7174        // `#t` の中身はテキストノード "x" のため pre-order 3件目はそのテキスト
7175        // ノード(`nodeValue` で判定。`#s` に到達するのは5件目)。
7176        (
7177            "var ni=document.createNodeIterator(document.getElementById('wrap')); \
7178             ni.nextNode(); ni.nextNode(); ni.nextNode().nodeValue",
7179            "x",
7180        ),
7181        (
7182            "var ni=document.createNodeIterator(document.getElementById('wrap')); \
7183             ni.nextNode(); ni.nextNode(); ni.nextNode(); ni.nextNode().id",
7184            "s",
7185        ),
7186        (
7187            "var ni=document.createNodeIterator(document.getElementById('wrap')); \
7188             ni.nextNode(); ni.nextNode(); ni.nextNode(); ni.nextNode(); ni.nextNode(); \
7189             ni.nextNode() === null",
7190            "true",
7191        ),
7192        (
7193            "var ni=document.createNodeIterator(document.getElementById('wrap')); \
7194             ni.nextNode(); ni.nextNode(); ni.previousNode().id",
7195            "t",
7196        ),
7197        (
7198            "var ni=document.createNodeIterator(document.getElementById('wrap')); \
7199             ni.nextNode(); ni.nextNode(); ni.previousNode(); ni.previousNode().id",
7200            "wrap",
7201        ),
7202        // `element.contentEditable`/`.isContentEditable`/`document.designMode`
7203        // (丸ごと未対応だった。属性値の反映のみを行う簡略実装)。
7204        ("document.getElementById('wrap').contentEditable", "inherit"),
7205        ("document.getElementById('wrap').isContentEditable", "false"),
7206        (
7207            "var e=document.getElementById('wrap'); e.contentEditable='true'; \
7208             e.contentEditable + ',' + e.isContentEditable",
7209            "true,true",
7210        ),
7211        (
7212            "var e=document.getElementById('wrap'); e.contentEditable='plaintext-only'; \
7213             e.contentEditable + ',' + e.isContentEditable",
7214            "plaintext-only,true",
7215        ),
7216        (
7217            "var e=document.getElementById('wrap'); e.contentEditable='bogus'; e.contentEditable",
7218            "inherit",
7219        ),
7220        ("document.designMode", "off"),
7221        // `element.spellcheck`/`.inputMode`/`.enterKeyHint`(`spellcheck`/
7222        // `inputmode`/`enterkeyhint` グローバル属性の JS プロパティ版。丸ごと
7223        // 未対応だった。属性値の反映のみを行う簡略実装。2026-07-15 発見・実装)。
7224        ("document.getElementById('wrap').spellcheck", "true"),
7225        (
7226            "var e=document.getElementById('wrap'); e.spellcheck=false; e.spellcheck",
7227            "false",
7228        ),
7229        ("document.getElementById('wrap').inputMode", ""),
7230        (
7231            "var e=document.getElementById('wrap'); e.inputMode='numeric'; e.inputMode",
7232            "numeric",
7233        ),
7234        (
7235            "var e=document.getElementById('wrap'); e.enterKeyHint='search'; e.enterKeyHint",
7236            "search",
7237        ),
7238        // `element.autocapitalize`/`.popover`(`autocapitalize`/`popover`
7239        // グローバル属性の JS プロパティ版。丸ごと未対応だった。2026-07-15
7240        // 発見・実装。`popover` は `showPopover()`等のメソッド自体は前サイクル
7241        // 実装済みだが、属性読み書き用の IDL プロパティが欠けていた)。
7242        (
7243            "var e=document.getElementById('wrap'); e.autocapitalize='words'; e.autocapitalize",
7244            "words",
7245        ),
7246        ("document.createElement('div').popover", "null"),
7247        (
7248            "var e=document.createElement('div'); e.popover='manual'; e.popover",
7249            "manual",
7250        ),
7251        (
7252            "var e=document.createElement('div'); e.popover='auto'; e.popover=null; e.popover",
7253            "null",
7254        ),
7255        // `button.popoverTargetElement`/`.popoverTargetAction`(丸ごと未対応
7256        // だった。`popovertarget` idref 属性を実際の要素へ解決する IDL 属性。
7257        // 2026-07-15 発見・実装)。
7258        (
7259            "var b=document.createElement('button'); b.setAttribute('popovertarget','wrap'); \
7260             b.popoverTargetElement === document.getElementById('wrap')",
7261            "true",
7262        ),
7263        ("document.createElement('button').popoverTargetElement", "null"),
7264        (
7265            "var b=document.createElement('button'); b.popoverTargetElement = document.getElementById('wrap'); \
7266             b.getAttribute('popovertarget')",
7267            "wrap",
7268        ),
7269        (
7270            "var b=document.createElement('button'); b.popoverTargetAction='hide'; b.popoverTargetAction",
7271            "hide",
7272        ),
7273        // `input.list`(`list` idref 属性から実際の `<datalist>` 要素を解決する
7274        // IDL 属性。丸ごと未対応だった。2026-07-15 発見・実装)。
7275        ("document.createElement('input').list", "null"),
7276        (
7277            "var i=document.createElement('input'); i.setAttribute('list','missing'); i.list",
7278            "null",
7279        ),
7280        (
7281            "var dl=document.createElement('datalist'); dl.id='opts'; \
7282             document.getElementById('wrap').appendChild(dl); \
7283             var i=document.createElement('input'); i.setAttribute('list','opts'); \
7284             i.list === dl",
7285            "true",
7286        ),
7287        (
7288            "var d=document.createElement('div'); d.id='notdl'; \
7289             document.getElementById('wrap').appendChild(d); \
7290             var i=document.createElement('input'); i.setAttribute('list','notdl'); i.list",
7291            "null",
7292        ),
7293        // `a`/`area`/`link.relList`(`rel` 属性のトークン配列版。丸ごと未対応
7294        // だった。読み取り専用の簡略実装。2026-07-15 発見・実装)。
7295        ("document.createElement('a').relList.length", "0"),
7296        (
7297            "var a=document.createElement('a'); a.rel='noopener noreferrer'; \
7298             a.relList.join(',')",
7299            "noopener,noreferrer",
7300        ),
7301        (
7302            "var a=document.createElement('a'); a.rel='noopener noreferrer'; \
7303             a.relList.includes('noreferrer')",
7304            "true",
7305        ),
7306        // `.referrerPolicy`/`.loading`/`.decoding`/`.allow`/`.sandbox`(丸ごと
7307        // 未対応だった。属性値の反映のみを行う簡略実装。2026-07-15 発見・実装)。
7308        (
7309            "var a=document.createElement('a'); a.referrerPolicy='no-referrer'; \
7310             a.referrerPolicy",
7311            "no-referrer",
7312        ),
7313        (
7314            "var i=document.createElement('img'); i.loading='lazy'; i.loading",
7315            "lazy",
7316        ),
7317        (
7318            "var i=document.createElement('img'); i.decoding='async'; i.decoding",
7319            "async",
7320        ),
7321        (
7322            "var f=document.createElement('iframe'); f.allow='camera'; f.allow",
7323            "camera",
7324        ),
7325        ("document.createElement('iframe').sandbox.length", "0"),
7326        (
7327            "var f=document.createElement('iframe'); \
7328             f.setAttribute('sandbox','allow-scripts allow-forms'); \
7329             f.sandbox.join(',')",
7330            "allow-scripts,allow-forms",
7331        ),
7332        // `.crossOrigin`/`.fetchPriority`/`.integrity`/`script.async`/`.defer`/
7333        // `.noModule`(丸ごと未対応だった。属性値の反映のみを行う簡略実装。
7334        // 2026-07-15 発見・実装)。
7335        ("document.createElement('img').crossOrigin", "null"),
7336        (
7337            "var i=document.createElement('img'); i.crossOrigin='anonymous'; i.crossOrigin",
7338            "anonymous",
7339        ),
7340        ("document.createElement('img').fetchPriority", "auto"),
7341        (
7342            "var i=document.createElement('img'); i.fetchPriority='high'; i.fetchPriority",
7343            "high",
7344        ),
7345        (
7346            "var s=document.createElement('script'); s.integrity='sha256-x'; s.integrity",
7347            "sha256-x",
7348        ),
7349        (
7350            "var s=document.createElement('script'); s.async=true; s.async + ',' + \
7351             s.hasAttribute('async')",
7352            "true,true",
7353        ),
7354        (
7355            "var s=document.createElement('script'); s.defer=true; s.defer=false; s.defer",
7356            "false",
7357        ),
7358        (
7359            "var s=document.createElement('script'); s.noModule=true; s.noModule",
7360            "true",
7361        ),
7362        // `.srcset`/`.sizes`/`.media`/`img.useMap`/`.isMap`/`.currentSrc`
7363        // (丸ごと未対応だった。属性値の反映のみを行う簡略実装。2026-07-15
7364        // 発見・実装)。
7365        (
7366            "var i=document.createElement('img'); i.srcset='a.jpg 1x, b.jpg 2x'; i.srcset",
7367            "a.jpg 1x, b.jpg 2x",
7368        ),
7369        (
7370            "var i=document.createElement('img'); i.sizes='(max-width:600px) 480px'; i.sizes",
7371            "(max-width:600px) 480px",
7372        ),
7373        (
7374            "var l=document.createElement('link'); l.media='print'; l.media",
7375            "print",
7376        ),
7377        (
7378            "var i=document.createElement('img'); i.useMap='#m'; i.useMap",
7379            "#m",
7380        ),
7381        ("document.createElement('img').isMap", "false"),
7382        (
7383            "var i=document.createElement('img'); i.isMap=true; i.hasAttribute('ismap')",
7384            "true",
7385        ),
7386        (
7387            "var i=document.createElement('img'); i.src='a.jpg'; i.currentSrc",
7388            "a.jpg",
7389        ),
7390        // `img.complete`/`.naturalWidth`/`.naturalHeight`/`.decode()`が丸ごと
7391        // 未対応だった(2026-07-17 発見・実装。実デコード寸法を追跡する
7392        // 経路が無いため`naturalWidth`/`.naturalHeight`は常に`0`を返す
7393        // 誠実な簡略実装。`complete`は`src`の有無のみで判定)。
7394        ("document.createElement('img').complete", "false"),
7395        (
7396            "var i=document.createElement('img'); i.src='a.jpg'; i.complete",
7397            "true",
7398        ),
7399        (
7400            "var i=document.createElement('img'); i.src='a.jpg'; i.naturalWidth + ',' + i.naturalHeight",
7401            "0,0",
7402        ),
7403        (
7404            "var i=document.createElement('img'); i.src='a.jpg'; \
7405             await i.decode().then(() => 'ok')",
7406            "ok",
7407        ),
7408        (
7409            "var i=document.createElement('img'); \
7410             await i.decode().catch(e => e.name)",
7411            "EncodingError",
7412        ),
7413        // `audio`/`video.autoplay`/`.controls`/`.loop`/`.muted`/`.playsInline`/
7414        // `.preload`/`.poster`/`a.ping`/`.hreflang`(丸ごと未対応だった。属性値の
7415        // 反映のみを行う簡略実装。2026-07-15 発見・実装)。
7416        ("document.createElement('video').autoplay", "false"),
7417        (
7418            "var v=document.createElement('video'); v.autoplay=true; v.autoplay + \
7419             ',' + v.hasAttribute('autoplay')",
7420            "true,true",
7421        ),
7422        (
7423            "var v=document.createElement('video'); v.controls=true; v.controls=false; \
7424             v.controls",
7425            "false",
7426        ),
7427        (
7428            "var v=document.createElement('video'); v.loop=true; v.loop",
7429            "true",
7430        ),
7431        (
7432            "var v=document.createElement('video'); v.muted=true; v.muted",
7433            "true",
7434        ),
7435        (
7436            "var v=document.createElement('video'); v.playsInline=true; \
7437             v.hasAttribute('playsinline')",
7438            "true",
7439        ),
7440        (
7441            "var v=document.createElement('video'); v.preload='auto'; v.preload",
7442            "auto",
7443        ),
7444        (
7445            "var v=document.createElement('video'); v.poster='p.jpg'; v.poster",
7446            "p.jpg",
7447        ),
7448        (
7449            "var a=document.createElement('a'); a.ping='https://x'; a.ping",
7450            "https://x",
7451        ),
7452        (
7453            "var a=document.createElement('a'); a.hreflang='en'; a.hreflang",
7454            "en",
7455        ),
7456        // `audio`/`video.paused`/`.currentTime`/`.duration`/`.volume`/
7457        // `.playbackRate`(丸ごと未対応だった。`.play()`/`.pause()`と同じ
7458        // 状態追跡のみの簡略実装。2026-07-15 発見・実装)。
7459        ("document.createElement('video').paused", "true"),
7460        (
7461            "var v=document.createElement('video'); await v.play(); v.paused",
7462            "false",
7463        ),
7464        (
7465            "var v=document.createElement('video'); await v.play(); v.pause(); v.paused",
7466            "true",
7467        ),
7468        (
7469            "var v=document.createElement('video'); v.currentTime=12.5; v.currentTime",
7470            "12.5",
7471        ),
7472        (
7473            "var v=document.createElement('video'); Number.isNaN(v.duration)",
7474            "true",
7475        ),
7476        ("document.createElement('video').volume", "1"),
7477        (
7478            "var v=document.createElement('video'); v.volume=0.5; v.volume",
7479            "0.5",
7480        ),
7481        (
7482            "var v=document.createElement('video'); v.volume=5; v.volume",
7483            "1",
7484        ),
7485        (
7486            "var v=document.createElement('video'); v.playbackRate=2; v.playbackRate",
7487            "2",
7488        ),
7489        // `.play()`/`.pause()` の `play`/`playing`/`pause` イベント発火(丸ごと
7490        // 未対応だった。既に同じ状態なら発火しない冪等性も含む。2026-07-15
7491        // 発見・実装)。
7492        (
7493            "var v=document.createElement('video'); var log=[]; \
7494             v.addEventListener('play', ()=>log.push('play')); \
7495             v.addEventListener('playing', ()=>log.push('playing')); \
7496             await v.play(); log.join(',')",
7497            "play,playing",
7498        ),
7499        (
7500            "var v=document.createElement('video'); var n=0; \
7501             v.addEventListener('pause', ()=>n++); \
7502             await v.play(); v.pause(); v.pause(); n",
7503            "1",
7504        ),
7505        (
7506            "var v=document.createElement('video'); var n=0; \
7507             v.addEventListener('play', ()=>n++); \
7508             await v.play(); await v.play(); n",
7509            "1",
7510        ),
7511        // `.currentTime`/`.volume`/`.muted`/`.playbackRate` 代入時の
7512        // `seeked`/`volumechange`/`ratechange` イベント発火(丸ごと未対応
7513        // だった。2026-07-15 発見・実装)。
7514        (
7515            "var v=document.createElement('video'); var f=false; \
7516             v.addEventListener('seeked', ()=>f=true); v.currentTime=5; f",
7517            "true",
7518        ),
7519        (
7520            "var v=document.createElement('video'); var f=false; \
7521             v.addEventListener('volumechange', ()=>f=true); v.volume=0.3; f",
7522            "true",
7523        ),
7524        (
7525            "var v=document.createElement('video'); var f=false; \
7526             v.addEventListener('volumechange', ()=>f=true); v.muted=true; f",
7527            "true",
7528        ),
7529        (
7530            "var v=document.createElement('video'); var f=false; \
7531             v.addEventListener('ratechange', ()=>f=true); v.playbackRate=1.5; f",
7532            "true",
7533        ),
7534        // `.readyState`/`.networkState`/`.error`(丸ごと未対応だった。実際の
7535        // ネットワーク層/デコーダが無いため誠実に「何もロードしていない」
7536        // 状態を返す簡略実装。2026-07-15 発見・実装)。
7537        ("document.createElement('video').readyState", "0"),
7538        ("document.createElement('video').networkState", "0"),
7539        (
7540            "var v=document.createElement('video'); v.src='a.mp4'; v.networkState",
7541            "3",
7542        ),
7543        ("document.createElement('video').error", "null"),
7544        // `.buffered`/`.seekable`(`TimeRanges`。丸ごと未対応だった。実際には
7545        // メディアを一切ロードしないため常に空。範囲外アクセスは仕様どおり
7546        // 例外を投げる誠実な簡略実装)。`.defaultPlaybackRate` は単純な数値
7547        // 状態の保持のみ(`.playbackRate`とは独立)。2026-07-15 発見・実装。
7548        ("document.createElement('video').buffered.length", "0"),
7549        ("document.createElement('video').seekable.length", "0"),
7550        (
7551            "try { document.createElement('video').buffered.start(0); 'no-throw' } \
7552             catch(e) { 'threw' }",
7553            "threw",
7554        ),
7555        ("document.createElement('video').defaultPlaybackRate", "1"),
7556        (
7557            "var v=document.createElement('video'); v.defaultPlaybackRate=1.5; \
7558             v.defaultPlaybackRate + ',' + v.playbackRate",
7559            "1.5,1",
7560        ),
7561        // `element.getAnimations()`/`document.getAnimations()`(丸ごと未対応
7562        // だった。`.animate()` の戻り値を一切保持していないため常に空配列を
7563        // 返す誠実な簡略実装。2026-07-15 発見・実装)。
7564        (
7565            "document.createElement('div').getAnimations().length",
7566            "0",
7567        ),
7568        (
7569            "var e=document.createElement('div'); e.animate([], 100); \
7570             e.getAnimations().length",
7571            "0",
7572        ),
7573        ("document.getAnimations().length", "0"),
7574        // `Animation.reverse()`/`.finish()`/`.playbackRate`(丸ごと未対応
7575        // だった。`play`/`pause`/`cancel`と同じno-opの簡略方針。
7576        // 2026-07-17 発見・実装)。
7577        (
7578            "var a=document.createElement('div').animate([],100); \
7579             typeof a.reverse + ',' + typeof a.finish + ',' + a.playbackRate",
7580            "function,function,1",
7581        ),
7582        (
7583            "var a=document.createElement('div').animate([],100); a.reverse(); a.finish(); a.playbackRate=2; a.playbackRate",
7584            "2",
7585        ),
7586        // `script`/`style.nonce`(丸ごと未対応だった。属性値の反映のみを行う
7587        // 簡略実装。2026-07-15 発見・実装)。
7588        (
7589            "var s=document.createElement('script'); s.nonce='abc123'; s.nonce",
7590            "abc123",
7591        ),
7592        // `Selection`(`document.getSelection()`)の残りのメソッド群/
7593        // プロパティが丸ごと未対応だった。テキスト選択機構自体が無いため
7594        // 「常に何も選択していない」誠実な簡略実装。2026-07-15 発見・実装。
7595        (
7596            "var s=document.getSelection(); s.collapse(document.body, 0); \
7597             s.anchorNode + ',' + s.type",
7598            "null,None",
7599        ),
7600        (
7601            "var s=document.getSelection(); s.containsNode(document.body)",
7602            "false",
7603        ),
7604        // `Selection.removeRange(range)`が`addRange`/`removeAllRanges`の隣で
7605        // 漏れていた兄弟ギャップ(丸ごと未対応だった。2026-07-17 発見・
7606        // 実装)。
7607        (
7608            "typeof document.getSelection().removeRange",
7609            "function",
7610        ),
7611        (
7612            "document.getSelection().removeRange(document.createRange())",
7613            "undefined",
7614        ),
7615        (
7616            "try { document.getSelection().getRangeAt(0); 'no-throw' } \
7617             catch(e) { 'threw' }",
7618            "threw",
7619        ),
7620        // `Range`(`document.createRange()`)の残りのメソッド群/プロパティが
7621        // 丸ごと未対応だった。`Selection` と同方針の誠実な簡略実装。
7622        // 2026-07-15 発見・実装。
7623        (
7624            "var r=document.createRange(); r.setStart(document.body, 0); \
7625             r.collapsed + ',' + r.toString()",
7626            "true,",
7627        ),
7628        (
7629            "var r=document.createRange(); var r2=r.cloneRange(); \
7630             r2.collapsed",
7631            "true",
7632        ),
7633        (
7634            "document.createRange().isPointInRange(document.body, 0)",
7635            "false",
7636        ),
7637        (
7638            "document.designMode='on'; document.designMode",
7639            "on",
7640        ),
7641        // `element.hasAttributes()`(丸ごと未対応だった。単一属性の有無を見る
7642        // `hasAttribute(name)` は既に対応済みだったが、こちらが漏れていた)。
7643        ("document.getElementById('wrap').hasAttributes()", "true"),
7644        ("document.createElement('div').hasAttributes()", "false"),
7645        // `node.isSameNode(other)`/`node.isEqualNode(other)` が丸ごと未対応だった。
7646        ("var e=document.getElementById('wrap'); e.isSameNode(e)", "true"),
7647        (
7648            "document.getElementById('wrap').isSameNode(document.getElementById('t'))",
7649            "false",
7650        ),
7651        (
7652            "var a=document.createElement('div'); a.id='x'; var b=document.createElement('div'); b.id='x'; a.isEqualNode(b)",
7653            "true",
7654        ),
7655        (
7656            "var a=document.createElement('div'); a.id='x'; var b=document.createElement('div'); b.id='y'; a.isEqualNode(b)",
7657            "false",
7658        ),
7659        // `node.normalize()` が丸ごと未対応だった。連続する隣接テキストノードを
7660        // 1つに連結し、空のテキストノードを取り除く。
7661        (
7662            "var d=document.createElement('div'); d.appendChild(document.createTextNode('a')); d.appendChild(document.createTextNode('b')); d.normalize(); d.childNodes.length",
7663            "1",
7664        ),
7665        (
7666            "var d=document.createElement('div'); d.appendChild(document.createTextNode('a')); d.appendChild(document.createTextNode('b')); d.normalize(); d.textContent",
7667            "ab",
7668        ),
7669        (
7670            "var d=document.createElement('div'); d.appendChild(document.createTextNode('')); d.normalize(); d.childNodes.length",
7671            "0",
7672        ),
7673        // `document.createDocumentFragment()` が丸ごと未対応だった。挿入すると
7674        // フラグメント自身ではなく中身の子要素群がそのまま移動する(実 DOM の挙動)。
7675        ("typeof document.createDocumentFragment", "function"),
7676        (
7677            "var f=document.createDocumentFragment(); f.appendChild(document.createElement('span')); f.appendChild(document.createElement('em')); var d=document.createElement('div'); d.appendChild(f); d.children.length+','+d.children[0].tagName+','+d.children[1].tagName",
7678            "2,SPAN,EM",
7679        ),
7680        // フラグメント自身は挿入後に空になる(中身だけが移動したことの確認)。
7681        (
7682            "var f=document.createDocumentFragment(); f.appendChild(document.createElement('span')); var d=document.createElement('div'); d.appendChild(f); f.childNodes.length",
7683            "0",
7684        ),
7685        // getElementsByClassName / getElementsByTagName
7686        ("document.getElementsByTagName('p').length", "1"),
7687        ("document.getElementsByClassName('a')[0].id", "t"),
7688        ("document.getElementsByTagName('span')[0].id", "s"),
7689        // 要素インスタンス版の getElementsByClassName/getElementsByTagName
7690        // (`document` 版は対応済みだったがこちらが丸ごと未対応だった)。
7691        (
7692            "document.getElementById('wrap').getElementsByClassName('a')[0].id",
7693            "t",
7694        ),
7695        (
7696            "document.getElementById('wrap').getElementsByTagName('span')[0].id",
7697            "s",
7698        ),
7699        (
7700            "document.getElementById('s').getElementsByClassName('a').length",
7701            "0",
7702        ),
7703        // getElementsByTagNameNS / getAttributeNodeNS(丸ごと未対応だった)。
7704        ("document.getElementsByTagNameNS('*', 'span')[0].id", "s"),
7705        (
7706            "document.getElementById('wrap').getElementsByTagNameNS(null, 'span')[0].id",
7707            "s",
7708        ),
7709        (
7710            "document.getElementById('wrap').getAttributeNodeNS(null, 'id').value",
7711            "wrap",
7712        ),
7713        (
7714            "document.getElementById('wrap').getAttributeNodeNS('http://www.w3.org/1999/xhtml', 'missing') === null",
7715            "true",
7716        ),
7717        // `document.getElementsByName(name)`(丸ごと未対応だった。ラジオボタン
7718        // グループ等、同じ `name` 属性を持つ要素群をまとめて取得する定番
7719        // パターン。2026-07-15 発見・実装)。
7720        (
7721            "var a=document.createElement('input'); a.setAttribute('name','q'); \
7722             var b=document.createElement('input'); b.setAttribute('name','q'); \
7723             var wrap=document.getElementById('wrap'); \
7724             wrap.appendChild(a); wrap.appendChild(b); \
7725             document.getElementsByName('q').length",
7726            "2",
7727        ),
7728        ("document.getElementsByName('no-such-name').length", "0"),
7729        // append / prepend / before / after / replaceWith
7730        ("var w=document.getElementById('wrap'); var n=document.createElement('b'); n.id='ap'; w.append(n); w.lastElementChild.id", "ap"),
7731        ("var w=document.getElementById('wrap'); var n=document.createElement('b'); n.id='pp'; w.prepend(n); w.firstElementChild.id", "pp"),
7732        ("var n=document.createElement('b'); n.id='bf'; document.getElementById('t').before(n); document.getElementById('t').previousElementSibling.id", "bf"),
7733        ("var n=document.createElement('b'); n.id='af'; document.getElementById('t').after(n); document.getElementById('t').nextElementSibling.id", "af"),
7734        ("var n=document.createElement('b'); n.id='rw'; document.getElementById('s').replaceWith(n); document.getElementById('wrap').lastElementChild.id", "rw"),
7735        ("var w=document.getElementById('wrap'); w.append('TXT'); w.textContent.indexOf('TXT') >= 0", "true"),
7736        // `element.replaceChildren(...nodes)`(`innerHTML=''; append(...)` の1行版)
7737        // が丸ごと未対応だった。既存の子は全て消え、渡した内容だけが残る。
7738        (
7739            "var w=document.getElementById('wrap'); var n=document.createElement('b'); n.id='rc'; w.replaceChildren(n); w.children.length + ',' + w.firstElementChild.id",
7740            "1,rc",
7741        ),
7742        (
7743            "var w=document.getElementById('wrap'); w.replaceChildren(); w.children.length",
7744            "0",
7745        ),
7746        // `element.scrollTo(x,y)`/`.scrollBy(x,y)`(オブジェクト引数 `{top}` 形式も
7747        // 含む)が丸ごと未対応だった。既存の `element.scrollTop` setter へ委譲する。
7748        (
7749            "var w=document.getElementById('wrap'); w.scrollTo(0, 50); w.scrollTop",
7750            "50",
7751        ),
7752        (
7753            "var w=document.getElementById('wrap'); w.scrollTo({top: 30}); w.scrollTop",
7754            "30",
7755        ),
7756        (
7757            "var w=document.getElementById('wrap'); w.scrollTop=10; w.scrollBy(0, 5); w.scrollTop",
7758            "15",
7759        ),
7760        // classList 追加(length / value / item / toString)
7761        ("document.getElementById('t').classList.length", "2"),
7762        ("document.getElementById('t').classList.value", "a b"),
7763        ("document.getElementById('t').classList.item(0)", "a"),
7764        ("document.getElementById('t').classList.toString()", "a b"),
7765        // insertAdjacentHTML / insertAdjacentElement / toggleAttribute
7766        ("var w=document.getElementById('wrap'); w.insertAdjacentHTML('beforeend','<i id=iah>z</i>'); document.getElementById('iah').tagName", "I"),
7767        // position 引数は仕様上 ASCII 大文字小文字を無視すべきだが、以前は完全一致
7768        // のみで `"beforeEnd"` 等を渡すと静かに no-op になっていた。
7769        ("var w=document.getElementById('wrap'); w.insertAdjacentHTML('beforeEnd','<b id=iahc>z</b>'); document.getElementById('iahc').tagName", "B"),
7770        ("var t=document.getElementById('t'); t.insertAdjacentHTML('afterend','<u id=iae>q</u>'); t.nextElementSibling.id", "iae"),
7771        ("var t=document.getElementById('t'); var n=document.createElement('b'); n.id='iel'; t.insertAdjacentElement('beforebegin', n); t.previousElementSibling.id", "iel"),
7772        ("var t=document.getElementById('t'); t.toggleAttribute('hidden'); t.hasAttribute('hidden')", "true"),
7773        ("var t=document.getElementById('t'); t.toggleAttribute('hidden'); t.toggleAttribute('hidden'); t.hasAttribute('hidden')", "false"),
7774        ("var t=document.getElementById('t'); t.toggleAttribute('hidden', true); t.hasAttribute('hidden')", "true"),
7775        ("var t=document.getElementById('t'); t.toggleAttribute('hidden', true); t.toggleAttribute('hidden', false); t.hasAttribute('hidden')", "false"),
7776        // <dialog> の show()/showModal()/close()(以前は丸ごと未対応だった)。
7777        ("var d=document.createElement('dialog'); d.open", "false"),
7778        ("var d=document.createElement('dialog'); d.show(); d.open", "true"),
7779        ("var d=document.createElement('dialog'); d.showModal(); d.open", "true"),
7780        // `.show()`と`.showModal()`が同一実装を共有しておりCSS `:modal`用の
7781        // 区別が一切無かった(2026-07-18 発見・実装)。`_modal`は
7782        // `showModal()`のみが書き込む内部専用属性で、`.show()`では
7783        // 付かず、`.close()`/`.requestClose()`でどちらも除去される。
7784        ("var d=document.createElement('dialog'); d.show(); d.hasAttribute('_modal')", "false"),
7785        ("var d=document.createElement('dialog'); d.showModal(); d.hasAttribute('_modal')", "true"),
7786        ("var d=document.createElement('dialog'); d.showModal(); d.close(); d.hasAttribute('_modal')", "false"),
7787        ("var d=document.createElement('dialog'); d.show(); d.close(); d.open", "false"),
7788        ("var d=document.createElement('dialog'); d.show(); d.close('ok'); d.returnValue", "ok"),
7789        (
7790            "var d=document.createElement('dialog'); var closed=false; d.addEventListener('close', function(){ closed=true; }); d.show(); d.close(); closed",
7791            "true",
7792        ),
7793        ("var d=document.createElement('dialog'); d.open=true; d.hasAttribute('open')", "true"),
7794        // Popover API `showPopover()`/`hidePopover()`/`togglePopover(force?)`
7795        // (丸ごと未対応だった。`<dialog>` の `open` 属性と同じ内部専用属性の
7796        // 付け外しのみの簡略実装。2026-07-15 発見・実装)。
7797        ("var e=document.createElement('div'); e.togglePopover()", "true"),
7798        (
7799            "var e=document.createElement('div'); e.togglePopover(); e.togglePopover()",
7800            "false",
7801        ),
7802        (
7803            "var e=document.createElement('div'); e.togglePopover(true); e.togglePopover(true)",
7804            "true",
7805        ),
7806        (
7807            "var e=document.createElement('div'); e.showPopover(); e.togglePopover()",
7808            "false",
7809        ),
7810        // Popover API が状態変化を`toggle`イベントとして発火していな
7811        // かった(`<dialog>.close()`は既に`"close"`イベントを発火して
7812        // いたのに同じ役目のPopover側だけ漏れていた。丸ごと未対応
7813        // だった。2026-07-17 発見・実装)。
7814        (
7815            "var e=document.createElement('div'); var n=0; \
7816             e.addEventListener('toggle', () => n++); e.showPopover(); e.hidePopover(); n",
7817            "2",
7818        ),
7819        (
7820            "var e=document.createElement('div'); var n=0; \
7821             e.addEventListener('toggle', () => n++); e.togglePopover(); n",
7822            "1",
7823        ),
7824        // `toggle`イベントの`oldState`/`newState`(仕様上の`ToggleEvent`の
7825        // 主要プロパティ。丸ごと未対応だった。2026-07-17 発見・実装)。
7826        (
7827            "var e=document.createElement('div'); var s=''; \
7828             e.addEventListener('toggle', ev => s=ev.oldState+'>'+ev.newState); \
7829             e.showPopover(); s",
7830            "closed>open",
7831        ),
7832        (
7833            "var e=document.createElement('div'); var s=''; \
7834             e.addEventListener('toggle', ev => s=ev.oldState+'>'+ev.newState); \
7835             e.showPopover(); e.hidePopover(); s",
7836            "open>closed",
7837        ),
7838        // `beforetoggle`(状態変更「前」に発火する取消可能なイベント。
7839        // `preventDefault()`で開閉を中止できる。丸ごと未対応だった。
7840        // 2026-07-17 発見・実装)。
7841        (
7842            "var e=document.createElement('div'); var n=0; \
7843             e.addEventListener('beforetoggle', () => n++); e.showPopover(); n",
7844            "1",
7845        ),
7846        (
7847            "var e=document.createElement('div'); var toggled=false; \
7848             e.addEventListener('beforetoggle', ev => ev.preventDefault()); \
7849             e.addEventListener('toggle', () => toggled=true); \
7850             e.showPopover(); toggled",
7851            "false",
7852        ),
7853        (
7854            "var e=document.createElement('div'); \
7855             e.addEventListener('beforetoggle', ev => ev.preventDefault()); \
7856             e.showPopover(); e.togglePopover()",
7857            "false",
7858        ),
7859        // `inert` グローバル真偽属性(丸ごと未対応だった)。
7860        ("var t=document.createElement('div'); t.inert", "false"),
7861        ("var t=document.createElement('div'); t.setAttribute('inert',''); t.inert", "true"),
7862        ("var t=document.createElement('div'); t.inert=true; t.hasAttribute('inert')", "true"),
7863        ("var t=document.createElement('div'); t.inert=true; t.inert=false; t.hasAttribute('inert')", "false"),
7864        // <template>.content(丸ごと未対応だった)。
7865        (
7866            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; tpl.content.children.length",
7867            "1",
7868        ),
7869        (
7870            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; tpl.content.firstChild.textContent",
7871            "hi",
7872        ),
7873        // 初回アクセスで子がフラグメント側へ移り、template 自身は空になる。
7874        (
7875            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; var f=tpl.content; tpl.children.length",
7876            "0",
7877        ),
7878        // 2回目以降のアクセスでも同じフラグメントを返す(子を再度奪わない)。
7879        (
7880            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; var f1=tpl.content; var f2=tpl.content; f1===f2",
7881            "true",
7882        ),
7883        // DocumentFragment を appendChild すると中身だけが移動する(実 DOM と同じ挙動)。
7884        (
7885            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; var host=document.createElement('div'); host.appendChild(tpl.content); host.children.length",
7886            "1",
7887        ),
7888        // `document.importNode(externalNode, deep)`/`document.adoptNode(node)`
7889        // (丸ごと未対応だった。`<template>` の中身を取り込む定番パターン。
7890        // 2026-07-15 発見・実装)。`importNode` はコピーなので元の `.content`
7891        // には影響しない。
7892        (
7893            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; \
7894             var imported=document.importNode(tpl.content, true); \
7895             var host=document.createElement('div'); host.appendChild(imported); \
7896             host.children.length + ',' + tpl.content.children.length",
7897            "1,1",
7898        ),
7899        (
7900            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; \
7901             document.importNode(tpl.content, true).firstChild.textContent",
7902            "hi",
7903        ),
7904        (
7905            "var host1=document.createElement('div'); var host2=document.createElement('div'); \
7906             var child=document.createElement('span'); host1.appendChild(child); \
7907             document.adoptNode(child); host2.appendChild(child); \
7908             host1.children.length + ',' + host2.children.length",
7909            "0,1",
7910        ),
7911        // cloneNode(): .content に一度アクセス済みの <template> を複製すると、
7912        // クローン先の .content が元の(同じ)フラグメントを指したまま共有されて
7913        // しまうバグだった(複製後にクローン側の中身を変更しても元に影響しない、
7914        // という cloneNode の基本契約が破られていた)。
7915        (
7916            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; var f=tpl.content; \
7917             var clone=tpl.cloneNode(true); clone.content===tpl.content",
7918            "false",
7919        ),
7920        (
7921            "var tpl=document.createElement('template'); tpl.innerHTML='<p>hi</p>'; var f=tpl.content; \
7922             var clone=tpl.cloneNode(true); clone.content.children.length+','+tpl.content.children.length",
7923            "1,1",
7924        ),
7925        // outerHTML/getAttributeNames() が `_content_frag`(`.content` アクセス済み
7926        // <template> が内部で持つ housekeeping 属性)等の `_` 始まり内部専用属性を
7927        // そのまま漏らしていたバグ。
7928        (
7929            "var tpl=document.createElement('template'); tpl.innerHTML='hi'; var f=tpl.content; tpl.outerHTML",
7930            "<template></template>",
7931        ),
7932        (
7933            "var tpl=document.createElement('template'); tpl.innerHTML='hi'; var f=tpl.content; \
7934             tpl.getAttributeNames().join(',')",
7935            "",
7936        ),
7937        // <progress>/<meter> の .value/.max/.min(丸ごと未対応だった。既定値は
7938        // 仕様どおり value=0/max=1/meter.min=0)。
7939        ("document.createElement('progress').value", "0"),
7940        ("document.createElement('progress').max", "1"),
7941        ("var p=document.createElement('progress'); p.setAttribute('value','30'); p.setAttribute('max','50'); p.value+'/'+p.max", "30/50"),
7942        ("var p=document.createElement('progress'); p.value=40; p.getAttribute('value')", "40"),
7943        ("document.createElement('meter').min", "0"),
7944        ("var m=document.createElement('meter'); m.setAttribute('value','5'); typeof m.value", "number"),
7945        // `input.valueAsNumber`(HTML5。丸ごと未対応だった)。
7946        ("var i=document.createElement('input'); i.type='number'; i.value='42'; i.valueAsNumber", "42"),
7947        ("var i=document.createElement('input'); i.type='text'; i.value='42'; isNaN(i.valueAsNumber)", "true"),
7948        ("var i=document.createElement('input'); i.type='number'; i.valueAsNumber=7; i.value", "7"),
7949        ("var i=document.createElement('input'); i.type='number'; i.value='5'; i.valueAsNumber=NaN; i.value", ""),
7950        // `input.valueAsDate`(HTML5。`type="date"`/`"month"` の丸ごと未対応だった。
7951        // `week`/その他の type は仕様どおり `null`)。
7952        (
7953            "var i=document.createElement('input'); i.type='date'; i.value='2024-03-15'; \
7954             i.valueAsDate.getUTCFullYear()+'-'+(i.valueAsDate.getUTCMonth()+1)+'-'+i.valueAsDate.getUTCDate()",
7955            "2024-3-15",
7956        ),
7957        ("var i=document.createElement('input'); i.type='text'; i.value='2024-03-15'; i.valueAsDate", "null"),
7958        (
7959            "var i=document.createElement('input'); i.type='date'; i.valueAsDate=new Date(Date.UTC(2024,2,15)); i.value",
7960            "2024-03-15",
7961        ),
7962        (
7963            "var i=document.createElement('input'); i.type='month'; i.valueAsDate=new Date(Date.UTC(2024,2,15)); i.value",
7964            "2024-03",
7965        ),
7966        (
7967            "var i=document.createElement('input'); i.type='date'; i.value='x'; i.valueAsDate=null; i.value",
7968            "",
7969        ),
7970        // `input[type=date/month/datetime-local].value`は仕様上、属性値が
7971        // 正しい形式でなければ空文字列を返すべきだが、`color`/`range`と同じ
7972        // バグパターンで不正な生文字列がそのまま漏れていた。
7973        (
7974            "var i=document.createElement('input'); i.type='date'; \
7975             i.setAttribute('value','not-a-date'); i.value",
7976            "",
7977        ),
7978        (
7979            "var i=document.createElement('input'); i.type='date'; \
7980             i.setAttribute('value','2026-07-18'); i.value",
7981            "2026-07-18",
7982        ),
7983        (
7984            "var i=document.createElement('input'); i.type='month'; \
7985             i.setAttribute('value','garbage'); i.value",
7986            "",
7987        ),
7988        (
7989            "var i=document.createElement('input'); i.type='datetime-local'; \
7990             i.setAttribute('value','2026-07-18T14:30'); i.value",
7991            "2026-07-18T14:30",
7992        ),
7993        // getAttributeNS/setAttributeNS/hasAttributeNS/removeAttributeNS(丸ごと
7994        // 未対応だった。名前空間は無視し非NS版と同じ属性ストアへ委譲する簡略実装)。
7995        ("var e=document.createElement('div'); e.setAttributeNS(null,'data-k','v'); e.getAttribute('data-k')", "v"),
7996        ("var e=document.createElement('div'); e.setAttribute('data-k','v'); e.getAttributeNS(null,'data-k')", "v"),
7997        ("var e=document.createElement('div'); e.setAttributeNS(null,'data-k','v'); e.hasAttributeNS(null,'data-k')", "true"),
7998        (
7999            "var e=document.createElement('div'); e.setAttributeNS(null,'data-k','v'); e.removeAttributeNS(null,'data-k'); e.hasAttribute('data-k')",
8000            "false",
8001        ),
8002        // node.getRootNode()(丸ごと未対応だった)。
8003        (
8004            "var p=document.createElement('div'); var c=document.createElement('span'); p.appendChild(c); c.getRootNode()===p",
8005            "true",
8006        ),
8007        // 接続済みツリーでは異なる要素同士でも同じルートを返す。
8008        (
8009            "document.getElementById('t').getRootNode()===document.getElementById('wrap').getRootNode()",
8010            "true",
8011        ),
8012        // 親の無い単独ノードは自分自身を返す。
8013        ("var e=document.createElement('div'); e.getRootNode()===e", "true"),
8014        // lookupNamespaceURI/lookupPrefix/isDefaultNamespace(丸ごと未対応
8015        // だった。2026-07-16 発見・実装。この処理系は XML 名前空間を一切
8016        // モデル化していないため常に「名前空間なし」を返す簡略実装)。
8017        ("var e=document.createElement('div'); e.lookupNamespaceURI('svg')", "null"),
8018        ("var e=document.createElement('div'); e.lookupPrefix('http://www.w3.org/2000/svg')", "null"),
8019        ("var e=document.createElement('div'); e.isDefaultNamespace(null)", "true"),
8020        ("var e=document.createElement('div'); e.isDefaultNamespace('http://www.w3.org/2000/svg')", "false"),
8021        // ノード読みプロパティ(nodeType / nodeName / hidden)
8022        ("document.getElementById('t').nodeType", "1"),
8023        ("document.getElementById('t').nodeName", "P"),
8024        ("document.getElementById('t').hidden", "false"),
8025        ("var e=document.getElementById('t'); e.toggleAttribute('hidden'); e.hidden", "true"),
8026        // dataset(data-* ↔ camelCase)  ※ getBoundingClientRect は別ブロックで rect を注入して検証
8027
8028        ("document.getElementById('t').dataset.k", "v"),
8029        ("var e=document.getElementById('t'); e.dataset.userId='42'; e.getAttribute('data-user-id')", "42"),
8030        ("var e=document.getElementById('t'); e.setAttribute('data-foo-bar','9'); e.dataset.fooBar", "9"),
8031        ("document.getElementById('t').dataset.missing === undefined", "true"),
8032        // `delete element.dataset.foo` は `dataset:` プロキシが `.props` を持たず実データが
8033        // DOM ノード属性側にあるため、何も起きない黙殺バグだった。
8034        (
8035            "var e=document.getElementById('t'); e.dataset.temp='x'; delete e.dataset.temp; e.dataset.temp === undefined",
8036            "true",
8037        ),
8038        (
8039            "var e=document.getElementById('t'); e.dataset.userId='42'; delete e.dataset.userId; e.hasAttribute('data-user-id')",
8040            "false",
8041        ),
8042        // `delete element.style.color` も同じ種類の黙殺バグだった。
8043        (
8044            "var e=document.getElementById('t'); e.style.color='red'; delete e.style.color; e.style.color",
8045            "",
8046        ),
8047        // insertBefore: 新ノードを s の前へ → wrap の子順が t, new, s
8048        ("var w=document.getElementById('wrap'); var n=document.createElement('b'); n.id='n'; w.insertBefore(n, document.getElementById('s')); w.children[1].id", "n"),
8049        ("var w=document.getElementById('wrap'); var n=document.createElement('b'); w.insertBefore(n, null); w.lastElementChild.tagName", "B"),
8050        // replaceChild: s を新ノードに置換
8051        ("var w=document.getElementById('wrap'); var n=document.createElement('i'); n.id='ni'; w.replaceChild(n, document.getElementById('s')); w.lastElementChild.id", "ni"),
8052        ("var w=document.getElementById('wrap'); var n=document.createElement('i'); var old=w.replaceChild(n, document.getElementById('s')); old.id", "s"),
8053        // `insertBefore`/`replaceChild`/`removeChild`は仕様上、参照ノードが
8054        // `this`の子でない場合`NotFoundError`を投げるべきだが、以前は検証が
8055        // 無く黙って末尾へ追加/何もせず成功したかのように振る舞っていた
8056        // (Table DOM/CharacterData/TypedArrayと同じ「不正な参照・範囲の
8057        // 黙殺」バグ族)。
8058        (
8059            "var w=document.getElementById('wrap'); var outsider=document.createElement('div'); \
8060             var n=document.createElement('b'); \
8061             try { w.insertBefore(n, outsider); 'no-throw' } catch(e) { e.name }",
8062            "NotFoundError",
8063        ),
8064        (
8065            "var w=document.getElementById('wrap'); var outsider=document.createElement('div'); \
8066             var n=document.createElement('b'); \
8067             try { w.replaceChild(n, outsider); 'no-throw' } catch(e) { e.name }",
8068            "NotFoundError",
8069        ),
8070        (
8071            "var w=document.getElementById('wrap'); var outsider=document.createElement('div'); \
8072             try { w.removeChild(outsider); 'no-throw' } catch(e) { e.name }",
8073            "NotFoundError",
8074        ),
8075        // cloneNode(deep): テキストごと複製
8076        ("document.getElementById('t').cloneNode(true).textContent", "x"),
8077        ("var c=document.getElementById('t').cloneNode(false); c.tagName + ':' + c.children.length", "P:0"),
8078        ("document.getElementById('t').cloneNode(true).parentElement === null", "true"),
8079        // classList.replace
8080        ("var e=document.getElementById('t'); e.classList.replace('a','z'); e.classList.contains('z') + ',' + e.classList.contains('a')", "true,false"),
8081        ("document.getElementById('t').classList.replace('nope','z')", "false"),
8082        // classList.toggle(name, force) — 第2引数 force が以前は完全に無視されていた。
8083        (
8084            "var e=document.getElementById('t'); e.classList.toggle('force-on', true); e.classList.contains('force-on')",
8085            "true",
8086        ),
8087        (
8088            "var e=document.getElementById('t'); e.classList.add('force-off'); e.classList.toggle('force-off', false); e.classList.contains('force-off')",
8089            "false",
8090        ),
8091        // classList.add/remove の複数引数(可変長引数)対応、classList.supports、webkitMatchesSelector、execCommand
8092        (
8093            "var e=document.createElement('div'); e.classList.add('c1', 'c2', 'c3'); e.className",
8094            "c1 c2 c3",
8095        ),
8096        (
8097            "var e=document.createElement('div'); e.classList.add('c1', 'c2', 'c3'); e.classList.remove('c1', 'c3'); e.className",
8098            "c2",
8099        ),
8100        (
8101            "var e=document.createElement('div'); e.classList.supports('foo')",
8102            "false",
8103        ),
8104        // 元は`document.body.appendChild`/`.remove()`を経由していたが、この
8105        // 自己テスト実行環境(`JsRuntime::new()`直後)には既定でパース済みの
8106        // `<html>`/`<body>`が一切存在しない(`document.body`は常に`null`)
8107        // ため、その`null`への`.appendChild`呼び出しが例外を投げていた
8108        // (2026-07-18 発見・修正)。`#id`セレクタの`matches()`判定は
8109        // ツリーへの接続を要さない(自身の`id`属性のみ見る)ため、
8110        // 接続操作自体が不要と判明し削除。
8111        (
8112            "var e=document.createElement('div'); e.id='wm'; e.webkitMatchesSelector('#wm')",
8113            "true",
8114        ),
8115        (
8116            "document.execCommand('copy')",
8117            "true",
8118        ),
8119        // `classList` の `Symbol.iterator`/`forEach` が丸ごと未対応で、`for...of`/
8120        // スプレッド/`forEach` のいずれも常に何も反復しない黙殺バグだった。
8121        (
8122            "var e=document.createElement('div'); e.className='a b c'; var out=[]; for (const c of e.classList) out.push(c); out.join(',')",
8123            "a,b,c",
8124        ),
8125        (
8126            "var e=document.createElement('div'); e.className='x y'; [...e.classList].join(',')",
8127            "x,y",
8128        ),
8129        (
8130            "var e=document.createElement('div'); e.className='p q'; var out=''; e.classList.forEach(function(c,i){out+=i+':'+c+' '}); out.trim()",
8131            "0:p 1:q",
8132        ),
8133        // `classList.entries()`/`.keys()`/`.values()`(丸ごと未対応だった。`forEach`
8134        // と for-of だけが配線され、明示イテレータ取得メソッド3種は未配線だった。
8135        // 2026-07-15 発見・実装)。
8136        (
8137            "var e=document.createElement('div'); e.className='r s'; var out=[]; for (const [i,c] of e.classList.entries()) out.push(i+':'+c); out.join(',')",
8138            "0:r,1:s",
8139        ),
8140        (
8141            "var e=document.createElement('div'); e.className='r s'; [...e.classList.keys()].join(',')",
8142            "0,1",
8143        ),
8144        (
8145            "var e=document.createElement('div'); e.className='r s'; [...e.classList.values()].join(',')",
8146            "r,s",
8147        ),
8148        // `Array.prototype.with(index, value)`(ES2023)— 範囲外 index は仕様上 RangeError
8149        // だが、以前は黙って無視して元と同じ配列を返していた。
8150        ("[1,2,3].with(1, 'x').join(',')", "1,x,3"),
8151        ("[1,2,3].with(-1, 'x').join(',')", "1,2,x"),
8152        (
8153            "try { [1,2,3].with(5, 'x'); 'no-throw' } catch(e) { 'threw' }",
8154            "threw",
8155        ),
8156        // `element.style.setProperty`/`getPropertyValue`/`removeProperty` が丸ごと
8157        // 欠落していた(直接のプロパティ代入 `el.style.color='red'` はあったが、
8158        // メソッド経由のインラインスタイル読み書きが無く、特に CSS カスタムプロパティ
8159        // `--foo` はこの経路でしか設定できないため影響が大きかった)。
8160        (
8161            "var e=document.getElementById('t'); e.style.setProperty('color','red'); e.style.color",
8162            "red",
8163        ),
8164        (
8165            "var e=document.getElementById('t'); e.style.setProperty('--my-var','10px'); e.style.getPropertyValue('--my-var')",
8166            "10px",
8167        ),
8168        (
8169            "var e=document.getElementById('t'); e.style.setProperty('color','blue'); e.style.removeProperty('color'); e.style.color",
8170            "",
8171        ),
8172        // `style.getPropertyPriority(name)`/`setProperty(name, value, priority)` の
8173        // 第3引数(丸ごと未対応・無視されていた。2026-07-16 発見・実装)。
8174        (
8175            "var e=document.getElementById('t'); e.style.setProperty('color','red','important'); e.style.getPropertyPriority('color')",
8176            "important",
8177        ),
8178        // `!important` 付きで設定した値を `getPropertyValue`/直接プロパティ双方から
8179        // 読んでも、優先度マーカーを含まない値本体のみが返るべき(仕様どおり)。
8180        (
8181            "var e=document.getElementById('t'); e.style.setProperty('color','red','important'); e.style.color",
8182            "red",
8183        ),
8184        (
8185            "var e=document.getElementById('t'); e.style.setProperty('color','red'); e.style.getPropertyPriority('color')",
8186            "",
8187        ),
8188        // `style.length`/`.item(index)`(丸ごと未対応だった。反復イディオム
8189        // `for (let i=0;i<style.length;i++) style.item(i)`。2026-07-17
8190        // 発見・実装)。
8191        (
8192            "var e=document.getElementById('t'); e.style.cssText='color: red; font-size: 12px'; e.style.length",
8193            "2",
8194        ),
8195        (
8196            "var e=document.getElementById('t'); e.style.cssText='color: red; font-size: 12px'; \
8197             e.style.item(0)+','+e.style.item(1)",
8198            "color,font-size",
8199        ),
8200        ("document.getElementById('t').style.length", "0"),
8201        // `style.cssFloat`(`float`プロパティのレガシー別名。丸ごと未対応
8202        // だった。2026-07-17 発見・実装)。
8203        (
8204            "var e=document.getElementById('t'); e.style.cssFloat='left'; e.style.float",
8205            "left",
8206        ),
8207        (
8208            "var e=document.getElementById('t'); e.style.float='right'; e.style.cssFloat",
8209            "right",
8210        ),
8211        // `element.style.cssText`(全プロパティを1つの文字列で読み書きする定番パターン)が
8212        // 丸ごと未対応で、通常のプロパティ名として扱われ常に空文字列になっていた。
8213        (
8214            "var e=document.getElementById('t'); e.style.cssText='color: red; font-size: 12px'; e.style.color+','+e.style.fontSize",
8215            "red,12px",
8216        ),
8217        (
8218            "var e=document.getElementById('t'); e.style.color='blue'; e.style.fontSize='10px'; e.style.cssText",
8219            "color: blue; font-size: 10px",
8220        ),
8221        // innerHTML/outerHTML が丸ごと未対応(innerHTML は textContent と同じ実装を
8222        // 共有しておりマークアップ自体が失われていた)だったバグ。
8223        (
8224            "var e=document.getElementById('t'); e.innerHTML='<b>bold</b>text'; e.innerHTML",
8225            "<b>bold</b>text",
8226        ),
8227        (
8228            "var e=document.getElementById('t'); e.innerHTML='hi'; e.outerHTML",
8229            "<p id=\"t\" class=\"a b\" data-k=\"v\">hi</p>",
8230        ),
8231        // nodeType/nodeName がテキストノードでも常に ELEMENT_NODE(1)/空文字列固定
8232        // だったバグ。childNodes/firstChild/lastChild/nextSibling/previousSibling
8233        // (テキストノードも含む Node レベル走査 API)も丸ごと未対応だった。
8234        (
8235            "var e=document.getElementById('t'); e.innerHTML='a<b>x</b>'; e.childNodes.length+','+e.firstChild.nodeType+','+e.lastChild.nodeType",
8236            "2,3,1",
8237        ),
8238        (
8239            "var e=document.getElementById('t'); e.innerHTML='a<b>x</b>'; e.firstChild.nextSibling.nodeName",
8240            "B",
8241        ),
8242        // `node.nodeValue`(テキストノードは自身のテキスト、要素ノードは仕様どおり
8243        // `null`)が丸ごと未対応だった。代入は要素ノードでは no-op。
8244        (
8245            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.nodeValue",
8246            "hello",
8247        ),
8248        (
8249            "var e=document.getElementById('t'); e.innerHTML='hello'; e.nodeValue === null",
8250            "true",
8251        ),
8252        (
8253            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.nodeValue='bye'; e.textContent",
8254            "bye",
8255        ),
8256        // `CharacterData.data`/`.length`/`.appendData`/`.deleteData`/
8257        // `.insertData`/`.replaceData`/`.substringData`(丸ごと未対応
8258        // だった。`nodeValue`は対応済みだったが単体アクセサ`data`と
8259        // 編集用メソッド群は漏れていた。2026-07-16 発見・実装)。
8260        (
8261            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.data",
8262            "hello",
8263        ),
8264        (
8265            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.length",
8266            "5",
8267        ),
8268        (
8269            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.data='world'; e.textContent",
8270            "world",
8271        ),
8272        (
8273            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.appendData(' world'); e.textContent",
8274            "hello world",
8275        ),
8276        (
8277            "var e=document.getElementById('t'); e.innerHTML='hello world'; e.firstChild.deleteData(5,6); e.textContent",
8278            "hello",
8279        ),
8280        (
8281            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.insertData(5,' world'); e.textContent",
8282            "hello world",
8283        ),
8284        (
8285            "var e=document.getElementById('t'); e.innerHTML='hello world'; e.firstChild.replaceData(6,5,'there'); e.textContent",
8286            "hello there",
8287        ),
8288        (
8289            "var e=document.getElementById('t'); e.innerHTML='hello world'; e.firstChild.substringData(6,5)",
8290            "world",
8291        ),
8292        // `deleteData`/`insertData`/`replaceData`/`substringData`/`splitText`は
8293        // 仕様上`offset`が`length`を超えると`IndexSizeError`を投げるべきだが、
8294        // 以前は`offset`を黙って`length`へクランプしてしまい、範囲外呼び出しが
8295        // 常に無害なno-op/末尾追記として成立していた(Table DOMの
8296        // `insertRow`等と同じ「範囲外インデックスの黙殺」バグ族)。
8297        (
8298            "var e=document.getElementById('t'); e.innerHTML='hi'; \
8299             try { e.firstChild.deleteData(99,1); 'no-throw' } catch(e) { e.name }",
8300            "IndexSizeError",
8301        ),
8302        (
8303            "var e=document.getElementById('t'); e.innerHTML='hi'; \
8304             try { e.firstChild.insertData(99,'x'); 'no-throw' } catch(e) { e.name }",
8305            "IndexSizeError",
8306        ),
8307        (
8308            "var e=document.getElementById('t'); e.innerHTML='hi'; \
8309             try { e.firstChild.replaceData(99,1,'x'); 'no-throw' } catch(e) { e.name }",
8310            "IndexSizeError",
8311        ),
8312        (
8313            "var e=document.getElementById('t'); e.innerHTML='hi'; \
8314             try { e.firstChild.substringData(99,1); 'no-throw' } catch(e) { e.name }",
8315            "IndexSizeError",
8316        ),
8317        (
8318            "var e=document.getElementById('t'); e.innerHTML='hi'; \
8319             try { e.firstChild.splitText(99); 'no-throw' } catch(e) { e.name }",
8320            "IndexSizeError",
8321        ),
8322        (
8323            // offset===lengthはちょうど境界で範囲内(末尾扱い)なので例外にならない。
8324            "var e=document.getElementById('t'); e.innerHTML='hi'; \
8325             e.firstChild.insertData(2,'!'); e.textContent",
8326            "hi!",
8327        ),
8328        // `Text.splitText(offset)`(丸ごと未対応だった。2026-07-16 発見・
8329        // 実装。テキストノードを分割し、後半を新しい兄弟ノードとして
8330        // 挿入する)。
8331        (
8332            "var e=document.getElementById('t'); e.innerHTML='hello world'; \
8333             var t1=e.firstChild; var t2=t1.splitText(5); t1.data+'|'+t2.data",
8334            "hello| world",
8335        ),
8336        (
8337            "var e=document.getElementById('t'); e.innerHTML='hello world'; \
8338             var t1=e.firstChild; t1.splitText(5); e.childNodes.length",
8339            "2",
8340        ),
8341        (
8342            "var e=document.getElementById('t'); e.innerHTML='hello world'; \
8343             var t1=e.firstChild; var t2=t1.splitText(5); t2.nextSibling===null && t2.previousSibling===t1",
8344            "true",
8345        ),
8346        // `Text.wholeText`(丸ごと未対応だった。`splitText`と対をなす読み
8347        // 取りプロパティ。2026-07-16 発見・実装。連続するテキストノード
8348        // 兄弟の`data`を全て連結して返す)。
8349        (
8350            "var e=document.getElementById('t'); e.innerHTML='hello world'; \
8351             var t1=e.firstChild; var t2=t1.splitText(5); t1.wholeText+'|'+t2.wholeText",
8352            "hello world|hello world",
8353        ),
8354        (
8355            "var e=document.getElementById('t'); e.innerHTML='hello'; e.firstChild.wholeText",
8356            "hello",
8357        ),
8358        // HTML5 Table DOM(`table.insertRow`/`.deleteRow`/`.rows`、
8359        // `tr.insertCell`/`.deleteCell`/`.cells`、`.rowIndex`/`.cellIndex`)が
8360        // 丸ごと未対応だった(2026-07-17 発見・実装)。
8361        (
8362            "var t=document.createElement('table'); \
8363             t.insertRow(); t.insertRow(); t.rows.length",
8364            "2",
8365        ),
8366        (
8367            "var t=document.createElement('table'); var r=t.insertRow(); r.insertCell(); r.insertCell(); \
8368             r.cells.length",
8369            "2",
8370        ),
8371        (
8372            "var t=document.createElement('table'); \
8373             var r0=t.insertRow(); var r1=t.insertRow(); var rmid=t.insertRow(1); \
8374             rmid===t.rows[1]",
8375            "true",
8376        ),
8377        (
8378            "var t=document.createElement('table'); t.insertRow(); t.insertRow(); t.deleteRow(0); \
8379             t.rows.length",
8380            "1",
8381        ),
8382        (
8383            "var t=document.createElement('table'); var r=t.insertRow(); \
8384             r.insertCell(); var mid=r.insertCell(); r.insertCell(); \
8385             mid===r.cells[1]",
8386            "true",
8387        ),
8388        (
8389            "var t=document.createElement('table'); var r=t.insertRow(); r.insertCell(); r.insertCell(); \
8390             r.deleteCell(0); r.cells.length",
8391            "1",
8392        ),
8393        (
8394            "var t=document.createElement('table'); t.insertRow(); var r=t.insertRow(); r.rowIndex",
8395            "1",
8396        ),
8397        (
8398            "var t=document.createElement('table'); var r=t.insertRow(); r.insertCell(); var c=r.insertCell(); c.cellIndex",
8399            "1",
8400        ),
8401        // `insertRow`/`deleteRow`/`insertCell`/`deleteCell`は仕様上、範囲外の
8402        // インデックス(`-1`未満、または`insertRow`/`insertCell`なら
8403        // `length`超過、`deleteRow`/`deleteCell`なら`length`以上)で
8404        // `IndexSizeError`を投げるべきだが、以前は範囲外の値をすべて
8405        // 「末尾へ追加」/「末尾を削除」として黙って受け入れてしまっていた。
8406        (
8407            "var t=document.createElement('table'); t.insertRow(); \
8408             try { t.insertRow(5); 'no-throw' } catch(e) { e.name }",
8409            "IndexSizeError",
8410        ),
8411        (
8412            "var t=document.createElement('table'); t.insertRow(); \
8413             try { t.deleteRow(5); 'no-throw' } catch(e) { e.name }",
8414            "IndexSizeError",
8415        ),
8416        (
8417            "var t=document.createElement('table'); var r=t.insertRow(); r.insertCell(); \
8418             try { r.insertCell(5); 'no-throw' } catch(e) { e.name }",
8419            "IndexSizeError",
8420        ),
8421        (
8422            "var t=document.createElement('table'); var r=t.insertRow(); r.insertCell(); \
8423             try { r.deleteCell(5); 'no-throw' } catch(e) { e.name }",
8424            "IndexSizeError",
8425        ),
8426        (
8427            // `insertRow(length)`(末尾追加)と`insertRow(-1)`(同じく末尾追加)は
8428            // 範囲内なので従来どおり例外にならない。
8429            "var t=document.createElement('table'); t.insertRow(); \
8430             t.insertRow(1); t.insertRow(-1); t.rows.length",
8431            "3",
8432        ),
8433        // `tr.sectionRowIndex`(丸ごと未対応だった。`rowIndex`は実装済み
8434        // なのに対になるこちらだけ抜けていた兄弟ギャップ。2026-07-17
8435        // 発見・実装)。`thead`2行+`tbody`3行の表で、`tbody`側2行目
8436        // (テーブル全体では4番目=`rowIndex:3`)の`sectionRowIndex`は
8437        // `tbody`内での位置`1`(0始まり)になる。
8438        (
8439            "var t=document.createElement('table'); \
8440             var h=t.createTHead(); h.insertRow(); h.insertRow(); \
8441             var b=document.createElement('tbody'); t.appendChild(b); \
8442             b.insertRow(); var r=b.insertRow(); b.insertRow(); \
8443             r.rowIndex + ',' + r.sectionRowIndex",
8444            "3,1",
8445        ),
8446        // `HTMLTableSectionElement.insertRow`/`.rows`(丸ごと未対応だった。
8447        // `table.insertRow`は実装済みなのに対になるこちらだけ抜けていた
8448        // 兄弟ギャップ。`tbody`自身が持つ`insertRow`/`.rows`はそのセクション
8449        // 内に限定される。2026-07-17 発見・実装)。
8450        (
8451            "var t=document.createElement('table'); var b=document.createElement('tbody'); \
8452             t.appendChild(b); b.insertRow(); b.insertRow(); b.rows.length",
8453            "2",
8454        ),
8455        // `thead`/`tbody`ラッパー無しで直接`table`に`insertRow`した場合は
8456        // `rowIndex`と`sectionRowIndex`が一致する。
8457        (
8458            "var t=document.createElement('table'); t.insertRow(); var r=t.insertRow(); \
8459             r.rowIndex + ',' + r.sectionRowIndex",
8460            "1,1",
8461        ),
8462        // `table.tHead`/`.tFoot`/`.caption`/`.tBodies`/`.createTHead()`/
8463        // `.createTFoot()`/`.createCaption()`/`.delete*()`(丸ごと未対応
8464        // だった。2026-07-17 発見・実装。「既存要素があれば返す、無ければ
8465        // 新規作成」の冪等な動作)。
8466        ("document.createElement('table').tHead", "null"),
8467        (
8468            "var t=document.createElement('table'); var h=t.createTHead(); h===t.tHead && h.tagName",
8469            "THEAD",
8470        ),
8471        (
8472            "var t=document.createElement('table'); var h1=t.createTHead(); var h2=t.createTHead(); h1===h2",
8473            "true",
8474        ),
8475        (
8476            "var t=document.createElement('table'); t.createTHead(); t.deleteTHead(); t.tHead",
8477            "null",
8478        ),
8479        (
8480            "var t=document.createElement('table'); var f=t.createTFoot(); f===t.tFoot && f.tagName",
8481            "TFOOT",
8482        ),
8483        (
8484            "var t=document.createElement('table'); var c=t.createCaption(); c===t.caption && c.tagName",
8485            "CAPTION",
8486        ),
8487        (
8488            "var t=document.createElement('table'); t.appendChild(document.createElement('tbody')); \
8489             t.appendChild(document.createElement('tbody')); t.tBodies.length",
8490            "2",
8491        ),
8492        // `node.isConnected`(click-outside 判定等で使われる)が丸ごと未対応だった。
8493        (
8494            "document.createElement('div').isConnected",
8495            "false",
8496        ),
8497        (
8498            "document.getElementById('t').isConnected",
8499            "true",
8500        ),
8501        (
8502            "var e=document.getElementById('t'); e.remove(); e.isConnected",
8503            "false",
8504        ),
8505        // `Node.ELEMENT_NODE`/`Node.TEXT_NODE`(`if (node.nodeType === Node.
8506        // ELEMENT_NODE)` という定番パターンで使われる標準定数)が丸ごと未対応
8507        // だった(グローバル `Node` 自体が存在しなかった)。
8508        (
8509            "document.createElement('div').nodeType === Node.ELEMENT_NODE",
8510            "true",
8511        ),
8512        ("Node.TEXT_NODE", "3"),
8513        ("Node.COMMENT_NODE", "8"),
8514        ("var c=document.createComment('test'); c.nodeType", "8"),
8515        ("var c=document.createComment('test'); c.nodeName", "#comment"),
8516        ("var c=document.createComment('test'); c.nodeValue", "test"),
8517        ("var c=document.createComment('test'); c.data", "test"),
8518        ("var c=document.createComment('test'); c.length", "4"),
8519        ("var c=document.createComment('test'); c.nodeValue='new'; c.nodeValue", "new"),
8520        ("var c=document.createComment('test'); c.data='new'; c.data", "new"),
8521        ("var c=document.createComment('test'); c.textContent", "test"),
8522        ("var c=document.createComment('test'); c.textContent='new'; c.textContent", "new"),
8523        (
8524            "var d=document.createElement('div'); var c=document.createComment('foo'); d.appendChild(c); d.innerHTML",
8525            "<!--foo-->",
8526        ),
8527        (
8528            "var t=document.getElementById('t'); var s=document.createElement('style'); s.textContent='div { color: red; }'; t.appendChild(s); var sheet=document.styleSheets[0]; sheet.cssRules.length",
8529            "1",
8530        ),
8531        (
8532            "var t=document.getElementById('t'); var s=document.createElement('style'); s.textContent='div { color: red; }'; t.appendChild(s); var sheet=document.styleSheets[0]; sheet.cssRules[0].cssText",
8533            "div { color: red; }",
8534        ),
8535        (
8536            "var t=document.getElementById('t'); var s=document.createElement('style'); s.textContent='div { color: red; }'; t.appendChild(s); var sheet=document.styleSheets[0]; sheet.cssRules[0].selectorText",
8537            "div",
8538        ),
8539        (
8540            "var t=document.getElementById('t'); var s=document.createElement('style'); s.textContent='div { color: red; }'; t.appendChild(s); var sheet=document.styleSheets[0]; sheet.insertRule('span { color: blue; }', 1); sheet.cssRules.length",
8541            "2",
8542        ),
8543        (
8544            "var t=document.getElementById('t'); var s=document.createElement('style'); s.textContent='div { color: red; }'; t.appendChild(s); var sheet=document.styleSheets[0]; sheet.insertRule('span { color: blue; }', 1); sheet.cssRules[1].cssText",
8545            "span { color: blue; }",
8546        ),
8547        (
8548            "var t=document.getElementById('t'); var s=document.createElement('style'); s.textContent='div { color: red; }'; t.appendChild(s); var sheet=document.styleSheets[0]; sheet.insertRule('span { color: blue; }', 1); sheet.deleteRule(0); sheet.cssRules.length",
8549            "1",
8550        ),
8551        (
8552            "var t=document.getElementById('t'); var s=document.createElement('style'); s.textContent='div { color: red; }'; t.appendChild(s); var sheet=document.styleSheets[0]; sheet.insertRule('span { color: blue; }', 1); sheet.deleteRule(0); sheet.cssRules[0].cssText",
8553            "span { color: blue; }",
8554        ),
8555        // `document.title` が常に install 時の空文字列固定で `<title>` 要素の中身/
8556        // 代入結果と一切連動していなかったバグ。ここでは `<head>` 自体が無い状態
8557        // (フレッシュな JsRuntime)での内部プロパティへのフォールバック保存/読み出し
8558        // を確認する(`<head>`/`<title>` 実要素との連動は別ブロックで確認)。
8559        ("document.title = 'Hello'; document.title", "Hello"),
8560        // `element.lang`/`.dir`(グローバル属性の JS プロパティ版)が他の属性直結
8561        // プロパティ群の点検から漏れて丸ごと未対応だった。
8562        (
8563            "var e=document.createElement('p'); e.lang='ja'; e.lang",
8564            "ja",
8565        ),
8566        (
8567            "var e=document.createElement('p'); e.dir='rtl'; e.dir",
8568            "rtl",
8569        ),
8570        (
8571            "var e=document.createElement('p'); e.setAttribute('lang','fr'); e.lang",
8572            "fr",
8573        ),
8574        // `element.title`/`.accessKey`/`.translate`(同じ理由で丸ごと未対応
8575        // だった。未マッチキーは巨大 match の末尾 `_ => Value::Undefined`
8576        // に落ちるだけで汎用フォールバックが無いため常に `undefined` に
8577        // なっていた。2026-07-16 発見・実装)。
8578        (
8579            "var e=document.createElement('p'); e.title='tip'; e.title",
8580            "tip",
8581        ),
8582        (
8583            "var e=document.createElement('p'); e.setAttribute('title','x'); e.title",
8584            "x",
8585        ),
8586        (
8587            "var e=document.createElement('p'); e.accessKey='k'; e.getAttribute('accesskey')",
8588            "k",
8589        ),
8590        // `translate` は既定 `true`、`translate=\"no\"` の時のみ `false`。
8591        ("document.createElement('p').translate", "true"),
8592        (
8593            "var e=document.createElement('p'); e.setAttribute('translate','no'); e.translate",
8594            "false",
8595        ),
8596        (
8597            "var e=document.createElement('p'); e.translate=false; e.getAttribute('translate')",
8598            "no",
8599        ),
8600        // `element.draggable`(丸ごと未対応だった。2026-07-16 発見・実装。
8601        // 明示指定が無ければ既定 `false` の簡略実装)。
8602        ("document.createElement('div').draggable", "false"),
8603        (
8604            "var e=document.createElement('div'); e.draggable=true; e.getAttribute('draggable')",
8605            "true",
8606        ),
8607        (
8608            "var e=document.createElement('div'); e.setAttribute('draggable','true'); e.draggable",
8609            "true",
8610        ),
8611        // `element.slot`(named slot 割り当て名。丸ごと未対応だった。
8612        // 2026-07-16 発見・実装)。
8613        (
8614            "var e=document.createElement('div'); e.slot='header'; e.getAttribute('slot')",
8615            "header",
8616        ),
8617        // DOM コンストラクタ階層 (Node -> Element -> HTMLElement -> HTMLXxxElement)
8618        // および `instanceof` / `.constructor` 連携の自己テスト
8619        (
8620            "document.createElement('div') instanceof HTMLDivElement",
8621            "true",
8622        ),
8623        (
8624            "document.createElement('div') instanceof HTMLElement",
8625            "true",
8626        ),
8627        (
8628            "document.createElement('div') instanceof Element",
8629            "true",
8630        ),
8631        (
8632            "document.createElement('div') instanceof Node",
8633            "true",
8634        ),
8635        (
8636            "document.createElement('div') instanceof HTMLInputElement",
8637            "false",
8638        ),
8639        (
8640            "document.createElement('input') instanceof HTMLInputElement",
8641            "true",
8642        ),
8643        (
8644            "document.createElement('input') instanceof HTMLElement",
8645            "true",
8646        ),
8647        (
8648            "document.createElement('form') instanceof HTMLFormElement",
8649            "true",
8650        ),
8651        (
8652            "document.createElement('div').constructor === HTMLDivElement",
8653            "true",
8654        ),
8655        (
8656            "try { new Node(); false; } catch(e) { e instanceof TypeError }",
8657            "true",
8658        ),
8659        (
8660            "try { new HTMLElement(); false; } catch(e) { e instanceof TypeError }",
8661            "true",
8662        ),
8663        (
8664            "var e=document.createElement('div'); e.setAttribute('slot','footer'); e.slot",
8665            "footer",
8666        ),
8667        // `element.tabIndex`(`tabindex` 属性の数値プロパティ版)も同じ理由で
8668        // 丸ごと未対応だった。未指定時の既定値は `-1`。
8669        (
8670            "document.createElement('div').tabIndex",
8671            "-1",
8672        ),
8673        (
8674            "var e=document.createElement('div'); e.tabIndex=3; e.tabIndex",
8675            "3",
8676        ),
8677        (
8678            "var e=document.createElement('div'); e.tabIndex=2; e.getAttribute('tabindex')",
8679            "2",
8680        ),
8681        // `element.hidden = true/false`(要素の表示/非表示切替の定番イディオム)の
8682        // setter が丸ごと未対応だった(getter は既に対応済み)。
8683        (
8684            "var e=document.createElement('div'); e.hidden=true; e.hidden",
8685            "true",
8686        ),
8687        (
8688            "var e=document.createElement('div'); e.hidden=true; e.hasAttribute('hidden')",
8689            "true",
8690        ),
8691        (
8692            "var e=document.createElement('div'); e.hidden=true; e.hidden=false; e.hidden",
8693            "false",
8694        ),
8695        // `form.noValidate`(`novalidate` 属性の camelCase プロパティ版)が丸ごと
8696        // 未対応だった。
8697        (
8698            "var f=document.createElement('form'); f.noValidate=true; f.noValidate",
8699            "true",
8700        ),
8701        (
8702            "var f=document.createElement('form'); f.noValidate=true; f.hasAttribute('novalidate')",
8703            "true",
8704        ),
8705        (
8706            "document.createElement('form').noValidate",
8707            "false",
8708        ),
8709        // `form.elements`(全フォームコントロールを走査する定番イディオム)が丸ごと
8710        // 未対応だった。
8711        (
8712            "var f=document.createElement('form'); var i=document.createElement('input'); f.appendChild(i); f.elements.length",
8713            "1",
8714        ),
8715        (
8716            "var f=document.createElement('form'); var i=document.createElement('input'); i.name='x'; f.appendChild(i); f.elements[0].name",
8717            "x",
8718        ),
8719        // `form.length`(`form.elements.length`と同じ値を返すIDL属性の数値
8720        // ショートハンド。`select.length`の対になる`<form>`側だが丸ごと
8721        // 未対応だった)。
8722        (
8723            "var f=document.createElement('form'); var i=document.createElement('input'); \
8724             var s=document.createElement('select'); f.appendChild(i); f.appendChild(s); \
8725             f.length + ',' + f.elements.length",
8726            "2,2",
8727        ),
8728        (
8729            "document.createElement('form').length",
8730            "0",
8731        ),
8732        // `fieldset.elements`(`HTMLFieldSetElement`のIDLプロパティ。丸ごと
8733        // 未対応だった。`form.elements`と異なり純粋なDOM子孫のみが対象。
8734        // 2026-07-17 発見・実装)。
8735        (
8736            "var fs=document.createElement('fieldset'); var i=document.createElement('input'); \
8737             fs.appendChild(i); fs.elements.length",
8738            "1",
8739        ),
8740        (
8741            "document.createElement('fieldset').elements.length",
8742            "0",
8743        ),
8744        // `fieldset.type`(仕様上、属性値に関わらず常に固定文字列
8745        // `"fieldset"`を返す読み取り専用IDLプロパティ。丸ごと未対応
8746        // だった。2026-07-17 発見・実装)。
8747        ("document.createElement('fieldset').type", "fieldset"),
8748        (
8749            "var fs=document.createElement('fieldset'); fs.setAttribute('type','x'); fs.type",
8750            "fieldset",
8751        ),
8752        // `select.type`(仕様上、`multiple`属性の有無で`"select-multiple"`/
8753        // `"select-one"`のいずれか固定文字列を返す読み取り専用IDL
8754        // プロパティ。`fieldset.type`と同じ理由で丸ごと未対応だった。
8755        // 2026-07-17 発見・実装)。
8756        ("document.createElement('select').type", "select-one"),
8757        (
8758            "var s=document.createElement('select'); s.multiple=true; s.type",
8759            "select-multiple",
8760        ),
8761        // `button.type`(`type`属性が無いか不正な値なら既定`"submit"`に
8762        // フォールバックする。`fieldset.type`/`select.type`と同じ理由で
8763        // 丸ごと未対応だった。`if (btn.type === 'submit')`という定番
8764        // チェックが常に偽になる実害があった。2026-07-17 発見・実装)。
8765        ("document.createElement('button').type", "submit"),
8766        // `textarea.type`(仕様上、属性値に関わらず常に固定文字列
8767        // `"textarea"`を返す読み取り専用IDLプロパティ。`fieldset.type`と
8768        // 同じ理由で丸ごと未対応だった。2026-07-17 発見・実装)。
8769        ("document.createElement('textarea').type", "textarea"),
8770        (
8771            "var b=document.createElement('button'); b.setAttribute('type','reset'); b.type",
8772            "reset",
8773        ),
8774        (
8775            "var b=document.createElement('button'); b.setAttribute('type','button'); b.type",
8776            "button",
8777        ),
8778        (
8779            "var b=document.createElement('button'); b.setAttribute('type','bogus'); b.type",
8780            "submit",
8781        ),
8782        // `input.type`(`type`属性が無いか既知の列挙値以外なら既定
8783        // `"text"`にフォールバックする。`fieldset.type`/`select.type`/
8784        // `button.type`と同じ理由で丸ごと未対応だった。全入力系IDL
8785        // プロパティの中で最も広く参照される`.type`が常に空文字列に
8786        // なっていた実害の大きいバグ。2026-07-17 発見・実装)。
8787        ("document.createElement('input').type", "text"),
8788        (
8789            "var i=document.createElement('input'); i.setAttribute('type','checkbox'); i.type",
8790            "checkbox",
8791        ),
8792        (
8793            "var i=document.createElement('input'); i.setAttribute('type','DATE'); i.type",
8794            "date",
8795        ),
8796        (
8797            "var i=document.createElement('input'); i.setAttribute('type','bogus'); i.type",
8798            "text",
8799        ),
8800        // `form.elements`が`form="formId"`属性によるフォーム外関連付け
8801        // (HTML5仕様の「form owner」)を反映していなかった兄弟ギャップの
8802        // 自己テストは、この配列が`(&str, &str)`(HTML fixtureを持たない)
8803        // 型のため3要素タプルでは追加できない。別途、独自のHTML fixtureを
8804        // 持つ`fd2_cases`(本ファイル内。`FormData`のform owner対応テストと
8805        // 同じfixtureを再利用)に追加してある。
8806        // `input.form`(自身が属する `<form>` への逆参照)が丸ごと未対応だった。
8807        (
8808            "var f=document.createElement('form'); f.id='ff'; var i=document.createElement('input'); f.appendChild(i); i.form.id",
8809            "ff",
8810        ),
8811        (
8812            "document.createElement('input').form === null",
8813            "true",
8814        ),
8815        // `input.labels`(`for` 属性の明示的な関連付けと、`<label>` 子孫の暗黙の
8816        // 関連付けの両方)が丸ごと未対応だった。
8817        (
8818            "var i=document.createElement('input'); i.id='u'; var l=document.createElement('label'); l.setAttribute('for','u'); i.labels.length",
8819            "1",
8820        ),
8821        (
8822            "var l=document.createElement('label'); var i=document.createElement('input'); l.appendChild(i); i.labels.length",
8823            "1",
8824        ),
8825        (
8826            "document.createElement('input').labels.length",
8827            "0",
8828        ),
8829        // `label.control`(`labels` の逆方向)が丸ごと未対応だった。
8830        (
8831            "var i=document.createElement('input'); i.id='v'; var l=document.createElement('label'); l.setAttribute('for','v'); l.control.id",
8832            "v",
8833        ),
8834        (
8835            "var l=document.createElement('label'); var i=document.createElement('input'); i.name='y'; l.appendChild(i); l.control.name",
8836            "y",
8837        ),
8838        (
8839            "document.createElement('label').control === null",
8840            "true",
8841        ),
8842        // `node.contains(other)`(click-outside 判定等で広く使われる)が丸ごと
8843        // 未対応だった。
8844        (
8845            "var w=document.getElementById('wrap'); var t=document.getElementById('t'); w.contains(t)+','+t.contains(w)+','+t.contains(t)",
8846            "true,false,true",
8847        ),
8848        // `Node.compareDocumentPosition(other)` が丸ごと未対応だった(`contains()` はあったが
8849        // 兄弟同士の前後関係や祖先/子孫の判定を1回のビットマスクで得る標準APIが欠落)。
8850        (
8851            "var t=document.getElementById('t'); t.compareDocumentPosition(t)",
8852            "0",
8853        ),
8854        (
8855            "var w=document.getElementById('wrap'); var t=document.getElementById('t'); t.compareDocumentPosition(w)",
8856            "10",
8857        ),
8858        (
8859            "var w=document.getElementById('wrap'); var t=document.getElementById('t'); w.compareDocumentPosition(t)",
8860            "20",
8861        ),
8862        (
8863            "var t=document.getElementById('t'); var s=document.getElementById('s'); t.compareDocumentPosition(s)",
8864            "4",
8865        ),
8866        (
8867            "var t=document.getElementById('t'); var s=document.getElementById('s'); s.compareDocumentPosition(t)",
8868            "2",
8869        ),
8870        // `element.click()` が単なる no-op で、実際のクリック(登録済みリスナの発火)を
8871        // 一切引き起こさなかったバグ(プログラム的クリックはテストコードで広く使われる
8872        // 定番パターン)。
8873        (
8874            "var e=document.getElementById('t'); var clicked=false; e.addEventListener('click', function(){ clicked=true; }); e.click(); clicked",
8875            "true",
8876        ),
8877        // `document.scrollingElement`(HTML5。丸ごと未対応だった。標準準拠
8878        // モードでは `documentElement`(`<html>`)と同じ要素を返す仕様)。
8879        ("document.scrollingElement.tagName", "HTML"),
8880        ("document.scrollingElement === document.documentElement", "true"),
8881        // `document.activeElement`/`element.focus()`/`.blur()`(HTML5。丸ごと
8882        // 未対応だった。以前は `activeElement` が常に `null` 固定、
8883        // `.focus()`/`.blur()` 自体が丸ごと存在しなかった)。
8884        (
8885            "document.getElementById('t').focus(); document.activeElement.id",
8886            "t",
8887        ),
8888        (
8889            "document.getElementById('t').focus(); document.getElementById('t') === document.activeElement",
8890            "true",
8891        ),
8892        (
8893            "document.getElementById('t').focus(); document.getElementById('s').focus(); document.activeElement.id",
8894            "s",
8895        ),
8896        (
8897            "document.getElementById('t').focus(); document.getElementById('t').blur(); \
8898             document.getElementById('t') === document.activeElement",
8899            "false",
8900        ),
8901        (
8902            "var log=''; document.getElementById('t').addEventListener('focus', ()=>log+='f'); \
8903             document.getElementById('t').addEventListener('blur', ()=>log+='b'); \
8904             document.getElementById('t').focus(); document.getElementById('s').focus(); log",
8905            "fb",
8906        ),
8907        (
8908            "var n=0; document.getElementById('t').addEventListener('focus', ()=>n++); \
8909             document.getElementById('t').focus(); document.getElementById('t').focus(); n",
8910            "1",
8911        ),
8912    ];
8913    total += dom2_cases.len();
8914    for (src, expect) in dom2_cases {
8915        let mut rt = JsRuntime::new();
8916        rt.dom
8917            .borrow_mut()
8918            .build_from(&crate::os_lib::dom::parse_html(dom2_html));
8919        match rt.eval(src) {
8920            Ok(v) if &v.to_js_string() == expect => passed += 1,
8921            Ok(v) => crate::println!(
8922                "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
8923                src,
8924                v.to_js_string(),
8925                expect
8926            ),
8927            Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
8928        }
8929    }
8930    // ===== DOM イベントシステム(Phase 3+)=====
8931    // addEventListener / removeEventListener / dispatchEvent と Event オブジェクトの
8932    // 伝播(capture→target→bubble)、preventDefault / stopPropagation /
8933    // stopImmediatePropagation / once / target・currentTarget を JS 層から検証する。
8934    // dispatchEvent はホストの dispatch_key/dispatch_mouse と同じ dispatch_event_in_interp を
8935    // 通るため、ここで伝播ロジック全体を決定論的にカバーできる。
8936    let ev_html = "<div id='outer'><div id='mid'><button id='btn'>go</button></div></div>";
8937    let event_cases: &[(&str, &str)] = &[
8938        // 基本: addEventListener したリスナが dispatchEvent で発火する。
8939        ("var n=0; var b=document.getElementById('btn'); b.addEventListener('click', function(){ n++; }); b.dispatchEvent({type:'click'}); n", "1"),
8940        // 同一要素に複数リスナ → 登録順に全て発火。
8941        ("var s=''; var b=document.getElementById('btn'); b.addEventListener('click', ()=>s+='a'); b.addEventListener('click', ()=>s+='b'); b.dispatchEvent({type:'click'}); s", "ab"),
8942        // removeEventListener で解除したリスナは発火しない。
8943        ("var n=0; var f=()=>n++; var b=document.getElementById('btn'); b.addEventListener('click', f); b.removeEventListener('click', f); b.dispatchEvent({type:'click'}); n", "0"),
8944        // バブリング: button で発火 → 祖先 mid / outer の click リスナも発火。
8945        ("var s=''; document.getElementById('outer').addEventListener('click', ()=>s+='O'); document.getElementById('mid').addEventListener('click', ()=>s+='M'); document.getElementById('btn').addEventListener('click', ()=>s+='B'); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); s", "BMO"),
8946        // event.target は発火元、currentTarget は処理中の要素(バブリング中は祖先)。
8947        ("var t=''; document.getElementById('outer').addEventListener('click', function(e){ t = e.target.id + '/' + e.currentTarget.id; }); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); t", "btn/outer"),
8948        // stopPropagation: button のリスナで止めると祖先には伝わらない。
8949        ("var s=''; document.getElementById('outer').addEventListener('click', ()=>s+='O'); document.getElementById('btn').addEventListener('click', function(e){ s+='B'; e.stopPropagation(); }); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); s", "B"),
8950        // stopImmediatePropagation: 同一要素の後続リスナも止める。
8951        ("var s=''; var b=document.getElementById('btn'); b.addEventListener('click', function(e){ s+='1'; e.stopImmediatePropagation(); }); b.addEventListener('click', ()=>s+='2'); b.dispatchEvent({type:'click'}); s", "1"),
8952        // preventDefault → dispatchEvent は false を返す(defaultPrevented)。
8953        ("var b=document.getElementById('btn'); b.addEventListener('click', e=>e.preventDefault()); b.dispatchEvent({type:'click'})", "false"),
8954        // preventDefault しなければ dispatchEvent は true を返す。
8955        ("var b=document.getElementById('btn'); b.addEventListener('click', ()=>{}); b.dispatchEvent({type:'click'})", "true"),
8956        // e.defaultPrevented フラグが preventDefault 後に true。
8957        ("var d=false; var b=document.getElementById('btn'); b.addEventListener('click', function(e){ e.preventDefault(); d=e.defaultPrevented; }); b.dispatchEvent({type:'click'}); d", "true"),
8958        // once: true のリスナは 1 回だけ発火し、2 回目は無視される。
8959        ("var n=0; var b=document.getElementById('btn'); b.addEventListener('click', ()=>n++, {once:true}); b.dispatchEvent({type:'click'}); b.dispatchEvent({type:'click'}); n", "1"),
8960        // capture フェーズ: 祖先の capture リスナは target より先に発火。
8961        ("var s=''; document.getElementById('outer').addEventListener('click', ()=>s+='C', true); document.getElementById('btn').addEventListener('click', ()=>s+='T'); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); s", "CT"),
8962        // event.type が正しく渡る。
8963        ("var ty=''; var b=document.getElementById('btn'); b.addEventListener('custom', function(e){ ty=e.type; }); b.dispatchEvent({type:'custom'}); ty", "custom"),
8964        // カスタムプロパティ(detail 等)がリスナ側へ転送される。
8965        ("var d=0; var b=document.getElementById('btn'); b.addEventListener('ping', function(e){ d=e.detail; }); b.dispatchEvent({type:'ping', detail:7}); d", "7"),
8966        // 異なるイベント型のリスナは発火しない。
8967        ("var n=0; var b=document.getElementById('btn'); b.addEventListener('mouseover', ()=>n++); b.dispatchEvent({type:'click'}); n", "0"),
8968        // バブリングフラグ未指定でも、本エンジンは委譲(イベント委譲)を成立させるため
8969        // 祖先のリスナも発火する設計(blur/focus を親で受ける UI を許容)。
8970        ("var s=''; document.getElementById('outer').addEventListener('foo', ()=>s+='O'); document.getElementById('btn').addEventListener('foo', ()=>s+='B'); document.getElementById('btn').dispatchEvent({type:'foo'}); s", "BO"),
8971        // `document.addEventListener('click', fn)` によるイベント委譲パターンが
8972        // 丸ごと未対応だった(`document` は `self.dom.nodes` の一員ではないため
8973        // 祖先チェーンに一切含まれず、`click`等の汎用種別は登録すら黙って
8974        // 捨てられていた。2026-07-16 発見・実装)。要素からバブリングした
8975        // クリックが `document` レベルのリスナまで届くことを確認。
8976        ("var s=''; document.addEventListener('click', ()=>s+='D'); document.getElementById('btn').addEventListener('click', ()=>s+='B'); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); s", "BD"),
8977        // `stopPropagation()` すれば document レベルへは届かない。
8978        ("var s=''; document.addEventListener('click', ()=>s+='D'); document.getElementById('btn').addEventListener('click', function(e){ s+='B'; e.stopPropagation(); }); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); s", "B"),
8979        // document.removeEventListener('click', fn) で解除できる。
8980        ("var n=0; var f=()=>n++; document.addEventListener('click', f); document.removeEventListener('click', f); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); n", "0"),
8981        // document 側でも e.target は発火元要素、currentTarget は document 自身。
8982        ("var r=''; document.addEventListener('click', function(e){ r = e.target.id + '/' + (e.currentTarget === document); }); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); r", "btn/true"),
8983        // `document.dispatchEvent(new Event('click'))` で document 自身から直接
8984        // 合成発火することもできる(実DOM要素を経由しない経路)。
8985        ("var n=0; document.addEventListener('click', ()=>n++); document.dispatchEvent(new Event('click')); n", "1"),
8986        // `document.addEventListener(type, fn, {capture:true})` がキャプチャ
8987        // フェーズを一切扱えないバグ(丸ごと未対応だった。`document` の汎用
8988        // リスナは `event_target_add_event_listener` を再利用しており、
8989        // `capture` オプションは元々「DOM ツリーを持たないクラスでは無意味」
8990        // として即座に破棄されていた。`document` に対して再利用したことで
8991        // 意味が変わり、キャプチャ登録が黙ってバブル扱いになっていた。
8992        // 2026-07-16 発見・実装)。document の capture リスナは祖先の
8993        // capture リスナより先(最も外側)に発火する。
8994        ("var s=''; document.addEventListener('click', ()=>s+='D', true); document.getElementById('outer').addEventListener('click', ()=>s+='O', true); document.getElementById('btn').addEventListener('click', ()=>s+='B'); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); s", "DOB"),
8995        // capture 登録は bubble フェーズでは発火しない(両方登録すれば両方発火)。
8996        ("var s=''; document.addEventListener('click', ()=>s+='C', true); document.addEventListener('click', ()=>s+='B'); document.getElementById('btn').dispatchEvent({type:'click', bubbles:true}); s", "CB"),
8997    ];
8998    total += event_cases.len();
8999    for (src, expect) in event_cases {
9000        let mut rt = JsRuntime::new();
9001        rt.dom
9002            .borrow_mut()
9003            .build_from(&crate::os_lib::dom::parse_html(ev_html));
9004        match rt.eval(src) {
9005            Ok(v) if &v.to_js_string() == expect => passed += 1,
9006            Ok(v) => crate::println!(
9007                "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9008                src,
9009                v.to_js_string(),
9010                expect
9011            ),
9012            Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9013        }
9014    }
9015
9016    // FormData(form): <form> 配下のコントロールから値を収集(DOM 必須)。
9017    {
9018        let fd_html = "<form id='f'>\
9019            <input name='user' value='alice'>\
9020            <input name='nope'>\
9021            <input type='checkbox' name='agree' value='yes' checked>\
9022            <input type='checkbox' name='news' value='1'>\
9023            <input type='radio' name='plan' value='free'>\
9024            <input type='radio' name='plan' value='pro' checked>\
9025            <input type='submit' name='btn' value='go'>\
9026            <select name='color' value='green'><option value='red'>R</option><option value='green'>G</option></select>\
9027            <textarea name='bio'>hello</textarea>\
9028        </form>";
9029        let fd_cases: &[(&str, &str)] = &[
9030            // user=alice, agree=yes(checked), plan=pro(checked radio), color=green, bio=hello。
9031            // nope は name はあるが value 空 → 空値で含まれる。news/free は未チェックで除外。submit は除外。
9032            (
9033                "new FormData(document.getElementById('f')).get('user')",
9034                "alice",
9035            ),
9036            (
9037                "new FormData(document.getElementById('f')).get('agree')",
9038                "yes",
9039            ),
9040            (
9041                "new FormData(document.getElementById('f')).get('plan')",
9042                "pro",
9043            ),
9044            (
9045                "var f=new FormData(document.getElementById('f')); f.has('news')+','+f.has('btn')",
9046                "false,false",
9047            ),
9048        ];
9049        total += fd_cases.len();
9050        for (src, expect) in fd_cases {
9051            let mut rt = JsRuntime::new();
9052            rt.dom
9053                .borrow_mut()
9054                .build_from(&crate::os_lib::dom::parse_html(fd_html));
9055            match rt.eval(src) {
9056                Ok(v) if &v.to_js_string() == expect => passed += 1,
9057                Ok(v) => crate::println!(
9058                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9059                    src,
9060                    v.to_js_string(),
9061                    expect
9062                ),
9063                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9064            }
9065        }
9066    }
9067    {
9068        // `collect_form_data`(FormData収集の実体)が`form="formId"`属性による
9069        // フォーム外関連付け(HTML5仕様の「form owner」。`element.form`/
9070        // `checkValidity()`は既に対応済み)を反映していなかった兄弟ギャップ。
9071        // 併せて`<fieldset disabled>`祖先による暗黙の無効化(`is_disabled`)も
9072        // 送信データ収集側では未反映だったため同時に修正した。
9073        let fd2_html = "<form id='f6'>\
9074            <input name='inside' value='a'>\
9075            <fieldset disabled><input name='hidden_by_fieldset' value='b'></fieldset>\
9076        </form>\
9077        <input name='outside' form='f6' value='c'>\
9078        <input name='not_associated' value='d'>";
9079        let fd2_cases: &[(&str, &str)] = &[
9080            (
9081                "new FormData(document.getElementById('f6')).get('inside')",
9082                "a",
9083            ),
9084            (
9085                "new FormData(document.getElementById('f6')).get('outside')",
9086                "c",
9087            ),
9088            (
9089                "var f=new FormData(document.getElementById('f6')); f.has('hidden_by_fieldset')",
9090                "false",
9091            ),
9092            (
9093                "var f=new FormData(document.getElementById('f6')); f.has('not_associated')",
9094                "false",
9095            ),
9096            // `form.elements`も同じ`form_associated_controls`を再利用するため、
9097            // フォーム外の`outside`(`form='f6'`)を含む3件(inside/
9098            // hidden_by_fieldset/outside。`not_associated`は含まれない)になる。
9099            (
9100                "document.getElementById('f6').elements.length",
9101                "3",
9102            ),
9103        ];
9104        total += fd2_cases.len();
9105        for (src, expect) in fd2_cases {
9106            let mut rt = JsRuntime::new();
9107            rt.dom
9108                .borrow_mut()
9109                .build_from(&crate::os_lib::dom::parse_html(fd2_html));
9110            match rt.eval(src) {
9111                Ok(v) if &v.to_js_string() == expect => passed += 1,
9112                Ok(v) => crate::println!(
9113                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9114                    src,
9115                    v.to_js_string(),
9116                    expect
9117                ),
9118                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9119            }
9120        }
9121    }
9122    // `<select multiple>` — 選択された option 1つにつき1エントリを送信する仕様が
9123    // FormData 収集で丸ごと未対応だったバグ(複数選択リストボックスという定番 UI
9124    // パターン)。`selectedOptions`(丸ごと未対応だった)も合わせて確認する。
9125    {
9126        let sm_html = "<form id='f2'>\
9127            <select name='tags' multiple>\
9128                <option value='a' selected>A</option>\
9129                <option value='b'>B</option>\
9130                <option value='c' selected>C</option>\
9131            </select>\
9132        </form>";
9133        let sm_cases: &[(&str, &str)] = &[
9134            (
9135                "new FormData(document.getElementById('f2')).getAll('tags').join(',')",
9136                "a,c",
9137            ),
9138            (
9139                "document.querySelector('select').selectedOptions.length",
9140                "2",
9141            ),
9142            (
9143                "Array.from(document.querySelector('select').selectedOptions).map(o => o.value).join(',')",
9144                "a,c",
9145            ),
9146            // `select.options`(動的にドロップダウンを構築する定番イディオム)が
9147            // 丸ごと未対応だった。
9148            (
9149                "document.querySelector('select').options.length",
9150                "3",
9151            ),
9152            (
9153                "Array.from(document.querySelector('select').options).map(o => o.value).join(',')",
9154                "a,b,c",
9155            ),
9156            // `select.options.add`/`.remove`/`.namedItem`(`HTMLOptionsCollection`。
9157            // 丸ごと未対応だった)。
9158            (
9159                "var s=document.querySelector('select'); \
9160                 var o=new Option('D','d'); o.id='optd'; s.options.add(o); \
9161                 s.options.length + ':' + s.options[3].value",
9162                "4:d",
9163            ),
9164            (
9165                "var s=document.querySelector('select'); s.options.remove(1); \
9166                 Array.from(s.options).map(o => o.value).join(',')",
9167                "a,c",
9168            ),
9169            (
9170                "document.querySelector('select').options.namedItem('nope')",
9171                "null",
9172            ),
9173            (
9174                "var s=document.querySelector('select'); s.options.item(0).value",
9175                "a",
9176            ),
9177            (
9178                "var s=document.querySelector('select'); s.options.selectedIndex=1; s.selectedIndex",
9179                "1",
9180            ),
9181            (
9182                "var s=document.querySelector('select'); s.selectedIndex=2; s.options.selectedIndex",
9183                "2",
9184            ),
9185        ];
9186        total += sm_cases.len();
9187        for (src, expect) in sm_cases {
9188            let mut rt = JsRuntime::new();
9189            rt.dom
9190                .borrow_mut()
9191                .build_from(&crate::os_lib::dom::parse_html(sm_html));
9192            match rt.eval(src) {
9193                Ok(v) if &v.to_js_string() == expect => passed += 1,
9194                Ok(v) => crate::println!(
9195                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9196                    src,
9197                    v.to_js_string(),
9198                    expect
9199                ),
9200                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9201            }
9202        }
9203    }
9204    // `<select>`配下`<optgroup>`でグループ化された`<option>`が丸ごと見えなく
9205    // なっていたバグ(`select.options`/`selectedOptions`/フォーム送信の
9206    // どちらも直接の子要素しか見ておらず、`<optgroup>`という非常によく
9207    // 使われるカテゴリ分けパターンを完全に見落としていた。2026-07-17
9208    // 発見・実装)。
9209    // 【2026-07-24】以前`optgroup_total`が固定値`5`だったが、実際の`og_cases`
9210    // 配列は4件しか無く、`total`側だけ1件多く数える「集計ずれ」の一例だった
9211    // (`pass == total`の一言判定のみでは個別FAIL行が一切出ないままカテゴリ
9212    // 丸ごとFAILになり原因不明だった)。`.len()`から動的に求めることで
9213    // 今後同じズレが再発しないようにする。
9214    let og_html = "<form id='f3'><select name='fruit'><optgroup label='Citrus'><option value='lemon'>Lemon</option><option value='lime' selected>Lime</option></optgroup><optgroup label='Berries'><option value='straw'>Strawberry</option></optgroup></select></form>";
9215    let og_cases: &[(&str, &str)] = &[
9216        (
9217            "document.querySelector('select').options.length",
9218            "3",
9219        ),
9220        (
9221            "Array.from(document.querySelector('select').options).map(o => o.value).join(',')",
9222            "lemon,lime,straw",
9223        ),
9224        (
9225            "document.querySelector('select').selectedOptions.length",
9226            "1",
9227        ),
9228        (
9229            "document.querySelector('select').value",
9230            "lime",
9231        ),
9232    ];
9233    total += og_cases.len();
9234    {
9235        for (src, expect) in og_cases {
9236            let mut rt = JsRuntime::new();
9237            rt.dom
9238                .borrow_mut()
9239                .build_from(&crate::os_lib::dom::parse_html(og_html));
9240            match rt.eval(src) {
9241                Ok(v) if &v.to_js_string() == expect => passed += 1,
9242                Ok(v) => crate::println!(
9243                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9244                    src,
9245                    v.to_js_string(),
9246                    expect
9247                ),
9248                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9249            }
9250        }
9251    }
9252    // `document.title` が実際の `<head><title>` 要素と連動すること(既存 `<title>` の
9253    // 更新、無い場合は `<head>` へ新規作成)を、実 DOM 構造を持つフィクスチャで確認する。
9254    {
9255        let title_cases: &[(&str, &str, &str)] = &[
9256            // 既存の <title> があれば、その中身が更新される。
9257            (
9258                "<html><head><title>old</title></head><body></body></html>",
9259                "document.title",
9260                "old",
9261            ),
9262            (
9263                "<html><head><title>old</title></head><body></body></html>",
9264                "document.title='new'; document.querySelector('title').textContent",
9265                "new",
9266            ),
9267            // <head> はあるが <title> が無い場合、新規作成されて連動する。
9268            (
9269                "<html><head></head><body></body></html>",
9270                "document.title='created'; document.querySelector('title').textContent",
9271                "created",
9272            ),
9273        ];
9274        total += title_cases.len();
9275        for (html, src, expect) in title_cases {
9276            let mut rt = JsRuntime::new();
9277            rt.dom
9278                .borrow_mut()
9279                .build_from(&crate::os_lib::dom::parse_html(html));
9280            match rt.eval(src) {
9281                Ok(v) if &v.to_js_string() == expect => passed += 1,
9282                Ok(v) => crate::println!(
9283                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9284                    src,
9285                    v.to_js_string(),
9286                    expect
9287                ),
9288                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9289            }
9290        }
9291    }
9292    // `document.forms`/`.images`/`.scripts`(文書全体をタグ横断で集約する定番の
9293    // HTMLCollection 群)が丸ごと未対応だった。
9294    {
9295        let coll_html = "<html><body>\
9296            <form id='f1'></form><form id='f2'></form>\
9297            <img src='a.png'><img src='b.png'><img src='c.png'>\
9298            <script>1</script>\
9299            <a href='x.html'>x</a><a name='top'>anchor</a>\
9300            <area href='y.html' alt='y'>\
9301            <embed src='z.swf'>\
9302        </body></html>";
9303        let coll_cases: &[(&str, &str)] = &[
9304            ("document.forms.length", "2"),
9305            ("document.images.length", "3"),
9306            ("document.scripts.length", "1"),
9307            // `document.links`(href 付きの a/area のみ。`name` のみの a は含まない)が
9308            // 丸ごと未対応だった。
9309            ("document.links.length", "2"),
9310            // `document.anchors`(`name` 属性付きの a のみ)も丸ごと未対応だった。
9311            ("document.anchors.length", "1"),
9312            // `document.embeds`/`.plugins`(仕様上同じ集合を指すエイリアス)/
9313            // `document.applets`(Java アプレット廃止に伴い常に空固定のレガシー
9314            // スタブ)が丸ごと未対応だった。2026-07-16 発見・実装。
9315            ("document.embeds.length", "1"),
9316            ("document.plugins.length", "1"),
9317            ("document.applets.length", "0"),
9318        ];
9319        total += coll_cases.len();
9320        for (src, expect) in coll_cases {
9321            let mut rt = JsRuntime::new();
9322            rt.dom
9323                .borrow_mut()
9324                .build_from(&crate::os_lib::dom::parse_html(coll_html));
9325            match rt.eval(src) {
9326                Ok(v) if &v.to_js_string() == expect => passed += 1,
9327                Ok(v) => crate::println!(
9328                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9329                    src,
9330                    v.to_js_string(),
9331                    expect
9332                ),
9333                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9334            }
9335        }
9336    }
9337    // getBoundingClientRect / offset* (レンダラが set_rect で注入する矩形を JS から読む)。
9338    {
9339        let rect_checks: &[(&str, &str)] = &[
9340            (
9341                "document.getElementById('t').getBoundingClientRect().width",
9342                "30",
9343            ),
9344            (
9345                "document.getElementById('t').getBoundingClientRect().height",
9346                "40",
9347            ),
9348            (
9349                "document.getElementById('t').getBoundingClientRect().left",
9350                "10",
9351            ),
9352            (
9353                "document.getElementById('t').getBoundingClientRect().bottom",
9354                "60",
9355            ),
9356            ("document.getElementById('t').offsetWidth", "30"),
9357            ("document.getElementById('t').offsetTop", "20"),
9358            // clientWidth = 30 (width) - 5 (left border) - 3 (right border) = 22
9359            ("document.getElementById('t').clientWidth", "22"),
9360            // clientHeight = 40 (height) - 2 (top border) - 4 (bottom border) = 34
9361            ("document.getElementById('t').clientHeight", "34"),
9362            // clientLeft = 5 (left border)
9363            ("document.getElementById('t').clientLeft", "5"),
9364            // clientTop = 2 (top border)
9365            ("document.getElementById('t').clientTop", "2"),
9366            // `element.getClientRects()`(丸ごと未対応だった。テキスト行フラグメント
9367            // モデルが無いこの処理系では要素1つにつき `getBoundingClientRect()` と
9368            // 同じ矩形を1個だけ持つ配列として返す簡略実装)。
9369            ("document.getElementById('t').getClientRects().length", "1"),
9370            (
9371                "document.getElementById('t').getClientRects()[0].width",
9372                "30",
9373            ),
9374            // rect が未登録(レンダラ未描画)の要素は仕様どおり空配列。
9375            ("document.getElementById('s').getClientRects().length", "0"),
9376            // `element.offsetParent`(丸ごと未対応だった。祖先に position が
9377            // static 以外の要素も `<body>` も無ければ仕様どおり `null`)。
9378            (
9379                "document.getElementById('t').offsetParent",
9380                "null",
9381            ),
9382            (
9383                "document.getElementById('wrap').style.position='relative'; \
9384                 document.getElementById('t').offsetParent.id",
9385                "wrap",
9386            ),
9387        ];
9388        total += rect_checks.len();
9389        for (src, expect) in rect_checks {
9390            let mut rt = JsRuntime::new();
9391            rt.dom
9392                .borrow_mut()
9393                .build_from(&crate::os_lib::dom::parse_html(dom2_html));
9394            // レンダラ相当: #t の矩形 (x=10,y=20,w=30,h=40) を注入。
9395            let idx = rt.dom.borrow().get_element_by_id("t");
9396            if let Some(i) = idx {
9397                rt.dom.borrow_mut().set_rect(i, 10, 20, 30, 40);
9398                rt.dom.borrow_mut().set_border_widths(i, 2, 3, 4, 5); // top=2, right=3, bottom=4, left=5
9399            }
9400            match rt.eval(src) {
9401                Ok(v) if &v.to_js_string() == expect => passed += 1,
9402                Ok(v) => crate::println!(
9403                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9404                    src,
9405                    v.to_js_string(),
9406                    expect
9407                ),
9408                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9409            }
9410        }
9411    }
9412
9413    // `document.elementFromPoint(x,y)`/`.elementsFromPoint(x,y)`(座標ヒットテスト)
9414    // が丸ごと未対応だった。`rects`(レンダラが `set_rect` で注入)から座標を含む
9415    // 矩形を集め、最も面積が小さい(=最も深くネストした)要素を返す簡略実装。
9416    {
9417        let point_checks: &[(&str, &str)] = &[
9418            ("document.elementFromPoint(20, 15).id", "t"),
9419            ("document.elementFromPoint(5, 5).id", "wrap"),
9420            ("document.elementFromPoint(500, 500)", "null"),
9421            (
9422                "document.elementsFromPoint(20, 15).map(e => e.id).join(',')",
9423                "t,wrap",
9424            ),
9425        ];
9426        total += point_checks.len();
9427        for (src, expect) in point_checks {
9428            let mut rt = JsRuntime::new();
9429            rt.dom
9430                .borrow_mut()
9431                .build_from(&crate::os_lib::dom::parse_html(dom2_html));
9432            // レンダラ相当: 'wrap'(外側、大きい矩形)に 't'(内側、小さい矩形)が
9433            // ネストした状態を注入。
9434            let wrap_idx = rt.dom.borrow().get_element_by_id("wrap");
9435            if let Some(i) = wrap_idx {
9436                rt.dom.borrow_mut().set_rect(i, 0, 0, 100, 100);
9437            }
9438            let t_idx = rt.dom.borrow().get_element_by_id("t");
9439            if let Some(i) = t_idx {
9440                rt.dom.borrow_mut().set_rect(i, 10, 10, 30, 20);
9441            }
9442            match rt.eval(src) {
9443                Ok(v) if &v.to_js_string() == expect => passed += 1,
9444                Ok(v) => crate::println!(
9445                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9446                    src,
9447                    v.to_js_string(),
9448                    expect
9449                ),
9450                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9451            }
9452        }
9453    }
9454
9455    // getComputedStyle(set_computed_style で注入した算出スタイルを camelCase/kebab/getPropertyValue で読む)。
9456    {
9457        let cs_checks: &[(&str, &str)] = &[
9458            ("getComputedStyle(document.getElementById('t')).color", "red"),
9459            ("getComputedStyle(document.getElementById('t')).backgroundColor", "blue"),
9460            ("getComputedStyle(document.getElementById('t')).getPropertyValue('background-color')", "blue"),
9461            ("window.getComputedStyle(document.getElementById('t')).fontSize", "16px"),
9462        ];
9463        total += cs_checks.len();
9464        for (src, expect) in cs_checks {
9465            let mut rt = JsRuntime::new();
9466            rt.dom
9467                .borrow_mut()
9468                .build_from(&crate::os_lib::dom::parse_html(dom2_html));
9469            let idx = rt.dom.borrow().get_element_by_id("t");
9470            if let Some(i) = idx {
9471                let mut d = rt.dom.borrow_mut();
9472                d.set_computed_style(i, "color", "red");
9473                d.set_computed_style(i, "background-color", "blue");
9474                d.set_computed_style(i, "font-size", "16px");
9475            }
9476            match rt.eval(src) {
9477                Ok(v) if &v.to_js_string() == expect => passed += 1,
9478                Ok(v) => crate::println!(
9479                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9480                    src,
9481                    v.to_js_string(),
9482                    expect
9483                ),
9484                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9485            }
9486        }
9487    }
9488
9489    // `element.checkVisibility()`(DOM 標準。丸ごと未対応だった)。
9490    {
9491        let cv_checks: &[(&str, &str, &[(&str, &str, &str)])] = &[
9492            (
9493                "document.getElementById('t').checkVisibility()",
9494                "true",
9495                &[],
9496            ),
9497            (
9498                "document.getElementById('t').checkVisibility()",
9499                "false",
9500                &[("t", "display", "none")],
9501            ),
9502            (
9503                "document.getElementById('t').checkVisibility()",
9504                "false",
9505                &[("wrap", "display", "none")],
9506            ),
9507            (
9508                "document.getElementById('t').checkVisibility()",
9509                "false",
9510                &[("t", "visibility", "hidden")],
9511            ),
9512        ];
9513        total += cv_checks.len();
9514        for (src, expect, styles) in cv_checks {
9515            let mut rt = JsRuntime::new();
9516            rt.dom
9517                .borrow_mut()
9518                .build_from(&crate::os_lib::dom::parse_html(dom2_html));
9519            for (id, prop, val) in *styles {
9520                let idx = rt.dom.borrow().get_element_by_id(id);
9521                if let Some(i) = idx {
9522                    rt.dom.borrow_mut().set_computed_style(i, prop, val);
9523                }
9524            }
9525            match rt.eval(src) {
9526                Ok(v) if &v.to_js_string() == expect => passed += 1,
9527                Ok(v) => crate::println!(
9528                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
9529                    src,
9530                    v.to_js_string(),
9531                    expect
9532                ),
9533                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
9534            }
9535        }
9536    }
9537
9538    // クリックディスパッチの統合確認(リスナ登録→dispatch_click→カウンタ増加)。
9539    {
9540        total += 1;
9541        let mut rt = JsRuntime::new();
9542        rt.dom
9543            .borrow_mut()
9544            .build_from(&crate::os_lib::dom::parse_html(dom_html));
9545        let _ = rt.eval("var clicks=0; document.getElementById('b').addEventListener('click', () => { clicks++; document.getElementById('out').textContent = 'clicked ' + clicks; });");
9546        let bidx = rt.dom.borrow().get_element_by_id("b");
9547        if let Some(i) = bidx {
9548            rt.dispatch_click(i);
9549            rt.dispatch_click(i);
9550        }
9551        match rt.eval("clicks + ':' + document.getElementById('out').textContent") {
9552            Ok(v) if v.to_js_string() == "2:clicked 2" => passed += 1,
9553            Ok(v) => crate::println!(
9554                "JS_SELFTEST FAIL: click dispatch => `{}` (want `2:clicked 2`)",
9555                v.to_js_string()
9556            ),
9557            Err(e) => crate::println!("JS_SELFTEST ERR:  click dispatch => {}", e),
9558        }
9559    }
9560
9561    // Event: バブリング / target・currentTarget / stopPropagation / preventDefault / stopImmediate。
9562    let event_html = "<div id='par'><button id='btn'>go</button></div>";
9563    let event_checks: &[(&str, &str, &str)] = &[
9564        // 委譲: par のリスナが子 btn のクリックで発火。target=btn, currentTarget=par。
9565        ("var L=''; document.getElementById('par').addEventListener('click', function(e){ L += 'par('+e.target.id+','+e.currentTarget.id+')'; });",
9566         "L", "par(btn,par)"),
9567        // バブリング順: target(btn) → 祖先(par)。
9568        ("var L=''; document.getElementById('btn').addEventListener('click', function(){ L+='btn'; }); document.getElementById('par').addEventListener('click', function(){ L+='-par'; });",
9569         "L", "btn-par"),
9570        // stopPropagation で祖先に伝播しない。
9571        ("var L=''; document.getElementById('btn').addEventListener('click', function(e){ L+='btn'; e.stopPropagation(); }); document.getElementById('par').addEventListener('click', function(){ L+='-par'; });",
9572         "L", "btn"),
9573        // preventDefault → defaultPrevented。
9574        ("var L=''; document.getElementById('btn').addEventListener('click', function(e){ e.preventDefault(); L += e.defaultPrevented; });",
9575         "L", "true"),
9576        // stopImmediatePropagation で同一ノードの後続リスナも止まる。
9577        ("var L=''; document.getElementById('btn').addEventListener('click', function(e){ L+='1'; e.stopImmediatePropagation(); }); document.getElementById('btn').addEventListener('click', function(){ L+='2'; });",
9578         "L", "1"),
9579        // キャプチャ→ターゲット→バブルの順。par(capture) → btn → par(bubble)。
9580        ("var L=''; document.getElementById('par').addEventListener('click', function(){ L+='cap'; }, true); document.getElementById('btn').addEventListener('click', function(){ L+='-tgt'; }); document.getElementById('par').addEventListener('click', function(){ L+='-bub'; });",
9581         "L", "cap-tgt-bub"),
9582        // eventPhase: capture=1, at target=2, bubble=3。
9583        ("var P=''; document.getElementById('par').addEventListener('click', function(e){ P+=e.eventPhase; }, true); document.getElementById('btn').addEventListener('click', function(e){ P+=e.eventPhase; }); document.getElementById('par').addEventListener('click', function(e){ P+=e.eventPhase; });",
9584         "P", "123"),
9585        // once: クリックで一度発火する(複数回 dispatch のテストは専用ブロックで実施)。
9586        ("var N=0; document.getElementById('btn').addEventListener('click', function(){ N++; }, {once:true}); N",
9587         "N", "1"),
9588        // removeEventListener: 登録解除すると発火しない。
9589        ("var L=''; function h(){ L+='x'; } document.getElementById('btn').addEventListener('click', h); document.getElementById('btn').removeEventListener('click', h);",
9590         "L", ""),
9591        // composedPath: target→祖先(btn, par, ...)。先頭2件の id を確認。
9592        ("var C=''; document.getElementById('btn').addEventListener('click', function(e){ var p=e.composedPath(); C=p[0].id+','+p[1].id; });",
9593         "C", "btn,par"),
9594    ];
9595    total += event_checks.len();
9596    for (setup, read, expect) in event_checks {
9597        let mut rt = JsRuntime::new();
9598        rt.dom
9599            .borrow_mut()
9600            .build_from(&crate::os_lib::dom::parse_html(event_html));
9601        let _ = rt.eval(setup);
9602        let bidx = rt.dom.borrow().get_element_by_id("btn");
9603        if let Some(i) = bidx {
9604            rt.dispatch_click(i);
9605        }
9606        match rt.eval(read) {
9607            Ok(v) if &v.to_js_string() == expect => passed += 1,
9608            Ok(v) => crate::println!(
9609                "JS_SELFTEST FAIL: event `{}` => `{}` (want `{}`)",
9610                read,
9611                v.to_js_string(),
9612                expect
9613            ),
9614            Err(e) => crate::println!("JS_SELFTEST ERR:  event `{}` => {}", read, e),
9615        }
9616    }
9617
9618    // dispatchEvent: 手動で Event を発火し、リスナが受け取りバブリングする
9619    // (`bubbles: true` を明示。仕様上 `new Event('foo')` の既定は `bubbles: false`
9620    // で、以前は `bubbles` を一切見ず常に無条件で祖先まで伝播していたバグが
9621    // あったため、明示的に `true` を指定してバブリングの意図を検証する)。
9622    {
9623        total += 1;
9624        let mut rt = JsRuntime::new();
9625        rt.dom
9626            .borrow_mut()
9627            .build_from(&crate::os_lib::dom::parse_html(event_html));
9628        let _ = rt.eval(
9629            "var L=''; \
9630             document.getElementById('btn').addEventListener('foo', function(e){ L+='btn:'+e.type; }); \
9631             document.getElementById('par').addEventListener('foo', function(){ L+='-par'; }); \
9632             document.getElementById('btn').dispatchEvent(new Event('foo', {bubbles:true}));",
9633        );
9634        match rt.eval("L") {
9635            Ok(v) if v.to_js_string() == "btn:foo-par" => passed += 1,
9636            Ok(v) => crate::println!(
9637                "JS_SELFTEST FAIL: dispatchEvent => `{}` (want `btn:foo-par`)",
9638                v.to_js_string()
9639            ),
9640            Err(e) => crate::println!("JS_SELFTEST ERR:  dispatchEvent => {}", e),
9641        }
9642    }
9643
9644    // dispatchEvent: `bubbles: false`(既定)なら祖先へ伝播しない。以前は
9645    // `bubbles` を一切見ず常に無条件で祖先まで伝播してしまう仕様違反バグだった。
9646    {
9647        total += 1;
9648        let mut rt = JsRuntime::new();
9649        rt.dom
9650            .borrow_mut()
9651            .build_from(&crate::os_lib::dom::parse_html(event_html));
9652        let _ = rt.eval(
9653            "var L=''; \
9654             document.getElementById('btn').addEventListener('foo', function(e){ L+='btn:'+e.type; }); \
9655             document.getElementById('par').addEventListener('foo', function(){ L+='-par'; }); \
9656             document.getElementById('btn').dispatchEvent(new Event('foo'));",
9657        );
9658        match rt.eval("L") {
9659            Ok(v) if v.to_js_string() == "btn:foo" => passed += 1,
9660            Ok(v) => crate::println!(
9661                "JS_SELFTEST FAIL: dispatchEvent non-bubbling => `{}` (want `btn:foo`)",
9662                v.to_js_string()
9663            ),
9664            Err(e) => crate::println!("JS_SELFTEST ERR:  dispatchEvent non-bubbling => {}", e),
9665        }
9666    }
9667
9668    // dispatchEvent: CustomEvent の `bubbles` オプションが正しく反映される
9669    // (以前は常に `false` 決め打ちで、`{bubbles:true}` を渡しても無視されていた)。
9670    {
9671        total += 1;
9672        let mut rt = JsRuntime::new();
9673        rt.dom
9674            .borrow_mut()
9675            .build_from(&crate::os_lib::dom::parse_html(event_html));
9676        let _ = rt.eval(
9677            "var L=''; \
9678             document.getElementById('btn').addEventListener('foo', function(e){ L+='btn:'+e.type; }); \
9679             document.getElementById('par').addEventListener('foo', function(){ L+='-par'; }); \
9680             document.getElementById('btn').dispatchEvent(new CustomEvent('foo', {bubbles:true}));",
9681        );
9682        match rt.eval("L") {
9683            Ok(v) if v.to_js_string() == "btn:foo-par" => passed += 1,
9684            Ok(v) => crate::println!(
9685                "JS_SELFTEST FAIL: CustomEvent bubbles => `{}` (want `btn:foo-par`)",
9686                v.to_js_string()
9687            ),
9688            Err(e) => crate::println!("JS_SELFTEST ERR:  CustomEvent bubbles => {}", e),
9689        }
9690    }
9691
9692    // once: 複数回 dispatch しても 1 回だけ発火。
9693    {
9694        total += 1;
9695        let mut rt = JsRuntime::new();
9696        rt.dom
9697            .borrow_mut()
9698            .build_from(&crate::os_lib::dom::parse_html(event_html));
9699        let _ = rt.eval(
9700            "var N=0; document.getElementById('btn').addEventListener('foo', function(){ N++; }, {once:true}); \
9701             document.getElementById('btn').dispatchEvent(new Event('foo')); \
9702             document.getElementById('btn').dispatchEvent(new Event('foo')); \
9703             document.getElementById('btn').dispatchEvent(new Event('foo'));",
9704        );
9705        match rt.eval("N") {
9706            Ok(v) if v.to_js_string() == "1" => passed += 1,
9707            Ok(v) => crate::println!(
9708                "JS_SELFTEST FAIL: once dispatch => `{}` (want `1`)",
9709                v.to_js_string()
9710            ),
9711            Err(e) => crate::println!("JS_SELFTEST ERR:  once dispatch => {}", e),
9712        }
9713    }
9714
9715    // dispatchEvent 戻り値: preventDefault されると false(`cancelable:true` の場合のみ。
9716    // 以前は `cancelable` が一切読まれておらず preventDefault() が常に効いていた
9717    // バグの修正に伴い、このテストが元々検証したかった「妨害可能なイベントで
9718    // preventDefault() が効く」という意図を保つため `new Event('foo', {cancelable:
9719    // true})` に明示する)。
9720    {
9721        total += 1;
9722        let mut rt = JsRuntime::new();
9723        rt.dom
9724            .borrow_mut()
9725            .build_from(&crate::os_lib::dom::parse_html(event_html));
9726        let _ = rt.eval(
9727            "document.getElementById('btn').addEventListener('foo', function(e){ e.preventDefault(); }); \
9728             var R = document.getElementById('btn').dispatchEvent(new Event('foo', {cancelable:true}));",
9729        );
9730        match rt.eval("R") {
9731            Ok(v) if v.to_js_string() == "false" => passed += 1,
9732            Ok(v) => crate::println!(
9733                "JS_SELFTEST FAIL: dispatchEvent return => `{}` (want `false`)",
9734                v.to_js_string()
9735            ),
9736            Err(e) => crate::println!("JS_SELFTEST ERR:  dispatchEvent return => {}", e),
9737        }
9738    }
9739
9740    // keydown / input / focus / blur / change / submit / mouse(座標含む) イベント。
9741    // change イベント: input/select に change リスナを登録し dispatch_event で発火。
9742    let change_html = "<form><input id='cb' type='checkbox'><select id='sel'><option value='a'>A</option><option value='b'>B</option></select></form>";
9743    let mut change_passed = 0usize;
9744    {
9745        total += 1;
9746        let mut rt = JsRuntime::new();
9747        rt.dom
9748            .borrow_mut()
9749            .build_from(&crate::os_lib::dom::parse_html(change_html));
9750        let _ = rt.eval("var c=0; document.getElementById('cb').addEventListener('change', function(){ c++; });");
9751        let cidx = rt.dom.borrow().get_element_by_id("cb");
9752        if let Some(i) = cidx {
9753            rt.dispatch_event(i, "change");
9754            rt.dispatch_event(i, "change");
9755        }
9756        match rt.eval("c") {
9757            Ok(v) if v.to_js_string() == "2" => change_passed += 1,
9758            Ok(v) => crate::println!(
9759                "JS_SELFTEST FAIL: change count => `{}` (want `2`)",
9760                v.to_js_string()
9761            ),
9762            Err(e) => crate::println!("JS_SELFTEST ERR:  change count => {}", e),
9763        }
9764    }
9765    {
9766        total += 1;
9767        // change イベントの target が発火要素であること。
9768        let mut rt = JsRuntime::new();
9769        rt.dom
9770            .borrow_mut()
9771            .build_from(&crate::os_lib::dom::parse_html(change_html));
9772        let _ = rt.eval("var t=''; document.getElementById('sel').addEventListener('change', function(e){ t=e.target.id; });");
9773        let sidx = rt.dom.borrow().get_element_by_id("sel");
9774        if let Some(i) = sidx {
9775            rt.dispatch_event(i, "change");
9776        }
9777        match rt.eval("t") {
9778            Ok(v) if v.to_js_string() == "sel" => change_passed += 1,
9779            Ok(v) => crate::println!(
9780                "JS_SELFTEST FAIL: change target => `{}` (want `sel`)",
9781                v.to_js_string()
9782            ),
9783            Err(e) => crate::println!("JS_SELFTEST ERR:  change target => {}", e),
9784        }
9785    }
9786    passed += change_passed;
9787
9788    {
9789        let kev_html = "<div id='par'><input id='inp'></div>";
9790        // 1. keydown が e.type/e.key 付きで発火。
9791        {
9792            total += 1;
9793            let mut rt = JsRuntime::new();
9794            rt.dom
9795                .borrow_mut()
9796                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9797            let _ = rt.eval("var L=''; document.getElementById('inp').addEventListener('keydown', function(e){ L = e.type + ':' + e.key; });");
9798            let idx = rt.dom.borrow().get_element_by_id("inp");
9799            if let Some(i) = idx {
9800                rt.dispatch_key(i, "keydown", "x");
9801            }
9802            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "keydown:x" {
9803                passed += 1;
9804            } else {
9805                crate::println!("JS_SELFTEST FAIL: keydown e.key");
9806            }
9807        }
9808        // 2. keydown が祖先へバブリング(委譲)。
9809        {
9810            total += 1;
9811            let mut rt = JsRuntime::new();
9812            rt.dom
9813                .borrow_mut()
9814                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9815            let _ = rt.eval("var L=''; document.getElementById('par').addEventListener('keydown', function(e){ L = 'par:' + e.target.id; });");
9816            let idx = rt.dom.borrow().get_element_by_id("inp");
9817            if let Some(i) = idx {
9818                rt.dispatch_key(i, "keydown", "a");
9819            }
9820            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "par:inp" {
9821                passed += 1;
9822            } else {
9823                crate::println!("JS_SELFTEST FAIL: keydown bubbling");
9824            }
9825        }
9826        // 3. keydown の preventDefault が dispatch_key の戻り値 .1 に反映。
9827        {
9828            total += 1;
9829            let mut rt = JsRuntime::new();
9830            rt.dom
9831                .borrow_mut()
9832                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9833            let _ = rt.eval("document.getElementById('inp').addEventListener('keydown', function(e){ e.preventDefault(); });");
9834            let idx = rt.dom.borrow().get_element_by_id("inp");
9835            let prevented = idx
9836                .map(|i| rt.dispatch_key(i, "keydown", "a").1)
9837                .unwrap_or(false);
9838            if prevented {
9839                passed += 1;
9840            } else {
9841                crate::println!("JS_SELFTEST FAIL: keydown preventDefault");
9842            }
9843        }
9844        // 4. input イベントが発火。
9845        {
9846            total += 1;
9847            let mut rt = JsRuntime::new();
9848            rt.dom
9849                .borrow_mut()
9850                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9851            let _ = rt.eval("var N=0; document.getElementById('inp').addEventListener('input', function(){ N++; });");
9852            let idx = rt.dom.borrow().get_element_by_id("inp");
9853            if let Some(i) = idx {
9854                rt.dispatch_event(i, "input");
9855            }
9856            if rt.eval("N").map(|v| v.to_js_string()).unwrap_or_default() == "1" {
9857                passed += 1;
9858            } else {
9859                crate::println!("JS_SELFTEST FAIL: input event");
9860            }
9861        }
9862        // 5. focus イベントが発火(e.type)。
9863        {
9864            total += 1;
9865            let mut rt = JsRuntime::new();
9866            rt.dom
9867                .borrow_mut()
9868                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9869            let _ = rt.eval("var L=''; document.getElementById('inp').addEventListener('focus', function(e){ L = e.type; });");
9870            let idx = rt.dom.borrow().get_element_by_id("inp");
9871            if let Some(i) = idx {
9872                rt.dispatch_event(i, "focus");
9873            }
9874            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "focus" {
9875                passed += 1;
9876            } else {
9877                crate::println!("JS_SELFTEST FAIL: focus event");
9878            }
9879        }
9880        // 6. blur イベントが祖先へバブリング。
9881        {
9882            total += 1;
9883            let mut rt = JsRuntime::new();
9884            rt.dom
9885                .borrow_mut()
9886                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9887            let _ = rt.eval("var L=''; document.getElementById('par').addEventListener('blur', function(e){ L = 'blur:' + e.target.id; });");
9888            let idx = rt.dom.borrow().get_element_by_id("inp");
9889            if let Some(i) = idx {
9890                rt.dispatch_event(i, "blur");
9891            }
9892            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "blur:inp" {
9893                passed += 1;
9894            } else {
9895                crate::println!("JS_SELFTEST FAIL: blur bubbling");
9896            }
9897        }
9898        // 7. change イベントが発火。
9899        {
9900            total += 1;
9901            let mut rt = JsRuntime::new();
9902            rt.dom
9903                .borrow_mut()
9904                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9905            let _ = rt.eval("var L=''; document.getElementById('inp').addEventListener('change', function(e){ L = e.type; });");
9906            let idx = rt.dom.borrow().get_element_by_id("inp");
9907            if let Some(i) = idx {
9908                rt.dispatch_event(i, "change");
9909            }
9910            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "change" {
9911                passed += 1;
9912            } else {
9913                crate::println!("JS_SELFTEST FAIL: change event");
9914            }
9915        }
9916        // 8/9. submit イベントの発火と preventDefault(送信キャンセル)。
9917        let form_html = "<form id='f'><input id='inp2'></form>";
9918        {
9919            total += 1;
9920            let mut rt = JsRuntime::new();
9921            rt.dom
9922                .borrow_mut()
9923                .build_from(&crate::os_lib::dom::parse_html(form_html));
9924            let _ = rt.eval("var S=0; document.getElementById('f').addEventListener('submit', function(){ S++; });");
9925            let idx = rt.dom.borrow().get_element_by_id("f");
9926            if let Some(i) = idx {
9927                rt.dispatch_event_with(i, "submit", &[]);
9928            }
9929            if rt.eval("S").map(|v| v.to_js_string()).unwrap_or_default() == "1" {
9930                passed += 1;
9931            } else {
9932                crate::println!("JS_SELFTEST FAIL: submit event");
9933            }
9934        }
9935        {
9936            total += 1;
9937            let mut rt = JsRuntime::new();
9938            rt.dom
9939                .borrow_mut()
9940                .build_from(&crate::os_lib::dom::parse_html(form_html));
9941            let _ = rt.eval("document.getElementById('f').addEventListener('submit', function(e){ e.preventDefault(); });");
9942            let idx = rt.dom.borrow().get_element_by_id("f");
9943            let prevented = idx
9944                .map(|i| rt.dispatch_event_with(i, "submit", &[]).1)
9945                .unwrap_or(false);
9946            if prevented {
9947                passed += 1;
9948            } else {
9949                crate::println!("JS_SELFTEST FAIL: submit preventDefault");
9950            }
9951        }
9952        // 10. mouseover が発火(e.type)。
9953        {
9954            total += 1;
9955            let mut rt = JsRuntime::new();
9956            rt.dom
9957                .borrow_mut()
9958                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9959            let _ = rt.eval("var L=''; document.getElementById('inp').addEventListener('mouseover', function(e){ L = e.type; });");
9960            let idx = rt.dom.borrow().get_element_by_id("inp");
9961            if let Some(i) = idx {
9962                rt.dispatch_event(i, "mouseover");
9963            }
9964            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "mouseover" {
9965                passed += 1;
9966            } else {
9967                crate::println!("JS_SELFTEST FAIL: mouseover event");
9968            }
9969        }
9970        // 11. mouseout が祖先へバブリング。
9971        {
9972            total += 1;
9973            let mut rt = JsRuntime::new();
9974            rt.dom
9975                .borrow_mut()
9976                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9977            let _ = rt.eval("var L=''; document.getElementById('par').addEventListener('mouseout', function(e){ L = 'out:' + e.target.id; });");
9978            let idx = rt.dom.borrow().get_element_by_id("inp");
9979            if let Some(i) = idx {
9980                rt.dispatch_event(i, "mouseout");
9981            }
9982            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "out:inp" {
9983                passed += 1;
9984            } else {
9985                crate::println!("JS_SELFTEST FAIL: mouseout bubbling");
9986            }
9987        }
9988        // 12. mousemove が発火(回数カウント)。
9989        {
9990            total += 1;
9991            let mut rt = JsRuntime::new();
9992            rt.dom
9993                .borrow_mut()
9994                .build_from(&crate::os_lib::dom::parse_html(kev_html));
9995            let _ = rt.eval("var N=0; document.getElementById('inp').addEventListener('mousemove', function(){ N++; });");
9996            let idx = rt.dom.borrow().get_element_by_id("inp");
9997            if let Some(i) = idx {
9998                rt.dispatch_event(i, "mousemove");
9999                rt.dispatch_event(i, "mousemove");
10000            }
10001            if rt.eval("N").map(|v| v.to_js_string()).unwrap_or_default() == "2" {
10002                passed += 1;
10003            } else {
10004                crate::println!("JS_SELFTEST FAIL: mousemove event");
10005            }
10006        }
10007        // 13. dispatch_mouse が clientX/clientY を付与。
10008        {
10009            total += 1;
10010            let mut rt = JsRuntime::new();
10011            rt.dom
10012                .borrow_mut()
10013                .build_from(&crate::os_lib::dom::parse_html(kev_html));
10014            let _ = rt.eval("var L=''; document.getElementById('inp').addEventListener('click', function(e){ L = e.clientX + ',' + e.clientY; });");
10015            let idx = rt.dom.borrow().get_element_by_id("inp");
10016            if let Some(i) = idx {
10017                rt.dispatch_mouse(i, "click", 42, 17);
10018            }
10019            if rt.eval("L").map(|v| v.to_js_string()).unwrap_or_default() == "42,17" {
10020                passed += 1;
10021            } else {
10022                crate::println!("JS_SELFTEST FAIL: mouse clientX/clientY");
10023            }
10024        }
10025    }
10026
10027    // window/document.location(set_page_url 後に各成分を検証)。
10028    let location_cases: &[(&str, &str)] = &[
10029        ("location.href", "https://example.com/foo/bar?q=1#h"),
10030        ("location.protocol", "https:"),
10031        ("location.host", "example.com"),
10032        ("location.hostname", "example.com"),
10033        ("location.pathname", "/foo/bar"),
10034        ("location.search", "?q=1"),
10035        ("location.hash", "#h"),
10036        ("location.origin", "https://example.com"),
10037        ("window.location.href", "https://example.com/foo/bar?q=1#h"),
10038        ("document.location.pathname", "/foo/bar"),
10039        ("location.toString()", "https://example.com/foo/bar?q=1#h"),
10040        ("window.location === document.location", "true"),
10041        // `location.protocol`/`.host`/`.hostname`/`.port`への代入は仕様上
10042        // 再ナビゲーションを要求すべきだが、`href`/`hash`/`pathname`/`search`
10043        // だけがナビゲーション要求(`_pending_location`)に変換されており、
10044        // この4つは常に黙って見かけ上の`.props`値だけ更新され、実際には
10045        // どこへも「移動しない」バグだった。
10046        // `location_cases`は1つの`rt`を全ケースで使い回す(前のケースの代入が
10047        // 後続へ持ち越される)ため、各ケースの先頭で`protocol`/`host`を
10048        // 明示的に既知の状態へリセットしてから検証し、実行順に依存しない
10049        // ようにする。
10050        (
10051            "location.protocol='http:'; location._pending_location",
10052            "http://example.com/foo/bar?q=1",
10053        ),
10054        (
10055            "location.protocol='https:'; location.host='example.com'; \
10056             location.hostname='other.com'; location._pending_location",
10057            "https://other.com/foo/bar?q=1",
10058        ),
10059        (
10060            "location.host='example.com'; location.port='8080'; location._pending_location",
10061            "https://example.com:8080/foo/bar?q=1",
10062        ),
10063        (
10064            "location.host='new.com:9090'; location._pending_location",
10065            "https://new.com:9090/foo/bar?q=1",
10066        ),
10067        (
10068            "location.host='new.com:9090'; location.hostname + ',' + location.port",
10069            "new.com,9090",
10070        ),
10071        // `<a>.href`/`<img>.src`/`<form>.action` は仕様上、生の属性文字列ではなく
10072        // ページURL基準で解決した絶対URLを返す必要があるが、以前はこの解決が
10073        // 一切行われず相対文字列がそのまま返るバグだった。
10074        (
10075            "var a=document.createElement('a'); a.setAttribute('href','/p3'); a.href",
10076            "https://example.com/p3",
10077        ),
10078        (
10079            "var i=document.createElement('img'); i.setAttribute('src','x.png'); i.src",
10080            "https://example.com/foo/x.png",
10081        ),
10082        (
10083            "var a=document.createElement('a'); a.setAttribute('href','https://o.com/z'); a.href",
10084            "https://o.com/z",
10085        ),
10086        // `document.URL`/`document.documentURI`/`node.baseURI`(DOM 標準。丸ごと
10087        // 未対応だった。`<base>` タグ非対応のため常に `location.href` と同値)。
10088        ("document.URL", "https://example.com/foo/bar?q=1#h"),
10089        ("document.documentURI", "https://example.com/foo/bar?q=1#h"),
10090        (
10091            "document.createElement('div').baseURI",
10092            "https://example.com/foo/bar?q=1#h",
10093        ),
10094        // `document.readyState`(HTML5。丸ごと未対応だった。この処理系は常に
10095        // "complete" を返す簡略実装)。
10096        ("document.readyState", "complete"),
10097        // `document.cookie`(丸ごと未対応だった。`expires`/`path` 等の属性は
10098        // 無視する簡略実装で `name=value` のみ反映する)。
10099        (
10100            "document.cookie='ck_a=1'; document.cookie='ck_b=2; path=/'; document.cookie",
10101            "ck_a=1; ck_b=2",
10102        ),
10103        // `cookieStore`(Cookie Store API。丸ごと未対応だった。`document.cookie`
10104        // と同じ実データを共有する非同期版。他のテストと Cookie 名が衝突しない
10105        // よう `cs_` 接頭辞を使う)。
10106        (
10107            "await cookieStore.set('cs_x', '1'); (await cookieStore.get('cs_x')).value",
10108            "1",
10109        ),
10110        ("(await cookieStore.get('cs_missing'))", "null"),
10111        (
10112            "await cookieStore.set('cs_y', '2'); \
10113             (await cookieStore.getAll('cs_y')).map(c => c.name+'='+c.value).join(',')",
10114            "cs_y=2",
10115        ),
10116        (
10117            "await cookieStore.set('cs_z', '3'); await cookieStore.delete('cs_z'); \
10118             (await cookieStore.get('cs_z'))",
10119            "null",
10120        ),
10121        (
10122            "await cookieStore.set({name:'cs_w', value:'4'}); (await cookieStore.get('cs_w')).value",
10123            "4",
10124        ),
10125        // Page Visibility API(`document.hidden`/`.visibilityState`)が丸ごと
10126        // 未対応だった。タブ/バックグラウンド化の概念が無いため常に「表示中」。
10127        ("document.hidden", "false"),
10128        ("document.visibilityState", "visible"),
10129    ];
10130    total += location_cases.len();
10131    {
10132        let mut rt = JsRuntime::new();
10133        rt.set_page_url("https://example.com/foo/bar?q=1#h");
10134        for (src, expect) in location_cases {
10135            match rt.eval(src) {
10136                Ok(v) if &v.to_js_string() == expect => passed += 1,
10137                Ok(v) => crate::println!(
10138                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
10139                    src,
10140                    v.to_js_string(),
10141                    expect
10142                ),
10143                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
10144            }
10145        }
10146    }
10147
10148    // history.pushState / replaceState(location 書換え + state/length)。
10149    let history_cases: &[(&str, &str)] = &[
10150        (
10151            "history.pushState({a:1},'','/p2'); location.pathname",
10152            "/p2",
10153        ),
10154        (
10155            "history.pushState({},'','/p2'); location.href",
10156            "https://example.com/p2",
10157        ),
10158        ("history.pushState({a:9},'','/p2'); history.state.a", "9"),
10159        (
10160            "var n=history.length; history.pushState({},'','/x'); history.length - n",
10161            "1",
10162        ),
10163        (
10164            "var n=history.length; history.replaceState({},'','/y'); history.length - n",
10165            "0",
10166        ),
10167        (
10168            "history.replaceState({},'','about.html'); location.pathname",
10169            "/foo/about.html",
10170        ),
10171        ("typeof history.pushState", "function"),
10172        ("typeof history.back", "function"),
10173    ];
10174    total += history_cases.len();
10175    for (src, expect) in history_cases {
10176        let mut rt = JsRuntime::new();
10177        rt.set_page_url("https://example.com/foo/bar");
10178        match rt.eval(src) {
10179            Ok(v) if &v.to_js_string() == expect => passed += 1,
10180            Ok(v) => crate::println!(
10181                "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
10182                src,
10183                v.to_js_string(),
10184                expect
10185            ),
10186            Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
10187        }
10188    }
10189
10190    // 相対URL解決(resolve_url 純関数。fetch/XHR の base 基準解決)。
10191    let resolve_cases: &[(&str, &str, &str)] = &[
10192        ("http://h.com/a/b.html", "/api", "http://h.com/api"),
10193        ("http://h.com/a/b.html", "x.json", "http://h.com/a/x.json"),
10194        ("http://h.com/a/", "x.json", "http://h.com/a/x.json"),
10195        (
10196            "http://h.com/a/b.html",
10197            "https://o.com/z",
10198            "https://o.com/z",
10199        ),
10200        ("http://h.com/a/b.html", "data:,hi", "data:,hi"),
10201        ("https://h.com/", "/p/q", "https://h.com/p/q"),
10202        ("http://h.com", "y", "http://h.com/y"),
10203        ("", "/api", "/api"),
10204        // `.`/`..` セグメント解決(以前は文字列結合のみで未解決のまま残っていた)。
10205        ("https://h.com/a/b/c", "../style.css", "https://h.com/a/style.css"),
10206        ("https://h.com/a/b/c", "./style.css", "https://h.com/a/b/style.css"),
10207        ("https://h.com/a/b/c/d", "../../x", "https://h.com/a/x"),
10208    ];
10209    total += resolve_cases.len();
10210    for (base, url, expect) in resolve_cases {
10211        let got = builtins::resolve_url(base, url);
10212        if &got == expect {
10213            passed += 1;
10214        } else {
10215            crate::println!(
10216                "JS_SELFTEST FAIL: resolve_url({:?},{:?}) => `{}` (want `{}`)",
10217                base,
10218                url,
10219                got,
10220                expect
10221            );
10222        }
10223    }
10224
10225    // HTML5 制約バリデーション(checkValidity / setCustomValidity / validity)。
10226    let validity_cases: &[(&str, &str, &str)] = &[
10227        // (html, script, expect)
10228        ("<input id='a' required value=''>", "document.getElementById('a').checkValidity()", "false"),
10229        ("<input id='a' required value='x'>", "document.getElementById('a').checkValidity()", "true"),
10230        ("<input id='a' value='hi'>", "document.getElementById('a').checkValidity()", "true"),
10231        ("<input id='a' type='email' value='nope'>", "document.getElementById('a').checkValidity()", "false"),
10232        ("<input id='a' type='email' value='x@y.com'>", "document.getElementById('a').checkValidity()", "true"),
10233        ("<input id='a' type='number' min='5' value='3'>", "document.getElementById('a').checkValidity()", "false"),
10234        ("<input id='a' type='number' min='5' value='9'>", "document.getElementById('a').checkValidity()", "true"),
10235        ("<input id='a' minlength='3' value='ab'>", "document.getElementById('a').checkValidity()", "false"),
10236        ("<input id='a' maxlength='2' value='abc'>", "document.getElementById('a').checkValidity()", "false"),
10237        ("<input id='a' value='ok'>", "var e=document.getElementById('a'); e.setCustomValidity('bad'); e.checkValidity()", "false"),
10238        ("<input id='a' value='ok'>", "var e=document.getElementById('a'); e.setCustomValidity('bad'); e.setCustomValidity(''); e.checkValidity()", "true"),
10239        ("<input id='a' required value=''>", "document.getElementById('a').validity.valueMissing", "true"),
10240        ("<input id='a' required value='x'>", "document.getElementById('a').validity.valid", "true"),
10241        ("<input id='a' value='hi'>", "document.getElementById('a').willValidate", "true"),
10242        ("<input id='a' type='submit'>", "document.getElementById('a').willValidate", "false"),
10243        // `pattern` 属性が以前はメタ文字を含む複雑なパターンを「誤検知回避のため」
10244        // 無条件で通過させる簡易近似だった。本物の RegExp エンジンで `^(?:pattern)$`
10245        // として正しく検証するよう修正(`\d+`/`[0-9]{3}` 等、通常の JS RegExp 構文)。
10246        ("<input id='a' pattern='[0-9]{3}' value='123'>", "document.getElementById('a').checkValidity()", "true"),
10247        ("<input id='a' pattern='[0-9]{3}' value='ab'>", "document.getElementById('a').checkValidity()", "false"),
10248        ("<input id='a' pattern='\\d+' value='123'>", "document.getElementById('a').checkValidity()", "true"),
10249        ("<input id='a' pattern='\\d+' value='abc'>", "document.getElementById('a').checkValidity()", "false"),
10250        // 空値は非 required なら pattern チェック自体を適用しない(仕様どおり)。
10251        ("<input id='a' pattern='[0-9]{3}' value=''>", "document.getElementById('a').checkValidity()", "true"),
10252        // `stepMismatch`(HTML5 制約検証。min/max はあったが step が丸ごと未対応だった)。
10253        ("<input id='a' type='number' step='2' value='4'>", "document.getElementById('a').checkValidity()", "true"),
10254        ("<input id='a' type='number' step='2' value='3'>", "document.getElementById('a').checkValidity()", "false"),
10255        // step の基準点は min(無ければ 0)。min=1, step=2 なら 1,3,5,... が有効。
10256        ("<input id='a' type='number' min='1' step='2' value='5'>", "document.getElementById('a').checkValidity()", "true"),
10257        ("<input id='a' type='number' min='1' step='2' value='4'>", "document.getElementById('a').checkValidity()", "false"),
10258        // `step=\"any\"` は制約なし。
10259        ("<input id='a' type='number' step='any' value='3.14159'>", "document.getElementById('a').checkValidity()", "true"),
10260        // `validity` オブジェクトが `valid`/`valueMissing`/`customError` の3つしか
10261        // 公開しておらず、`patternMismatch`/`typeMismatch`/`rangeOverflow`/
10262        // `rangeUnderflow`/`stepMismatch`/`tooLong`/`tooShort`/`badInput` という
10263        // 特定の制約を狙い撃ちする定番パターンが常に `undefined` になっていた。
10264        ("<input id='a' pattern='[0-9]{3}' value='ab'>", "document.getElementById('a').validity.patternMismatch", "true"),
10265        ("<input id='a' pattern='[0-9]{3}' value='123'>", "document.getElementById('a').validity.patternMismatch", "false"),
10266        ("<input id='a' type='email' value='nope'>", "document.getElementById('a').validity.typeMismatch", "true"),
10267        ("<input id='a' type='number' min='5' value='3'>", "document.getElementById('a').validity.rangeUnderflow", "true"),
10268        ("<input id='a' type='number' max='5' value='9'>", "document.getElementById('a').validity.rangeOverflow", "true"),
10269        ("<input id='a' type='number' step='2' value='3'>", "document.getElementById('a').validity.stepMismatch", "true"),
10270        ("<input id='a' maxlength='2' value='abc'>", "document.getElementById('a').validity.tooLong", "true"),
10271        ("<input id='a' minlength='3' value='ab'>", "document.getElementById('a').validity.tooShort", "true"),
10272        ("<input id='a' type='number' value='abc'>", "document.getElementById('a').validity.badInput", "true"),
10273        // **重要**: `<form>.checkValidity()` が子孫コントロールを一切見ず、`<form>`
10274        // タグ自身に対する検証(常に無条件で true)を返すだけだったバグ。
10275        (
10276            "<form id='f3'><input required value=''></form>",
10277            "document.getElementById('f3').checkValidity()",
10278            "false",
10279        ),
10280        (
10281            "<form id='f3'><input required value='x'></form>",
10282            "document.getElementById('f3').checkValidity()",
10283            "true",
10284        ),
10285        (
10286            "<form id='f3'><input required value='x'><input type='email' value='nope'></form>",
10287            "document.getElementById('f3').checkValidity()",
10288            "false",
10289        ),
10290        // `checkValidity()`/`.validationMessage`/`.validity`が`get_attr(idx,"value")`
10291        // だけを見ており、`value`属性を持たない`<select>`(選択状態は子の
10292        // `<option selected>`が持つ)や、初期値がテキストノードで表現される
10293        // `<textarea>`では、実際には値があっても常に空文字列=未入力として
10294        // 誤って`required`違反になっていたバグ。`.value`ゲッターは既に正しい
10295        // フォールバックを持っていたが、制約バリデーション側に伝わっていなかった。
10296        (
10297            "<select id='a' required><option value='x' selected>X</option></select>",
10298            "document.getElementById('a').checkValidity()",
10299            "true",
10300        ),
10301        (
10302            "<select id='a' required><option value='x' selected>X</option></select>",
10303            "document.getElementById('a').validity.valueMissing",
10304            "false",
10305        ),
10306        (
10307            "<textarea id='a' required>hello</textarea>",
10308            "document.getElementById('a').checkValidity()",
10309            "true",
10310        ),
10311        (
10312            "<textarea id='a' required></textarea>",
10313            "document.getElementById('a').checkValidity()",
10314            "false",
10315        ),
10316        (
10317            "<form id='f4'><select required><option value='x' selected>X</option></select></form>",
10318            "document.getElementById('f4').checkValidity()",
10319            "true",
10320        ),
10321        // `required`のcheckbox/radioは「値」でなく「チェック状態」で判定すべき
10322        // (HTML5仕様)だが、以前は他のinputと同じ「value属性が空か」で判定して
10323        // いたため、value属性を持たないcheckedなチェックボックスが常にrequired
10324        // 違反(valueが空文字列扱い)、逆にvalue属性だけ設定した未チェックの
10325        // チェックボックスが常に妥当、というチェック状態と無関係な結果になっていた。
10326        (
10327            "<input id='a' type='checkbox' required>",
10328            "document.getElementById('a').checkValidity()",
10329            "false",
10330        ),
10331        (
10332            "<input id='a' type='checkbox' required checked>",
10333            "document.getElementById('a').checkValidity()",
10334            "true",
10335        ),
10336        (
10337            "<input id='a' type='checkbox' value='yes' required>",
10338            "document.getElementById('a').validity.valueMissing",
10339            "true",
10340        ),
10341        (
10342            "<input id='a' type='checkbox' required checked>",
10343            "document.getElementById('a').validity.valueMissing",
10344            "false",
10345        ),
10346        // radioはグループ内のいずれかがcheckedであれば充足(自身がcheckedで
10347        // なくても良い)。
10348        (
10349            "<input id='a' type='radio' name='g' required><input type='radio' name='g' checked>",
10350            "document.getElementById('a').checkValidity()",
10351            "true",
10352        ),
10353        (
10354            "<input id='a' type='radio' name='g' required><input type='radio' name='g'>",
10355            "document.getElementById('a').checkValidity()",
10356            "false",
10357        ),
10358        // `type="range"`は`type="number"`と同じmin/max/step制約検証規則を持つ
10359        // (`.stepUp()`/`.valueAsNumber`は既に両者を同列に扱っていたが、
10360        // `validity.rangeOverflow`/`.rangeUnderflow`/`.stepMismatch`だけ
10361        // `number`型限定になっており、range型では常にfalse固定だった)。
10362        (
10363            "<input id='a' type='range' min='0' max='10' value='20'>",
10364            "document.getElementById('a').validity.rangeOverflow",
10365            "true",
10366        ),
10367        (
10368            "<input id='a' type='range' min='0' max='10' value='-5'>",
10369            "document.getElementById('a').validity.rangeUnderflow",
10370            "true",
10371        ),
10372        (
10373            "<input id='a' type='range' min='0' max='10' value='5'>",
10374            "document.getElementById('a').checkValidity()",
10375            "true",
10376        ),
10377        (
10378            "<input id='a' type='range' min='0' max='10' step='2' value='3'>",
10379            "document.getElementById('a').validity.stepMismatch",
10380            "true",
10381        ),
10382        // `type="date"`/`type="month"`の形式チェックが丸ごと未対応だった
10383        // (`input.valueAsDate`は既存の`input_value_as_date_ms()`で妥当性を
10384        // 判定していたが、`validity.typeMismatch`には配線されておらず、
10385        // 不正な日付文字列を代入しても常に`typeMismatch: false`だった)。
10386        (
10387            "<input id='a' type='date' value='2026-07-17'>",
10388            "document.getElementById('a').validity.typeMismatch",
10389            "false",
10390        ),
10391        (
10392            "<input id='a' type='date' value='not-a-date'>",
10393            "document.getElementById('a').validity.typeMismatch",
10394            "true",
10395        ),
10396        (
10397            "<input id='a' type='month' value='2026-07'>",
10398            "document.getElementById('a').checkValidity()",
10399            "true",
10400        ),
10401        (
10402            "<input id='a' type='date' value='2026-13-99'>",
10403            "document.getElementById('a').checkValidity()",
10404            "false",
10405        ),
10406        // `<input type="email" multiple>`はカンマ区切りで複数アドレスを入力できる
10407        // 仕様だが、以前は`is_valid_email(value)`を値全体にそのまま適用しており、
10408        // 2件目以降のアドレスやカンマを含む正当な値が常に不正判定されていた。
10409        (
10410            "<input id='a' type='email' multiple value='a@b.com,c@d.com'>",
10411            "document.getElementById('a').checkValidity()",
10412            "true",
10413        ),
10414        (
10415            "<input id='a' type='email' multiple value='a@b.com, c@d.com'>",
10416            "document.getElementById('a').checkValidity()",
10417            "true",
10418        ),
10419        (
10420            "<input id='a' type='email' multiple value='a@b.com,nope'>",
10421            "document.getElementById('a').checkValidity()",
10422            "false",
10423        ),
10424        (
10425            // `multiple`が無ければ従来どおりカンマ区切りは単一アドレスとして不正。
10426            "<input id='a' type='email' value='a@b.com,c@d.com'>",
10427            "document.getElementById('a').checkValidity()",
10428            "false",
10429        ),
10430        // `type="date"`/`type="month"`の`min`/`max`範囲制約(`type="number"`と同様に
10431        // 対応可能な仕様なのに丸ごと欠けていた)。
10432        (
10433            "<input id='a' type='date' min='2026-01-01' max='2026-12-31' value='2025-12-31'>",
10434            "document.getElementById('a').validity.rangeUnderflow",
10435            "true",
10436        ),
10437        (
10438            "<input id='a' type='date' min='2026-01-01' max='2026-12-31' value='2027-01-01'>",
10439            "document.getElementById('a').validity.rangeOverflow",
10440            "true",
10441        ),
10442        (
10443            "<input id='a' type='date' min='2026-01-01' max='2026-12-31' value='2026-06-15'>",
10444            "document.getElementById('a').checkValidity()",
10445            "true",
10446        ),
10447        (
10448            "<input id='a' type='month' min='2026-03' max='2026-09' value='2026-01'>",
10449            "document.getElementById('a').checkValidity()",
10450            "false",
10451        ),
10452        // `<fieldset disabled>`の子孫コントロールは自身に`disabled`属性が無くても
10453        // 暗黙に無効化される(HTML5仕様)。以前は要素自身の`disabled`属性しか
10454        // 見ておらず、`.disabled`が常に`false`・制約バリデーションも無効化
10455        // されずに常に対象内のままだった。
10456        (
10457            "<fieldset disabled><input id='a' required></fieldset>",
10458            "document.getElementById('a').disabled",
10459            "true",
10460        ),
10461        (
10462            "<fieldset disabled><input id='a' required></fieldset>",
10463            "document.getElementById('a').checkValidity()",
10464            "true",
10465        ),
10466        (
10467            "<fieldset><input id='a' required></fieldset>",
10468            "document.getElementById('a').disabled",
10469            "false",
10470        ),
10471        (
10472            // 無効化されている以上、値が実際には空でも valueMissing にはならない
10473            // (disabled要素は制約バリデーションの対象外という仕様どおり)。
10474            "<fieldset disabled><input id='a' required value=''></fieldset>",
10475            "document.getElementById('a').validity.valueMissing",
10476            "false",
10477        ),
10478        // `form="formId"`属性(HTML5仕様の「form owner」)による、`<form>`の外に
10479        // 置かれたコントロールとの明示的な関連付けが丸ごと未対応だった。
10480        // `element.form`は祖先探索(`closest_tag`)しか見ておらず、
10481        // `<form>.checkValidity()`もフォームの子孫しか走査していなかったため、
10482        // フォーム外配置のコントロールが両方から漏れていた。
10483        (
10484            "<form id='f5'></form><input id='a' form='f5'>",
10485            "document.getElementById('a').form === document.getElementById('f5')",
10486            "true",
10487        ),
10488        (
10489            "<form id='f5'></form><input id='a' form='f5' required>",
10490            "document.getElementById('f5').checkValidity()",
10491            "false",
10492        ),
10493        (
10494            "<form id='f5'></form><input id='a' form='f5' required value='x'>",
10495            "document.getElementById('f5').checkValidity()",
10496            "true",
10497        ),
10498        (
10499            // `form`属性が無い通常の input はこれまでどおり祖先探索のみ。
10500            "<div><input id='a'></div>",
10501            "document.getElementById('a').form",
10502            "null",
10503        ),
10504        // `type="datetime-local"`の`validity.typeMismatch`/`min`/`max`が丸ごと
10505        // 未対応だった。`input_value_as_date_ms()`は既存の`parse_iso_date`が
10506        // `YYYY-MM-DDTHH:MM`形式を既にサポートしているため、`date`/`month`と
10507        // 同じ扱いに含めるだけで対応できた。
10508        (
10509            "<input id='a' type='datetime-local' value='2026-07-17T14:30'>",
10510            "document.getElementById('a').validity.typeMismatch",
10511            "false",
10512        ),
10513        (
10514            "<input id='a' type='datetime-local' value='not-a-datetime'>",
10515            "document.getElementById('a').checkValidity()",
10516            "false",
10517        ),
10518        (
10519            "<input id='a' type='datetime-local' min='2026-01-01T00:00' max='2026-12-31T23:59' value='2025-06-01T00:00'>",
10520            "document.getElementById('a').validity.rangeUnderflow",
10521            "true",
10522        ),
10523        // `<details name="...">`排他グループの初期状態正規化(HTML5。
10524        // 丸ごと未対応だった)。同じ`name`を持つ複数の`<details open>`が
10525        // HTML記述時点で並んでいた場合、文書順で最後の1つだけが開いた
10526        // ままで残り、それより前のものは`build_from`(パース直後)の
10527        // 時点で自動的に閉じられる必要がある。2026-07-18 発見・実装)。
10528        (
10529            "<details id='a' name='g' open></details><details id='b' name='g' open></details>",
10530            "document.getElementById('a').open+','+document.getElementById('b').open",
10531            "false,true",
10532        ),
10533        (
10534            "<details id='a' name='g' open></details><details id='b' open></details>",
10535            "document.getElementById('a').open+','+document.getElementById('b').open",
10536            "true,true",
10537        ),
10538        // `HTMLCollection` / `HTMLFormControlsCollection` の `item(index)` / `namedItem(name)`
10539        // および `classList.value` ゲッター/セッター。
10540        (
10541            "<div id='wrap'><p id='t' name='foo'></p><span id='s'></span></div>",
10542            "document.getElementById('wrap').children.item(0).id + ',' + document.getElementById('wrap').children.namedItem('s').tagName + ',' + document.getElementById('wrap').children.namedItem('foo').id",
10543            "t,SPAN,t",
10544        ),
10545        (
10546            "<div id='wrap'><span></span></div>",
10547            "String(document.getElementById('wrap').children.item(99))",
10548            "null",
10549        ),
10550        (
10551            "<form id='f'><input id='i' name='user'></form>",
10552            "document.getElementById('f').elements.item(0).id + ',' + document.getElementById('f').elements.namedItem('user').id",
10553            "i,i",
10554        ),
10555        (
10556            "<form id='f'><input type='radio' name='r' value='v1'><input type='radio' name='r' value='v2' checked></form>",
10557            "var f=document.getElementById('f'); var list=f.elements.namedItem('r'); var v1=list.value; var len=list.length; list.value='v1'; var v2=list.value; var c0=list[0].checked; v1+','+len+','+v2+','+c0",
10558            "v2,2,v1,true",
10559        ),
10560        (
10561            "<div id='d' class='a b'></div>",
10562            "var d=document.getElementById('d'); var v=d.classList.value; d.classList.value='c d'; v+','+d.className",
10563            "a b,c d",
10564        ),
10565    ];
10566    total += validity_cases.len();
10567    for (html, src, expect) in validity_cases {
10568        let mut rt = JsRuntime::new();
10569        rt.dom
10570            .borrow_mut()
10571            .build_from(&crate::os_lib::dom::parse_html(html));
10572        match rt.eval(src) {
10573            Ok(v) if &v.to_js_string() == expect => passed += 1,
10574            Ok(v) => crate::println!(
10575                "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
10576                src,
10577                v.to_js_string(),
10578                expect
10579            ),
10580            Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
10581        }
10582    }
10583
10584    // ES modules(import/export): 複数モジュールを define_module で登録し、
10585    // import 側スクリプトを評価して名前解決・default・namespace・再エクスポートを検証。
10586    {
10587        // 単純な named export を持つ算術モジュール。
10588        let math_src = "export const PI = 3; export function sq(x){ return x*x; } \
10589                        const e = 2; export { e as E };";
10590        // default export と named export の混在。
10591        let greet_src = "export default function(n){ return 'hi ' + n; } \
10592                          export const lang = 'ja';";
10593        // 別モジュールからの再エクスポート(集約モジュール)。
10594        let index_src = "export { PI, sq } from 'math'; export * from 'math';";
10595        // 循環 import: a は b を import し、b は a を import する。
10596        let cyc_a = "import { bVal } from 'cycb'; export const aVal = 10; \
10597                      export function getB(){ return bVal; }";
10598        let cyc_b = "import { aVal } from 'cyca'; export const bVal = 20;";
10599
10600        let module_checks: &[(&str, &str)] = &[
10601            // named import + 関数呼び出し。
10602            ("import { PI, sq } from 'math'; sq(PI)", "9"),
10603            // as による別名 export を import。
10604            ("import { E } from 'math'; E", "2"),
10605            // default import。
10606            ("import greet from 'greet'; greet('bob')", "hi bob"),
10607            // default + named を同時 import。
10608            ("import greet, { lang } from 'greet'; greet(lang)", "hi ja"),
10609            // namespace import。
10610            ("import * as M from 'math'; M.sq(4) + M.PI", "19"),
10611            // 再エクスポート(named)。
10612            ("import { PI } from 'index'; PI", "3"),
10613            // 再エクスポート(export *)経由の関数。
10614            ("import { sq } from 'index'; sq(5)", "25"),
10615            // 循環 import: a 経由で b の値を取得。
10616            ("import { getB } from 'cyca'; getB()", "20"),
10617            // 副作用のみ import(エラーにならず undefined 評価が通ること)。
10618            ("import 'math'; 42", "42"),
10619        ];
10620        total += module_checks.len();
10621        for (src, expect) in module_checks {
10622            let mut rt = JsRuntime::new();
10623            rt.define_module("math", math_src);
10624            rt.define_module("greet", greet_src);
10625            rt.define_module("index", index_src);
10626            rt.define_module("cyca", cyc_a);
10627            rt.define_module("cycb", cyc_b);
10628            match rt.eval(src) {
10629                Ok(v) if &v.to_js_string() == expect => passed += 1,
10630                Ok(v) => crate::println!(
10631                    "JS_SELFTEST FAIL: `{}` => `{}` (want `{}`)",
10632                    src,
10633                    v.to_js_string(),
10634                    expect
10635                ),
10636                Err(e) => crate::println!("JS_SELFTEST ERR:  `{}` => {}", src, e),
10637            }
10638        }
10639    }
10640
10641    // `window.scrollTo(x, y)`/`window.scrollBy(x, y)`(丸ごと未対応だった。
10642    // 以前は`scrollTo`が孤立した`SCROLL_Y` staticへ書くだけ、`scrollBy`は
10643    // `dom_noop`のままで、どちらも`window.scrollY`や実際の描画位置に一切
10644    // 反映されない死んだ経路だった。2026-07-16 発見・実装)。実際の視覚的
10645    // 反映は`web_engine/render.rs`の`draw()`側で行われヘッドレスJS自己テスト
10646    // からは駆動できないため、ここではJS→`DomBridge`への配線(`window_
10647    // scroll_to`/`window_scroll_by`が正しい保留値を書き込むこと)のみを
10648    // 直接Rustレベルで検証する。
10649    {
10650        total += 1;
10651        let mut rt = JsRuntime::new();
10652        let _ = rt.eval("window.scrollTo(0, 120);");
10653        let y = rt.dom.borrow().pending_scroll_abs_y;
10654        if y == Some(120) {
10655            passed += 1;
10656        } else {
10657            crate::println!(
10658                "JS_SELFTEST FAIL: window.scrollTo pending_scroll_abs_y => `{:?}` (want `Some(120)`)",
10659                y
10660            );
10661        }
10662    }
10663    {
10664        total += 1;
10665        let mut rt = JsRuntime::new();
10666        let _ = rt.eval("window.scrollBy(0, 30); window.scrollBy(0, 15);");
10667        let dy = rt.dom.borrow().pending_scroll_by_y;
10668        if dy == Some(45) {
10669            passed += 1;
10670        } else {
10671            crate::println!(
10672                "JS_SELFTEST FAIL: window.scrollBy pending_scroll_by_y (累積) => `{:?}` (want `Some(45)`)",
10673                dy
10674            );
10675        }
10676    }
10677    // `element.scrollIntoView()`が丸ごとno-opだった(実サイト www.sugi-lab.net の
10678    // ナビゲーションリンクの`target.scrollIntoView({behavior:'smooth',
10679    // block:'start'})`が完全に無効化されていた。2026-07-22発見・修正)。
10680    // `window.scrollTo`と同じ`pending_scroll_abs_y`機構を再利用しているかを
10681    // 直接Rustレベルで検証する(対象要素のY座標250px、scrollY=0の状態から
10682    // `scrollIntoView()`を呼ぶと、絶対Y座標250へスクロールする保留値が
10683    // 設定されるはず)。
10684    {
10685        total += 1;
10686        let mut rt = JsRuntime::new();
10687        rt.dom
10688            .borrow_mut()
10689            .build_from(&crate::os_lib::dom::parse_html("<div id='t'></div>"));
10690        let idx = rt.dom.borrow().get_element_by_id("t");
10691        if let Some(i) = idx {
10692            rt.dom.borrow_mut().set_rect(i, 0, 250, 100, 50);
10693        }
10694        let _ = rt.eval("document.getElementById('t').scrollIntoView();");
10695        let y = rt.dom.borrow().pending_scroll_abs_y;
10696        if y == Some(250) {
10697            passed += 1;
10698        } else {
10699            crate::println!(
10700                "JS_SELFTEST FAIL: element.scrollIntoView pending_scroll_abs_y => `{:?}` (want `Some(250)`)",
10701                y
10702            );
10703        }
10704    }
10705
10706    let dom_parser_cases: &[(&str, &str)] = &[
10707        (
10708            "var p = new DOMParser(); var doc = p.parseFromString('<div id=\"dp\">hello</div>', 'text/html'); doc.getElementById('dp').textContent",
10709            "hello",
10710        ),
10711    ];
10712    total += dom_parser_cases.len();
10713    for (code, expect) in dom_parser_cases {
10714        let mut rt = JsRuntime::new();
10715        let res = rt.eval(code);
10716        let got = res.map(|v| v.to_js_string()).unwrap_or_else(|e| alloc::format!("ERR: {:?}", e));
10717        if &got == expect {
10718            passed += 1;
10719        } else {
10720            crate::println!(
10721                "JS_SELFTEST FAIL: DOMParser code {:?} => `{}` (want `{}`)",
10722                code,
10723                got,
10724                expect
10725            );
10726        }
10727    }
10728
10729    // DOMMatrix / DOMPoint / URLPattern / Iterator.concat / Iterator.from 自己テスト
10730    let geom_web_cases = [
10731        ("const m = new DOMMatrix([1,0,0,1,10,20]); m.a === 1 && m.e === 10", "true"),
10732        ("const m = new DOMMatrix(); m.translateSelf(10, 20); m.e === 10 && m.f === 20", "true"),
10733        ("const m = new DOMMatrix(); m.scaleSelf(2, 3); m.a === 2 && m.d === 3", "true"),
10734        ("const m = new DOMMatrix(); const p = new DOMPoint(5, 10); const p2 = m.translate(10, 20).transformPoint(p); p2.x === 15 && p2.y === 30", "true"),
10735        ("const m2 = DOMMatrix.fromMatrix({a: 2, d: 3, e: 5, f: 10}); m2.a === 2 && m2.e === 5", "true"),
10736        ("const p3 = new DOMPoint(1, 2).matrixTransform(new DOMMatrix([2,0,0,3,10,20])); p3.x === 12 && p3.y === 26", "true"),
10737        ("const pat = new URLPattern({ pathname: '/api/*' }); pat.test('/api/users')", "true"),
10738        ("const res = new URLPattern({ pathname: '/users/:id' }).exec('https://example.com/users/42'); res.pathname.groups.id", "42"),
10739        ("const it = Iterator.concat([1, 2], [3, 4]); it.toArray().join(',')", "1,2,3,4"),
10740        ("const it = Iterator.from([10, 20, 30]); it.take(2).toArray().join(',')", "10,20"),
10741        ("new Intl.DurationFormat('en', {style:'long'}).format({hours:1, minutes:30})", "1 hour, 30 minutes"),
10742        ("new Intl.DurationFormat('en').resolvedOptions().locale", "en-US"),
10743        ("Iterator.range(1, 5).toArray().join(',')", "1,2,3,4"),
10744        ("Iterator.range(0, 10, 2).toArray().join(',')", "0,2,4,6,8"),
10745        ("Iterator.range(5, 1, -1).toArray().join(',')", "5,4,3,2"),
10746    ];
10747    total += geom_web_cases.len();
10748    for (code, expect) in geom_web_cases {
10749        let mut rt = JsRuntime::new();
10750        let res = rt.eval(code);
10751        let got = res.map(|v| v.to_js_string()).unwrap_or_else(|e| alloc::format!("ERR: {:?}", e));
10752        if &got == expect {
10753            passed += 1;
10754        } else {
10755            crate::println!(
10756                "JS_SELFTEST FAIL: Geom/Web API code {:?} => `{}` (want `{}`)",
10757                code,
10758                got,
10759                expect
10760            );
10761        }
10762    }
10763
10764    let template_html = r#"<div><template id="tmpl"><span>Hello</span></template></div>"#;
10765    let template_cases = [
10766        ("document.createElement('template') instanceof HTMLTemplateElement", "true"),
10767        ("const t = document.createElement('template'); t.content.nodeType === 11", "true"),
10768        ("const t = document.getElementById('tmpl'); t.content.childNodes.length", "1"),
10769        ("const t = document.getElementById('tmpl'); t.content.children[0].tagName", "SPAN"),
10770        ("const t = document.getElementById('tmpl'); const f = t.content.cloneNode(true); f.nodeType === 11 && f.children[0].textContent === 'Hello'", "true"),
10771        ("const s = document.createElement('select'); const o = document.createElement('option'); o.value='v1'; o.text='t1'; s.add(o); s.options.length === 1 && s.value === 'v1'", "true"),
10772        ("const u = new URL('https://www.sugi-lab.net/?a=1'); u.searchParams.set('b', '2'); u.search === '?a=1&b=2'", "true"),
10773        ("const div = document.createElement('div'); div.setAttribute('id', 'd1'); const c = div.cloneNode(true); c.id === 'd1' && c !== div", "true"),
10774        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx !== null && ctx.canvas === cv", "true"),
10775        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx.measureText('test').width > 0", "true"),
10776        ("const cv = document.createElement('canvas'); cv.toDataURL().startsWith('data:image/png;base64,')", "true"),
10777        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx.lineWidth = 10; ctx.reset(); ctx.lineWidth === 1", "true"),
10778        ("const p = new Path2D('M10 10 H 90 V 90 H 10 Z'); typeof p.moveTo === 'function' && typeof p.roundRect === 'function'", "true"),
10779        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); const p = new Path2D('M 10 10 H 90 V 90 Z'); ctx.fill(p); ctx.stroke(p); ctx.clip(p); true", "true"),
10780        ("const p1 = new Path2D(); p1.rect(0, 0, 10, 10); const p2 = new Path2D(p1); p2.addPath(p1); typeof p2.closePath === 'function'", "true"),
10781        ("const div = document.createElement('div'); div.getRootNode() === div", "true"),
10782        ("const div = document.getElementById('tmpl'); div.getRootNode() === document", "true"),
10783        ("const el = document.createElement('div'); document.adoptNode(el) === el", "true"),
10784        ("const el = document.createElement('div'); document.importNode(el, true) !== null", "true"),
10785        ("ArrayBuffer.isView(new Uint8Array(5)) && !ArrayBuffer.isView(new ArrayBuffer(5))", "true"),
10786        ("Math.clamp(15, 0, 10) === 10 && Math.clamp(-5, 0, 10) === 0 && Math.clamp(5, 0, 10) === 5", "true"),
10787        ("const io = new IntersectionObserver(()=>{}); typeof io.takeRecords === 'function' && io.takeRecords().length === 0", "true"),
10788        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx.fillStyle = 'red'; ctx.save(); ctx.fillStyle = 'blue'; ctx.restore(); ctx.fillStyle === 'red'", "true"),
10789        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx.strokeStyle = 'green'; ctx.font = '24px serif'; ctx.save(); ctx.strokeStyle = 'purple'; ctx.font = '12px sans-serif'; ctx.restore(); ctx.strokeStyle === 'green' && ctx.font === '24px serif'", "true"),
10790        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); typeof ctx.strokeText === 'function'", "true"),
10791        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx.imageSmoothingEnabled === true", "true"),
10792        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx.imageSmoothingEnabled = false; ctx.imageSmoothingEnabled === false", "true"),
10793        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); ctx.imageSmoothingEnabled = false; ctx.save(); ctx.imageSmoothingEnabled = true; ctx.restore(); ctx.imageSmoothingEnabled === false", "true"),
10794        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); const g = ctx.createLinearGradient(0, 0, 100, 0); g.addColorStop(0, 'red'); g.addColorStop(1, 'blue'); ctx.fillStyle = g; typeof ctx.fillRect === 'function'", "true"),
10795        ("const cv = document.createElement('canvas'); const ctx = cv.getContext('2d'); const g = ctx.createRadialGradient(50, 50, 0, 50, 50, 50); g.addColorStop(0, 'white'); g.addColorStop(1, 'black'); ctx.fillStyle = g; ctx.fillRect(0, 0, 100, 100); true", "true"),
10796    ];
10797    total += template_cases.len();
10798    for (src, expect) in template_cases {
10799        let mut rt = JsRuntime::new();
10800        rt.dom
10801            .borrow_mut()
10802            .build_from(&crate::os_lib::dom::parse_html(template_html));
10803        match rt.eval(src) {
10804            Ok(v) if &v.to_js_string() == expect => passed += 1,
10805            Ok(v) => crate::println!(
10806                "JS_SELFTEST FAIL: Template test `{}` => `{}` (want `{}`)",
10807                src,
10808                v.to_js_string(),
10809                expect
10810            ),
10811            Err(e) => crate::println!("JS_SELFTEST ERR:  Template test `{}` => {}", src, e),
10812        }
10813    }
10814
10815    (passed, total)
10816}
10817
10818
10819
10820