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            }
282        }
283    }
284    Ok(target)
285}
286
287// ============ Array ============
288
289pub(crate) fn array_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
290    // Array(n) で長さ n、Array(a,b,..) で要素列。
291    if a.len() == 1 {
292        if let Value::Number(n) = a[0] {
293            return Ok(Value::Object(Obj::array(vec![
294                Value::Undefined;
295                n as usize
296            ])));
297        }
298    }
299    Ok(Value::Object(Obj::array(a.to_vec())))
300}
301/// `Array.isArray(v)`。仕様上は `Proxy` を透過して target を見る必要がある
302/// (以前は Proxy でラップした配列が false 判定になっていた)。
303/// `Array.isArray(v)`。TypedArray も内部表現は `ObjKind::Array` を流用するため、
304/// `_ta_kind` タグの有無を見ずに判定すると `Array.isArray(new Int8Array(...))` が
305/// 仕様に反して `true` になるバグだった(TypedArray は Array exotic object では
306/// ないため、仕様上ここは常に `false` になるべき)。
307pub(crate) fn array_is_array(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
308    fn is_array_value(v: &Value) -> bool {
309        match v {
310            Value::Object(o) => {
311                let b = o.borrow();
312                match &b.kind {
313                    ObjKind::Array(_) => !b.props.contains_key("_ta_kind"),
314                    ObjKind::Proxy { target, .. } => {
315                        let target = target.clone();
316                        drop(b);
317                        is_array_value(&Value::Object(target))
318                    }
319                    _ => false,
320                }
321            }
322            _ => false,
323        }
324    }
325    Ok(Value::Bool(is_array_value(&arg(a, 0))))
326}
327pub(crate) fn array_from(i: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
328    let src = arg(a, 0);
329    let mut items = match &src {
330        Value::Object(o) => {
331            // Array/Set/Map/Generator など全イテラブルを展開(generator は遅延駆動)。
332            // `ObjKind::Host("iterator")`(`entries`/`keys`/`values` が返す軽量イテレータ。
333            // `make_iterator()` 参照)も含める。以前はこれが漏れており
334            // `Array.from(arr.values())` が「array-like(length走査)」経路に誤って落ち、
335            // `length` プロパティを持たないイテレータオブジェクトから何も取れず空配列に
336            // なるバグだった。
337            let is_iterable = matches!(
338                &o.borrow().kind,
339                ObjKind::Array(_)
340                    | ObjKind::SetObj(_)
341                    | ObjKind::MapObj(_)
342                    | ObjKind::Generator(_)
343            ) || matches!(&o.borrow().kind, ObjKind::Host(t) if t == "iterator");
344            if is_iterable {
345                i.iter_to_vec(&src)
346            } else {
347                // array-like: length + 数値インデックス。
348                let len = o
349                    .borrow()
350                    .props
351                    .get("length")
352                    .map(|v| v.to_number() as usize)
353                    .unwrap_or(0);
354                (0..len)
355                    .map(|idx| {
356                        o.borrow()
357                            .props
358                            .get(&idx.to_string())
359                            .cloned()
360                            .unwrap_or(Value::Undefined)
361                    })
362                    .collect()
363            }
364        }
365        Value::Str(s) => s.chars().map(|c| Value::str(c.to_string())).collect(),
366        _ => Vec::new(),
367    };
368    // 第2引数 mapFn(value, index) があれば各要素に適用(第3引数 thisArg も仕様どおり
369    // mapFn の this に束縛する。以前は無視して常に undefined 決め打ちだった)。
370    let map_fn = arg(a, 1);
371    let this_arg = arg(a, 2);
372    if matches!(&map_fn, Value::Object(o) if o.borrow().is_callable()) {
373        let mut mapped = Vec::with_capacity(items.len());
374        for (idx, v) in items.into_iter().enumerate() {
375            let r = i.call_value(&map_fn, this_arg.clone(), &[v, Value::Number(idx as f64)])?;
376            mapped.push(r);
377        }
378        items = mapped;
379    }
380    Ok(Value::Object(Obj::array(items)))
381}
382
383/// `Array.fromAsync(iterableOrLike, mapFn?, thisArg?)`(ES2024/2025 相当)。
384/// 本来は非同期イテレータプロトコル(`Symbol.asyncIterator`)にも対応するが、この処理系には
385/// まだ for-await-of / 非同期イテレータが無いため、同期イテラブル(配列・Set・Map・
386/// generator・文字列・array-like)を対象に、各要素が Promise ならその場で await する
387/// 簡略実装。戻り値は仕様どおり Promise でラップする。
388pub(crate) fn array_from_async(it: &mut Interp, t: Value, a: &[Value]) -> Result<Value, Value> {
389    let inner = (|| -> Result<Value, Value> {
390        let items = it.iter_to_vec(&arg(a, 0));
391        let map_fn = arg(a, 1);
392        let has_map = matches!(&map_fn, Value::Object(o) if o.borrow().is_callable());
393        let this_arg = arg(a, 2);
394        let mut out = Vec::with_capacity(items.len());
395        for (idx, item) in items.into_iter().enumerate() {
396            let awaited = it.await_value(item)?;
397            let v = if has_map {
398                it.call_value(&map_fn, this_arg.clone(), &[awaited, Value::Number(idx as f64)])?
399            } else {
400                awaited
401            };
402            out.push(v);
403        }
404        Ok(Value::Object(Obj::array(out)))
405    })();
406    let _ = t;
407    match inner {
408        Ok(v) => Ok(resolved_promise(it, v)),
409        Err(e) => Ok(rejected_promise(it, e)),
410    }
411}
412
413// ============ 型変換コンストラクタ ============
414
415pub(crate) fn string_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
416    Ok(Value::str(arg(a, 0).to_js_string()))
417}
418pub(crate) fn number_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
419    if a.is_empty() {
420        return Ok(Value::Number(0.0));
421    }
422    Ok(Value::Number(arg(a, 0).to_number()))
423}
424pub(crate) fn boolean_ctor(_: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
425    Ok(Value::Bool(arg(a, 0).truthy()))
426}
427