Skip to main content

atmos/os_lib/js/interp/
calls.rs

1// 分割: interp.rs の `impl Interp` から機械的に移動(2026-07-16 リファクタ フェーズ5)。
2// ロジック不変。メソッド可視性のみ pub(crate) へ昇格(別モジュールの impl Interp
3// からの self 呼び出しのため)。impl ブロックは型 Interp に自動で結合する。
4use super::*;
5
6impl Interp {
7    pub(crate) fn eval_call(
8        &mut self,
9        callee: &Expression,
10        arguments: &[Expression],
11        optional: bool,
12        scope: &Rc<RefCell<Scope>>,
13        this: &Value,
14    ) -> EvalResult {
15        // super(...): 基底クラスのコンストラクタを現在の this で呼ぶ。
16        if matches!(callee, Expression::Super) {
17            let sc = self.super_ctor(scope)?;
18            let args = self.eval_arguments(arguments, scope, this)?;
19            return self.call_value(&sc, this.clone(), &args);
20        }
21        // super.method(...): 基底プロトタイプのメソッドを現在の this で呼ぶ。
22        if let Expression::Member {
23            object, property, ..
24        } = callee
25        {
26            if matches!(**object, Expression::Super) {
27                let sp = self.super_proto(scope)?;
28                let f = self.get_property(&sp, property)?;
29                let args = self.eval_arguments(arguments, scope, this)?;
30                return self.call_value(&f, this.clone(), &args);
31            }
32        }
33        // this の決定: メンバ/添字呼出なら receiver。オブジェクト側のオプショナルチェイン
34        // (`a?.b.c()` 等)の短絡は `resolve_callee` 内で検知される。
35        let (func, call_this, broke) = self.resolve_callee(callee, scope, this)?;
36        if broke {
37            return Ok(Value::Undefined);
38        }
39        if optional && matches!(func, Value::Undefined | Value::Null) {
40            return Ok(Value::Undefined);
41        }
42        let args = self.eval_arguments(arguments, scope, this)?;
43        self.call_value(&func, call_this, &args)
44    }
45
46    /// `Call` の callee を this 束縛込みで解決する(`super`/`super.method` は呼出元
47    /// `eval_call` で先に処理済みのためここには来ない)。戻り値は
48    /// `(関数, call_this, 途中で ?. 短絡したか)`。`object`/`callee` 側は
49    /// `eval_chain_object` を再帰的に使うため、`a?.b.c()` のように途中で短絡した場合は
50    /// 以降のプロパティアクセス/引数評価/呼出を一切行わずチェーン全体が `undefined` になる
51    /// (ECMA-262 の OptionalChain セマンティクス。以前は最初の `?.` の直近1段しか
52    /// 短絡せず、後続のアクセスで `TypeError` になっていた)。
53    pub(crate) fn resolve_callee(
54        &mut self,
55        callee: &Expression,
56        scope: &Rc<RefCell<Scope>>,
57        this: &Value,
58    ) -> Result<(Value, Value, bool), Value> {
59        match callee {
60            Expression::Member {
61                object,
62                property,
63                optional: mopt,
64            } if !matches!(**object, Expression::Super) => {
65                let (obj, broke) = self.eval_chain_object(object, scope, this)?;
66                if broke {
67                    return Ok((Value::Undefined, Value::Undefined, true));
68                }
69                if *mopt && matches!(obj, Value::Undefined | Value::Null) {
70                    return Ok((Value::Undefined, Value::Undefined, true));
71                }
72                let f = self.get_property(&obj, property)?;
73                Ok((f, obj, false))
74            }
75            Expression::Index {
76                object,
77                index,
78                optional: iopt,
79            } => {
80                let (obj, broke) = self.eval_chain_object(object, scope, this)?;
81                if broke {
82                    return Ok((Value::Undefined, Value::Undefined, true));
83                }
84                if *iopt && matches!(obj, Value::Undefined | Value::Null) {
85                    return Ok((Value::Undefined, Value::Undefined, true));
86                }
87                let key = self.eval(index, scope, this)?;
88                let f = self.get_property(&obj, &to_property_key(&key))?;
89                Ok((f, obj, false))
90            }
91            _ => {
92                let (f, broke) = self.eval_chain_object(callee, scope, this)?;
93                Ok((f, Value::Undefined, broke))
94            }
95        }
96    }
97
98    /// `Member`/`Index`/`Call` の「object/callee」位置を短絡込みで評価する。戻り値の
99    /// `bool` は「このサブチェーンのどこかで `?.` が短絡したか」。短絡時、値は常に
100    /// `Value::Undefined`。グローバルな可変フラグではなく戻り値だけで伝播するため、
101    /// 無関係な式の評価に短絡状態が漏れることが無い。`super`/`super.method` を含む
102    /// `Call` は短絡セマンティクス対象外のため既存の `eval_call` にそのまま委譲する。
103    pub(crate) fn eval_chain_object(
104        &mut self,
105        expr: &Expression,
106        scope: &Rc<RefCell<Scope>>,
107        this: &Value,
108    ) -> Result<(Value, bool), Value> {
109        match expr {
110            Expression::Member {
111                object,
112                property,
113                optional,
114            } => {
115                if matches!(**object, Expression::Super) {
116                    let sp = self.super_proto(scope)?;
117                    return Ok((self.get_property(&sp, property)?, false));
118                }
119                let (obj, broke) = self.eval_chain_object(object, scope, this)?;
120                if broke {
121                    return Ok((Value::Undefined, true));
122                }
123                if *optional && matches!(obj, Value::Undefined | Value::Null) {
124                    return Ok((Value::Undefined, true));
125                }
126                Ok((self.get_property(&obj, property)?, false))
127            }
128            Expression::Index {
129                object,
130                index,
131                optional,
132            } => {
133                let (obj, broke) = self.eval_chain_object(object, scope, this)?;
134                if broke {
135                    return Ok((Value::Undefined, true));
136                }
137                if *optional && matches!(obj, Value::Undefined | Value::Null) {
138                    return Ok((Value::Undefined, true));
139                }
140                let key = self.eval(index, scope, this)?;
141                Ok((self.get_property(&obj, &to_property_key(&key))?, false))
142            }
143            Expression::Call {
144                callee,
145                arguments,
146                optional,
147            } => {
148                let involves_super = matches!(**callee, Expression::Super)
149                    || matches!(&**callee, Expression::Member { object, .. } if matches!(**object, Expression::Super));
150                if involves_super {
151                    return Ok((self.eval(expr, scope, this)?, false));
152                }
153                let (func, call_this, broke) = self.resolve_callee(callee, scope, this)?;
154                if broke {
155                    return Ok((Value::Undefined, true));
156                }
157                if *optional && matches!(func, Value::Undefined | Value::Null) {
158                    return Ok((Value::Undefined, true));
159                }
160                let args = self.eval_arguments(arguments, scope, this)?;
161                Ok((self.call_value(&func, call_this, &args)?, false))
162            }
163            _ => Ok((self.eval(expr, scope, this)?, false)),
164        }
165    }
166
167    /// 呼出引数を評価し、スプレッド `...x` を展開した Vec を返す。
168    pub(crate) fn eval_arguments(
169        &mut self,
170        arguments: &[Expression],
171        scope: &Rc<RefCell<Scope>>,
172        this: &Value,
173    ) -> Result<Vec<Value>, Value> {
174        let mut args = Vec::with_capacity(arguments.len());
175        for a in arguments {
176            if let Expression::Spread(inner) = a {
177                let v = self.eval(inner, scope, this)?;
178                let items = self.iter_to_vec(&v);
179                args.extend(items);
180            } else {
181                args.push(self.eval(a, scope, this)?);
182            }
183        }
184        Ok(args)
185    }
186
187    pub(crate) fn eval_new(
188        &mut self,
189        callee: &Expression,
190        arguments: &[Expression],
191        scope: &Rc<RefCell<Scope>>,
192        this: &Value,
193    ) -> EvalResult {
194        let func = self.eval(callee, scope, this)?;
195        let args = self.eval_arguments(arguments, scope, this)?;
196        // Proxy: construct トラップがあれば呼ぶ。無ければ target を new する。
197        let proxy = match &func {
198            Value::Object(o) => match &o.borrow().kind {
199                ObjKind::Proxy { target, handler } => Some((target.clone(), handler.clone())),
200                _ => None,
201            },
202            _ => None,
203        };
204        if let Some((target, handler)) = proxy {
205            let trap = handler.borrow().props.get("construct").cloned();
206            if let Some(trap) =
207                trap.filter(|t| matches!(t, Value::Object(f) if f.borrow().is_callable()))
208            {
209                let arg_arr = Value::Object(Obj::array(args.clone()));
210                let targs = [
211                    Value::Object(target),
212                    arg_arr,
213                    Value::Object(handler.clone()),
214                ];
215                return self.call_value(&trap, Value::Object(handler), &targs);
216            }
217            return self.construct_object(&Value::Object(target), &args);
218        }
219        self.construct_object(&func, &args)
220    }
221
222    /// `new` の本体: インスタンスを生成しコンストラクタを呼ぶ。
223    pub fn construct_value(&mut self, func: &Value, args: &[Value]) -> EvalResult {
224        self.construct_object(func, args)
225    }
226
227    pub(crate) fn construct_object(&mut self, func: &Value, args: &[Value]) -> EvalResult {
228        // インスタンスを生成し、コンストラクタの prototype を proto に設定(メソッド解決用)。
229        let inst_obj = Obj::plain();
230        if let Value::Object(fo) = func {
231            if let Some(Value::Object(p)) = fo.borrow().props.get("prototype") {
232                inst_obj.borrow_mut().proto = Some(p.clone());
233            }
234        }
235        let inst = Value::Object(inst_obj);
236        // `new.target` はこの呼び出し先関数(コンストラクタ自身)。`call_value_inner` が
237        // 消費してスタックへ push する(`super(...)` は construct_object を介さず直接
238        // call_value を呼ぶため、基底クラスのコンストラクタ内では `new.target` が
239        // undefined になる既知の簡略化。実用上最も一般的な「コンストラクタ先頭での
240        // `new.target` チェック」は正しく動作する)。
241        self.pending_new_target = Some(func.clone());
242        let ret = self.call_value(func, inst.clone(), args)?;
243        match ret {
244            Value::Object(_) => Ok(ret),
245            _ => Ok(inst),
246        }
247    }
248
249    /// 関数値を呼び出す。
250    pub fn call_value(&mut self, func: &Value, this: Value, args: &[Value]) -> EvalResult {
251        self.depth += 1;
252        if self.depth > self.max_depth {
253            self.aborted = true;
254            self.depth -= 1;
255            return Err(Value::Undefined);
256        }
257        let result = self.call_value_inner(func, this, args);
258        self.depth -= 1;
259        result
260    }
261
262    /// イベントリスナ・タイマコールバック・オブザーバ通知など、
263    /// **呼び出し元へ例外を返せない場所**からの関数呼び出し。
264    ///
265    /// 【2026-07-28】これらは従来 `let _ = self.call_value(...)` と書かれており、
266    /// ハンドラ内で投げられた例外はコンソールにもログにも現れず完全に消えていた
267    /// (「ボタンを押しても何も起きない」だけが症状として残る)。ブラウザ同様、
268    /// 捕捉されなかった例外は必ず報告する。
269    pub fn call_listener(
270        &mut self,
271        func: &Value,
272        this: Value,
273        args: &[Value],
274        context: &str,
275    ) -> bool {
276        match self.call_value(func, this, args) {
277            Ok(_) => true,
278            Err(thrown) => {
279                self.report_uncaught(context, &thrown);
280                false
281            }
282        }
283    }
284
285    /// 捕捉されなかった例外を、ページのコンソール出力とシリアルログの両方へ出す。
286    pub fn report_uncaught(&mut self, context: &str, thrown: &Value) {
287        let line = crate::os_lib::js::uncaught::format_uncaught(context, &thrown.to_js_string());
288        crate::warn!("[JS] {}", line);
289        self.out.push_str(&line);
290        self.out.push('\n');
291    }
292
293    /// element.dispatchEvent から呼ぶ Interp 内ディスパッチ。JsRuntime 版と同じ
294    /// capture→target→bubble の3フェーズ・once・stopPropagation を再現するが、
295    /// リスナは self.call_value で同一 Interp 上で実行する。戻り値: (発火したか, defaultPrevented)。
296    pub fn dispatch_event_in_interp(
297        &mut self,
298        node_idx: usize,
299        event_type: &str,
300        extra: &[(String, Value)],
301    ) -> (bool, bool) {
302        // 伝播パス(target→祖先)。
303        let path = {
304            let dom = self.dom.borrow();
305            let mut path = alloc::vec![node_idx];
306            let mut cur = node_idx;
307            let mut guard = 0;
308            while let Some(p) = dom.nodes.get(cur).and_then(|n| n.parent) {
309                path.push(p);
310                cur = p;
311                guard += 1;
312                if guard > dom.nodes.len() {
313                    break;
314                }
315            }
316            path
317        };
318        let any = path
319            .iter()
320            .any(|&n| self.dom.borrow().has_listener_on(n, event_type));
321        // `document.addEventListener('click', fn)` のみが登録され、経路上の
322        // どの DOM ノードにも直接のリスナが無いケース(要素側リスナ0件の
323        // 純粋なイベント委譲)を早期 return で取りこぼさないよう、document
324        // 側の汎用リスナ(`_listeners`)の有無も合わせて確認する(2026-07-16
325        // 発見・実装。以前は `any` が false ならここで即座に `(false, false)`
326        // を返しており、後段の document リスナ発火コードへ一切到達しなかった)。
327        let doc_has_listener = {
328            let doc_opt = self.global.borrow().vars.get("document").cloned();
329            doc_opt
330                .and_then(|d| super::super::builtins::event_target_listeners(&d))
331                .and_then(|listeners| listeners.borrow().props.get(event_type).cloned())
332                .is_some_and(|arr| match arr {
333                    Value::Object(o) => matches!(&o.borrow().kind, ObjKind::Array(items) if !items.is_empty()),
334                    _ => false,
335                })
336        };
337        let on_attr_name = alloc::format!("on{}", event_type);
338        // 【2026-07-23 発見・修正】`Obj::dom(n)`は呼び出す度に新規生成される使い捨ての
339        // ラッパーオブジェクトのため、`element.onchange = fn`で書き込まれた関数値は
340        // `dom_set_property`が書いた先の`Obj::dom(n)`インスタンスが破棄された瞬間に
341        // 失われ、ここで`obj_prop`経由で新規生成した別インスタンスを読んでも常に
342        // 見つからなかった(=ハンドラ関数値が丸ごと発火しない静かなバグ)。
343        // `DomBridge::on_handlers`(`listeners`と同じくDOM構造体側の永続ストレージ)
344        // から読む。
345        let has_on_handler = path.iter().any(|&n| {
346            if self.dom.borrow().get_on_handler(n, &on_attr_name).is_some() {
347                return true;
348            }
349            self.dom.borrow().get_attr(n, &on_attr_name).is_some()
350        });
351        if !any && !doc_has_listener && !has_on_handler {
352            return (false, false);
353        }
354
355        // `bubbles` を `extra` から読む(`Event`/`CustomEvent` から転送された実値。
356        // 無ければ内部発火イベント向けの既定値として `true` を使う)。以前はここを
357        // 一切見ずバブリング段階を常に無条件で全祖先まで辿っていたため、
358        // `new CustomEvent('x', {bubbles: false})` を dispatch しても祖先の
359        // リスナーが発火してしまう仕様違反バグだった(capture 段階は仕様上
360        // `bubbles` に関わらず常に祖先を辿るため、そちらは変更しない)。
361        let bubbles = extra
362            .iter()
363            .find(|(k, _)| k == "bubbles")
364            .map(|(_, v)| v.truthy())
365            .unwrap_or(true);
366        let composed: Vec<Value> = path.iter().map(|&n| Value::Object(Obj::dom(n))).collect();
367        let target = Value::Object(Obj::dom(node_idx));
368        let ev = Obj::plain();
369        {
370            let mut e = ev.borrow_mut();
371            e.props.insert(String::from("type"), Value::str(event_type));
372            e.props.insert(String::from("target"), target.clone());
373            e.props.insert(String::from("bubbles"), Value::Bool(true));
374            // `cancelable` が一切読まれておらず、`{cancelable: false}` で構築した
375            // イベントでも `preventDefault()` が常に効いてしまう仕様違反バグだった。
376            // 内部発火(click/submit 等)は `extra` が空のまま呼ばれ続けており、
377            // それらは「妨害可能」という既存の挙動に依存しているため、`bubbles`
378            // と同じく既定値は `true`(後段の `extra` ループが明示指定時のみ上書き)。
379            e.props.insert(String::from("cancelable"), Value::Bool(true));
380            // `isTrusted` が丸ごと未対応だった。仕様上、スクリプトから発火した
381            // イベント(`dispatchEvent()`・内部シミュレートの `.click()` 等)は
382            // 常に `false`(実ユーザー入力由来のイベントのみ `true`。この処理系に
383            // その区別自体が無いため常に `false` で問題ない)。読み取り専用の
384            // 定数のため `extra` での上書きは考慮しない。
385            e.props.insert(String::from("isTrusted"), Value::Bool(false));
386            e.props
387                .insert(String::from("eventPhase"), Value::Number(0.0));
388            e.props
389                .insert(String::from("defaultPrevented"), Value::Bool(false));
390            // `timeStamp` が丸ごと未対応で常に `undefined` だった(このエンジン
391            // 全体に存在しない機能だった。`EventTarget` 経路にも同時に追加済み)。
392            e.props.insert(
393                String::from("timeStamp"),
394                Value::Number(super::super::builtins::next_perf_timestamp()),
395            );
396            // レガシー DOM Level 0 の `returnValue`/`cancelBubble`/`srcElement`
397            // が丸ごと未対応だった。`srcElement` は `target` と同じ値の別名。
398            e.props.insert(String::from("returnValue"), Value::Bool(true));
399            e.props.insert(String::from("cancelBubble"), Value::Bool(false));
400            e.props.insert(String::from("srcElement"), target.clone());
401            e.props.insert(
402                String::from("_composedPath"),
403                Value::Object(Obj::array(composed)),
404            );
405            for (k, v) in extra {
406                e.props.insert(k.clone(), v.clone());
407            }
408            e.props.insert(
409                String::from("preventDefault"),
410                Value::Object(Obj::native("preventDefault", |_, this, _| {
411                    if let Value::Object(o) = &this {
412                        let cancelable = o
413                            .borrow()
414                            .props
415                            .get("cancelable")
416                            .map(|v| v.truthy())
417                            .unwrap_or(true);
418                        if cancelable {
419                            let mut b = o.borrow_mut();
420                            b.props
421                                .insert(String::from("defaultPrevented"), Value::Bool(true));
422                            // レガシー DOM Level 0 の `event.returnValue`
423                            // (`preventDefault()` と等価の意味を持つべきだが
424                            // 丸ごと未対応で常に `undefined` だった)を追従させる。
425                            b.props.insert(String::from("returnValue"), Value::Bool(false));
426                        }
427                    }
428                    Ok(Value::Undefined)
429                })),
430            );
431            e.props.insert(
432                String::from("stopPropagation"),
433                Value::Object(Obj::native("stopPropagation", |_, this, _| {
434                    if let Value::Object(o) = &this {
435                        let mut b = o.borrow_mut();
436                        b.props
437                            .insert(String::from("_stop"), Value::Bool(true));
438                        // レガシー DOM Level 0 の `event.cancelBubble`(同じく
439                        // 丸ごと未対応だった)を追従させる。
440                        b.props.insert(String::from("cancelBubble"), Value::Bool(true));
441                    }
442                    Ok(Value::Undefined)
443                })),
444            );
445            e.props.insert(
446                String::from("stopImmediatePropagation"),
447                Value::Object(Obj::native("stopImmediatePropagation", |_, this, _| {
448                    if let Value::Object(o) = &this {
449                        let mut b = o.borrow_mut();
450                        b.props.insert(String::from("_stop"), Value::Bool(true));
451                        b.props
452                            .insert(String::from("_stopImmediate"), Value::Bool(true));
453                        b.props.insert(String::from("cancelBubble"), Value::Bool(true));
454                    }
455                    Ok(Value::Undefined)
456                })),
457            );
458            e.props.insert(
459                String::from("composedPath"),
460                Value::Object(Obj::native("composedPath", |_, this, _| {
461                    if let Value::Object(o) = &this {
462                        if let Some(p) = o.borrow().props.get("_composedPath") {
463                            return Ok(p.clone());
464                        }
465                    }
466                    Ok(Value::Object(Obj::array(Vec::new())))
467                })),
468            );
469        }
470
471        let mut steps: Vec<(usize, bool, f64)> = Vec::new();
472        for &n in path.iter().skip(1).rev() {
473            steps.push((n, true, 1.0));
474        }
475        steps.push((node_idx, true, 2.0));
476        steps.push((node_idx, false, 2.0));
477        if bubbles {
478            for &n in path.iter().skip(1) {
479                steps.push((n, false, 3.0));
480            }
481        }
482
483        let mut fired = false;
484        let mut to_remove: Vec<u64> = Vec::new();
485        let mut propagation_stopped = false;
486        // `document`のキャプチャフェーズリスナ(`{capture:true}`)を、DOMノードの
487        // キャプチャフェーズ(`path`を祖先→target方向に辿る`steps`の`want_
488        // capture=true`群)より先に発火する。`document`は`self.dom.nodes`の
489        // 一員ではなく`path`祖先チェーンに含まれないため、キャプチャ方向では
490        // 最も外側の仮想的な祖先として扱う(仕様上キャプチャフェーズは`bubbles`
491        // に関わらず常に発火する。2026-07-16 発見・実装。詳細は
492        // `fire_document_listeners`参照)。
493        let (doc_cap_fired, doc_cap_stopped) = self.fire_document_listeners(&ev, event_type, true);
494        if doc_cap_fired {
495            fired = true;
496        }
497        if doc_cap_stopped {
498            propagation_stopped = true;
499        }
500        if !propagation_stopped {
501            'outer: for (n, want_capture, phase) in steps {
502                let listeners = self
503                    .dom
504                    .borrow()
505                    .listeners_phase(n, event_type, want_capture);
506                let on_attr_name = alloc::format!("on{}", event_type);
507                let node_this = Value::Object(Obj::dom(n));
508                if !want_capture {
509                    // 上の`has_on_handler`と同じ理由で、使い捨ての`Obj::dom(n)`ではなく
510                    // 永続ストレージ`DomBridge::on_handlers`から読む。
511                    let on_handler = self
512                        .dom
513                        .borrow()
514                        .get_on_handler(n, &on_attr_name)
515                        .filter(|v| !matches!(v, Value::Undefined | Value::Null))
516                        .or_else(|| self.dom.borrow().get_attr(n, &on_attr_name).map(Value::str));
517                    if let Some(h) = on_handler {
518                        match h {
519                            Value::Object(_) => {
520                                let ctx = alloc::format!("on{} handler", event_type);
521                                self.call_listener(
522                                    &h,
523                                    node_this.clone(),
524                                    &[Value::Object(ev.clone())],
525                                    &ctx,
526                                );
527                                fired = true;
528                            }
529                            Value::Str(code) if !code.is_empty() => {
530                                if let Err(thrown) = self.eval_source(&code) {
531                                    let ctx = alloc::format!("on{} attribute", event_type);
532                                    self.report_uncaught(&ctx, &thrown);
533                                }
534                                fired = true;
535                            }
536                            _ => {}
537                        }
538                    }
539                }
540                if listeners.is_empty() {
541                    continue;
542                }
543                {
544                    let mut e = ev.borrow_mut();
545                    e.props
546                        .insert(String::from("currentTarget"), Value::Object(Obj::dom(n)));
547                    e.props
548                        .insert(String::from("eventPhase"), Value::Number(phase));
549                }
550                let node_this = Value::Object(Obj::dom(n));
551                for (func, once, id) in listeners {
552                    let ctx = alloc::format!("{} listener", event_type);
553                    self.call_listener(
554                        &func,
555                        node_this.clone(),
556                        &[Value::Object(ev.clone())],
557                        &ctx,
558                    );
559                    fired = true;
560                    if once {
561                        to_remove.push(id);
562                    }
563                    if ev
564                        .borrow()
565                        .props
566                        .get("_stopImmediate")
567                        .map(|v| v.truthy())
568                        .unwrap_or(false)
569                    {
570                        propagation_stopped = true;
571                        break 'outer;
572                    }
573                }
574                if ev
575                    .borrow()
576                    .props
577                    .get("_stop")
578                    .map(|v| v.truthy())
579                    .unwrap_or(false)
580                {
581                    propagation_stopped = true;
582                    break 'outer;
583                }
584            }
585        }
586        if !to_remove.is_empty() {
587            let mut dom = self.dom.borrow_mut();
588            for id in to_remove {
589                dom.remove_listener_by_id(id);
590            }
591        }
592        // `document.addEventListener('click', fn)` 等の汎用イベント委譲パターン
593        // (丸ごと未対応だった。`document` は `self.dom.nodes` の一員ではないため
594        // 上記の祖先チェーン `path` に一切含まれず、`document`自身の`_listeners`
595        // (`document_add_event_listener`が書き込む先)は従来一切読まれていな
596        // かった。実DOMイベントのバブリングがトップまで到達し `stopPropagation()`
597        // で止められていなければ、最後に `document` を仮想的な最終祖先として
598        // 扱い、対応する(非capture)汎用リスナを発火する。2026-07-16
599        // 発見・実装。capture版は関数冒頭の`fire_document_listeners(...,
600        // true)`呼び出し参照)。
601        if bubbles && !propagation_stopped {
602            let (doc_bub_fired, _) = self.fire_document_listeners(&ev, event_type, false);
603            if doc_bub_fired {
604                fired = true;
605            }
606        }
607        let default_prevented = ev
608            .borrow()
609            .props
610            .get("defaultPrevented")
611            .map(|v| v.truthy())
612            .unwrap_or(false);
613        (fired, default_prevented)
614    }
615
616    /// `document`の`_listeners[event_type]`のうち`capture`フラグが`want_capture`
617    /// と一致するリスナを発火する。`document`は`self.dom.nodes`の一員では
618    /// なくDOMの祖先チェーン`path`に含まれないため、キャプチャ方向では
619    /// 最も外側、バブル方向では最後の仮想的な祖先として`dispatch_event_
620    /// in_interp`の冒頭(キャプチャ)と末尾(バブル)から呼ばれる
621    /// (2026-07-16新設。`event_target_dispatch_event`の発火ループと同型の
622    /// ロジックだが、`document`用にターゲット/フェーズ番号を差し替えている)。
623    /// 戻り値は `(1件でも発火したか, stopPropagation/stopImmediatePropagation
624    /// で打ち切られたか)`。
625    fn fire_document_listeners(
626        &mut self,
627        ev: &super::super::value::ObjRef,
628        event_type: &str,
629        want_capture: bool,
630    ) -> (bool, bool) {
631        let mut fired = false;
632        // `self.global.borrow()`の一時`Ref`を`if let`の被走査式に直接置くと、
633        // 一時値ライフタイム延長でブロック終端まで生き続け、ブロック内の
634        // `self.call_value`(`&mut self`要求)と借用が衝突してビルドが通ら
635        // なくなる。ブロックで包んで明示的にドロップさせる。
636        let doc_opt = {
637            let g = self.global.borrow();
638            g.vars.get("document").cloned()
639        };
640        let Some(Value::Object(doc)) = doc_opt else {
641            return (false, false);
642        };
643        let doc_val = Value::Object(doc);
644        let Some(listeners) = super::super::builtins::event_target_listeners(&doc_val) else {
645            return (false, false);
646        };
647        let cbs: alloc::vec::Vec<Value> = match listeners.borrow().props.get(event_type) {
648            Some(Value::Object(arr)) => match &arr.borrow().kind {
649                ObjKind::Array(items) => items.clone(),
650                _ => alloc::vec::Vec::new(),
651            },
652            _ => alloc::vec::Vec::new(),
653        };
654        let matching: alloc::vec::Vec<Value> = cbs
655            .into_iter()
656            .filter(|entry| super::super::builtins::event_target_listener_capture(entry) == want_capture)
657            .collect();
658        if matching.is_empty() {
659            return (false, false);
660        }
661        {
662            let mut e = ev.borrow_mut();
663            e.props.insert(String::from("currentTarget"), doc_val.clone());
664            e.props
665                .insert(String::from("eventPhase"), Value::Number(if want_capture { 1.0 } else { 3.0 }));
666        }
667        for entry in matching {
668            if super::super::builtins::event_target_listener_aborted(&entry) {
669                continue;
670            }
671            let cb = super::super::builtins::event_target_listener_cb(&entry);
672            let once = super::super::builtins::event_target_listener_once(&entry);
673            let ctx = alloc::format!("{} listener (document)", event_type);
674            self.call_listener(&cb, doc_val.clone(), &[Value::Object(ev.clone())], &ctx);
675            fired = true;
676            if once {
677                if let Some(Value::Object(arr)) = listeners.borrow().props.get(event_type).cloned() {
678                    if let ObjKind::Array(items) = &mut arr.borrow_mut().kind {
679                        items.retain(|v| match (&super::super::builtins::event_target_listener_cb(v), &cb) {
680                            (Value::Object(x), Value::Object(y)) => !Rc::ptr_eq(x, y),
681                            _ => true,
682                        });
683                    }
684                }
685            }
686            if ev
687                .borrow()
688                .props
689                .get("_stopImmediate")
690                .map(|v| v.truthy())
691                .unwrap_or(false)
692            {
693                return (fired, true);
694            }
695        }
696        let stopped = ev.borrow().props.get("_stop").map(|v| v.truthy()).unwrap_or(false);
697        (fired, stopped)
698    }
699
700    pub(crate) fn call_value_inner(&mut self, func: &Value, this: Value, args: &[Value]) -> EvalResult {
701        let obj = match func {
702            Value::Object(o) => o.clone(),
703            // 【2026-09-05】何が関数でなかったのかを出す。
704            // 「not a function」だけでは、どの呼び出しか分からない。
705            _ => {
706                let d = alloc::format!("{}", func.to_js_string());
707                // 【一時】直前のプロパティ参照の並びを添える。
708                // `$` 自体なのか `.ready` なのかを区別するため。
709                return Err(self.throw(alloc::format!("not a function: {}", d)));
710            }
711        };
712        // Proxy: apply トラップがあれば呼ぶ。無ければ target を呼ぶ。
713        let proxy = match &obj.borrow().kind {
714            ObjKind::Proxy { target, handler } => Some((target.clone(), handler.clone())),
715            _ => None,
716        };
717        if let Some((target, handler)) = proxy {
718            let trap = handler.borrow().props.get("apply").cloned();
719            if let Some(trap) =
720                trap.filter(|t| matches!(t, Value::Object(f) if f.borrow().is_callable()))
721            {
722                let arg_arr = Value::Object(Obj::array(args.to_vec()));
723                let targs = [Value::Object(target), this, arg_arr];
724                return self.call_value(&trap, Value::Object(handler), &targs);
725            }
726            return self.call_value(&Value::Object(target), this, args);
727        }
728        // `Function.prototype.bind()` が返す束縛済み関数: `bound_this` を this に固定し、
729        // 束縛済み引数 `bound_args` の後ろに実引数 `args` を連結して `target` を呼ぶ
730        // (呼び出し側が渡す `this` は spec 通り無視する)。
731        let bound = match &obj.borrow().kind {
732            ObjKind::Bound {
733                target,
734                bound_this,
735                bound_args,
736            } => Some((target.clone(), bound_this.clone(), bound_args.clone())),
737            _ => None,
738        };
739        if let Some((target, bound_this, bound_args)) = bound {
740            let mut combined = bound_args;
741            combined.extend_from_slice(args);
742            return self.call_value(&target, bound_this, &combined);
743        }
744        // kind を取り出す(借用を跨がないようクローン)。
745        let kind = {
746            let b = obj.borrow();
747            match &b.kind {
748                ObjKind::Function(fd) => CallKind::User(fd.clone()),
749                ObjKind::Native { func, .. } => CallKind::Native(*func),
750                ObjKind::Resolver { state, reject } => CallKind::Resolver(state.clone(), *reject),
751                // 呼べないオブジェクトの種別を出す(配列なのか素の
752                // オブジェクトなのかで原因の見当が変わる)。
753                other => {
754                    let kind_name = match other {
755                        ObjKind::Array(_) => "array",
756                        ObjKind::Plain => "object",
757                        ObjKind::DomElement(_) => "dom element",
758                        ObjKind::Host(_) => "host object",
759                        _ => "other",
760                    };
761                    return Err(self.throw(alloc::format!(
762                        "not a function: {} (呼べない種別)",
763                        kind_name
764                    )));
765                }
766            }
767        };
768        match kind {
769            CallKind::Native(f) => f(self, this, args),
770            CallKind::Resolver(state, reject) => {
771                let arg = args.first().cloned().unwrap_or(Value::Undefined);
772                if reject {
773                    self.promise_reject(&state, arg);
774                } else {
775                    self.promise_resolve(&state, arg);
776                }
777                Ok(Value::Undefined)
778            }
779            CallKind::User(fd) => {
780                // `new.target`: アロー関数以外の呼び出し毎に push/pop する(アローは
781                // `this` と同様に周囲のスコープからレキシカルに継承するため対象外)。
782                // `construct_object()` が `new` 経由の呼び出し直前に `pending_new_target`
783                // をセットする。早期 return(`?`)を挟んでもスタックの対応が崩れないよう、
784                // 本体の実行を別メソッドへ切り出し、push/pop はこの呼び出しの外側で行う。
785                let nt = self.pending_new_target.take().unwrap_or(Value::Undefined);
786                let pushed = !fd.is_arrow;
787                if pushed {
788                    self.new_target_stack.push(nt);
789                }
790                let result = self.call_user_function(fd, this, args, func.clone());
791                if pushed {
792                    self.new_target_stack.pop();
793                }
794                result
795            }
796        }
797    }
798
799    pub(crate) fn call_user_function(
800        &mut self,
801        fd: FunctionData,
802        this: Value,
803        args: &[Value],
804        callee: Value,
805    ) -> EvalResult {
806        {
807                let call_scope = Scope::child(fd.closure.clone());
808                for (i, p) in fd.params.iter().enumerate() {
809                    if p.is_rest {
810                        // レスト引数: 残余を配列束縛。
811                        let rest_args: Vec<Value> =
812                            args.get(i..).map(|s| s.to_vec()).unwrap_or_default();
813                        self.bind_pattern(
814                            &p.pattern,
815                            Value::Object(Obj::array(rest_args)),
816                            &call_scope,
817                            &this,
818                        )?;
819                        break;
820                    }
821                    let mut v = args.get(i).cloned().unwrap_or(Value::Undefined);
822                    // デフォルト引数: 実引数が undefined のとき評価。
823                    if matches!(v, Value::Undefined) {
824                        if let Some(def) = &p.default {
825                            v = self.eval(def, &call_scope, &this)?;
826                        }
827                    }
828                    self.bind_pattern(&p.pattern, v, &call_scope, &this)?;
829                }
830                // arguments 配列。`.callee`(Annex B。無名関数の自己再帰呼び出し
831                // イディオム `arguments.callee` で今も使われる)が丸ごと未対応
832                // だったため、呼び出された関数自身への参照を追加する。
833                let arguments_obj = Obj::array(args.to_vec());
834                arguments_obj.borrow_mut().props.insert("callee".into(), callee);
835                scope_declare(&call_scope, "arguments", Value::Object(arguments_obj));
836                let use_this = if fd.is_arrow {
837                    fd.bound_this
838                        .as_ref()
839                        .map(|b| (**b).clone())
840                        .unwrap_or(Value::Undefined)
841                } else {
842                    this
843                };
844                if fd.is_generator {
845                    // generator は遅延評価: 本体は実行せず、関数定義と実引数/this を
846                    // GenState に保存して Generator オブジェクトを返す。実際の本体実行は
847                    // .next()/for-of/spread での resume 時に replay 方式で行う。
848                    return Ok(Value::Object(Obj::generator(GenState {
849                        func: fd.clone(),
850                        args: args.to_vec(),
851                        this: use_this,
852                        sent: Vec::new(),
853                        started: false,
854                        done: false,
855                        returned: Value::Undefined,
856                    })));
857                }
858                if fd.is_async {
859                    // async 関数は同期実行し、結果/例外を Promise に包んで返す
860                    // (await はマイクロタスクを駆動して解決まで進める)。
861                    let outcome = self.exec_statements(&fd.body, &call_scope, &use_this);
862                    let pstate = Rc::new(RefCell::new(PromiseState::pending()));
863                    match outcome {
864                        Ok(Completion::Return(v)) => self.promise_resolve(&pstate, v),
865                        Ok(_) => self.promise_resolve(&pstate, Value::Undefined),
866                        Err(e) => {
867                            if self.aborted {
868                                return Err(e);
869                            }
870                            self.promise_reject(&pstate, e);
871                        }
872                    }
873                    return Ok(Value::Object(Obj::promise(pstate)));
874                }
875                match self.exec_statements(&fd.body, &call_scope, &use_this)? {
876                    Completion::Return(v) => Ok(v),
877                    _ => Ok(Value::Undefined),
878                }
879        }
880    }
881
882    // ============ プロパティアクセス ============
883
884}