Skip to main content

atmos/os_lib/js/
ast.rs

1//! JavaScript の抽象構文木(実用コア相当)。
2//!
3//! Phase 1 のスコープ: 関数/クロージャ・制御構文・全演算子・オブジェクト/配列・
4//! メンバ/添字/呼出/new・三項・論理・代入・try/catch/throw。
5//! 分割代入・デフォルト引数・class などのモダン構文は Phase 4 で拡張する。
6
7use alloc::boxed::Box;
8use alloc::rc::Rc;
9use core::cell::RefCell;
10use alloc::string::String;
11use alloc::vec::Vec;
12
13#[derive(Debug, Clone, PartialEq)]
14pub enum BinaryOp {
15    Add,
16    Sub,
17    Mul,
18    Div,
19    Mod,
20    Pow,
21    Eq,
22    NotEq,
23    StrictEq,
24    StrictNotEq,
25    Lt,
26    Gt,
27    LtEq,
28    GtEq,
29    BitAnd,
30    BitOr,
31    BitXor,
32    Shl,
33    Shr,
34    UShr,
35    InstanceOf,
36    In,
37}
38
39#[derive(Debug, Clone, PartialEq)]
40pub enum LogicalOp {
41    And,     // &&
42    Or,      // ||
43    Nullish, // ??
44}
45
46#[derive(Debug, Clone, PartialEq)]
47pub enum UnaryOp {
48    Neg,    // -x
49    Pos,    // +x
50    Not,    // !x
51    BitNot, // ~x
52    TypeOf, // typeof x
53    Void,   // void x
54    Delete, // delete x
55}
56
57/// オブジェクトリテラルのプロパティ。
58#[derive(Debug, Clone, PartialEq)]
59pub struct Property {
60    pub key: String,
61    pub value: Expression,
62    /// 算出キー `{[expr]: v}` の場合のキー式。None なら `key` を使う。
63    pub computed: Option<Expression>,
64    /// `{get x(){...}}`/`{set x(v){...}}` アクセサの場合 `Some(true)`/`Some(false)`。
65    /// 通常のプロパティ(値/メソッド短縮/短縮プロパティ)は `None`。
66    pub accessor: Option<bool>,
67}
68
69/// オブジェクトスプレッド `{...o}` を表す Property.key の番兵(通常キーと衝突しない制御文字)。
70pub const OBJECT_SPREAD_KEY: &str = "\u{0}spread";
71
72#[derive(Debug, Clone, PartialEq)]
73pub enum Expression {
74    Number(f64),
75    /// BigInt リテラル(数値部分の文字列。0x/0o/0b 接頭辞を含み得る)。
76    BigIntLit(String),
77    Str(String),
78    Bool(bool),
79    Null,
80    Undefined,
81    Identifier(String),
82    This,
83    TemplateLiteral {
84        // quasis.len() == exprs.len() + 1
85        quasis: Vec<String>,
86        exprs: Vec<Expression>,
87    },
88    /// タグ付きテンプレート `` tag`...${e}...` ``(ES2015)。`quasis`/`raw` は
89    /// `TemplateLiteral` と同様 exprs より1つ多い。`tag(strings, ...values)` 呼出しで
90    /// `strings` は `quasis` の配列に `.raw`(`raw` の配列)を added property として持つ。
91    TaggedTemplate {
92        tag: Box<Expression>,
93        quasis: Vec<String>,
94        raw: Vec<String>,
95        exprs: Vec<Expression>,
96    },
97    Array(Vec<Expression>),
98    Object(Vec<Property>),
99    /// 関数式・アロー関数。本体と引数は `Rc` で共有する(【2026-10-17】従来は評価のたびに
100    /// 本体の構文木を丸ごと複製しており、YouTube の base.js(全体が 1 つの即時関数)で
101    /// 約 100MB の複製が起きていた)。
102    Function {
103        name: Option<String>,
104        params: Rc<Vec<Param>>,
105        body: Rc<FnBody>,
106        is_arrow: bool,
107        is_async: bool,
108        is_generator: bool,
109    },
110    /// `await expr`。
111    Await(Box<Expression>),
112    /// `yield expr` / `yield* expr`。
113    Yield {
114        argument: Option<Box<Expression>>,
115        delegate: bool,
116    },
117    Unary {
118        op: UnaryOp,
119        expr: Box<Expression>,
120    },
121    Update {
122        op: String, // "++" / "--"
123        prefix: bool,
124        target: Box<Expression>,
125    },
126    Binary {
127        op: BinaryOp,
128        left: Box<Expression>,
129        right: Box<Expression>,
130    },
131    Logical {
132        op: LogicalOp,
133        left: Box<Expression>,
134        right: Box<Expression>,
135    },
136    Assign {
137        op: String, // "=", "+=", "-=", ...
138        target: Box<Expression>,
139        value: Box<Expression>,
140    },
141    Conditional {
142        test: Box<Expression>,
143        consequent: Box<Expression>,
144        alternate: Box<Expression>,
145    },
146    Member {
147        object: Box<Expression>,
148        property: String,
149        optional: bool, // a?.b
150    },
151    Index {
152        object: Box<Expression>,
153        index: Box<Expression>,
154        optional: bool, // a?.[b]
155    },
156    Call {
157        callee: Box<Expression>,
158        arguments: Vec<Expression>,
159        optional: bool, // a?.()
160    },
161    New {
162        callee: Box<Expression>,
163        arguments: Vec<Expression>,
164    },
165    /// スプレッド要素 `...expr`(配列リテラル/呼出引数/オブジェクト内で展開される)。
166    Spread(Box<Expression>),
167    /// `class` 式/宣言。
168    Class {
169        name: Option<String>,
170        superclass: Option<Box<Expression>>,
171        members: Vec<ClassMember>,
172    },
173    /// `super`(`super(...)` / `super.x`)。
174    Super,
175    /// カンマ演算子 `a, b, c`。全要素を順に評価し、最後の値を返す
176    /// (途中の要素の副作用も実行する。`for(i=0,j=10; ...)` 等で使われる)。
177    Sequence(Vec<Expression>),
178    /// `new.target`(MetaProperty。`new` 経由の呼び出しではコンストラクタ関数自身、
179    /// 通常呼び出しでは `undefined`)。
180    NewTarget,
181    /// 正規表現リテラル `/pattern/flags`。
182    Regex(String, String),
183}
184
185/// クラスメンバの種別。
186#[derive(Debug, Clone, PartialEq)]
187pub enum MethodKind {
188    Method,
189    Constructor,
190    /// フィールド宣言(`x = expr;` または `x;`、`#x = expr;` を含む)。
191    /// params/body は使わず、初期化式は ClassMember::field_init に持つ。
192    Field,
193    /// `get name() {...}`(アクセサ getter)。
194    Getter,
195    /// `set name(v) {...}`(アクセサ setter)。
196    Setter,
197    /// `static { ... }`(ES2022 静的初期化ブロック)。`body` に文の列を持つ
198    /// (`key`/`params`/`field_init` は使わない)。
199    StaticBlock,
200}
201
202/// クラスのメソッド/静的メソッド/フィールド宣言。
203#[derive(Debug, Clone, PartialEq)]
204pub struct ClassMember {
205    pub key: String,
206    /// 算出メソッド名 `[expr](){}`(`[Symbol.iterator]` 等)。Some の場合、実際のキーは
207    /// クラス定義時にこれを評価して得る(`key` フィールドは無視される)。
208    pub computed_key: Option<Expression>,
209    pub kind: MethodKind,
210    pub params: Rc<Vec<Param>>,
211    pub body: Rc<FnBody>,
212    pub is_static: bool,
213    /// kind == Field のときのみ Some(初期化式。省略時は None → undefined 初期化)。
214    pub field_init: Option<Expression>,
215    /// `async method(){}`(kind == Method のときのみ意味を持つ)。
216    pub is_async: bool,
217    /// `*method(){}`/`async *method(){}`(kind == Method のときのみ意味を持つ)。
218    pub is_generator: bool,
219}
220
221#[derive(Debug, Clone, PartialEq)]
222pub enum VarKind {
223    Var,
224    Let,
225    Const,
226    /// `using x = expr;`(Explicit Resource Management)。囲むブロック実行終了時に
227    /// `x[Symbol.dispose]()` を宣言の逆順で呼ぶ。
228    Using,
229    /// `await using x = expr;`。`x[Symbol.asyncDispose]()` の戻り値を await する。
230    AwaitUsing,
231}
232
233/// 束縛パターン(分割代入・引数)。
234#[derive(Debug, Clone, PartialEq)]
235pub enum Pattern {
236    Identifier(String),
237    /// `{ a, b: c, d = 1, ...rest }`
238    Object(Vec<ObjPatProp>),
239    /// `[ a, , b = 1, ...rest ]`
240    Array(Vec<ArrPatElem>),
241    /// メンバー/添字式への代入ターゲット(`for (obj.prop of arr)` / `for (obj[k] of arr)` 用)。
242    /// 分割代入の宣言では使わず、非宣言形式 for-of/for-in の代入先としてのみ生成される。
243    Expr(Box<Expression>),
244}
245
246/// オブジェクトパターンの 1 プロパティ。
247#[derive(Debug, Clone, PartialEq)]
248pub struct ObjPatProp {
249    /// 取り出すソースキー(`is_rest` の場合は未使用。`computed_key` が Some の場合も
250    /// 未使用で、実際のキーは束縛時に `computed_key` を評価して得る)。
251    pub key: String,
252    /// 算出プロパティ名 `{[expr]: target}`。Some の場合、`key` フィールドは無視される。
253    pub computed_key: Option<Expression>,
254    /// 束縛先パターン(`{a}` なら Identifier("a")、`{a: {b}}` ならネスト)。
255    pub value: Pattern,
256    pub default: Option<Expression>,
257    pub is_rest: bool,
258}
259
260/// 配列パターンの 1 要素。
261#[derive(Debug, Clone, PartialEq)]
262pub struct ArrPatElem {
263    /// None は穴 `[a, , b]`。
264    pub pattern: Option<Pattern>,
265    pub default: Option<Expression>,
266    pub is_rest: bool,
267}
268
269/// 関数パラメータ(パターン+デフォルト+レスト)。
270#[derive(Debug, Clone, PartialEq)]
271pub struct Param {
272    pub pattern: Pattern,
273    pub default: Option<Expression>,
274    pub is_rest: bool,
275}
276
277#[derive(Debug, Clone, PartialEq)]
278pub enum Statement {
279    Expression(Expression),
280    VarDeclaration {
281        kind: VarKind,
282        // 1 文で複数宣言可能: `let a = 1, b = 2;`。分割代入のため左辺は Pattern。
283        decls: Vec<(Pattern, Option<Expression>)>,
284    },
285    Block(Vec<Statement>),
286    If {
287        test: Expression,
288        consequent: Box<Statement>,
289        alternate: Option<Box<Statement>>,
290    },
291    While {
292        test: Expression,
293        body: Box<Statement>,
294    },
295    DoWhile {
296        body: Box<Statement>,
297        test: Expression,
298    },
299    For {
300        init: Option<Box<Statement>>,
301        test: Option<Expression>,
302        update: Option<Expression>,
303        body: Box<Statement>,
304    },
305    ForIn {
306        decl_kind: Option<VarKind>,
307        pattern: Pattern,
308        object: Expression,
309        body: Box<Statement>,
310        of: bool, // true: for-of, false: for-in
311        /// `for await (x of iterable)`。`Symbol.asyncIterator` があればそれを駆動し、
312        /// 無ければ同期イテラブルの各要素を await するフォールバック(`of` の時のみ意味を持つ)。
313        is_await: bool,
314    },
315    FunctionDeclaration {
316        name: String,
317        params: Rc<Vec<Param>>,
318        body: Rc<FnBody>,
319        is_async: bool,
320        is_generator: bool,
321    },
322    Return(Option<Expression>),
323    /// `break;` / `break label;`。
324    Break(Option<String>),
325    /// `continue;` / `continue label;`。
326    Continue(Option<String>),
327    /// `label: statement`(ラベル付き文。主にループの `break label`/`continue label` の
328    /// ターゲットとして使う)。
329    Labeled(String, Box<Statement>),
330    Throw(Expression),
331    Switch {
332        discriminant: Expression,
333        cases: Vec<SwitchCase>,
334    },
335    Try {
336        block: Vec<Statement>,
337        /// `catch (e)`/`catch ({message})` 等。分割代入パターンにも対応(ES2015)。
338        catch_param: Option<Pattern>,
339        catch_block: Option<Vec<Statement>>,
340        finally_block: Option<Vec<Statement>>,
341    },
342    /// `import ... from "spec"` 宣言。
343    /// 例: `import def, { a, b as c }, * as ns from "mod"`。
344    Import {
345        /// モジュール指定子(指定子文字列)。
346        source: String,
347        /// default インポートの束縛名(`import def from ...`)。
348        default: Option<String>,
349        /// 名前空間インポートの束縛名(`import * as ns from ...`)。
350        namespace: Option<String>,
351        /// 名前付きインポート: (元の export 名, ローカル束縛名)。
352        named: Vec<(String, String)>,
353        /// 副作用のみ `import "mod"`(束縛なし)。
354        side_effect_only: bool,
355    },
356    /// `export { a, b as c }` / `export { x } from "mod"`(再エクスポート)。
357    ExportNamed {
358        /// (ローカル名 or 元名, 公開する export 名)。
359        specifiers: Vec<(String, String)>,
360        /// 再エクスポート元の指定子(`export { x } from "mod"`)。None なら自モジュール。
361        source: Option<String>,
362    },
363    /// `export default <expr>`。
364    ExportDefault(Expression),
365    /// `export var/let/const/function/class ...`(宣言を実行しつつ名前を export)。
366    /// 内側の宣言文と、そこから公開する名前のリストを保持する。
367    ExportDecl {
368        declaration: Box<Statement>,
369        names: Vec<String>,
370    },
371    /// `export * from "mod"`(全名前の再エクスポート)。
372    ExportAll {
373        source: String,
374    },
375    Empty,
376}
377
378/// switch の 1 ケース。`test` が None なら default。
379#[derive(Debug, Clone, PartialEq)]
380pub struct SwitchCase {
381    pub test: Option<Expression>,
382    pub body: Vec<Statement>,
383}
384
385#[derive(Debug, Clone, PartialEq)]
386pub struct Program {
387    pub body: Vec<Statement>,
388}
389
390/// 関数本体。大きな本体は解析せずにソースだけ持ち、初めて実行するときに解析する(遅延解析)。
391///
392/// 【2026-10-17】YouTube のアプリ JS(10.9MB)・player(3MB)は関数の大半が呼ばれないのに、
393/// 全関数の構文木を作るとヒープ 512MB に収まらなかった。未解析の本体同士は入れ子に
394/// ならない(外側を解析した時点で内側が作られる)ので、保持する文字列の合計はソース以下。
395/// パーサは文脈(generator / async)に依存しないので、後から本体だけ解析しても結果は同じ。
396pub struct FnBody {
397    state: RefCell<FnBodyState>,
398    /// strict モードの本体か(`"use strict"` の指令・strict な外側・クラス)。
399    /// 非 strict の関数は this が undefined/null のときグローバル(window)になる。
400    pub strict: bool,
401    /// 本体(または内側のアロー関数)が `arguments` / `eval` を参照するか(0=未計算, 1=いいえ, 2=はい)。
402    uses_args: core::cell::Cell<u8>,
403}
404
405enum FnBodyState {
406    Parsed(Rc<Vec<Statement>>),
407    Source { src: Rc<String>, calls: u8 },
408}
409
410impl FnBody {
411    pub fn parsed(stmts: Vec<Statement>) -> Rc<Self> {
412        Self::parsed_with(stmts, false)
413    }
414
415    pub fn parsed_with(stmts: Vec<Statement>, strict: bool) -> Rc<Self> {
416        Rc::new(Self {
417            state: RefCell::new(FnBodyState::Parsed(Rc::new(stmts))),
418            strict,
419            uses_args: core::cell::Cell::new(0),
420        })
421    }
422
423    pub fn from_rc(stmts: Rc<Vec<Statement>>) -> Rc<Self> {
424        Rc::new(Self {
425            state: RefCell::new(FnBodyState::Parsed(stmts)),
426            strict: false,
427            uses_args: core::cell::Cell::new(0),
428        })
429    }
430
431    pub fn lazy(source: String, strict: bool) -> Rc<Self> {
432        Rc::new(Self {
433            state: RefCell::new(FnBodyState::Source {
434                src: Rc::new(source),
435                calls: 0,
436            }),
437            strict,
438            uses_args: core::cell::Cell::new(0),
439        })
440    }
441
442    /// まだ解析していないか。
443    pub fn is_lazy(&self) -> bool {
444        matches!(&*self.state.borrow(), FnBodyState::Source { .. })
445    }
446
447    /// 本体の文(初回は構文木を保持せず返し、2 回目以降だけ構文木に置き換える)。
448    ///
449    /// 【2026-10-19】従来は 1 回呼ばれただけの関数でも構文木(ソース文字列の約 5〜8 倍の
450    /// 大きさ)を `self.state` に永続保持していた。特に YouTube の `kevlar_base`(10.88MB)や
451    /// `base.js`(2.97MB)の最外の即時関数は 1 回しか実行されないのに、約 40MB の構文木が
452    /// クロージャ経由で最後まで残り続けていた。
453    /// - 64KB 以上の巨大な本体(バンドル全体の即時関数など)は初回でソースも構文木も手放す。
454    /// - それ以外は 1 回目の呼び出しではソースのまま残して構文木は呼び出し終了時に解放し、
455    ///   2 回目に呼ばれた関数だけ構文木をキャッシュする。
456    pub fn stmts(&self) -> Rc<Vec<Statement>> {
457        enum Action {
458            Cached(Rc<Vec<Statement>>),
459            ParseOnce(Rc<String>),
460            ParseAndDrop(Rc<String>),
461            ParseAndCache(Rc<String>),
462        }
463        let action = {
464            let mut b = self.state.borrow_mut();
465            match &mut *b {
466                FnBodyState::Parsed(s) => Action::Cached(s.clone()),
467                FnBodyState::Source { src, calls } => {
468                    if src.len() >= 65_536 {
469                        let s = src.clone();
470                        *b = FnBodyState::Parsed(Rc::new(Vec::new()));
471                        Action::ParseAndDrop(s)
472                    } else if *calls == 0 {
473                        *calls = 1;
474                        Action::ParseOnce(src.clone())
475                    } else {
476                        let s = src.clone();
477                        *b = FnBodyState::Parsed(Rc::new(Vec::new()));
478                        Action::ParseAndCache(s)
479                    }
480                }
481            }
482        };
483        match action {
484            Action::Cached(s) => s,
485            Action::ParseOnce(src) => {
486                Rc::new(super::parser::parse_function_body_source(&src, self.strict))
487            }
488            Action::ParseAndDrop(src) => {
489                let parsed = Rc::new(super::parser::parse_function_body_source(&src, self.strict));
490                drop(src);
491                parsed
492            }
493            Action::ParseAndCache(src) => {
494                let parsed = Rc::new(super::parser::parse_function_body_source(&src, self.strict));
495                drop(src);
496                *self.state.borrow_mut() = FnBodyState::Parsed(parsed.clone());
497                parsed
498            }
499        }
500    }
501
502    /// この関数本体(および内側のアロー関数)が `arguments` または `eval` を参照するか。
503    pub fn uses_arguments(&self, stmts: &[Statement]) -> bool {
504        match self.uses_args.get() {
505            1 => false,
506            2 => true,
507            _ => {
508                let u = stmts_use_arguments(stmts);
509                self.uses_args.set(if u { 2 } else { 1 });
510                u
511            }
512        }
513    }
514
515    fn raw_might_use_arguments(&self) -> bool {
516        match self.uses_args.get() {
517            1 => false,
518            2 => true,
519            _ => match &*self.state.borrow() {
520                FnBodyState::Source { src, .. } => {
521                    src.contains("arguments") || src.contains("eval")
522                }
523                FnBodyState::Parsed(s) => {
524                    let u = stmts_use_arguments(s);
525                    self.uses_args.set(if u { 2 } else { 1 });
526                    u
527                }
528            },
529        }
530    }
531}
532
533fn stmts_use_arguments(stmts: &[Statement]) -> bool {
534    stmts.iter().any(stmt_uses_arguments)
535}
536
537fn stmt_uses_arguments(stmt: &Statement) -> bool {
538    match stmt {
539        Statement::Expression(e)
540        | Statement::Return(Some(e))
541        | Statement::Throw(e)
542        | Statement::ExportDefault(e) => expr_uses_arguments(e),
543        Statement::VarDeclaration { decls, .. } => decls
544            .iter()
545            .any(|(p, init)| pat_uses_arguments(p) || init.as_ref().is_some_and(expr_uses_arguments)),
546        Statement::Block(b) => stmts_use_arguments(b),
547        Statement::If {
548            test,
549            consequent,
550            alternate,
551        } => {
552            expr_uses_arguments(test)
553                || stmt_uses_arguments(consequent)
554                || alternate.as_deref().is_some_and(stmt_uses_arguments)
555        }
556        Statement::While { test, body } | Statement::DoWhile { body, test } => {
557            expr_uses_arguments(test) || stmt_uses_arguments(body)
558        }
559        Statement::For {
560            init,
561            test,
562            update,
563            body,
564        } => {
565            init.as_deref().is_some_and(stmt_uses_arguments)
566                || test.as_ref().is_some_and(expr_uses_arguments)
567                || update.as_ref().is_some_and(expr_uses_arguments)
568                || stmt_uses_arguments(body)
569        }
570        Statement::ForIn {
571            pattern,
572            object,
573            body,
574            ..
575        } => {
576            pat_uses_arguments(pattern)
577                || expr_uses_arguments(object)
578                || stmt_uses_arguments(body)
579        }
580        Statement::Labeled(_, s) | Statement::ExportDecl { declaration: s, .. } => {
581            stmt_uses_arguments(s)
582        }
583        Statement::Switch {
584            discriminant,
585            cases,
586        } => {
587            expr_uses_arguments(discriminant)
588                || cases.iter().any(|c| {
589                    c.test.as_ref().is_some_and(expr_uses_arguments)
590                        || stmts_use_arguments(&c.body)
591                })
592        }
593        Statement::Try {
594            block,
595            catch_param,
596            catch_block,
597            finally_block,
598        } => {
599            stmts_use_arguments(block)
600                || catch_param.as_ref().is_some_and(pat_uses_arguments)
601                || catch_block.as_deref().is_some_and(stmts_use_arguments)
602                || finally_block.as_deref().is_some_and(stmts_use_arguments)
603        }
604        // 非アローの関数宣言は独自の `arguments` を持つため外側の `arguments` は参照しない。
605        Statement::FunctionDeclaration { .. }
606        | Statement::Return(None)
607        | Statement::Break(_)
608        | Statement::Continue(_)
609        | Statement::Import { .. }
610        | Statement::ExportNamed { .. }
611        | Statement::ExportAll { .. }
612        | Statement::Empty => false,
613    }
614}
615
616fn pat_uses_arguments(pat: &Pattern) -> bool {
617    match pat {
618        Pattern::Identifier(n) => n == "arguments" || n == "eval",
619        Pattern::Object(props) => props.iter().any(|p| {
620            p.computed_key.as_ref().is_some_and(expr_uses_arguments)
621                || pat_uses_arguments(&p.value)
622                || p.default.as_ref().is_some_and(expr_uses_arguments)
623        }),
624        Pattern::Array(elems) => elems.iter().any(|e| {
625            e.pattern.as_ref().is_some_and(pat_uses_arguments)
626                || e.default.as_ref().is_some_and(expr_uses_arguments)
627        }),
628        Pattern::Expr(e) => expr_uses_arguments(e),
629    }
630}
631
632fn expr_uses_arguments(expr: &Expression) -> bool {
633    match expr {
634        Expression::Identifier(n) => n == "arguments" || n == "eval",
635        Expression::TemplateLiteral { exprs, .. } | Expression::Array(exprs) | Expression::Sequence(exprs) => {
636            exprs.iter().any(expr_uses_arguments)
637        }
638        Expression::TaggedTemplate { tag, exprs, .. } => {
639            expr_uses_arguments(tag) || exprs.iter().any(expr_uses_arguments)
640        }
641        Expression::Object(props) => props.iter().any(|p| {
642            p.computed.as_ref().is_some_and(expr_uses_arguments) || expr_uses_arguments(&p.value)
643        }),
644        Expression::Function {
645            params,
646            body,
647            is_arrow,
648            ..
649        } => {
650            if !*is_arrow {
651                false
652            } else {
653                params.iter().any(|p| {
654                    pat_uses_arguments(&p.pattern)
655                        || p.default.as_ref().is_some_and(expr_uses_arguments)
656                }) || body.raw_might_use_arguments()
657            }
658        }
659        Expression::Await(e)
660        | Expression::Unary { expr: e, .. }
661        | Expression::Update { target: e, .. }
662        | Expression::Spread(e)
663        | Expression::Member { object: e, .. } => expr_uses_arguments(e),
664        Expression::Yield { argument, .. } => argument.as_deref().is_some_and(expr_uses_arguments),
665        Expression::Binary { left, right, .. }
666        | Expression::Logical { left, right, .. }
667        | Expression::Assign {
668            target: left,
669            value: right,
670            ..
671        }
672        | Expression::Index {
673            object: left,
674            index: right,
675            ..
676        } => expr_uses_arguments(left) || expr_uses_arguments(right),
677        Expression::Conditional {
678            test,
679            consequent,
680            alternate,
681        } => {
682            expr_uses_arguments(test)
683                || expr_uses_arguments(consequent)
684                || expr_uses_arguments(alternate)
685        }
686        Expression::Call {
687            callee, arguments, ..
688        }
689        | Expression::New { callee, arguments } => {
690            expr_uses_arguments(callee) || arguments.iter().any(expr_uses_arguments)
691        }
692        Expression::Class {
693            superclass,
694            members,
695            ..
696        } => {
697            superclass.as_deref().is_some_and(expr_uses_arguments)
698                || members.iter().any(|m| {
699                    m.computed_key.as_ref().is_some_and(expr_uses_arguments)
700                        || m.field_init.as_ref().is_some_and(expr_uses_arguments)
701                })
702        }
703        Expression::Number(_)
704        | Expression::BigIntLit(_)
705        | Expression::Str(_)
706        | Expression::Bool(_)
707        | Expression::Null
708        | Expression::Undefined
709        | Expression::This
710        | Expression::Super
711        | Expression::NewTarget
712        | Expression::Regex(..) => false,
713    }
714}
715
716impl core::fmt::Debug for FnBody {
717    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
718        match &*self.state.borrow() {
719            FnBodyState::Parsed(s) => write!(f, "FnBody({} stmts)", s.len()),
720            FnBodyState::Source { src, .. } => write!(f, "FnBody(lazy {} chars)", src.len()),
721        }
722    }
723}
724
725/// 同じ本体(同じ定義)かどうかで比べる。
726impl PartialEq for FnBody {
727    fn eq(&self, other: &Self) -> bool {
728        core::ptr::eq(self, other)
729    }
730}