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(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
163    if let Value::Object(o) = arg(a, 0) {
164        return Ok(match o.borrow().proto.clone() {
165            Some(p) => Value::Object(p),
166            None => Value::Null,
167        });
168    }
169    Ok(Value::Null)
170}
171/// `Object.setPrototypeOf(obj, proto)`(ES6)。`proto` が `null` ならプロトタイプ連鎖を切る。
172pub(crate) fn object_set_prototype_of(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
173    let target = arg(a, 0);
174    if let Value::Object(o) = &target {
175        match arg(a, 1) {
176            Value::Object(p) => {
177                let mut b = o.borrow_mut();
178                b.proto = Some(p);
179                b.null_proto = false;
180            }
181            Value::Null => {
182                let mut b = o.borrow_mut();
183                b.proto = None;
184                // `setPrototypeOf(obj, null)`(`Object.create(null)`と同じ、
185                // 明示的なnullプロトタイプ化。2026-07-18 発見・実装)。
186                b.null_proto = true;
187            }
188            _ => {}
189        }
190    }
191    Ok(target)
192}
193pub(crate) fn object_from_entries(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
194    let o = Obj::plain();
195    for pair in it.iter_to_vec(&arg(a, 0)) {
196        let k = key_at(&pair, 0).to_js_string();
197        let v = key_at(&pair, 1);
198        o.borrow_mut().props.insert(k, v);
199    }
200    Ok(Value::Object(o))
201}
202/// `Object.freeze(obj)`。以前は「凍結は no-op(同一オブジェクトを返すだけ)」という
203/// 明示的な簡略化だったが、`set_property()`/`delete` 側に `frozen` フラグの実チェックを
204/// 追加したことで、実際にプロパティの追加・変更・削除を黙殺するようになった。
205pub(crate) fn object_freeze(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
206    let v = arg(a, 0);
207    if let Value::Object(o) = &v {
208        o.borrow_mut().frozen = true;
209    }
210    Ok(v)
211}
212/// `Object.isFrozen(obj)`。プリミティブは仕様どおり常に true(凍結済み扱い)。
213pub(crate) fn object_is_frozen(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
214    Ok(Value::Bool(match arg(a, 0) {
215        Value::Object(o) => o.borrow().frozen,
216        _ => true,
217    }))
218}
219/// `Object.seal(obj)`(`freeze` より弱い: 新規プロパティの追加・既存プロパティの削除のみ
220/// 禁止し、既存値の変更は許可する)。`freeze` と同様、以前は丸ごと欠落していた。
221pub(crate) fn object_seal(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
222    let v = arg(a, 0);
223    if let Value::Object(o) = &v {
224        o.borrow_mut().sealed = true;
225    }
226    Ok(v)
227}
228pub(crate) fn object_is_sealed(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
229    Ok(Value::Bool(match arg(a, 0) {
230        Value::Object(o) => {
231            let b = o.borrow();
232            b.frozen || b.sealed
233        }
234        _ => true,
235    }))
236}
237/// `Object.preventExtensions(obj)`(`seal` よりさらに弱い: 新規プロパティの追加のみ禁止)。
238pub(crate) fn object_prevent_extensions(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
239    let v = arg(a, 0);
240    if let Value::Object(o) = &v {
241        o.borrow_mut().non_extensible = true;
242    }
243    Ok(v)
244}
245pub(crate) fn object_is_extensible(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
246    Ok(Value::Bool(match arg(a, 0) {
247        Value::Object(o) => {
248            let b = o.borrow();
249            !(b.frozen || b.sealed || b.non_extensible)
250        }
251        _ => false,
252    }))
253}
254/// `Object.is(a, b)`(ES2015、SameValue)。`===`/SameValueZero と違い、`NaN` 同士は
255/// 等しいと判定し、かつ `+0`/`-0` は区別して等しくないと判定する(両方の性質を持つのは
256/// SameValue だけ。SameValueZero は `+0`/`-0` を区別しない)。
257pub(crate) fn object_is(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
258    let x = arg(a, 0);
259    let y = arg(a, 1);
260    let result = match (&x, &y) {
261        (Value::Number(nx), Value::Number(ny)) => {
262            if nx.is_nan() && ny.is_nan() {
263                true
264            } else if *nx == 0.0 && *ny == 0.0 {
265                nx.is_sign_positive() == ny.is_sign_positive()
266            } else {
267                nx == ny
268            }
269        }
270        _ => x.strict_eq(&y),
271    };
272    Ok(Value::Bool(result))
273}
274pub(crate) fn object_create(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
275    // Object.create(proto[, propertiesObject]): proto を持つ新規オブジェクトを返す。
276    let obj = Obj::plain();
277    // proto がオブジェクトならプロトタイプ連鎖に設定。Object.create(null) はプロト無し
278    // (かつ`hasOwnProperty`等のフォールバックメソッドも一切持たない、明示的な
279    // nullプロトタイプであることを`null_proto`でマークする。2026-07-18 発見・実装)。
280    match arg(a, 0) {
281        Value::Object(p) => obj.borrow_mut().proto = Some(p),
282        Value::Null => obj.borrow_mut().null_proto = true,
283        _ => {}
284    }
285    // 第2引数 `propertiesObject`(`Object.defineProperties` と同じ記述子マップ形式)が
286    // 以前は完全に無視されていた。`Object.create(proto, {x:{value:1}})` のような
287    // 定番パターンで `x` が一切定義されないバグだった。
288    let props = arg(a, 1);
289    if let Value::Object(d) = &props {
290        let keys: alloc::vec::Vec<String> = d.borrow().props.keys().map(Clone::clone).collect();
291        for key in keys {
292            let desc = obj_prop(&props, &key).unwrap_or(Value::Undefined);
293            apply_define_property(it, &Value::Object(obj.clone()), &key, &desc);
294        }
295    }
296    Ok(Value::Object(obj))
297}
298
299// ===== Object.defineProperty / getOwnPropertyDescriptor =====
300
301pub(crate) fn apply_define_property(it: &mut Interp, obj: &Value, key: &str, desc: &Value) {
302    let Value::Object(o) = obj else { return };
303    let o = unwrap_proxy_target(o);
304    
305    let writable = obj_prop(desc, "writable").map(|v| v.truthy());
306    let configurable = obj_prop(desc, "configurable").map(|v| v.truthy());
307    let enumerable = obj_prop(desc, "enumerable").map(|v| v.truthy());
308
309    let is_array = matches!(&o.borrow().kind, ObjKind::Array(_));
310    if is_array {
311        if key == "length" {
312            if let Some(v) = obj_prop(desc, "value") {
313                let new_len = v.to_number().max(0.0) as usize;
314                if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
315                    items.resize(new_len, Value::Undefined);
316                }
317            }
318            return;
319        }
320        if let Ok(idx) = key.parse::<usize>() {
321            if let Some(v) = obj_prop(desc, "value") {
322                if let ObjKind::Array(items) = &mut o.borrow_mut().kind {
323                    if idx >= items.len() {
324                        items.resize(idx + 1, Value::Undefined);
325                    }
326                    items[idx] = v;
327                }
328            }
329            return;
330        }
331    }
332    let get = obj_prop(desc, "get");
333    let set = obj_prop(desc, "set");
334    let has_value = obj_prop(desc, "value").is_some();
335    
336    let is_new = !o.borrow().props.contains_key(key) && !o.borrow().accessors.contains_key(key);
337    let attr = if is_new {
338        super::super::value::PropertyAttributes {
339            writable: writable.unwrap_or(false),
340            configurable: configurable.unwrap_or(false),
341            enumerable: enumerable.unwrap_or(false),
342        }
343    } else {
344        let prev = o.borrow().attrs.get(key).copied().unwrap_or_default();
345        super::super::value::PropertyAttributes {
346            writable: writable.unwrap_or(prev.writable),
347            configurable: configurable.unwrap_or(prev.configurable),
348            enumerable: enumerable.unwrap_or(prev.enumerable),
349        }
350    };
351    
352    if get.is_some() || set.is_some() {
353        // アクセサ記述子。
354        use super::super::value::Accessor;
355        let getter = get.filter(|v| matches!(v, Value::Object(f) if f.borrow().is_callable()));
356        let setter = set.filter(|v| matches!(v, Value::Object(f) if f.borrow().is_callable()));
357        o.borrow_mut().accessors.insert(
358            alloc::string::String::from(key),
359            Accessor { get: getter, set: setter },
360        );
361        o.borrow_mut().props.shift_remove(key);
362    } else if has_value {
363        // データ記述子。
364        let val = obj_prop(desc, "value").unwrap_or(Value::Undefined);
365        o.borrow_mut().props.insert(alloc::string::String::from(key), val);
366        o.borrow_mut().accessors.shift_remove(key);
367    }
368    
369    o.borrow_mut().attrs.insert(alloc::string::String::from(key), attr);
370    let _ = it;
371}
372
373pub(crate) fn object_define_property(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
374    let obj = arg(a, 0);
375    let key = arg(a, 1).to_js_string();
376    let desc = arg(a, 2);
377    apply_define_property(it, &obj, &key, &desc);
378    Ok(obj)
379}
380
381pub(crate) fn object_define_properties(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
382    let obj = arg(a, 0);
383    let descs = arg(a, 1);
384    if let Value::Object(d) = &descs {
385        let keys: alloc::vec::Vec<String> = d.borrow().props.keys().map(Clone::clone).collect();
386        for key in keys {
387            let desc = obj_prop(&descs, &key).unwrap_or(Value::Undefined);
388            apply_define_property(it, &obj, &key, &desc);
389        }
390    }
391    Ok(obj)
392}
393
394pub(crate) fn make_property_descriptor(obj: &super::super::value::ObjRef, key: &str) -> Value {
395    let obj = unwrap_proxy_target(obj);
396    let b = obj.borrow();
397    let attr = b.attrs.get(key).copied().unwrap_or_default();
398    
399    if let ObjKind::Array(items) = &b.kind {
400        if key == "length" {
401            let desc = Obj::plain();
402            desc.borrow_mut().props.insert("value".into(), Value::Number(items.len() as f64));
403            desc.borrow_mut().props.insert("writable".into(), Value::Bool(true));
404            desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(false));
405            desc.borrow_mut().props.insert("configurable".into(), Value::Bool(false));
406            return Value::Object(desc);
407        }
408        if let Ok(idx) = key.parse::<usize>() {
409            if let Some(v) = items.get(idx) {
410                let desc = Obj::plain();
411                desc.borrow_mut().props.insert("value".into(), v.clone());
412                desc.borrow_mut().props.insert("writable".into(), Value::Bool(true));
413                desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(true));
414                desc.borrow_mut().props.insert("configurable".into(), Value::Bool(true));
415                return Value::Object(desc);
416            }
417            return Value::Undefined;
418        }
419    }
420    if let Some(acc) = b.accessors.get(key) {
421        let desc = Obj::plain();
422        if let Some(g) = &acc.get {
423            desc.borrow_mut().props.insert("get".into(), g.clone());
424        } else {
425            desc.borrow_mut().props.insert("get".into(), Value::Undefined);
426        }
427        if let Some(s) = &acc.set {
428            desc.borrow_mut().props.insert("set".into(), s.clone());
429        } else {
430            desc.borrow_mut().props.insert("set".into(), Value::Undefined);
431        }
432        desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(attr.enumerable));
433        desc.borrow_mut().props.insert("configurable".into(), Value::Bool(attr.configurable));
434        Value::Object(desc)
435    } else if let Some(v) = b.props.get(key) {
436        let desc = Obj::plain();
437        desc.borrow_mut().props.insert("value".into(), v.clone());
438        desc.borrow_mut().props.insert("writable".into(), Value::Bool(attr.writable));
439        desc.borrow_mut().props.insert("enumerable".into(), Value::Bool(attr.enumerable));
440        desc.borrow_mut().props.insert("configurable".into(), Value::Bool(attr.configurable));
441        Value::Object(desc)
442    } else {
443        Value::Undefined
444    }
445}
446
447pub(crate) fn object_get_own_property_descriptor(
448    _it: &mut Interp,
449    _t: Value,
450    a: &[Value],
451) -> Result<Value, Value> {
452    let Value::Object(o) = arg(a, 0) else { return Ok(Value::Undefined) };
453    let key = arg(a, 1).to_js_string();
454    Ok(make_property_descriptor(&o, &key))
455}
456
457pub(crate) fn object_get_own_property_descriptors(
458    _it: &mut Interp,
459    _t: Value,
460    a: &[Value],
461) -> Result<Value, Value> {
462    let Value::Object(o) = arg(a, 0) else { return Ok(Value::Object(Obj::plain())) };
463    let result = Obj::plain();
464    let keys: alloc::vec::Vec<String> = {
465        let b = o.borrow();
466        if let ObjKind::Array(items) = &b.kind {
467            let mut ks: alloc::vec::Vec<String> = (0..items.len()).map(|i| i.to_string()).collect();
468            ks.push(String::from("length"));
469            ks
470        } else {
471            let mut ks: alloc::vec::Vec<String> = b.props.keys().map(Clone::clone).collect();
472            for k in b.accessors.keys() {
473                if !ks.contains(k) {
474                    ks.push(k.clone());
475                }
476            }
477            ks
478        }
479    };
480    for key in keys {
481        let desc = make_property_descriptor(&o, &key);
482        result.borrow_mut().props.insert(key, desc);
483    }
484    Ok(Value::Object(result))
485}
486
487// ===== 追加 Number 静的 =====
488pub(crate) fn number_is_integer(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
489    Ok(Value::Bool(match arg(a, 0) {
490        Value::Number(x) => x.is_finite() && libm::floor(x) == x,
491        _ => false,
492    }))
493}
494pub(crate) fn number_is_safe_integer(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
495    Ok(Value::Bool(match arg(a, 0) {
496        Value::Number(x) => {
497            x.is_finite() && libm::floor(x) == x && libm::fabs(x) <= 9007199254740991.0
498        }
499        _ => false,
500    }))
501}
502pub(crate) fn number_is_finite_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
503    Ok(Value::Bool(
504        matches!(arg(a, 0), Value::Number(x) if x.is_finite()),
505    ))
506}
507pub(crate) fn number_is_nan_static(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
508    Ok(Value::Bool(
509        matches!(arg(a, 0), Value::Number(x) if x.is_nan()),
510    ))
511}
512