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_of_expr(&obj, property, object)?;
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        // 例外の発生箇所を示すため、呼び出し中の関数名を積む(深さ上限と同じく有限)。
258        let name = match func {
259            Value::Object(o) => o
260                .borrow()
261                .props
262                .get("name")
263                .map(|v| v.to_js_string())
264                .filter(|n| !n.is_empty())
265                .unwrap_or_else(|| String::from("<anonymous>")),
266            _ => String::from("<anonymous>"),
267        };
268        self.call_names.push(name);
269        let result = self.call_value_inner(func, this, args);
270        self.call_names.pop();
271        self.depth -= 1;
272        result
273    }
274
275    /// イベントリスナ・タイマコールバック・オブザーバ通知など、
276    /// **呼び出し元へ例外を返せない場所**からの関数呼び出し。
277    ///
278    /// 【2026-07-28】これらは従来 `let _ = self.call_value(...)` と書かれており、
279    /// ハンドラ内で投げられた例外はコンソールにもログにも現れず完全に消えていた
280    /// (「ボタンを押しても何も起きない」だけが症状として残る)。ブラウザ同様、
281    /// 捕捉されなかった例外は必ず報告する。
282    pub fn call_listener(
283        &mut self,
284        func: &Value,
285        this: Value,
286        args: &[Value],
287        context: &str,
288    ) -> bool {
289        match self.call_value(func, this, args) {
290            Ok(_) => true,
291            Err(thrown) => {
292                self.report_uncaught(context, &thrown);
293                false
294            }
295        }
296    }
297
298    /// 捕捉されなかった例外を、ページのコンソール出力とシリアルログの両方へ出す。
299    pub fn report_uncaught(&mut self, context: &str, thrown: &Value) {
300        let line = crate::os_lib::js::uncaught::format_uncaught(context, &thrown.to_js_string());
301        crate::warn!("[JS] {}", line);
302        self.out.push_str(&line);
303        self.out.push('\n');
304    }
305
306    /// element.dispatchEvent から呼ぶ Interp 内ディスパッチ。JsRuntime 版と同じ
307    /// capture→target→bubble の3フェーズ・once・stopPropagation を再現するが、
308    /// リスナは self.call_value で同一 Interp 上で実行する。戻り値: (発火したか, defaultPrevented)。
309    pub fn dispatch_event_in_interp(
310        &mut self,
311        node_idx: usize,
312        event_type: &str,
313        extra: &[(String, Value)],
314    ) -> (bool, bool) {
315        // 伝播パス(target→祖先)。
316        let path = {
317            let dom = self.dom.borrow();
318            let mut path = alloc::vec![node_idx];
319            let mut cur = node_idx;
320            let mut guard = 0;
321            while let Some(p) = dom.nodes.get(cur).and_then(|n| n.parent) {
322                path.push(p);
323                cur = p;
324                guard += 1;
325                if guard > dom.nodes.len() {
326                    break;
327                }
328            }
329            path
330        };
331        let any = path
332            .iter()
333            .any(|&n| self.dom.borrow().has_listener_on(n, event_type));
334        // `document.addEventListener('click', fn)` のみが登録され、経路上の
335        // どの DOM ノードにも直接のリスナが無いケース(要素側リスナ0件の
336        // 純粋なイベント委譲)を早期 return で取りこぼさないよう、document
337        // 側の汎用リスナ(`_listeners`)の有無も合わせて確認する(2026-07-16
338        // 発見・実装。以前は `any` が false ならここで即座に `(false, false)`
339        // を返しており、後段の document リスナ発火コードへ一切到達しなかった)。
340        let doc_has_listener = {
341            let doc_opt = self.global.borrow().vars.get("document").cloned();
342            doc_opt
343                .and_then(|d| super::super::builtins::event_target_listeners(&d))
344                .and_then(|listeners| listeners.borrow().props.get(event_type).cloned())
345                .is_some_and(|arr| match arr {
346                    Value::Object(o) => matches!(&o.borrow().kind, ObjKind::Array(items) if !items.is_empty()),
347                    _ => false,
348                })
349        };
350        let on_attr_name = alloc::format!("on{}", event_type);
351        // 【2026-07-23 発見・修正】`Obj::dom(n)`は呼び出す度に新規生成される使い捨ての
352        // ラッパーオブジェクトのため、`element.onchange = fn`で書き込まれた関数値は
353        // `dom_set_property`が書いた先の`Obj::dom(n)`インスタンスが破棄された瞬間に
354        // 失われ、ここで`obj_prop`経由で新規生成した別インスタンスを読んでも常に
355        // 見つからなかった(=ハンドラ関数値が丸ごと発火しない静かなバグ)。
356        // `DomBridge::on_handlers`(`listeners`と同じくDOM構造体側の永続ストレージ)
357        // から読む。
358        let has_on_handler = path.iter().any(|&n| {
359            if self.dom.borrow().get_on_handler(n, &on_attr_name).is_some() {
360                return true;
361            }
362            self.dom.borrow().get_attr(n, &on_attr_name).is_some()
363        });
364        if !any && !doc_has_listener && !has_on_handler {
365            return (false, false);
366        }
367
368        // `bubbles` を `extra` から読む(`Event`/`CustomEvent` から転送された実値。
369        // 無ければ内部発火イベント向けの既定値として `true` を使う)。以前はここを
370        // 一切見ずバブリング段階を常に無条件で全祖先まで辿っていたため、
371        // `new CustomEvent('x', {bubbles: false})` を dispatch しても祖先の
372        // リスナーが発火してしまう仕様違反バグだった(capture 段階は仕様上
373        // `bubbles` に関わらず常に祖先を辿るため、そちらは変更しない)。
374        let bubbles = extra
375            .iter()
376            .find(|(k, _)| k == "bubbles")
377            .map(|(_, v)| v.truthy())
378            .unwrap_or(true);
379        let composed: Vec<Value> = path.iter().map(|&n| Value::Object(Obj::dom(n))).collect();
380        let target = Value::Object(Obj::dom(node_idx));
381        let ev = Obj::plain();
382        {
383            let mut e = ev.borrow_mut();
384            e.props.insert(String::from("type"), Value::str(event_type));
385            e.props.insert(String::from("target"), target.clone());
386            e.props.insert(String::from("bubbles"), Value::Bool(true));
387            // `cancelable` が一切読まれておらず、`{cancelable: false}` で構築した
388            // イベントでも `preventDefault()` が常に効いてしまう仕様違反バグだった。
389            // 内部発火(click/submit 等)は `extra` が空のまま呼ばれ続けており、
390            // それらは「妨害可能」という既存の挙動に依存しているため、`bubbles`
391            // と同じく既定値は `true`(後段の `extra` ループが明示指定時のみ上書き)。
392            e.props.insert(String::from("cancelable"), Value::Bool(true));
393            // `isTrusted` が丸ごと未対応だった。仕様上、スクリプトから発火した
394            // イベント(`dispatchEvent()`・内部シミュレートの `.click()` 等)は
395            // 常に `false`(実ユーザー入力由来のイベントのみ `true`。この処理系に
396            // その区別自体が無いため常に `false` で問題ない)。読み取り専用の
397            // 定数のため `extra` での上書きは考慮しない。
398            e.props.insert(String::from("isTrusted"), Value::Bool(false));
399            e.props
400                .insert(String::from("eventPhase"), Value::Number(0.0));
401            e.props
402                .insert(String::from("defaultPrevented"), Value::Bool(false));
403            // `timeStamp` が丸ごと未対応で常に `undefined` だった(このエンジン
404            // 全体に存在しない機能だった。`EventTarget` 経路にも同時に追加済み)。
405            e.props.insert(
406                String::from("timeStamp"),
407                Value::Number(super::super::builtins::next_perf_timestamp()),
408            );
409            // レガシー DOM Level 0 の `returnValue`/`cancelBubble`/`srcElement`
410            // が丸ごと未対応だった。`srcElement` は `target` と同じ値の別名。
411            e.props.insert(String::from("returnValue"), Value::Bool(true));
412            e.props.insert(String::from("cancelBubble"), Value::Bool(false));
413            e.props.insert(String::from("srcElement"), target.clone());
414            e.props.insert(
415                String::from("_composedPath"),
416                Value::Object(Obj::array(composed)),
417            );
418            for (k, v) in extra {
419                e.props.insert(k.clone(), v.clone());
420            }
421            e.props.insert(
422                String::from("preventDefault"),
423                Value::Object(Obj::native("preventDefault", |_, this, _| {
424                    if let Value::Object(o) = &this {
425                        let cancelable = o
426                            .borrow()
427                            .props
428                            .get("cancelable")
429                            .map(|v| v.truthy())
430                            .unwrap_or(true);
431                        if cancelable {
432                            let mut b = o.borrow_mut();
433                            b.props
434                                .insert(String::from("defaultPrevented"), Value::Bool(true));
435                            // レガシー DOM Level 0 の `event.returnValue`
436                            // (`preventDefault()` と等価の意味を持つべきだが
437                            // 丸ごと未対応で常に `undefined` だった)を追従させる。
438                            b.props.insert(String::from("returnValue"), Value::Bool(false));
439                        }
440                    }
441                    Ok(Value::Undefined)
442                })),
443            );
444            e.props.insert(
445                String::from("stopPropagation"),
446                Value::Object(Obj::native("stopPropagation", |_, this, _| {
447                    if let Value::Object(o) = &this {
448                        let mut b = o.borrow_mut();
449                        b.props
450                            .insert(String::from("_stop"), Value::Bool(true));
451                        // レガシー DOM Level 0 の `event.cancelBubble`(同じく
452                        // 丸ごと未対応だった)を追従させる。
453                        b.props.insert(String::from("cancelBubble"), Value::Bool(true));
454                    }
455                    Ok(Value::Undefined)
456                })),
457            );
458            e.props.insert(
459                String::from("stopImmediatePropagation"),
460                Value::Object(Obj::native("stopImmediatePropagation", |_, this, _| {
461                    if let Value::Object(o) = &this {
462                        let mut b = o.borrow_mut();
463                        b.props.insert(String::from("_stop"), Value::Bool(true));
464                        b.props
465                            .insert(String::from("_stopImmediate"), Value::Bool(true));
466                        b.props.insert(String::from("cancelBubble"), Value::Bool(true));
467                    }
468                    Ok(Value::Undefined)
469                })),
470            );
471            e.props.insert(
472                String::from("composedPath"),
473                Value::Object(Obj::native("composedPath", |_, this, _| {
474                    if let Value::Object(o) = &this {
475                        if let Some(p) = o.borrow().props.get("_composedPath") {
476                            return Ok(p.clone());
477                        }
478                    }
479                    Ok(Value::Object(Obj::array(Vec::new())))
480                })),
481            );
482        }
483
484        let mut steps: Vec<(usize, bool, f64)> = Vec::new();
485        for &n in path.iter().skip(1).rev() {
486            steps.push((n, true, 1.0));
487        }
488        steps.push((node_idx, true, 2.0));
489        steps.push((node_idx, false, 2.0));
490        if bubbles {
491            for &n in path.iter().skip(1) {
492                steps.push((n, false, 3.0));
493            }
494        }
495
496        let mut fired = false;
497        let mut to_remove: Vec<u64> = Vec::new();
498        let mut propagation_stopped = false;
499        // `document`のキャプチャフェーズリスナ(`{capture:true}`)を、DOMノードの
500        // キャプチャフェーズ(`path`を祖先→target方向に辿る`steps`の`want_
501        // capture=true`群)より先に発火する。`document`は`self.dom.nodes`の
502        // 一員ではなく`path`祖先チェーンに含まれないため、キャプチャ方向では
503        // 最も外側の仮想的な祖先として扱う(仕様上キャプチャフェーズは`bubbles`
504        // に関わらず常に発火する。2026-07-16 発見・実装。詳細は
505        // `fire_document_listeners`参照)。
506        let (doc_cap_fired, doc_cap_stopped) = self.fire_document_listeners(&ev, event_type, true);
507        if doc_cap_fired {
508            fired = true;
509        }
510        if doc_cap_stopped {
511            propagation_stopped = true;
512        }
513        if !propagation_stopped {
514            'outer: for (n, want_capture, phase) in steps {
515                let listeners = self
516                    .dom
517                    .borrow()
518                    .listeners_phase(n, event_type, want_capture);
519                let on_attr_name = alloc::format!("on{}", event_type);
520                let node_this = Value::Object(Obj::dom(n));
521                if !want_capture {
522                    // 上の`has_on_handler`と同じ理由で、使い捨ての`Obj::dom(n)`ではなく
523                    // 永続ストレージ`DomBridge::on_handlers`から読む。
524                    let on_handler = self
525                        .dom
526                        .borrow()
527                        .get_on_handler(n, &on_attr_name)
528                        .filter(|v| !matches!(v, Value::Undefined | Value::Null))
529                        .or_else(|| self.dom.borrow().get_attr(n, &on_attr_name).map(Value::str));
530                    if let Some(h) = on_handler {
531                        match h {
532                            Value::Object(_) => {
533                                let ctx = alloc::format!("on{} handler", event_type);
534                                self.call_listener(
535                                    &h,
536                                    node_this.clone(),
537                                    &[Value::Object(ev.clone())],
538                                    &ctx,
539                                );
540                                fired = true;
541                            }
542                            Value::Str(code) if !code.is_empty() => {
543                                if let Err(thrown) = self.eval_source(&code) {
544                                    let ctx = alloc::format!("on{} attribute", event_type);
545                                    self.report_uncaught(&ctx, &thrown);
546                                }
547                                fired = true;
548                            }
549                            _ => {}
550                        }
551                    }
552                }
553                if listeners.is_empty() {
554                    continue;
555                }
556                {
557                    let mut e = ev.borrow_mut();
558                    e.props
559                        .insert(String::from("currentTarget"), Value::Object(Obj::dom(n)));
560                    e.props
561                        .insert(String::from("eventPhase"), Value::Number(phase));
562                }
563                let node_this = Value::Object(Obj::dom(n));
564                for (func, once, id) in listeners {
565                    let ctx = alloc::format!("{} listener", event_type);
566                    self.call_listener(
567                        &func,
568                        node_this.clone(),
569                        &[Value::Object(ev.clone())],
570                        &ctx,
571                    );
572                    fired = true;
573                    if once {
574                        to_remove.push(id);
575                    }
576                    if ev
577                        .borrow()
578                        .props
579                        .get("_stopImmediate")
580                        .map(|v| v.truthy())
581                        .unwrap_or(false)
582                    {
583                        propagation_stopped = true;
584                        break 'outer;
585                    }
586                }
587                if ev
588                    .borrow()
589                    .props
590                    .get("_stop")
591                    .map(|v| v.truthy())
592                    .unwrap_or(false)
593                {
594                    propagation_stopped = true;
595                    break 'outer;
596                }
597            }
598        }
599        if !to_remove.is_empty() {
600            let mut dom = self.dom.borrow_mut();
601            for id in to_remove {
602                dom.remove_listener_by_id(id);
603            }
604        }
605        // `document.addEventListener('click', fn)` 等の汎用イベント委譲パターン
606        // (丸ごと未対応だった。`document` は `self.dom.nodes` の一員ではないため
607        // 上記の祖先チェーン `path` に一切含まれず、`document`自身の`_listeners`
608        // (`document_add_event_listener`が書き込む先)は従来一切読まれていな
609        // かった。実DOMイベントのバブリングがトップまで到達し `stopPropagation()`
610        // で止められていなければ、最後に `document` を仮想的な最終祖先として
611        // 扱い、対応する(非capture)汎用リスナを発火する。2026-07-16
612        // 発見・実装。capture版は関数冒頭の`fire_document_listeners(...,
613        // true)`呼び出し参照)。
614        if bubbles && !propagation_stopped {
615            let (doc_bub_fired, _) = self.fire_document_listeners(&ev, event_type, false);
616            if doc_bub_fired {
617                fired = true;
618            }
619        }
620        let default_prevented = ev
621            .borrow()
622            .props
623            .get("defaultPrevented")
624            .map(|v| v.truthy())
625            .unwrap_or(false);
626        (fired, default_prevented)
627    }
628
629    /// `document`の`_listeners[event_type]`のうち`capture`フラグが`want_capture`
630    /// と一致するリスナを発火する。`document`は`self.dom.nodes`の一員では
631    /// なくDOMの祖先チェーン`path`に含まれないため、キャプチャ方向では
632    /// 最も外側、バブル方向では最後の仮想的な祖先として`dispatch_event_
633    /// in_interp`の冒頭(キャプチャ)と末尾(バブル)から呼ばれる
634    /// (2026-07-16新設。`event_target_dispatch_event`の発火ループと同型の
635    /// ロジックだが、`document`用にターゲット/フェーズ番号を差し替えている)。
636    /// 戻り値は `(1件でも発火したか, stopPropagation/stopImmediatePropagation
637    /// で打ち切られたか)`。
638    fn fire_document_listeners(
639        &mut self,
640        ev: &super::super::value::ObjRef,
641        event_type: &str,
642        want_capture: bool,
643    ) -> (bool, bool) {
644        let mut fired = false;
645        // `self.global.borrow()`の一時`Ref`を`if let`の被走査式に直接置くと、
646        // 一時値ライフタイム延長でブロック終端まで生き続け、ブロック内の
647        // `self.call_value`(`&mut self`要求)と借用が衝突してビルドが通ら
648        // なくなる。ブロックで包んで明示的にドロップさせる。
649        let doc_opt = {
650            let g = self.global.borrow();
651            g.vars.get("document").cloned()
652        };
653        let Some(Value::Object(doc)) = doc_opt else {
654            return (false, false);
655        };
656        let doc_val = Value::Object(doc);
657        let Some(listeners) = super::super::builtins::event_target_listeners(&doc_val) else {
658            return (false, false);
659        };
660        let cbs: alloc::vec::Vec<Value> = match listeners.borrow().props.get(event_type) {
661            Some(Value::Object(arr)) => match &arr.borrow().kind {
662                ObjKind::Array(items) => items.clone(),
663                _ => alloc::vec::Vec::new(),
664            },
665            _ => alloc::vec::Vec::new(),
666        };
667        let matching: alloc::vec::Vec<Value> = cbs
668            .into_iter()
669            .filter(|entry| super::super::builtins::event_target_listener_capture(entry) == want_capture)
670            .collect();
671        if matching.is_empty() {
672            return (false, false);
673        }
674        {
675            let mut e = ev.borrow_mut();
676            e.props.insert(String::from("currentTarget"), doc_val.clone());
677            e.props
678                .insert(String::from("eventPhase"), Value::Number(if want_capture { 1.0 } else { 3.0 }));
679        }
680        for entry in matching {
681            if super::super::builtins::event_target_listener_aborted(&entry) {
682                continue;
683            }
684            let cb = super::super::builtins::event_target_listener_cb(&entry);
685            let once = super::super::builtins::event_target_listener_once(&entry);
686            let ctx = alloc::format!("{} listener (document)", event_type);
687            self.call_listener(&cb, doc_val.clone(), &[Value::Object(ev.clone())], &ctx);
688            fired = true;
689            if once {
690                if let Some(Value::Object(arr)) = listeners.borrow().props.get(event_type).cloned() {
691                    if let ObjKind::Array(items) = &mut arr.borrow_mut().kind {
692                        items.retain(|v| match (&super::super::builtins::event_target_listener_cb(v), &cb) {
693                            (Value::Object(x), Value::Object(y)) => !Rc::ptr_eq(x, y),
694                            _ => true,
695                        });
696                    }
697                }
698            }
699            if ev
700                .borrow()
701                .props
702                .get("_stopImmediate")
703                .map(|v| v.truthy())
704                .unwrap_or(false)
705            {
706                return (fired, true);
707            }
708        }
709        let stopped = ev.borrow().props.get("_stop").map(|v| v.truthy()).unwrap_or(false);
710        (fired, stopped)
711    }
712
713    pub(crate) fn call_value_inner(&mut self, func: &Value, this: Value, args: &[Value]) -> EvalResult {
714        let obj = match func {
715            Value::Object(o) => o.clone(),
716            // 【2026-09-05】何が関数でなかったのかを出す。
717            // 「not a function」だけでは、どの呼び出しか分からない。
718            _ => {
719                let d = alloc::format!("{}", func.to_js_string());
720                // 【一時】直前のプロパティ参照の並びを添える。
721                // `$` 自体なのか `.ready` なのかを区別するため。
722                return Err(self.throw(alloc::format!("not a function: {}", d)));
723            }
724        };
725        // Proxy: apply トラップがあれば呼ぶ。無ければ target を呼ぶ。
726        let proxy = match &obj.borrow().kind {
727            ObjKind::Proxy { target, handler } => Some((target.clone(), handler.clone())),
728            _ => None,
729        };
730        if let Some((target, handler)) = proxy {
731            let trap = handler.borrow().props.get("apply").cloned();
732            if let Some(trap) =
733                trap.filter(|t| matches!(t, Value::Object(f) if f.borrow().is_callable()))
734            {
735                let arg_arr = Value::Object(Obj::array(args.to_vec()));
736                let targs = [Value::Object(target), this, arg_arr];
737                return self.call_value(&trap, Value::Object(handler), &targs);
738            }
739            return self.call_value(&Value::Object(target), this, args);
740        }
741        // `Function.prototype.bind()` が返す束縛済み関数: `bound_this` を this に固定し、
742        // 束縛済み引数 `bound_args` の後ろに実引数 `args` を連結して `target` を呼ぶ
743        // (呼び出し側が渡す `this` は spec 通り無視する)。
744        let bound = match &obj.borrow().kind {
745            ObjKind::Bound {
746                target,
747                bound_this,
748                bound_args,
749            } => Some((target.clone(), bound_this.clone(), bound_args.clone())),
750            _ => None,
751        };
752        if let Some((target, bound_this, bound_args)) = bound {
753            let mut combined = bound_args;
754            combined.extend_from_slice(args);
755            return self.call_value(&target, bound_this, &combined);
756        }
757        // kind を取り出す(借用を跨がないようクローン)。
758        let kind = {
759            let b = obj.borrow();
760            match &b.kind {
761                ObjKind::Function(fd) => CallKind::User(fd.clone()),
762                ObjKind::Native { func, .. } => CallKind::Native(*func),
763                ObjKind::Resolver { state, reject } => CallKind::Resolver(state.clone(), *reject),
764                // 呼べないオブジェクトの種別を出す(配列なのか素の
765                // オブジェクトなのかで原因の見当が変わる)。
766                other => {
767                    let kind_name = match other {
768                        ObjKind::Array(_) => "array",
769                        ObjKind::Plain => "object",
770                        ObjKind::DomElement(_) => "dom element",
771                        ObjKind::Host(_) => "host object",
772                        _ => "other",
773                    };
774                    return Err(self.throw(alloc::format!(
775                        "not a function: {} (呼べない種別)",
776                        kind_name
777                    )));
778                }
779            }
780        };
781        match kind {
782            CallKind::Native(f) => f(self, this, args),
783            CallKind::Resolver(state, reject) => {
784                let arg = args.first().cloned().unwrap_or(Value::Undefined);
785                if reject {
786                    self.promise_reject(&state, arg);
787                } else {
788                    self.promise_resolve(&state, arg);
789                }
790                Ok(Value::Undefined)
791            }
792            CallKind::User(fd) => {
793                // `new.target`: アロー関数以外の呼び出し毎に push/pop する(アローは
794                // `this` と同様に周囲のスコープからレキシカルに継承するため対象外)。
795                // `construct_object()` が `new` 経由の呼び出し直前に `pending_new_target`
796                // をセットする。早期 return(`?`)を挟んでもスタックの対応が崩れないよう、
797                // 本体の実行を別メソッドへ切り出し、push/pop はこの呼び出しの外側で行う。
798                let nt = self.pending_new_target.take().unwrap_or(Value::Undefined);
799                let pushed = !fd.is_arrow;
800                if pushed {
801                    self.new_target_stack.push(nt);
802                }
803                let result = self.call_user_function(fd, this, args, func.clone());
804                if pushed {
805                    self.new_target_stack.pop();
806                }
807                result
808            }
809        }
810    }
811
812    pub(crate) fn call_user_function(
813        &mut self,
814        fd: FunctionData,
815        this: Value,
816        args: &[Value],
817        callee: Value,
818    ) -> EvalResult {
819        {
820                let call_scope = Scope::child(fd.closure.clone());
821                for (i, p) in fd.params.iter().enumerate() {
822                    if p.is_rest {
823                        // レスト引数: 残余を配列束縛。
824                        let rest_args: Vec<Value> =
825                            args.get(i..).map(|s| s.to_vec()).unwrap_or_default();
826                        self.bind_pattern(
827                            &p.pattern,
828                            Value::Object(Obj::array(rest_args)),
829                            &call_scope,
830                            &this,
831                        )?;
832                        break;
833                    }
834                    let mut v = args.get(i).cloned().unwrap_or(Value::Undefined);
835                    // デフォルト引数: 実引数が undefined のとき評価。
836                    if matches!(v, Value::Undefined) {
837                        if let Some(def) = &p.default {
838                            v = self.eval(def, &call_scope, &this)?;
839                        }
840                    }
841                    self.bind_pattern(&p.pattern, v, &call_scope, &this)?;
842                }
843                // arguments 配列。`.callee`(Annex B。無名関数の自己再帰呼び出し
844                // イディオム `arguments.callee` で今も使われる)が丸ごと未対応
845                // だったため、呼び出された関数自身への参照を追加する。
846                let arguments_obj = Obj::array(args.to_vec());
847                arguments_obj.borrow_mut().props.insert("callee".into(), callee);
848                scope_declare(&call_scope, "arguments", Value::Object(arguments_obj));
849                let use_this = if fd.is_arrow {
850                    fd.bound_this
851                        .as_ref()
852                        .map(|b| (**b).clone())
853                        .unwrap_or(Value::Undefined)
854                } else {
855                    this
856                };
857                if fd.is_generator {
858                    // generator は遅延評価: 本体は実行せず、関数定義と実引数/this を
859                    // GenState に保存して Generator オブジェクトを返す。実際の本体実行は
860                    // .next()/for-of/spread での resume 時に replay 方式で行う。
861                    return Ok(Value::Object(Obj::generator(GenState {
862                        func: fd.clone(),
863                        args: args.to_vec(),
864                        this: use_this,
865                        sent: Vec::new(),
866                        started: false,
867                        done: false,
868                        returned: Value::Undefined,
869                    })));
870                }
871                if fd.is_async {
872                    // async 関数は同期実行し、結果/例外を Promise に包んで返す
873                    // (await はマイクロタスクを駆動して解決まで進める)。
874                    let outcome = self.exec_statements(&fd.body, &call_scope, &use_this);
875                    let pstate = Rc::new(RefCell::new(PromiseState::pending()));
876                    match outcome {
877                        Ok(Completion::Return(v)) => self.promise_resolve(&pstate, v),
878                        Ok(_) => self.promise_resolve(&pstate, Value::Undefined),
879                        Err(e) => {
880                            if self.aborted {
881                                return Err(e);
882                            }
883                            self.promise_reject(&pstate, e);
884                        }
885                    }
886                    return Ok(Value::Object(Obj::promise(pstate)));
887                }
888                match self.exec_statements(&fd.body, &call_scope, &use_this)? {
889                    Completion::Return(v) => Ok(v),
890                    _ => Ok(Value::Undefined),
891                }
892        }
893    }
894
895    // ============ プロパティアクセス ============
896
897}