Skip to main content

atmos/os_lib/js/builtins/
object_array.rs

1// 分割: builtins.rs より機械的に移動(2026-07-16 リファクタ フェーズ2)。
2// ロジック不変。可視性のみ pub(crate) へ昇格し、親が pub(crate) use で再エクスポート。
3use super::*;
4
5// ============ Object ============
6
7pub(crate) fn object_ctor(_: &mut Interp, _t: Value, _a: &[Value]) -> Result<Value, Value> {
8    Ok(Value::Object(Obj::plain()))
9}
10/// `Object.keys`/`values`/`entries` は `Proxy` を素通ししてしまうと(`Proxy` 自身は
11/// `props`/`ObjKind::Array` を持たないため)常に空になるバグだった。`ownKeys`/
12/// `getOwnPropertyDescriptor` トラップの呼び出しまでは行わない簡略実装だが、実用上多い
13/// 「target にフォワードするだけの Proxy」のケースはこれで正しく動くようになる。
14pub(crate) fn unwrap_proxy_target(o: &super::super::value::ObjRef) -> super::super::value::ObjRef {
15    match &o.borrow().kind {
16        ObjKind::Proxy { target, .. } => unwrap_proxy_target(target),
17        _ => o.clone(),
18    }
19}
20/// accessor(getter/setter) 専用プロパティのうち `props` に無いキーだけを返す
21/// (`props` 側に同名の通常プロパティがあればそちらを優先し二重にしない)。
22pub fn accessor_only_keys(b: &super::super::value::Obj) -> Vec<String> {
23    b.accessors
24        .keys()
25        .filter(|k| !b.props.contains_key(*k))
26        .cloned()
27        .collect()
28}
29/// `ObjKind::Host` は DOM プロキシ(style/classList/dataset/storage/document)や
30/// `make_iterator()` の内部状態(`_items`/`_pos`)を `.props` に格納する「内部実装専用」の
31/// 種別で、仕様上は列挙可能な own プロパティを持たない(実データは別経路にある)。
32/// `Object.keys`/`values`/`entries`/`assign`/`JSON.stringify` がこれを素通りして
33/// `.props` を直接列挙すると、`make_iterator()` 導入で新たに `_items`/`_pos` という
34/// 内部実装詳細が `Object.keys(arr.values())` 等から見えてしまうバグがあった。
35pub(crate) fn is_internal_host(kind: &ObjKind) -> bool {
36    matches!(kind, ObjKind::Host(_))
37}
38/// この処理系には実の `Symbol` 型が無く、`Symbol.iterator` 等の well-known symbol は
39/// `"Symbol(Symbol.iterator)"` という見た目のリテラル文字列で代用されている
40/// (`obj[Symbol.iterator] = fn` は内部的に `obj.props["Symbol(Symbol.iterator)"] = fn`
41/// という通常の文字列キー代入に化ける)。仕様上 Symbol キーは `Object.keys`/`values`/
42/// `entries`/`for...in`/`JSON.stringify`/スプレッド構文等のあらゆる「文字列キー列挙」から
43/// 除外されるべきだが、これらの区別が無かったため、`class Range { [Symbol.iterator](){} }`
44/// のようなカスタムイテラブルの `Symbol.iterator` メソッド自体が列挙結果に漏れて見える
45/// バグだった(`Object.assign`/`Reflect.ownKeys` は仕様上 Symbol キーも対象のため
46/// 意図的にこのフィルタを適用しない)。
47pub(crate) fn is_symbol_like_key(k: &str) -> bool {
48    k.starts_with("Symbol(") && k.ends_with(')')
49}
50/// 文字列キーが仕様上の「配列インデックス」(`"0"`〜`"4294967294"`、先頭ゼロ無し)
51/// かどうかを判定し、そうならその数値を返す(ECMA-262 `CanonicalNumericIndexString`/
52/// `IsArrayIndex` 相当の簡略判定)。
53fn array_index_key(k: &str) -> Option<u32> {
54    if k == "0" {
55        return Some(0);
56    }
57    if k.is_empty() || k.starts_with('0') || !k.bytes().all(|b| b.is_ascii_digit()) {
58        return None;
59    }
60    k.parse::<u32>().ok().filter(|&n| n != u32::MAX)
61}
62/// ECMA-262 `OrdinaryOwnPropertyKeys`が要求する2段階の列挙順序へ並べ替える:
63/// (1) 配列インデックス風のキーを数値昇順で先頭に、(2) 残りのキーは元の順序
64/// (`props`が`IndexMap`のため既に挿入順)のまま後ろに続ける。`Object.keys`/
65/// `.values`/`.entries`が丸ごと文字列の辞書式順序(旧`BTreeMap`由来)だった
66/// バグの残り半分(数値キー優先順序)を解消する。2026-07-18 発見・実装。
67pub(crate) fn spec_key_order<T>(items: Vec<(String, T)>) -> Vec<(String, T)> {
68    let mut numeric: Vec<(u32, String, T)> = Vec::new();
69    let mut rest: Vec<(String, T)> = Vec::new();
70    for (k, v) in items {
71        match array_index_key(&k) {
72            Some(n) => numeric.push((n, k, v)),
73            None => rest.push((k, v)),
74        }
75    }
76    numeric.sort_by_key(|(n, _, _)| *n);
77    let mut out: Vec<(String, T)> = numeric.into_iter().map(|(_, k, v)| (k, v)).collect();
78    out.extend(rest);
79    out
80}
81pub(crate) fn object_keys(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
82    let mut keys = Vec::new();
83    if let Value::Object(o) = arg(a, 0) {
84        let o = unwrap_proxy_target(&o);
85        let b = o.borrow();
86        if let ObjKind::Array(items) = &b.kind {
87            for i in 0..items.len() {
88                keys.push(Value::str(i.to_string()));
89            }
90        } else if let Some(sk) = storage_host_keys(&b.kind) {
91            for k in sk {
92                keys.push(Value::str(k));
93            }
94        } else if let Some(sk) = dataset_host_keys(&b.kind, &it.dom.borrow()) {
95            for k in sk {
96                keys.push(Value::str(k));
97            }
98        } else if is_internal_host(&b.kind) {
99            // 列挙可能な own プロパティ無し。
100        } else {
101            let mut plain_keys: Vec<(String, ())> = Vec::new();
102            for k in b.props.keys().filter(|k| !is_symbol_like_key(k)) {
103                let attr = b.attrs.get(k).copied().unwrap_or_default();
104                if attr.enumerable {
105                    plain_keys.push((k.clone(), ()));
106                }
107            }
108            for k in accessor_only_keys(&b) {
109                let attr = b.attrs.get(&k).copied().unwrap_or_default();
110                if attr.enumerable {
111                    plain_keys.push((k, ()));
112                }
113            }
114            for (k, ()) in spec_key_order(plain_keys) {
115                keys.push(Value::str(k));
116            }
117        }
118    }
119    Ok(Value::Object(Obj::array(keys)))
120}
121pub(crate) fn object_values(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
122    let mut vals = Vec::new();
123    if let Value::Object(o) = arg(a, 0) {
124        let o = unwrap_proxy_target(&o);
125        let (is_array, prop_vals, getters) = {
126            let b = o.borrow();
127            if let ObjKind::Array(items) = &b.kind {
128                (true, items.clone(), Vec::new())
129            } else if let Some(sk) = storage_host_keys(&b.kind) {
130                let tag = if let ObjKind::Host(t) = &b.kind { t.clone() } else { String::new() };
131                let vals: Vec<Value> = sk
132                    .iter()
133                    .filter_map(|k| storage_map(&tag).lock().get(k).map(|v| Value::str(v.clone())))
134                    .collect();
135                (true, vals, Vec::new())
136            } else if let Some(entries) = dataset_host_entries(&b.kind, &it.dom.borrow()) {
137                let vals: Vec<Value> = entries.into_iter().map(|(_, v)| Value::str(v)).collect();
138                (true, vals, Vec::new())
139            } else if is_internal_host(&b.kind) {
140                (false, Vec::new(), Vec::new())
141            } else {
142                let plain_vals: Vec<(String, Value)> = b
143                    .props
144                    .iter()
145                    .filter(|(k, _)| {
146                        if is_symbol_like_key(k) { return false; }
147                        b.attrs.get(*k).copied().unwrap_or_default().enumerable
148                    })
149                    .map(|(k, v)| (k.clone(), v.clone()))
150                    .collect();
151                let prop_vals: Vec<Value> =
152                    spec_key_order(plain_vals).into_iter().map(|(_, v)| v).collect();
153                let getters: Vec<Value> = accessor_only_keys(&b)
154                    .into_iter()
155                    .filter(|k| b.attrs.get(k).copied().unwrap_or_default().enumerable)
156                    .filter_map(|k| b.accessors.get(&k).and_then(|acc| acc.get.clone()))
157                    .collect();
158                (false, prop_vals, getters)
159            }
160        };
161        vals = prop_vals;
162        if !is_array {
163            // getter 呼出しは `o` を再借用し得るため、上のブロックで既に借用を終えてから行う。
164            for g in getters {
165                vals.push(it.call_value(&g, Value::Object(o.clone()), &[]).unwrap_or(Value::Undefined));
166            }
167        }
168    }
169    Ok(Value::Object(Obj::array(vals)))
170}
171pub(crate) fn object_entries(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
172    let mut entries = Vec::new();
173    if let Value::Object(o) = arg(a, 0) {
174        let o = unwrap_proxy_target(&o);
175        let (prop_entries, getters) = {
176            let b = o.borrow();
177            if let Some(sk) = storage_host_keys(&b.kind) {
178                let tag = if let ObjKind::Host(t) = &b.kind { t.clone() } else { String::new() };
179                let entries: Vec<(String, Value)> = sk
180                    .into_iter()
181                    .filter_map(|k| {
182                        storage_map(&tag).lock().get(&k).map(|v| (k, Value::str(v.clone())))
183                    })
184                    .collect();
185                (entries, Vec::new())
186            } else if let Some(ds) = dataset_host_entries(&b.kind, &it.dom.borrow()) {
187                let entries: Vec<(String, Value)> =
188                    ds.into_iter().map(|(k, v)| (k, Value::str(v))).collect();
189                (entries, Vec::new())
190            } else if is_internal_host(&b.kind) {
191                (Vec::new(), Vec::new())
192            } else {
193                let plain_entries: Vec<(String, Value)> = b
194                    .props
195                    .iter()
196                    .filter(|(k, _)| {
197                        if is_symbol_like_key(k) { return false; }
198                        b.attrs.get(*k).copied().unwrap_or_default().enumerable
199                    })
200                    .map(|(k, v)| (k.clone(), v.clone()))
201                    .collect();
202                let prop_entries: Vec<(String, Value)> = spec_key_order(plain_entries);
203                let getters: Vec<(String, Value)> = accessor_only_keys(&b)
204                    .into_iter()
205                    .filter(|k| b.attrs.get(k).copied().unwrap_or_default().enumerable)
206                    .filter_map(|k| b.accessors.get(&k).and_then(|acc| acc.get.clone()).map(|g| (k, g)))
207                    .collect();
208                (prop_entries, getters)
209            }
210        };
211        for (k, v) in prop_entries {
212            entries.push(Value::Object(Obj::array(vec![Value::str(k), v])));
213        }
214        for (k, g) in getters {
215            let v = it.call_value(&g, Value::Object(o.clone()), &[]).unwrap_or(Value::Undefined);
216            entries.push(Value::Object(Obj::array(vec![Value::str(k), v])));
217        }
218    }
219    Ok(Value::Object(Obj::array(entries)))
220}
221/// `Object.assign` の1ソース分の「自身の列挙可能プロパティ」を集める。
222/// 以前は `s.borrow().props` を直接見るだけだったため、(1) `Proxy`(自身は `props` を
223/// 持たず target 側にある)と (2) 配列(要素は `props` ではなく `ObjKind::Array` 側にある)
224/// の2種類のソースからは何もコピーされないバグだった。
225pub(crate) fn own_enumerable_entries(it: &mut Interp, s: &super::super::value::ObjRef) -> Vec<(String, Value)> {
226    let s = unwrap_proxy_target(s);
227    let (prop_entries, getters) = {
228        let b = s.borrow();
229        if let ObjKind::Array(items) = &b.kind {
230            return items
231                .iter()
232                .enumerate()
233                .map(|(i, v)| (i.to_string(), v.clone()))
234                .collect();
235        }
236        if let Some(sk) = storage_host_keys(&b.kind) {
237            let tag = if let ObjKind::Host(t) = &b.kind { t.clone() } else { String::new() };
238            return sk
239                .into_iter()
240                .filter_map(|k| storage_map(&tag).lock().get(&k).map(|v| (k, Value::str(v.clone()))))
241                .collect();
242        }
243        if let Some(ds) = dataset_host_entries(&b.kind, &it.dom.borrow()) {
244            return ds.into_iter().map(|(k, v)| (k, Value::str(v))).collect();
245        }
246        if is_internal_host(&b.kind) {
247            return Vec::new();
248        }
249        let plain_entries: Vec<(String, Value)> = b
250            .props
251            .iter()
252            .filter(|(k, _)| {
253                if is_symbol_like_key(k) { return false; }
254                b.attrs.get(*k).copied().unwrap_or_default().enumerable
255            })
256            .map(|(k, v)| (k.clone(), v.clone()))
257            .collect();
258        let prop_entries: Vec<(String, Value)> = spec_key_order(plain_entries);
259        let getters: Vec<(String, Value)> = accessor_only_keys(&b)
260            .into_iter()
261            .filter(|k| b.attrs.get(k).copied().unwrap_or_default().enumerable)
262            .filter_map(|k| b.accessors.get(&k).and_then(|acc| acc.get.clone()).map(|g| (k, g)))
263            .collect();
264        (prop_entries, getters)
265    };
266    let mut out = prop_entries;
267    for (k, g) in getters {
268        let v = it.call_value(&g, Value::Object(s.clone()), &[]).unwrap_or(Value::Undefined);
269        out.push((k, v));
270    }
271    out
272}
273pub(crate) fn object_assign(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
274    let target = arg(a, 0);
275    if let Value::Object(t) = &target {
276        for src in &a[1.min(a.len())..] {
277            if let Value::Object(s) = src {
278                for (k, v) in own_enumerable_entries(it, s) {
279                    t.borrow_mut().props.insert(k, v);
280                }
281                // 列挙可能な Symbol キーも写す(仕様: OwnPropertyKeys は文字列と Symbol の両方)。
282                // 【2026-10-18】YouTube の Wiz の文脈トークン(`d[_.nla]=!0` を Object.assign で写す)が
283                // `_.nla in token` で偽になり、文脈の取得が失敗して中央の再生ボタンが作られなかった。
284                let sym: Vec<(String, Value)> = {
285                    let src = unwrap_proxy_target(s);
286                    let b = src.borrow();
287                    b.props
288                        .iter()
289                        .filter(|(k, _)| is_symbol_like_key(k) && b.attrs.get(*k).copied().unwrap_or_default().enumerable)
290                        .map(|(k, v)| (k.clone(), v.clone()))
291                        .collect()
292                };
293                for (k, v) in sym {
294                    t.borrow_mut().props.insert(k, v);
295                }
296            }
297        }
298    }
299    Ok(target)
300}
301
302// ============ Array ============
303
304pub(crate) fn array_ctor(_it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
305    // Array(n) で長さ n(要素はすべて穴)、Array(a,b,..) で要素列。
306    let sized = a.len() == 1 && matches!(a[0], Value::Number(_));
307    let items = if a.len() == 1 {
308        if let Value::Number(n) = a[0] {
309            if n >= 0.0 && (n as u32 as f64) == n && n <= u32::MAX as f64 {
310                vec![Value::Undefined; n as usize]
311            } else {
312                return Err(Value::Str(alloc::rc::Rc::new(String::from("RangeError: Invalid array length"))));
313            }
314        } else {
315            a.to_vec()
316        }
317    } else {
318        a.to_vec()
319    };
320
321    if let Value::Object(ref o) = t {
322        let is_plain = matches!(o.borrow().kind, ObjKind::Plain);
323        if is_plain {
324            let len = items.len();
325            let mut b = o.borrow_mut();
326            b.kind = ObjKind::Array(items);
327            b.props.insert("length".into(), Value::Number(len as f64));
328            if sized {
329                b.holes = Some(alloc::boxed::Box::new(vec![true; len]));
330            }
331            drop(b);
332            return Ok(t);
333        }
334    }
335
336    let len = items.len();
337    let r = Obj::array(items);
338    if sized {
339        r.borrow_mut().holes = Some(alloc::boxed::Box::new(vec![true; len]));
340    }
341    Ok(Value::Object(r))
342}
343/// `Array.isArray(v)`。仕様上は `Proxy` を透過して target を見る必要がある
344/// (以前は Proxy でラップした配列が false 判定になっていた)。
345/// `Array.isArray(v)`。TypedArray も内部表現は `ObjKind::Array` を流用するため、
346/// `_ta_kind` タグの有無を見ずに判定すると `Array.isArray(new Int8Array(...))` が
347/// 仕様に反して `true` になるバグだった(TypedArray は Array exotic object では
348/// ないため、仕様上ここは常に `false` になるべき)。
349pub(crate) fn array_is_array(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
350    fn is_array_value(v: &Value) -> bool {
351        match v {
352            Value::Object(o) => {
353                let b = o.borrow();
354                match &b.kind {
355                    ObjKind::Array(_) => !b.props.contains_key("_ta_kind"),
356                    ObjKind::Proxy { target, .. } => {
357                        let target = target.clone();
358                        drop(b);
359                        is_array_value(&Value::Object(target))
360                    }
361                    _ => false,
362                }
363            }
364            _ => false,
365        }
366    }
367    Ok(Value::Bool(is_array_value(&arg(a, 0))))
368}
369pub(crate) fn array_from(i: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
370    let src = arg(a, 0);
371    let mut items = match &src {
372        Value::Object(o) => {
373            // Array/Set/Map/Generator など全イテラブルを展開(generator は遅延駆動)。
374            // `ObjKind::Host("iterator")`(`entries`/`keys`/`values` が返す軽量イテレータ。
375            // `make_iterator()` 参照)も含める。以前はこれが漏れており
376            // `Array.from(arr.values())` が「array-like(length走査)」経路に誤って落ち、
377            // `length` プロパティを持たないイテレータオブジェクトから何も取れず空配列に
378            // なるバグだった。
379            let is_iterable = matches!(
380                &o.borrow().kind,
381                ObjKind::Array(_)
382                    | ObjKind::Bytes(_)
383                    | ObjKind::SetObj(_)
384                    | ObjKind::MapObj(_)
385                    | ObjKind::Generator(_)
386            ) || matches!(&o.borrow().kind, ObjKind::Host(t) if t == "iterator");
387            if is_iterable {
388                i.iter_to_vec(&src)
389            } else {
390                // array-like: length + 数値インデックス。
391                let len = o
392                    .borrow()
393                    .props
394                    .get("length")
395                    .map(|v| v.to_number() as usize)
396                    .unwrap_or(0);
397                (0..len)
398                    .map(|idx| {
399                        o.borrow()
400                            .props
401                            .get(&idx.to_string())
402                            .cloned()
403                            .unwrap_or(Value::Undefined)
404                    })
405                    .collect()
406            }
407        }
408        Value::Str(s) => s.chars().map(|c| Value::str(c.to_string())).collect(),
409        _ => Vec::new(),
410    };
411    // 第2引数 mapFn(value, index) があれば各要素に適用(第3引数 thisArg も仕様どおり
412    // mapFn の this に束縛する。以前は無視して常に undefined 決め打ちだった)。
413    let map_fn = arg(a, 1);
414    let this_arg = arg(a, 2);
415    if matches!(&map_fn, Value::Object(o) if o.borrow().is_callable()) {
416        let mut mapped = Vec::with_capacity(items.len());
417        for (idx, v) in items.into_iter().enumerate() {
418            let r = i.call_value(&map_fn, this_arg.clone(), &[v, Value::Number(idx as f64)])?;
419            mapped.push(r);
420        }
421        items = mapped;
422    }
423    Ok(Value::Object(Obj::array(items)))
424}
425
426/// `Array.fromAsync(iterableOrLike, mapFn?, thisArg?)`(ES2024/2025 相当)。
427/// 本来は非同期イテレータプロトコル(`Symbol.asyncIterator`)にも対応するが、この処理系には
428/// まだ for-await-of / 非同期イテレータが無いため、同期イテラブル(配列・Set・Map・
429/// generator・文字列・array-like)を対象に、各要素が Promise ならその場で await する
430/// 簡略実装。戻り値は仕様どおり Promise でラップする。
431pub(crate) fn array_from_async(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
432    let inner = (|| -> Result<Value, Value> {
433        let items = it.iter_to_vec(&arg(a, 0));
434        let map_fn = arg(a, 1);
435        let has_map = matches!(&map_fn, Value::Object(o) if o.borrow().is_callable());
436        let this_arg = arg(a, 2);
437        let mut out = Vec::with_capacity(items.len());
438        for (idx, item) in items.into_iter().enumerate() {
439            let awaited = it.await_value(item)?;
440            let v = if has_map {
441                it.call_value(&map_fn, this_arg.clone(), &[awaited, Value::Number(idx as f64)])?
442            } else {
443                awaited
444            };
445            out.push(v);
446        }
447        Ok(Value::Object(Obj::array(out)))
448    })();
449    let _ = t;
450    match inner {
451        Ok(v) => Ok(resolved_promise(it, v)),
452        Err(e) => Ok(rejected_promise(it, e)),
453    }
454}
455
456// ============ 型変換コンストラクタ ============
457
458pub(crate) fn string_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
459    Ok(Value::str(it.to_string_value(arg(a, 0))?))
460}
461pub(crate) fn number_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
462    if a.is_empty() {
463        return Ok(Value::Number(0.0));
464    }
465    Ok(Value::Number(arg(a, 0).to_number()))
466}
467pub(crate) fn boolean_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
468    Ok(Value::Bool(arg(a, 0).truthy()))
469}
470