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