Skip to main content

atmos/os_lib/js/builtins/
extra_static.rs

1// 分割: builtins.rs より機械的に移動(2026-07-16 リファクタ フェーズ2)。
2// ロジック不変。可視性のみ pub(crate) へ昇格し、親が pub(crate) use で再エクスポート。
3use super::*;
4
5// ===== 追加 Math(libm)=====
6pub(crate) fn math_log2(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
7    Ok(Value::Number(libm::log2(arg(a, 0).to_number())))
8}
9pub(crate) fn math_log10(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
10    Ok(Value::Number(libm::log10(arg(a, 0).to_number())))
11}
12pub(crate) fn math_cbrt(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
13    Ok(Value::Number(libm::cbrt(arg(a, 0).to_number())))
14}
15pub(crate) fn math_tan(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
16    Ok(Value::Number(libm::tan(arg(a, 0).to_number())))
17}
18pub(crate) fn math_atan(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
19    Ok(Value::Number(libm::atan(arg(a, 0).to_number())))
20}
21pub(crate) fn math_asin(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
22    Ok(Value::Number(libm::asin(arg(a, 0).to_number())))
23}
24pub(crate) fn math_acos(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
25    Ok(Value::Number(libm::acos(arg(a, 0).to_number())))
26}
27pub(crate) fn math_atan2(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
28    Ok(Value::Number(libm::atan2(
29        arg(a, 0).to_number(),
30        arg(a, 1).to_number(),
31    )))
32}
33pub(crate) fn math_hypot(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
34    let sum: f64 = a
35        .iter()
36        .map(|v| {
37            let x = v.to_number();
38            x * x
39        })
40        .sum();
41    Ok(Value::Number(libm::sqrt(sum)))
42}
43/// `Math.fround(x)`(ES2015)が丸ごと未対応だった。32bit 単精度浮動小数点として
44/// 表現可能な最も近い値へ丸める(WebGL/Canvas 等で GPU 側の float32 精度に
45/// 合わせる定番イディオム)。`f64 -> f32 -> f64` の往復キャストで単精度の
46/// 丸め誤差を再現する。
47pub(crate) fn math_fround(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
48    Ok(Value::Number(arg(a, 0).to_number() as f32 as f64))
49}
50/// `Math.f16round(x)`(ES2025)。半精度 16bit 浮動小数点数(IEEE 754 half-precision float)
51/// として表現可能な最も近い値へ丸める(Float16Array と同等の丸め)。
52pub(crate) fn math_f16round(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
53    let n = arg(a, 0).to_number();
54    let bits = super::typed_array::f64_to_f16_bits(n);
55    Ok(Value::Number(super::typed_array::f16_bits_to_f64(bits)))
56}
57/// `Math.clamp(x, min, max)`(ECMAScript Proposal Stage 3/4)。
58/// 数値 x を [min, max] の範囲内に制限する。
59pub(crate) fn math_clamp(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
60    let val = arg(a, 0).to_number();
61    let min = arg(a, 1).to_number();
62    let max = arg(a, 2).to_number();
63    if val.is_nan() || min.is_nan() || max.is_nan() || min > max {
64        Ok(Value::Number(f64::NAN))
65    } else {
66        Ok(Value::Number(val.max(min).min(max)))
67    }
68}
69/// `Math.clz32(x)`(ES2015)が丸ごと未対応だった。`x` を仕様どおり `ToUint32`
70/// (下位32bitへ切り詰め)してから先頭の連続 0 ビット数を返す(`x=0` は 32)。
71pub(crate) fn math_clz32(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
72    let n = arg(a, 0).to_number();
73    let u = if n.is_finite() { n as i64 as u32 } else { 0 };
74    Ok(Value::Number(u.leading_zeros() as f64))
75}
76/// `Math.imul(a, b)`(ES2015)が丸ごと未対応だった。両引数を `ToInt32` してから
77/// 32bit 整数の乗算をラップアラウンド(オーバーフロー時は下位32bitのみ保持)で
78/// 行う。通常の浮動小数点乗算は 2^53 を超えると精度落ちするため、64bit整数の
79/// 大きな値を正確に掛け合わせたい場面で使われる。
80pub(crate) fn math_imul(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
81    let x = arg(a, 0).to_number() as i64 as i32;
82    let y = arg(a, 1).to_number() as i64 as i32;
83    Ok(Value::Number(x.wrapping_mul(y) as f64))
84}
85pub(crate) fn math_sinh(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
86    Ok(Value::Number(libm::sinh(arg(a, 0).to_number())))
87}
88pub(crate) fn math_cosh(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
89    Ok(Value::Number(libm::cosh(arg(a, 0).to_number())))
90}
91pub(crate) fn math_tanh(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
92    Ok(Value::Number(libm::tanh(arg(a, 0).to_number())))
93}
94pub(crate) fn math_asinh(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
95    Ok(Value::Number(libm::asinh(arg(a, 0).to_number())))
96}
97pub(crate) fn math_acosh(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
98    Ok(Value::Number(libm::acosh(arg(a, 0).to_number())))
99}
100pub(crate) fn math_atanh(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
101    Ok(Value::Number(libm::atanh(arg(a, 0).to_number())))
102}
103pub(crate) fn math_expm1(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
104    Ok(Value::Number(libm::expm1(arg(a, 0).to_number())))
105}
106pub(crate) fn math_log1p(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
107    Ok(Value::Number(libm::log1p(arg(a, 0).to_number())))
108}
109/// `Math.sumPrecise(iterable)`(ES2025 Standard)。
110/// 反復可能オブジェクト(Array, Set, Map Iterator 等)内のすべての数値を正確に合計する。
111/// 特殊値 (NaN, +Inf/-Inf の混在) や IEEE 754 の丸め誤差を Kahan-Babuška-Neumaier 補正加算で精密に処理。
112pub(crate) fn math_sum_precise(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
113    let source = arg(a, 0);
114    let items = it.iter_to_vec(&source);
115
116    let mut has_nan = false;
117    let mut has_pos_inf = false;
118    let mut has_neg_inf = false;
119
120    let mut numbers = alloc::vec::Vec::with_capacity(items.len());
121    for item in items {
122        let n = item.to_number();
123        if n.is_nan() {
124            has_nan = true;
125        } else if n == f64::INFINITY {
126            has_pos_inf = true;
127        } else if n == f64::NEG_INFINITY {
128            has_neg_inf = true;
129        } else {
130            numbers.push(n);
131        }
132    }
133
134    if has_nan || (has_pos_inf && has_neg_inf) {
135        return Ok(Value::Number(f64::NAN));
136    }
137    if has_pos_inf {
138        return Ok(Value::Number(f64::INFINITY));
139    }
140    if has_neg_inf {
141        return Ok(Value::Number(f64::NEG_INFINITY));
142    }
143
144    // Neumaier 加算アルゴリズム (Kahan-Babuška-Neumaier Summation)
145    let mut sum = 0.0f64;
146    let mut c = 0.0f64;
147    for x in numbers {
148        let t = sum + x;
149        if libm::fabs(sum) >= libm::fabs(x) {
150            c += (sum - t) + x;
151        } else {
152            c += (x - t) + sum;
153        }
154        sum = t;
155    }
156    Ok(Value::Number(sum + c))
157}
158
159// ===== 追加 Object 静的 =====
160/// `Object.getPrototypeOf(obj)`(ES5)。`Reflect.getPrototypeOf` と同じロジックだが、
161/// こちらは `instanceof`/クラス継承の確認によく使われる、はるかに一般的な API。
162pub(crate) fn object_get_prototype_of(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
163    if let Value::Object(o) = arg(a, 0) {
164        return Ok(effective_proto(it, &o));
165    }
166    Ok(Value::Null)
167}
168
169/// オブジェクトのプロトタイプ(`Object.getPrototypeOf` の結果)。
170///
171/// この処理系は `{}`・配列・関数に組込みのプロトタイプを `proto` として持たせず、メソッドを
172/// 実体から動的に解決する。そのため `proto` が無く `null_proto` でもないオブジェクトは、
173/// 種類に応じた `Array.prototype`/`Function.prototype`/`Object.prototype` を返す
174/// (`Object.prototype` 自身は null)。【2026-10-17】従来は null を返し、Redux の
175/// `isPlainObject`(連鎖の終端と `getPrototypeOf(x)` を比べる)が `{}` を弾いていた(YouTube player)。
176pub(crate) fn effective_proto(it: &Interp, o: &Rc<RefCell<Obj>>) -> Value {
177    match implicit_proto(it, o) {
178        Some(p) => Value::Object(p),
179        None => Value::Null,
180    }
181}
182
183/// グローバルのコンストラクタ `name` の `prototype`。
184pub(crate) fn global_ctor_proto(it: &Interp, name: &str) -> Option<Rc<RefCell<Obj>>> {
185    match it.global.borrow().vars.get(name) {
186        Some(Value::Object(c)) => match c.borrow().props.get("prototype") {
187            Some(Value::Object(p)) => Some(p.clone()),
188            _ => None,
189        },
190        _ => None,
191    }
192}
193
194/// `o` のプロトタイプ(明示的な `proto`、無ければ種類に応じた組込みの prototype、
195/// その先は `Object.prototype`。`Object.prototype` 自身と `null_proto` は None)。
196/// `Object.getPrototypeOf` と `instanceof` が共有する。【2026-10-17】`instanceof` は
197/// 配列・素のオブジェクト以外の組込み(Promise・Map 等)を辿れず、`p instanceof Promise`
198/// が偽になっていた(YouTube の遅延コンポーネントが Promise を要素名として描こうとした)。
199pub(crate) fn implicit_proto(it: &Interp, o: &Rc<RefCell<Obj>>) -> Option<Rc<RefCell<Obj>>> {
200    let (explicit, null_proto, ctor_name) = {
201        let b = o.borrow();
202        let name = match &b.kind {
203            ObjKind::Array(_) => Some("Array"),
204            ObjKind::PromiseObj(_) => Some("Promise"),
205            ObjKind::MapObj(_) => Some("Map"),
206            ObjKind::SetObj(_) => Some("Set"),
207            ObjKind::RegExpObj(_) => Some("RegExp"),
208            ObjKind::DateObj(_) => Some("Date"),
209            _ if b.is_callable() => Some("Function"),
210            _ => None,
211        };
212        (b.proto.clone(), b.null_proto, name)
213    };
214    if explicit.is_some() {
215        return explicit;
216    }
217    if null_proto {
218        return None;
219    }
220    // DOM 要素のハンドルは原型を要素側(タグ・昇格したクラス)から引く。
221    let dom_idx = match o.borrow().kind {
222        ObjKind::DomElement(i) => Some(i),
223        _ => None,
224    };
225    if let Some(i) = dom_idx {
226        if let Some(p) = it.get_dom_node_proto(i) {
227            return Some(p);
228        }
229    }
230    let object_proto = global_ctor_proto(it, "Object")?;
231    if Rc::ptr_eq(&object_proto, o) {
232        return None;
233    }
234    match ctor_name.and_then(|n| global_ctor_proto(it, n)) {
235        Some(p) if !Rc::ptr_eq(&p, o) => Some(p),
236        _ => Some(object_proto),
237    }
238}
239/// `Object.setPrototypeOf(obj, proto)`(ES6)。`proto` が `null` ならプロトタイプ連鎖を切る。
240pub(crate) fn object_set_prototype_of(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
241    let target = arg(a, 0);
242    if let Value::Object(o) = &target {
243        // DOM 要素のハンドルは作り直されるので、原型は要素側に持たせる。
244        let dom_idx = match o.borrow().kind {
245            ObjKind::DomElement(i) => Some(i),
246            _ => None,
247        };
248        if let (Some(i), Value::Object(p)) = (dom_idx, arg(a, 1)) {
249            it.dom.borrow_mut().node_protos.insert(i, p);
250            return Ok(target);
251        }
252        match arg(a, 1) {
253            Value::Object(p) => {
254                let mut b = o.borrow_mut();
255                b.proto = Some(p);
256                b.null_proto = false;
257            }
258            Value::Null => {
259                let mut b = o.borrow_mut();
260                b.proto = None;
261                // `setPrototypeOf(obj, null)`(`Object.create(null)`と同じ、
262                // 明示的なnullプロトタイプ化。2026-07-18 発見・実装)。
263                b.null_proto = true;
264            }
265            _ => {}
266        }
267    }
268    Ok(target)
269}
270pub(crate) fn object_from_entries(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
271    let o = Obj::plain();
272    for pair in it.iter_to_vec(&arg(a, 0)) {
273        let k = key_at(&pair, 0).to_js_string();
274        let v = key_at(&pair, 1);
275        o.borrow_mut().props.insert(k, v);
276    }
277    Ok(Value::Object(o))
278}
279/// `Object.freeze(obj)`。以前は「凍結は no-op(同一オブジェクトを返すだけ)」という
280/// 明示的な簡略化だったが、`set_property()`/`delete` 側に `frozen` フラグの実チェックを
281/// 追加したことで、実際にプロパティの追加・変更・削除を黙殺するようになった。
282pub(crate) fn object_freeze(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
283    let v = arg(a, 0);
284    if let Value::Object(o) = &v {
285        o.borrow_mut().frozen = true;
286    }
287    Ok(v)
288}
289/// `Object.isFrozen(obj)`。プリミティブは仕様どおり常に true(凍結済み扱い)。
290pub(crate) fn object_is_frozen(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
291    Ok(Value::Bool(match arg(a, 0) {
292        Value::Object(o) => o.borrow().frozen,
293        _ => true,
294    }))
295}
296/// `Object.seal(obj)`(`freeze` より弱い: 新規プロパティの追加・既存プロパティの削除のみ
297/// 禁止し、既存値の変更は許可する)。`freeze` と同様、以前は丸ごと欠落していた。
298pub(crate) fn object_seal(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
299    let v = arg(a, 0);
300    if let Value::Object(o) = &v {
301        o.borrow_mut().sealed = true;
302    }
303    Ok(v)
304}
305pub(crate) fn object_is_sealed(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
306    Ok(Value::Bool(match arg(a, 0) {
307        Value::Object(o) => {
308            let b = o.borrow();
309            b.frozen || b.sealed
310        }
311        _ => true,
312    }))
313}
314/// `Object.preventExtensions(obj)`(`seal` よりさらに弱い: 新規プロパティの追加のみ禁止)。
315pub(crate) fn object_prevent_extensions(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
316    let v = arg(a, 0);
317    if let Value::Object(o) = &v {
318        o.borrow_mut().non_extensible = true;
319    }
320    Ok(v)
321}
322pub(crate) fn object_is_extensible(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
323    Ok(Value::Bool(match arg(a, 0) {
324        Value::Object(o) => {
325            let b = o.borrow();
326            !(b.frozen || b.sealed || b.non_extensible)
327        }
328        _ => false,
329    }))
330}
331/// `Object.is(a, b)`(ES2015、SameValue)。`===`/SameValueZero と違い、`NaN` 同士は
332/// 等しいと判定し、かつ `+0`/`-0` は区別して等しくないと判定する(両方の性質を持つのは
333/// SameValue だけ。SameValueZero は `+0`/`-0` を区別しない)。
334pub(crate) fn object_is(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
335    let x = arg(a, 0);
336    let y = arg(a, 1);
337    let result = match (&x, &y) {
338        (Value::Number(nx), Value::Number(ny)) => {
339            if nx.is_nan() && ny.is_nan() {
340                true
341            } else if *nx == 0.0 && *ny == 0.0 {
342                nx.is_sign_positive() == ny.is_sign_positive()
343            } else {
344                nx == ny
345            }
346        }
347        _ => x.strict_eq(&y),
348    };
349    Ok(Value::Bool(result))
350}
351pub(crate) fn object_create(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
352    // Object.create(proto[, propertiesObject]): proto を持つ新規オブジェクトを返す。
353    let obj = Obj::plain();
354    // proto がオブジェクトならプロトタイプ連鎖に設定。Object.create(null) はプロト無し
355    // (かつ`hasOwnProperty`等のフォールバックメソッドも一切持たない、明示的な
356    // nullプロトタイプであることを`null_proto`でマークする。2026-07-18 発見・実装)。
357    match arg(a, 0) {
358        Value::Object(p) => obj.borrow_mut().proto = Some(p),
359        Value::Null => obj.borrow_mut().null_proto = true,
360        _ => {}
361    }
362    // 第2引数 `propertiesObject`(`Object.defineProperties` と同じ記述子マップ形式)が
363    // 以前は完全に無視されていた。`Object.create(proto, {x:{value:1}})` のような
364    // 定番パターンで `x` が一切定義されないバグだった。
365    let props = arg(a, 1);
366    if let Value::Object(d) = &props {
367        let keys: alloc::vec::Vec<String> = d.borrow().props.keys().map(Clone::clone).collect();
368        for key in keys {
369            let desc = obj_prop(&props, &key).unwrap_or(Value::Undefined);
370            apply_define_property(it, &Value::Object(obj.clone()), &key, &desc);
371        }
372    }
373    Ok(Value::Object(obj))
374}
375
376// ===== Object.defineProperty / getOwnPropertyDescriptor =====
377
378pub(crate) fn apply_define_property(it: &mut Interp, obj: &Value, key: &str, desc: &Value) {
379    let Value::Object(o) = obj else { return };
380    let o = unwrap_proxy_target(o);
381    
382    let writable = obj_prop(desc, "writable").map(|v| v.truthy());
383    let configurable = obj_prop(desc, "configurable").map(|v| v.truthy());
384    let enumerable = obj_prop(desc, "enumerable").map(|v| v.truthy());
385
386    let is_array = matches!(&o.borrow().kind, ObjKind::Array(_));
387    if is_array {
388        if key == "length" {
389            if let Some(v) = obj_prop(desc, "value") {
390                let new_len = v.to_number().max(0.0) as usize;
391                if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
392                    items.resize(new_len, Value::Undefined);
393                }
394            }
395            return;
396        }
397        if let Ok(idx) = key.parse::<usize>() {
398            if let Some(v) = obj_prop(desc, "value") {
399                if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
400                    if idx >= items.len() {
401                        items.resize(idx + 1, Value::Undefined);
402                    }
403                    items[idx] = v;
404                }
405            }
406            return;
407        }
408    }
409    let get = obj_prop(desc, "get");
410    let set = obj_prop(desc, "set");
411    let has_value = obj_prop(desc, "value").is_some();
412    
413    let is_new = !o.borrow().props.contains_key(key) && !o.borrow().accessors.contains_key(key);
414    let attr = if is_new {
415        super::super::value::PropertyAttributes {
416            writable: writable.unwrap_or(false),
417            configurable: configurable.unwrap_or(false),
418            enumerable: enumerable.unwrap_or(false),
419        }
420    } else {
421        let prev = o.borrow().attrs.get(key).copied().unwrap_or_default();
422        super::super::value::PropertyAttributes {
423            writable: writable.unwrap_or(prev.writable),
424            configurable: configurable.unwrap_or(prev.configurable),
425            enumerable: enumerable.unwrap_or(prev.enumerable),
426        }
427    };
428    
429    if get.is_some() || set.is_some() {
430        // アクセサ記述子。
431        use super::super::value::Accessor;
432        let getter = get.filter(|v| matches!(v, Value::Object(f) if f.borrow().is_callable()));
433        let setter = set.filter(|v| matches!(v, Value::Object(f) if f.borrow().is_callable()));
434        o.borrow_mut().accessors.insert(
435            alloc::string::String::from(key),
436            Accessor { get: getter, set: setter },
437        );
438        o.borrow_mut().props.shift_remove(key);
439    } else if has_value {
440        // データ記述子。
441        let val = obj_prop(desc, "value").unwrap_or(Value::Undefined);
442        o.borrow_mut().props.insert(alloc::string::String::from(key), val);
443        o.borrow_mut().accessors.shift_remove(key);
444    }
445    
446    o.borrow_mut().attrs.insert(alloc::string::String::from(key), attr);
447    let _ = it;
448}
449
450pub(crate) fn object_define_property(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
451    let obj = arg(a, 0);
452    let key = arg(a, 1).to_js_string();
453    let desc = arg(a, 2);
454    apply_define_property(it, &obj, &key, &desc);
455    Ok(obj)
456}
457
458pub(crate) fn object_define_properties(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
459    let obj = arg(a, 0);
460    let descs = arg(a, 1);
461    if let Value::Object(d) = &descs {
462        let keys: alloc::vec::Vec<String> = d.borrow().props.keys().map(Clone::clone).collect();
463        for key in keys {
464            let desc = obj_prop(&descs, &key).unwrap_or(Value::Undefined);
465            apply_define_property(it, &obj, &key, &desc);
466        }
467    }
468    Ok(obj)
469}
470
471pub(crate) fn make_property_descriptor(obj: &super::super::value::ObjRef, key: &str) -> Value {
472    let obj = unwrap_proxy_target(obj);
473    let b = obj.borrow();
474    let attr = b.attrs.get(key).copied().unwrap_or_default();
475    
476    if let ObjKind::Array(items) = &b.kind {
477        if key == "length" {
478            let desc = Obj::plain();
479            desc.borrow_mut().props.insert("value".into(), Value::Number(items.len() as f64));
480            desc.borrow_mut().props.insert("writable".into(), Value::Bool(true));
481            desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(false));
482            desc.borrow_mut().props.insert("configurable".into(), Value::Bool(false));
483            return Value::Object(desc);
484        }
485        if let Ok(idx) = key.parse::<usize>() {
486            if let Some(v) = items.get(idx) {
487                let desc = Obj::plain();
488                desc.borrow_mut().props.insert("value".into(), v.clone());
489                desc.borrow_mut().props.insert("writable".into(), Value::Bool(true));
490                desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(true));
491                desc.borrow_mut().props.insert("configurable".into(), Value::Bool(true));
492                return Value::Object(desc);
493            }
494            return Value::Undefined;
495        }
496    }
497    if let Some(acc) = b.accessors.get(key) {
498        let desc = Obj::plain();
499        if let Some(g) = &acc.get {
500            desc.borrow_mut().props.insert("get".into(), g.clone());
501        } else {
502            desc.borrow_mut().props.insert("get".into(), Value::Undefined);
503        }
504        if let Some(s) = &acc.set {
505            desc.borrow_mut().props.insert("set".into(), s.clone());
506        } else {
507            desc.borrow_mut().props.insert("set".into(), Value::Undefined);
508        }
509        desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(attr.enumerable));
510        desc.borrow_mut().props.insert("configurable".into(), Value::Bool(attr.configurable));
511        Value::Object(desc)
512    } else if let Some(v) = b.props.get(key) {
513        let desc = Obj::plain();
514        desc.borrow_mut().props.insert("value".into(), v.clone());
515        desc.borrow_mut().props.insert("writable".into(), Value::Bool(attr.writable));
516        desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(attr.enumerable));
517        desc.borrow_mut().props.insert("configurable".into(), Value::Bool(attr.configurable));
518        Value::Object(desc)
519    } else {
520        Value::Undefined
521    }
522}
523
524pub(crate) fn object_get_own_property_descriptor(
525    _it: &mut Interp,
526    _t: Value,
527    a: &[Value],
528) -> Result<Value, Value> {
529    let Value::Object(o) = arg(a, 0) else { return Ok(Value::Undefined) };
530    let key = arg(a, 1).to_js_string();
531    Ok(make_property_descriptor(&o, &key))
532}
533
534pub(crate) fn object_get_own_property_descriptors(
535    _it: &mut Interp,
536    _t: Value,
537    a: &[Value],
538) -> Result<Value, Value> {
539    let Value::Object(o) = arg(a, 0) else { return Ok(Value::Object(Obj::plain())) };
540    let result = Obj::plain();
541    let keys: alloc::vec::Vec<String> = {
542        let b = o.borrow();
543        if let ObjKind::Array(items) = &b.kind {
544            let mut ks: alloc::vec::Vec<String> = (0..items.len()).map(|i| i.to_string()).collect();
545            ks.push(String::from("length"));
546            ks
547        } else {
548            let mut ks: alloc::vec::Vec<String> = b.props.keys().map(Clone::clone).collect();
549            for k in b.accessors.keys() {
550                if !ks.contains(k) {
551                    ks.push(k.clone());
552                }
553            }
554            ks
555        }
556    };
557    for key in keys {
558        let desc = make_property_descriptor(&o, &key);
559        result.borrow_mut().props.insert(key, desc);
560    }
561    Ok(Value::Object(result))
562}
563
564// ===== 追加 Number 静的 =====
565pub(crate) fn number_is_integer(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
566    Ok(Value::Bool(match arg(a, 0) {
567        Value::Number(x) => x.is_finite() && libm::floor(x) == x,
568        _ => false,
569    }))
570}
571pub(crate) fn number_is_safe_integer(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
572    Ok(Value::Bool(match arg(a, 0) {
573        Value::Number(x) => {
574            x.is_finite() && libm::floor(x) == x && libm::fabs(x) <= 9007199254740991.0
575        }
576        _ => false,
577    }))
578}
579pub(crate) fn number_is_finite_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
580    Ok(Value::Bool(
581        matches!(arg(a, 0), Value::Number(x) if x.is_finite()),
582    ))
583}
584pub(crate) fn number_is_nan_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
585    Ok(Value::Bool(
586        matches!(arg(a, 0), Value::Number(x) if x.is_nan()),
587    ))
588}
589