Skip to main content

atmos/os_lib/js/builtins/
weak_symbol.rs

1// 分割: builtins.rs より機械的に移動(2026-07-16 リファクタ フェーズ2)。
2// ロジック不変。可視性のみ pub(crate) へ昇格し、親が pub(crate) use で再エクスポート。
3use super::*;
4
5// ============ WeakMap / WeakSet / WeakRef / Symbol ============
6
7// WeakMap: オブジェクトキー→値の Map。AtmOS では強参照(Rc)だが API は揃える。
8// 内部: props["_e"] = Array of [key, value] ペア。
9pub(crate) fn wm_get(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
10    let entries = wm_entries(&this);
11    let key = arg(a, 0);
12    if let Value::Object(arr) = entries {
13        let items = match &arr.borrow().kind {
14            super::super::value::ObjKind::Array(v) => v.clone(),
15            _ => alloc::vec![],
16        };
17        for pair in items {
18            if let Value::Object(p) = pair {
19                let b = p.borrow();
20                if let super::super::value::ObjKind::Array(kv) = &b.kind {
21                    if kv.len() == 2 && kv[0].same_ref(&key) {
22                        return Ok(kv[1].clone());
23                    }
24                }
25            }
26        }
27    }
28    Ok(Value::Undefined)
29}
30pub(crate) fn wm_set(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
31    let key = arg(a, 0);
32    let val = arg(a, 1);
33    // `WeakMap`/`WeakSet` は仕様上オブジェクト(`Symbol` を含む)以外のキーを
34    // 拒否し `TypeError` を投げるべきだが、以前は型を一切検証していなかった
35    // (`new WeakMap().set(1, 'x')` が静かに成功していた)。
36    if !matches!(key, Value::Object(_)) {
37        return Err(it.error("Invalid value used as weak map key"));
38    }
39    let entries = wm_entries(&this);
40    if let Value::Object(arr) = &entries {
41        // 既存エントリを更新。
42        let found = {
43            let b = arr.borrow();
44            if let super::super::value::ObjKind::Array(items) = &b.kind {
45                items.iter().enumerate().find_map(|(i, p)| {
46                    if let Value::Object(pair) = p {
47                        if let super::super::value::ObjKind::Array(kv) = &pair.borrow().kind {
48                            if kv.len() == 2 && kv[0].same_ref(&key) {
49                                return Some(i);
50                            }
51                        }
52                    }
53                    None
54                })
55            } else { None }
56        };
57        if let Some(i) = found {
58            let new_pair = Obj::array(alloc::vec![key, val]);
59            if let super::super::value::ObjKind::Array(items) = &mut arr.borrow_mut().kind {
60                items[i] = Value::Object(new_pair);
61            }
62        } else {
63            let new_pair = Obj::array(alloc::vec![key, val]);
64            if let super::super::value::ObjKind::Array(items) = &mut arr.borrow_mut().kind {
65                items.push(Value::Object(new_pair));
66            }
67        }
68    }
69    Ok(this)
70}
71pub(crate) fn wm_has(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
72    let key = arg(a, 0);
73    let got = wm_get(it, this, core::slice::from_ref(&key))?;
74    Ok(Value::Bool(!matches!(got, Value::Undefined)))
75}
76pub(crate) fn wm_delete(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
77    let key = arg(a, 0);
78    let entries = wm_entries(&this);
79    if let Value::Object(arr) = entries {
80        let mut b = arr.borrow_mut();
81        if let super::super::value::ObjKind::Array(items) = &mut b.kind {
82            let before = items.len();
83            items.retain(|p| {
84                if let Value::Object(pair) = p {
85                    if let super::super::value::ObjKind::Array(kv) = &pair.borrow().kind {
86                        return !kv[0].same_ref(&key);
87                    }
88                }
89                true
90            });
91            return Ok(Value::Bool(items.len() < before));
92        }
93    }
94    Ok(Value::Bool(false))
95}
96pub(crate) fn wm_entries(this: &Value) -> Value {
97    if let Value::Object(o) = this {
98        let o = unwrap_proxy_target(o);
99        let b = o.borrow();
100        b.props.get("_e").cloned().unwrap_or(Value::Undefined)
101    } else {
102        Value::Undefined
103    }
104}
105/// `new WeakMap(iterable)`。以前は `Map` と違い初期化用の `iterable` 引数(`[[k,v], ...]`)を
106/// 完全に無視しており、`new WeakMap([[key, val]])` が常に空の WeakMap になっていた。
107pub(crate) fn weakmap_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
108    let obj = Obj::plain();
109    {
110        let mut b = obj.borrow_mut();
111        b.props.insert("_e".into(), Value::Object(Obj::array(alloc::vec![])));
112        b.props.insert("get".into(), nv("get", wm_get));
113        b.props.insert("set".into(), nv("set", wm_set));
114        b.props.insert("has".into(), nv("has", wm_has));
115        b.props.insert("delete".into(), nv("delete", wm_delete));
116    }
117    let result = Value::Object(obj);
118    if let Some(init) = a.first() {
119        for pair in it.iter_to_vec(init) {
120            let k = key_at(&pair, 0);
121            let v = key_at(&pair, 1);
122            wm_set(it, result.clone(), &[k, v])?;
123        }
124    }
125    Ok(result)
126}
127
128// WeakSet: オブジェクトの集合。内部: Array で保持。
129pub(crate) fn ws_add(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
130    let val = arg(a, 0);
131    // `WeakSet` も `WeakMap` と同じ理由でオブジェクト以外の値を拒否すべき。
132    if !matches!(val, Value::Object(_)) {
133        return Err(it.error("Invalid value used in weak set"));
134    }
135    if let Value::Object(arr) = wm_entries(&this) {
136        let already = if let super::super::value::ObjKind::Array(items) = &arr.borrow().kind {
137            items.iter().any(|v| v.same_ref(&val))
138        } else { false };
139        if !already {
140            if let super::super::value::ObjKind::Array(items) = &mut arr.borrow_mut().kind {
141                items.push(val);
142            }
143        }
144    }
145    Ok(this)
146}
147pub(crate) fn ws_has(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
148    let val = arg(a, 0);
149    if let Value::Object(arr) = wm_entries(&this) {
150        if let super::super::value::ObjKind::Array(items) = &arr.borrow().kind {
151            return Ok(Value::Bool(items.iter().any(|v| v.same_ref(&val))));
152        }
153    }
154    Ok(Value::Bool(false))
155}
156pub(crate) fn ws_delete(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
157    let val = arg(a, 0);
158    if let Value::Object(arr) = wm_entries(&this) {
159        if let super::super::value::ObjKind::Array(items) = &mut arr.borrow_mut().kind {
160            let before = items.len();
161            items.retain(|v| !v.same_ref(&val));
162            return Ok(Value::Bool(items.len() < before));
163        }
164    }
165    Ok(Value::Bool(false))
166}
167/// `new WeakSet(iterable)`。`WeakMap` と同じ理由で初期化用の `iterable` 引数が
168/// 無視されていた(`new WeakSet([obj1, obj2])` が常に空になっていた)。
169pub(crate) fn weakset_ctor(it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
170    let obj = Obj::plain();
171    {
172        let mut b = obj.borrow_mut();
173        b.props.insert("_e".into(), Value::Object(Obj::array(alloc::vec![])));
174        b.props.insert("add".into(), nv("add", ws_add));
175        b.props.insert("has".into(), nv("has", ws_has));
176        b.props.insert("delete".into(), nv("delete", ws_delete));
177    }
178    let result = Value::Object(obj);
179    if let Some(init) = a.first() {
180        for v in it.iter_to_vec(init) {
181            ws_add(it, result.clone(), &[v])?;
182        }
183    }
184    Ok(result)
185}
186
187// WeakRef: オブジェクトへの「弱参照」(AtmOS では強参照だがAPIは揃える)。
188pub(crate) fn wr_deref(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
189    if let Value::Object(o) = &this {
190        return Ok(o.borrow().props.get("_t").cloned().unwrap_or(Value::Undefined));
191    }
192    Ok(Value::Undefined)
193}
194pub(crate) fn weakref_ctor(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
195    let target = arg(a, 0);
196    let obj = Obj::plain();
197    obj.borrow_mut().props.insert("_t".into(), target);
198    obj.borrow_mut().props.insert("deref".into(), nv("deref", wr_deref));
199    Ok(Value::Object(obj))
200}
201
202// Symbol: 一意なシンボル値(文字列で近似。グローバルカウンタで重複なし)。
203pub(crate) static SYMBOL_CTR: spin::Mutex<u64> = spin::Mutex::new(0);
204pub(crate) fn symbol_fn(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
205    let label = arg(a, 0).to_js_string();
206    let id = {
207        let mut ctr = SYMBOL_CTR.lock();
208        *ctr += 1;
209        *ctr
210    };
211    // 各呼び出しで一意になるよう内部 id を含める。
212    let s = if label == "undefined" {
213        alloc::format!("Symbol(@{})", id)
214    } else {
215        alloc::format!("Symbol({},@{})", label, id)
216    };
217    Ok(Value::str(&s))
218}
219pub(crate) fn symbol_for(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
220    let key = arg(a, 0).to_js_string();
221    Ok(Value::str(alloc::format!("Symbol({})", key)))
222}
223/// `Symbol.keyFor(sym)`(`Symbol.for` の逆引き)が丸ごと未対応だった。この処理系は
224/// symbol を `"Symbol(desc)"`(`Symbol.for` 由来)/`"Symbol(desc,@id)"`(通常の
225/// `Symbol()` 由来。一意性のため内部カウンタ id を含む)という文字列で近似表現
226/// している。`,@id` サフィックスが無ければ `Symbol.for` 由来とみなし中身の key を
227/// 返し、それ以外(ローカル symbol や symbol でない値)は仕様どおり `undefined`
228/// を返す。
229pub(crate) fn symbol_key_for(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
230    let s = arg(a, 0).to_js_string();
231    if let Some(inner) = s.strip_prefix("Symbol(").and_then(|s| s.strip_suffix(')')) {
232        if !inner.contains(",@") {
233            return Ok(Value::str(inner));
234        }
235    }
236    Ok(Value::Undefined)
237}
238
239/// `[Symbol.species]` ゲッター (ES2015)。呼び出された `this`(コンストラクタ自身)を返す。
240pub(crate) fn symbol_species_getter(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
241    Ok(this)
242}