Skip to main content

atmos/os_lib/js/interp/
exec.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    /// ステップを 1 消費。予算切れなら true(中断)。
8    pub(crate) fn tick(&mut self) -> bool {
9        self.steps += 1;
10        if self.steps > self.max_steps {
11            self.aborted = true;
12            return true;
13        }
14        false
15    }
16
17    /// ビルトインから使う public な例外生成。
18    pub fn error(&self, msg: impl Into<String>) -> Value {
19        self.throw(msg)
20    }
21
22    /// `it.error()`と同じだが`Error.prototype`ではなく`TypeError.prototype`へ
23    /// proto を繋ぐ(`name`文字列を`"TypeError"`へ書き換えるだけでは
24    /// `instanceof TypeError`が満たされない、`new Node()`等が投げる
25    /// "Illegal constructor" 例外で発覚したバグの修正用に新設。
26    /// 2026-07-18 発見・実装)。
27    pub fn type_error(&self, msg: impl Into<String>) -> Value {
28        let err = self.throw(msg);
29        if let Value::Object(o) = &err {
30            let msg_str = o.borrow().props.get("message").map(|v| v.to_js_string()).unwrap_or_default();
31            o.borrow_mut().props.insert(
32                String::from("stack"),
33                Value::str(super::super::builtins::build_error_stack("TypeError", &msg_str)),
34            );
35            o.borrow_mut().props.insert(String::from("name"), Value::str("TypeError"));
36            if let Some(Value::Object(ctor)) = self.global.borrow().vars.get("TypeError") {
37                if let Some(Value::Object(proto)) = ctor.borrow().props.get("prototype") {
38                    o.borrow_mut().proto = Some(proto.clone());
39                }
40            }
41        }
42        err
43    }
44
45    pub(crate) fn throw(&self, msg: impl Into<String>) -> Value {
46        // 簡易 Error オブジェクト(message プロパティ付き)。
47        let o = Obj::plain();
48        let msg: String = msg.into();
49        // `Error.prototype.stack`(丸ごと未対応だった。ツリーウォーク評価器
50        // なので実際のコールフレーム一覧は再現できず、V8 の1行目と同じ
51        // `"Error: message"` 形式の簡略実装。詳細は `builtins::
52        // build_error_stack` 参照。ここはエンジン内部が直接投げる大半の
53        // 例外(TypeError相当のメッセージ等)の共通経路のため、
54        // `set_error_props`(`new TypeError()` 等の明示コンストラクタ経由)
55        // とは別にここでも設定する)。
56        // 【2026-09-26】呼び出し中の関数名を V8 と同じ `    at 名前` 形式で積む(内側から、最大 12 段)。
57        let mut stack_str = if msg.is_empty() {
58            String::from("Error")
59        } else {
60            alloc::format!("Error: {msg}")
61        };
62        for name in self.call_names.iter().rev().take(12) {
63            stack_str.push_str("\n    at ");
64            stack_str.push_str(name);
65        }
66        o.borrow_mut().props.insert(String::from("stack"), Value::str(stack_str));
67        o.borrow_mut()
68            .props
69            .insert(String::from("message"), Value::str(msg));
70        o.borrow_mut()
71            .props
72            .insert(String::from("name"), Value::str("Error"));
73        // グローバルの `Error` コンストラクタが持つ `.prototype` に proto を繋ぐ。
74        // これによりエンジン内部から投げるエラー("X is not defined" 等)も
75        // `catch (e) { e instanceof Error }` で true になる。
76        if let Some(Value::Object(ctor)) = self.global.borrow().vars.get("Error") {
77            if let Some(Value::Object(proto)) = ctor.borrow().props.get("prototype") {
78                o.borrow_mut().proto = Some(proto.clone());
79            }
80        }
81        Value::Object(o)
82    }
83
84    // ============ 文 ============
85
86    /// グローバル `eval(source)` / `Function(...)` の実体。仕様上 direct eval は呼び出し元の
87    /// レキシカルスコープを見るが、この処理系は常にグローバルスコープで実行する簡略実装
88    /// (indirect eval `(0,eval)(code)` と同じセマンティクス)。
89    pub(crate) fn eval_source(&mut self, source: &str) -> Result<Value, Value> {
90        let mut parser = Parser::new(Lexer::new(source));
91        let program = parser.parse_program();
92        // `eval()` に渡されたソースの構文エラーも黙って捨てない
93        // (本来 SyntaxError を throw すべき箇所だが、既存のベストエフォート
94        //  実行を壊さないため、まずは可視化にとどめる)。
95        let errors = parser.error_count();
96        if errors > 0 {
97            crate::warn!("[JS] eval(): {} syntax error(s) recovered", errors);
98            for e in parser.errors().iter().take(3) {
99                crate::warn!("[JS] eval() SyntaxError: {}", e.message);
100            }
101        }
102        let scope = self.global.clone();
103        match self.exec_statements(&program.body, &scope, &Value::Undefined) {
104            Ok(Completion::Normal(v)) | Ok(Completion::Return(v)) => Ok(v),
105            Ok(_) => Ok(Value::Undefined),
106            Err(thrown) => Err(thrown),
107        }
108    }
109
110    pub(crate) fn exec_statements(
111        &mut self,
112        stmts: &[Statement],
113        scope: &Rc<RefCell<Scope>>,
114        this: &Value,
115    ) -> Result<Completion, Value> {
116        // 関数宣言の巻き上げ(`export function` でラップされたものも対象)。
117        for s in stmts {
118            let fdecl = match s {
119                Statement::FunctionDeclaration { .. } => Some(s),
120                Statement::ExportDecl { declaration, .. }
121                    if matches!(**declaration, Statement::FunctionDeclaration { .. }) =>
122                {
123                    Some(&**declaration)
124                }
125                _ => None,
126            };
127            if let Some(Statement::FunctionDeclaration {
128                name,
129                params,
130                body,
131                is_async,
132                is_generator,
133            }) = fdecl
134            {
135                let f = self.make_function(
136                    Some(name.clone()),
137                    params.clone(),
138                    body.clone(),
139                    false,
140                    *is_async,
141                    *is_generator,
142                    scope,
143                    this,
144                );
145                scope_declare(scope, name, f);
146            }
147        }
148        // `using`/`await using`(Explicit Resource Management): このステートメントリストの
149        // 実行が(正常終了・break/continue/return・例外のいずれで)終わっても、宣言の逆順で
150        // `[Symbol.dispose]`/`[Symbol.asyncDispose]` を呼ぶ。Phase 1 の簡略実装につき、
151        // 本来の「ブロックスコープ終了時」ではなく「この文リスト(関数本体/ブロック/
152        // プログラム本体)の実行終了時」を単位として扱う(ブロックが独自スコープを持たない
153        // という既存の制約と同じ粒度)。
154        let mut disposables: Vec<(Value, bool)> = Vec::new();
155        let mut last = Value::Undefined;
156        let mut outcome: Result<Completion, Value> = Ok(Completion::Normal(Value::Undefined));
157        for s in stmts {
158            if let Statement::VarDeclaration { kind, decls } = s {
159                if matches!(kind, VarKind::Using | VarKind::AwaitUsing) {
160                    let is_await = matches!(kind, VarKind::AwaitUsing);
161                    let mut bind_err = None;
162                    for (pat, init) in decls {
163                        let v = match init {
164                            Some(e) => match self.eval(e, scope, this) {
165                                Ok(v) => v,
166                                Err(e) => {
167                                    bind_err = Some(e);
168                                    break;
169                                }
170                            },
171                            None => Value::Undefined,
172                        };
173                        if let Err(e) = self.bind_pattern(pat, v.clone(), scope, this) {
174                            bind_err = Some(e);
175                            break;
176                        }
177                        if !matches!(v, Value::Undefined | Value::Null) {
178                            disposables.push((v, is_await));
179                        }
180                    }
181                    if let Some(e) = bind_err {
182                        outcome = Err(e);
183                        break;
184                    }
185                    last = Value::Undefined;
186                    continue;
187                }
188            }
189            match self.exec_stmt(s, scope, this) {
190                Ok(Completion::Normal(v)) => last = v,
191                Ok(other) => {
192                    outcome = Ok(other);
193                    break;
194                }
195                Err(e) => {
196                    outcome = Err(e);
197                    break;
198                }
199            }
200        }
201        if disposables.is_empty() {
202            return match outcome {
203                Ok(Completion::Normal(_)) => Ok(Completion::Normal(last)),
204                other => other,
205            };
206        }
207        // 宣言と逆順で dispose する。dispose 自体が投げた例外は、既存の outcome が
208        // 正常系ならそのまま採用し、既に別の例外が保留中なら `SuppressedError`
209        // (新しい例外を error、既存の保留中の例外を suppressed として包む)へ差し替える
210        // ことで、仕様どおり複数のエラーを1つのチェーンに集約する。
211        let mut pending_err: Option<Value> = match &outcome {
212            Err(e) => Some(e.clone()),
213            _ => None,
214        };
215        for (resource, is_await) in disposables.into_iter().rev() {
216            let key = if is_await {
217                "Symbol(Symbol.asyncDispose)"
218            } else {
219                "Symbol(Symbol.dispose)"
220            };
221            let method = match self.get_property(&resource, key) {
222                Ok(m) => m,
223                Err(e) => {
224                    pending_err = Some(self.chain_suppressed(pending_err, e));
225                    continue;
226                }
227            };
228            if matches!(method, Value::Undefined | Value::Null) {
229                continue;
230            }
231            let call_result = self.call_value(&method, resource, &[]);
232            let dispose_result = match call_result {
233                Ok(v) if is_await => self.await_value(v),
234                other => other,
235            };
236            if let Err(e) = dispose_result {
237                pending_err = Some(self.chain_suppressed(pending_err, e));
238            }
239        }
240        if let Some(e) = pending_err {
241            return Err(e);
242        }
243        match outcome {
244            Ok(Completion::Normal(_)) => Ok(Completion::Normal(last)),
245            other => other,
246        }
247    }
248
249    pub(crate) fn exec_stmt(
250        &mut self,
251        stmt: &Statement,
252        scope: &Rc<RefCell<Scope>>,
253        this: &Value,
254    ) -> Result<Completion, Value> {
255        if self.tick() {
256            return Err(Value::Undefined);
257        }
258        match stmt {
259            Statement::Empty => Ok(Completion::Normal(Value::Undefined)),
260            Statement::Expression(e) => {
261                let v = self.eval(e, scope, this)?;
262                Ok(Completion::Normal(v))
263            }
264            Statement::VarDeclaration { decls, .. } => {
265                for (pat, init) in decls {
266                    let v = match init {
267                        Some(e) => self.eval(e, scope, this)?,
268                        None => Value::Undefined,
269                    };
270                    // NamedEvaluation(ES2015): `const f = function(){}`/`const f = () => {}`
271                    // のような無名関数式を単純な識別子へ代入する場合、仕様上 `f.name` は
272                    // その識別子名を継承する。以前はこの推論が一切無く、`.name` が常に
273                    // 空文字列のままだった(デバッグ表示やスタックトレース、`.name` を
274                    // 参照するライブラリで無名関数が区別できない不便があった)。
275                    if let Pattern::Identifier(name) = pat {
276                        infer_function_name(&v, name);
277                    }
278                    self.bind_pattern(pat, v, scope, this)?;
279                }
280                Ok(Completion::Normal(Value::Undefined))
281            }
282            Statement::Block(body) => {
283                // Phase 1: ブロックは新スコープを作らず共有。
284                self.exec_statements(body, scope, this)
285            }
286            Statement::FunctionDeclaration { .. } => {
287                // 既に巻き上げ済み。
288                Ok(Completion::Normal(Value::Undefined))
289            }
290            Statement::If {
291                test,
292                consequent,
293                alternate,
294            } => {
295                if self.eval(test, scope, this)?.truthy() {
296                    self.exec_stmt(consequent, scope, this)
297                } else if let Some(alt) = alternate {
298                    self.exec_stmt(alt, scope, this)
299                } else {
300                    Ok(Completion::Normal(Value::Undefined))
301                }
302            }
303            Statement::While { test, body } => {
304                let own_label = self.pending_label.take();
305                while self.eval(test, scope, this)?.truthy() {
306                    if self.tick() {
307                        return Err(Value::Undefined);
308                    }
309                    match self.exec_stmt(body, scope, this)? {
310                        Completion::Break(lbl) if lbl.is_none() || lbl == own_label => break,
311                        Completion::Break(lbl) => return Ok(Completion::Break(lbl)),
312                        Completion::Continue(lbl) if lbl.is_none() || lbl == own_label => {}
313                        Completion::Continue(lbl) => return Ok(Completion::Continue(lbl)),
314                        Completion::Normal(_) => {}
315                        ret @ Completion::Return(_) => return Ok(ret),
316                    }
317                }
318                Ok(Completion::Normal(Value::Undefined))
319            }
320            Statement::DoWhile { body, test } => {
321                let own_label = self.pending_label.take();
322                loop {
323                    if self.tick() {
324                        return Err(Value::Undefined);
325                    }
326                    match self.exec_stmt(body, scope, this)? {
327                        Completion::Break(lbl) if lbl.is_none() || lbl == own_label => break,
328                        Completion::Break(lbl) => return Ok(Completion::Break(lbl)),
329                        Completion::Continue(lbl) if lbl.is_none() || lbl == own_label => {}
330                        Completion::Continue(lbl) => return Ok(Completion::Continue(lbl)),
331                        Completion::Normal(_) => {}
332                        ret @ Completion::Return(_) => return Ok(ret),
333                    }
334                    if !self.eval(test, scope, this)?.truthy() {
335                        break;
336                    }
337                }
338                Ok(Completion::Normal(Value::Undefined))
339            }
340            Statement::For {
341                init,
342                test,
343                update,
344                body,
345            } => {
346                let own_label = self.pending_label.take();
347                // `for (let i=0; ...)` は仕様上「反復ごとの束縛」(per-iteration binding)
348                // が必須で、各反復の本体内で作られたクロージャはその反復時点の値を捕捉
349                // しなければならない。以前は `init`/`test`/`body`/`update` を通して単一の
350                // 共有スコープしか使っておらず、`for(let i=0;i<3;i++){arr.push(()=>i)}` の
351                // ようなクロージャが全て同じ最終値(3)を見てしまうバグだった
352                // (`var` はこの per-iteration 束縛の対象外で、以前と同じ単一スコープの
353                // ままで仕様どおり)。
354                let is_let_like = matches!(
355                    init.as_deref(),
356                    Some(Statement::VarDeclaration {
357                        kind: VarKind::Let | VarKind::Const,
358                        ..
359                    })
360                );
361                let mut loop_scope = if is_let_like {
362                    Scope::child(scope.clone())
363                } else {
364                    scope.clone()
365                };
366                if let Some(init) = init {
367                    self.exec_stmt(init, &loop_scope, this)?;
368                }
369                let let_names: Vec<String> = if is_let_like {
370                    loop_scope.borrow().vars.keys().cloned().collect()
371                } else {
372                    Vec::new()
373                };
374                loop {
375                    if let Some(t) = test {
376                        if !self.eval(t, &loop_scope, this)?.truthy() {
377                            break;
378                        }
379                    }
380                    if self.tick() {
381                        return Err(Value::Undefined);
382                    }
383                    match self.exec_stmt(body, &loop_scope, this)? {
384                        Completion::Break(lbl) if lbl.is_none() || lbl == own_label => break,
385                        Completion::Break(lbl) => return Ok(Completion::Break(lbl)),
386                        Completion::Continue(lbl) if lbl.is_none() || lbl == own_label => {}
387                        Completion::Continue(lbl) => return Ok(Completion::Continue(lbl)),
388                        Completion::Normal(_) => {}
389                        ret @ Completion::Return(_) => return Ok(ret),
390                    }
391                    if is_let_like {
392                        let next_scope = Scope::child(scope.clone());
393                        for name in &let_names {
394                            let v = scope_get(&loop_scope, name).unwrap_or(Value::Undefined);
395                            next_scope.borrow_mut().vars.insert(name.clone(), v);
396                        }
397                        loop_scope = next_scope;
398                    }
399                    if let Some(u) = update {
400                        self.eval(u, &loop_scope, this)?;
401                    }
402                }
403                Ok(Completion::Normal(Value::Undefined))
404            }
405            Statement::ForIn {
406                decl_kind,
407                pattern,
408                object,
409                body,
410                of,
411                is_await,
412            } => {
413                let own_label = self.pending_label.take();
414                // `for (let x of iterable)` も C 形式の `for` と同じ「反復ごとの束縛」が
415                // 必須(`var`/宣言無しの代入先は対象外)。以前は毎回同じ `scope` へ
416                // `bind_pattern` していたため、本体で作ったクロージャが全て最終値を
417                // 捕捉するバグだった。
418                let is_let_like = matches!(decl_kind, Some(VarKind::Let) | Some(VarKind::Const));
419                let obj = self.eval(object, scope, this)?;
420                // for await (x of obj): Symbol.asyncIterator があれば手動で駆動(next() の
421                // 戻り値を await し {value, done} を読む)。無ければ同期イテラブルへ
422                // フォールバックし各要素を await する(配列などの Promise 混在を想定)。
423                if *of && *is_await {
424                    let async_iter_method =
425                        self.get_property(&obj, "Symbol(Symbol.asyncIterator)")?;
426                    if !matches!(async_iter_method, Value::Undefined | Value::Null) {
427                        let iterator = self.call_value(&async_iter_method, obj, &[])?;
428                        loop {
429                            if self.tick() {
430                                return Err(Value::Undefined);
431                            }
432                            let next_fn = self.get_property(&iterator, "next")?;
433                            let result = self.call_value(&next_fn, iterator.clone(), &[])?;
434                            let awaited = self.await_value(result)?;
435                            let done = self.get_property(&awaited, "done")?.truthy();
436                            if done {
437                                break;
438                            }
439                            let value = self.get_property(&awaited, "value")?;
440                            let iter_scope = if is_let_like { Scope::child(scope.clone()) } else { scope.clone() };
441                            self.bind_pattern(pattern, value, &iter_scope, this)?;
442                            match self.exec_stmt(body, &iter_scope, this)? {
443                                Completion::Break(lbl) if lbl.is_none() || lbl == own_label => {
444                                    break
445                                }
446                                Completion::Break(lbl) => return Ok(Completion::Break(lbl)),
447                                Completion::Continue(lbl)
448                                    if lbl.is_none() || lbl == own_label => {}
449                                Completion::Continue(lbl) => return Ok(Completion::Continue(lbl)),
450                                Completion::Normal(_) => {}
451                                ret @ Completion::Return(_) => return Ok(ret),
452                            }
453                        }
454                        return Ok(Completion::Normal(Value::Undefined));
455                    }
456                    let items = self.iterate_values(&obj, true);
457                    for item in items {
458                        if self.tick() {
459                            return Err(Value::Undefined);
460                        }
461                        let awaited = self.await_value(item)?;
462                        let iter_scope = if is_let_like { Scope::child(scope.clone()) } else { scope.clone() };
463                        self.bind_pattern(pattern, awaited, &iter_scope, this)?;
464                        match self.exec_stmt(body, &iter_scope, this)? {
465                            Completion::Break(lbl) if lbl.is_none() || lbl == own_label => break,
466                            Completion::Break(lbl) => return Ok(Completion::Break(lbl)),
467                            Completion::Continue(lbl) if lbl.is_none() || lbl == own_label => {}
468                            Completion::Continue(lbl) => return Ok(Completion::Continue(lbl)),
469                            Completion::Normal(_) => {}
470                            ret @ Completion::Return(_) => return Ok(ret),
471                        }
472                    }
473                    return Ok(Completion::Normal(Value::Undefined));
474                }
475                let items = self.iterate_values(&obj, *of);
476                for item in items {
477                    if self.tick() {
478                        return Err(Value::Undefined);
479                    }
480                    let iter_scope = if is_let_like { Scope::child(scope.clone()) } else { scope.clone() };
481                    self.bind_pattern(pattern, item, &iter_scope, this)?;
482                    match self.exec_stmt(body, &iter_scope, this)? {
483                        Completion::Break(lbl) if lbl.is_none() || lbl == own_label => break,
484                        Completion::Break(lbl) => return Ok(Completion::Break(lbl)),
485                        Completion::Continue(lbl) if lbl.is_none() || lbl == own_label => {}
486                        Completion::Continue(lbl) => return Ok(Completion::Continue(lbl)),
487                        Completion::Normal(_) => {}
488                        ret @ Completion::Return(_) => return Ok(ret),
489                    }
490                }
491                Ok(Completion::Normal(Value::Undefined))
492            }
493            Statement::Return(e) => {
494                let v = match e {
495                    Some(e) => self.eval(e, scope, this)?,
496                    None => Value::Undefined,
497                };
498                Ok(Completion::Return(v))
499            }
500            Statement::Break(label) => Ok(Completion::Break(label.clone())),
501            Statement::Continue(label) => Ok(Completion::Continue(label.clone())),
502            Statement::Labeled(label, inner) => {
503                // このラベルが(直下の)ループ自身のものであることをループ実行側へ伝える。
504                // ループはその実行開始直後に `take()` して own_label として使う。
505                self.pending_label = Some(label.clone());
506                match self.exec_stmt(inner, scope, this)? {
507                    // 自ラベル宛ての break はここで消費して正常終了に変換する
508                    // (ループではない `label: { ... }` のようなブロックの break にも対応)。
509                    Completion::Break(Some(lbl)) if lbl == *label => {
510                        Ok(Completion::Normal(Value::Undefined))
511                    }
512                    other => Ok(other),
513                }
514            }
515            Statement::Switch {
516                discriminant,
517                cases,
518            } => {
519                let d = self.eval(discriminant, scope, this)?;
520                // マッチする case を探す。無ければ default。フォールスルーあり。
521                let mut start = None;
522                for (i, c) in cases.iter().enumerate() {
523                    if let Some(test) = &c.test {
524                        let tv = self.eval(test, scope, this)?;
525                        if d.strict_eq(&tv) {
526                            start = Some(i);
527                            break;
528                        }
529                    }
530                }
531                if start.is_none() {
532                    start = cases.iter().position(|c| c.test.is_none());
533                }
534                if let Some(s) = start {
535                    for c in &cases[s..] {
536                        match self.exec_statements(&c.body, scope, this)? {
537                            // ラベル無しの break のみこの switch を抜ける。ラベル付き break は
538                            // この switch 宛てではない(外側のラベル付きループ/ブロック宛て)ため
539                            // 上位へ伝播する。
540                            Completion::Break(None) => {
541                                return Ok(Completion::Normal(Value::Undefined))
542                            }
543                            Completion::Normal(_) => {} // フォールスルー
544                            other => return Ok(other), // Return/Continue/ラベル付きBreak は上位へ
545                        }
546                    }
547                }
548                Ok(Completion::Normal(Value::Undefined))
549            }
550            Statement::Throw(e) => {
551                let v = self.eval(e, scope, this)?;
552                Err(v)
553            }
554            Statement::Try {
555                block,
556                catch_param,
557                catch_block,
558                finally_block,
559            } => {
560                let result = self.exec_statements(block, scope, this);
561                let after_catch = match result {
562                    // suspend 番兵(generator 中断)および強制Return例外は捕捉せず最上位の resume まで巻き戻す。
563                    Err(thrown) if !self.aborted && !self.is_suspending() && !self.is_gen_returning() => {
564                        if let Some(cb) = catch_block {
565                            if let Some(p) = catch_param {
566                                self.bind_pattern(p, thrown, scope, this)?;
567                            }
568                            self.exec_statements(cb, scope, this)
569                        } else {
570                            Err(thrown)
571                        }
572                    }
573                    other => other,
574                };
575                // generator 中断中は finally を実行せず巻き戻す(resume 時に replay される)。
576                if self.is_suspending() {
577                    return after_catch;
578                }
579                if let Some(fb) = finally_block {
580                    // finally は常に実行。finally が return/throw すればそれが優先。
581                    let fin = self.exec_statements(fb, scope, this)?;
582                    if let Completion::Return(_) | Completion::Break(_) | Completion::Continue(_) = fin {
583                        if let Some(r) = &mut self.gen_replay {
584                            r.returning = None;
585                        }
586                        return Ok(fin);
587                    }
588                }
589                after_catch
590            }
591            Statement::Import {
592                source,
593                default,
594                namespace,
595                named,
596                side_effect_only,
597            } => {
598                let exports = self.load_module(source)?;
599                if !side_effect_only {
600                    let exp = exports.borrow();
601                    if let Some(local) = default {
602                        let v = exp.get("default").cloned().unwrap_or(Value::Undefined);
603                        scope_declare(scope, local, v);
604                    }
605                    if let Some(ns) = namespace {
606                        // 名前空間オブジェクト: 全 export を props に持つ plain オブジェクト。
607                        let o = Obj::plain();
608                        {
609                            let mut ob = o.borrow_mut();
610                            for (k, v) in exp.iter() {
611                                ob.props.insert(k.clone(), v.clone());
612                            }
613                        }
614                        scope_declare(scope, ns, Value::Object(o));
615                    }
616                    for (orig, local) in named {
617                        let v = exp.get(orig).cloned().unwrap_or(Value::Undefined);
618                        scope_declare(scope, local, v);
619                    }
620                }
621                Ok(Completion::Normal(Value::Undefined))
622            }
623            Statement::ExportDefault(expr) => {
624                let v = self.eval(expr, scope, this)?;
625                self.record_export(scope, "default", v);
626                Ok(Completion::Normal(Value::Undefined))
627            }
628            Statement::ExportDecl { declaration, names } => {
629                let c = self.exec_stmt(declaration, scope, this)?;
630                // 宣言実行後、対象名をスコープから引いて export に転記する。
631                for name in names {
632                    let v = scope_get(scope, name).unwrap_or(Value::Undefined);
633                    self.record_export(scope, name, v);
634                }
635                Ok(c)
636            }
637            Statement::ExportNamed { specifiers, source } => {
638                match source {
639                    // 再エクスポート: `export { a, b as c } from "mod"`。
640                    Some(src) => {
641                        let exports = self.load_module(src)?;
642                        let exp = exports.borrow();
643                        for (orig, public) in specifiers {
644                            let v = exp.get(orig).cloned().unwrap_or(Value::Undefined);
645                            self.record_export(scope, public, v);
646                        }
647                    }
648                    // 自モジュールのローカル束縛を公開: `export { a, b as c }`。
649                    None => {
650                        for (local, public) in specifiers {
651                            let v = scope_get(scope, local).unwrap_or(Value::Undefined);
652                            self.record_export(scope, public, v);
653                        }
654                    }
655                }
656                Ok(Completion::Normal(Value::Undefined))
657            }
658            Statement::ExportAll { source } => {
659                let exports = self.load_module(source)?;
660                let exp = exports.borrow();
661                for (k, v) in exp.iter() {
662                    // `export *` は default を再エクスポートしない。
663                    if k != "default" {
664                        self.record_export(scope, k, v.clone());
665                    }
666                }
667                Ok(Completion::Normal(Value::Undefined))
668            }
669        }
670    }
671
672    /// 現在評価中モジュールの export マップにエントリを記録する。
673    /// モジュール文脈でない(通常スクリプト)の場合は無視する。
674    pub(crate) fn record_export(&mut self, scope: &Rc<RefCell<Scope>>, name: &str, val: Value) {
675        // モジュールの export マップは評価対象スコープに番兵キーで保持される。
676        if let Some(Value::Object(o)) = scope_get(scope, MODULE_EXPORTS_KEY) {
677            o.borrow_mut().props.insert(String::from(name), val);
678        }
679    }
680
681    /// 指定子のモジュールを(必要なら)評価し、export マップを返す。
682    /// 循環 import に備え、評価開始前に空マップをレジストリへ入れておく。
683    pub(crate) fn load_module(
684        &mut self,
685        specifier: &str,
686    ) -> Result<Rc<RefCell<BTreeMap<String, Value>>>, Value> {
687        // 既に評価済みならそのまま返す。
688        if let Some(rec) = self.modules.borrow().get(specifier) {
689            if let Some(ex) = &rec.exports {
690                return Ok(ex.clone());
691            }
692        }
693        // ソースを取り出す(未登録なら空エクスポートのモジュール扱い)。
694        let source = match self.modules.borrow().get(specifier) {
695            Some(rec) => rec.source.clone(),
696            None => {
697                let empty = Rc::new(RefCell::new(BTreeMap::new()));
698                return Ok(empty);
699            }
700        };
701        // 循環対策: 評価開始前に空マップを登録。
702        let exports = Rc::new(RefCell::new(BTreeMap::new()));
703        if let Some(rec) = self.modules.borrow_mut().get_mut(specifier) {
704            rec.exports = Some(exports.clone());
705        }
706        // モジュール本体を専用スコープで評価。export 文の記録先として
707        // exports マップを props に持つ plain オブジェクトを番兵キーで束縛する。
708        let module_scope = Scope::child(self.global.clone());
709        let collector = Obj::plain();
710        scope_declare(
711            &module_scope,
712            MODULE_EXPORTS_KEY,
713            Value::Object(collector.clone()),
714        );
715        let program = Parser::new(Lexer::new(&source)).parse_program();
716        let _ = self.exec_statements(&program.body, &module_scope, &Value::Undefined)?;
717        // collector の props を exports マップへ確定。
718        {
719            let cb = collector.borrow();
720            let mut ex = exports.borrow_mut();
721            for (k, v) in cb.props.iter() {
722                ex.insert(k.clone(), v.clone());
723            }
724        }
725        Ok(exports)
726    }
727
728}