Skip to main content

atmos/os_lib/js/builtins/
errors_misc.rs

1// 分割: builtins.rs より機械的に移動(2026-07-16 リファクタ フェーズ2)。
2// ロジック不変。可視性のみ pub(crate) へ昇格し、親が pub(crate) use で再エクスポート。
3use super::*;
4
5// ===== Error 階層(Error / TypeError / RangeError / SyntaxError)=====
6// `new` 経由なら `construct_object` が `this`(`t`)に既にコンストラクタの `.prototype` を
7// proto として設定済みなので、それを使い回して message/name を書き込む
8// (`new TypeError() instanceof Error` 等が成立するのはこの proto 連鎖のおかげ)。
9// `new` を介さない素の呼び出し(`Error(msg)` 等)では `t` がオブジェクトでないため、
10// 独立したプレーンオブジェクトへフォールバックする(proto 無しの簡略動作)。
11/// `name`/`message` から `Error.prototype.stack` の値を組み立てる。この処理系は
12/// ツリーウォーク評価器で呼び出しフレームスタックを保持していないため、実際の
13/// コールスタックのフレーム一覧は再現できない(V8 等が返す複数行のトレースは
14/// 非対応)。多くの実用コードは「`.stack` が空でない文字列で、エラー内容の説明で
15/// 始まる」ことしか当てにしないため、V8 の1行目と同じ `"{name}: {message}"`
16/// 形式(`message` 空なら `name` のみ)を返す簡略実装とする
17/// (2026-07-14、丸ごと未対応だったのを発見・実装)。
18pub(crate) fn build_error_stack(name: &str, msg: &str) -> String {
19    if msg.is_empty() {
20        String::from(name)
21    } else {
22        alloc::format!("{name}: {msg}")
23    }
24}
25pub(crate) fn set_error_props(it: &Interp, t: Value, msg: String, name: &str) -> Value {
26    match &t {
27        Value::Object(o) => {
28            let mut b = o.borrow_mut();
29            b.props.insert("stack".into(), Value::str(build_error_stack(name, &msg)));
30            b.props.insert("message".into(), Value::str(msg));
31            b.props.insert("name".into(), Value::str(name));
32            drop(b);
33            t
34        }
35        _ => {
36            // `new` を介さない素の呼び出し。`it.error()` と同じ経路で proto を
37            // `Error.prototype` に繋いでおく(`new` 経由と挙動を揃える)。
38            let stack = build_error_stack(name, &msg);
39            let err = it.error(msg);
40            if let Value::Object(o) = &err {
41                let mut b = o.borrow_mut();
42                b.props.insert("name".into(), Value::str(name));
43                b.props.insert("stack".into(), Value::str(stack));
44            }
45            err
46        }
47    }
48}
49/// `new Error(msg, {cause})`(ES2022 のエラーオプション。原因となった別の例外を連鎖させる
50/// 定番イディオム `catch(e){ throw new Error('wrap', {cause: e}) }` で使われる)。
51/// `options` が `cause` プロパティを(値に関わらず)持つ場合のみ `err.cause` を設定する
52/// (`in` 相当。`cause` キー自体が無ければ設定しない、が仕様上の挙動)。
53pub(crate) fn apply_error_cause(err: &Value, options: &Value) -> Value {
54    if let Value::Object(opt_obj) = options {
55        let has_cause = opt_obj.borrow().props.contains_key("cause");
56        if has_cause {
57            let cause = opt_obj.borrow().props.get("cause").cloned().unwrap_or(Value::Undefined);
58            if let Value::Object(o) = err {
59                o.borrow_mut().props.insert("cause".into(), cause);
60            }
61        }
62    }
63    err.clone()
64}
65pub(crate) fn error_ctor(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
66    let err = set_error_props(it, t, arg(a, 0).to_js_string(), "Error");
67    Ok(apply_error_cause(&err, &arg(a, 1)))
68}
69pub(crate) fn type_error_ctor(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
70    let err = set_error_props(it, t, arg(a, 0).to_js_string(), "TypeError");
71    Ok(apply_error_cause(&err, &arg(a, 1)))
72}
73pub(crate) fn range_error_ctor(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
74    let err = set_error_props(it, t, arg(a, 0).to_js_string(), "RangeError");
75    Ok(apply_error_cause(&err, &arg(a, 1)))
76}
77pub(crate) fn syntax_error_ctor(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
78    let err = set_error_props(it, t, arg(a, 0).to_js_string(), "SyntaxError");
79    Ok(apply_error_cause(&err, &arg(a, 1)))
80}
81/// `SuppressedError(error, suppressed, message)`(ES2023→ES2026、`using` の複数 dispose 失敗を
82/// 集約するための型)。`error`/`suppressed` プロパティを持つ Error オブジェクトを返す。
83pub(crate) fn suppressed_error_ctor(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
84    let err = set_error_props(it, t, arg(a, 2).to_js_string(), "SuppressedError");
85    if let Value::Object(o) = &err {
86        let mut b = o.borrow_mut();
87        b.props.insert("error".into(), arg(a, 0));
88        b.props.insert("suppressed".into(), arg(a, 1));
89    }
90    Ok(err)
91}
92/// `new AggregateError(errors, message)`(ES2021)が丸ごと欠落していた。`Promise.any` が
93/// 全て reject したときに投げるべき例外型で、`.errors`(全 reject 理由の配列)を持つ。
94pub(crate) fn aggregate_error_ctor(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
95    let errors_list = it.iter_to_vec(&arg(a, 0));
96    let msg = arg(a, 1).to_js_string();
97    let err = set_error_props(it, t, msg, "AggregateError");
98    if let Value::Object(o) = &err {
99        o.borrow_mut().props.insert("errors".into(), Value::Object(Obj::array(errors_list)));
100    }
101    Ok(err)
102}
103/// `Error.isError(value)`(ES2025)。`value instanceof Error` と違い、`Symbol.hasInstance`
104/// の上書きや `Error.prototype` の差し替えに影響されず、常に組込み `error_proto` を
105/// proto 連鎖から探す(`instanceof` と同じ仕組みを使うが対象を固定 proto に限定)。
106/// これが本来の存在意義(他レルムの Error でも正しく判定できる)に近い、単一レルムの
107/// この処理系での妥当な近似実装。
108pub(crate) fn error_is_error(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
109    let error_proto = match it.global.borrow().vars.get("Error") {
110        Some(Value::Object(ctor)) => match ctor.borrow().props.get("prototype") {
111            Some(Value::Object(p)) => Some(p.clone()),
112            _ => None,
113        },
114        _ => None,
115    };
116    let error_proto = match error_proto {
117        Some(p) => p,
118        None => return Ok(Value::Bool(false)),
119    };
120    let mut cur = match arg(a, 0) {
121        Value::Object(o) => Some(o),
122        _ => None,
123    };
124    let mut guard = 0;
125    while let Some(c) = cur {
126        if Rc::ptr_eq(&c, &error_proto) {
127            return Ok(Value::Bool(true));
128        }
129        cur = c.borrow().proto.clone();
130        guard += 1;
131        if guard > 1000 {
132            break;
133        }
134    }
135    Ok(Value::Bool(false))
136}
137/// Rust 側(`Promise.any` の内部実装)から直接 `AggregateError` インスタンスを組み立てる
138/// ヘルパ。`new` 式を経由できないため、`AggregateError` のプロトタイプへ手動で繋ぐ。
139pub(crate) fn make_aggregate_error(it: &Interp, errors: Vec<Value>, msg: &str) -> Value {
140    let o = Obj::plain();
141    o.borrow_mut().props.insert("message".into(), Value::str(msg));
142    o.borrow_mut().props.insert("name".into(), Value::str("AggregateError"));
143    o.borrow_mut().props.insert("errors".into(), Value::Object(Obj::array(errors)));
144    if let Some(Value::Object(ctor)) = it.global.borrow().vars.get("AggregateError") {
145        if let Some(Value::Object(proto)) = ctor.borrow().props.get("prototype") {
146            o.borrow_mut().proto = Some(proto.clone());
147        }
148    }
149    Value::Object(o)
150}
151
152// ===== FinalizationRegistry(GC 非搭載 → register は no-op)=====
153pub(crate) fn fr_register(_: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
154    Ok(Value::Undefined)
155}
156pub(crate) fn fr_unregister(_: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
157    Ok(Value::Bool(false))
158}
159pub(crate) fn finalization_registry_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
160    let cb = arg(a, 0);
161    let obj = Obj::plain();
162    {
163        let mut b = obj.borrow_mut();
164        b.props.insert("_cb".into(), cb);
165        b.props.insert("register".into(), nv("register", fr_register));
166        b.props.insert("unregister".into(), nv("unregister", fr_unregister));
167        b.props.insert("cleanupSome".into(), nv("cleanupSome", dom_noop));
168    }
169    Ok(Value::Object(obj))
170}
171
172// ===== requestIdleCallback(即時発火。IdleDeadline.timeRemaining() → 50ms 固定)=====
173pub(crate) fn idle_time_remaining(_: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
174    Ok(Value::Number(50.0))
175}
176pub(crate) fn request_idle_callback(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
177    let cb = arg(a, 0);
178    let deadline = Obj::plain();
179    {
180        let mut b = deadline.borrow_mut();
181        b.props.insert("didTimeout".into(), Value::Bool(false));
182        b.props.insert("timeRemaining".into(), nv("timeRemaining", idle_time_remaining));
183    }
184    it.call_listener(
185        &cb,
186        Value::Undefined,
187        &[Value::Object(deadline)],
188        "requestIdleCallback callback",
189    );
190    Ok(Value::Number(0.0))
191}
192
193// ===== Object.hasOwn(Object.prototype.hasOwnProperty の静的版)=====
194pub(crate) fn object_has_own_static(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
195    let obj = arg(a, 0);
196    let key = arg(a, 1).to_js_string();
197    if let Value::Object(o) = &obj {
198        return Ok(Value::Bool(has_own_property(o, &key)));
199    }
200    Ok(Value::Bool(false))
201}
202
203// ===== Object.groupBy(配列→キー関数でグループ化。ES2024)=====
204pub(crate) fn object_group_by(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
205    let items = it.iter_to_vec(&arg(a, 0));
206    let f = arg(a, 1);
207    let result = Obj::plain();
208    // 仕様上`Object.groupBy`の戻り値は`[[Prototype]]`が`null`のオブジェクト
209    // (`OrdinaryObjectCreate(null)`)。2026-07-18 発見・実装。
210    result.borrow_mut().null_proto = true;
211    for (i, v) in items.iter().enumerate() {
212        let key = it
213            .call_value(&f, Value::Undefined, &[v.clone(), Value::Number(i as f64)])?
214            .to_js_string();
215        let mut b = result.borrow_mut();
216        let entry = b
217            .props
218            .entry(key)
219            .or_insert_with(|| Value::Object(Obj::array(alloc::vec![])));
220        if let Value::Object(arr) = entry {
221            if let ObjKind::Array(vec) = &mut arr.borrow_mut().kind {
222                vec.push(v.clone());
223            }
224        }
225    }
226    Ok(Value::Object(result))
227}
228
229/// `Map.groupBy(items, callbackFn)`(ES2024)。`Object.groupBy` と異なりキーを文字列化せず、
230/// 任意の値(オブジェクト・数値等)をそのままキーにできる(`same_value_zero` で同一性判定)。
231pub(crate) fn map_group_by(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
232    let items: Vec<Value> = it.iter_to_vec(&arg(a, 0));
233    let f = arg(a, 1);
234    let mut entries: Vec<(Value, Value)> = Vec::new();
235    for (i, v) in items.iter().enumerate() {
236        let key = it.call_value(&f, Value::Undefined, &[v.clone(), Value::Number(i as f64)])?;
237        if let Some(slot) = entries.iter_mut().find(|(ek, _)| same_value_zero(ek, &key)) {
238            if let Value::Object(arr) = &slot.1 {
239                if let ObjKind::Array(vec) = &mut arr.borrow_mut().kind {
240                    vec.push(v.clone());
241                }
242            }
243        } else {
244            entries.push((key, Value::Object(Obj::array(alloc::vec![v.clone()]))));
245        }
246    }
247    Ok(Value::Object(Obj::map_obj(entries)))
248}