Skip to main content

pipa/builtins/
map_set.rs

1use crate::host::HostFunction;
2use crate::object::function::JSFunction;
3use crate::object::object::JSObject;
4use crate::runtime::context::JSContext;
5use crate::value::JSValue;
6
7fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
8    let mut func = JSFunction::new_builtin(ctx.intern(name), 1);
9    func.set_builtin_marker(ctx, name);
10    let ptr = Box::into_raw(Box::new(func)) as usize;
11    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
12    JSValue::new_function(ptr)
13}
14
15fn map_size(ctx: &mut JSContext, obj: &JSObject) -> usize {
16    let size_atom = ctx.intern("__map_size__");
17    obj.get(size_atom)
18        .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
19        .unwrap_or(0)
20}
21
22fn map_key_atom(ctx: &mut JSContext, i: usize) -> crate::runtime::atom::Atom {
23    ctx.intern(&format!("__mk_{}__", i))
24}
25
26fn map_val_atom(ctx: &mut JSContext, i: usize) -> crate::runtime::atom::Atom {
27    ctx.intern(&format!("__mv_{}__", i))
28}
29
30fn map_find(ctx: &mut JSContext, obj: &JSObject, search_key: &JSValue) -> Option<usize> {
31    let size = map_size(ctx, obj);
32    for i in 0..size {
33        let k_atom = map_key_atom(ctx, i);
34        if let Some(k) = obj.get(k_atom) {
35            if k.strict_eq(search_key) {
36                return Some(i);
37            }
38        }
39    }
40    None
41}
42
43fn set_size(ctx: &mut JSContext, obj: &JSObject) -> usize {
44    let size_atom = ctx.intern("__set_size__");
45    obj.get(size_atom)
46        .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
47        .unwrap_or(0)
48}
49
50fn set_val_atom(ctx: &mut JSContext, i: usize) -> crate::runtime::atom::Atom {
51    ctx.intern(&format!("__sv_{}__", i))
52}
53
54fn set_find(ctx: &mut JSContext, obj: &JSObject, search: &JSValue) -> Option<usize> {
55    let size = set_size(ctx, obj);
56    for i in 0..size {
57        let v_atom = set_val_atom(ctx, i);
58        if let Some(v) = obj.get(v_atom) {
59            if v.strict_eq(search) {
60                return Some(i);
61            }
62        }
63    }
64    None
65}
66
67fn call_callback(
68    ctx: &mut JSContext,
69    callback: JSValue,
70    args: &[JSValue],
71) -> Result<JSValue, String> {
72    if let Some(ptr) = ctx.get_register_vm_ptr() {
73        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
74        vm.call_function(ctx, callback, args)
75    } else {
76        Err("call_callback: VM not available".to_string())
77    }
78}
79
80fn make_array(ctx: &mut JSContext, items: Vec<JSValue>) -> JSValue {
81    let mut arr = JSObject::new_array();
82    if let Some(proto_ptr) = ctx.get_array_prototype() {
83        arr.prototype = Some(proto_ptr);
84    }
85    let len = items.len();
86    for (i, v) in items.into_iter().enumerate() {
87        let key = ctx.intern(&i.to_string());
88        arr.set(key, v);
89    }
90    arr.set(ctx.common_atoms.length, JSValue::new_int(len as i64));
91    JSValue::new_object(Box::into_raw(Box::new(arr)) as usize)
92}
93
94pub fn map_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
95    let mut obj = JSObject::new();
96
97    if let Some(proto_ptr) = ctx.get_map_prototype() {
98        obj.prototype = Some(proto_ptr);
99    }
100
101    let size_atom = ctx.intern("__map_size__");
102    obj.set(size_atom, JSValue::new_int(0));
103
104    if let Some(iterable) = args.first() {
105        if iterable.is_object() {
106            let iter_obj = iterable.as_object();
107            let len_atom = ctx.common_atoms.length;
108            let len = iter_obj
109                .get(len_atom)
110                .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
111                .unwrap_or(0);
112
113            for i in 0..len {
114                let idx_atom = ctx.intern(&i.to_string());
115                if let Some(pair) = iter_obj.get(idx_atom) {
116                    if pair.is_object() {
117                        let pair_obj = pair.as_object();
118
119                        let k_atom = ctx.intern("0");
120                        let v_atom = ctx.intern("1");
121                        let k = pair_obj.get(k_atom).unwrap_or_else(JSValue::undefined);
122                        let v = pair_obj.get(v_atom).unwrap_or_else(JSValue::undefined);
123
124                        let cur_size = map_size(ctx, &obj);
125
126                        if let Some(idx) = map_find(ctx, &obj, &k) {
127                            let mv_atom = map_val_atom(ctx, idx);
128                            obj.set(mv_atom, v);
129                        } else {
130                            let mk_atom = map_key_atom(ctx, cur_size);
131                            let mv_atom = map_val_atom(ctx, cur_size);
132                            obj.set(mk_atom, k);
133                            obj.set(mv_atom, v);
134                            obj.set(size_atom, JSValue::new_int((cur_size + 1) as i64));
135                        }
136                    }
137                }
138            }
139        }
140    }
141
142    let size_val = obj.get(size_atom).unwrap_or(JSValue::new_int(0));
143    let size_prop_atom = ctx.intern("size");
144    obj.set(size_prop_atom, size_val);
145
146    JSValue::new_object(Box::into_raw(Box::new(obj)) as usize)
147}
148
149fn sync_map_size(ctx: &mut JSContext, obj: &mut JSObject) {
150    let size_atom = ctx.intern("__map_size__");
151    let size_prop_atom = ctx.intern("size");
152    let size_val = obj.get(size_atom).unwrap_or(JSValue::new_int(0));
153    obj.set(size_prop_atom, size_val);
154}
155
156fn map_set(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
157    if args.len() < 3 {
158        return if args.is_empty() {
159            JSValue::undefined()
160        } else {
161            args[0]
162        };
163    }
164    let this = args[0];
165    if !this.is_object() {
166        return this;
167    }
168    let key = args[1];
169    let value = args[2];
170
171    let obj = this.as_object_mut();
172
173    let size_atom = ctx.intern("__map_size__");
174
175    if let Some(idx) = map_find(ctx, obj, &key) {
176        let mv_atom = map_val_atom(ctx, idx);
177        obj.set(mv_atom, value);
178    } else {
179        let cur_size = map_size(ctx, obj);
180        let mk_atom = map_key_atom(ctx, cur_size);
181        let mv_atom = map_val_atom(ctx, cur_size);
182        obj.set(mk_atom, key);
183        obj.set(mv_atom, value);
184        obj.set(size_atom, JSValue::new_int((cur_size + 1) as i64));
185    }
186
187    sync_map_size(ctx, obj);
188    this
189}
190
191fn map_get(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
192    if args.len() < 2 {
193        return JSValue::undefined();
194    }
195    let this = args[0];
196    if !this.is_object() {
197        return JSValue::undefined();
198    }
199    let key = args[1];
200    let obj = this.as_object();
201
202    if let Some(idx) = map_find(ctx, obj, &key) {
203        let mv_atom = map_val_atom(ctx, idx);
204        obj.get(mv_atom).unwrap_or_else(JSValue::undefined)
205    } else {
206        JSValue::undefined()
207    }
208}
209
210fn map_has(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
211    if args.len() < 2 {
212        return JSValue::bool(false);
213    }
214    let this = args[0];
215    if !this.is_object() {
216        return JSValue::bool(false);
217    }
218    let key = args[1];
219    let obj = this.as_object();
220    JSValue::bool(map_find(ctx, obj, &key).is_some())
221}
222
223fn map_delete(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
224    if args.len() < 2 {
225        return JSValue::bool(false);
226    }
227    let this = args[0];
228    if !this.is_object() {
229        return JSValue::bool(false);
230    }
231    let key = args[1];
232    let obj = this.as_object_mut();
233
234    let size = map_size(ctx, obj);
235    let idx = match map_find(ctx, obj, &key) {
236        Some(i) => i,
237        None => return JSValue::bool(false),
238    };
239
240    for i in idx..(size - 1) {
241        let mk_next = map_key_atom(ctx, i + 1);
242        let mv_next = map_val_atom(ctx, i + 1);
243        let mk_cur = map_key_atom(ctx, i);
244        let mv_cur = map_val_atom(ctx, i);
245        let k = obj.get(mk_next).unwrap_or_else(JSValue::undefined);
246        let v = obj.get(mv_next).unwrap_or_else(JSValue::undefined);
247        obj.set(mk_cur, k);
248        obj.set(mv_cur, v);
249    }
250
251    let mk_last = map_key_atom(ctx, size - 1);
252    let mv_last = map_val_atom(ctx, size - 1);
253    obj.delete(mk_last);
254    obj.delete(mv_last);
255
256    let size_atom = ctx.intern("__map_size__");
257    obj.set(size_atom, JSValue::new_int((size - 1) as i64));
258    sync_map_size(ctx, obj);
259    JSValue::bool(true)
260}
261
262fn map_clear(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
263    if args.is_empty() {
264        return JSValue::undefined();
265    }
266    let this = args[0];
267    if !this.is_object() {
268        return JSValue::undefined();
269    }
270    let obj = this.as_object_mut();
271
272    let size = map_size(ctx, obj);
273    for i in 0..size {
274        let mk = map_key_atom(ctx, i);
275        let mv = map_val_atom(ctx, i);
276        obj.delete(mk);
277        obj.delete(mv);
278    }
279    let size_atom = ctx.intern("__map_size__");
280    obj.set(size_atom, JSValue::new_int(0));
281    sync_map_size(ctx, obj);
282    JSValue::undefined()
283}
284
285fn map_for_each(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
286    if args.len() < 2 {
287        return JSValue::undefined();
288    }
289    let this = args[0];
290    let callback = args[1];
291    if !this.is_object() || !callback.is_function() {
292        return JSValue::undefined();
293    }
294
295    let obj = this.as_object();
296    let size = map_size(ctx, obj);
297
298    for i in 0..size {
299        let mk_atom = map_key_atom(ctx, i);
300        let mv_atom = map_val_atom(ctx, i);
301        let k = obj.get(mk_atom).unwrap_or_else(JSValue::undefined);
302        let v = obj.get(mv_atom).unwrap_or_else(JSValue::undefined);
303        let _ = call_callback(ctx, callback, &[v, k, this]);
304    }
305
306    JSValue::undefined()
307}
308
309fn map_keys(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
310    if args.is_empty() {
311        return make_array(ctx, vec![]);
312    }
313    let this = args[0];
314    if !this.is_object() {
315        return make_array(ctx, vec![]);
316    }
317    let obj = this.as_object();
318    let size = map_size(ctx, obj);
319
320    let mut keys = Vec::with_capacity(size);
321    for i in 0..size {
322        let mk_atom = map_key_atom(ctx, i);
323        keys.push(obj.get(mk_atom).unwrap_or_else(JSValue::undefined));
324    }
325    make_array(ctx, keys)
326}
327
328fn map_values(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
329    if args.is_empty() {
330        return make_array(ctx, vec![]);
331    }
332    let this = args[0];
333    if !this.is_object() {
334        return make_array(ctx, vec![]);
335    }
336    let obj = this.as_object();
337    let size = map_size(ctx, obj);
338
339    let mut vals = Vec::with_capacity(size);
340    for i in 0..size {
341        let mv_atom = map_val_atom(ctx, i);
342        vals.push(obj.get(mv_atom).unwrap_or_else(JSValue::undefined));
343    }
344    make_array(ctx, vals)
345}
346
347fn map_entries(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
348    if args.is_empty() {
349        return make_array(ctx, vec![]);
350    }
351    let this = args[0];
352    if !this.is_object() {
353        return make_array(ctx, vec![]);
354    }
355    let obj = this.as_object();
356    let size = map_size(ctx, obj);
357
358    let mut entries = Vec::with_capacity(size);
359    for i in 0..size {
360        let mk_atom = map_key_atom(ctx, i);
361        let mv_atom = map_val_atom(ctx, i);
362        let k = obj.get(mk_atom).unwrap_or_else(JSValue::undefined);
363        let v = obj.get(mv_atom).unwrap_or_else(JSValue::undefined);
364        let pair = make_array(ctx, vec![k, v]);
365        entries.push(pair);
366    }
367    make_array(ctx, entries)
368}
369
370fn map_symbol_iterator(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
371    map_entries(ctx, args)
372}
373
374fn set_symbol_iterator(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
375    set_values(ctx, args)
376}
377
378fn weakmap_constructor(ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
379    let mut obj = JSObject::new();
380    if let Some(proto_ptr) = ctx.get_weakmap_prototype() {
381        obj.prototype = Some(proto_ptr);
382    }
383    let size_atom = ctx.intern("__weakmap_size__");
384    obj.set(size_atom, JSValue::new_int(0));
385    let ptr = Box::into_raw(Box::new(obj)) as usize;
386    JSValue::new_object(ptr)
387}
388
389fn weakmap_size(ctx: &mut JSContext, obj: &JSObject) -> usize {
390    let size_atom = ctx.intern("__weakmap_size__");
391    obj.get(size_atom)
392        .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
393        .unwrap_or(0)
394}
395
396fn weakmap_key_atom(ctx: &mut JSContext, i: usize) -> crate::runtime::atom::Atom {
397    ctx.intern(&format!("__wmk_{}__", i))
398}
399
400fn weakmap_val_atom(ctx: &mut JSContext, i: usize) -> crate::runtime::atom::Atom {
401    ctx.intern(&format!("__wmv_{}__", i))
402}
403
404fn weakmap_find(ctx: &mut JSContext, obj: &JSObject, search_key: &JSValue) -> Option<usize> {
405    let size = weakmap_size(ctx, obj);
406    for i in 0..size {
407        let k_atom = weakmap_key_atom(ctx, i);
408        if let Some(k) = obj.get(k_atom) {
409            if k.strict_eq(search_key) {
410                return Some(i);
411            }
412        }
413    }
414    None
415}
416
417fn weakmap_set(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
418    if args.len() < 3 {
419        return JSValue::undefined();
420    }
421    let this = &args[0];
422    let key = &args[1];
423    let value = &args[2];
424
425    if !key.is_object() && !key.is_symbol() {
426        return JSValue::undefined();
427    }
428
429    let obj = this.as_object_mut();
430
431    if let Some(idx) = weakmap_find(ctx, obj, key) {
432        let v_atom = weakmap_val_atom(ctx, idx);
433        obj.set(v_atom, value.clone());
434    } else {
435        let size_atom = ctx.intern("__weakmap_size__");
436        let size = weakmap_size(ctx, obj);
437        let k_atom = weakmap_key_atom(ctx, size);
438        let v_atom = weakmap_val_atom(ctx, size);
439        obj.set(k_atom, key.clone());
440        obj.set(v_atom, value.clone());
441        obj.set(size_atom, JSValue::new_int((size + 1) as i64));
442    }
443    this.clone()
444}
445
446fn weakmap_get(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
447    if args.len() < 2 {
448        return JSValue::undefined();
449    }
450    let this = &args[0];
451    let key = &args[1];
452
453    if !this.is_object() {
454        return JSValue::undefined();
455    }
456    let obj = this.as_object();
457
458    if let Some(idx) = weakmap_find(ctx, obj, key) {
459        let v_atom = weakmap_val_atom(ctx, idx);
460        obj.get(v_atom).unwrap_or_else(JSValue::undefined)
461    } else {
462        JSValue::undefined()
463    }
464}
465
466fn weakmap_has(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
467    if args.len() < 2 {
468        return JSValue::bool(false);
469    }
470    let this = &args[0];
471    let key = &args[1];
472
473    if !this.is_object() {
474        return JSValue::bool(false);
475    }
476    let obj = this.as_object();
477
478    JSValue::bool(weakmap_find(ctx, obj, key).is_some())
479}
480
481fn weakmap_delete(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
482    if args.len() < 2 {
483        return JSValue::bool(false);
484    }
485    let this = &args[0];
486    let key = &args[1];
487
488    if !this.is_object() {
489        return JSValue::bool(false);
490    }
491    let obj = this.as_object_mut();
492
493    if let Some(idx) = weakmap_find(ctx, obj, key) {
494        let k_atom = weakmap_key_atom(ctx, idx);
495        let v_atom = weakmap_val_atom(ctx, idx);
496        obj.delete(k_atom);
497        obj.delete(v_atom);
498        JSValue::bool(true)
499    } else {
500        JSValue::bool(false)
501    }
502}
503
504fn weakset_constructor(ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
505    let mut obj = JSObject::new();
506    if let Some(proto_ptr) = ctx.get_weakset_prototype() {
507        obj.prototype = Some(proto_ptr);
508    }
509    let size_atom = ctx.intern("__weakset_size__");
510    obj.set(size_atom, JSValue::new_int(0));
511    let ptr = Box::into_raw(Box::new(obj)) as usize;
512    JSValue::new_object(ptr)
513}
514
515fn weakset_size(ctx: &mut JSContext, obj: &JSObject) -> usize {
516    let size_atom = ctx.intern("__weakset_size__");
517    obj.get(size_atom)
518        .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
519        .unwrap_or(0)
520}
521
522fn weakset_val_atom(ctx: &mut JSContext, i: usize) -> crate::runtime::atom::Atom {
523    ctx.intern(&format!("__wsv_{}__", i))
524}
525
526fn weakset_find(ctx: &mut JSContext, obj: &JSObject, search: &JSValue) -> Option<usize> {
527    let size = weakset_size(ctx, obj);
528    for i in 0..size {
529        let v_atom = weakset_val_atom(ctx, i);
530        if let Some(v) = obj.get(v_atom) {
531            if v.strict_eq(search) {
532                return Some(i);
533            }
534        }
535    }
536    None
537}
538
539fn weakset_add(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
540    if args.len() < 2 {
541        return JSValue::undefined();
542    }
543    let this = &args[0];
544    let value = &args[1];
545
546    if !value.is_object() && !value.is_symbol() {
547        return JSValue::undefined();
548    }
549
550    let obj = this.as_object_mut();
551
552    if weakset_find(ctx, obj, value).is_none() {
553        let size_atom = ctx.intern("__weakset_size__");
554        let size = weakset_size(ctx, obj);
555        let v_atom = weakset_val_atom(ctx, size);
556        obj.set(v_atom, value.clone());
557        obj.set(size_atom, JSValue::new_int((size + 1) as i64));
558    }
559    this.clone()
560}
561
562fn weakset_has(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
563    if args.len() < 2 {
564        return JSValue::bool(false);
565    }
566    let this = &args[0];
567    let value = &args[1];
568
569    if !this.is_object() {
570        return JSValue::bool(false);
571    }
572    let obj = this.as_object();
573
574    JSValue::bool(weakset_find(ctx, obj, value).is_some())
575}
576
577fn weakset_delete(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
578    if args.len() < 2 {
579        return JSValue::bool(false);
580    }
581    let this = &args[0];
582    let value = &args[1];
583
584    if !this.is_object() {
585        return JSValue::bool(false);
586    }
587    let obj = this.as_object_mut();
588
589    if let Some(idx) = weakset_find(ctx, obj, value) {
590        let v_atom = weakset_val_atom(ctx, idx);
591        obj.delete(v_atom);
592        JSValue::bool(true)
593    } else {
594        JSValue::bool(false)
595    }
596}
597
598pub fn set_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
599    let mut obj = JSObject::new();
600
601    if let Some(proto_ptr) = ctx.get_set_prototype() {
602        obj.prototype = Some(proto_ptr);
603    }
604
605    let size_atom = ctx.intern("__set_size__");
606    obj.set(size_atom, JSValue::new_int(0));
607
608    if let Some(iterable) = args.first() {
609        if iterable.is_object() {
610            let iter_obj = iterable.as_object();
611            let len_atom = ctx.common_atoms.length;
612            let len = iter_obj
613                .get(len_atom)
614                .map(|v| if v.is_int() { v.get_int() as usize } else { 0 })
615                .unwrap_or(0);
616
617            for i in 0..len {
618                let idx_atom = ctx.intern(&i.to_string());
619                if let Some(val) = iter_obj.get(idx_atom) {
620                    let cur_size = set_size(ctx, &obj);
621                    if set_find(ctx, &obj, &val).is_none() {
622                        let sv_atom = set_val_atom(ctx, cur_size);
623                        obj.set(sv_atom, val);
624                        obj.set(size_atom, JSValue::new_int((cur_size + 1) as i64));
625                    }
626                }
627            }
628        }
629    }
630
631    let size_val = obj.get(size_atom).unwrap_or(JSValue::new_int(0));
632    let size_prop_atom = ctx.intern("size");
633    obj.set(size_prop_atom, size_val);
634
635    JSValue::new_object(Box::into_raw(Box::new(obj)) as usize)
636}
637
638fn sync_set_size(ctx: &mut JSContext, obj: &mut JSObject) {
639    let size_atom = ctx.intern("__set_size__");
640    let size_prop_atom = ctx.intern("size");
641    let size_val = obj.get(size_atom).unwrap_or(JSValue::new_int(0));
642    obj.set(size_prop_atom, size_val);
643}
644
645fn set_add(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
646    if args.len() < 2 {
647        return if args.is_empty() {
648            JSValue::undefined()
649        } else {
650            args[0]
651        };
652    }
653    let this = args[0];
654    if !this.is_object() {
655        return this;
656    }
657    let value = args[1];
658
659    let obj = this.as_object_mut();
660
661    if set_find(ctx, obj, &value).is_none() {
662        let size_atom = ctx.intern("__set_size__");
663        let cur_size = set_size(ctx, obj);
664        let sv_atom = set_val_atom(ctx, cur_size);
665        obj.set(sv_atom, value);
666        obj.set(size_atom, JSValue::new_int((cur_size + 1) as i64));
667        sync_set_size(ctx, obj);
668    }
669
670    this
671}
672
673fn set_has(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
674    if args.len() < 2 {
675        return JSValue::bool(false);
676    }
677    let this = args[0];
678    if !this.is_object() {
679        return JSValue::bool(false);
680    }
681    let value = args[1];
682    let obj = this.as_object();
683    JSValue::bool(set_find(ctx, obj, &value).is_some())
684}
685
686fn set_delete(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
687    if args.len() < 2 {
688        return JSValue::bool(false);
689    }
690    let this = args[0];
691    if !this.is_object() {
692        return JSValue::bool(false);
693    }
694    let value = args[1];
695    let obj = this.as_object_mut();
696
697    let size = set_size(ctx, obj);
698    let idx = match set_find(ctx, obj, &value) {
699        Some(i) => i,
700        None => return JSValue::bool(false),
701    };
702
703    for i in idx..(size - 1) {
704        let sv_next = set_val_atom(ctx, i + 1);
705        let sv_cur = set_val_atom(ctx, i);
706        let v = obj.get(sv_next).unwrap_or_else(JSValue::undefined);
707        obj.set(sv_cur, v);
708    }
709
710    let sv_last = set_val_atom(ctx, size - 1);
711    obj.delete(sv_last);
712
713    let size_atom = ctx.intern("__set_size__");
714    obj.set(size_atom, JSValue::new_int((size - 1) as i64));
715    sync_set_size(ctx, obj);
716    JSValue::bool(true)
717}
718
719fn set_clear(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
720    if args.is_empty() {
721        return JSValue::undefined();
722    }
723    let this = args[0];
724    if !this.is_object() {
725        return JSValue::undefined();
726    }
727    let obj = this.as_object_mut();
728
729    let size = set_size(ctx, obj);
730    for i in 0..size {
731        let sv = set_val_atom(ctx, i);
732        obj.delete(sv);
733    }
734    let size_atom = ctx.intern("__set_size__");
735    obj.set(size_atom, JSValue::new_int(0));
736    sync_set_size(ctx, obj);
737    JSValue::undefined()
738}
739
740fn set_for_each(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
741    if args.len() < 2 {
742        return JSValue::undefined();
743    }
744    let this = args[0];
745    let callback = args[1];
746    if !this.is_object() || !callback.is_function() {
747        return JSValue::undefined();
748    }
749
750    let obj = this.as_object();
751    let size = set_size(ctx, obj);
752
753    for i in 0..size {
754        let sv_atom = set_val_atom(ctx, i);
755        let v = obj.get(sv_atom).unwrap_or_else(JSValue::undefined);
756
757        let _ = call_callback(ctx, callback, &[v, v, this]);
758    }
759
760    JSValue::undefined()
761}
762
763fn set_values(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
764    if args.is_empty() {
765        return make_array(ctx, vec![]);
766    }
767    let this = args[0];
768    if !this.is_object() {
769        return make_array(ctx, vec![]);
770    }
771    let obj = this.as_object();
772    let size = set_size(ctx, obj);
773
774    let mut vals = Vec::with_capacity(size);
775    for i in 0..size {
776        let sv_atom = set_val_atom(ctx, i);
777        vals.push(obj.get(sv_atom).unwrap_or_else(JSValue::undefined));
778    }
779    make_array(ctx, vals)
780}
781
782fn set_keys(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
783    set_values(ctx, args)
784}
785
786fn set_entries(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
787    if args.is_empty() {
788        return make_array(ctx, vec![]);
789    }
790    let this = args[0];
791    if !this.is_object() {
792        return make_array(ctx, vec![]);
793    }
794    let obj = this.as_object();
795    let size = set_size(ctx, obj);
796
797    let mut entries = Vec::with_capacity(size);
798    for i in 0..size {
799        let sv_atom = set_val_atom(ctx, i);
800        let v = obj.get(sv_atom).unwrap_or_else(JSValue::undefined);
801        let pair = make_array(ctx, vec![v, v]);
802        entries.push(pair);
803    }
804    make_array(ctx, entries)
805}
806
807pub fn init_map_set(ctx: &mut JSContext) {
808    let mut map_proto = JSObject::new();
809    map_proto.set(ctx.intern("set"), create_builtin_function(ctx, "map_set"));
810    map_proto.set(ctx.intern("get"), create_builtin_function(ctx, "map_get"));
811    map_proto.set(ctx.intern("has"), create_builtin_function(ctx, "map_has"));
812    map_proto.set(
813        ctx.intern("delete"),
814        create_builtin_function(ctx, "map_delete"),
815    );
816    map_proto.set(
817        ctx.intern("clear"),
818        create_builtin_function(ctx, "map_clear"),
819    );
820    map_proto.set(
821        ctx.intern("forEach"),
822        create_builtin_function(ctx, "map_forEach"),
823    );
824    map_proto.set(ctx.intern("keys"), create_builtin_function(ctx, "map_keys"));
825    map_proto.set(
826        ctx.intern("values"),
827        create_builtin_function(ctx, "map_values"),
828    );
829    map_proto.set(
830        ctx.intern("entries"),
831        create_builtin_function(ctx, "map_entries"),
832    );
833
834    let sym_iter_atom = crate::builtins::symbol::get_symbol_iterator_atom(ctx);
835    map_proto.set(
836        sym_iter_atom,
837        create_builtin_function(ctx, "map_symbol_iterator"),
838    );
839
840    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
841        map_proto.prototype = Some(obj_proto_ptr);
842    }
843
844    let map_proto_ptr = Box::into_raw(Box::new(map_proto)) as usize;
845    ctx.runtime_mut().gc_heap_mut().track(map_proto_ptr);
846    ctx.set_map_prototype(map_proto_ptr);
847
848    let map_ctor = create_builtin_function(ctx, "map_constructor");
849    let map_atom = ctx.intern("Map");
850    let global = ctx.global();
851    if global.is_object() {
852        let global_obj = global.as_object_mut();
853        global_obj.set(map_atom, map_ctor);
854    }
855
856    let mut set_proto = JSObject::new();
857    set_proto.set(ctx.intern("add"), create_builtin_function(ctx, "set_add"));
858    set_proto.set(ctx.intern("has"), create_builtin_function(ctx, "set_has"));
859    set_proto.set(
860        ctx.intern("delete"),
861        create_builtin_function(ctx, "set_delete"),
862    );
863    set_proto.set(
864        ctx.intern("clear"),
865        create_builtin_function(ctx, "set_clear"),
866    );
867    set_proto.set(
868        ctx.intern("forEach"),
869        create_builtin_function(ctx, "set_forEach"),
870    );
871    set_proto.set(
872        ctx.intern("values"),
873        create_builtin_function(ctx, "set_values"),
874    );
875    set_proto.set(ctx.intern("keys"), create_builtin_function(ctx, "set_keys"));
876    set_proto.set(
877        ctx.intern("entries"),
878        create_builtin_function(ctx, "set_entries"),
879    );
880
881    let sym_iter_atom = crate::builtins::symbol::get_symbol_iterator_atom(ctx);
882    set_proto.set(
883        sym_iter_atom,
884        create_builtin_function(ctx, "set_symbol_iterator"),
885    );
886
887    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
888        set_proto.prototype = Some(obj_proto_ptr);
889    }
890
891    let set_proto_ptr = Box::into_raw(Box::new(set_proto)) as usize;
892    ctx.runtime_mut().gc_heap_mut().track(set_proto_ptr);
893    ctx.set_set_prototype(set_proto_ptr);
894
895    let set_ctor = create_builtin_function(ctx, "set_constructor");
896    let set_atom = ctx.intern("Set");
897    let global = ctx.global();
898    if global.is_object() {
899        let global_obj = global.as_object_mut();
900        global_obj.set(set_atom, set_ctor);
901    }
902
903    let mut weakmap_proto = JSObject::new();
904    weakmap_proto.set(
905        ctx.intern("set"),
906        create_builtin_function(ctx, "weakmap_set"),
907    );
908    weakmap_proto.set(
909        ctx.intern("get"),
910        create_builtin_function(ctx, "weakmap_get"),
911    );
912    weakmap_proto.set(
913        ctx.intern("has"),
914        create_builtin_function(ctx, "weakmap_has"),
915    );
916    weakmap_proto.set(
917        ctx.intern("delete"),
918        create_builtin_function(ctx, "weakmap_delete"),
919    );
920
921    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
922        weakmap_proto.prototype = Some(obj_proto_ptr);
923    }
924
925    let weakmap_proto_ptr = Box::into_raw(Box::new(weakmap_proto)) as usize;
926    ctx.runtime_mut().gc_heap_mut().track(weakmap_proto_ptr);
927    ctx.set_weakmap_prototype(weakmap_proto_ptr);
928
929    let weakmap_ctor = create_builtin_function(ctx, "weakmap_constructor");
930    let weakmap_atom = ctx.intern("WeakMap");
931    if global.is_object() {
932        let global_obj = global.as_object_mut();
933        global_obj.set(weakmap_atom, weakmap_ctor);
934    }
935
936    let mut weakset_proto = JSObject::new();
937    weakset_proto.set(
938        ctx.intern("add"),
939        create_builtin_function(ctx, "weakset_add"),
940    );
941    weakset_proto.set(
942        ctx.intern("has"),
943        create_builtin_function(ctx, "weakset_has"),
944    );
945    weakset_proto.set(
946        ctx.intern("delete"),
947        create_builtin_function(ctx, "weakset_delete"),
948    );
949
950    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
951        weakset_proto.prototype = Some(obj_proto_ptr);
952    }
953
954    let weakset_proto_ptr = Box::into_raw(Box::new(weakset_proto)) as usize;
955    ctx.runtime_mut().gc_heap_mut().track(weakset_proto_ptr);
956    ctx.set_weakset_prototype(weakset_proto_ptr);
957
958    let weakset_ctor = create_builtin_function(ctx, "weakset_constructor");
959    let weakset_atom = ctx.intern("WeakSet");
960    if global.is_object() {
961        let global_obj = global.as_object_mut();
962        global_obj.set(weakset_atom, weakset_ctor);
963    }
964}
965
966pub fn register_builtins(ctx: &mut JSContext) {
967    ctx.register_builtin(
968        "map_constructor",
969        HostFunction::new("Map", 0, map_constructor),
970    );
971    ctx.register_builtin("map_set", HostFunction::new("set", 2, map_set));
972    ctx.register_builtin("map_get", HostFunction::new("get", 1, map_get));
973    ctx.register_builtin("map_has", HostFunction::new("has", 1, map_has));
974    ctx.register_builtin("map_delete", HostFunction::new("delete", 1, map_delete));
975    ctx.register_builtin("map_clear", HostFunction::new("clear", 0, map_clear));
976    ctx.register_builtin("map_forEach", HostFunction::new("forEach", 1, map_for_each));
977    ctx.register_builtin("map_keys", HostFunction::new("keys", 0, map_keys));
978    ctx.register_builtin("map_values", HostFunction::new("values", 0, map_values));
979    ctx.register_builtin("map_entries", HostFunction::new("entries", 0, map_entries));
980    ctx.register_builtin(
981        "map_symbol_iterator",
982        HostFunction::new("[Symbol.iterator]", 0, map_symbol_iterator),
983    );
984
985    ctx.register_builtin(
986        "set_constructor",
987        HostFunction::new("Set", 0, set_constructor),
988    );
989    ctx.register_builtin("set_add", HostFunction::new("add", 1, set_add));
990    ctx.register_builtin("set_has", HostFunction::new("has", 1, set_has));
991    ctx.register_builtin("set_delete", HostFunction::new("delete", 1, set_delete));
992    ctx.register_builtin("set_clear", HostFunction::new("clear", 0, set_clear));
993    ctx.register_builtin("set_forEach", HostFunction::new("forEach", 1, set_for_each));
994    ctx.register_builtin("set_values", HostFunction::new("values", 0, set_values));
995    ctx.register_builtin("set_keys", HostFunction::new("keys", 0, set_keys));
996    ctx.register_builtin("set_entries", HostFunction::new("entries", 0, set_entries));
997    ctx.register_builtin(
998        "set_symbol_iterator",
999        HostFunction::new("[Symbol.iterator]", 0, set_symbol_iterator),
1000    );
1001
1002    ctx.register_builtin(
1003        "weakmap_constructor",
1004        HostFunction::new("WeakMap", 0, weakmap_constructor),
1005    );
1006    ctx.register_builtin("weakmap_set", HostFunction::new("set", 2, weakmap_set));
1007    ctx.register_builtin("weakmap_get", HostFunction::new("get", 1, weakmap_get));
1008    ctx.register_builtin("weakmap_has", HostFunction::new("has", 1, weakmap_has));
1009    ctx.register_builtin(
1010        "weakmap_delete",
1011        HostFunction::new("delete", 1, weakmap_delete),
1012    );
1013
1014    ctx.register_builtin(
1015        "weakset_constructor",
1016        HostFunction::new("WeakSet", 0, weakset_constructor),
1017    );
1018    ctx.register_builtin("weakset_add", HostFunction::new("add", 1, weakset_add));
1019    ctx.register_builtin("weakset_has", HostFunction::new("has", 1, weakset_has));
1020    ctx.register_builtin(
1021        "weakset_delete",
1022        HostFunction::new("delete", 1, weakset_delete),
1023    );
1024}