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