Skip to main content

pipa/builtins/
array.rs

1use crate::host::HostFunction;
2use crate::object::array_obj::JSArrayObject;
3use crate::object::function::JSFunction;
4use crate::object::object::JSObject;
5use crate::runtime::context::JSContext;
6
7#[inline(always)]
8fn array_get(obj: &JSObject, index: usize, ctx: &mut JSContext) -> Option<crate::value::JSValue> {
9    if obj.is_array() {
10        let ptr = obj as *const JSObject as usize;
11        if obj.is_dense_array() {
12            let arr = unsafe { &*(ptr as *const JSArrayObject) };
13            if let Some(val) = arr.get(index) {
14                return Some(val);
15            }
16        }
17    }
18
19    if let Some(val) = obj.get_indexed(index) {
20        return Some(val);
21    }
22    let key = ctx.int_atom_mut(index);
23    obj.get(key)
24}
25use crate::value::JSValue;
26
27fn init_array_length(obj: &mut JSObject, len: i64, ctx: &mut JSContext) {
28    use crate::object::object::PropertyDescriptor;
29    let len_atom = ctx.common_atoms.length;
30    if obj.has_own(len_atom) {
31        obj.set_length(len_atom, JSValue::new_int(len));
32    } else {
33        obj.define_property(
34            len_atom,
35            PropertyDescriptor {
36                value: Some(JSValue::new_int(len)),
37                writable: true,
38                enumerable: false,
39                configurable: false,
40                get: None,
41                set: None,
42            },
43        );
44    }
45
46    obj.assign_shape_from_existing_props(ctx.shape_cache_mut());
47}
48
49fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
50    let mut func = JSFunction::new_builtin(ctx.intern(name), 1);
51    func.set_builtin_marker(ctx, name);
52    let ptr = Box::into_raw(Box::new(func)) as usize;
53    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
54    debug_assert_ne!(
55        unsafe { (*(ptr as *const JSObject)).gc_slot },
56        u32::MAX,
57        "track_function did not set gc_slot"
58    );
59    JSValue::new_function(ptr)
60}
61
62pub fn init_array(ctx: &mut JSContext) {
63    let array_atom = ctx.common_atoms.array;
64
65    let mut array_func = JSFunction::new_builtin(array_atom, 1);
66    array_func.set_builtin_marker(ctx, "array_constructor");
67
68    array_func.base.set(
69        ctx.intern("isArray"),
70        create_builtin_function(ctx, "array_isArray"),
71    );
72    array_func.base.set(
73        ctx.intern("fromAsync"),
74        create_builtin_function(ctx, "array_fromAsync"),
75    );
76
77    let array_ptr = Box::into_raw(Box::new(array_func)) as usize;
78    ctx.runtime_mut().gc_heap_mut().track_function(array_ptr);
79    let array_value = JSValue::new_function(array_ptr);
80    let global = ctx.global();
81    if global.is_object() {
82        let global_obj = global.as_object_mut();
83        global_obj.set(array_atom, array_value);
84    }
85
86    let proto_atom = ctx.intern("ArrayPrototype");
87    let mut proto_obj = JSObject::new();
88    proto_obj.set(
89        ctx.intern("push"),
90        create_builtin_function(ctx, "array_push"),
91    );
92    proto_obj.set(ctx.intern("pop"), create_builtin_function(ctx, "array_pop"));
93    proto_obj.set(
94        ctx.intern("shift"),
95        create_builtin_function(ctx, "array_shift"),
96    );
97    proto_obj.set(
98        ctx.intern("unshift"),
99        create_builtin_function(ctx, "array_unshift"),
100    );
101    proto_obj.set(
102        ctx.intern("concat"),
103        create_builtin_function(ctx, "array_concat"),
104    );
105    proto_obj.set(
106        ctx.intern("slice"),
107        create_builtin_function(ctx, "array_slice"),
108    );
109    proto_obj.set(
110        ctx.intern("indexOf"),
111        create_builtin_function(ctx, "array_indexOf"),
112    );
113    proto_obj.set(
114        ctx.intern("includes"),
115        create_builtin_function(ctx, "array_includes"),
116    );
117    proto_obj.set(
118        ctx.intern("join"),
119        create_builtin_function(ctx, "array_join"),
120    );
121    init_array_length(&mut proto_obj, 0, ctx);
122
123    proto_obj.set(
124        ctx.intern("forEach"),
125        create_builtin_function(ctx, "array_forEach"),
126    );
127    proto_obj.set(ctx.intern("map"), create_builtin_function(ctx, "array_map"));
128    proto_obj.set(
129        ctx.intern("filter"),
130        create_builtin_function(ctx, "array_filter"),
131    );
132    proto_obj.set(
133        ctx.intern("reduce"),
134        create_builtin_function(ctx, "array_reduce"),
135    );
136    proto_obj.set(
137        ctx.intern("every"),
138        create_builtin_function(ctx, "array_every"),
139    );
140    proto_obj.set(
141        ctx.intern("some"),
142        create_builtin_function(ctx, "array_some"),
143    );
144    proto_obj.set(
145        ctx.intern("find"),
146        create_builtin_function(ctx, "array_find"),
147    );
148    proto_obj.set(
149        ctx.intern("findIndex"),
150        create_builtin_function(ctx, "array_findIndex"),
151    );
152    proto_obj.set(
153        ctx.intern("reduceRight"),
154        create_builtin_function(ctx, "array_reduceRight"),
155    );
156    proto_obj.set(
157        ctx.intern("sort"),
158        create_builtin_function(ctx, "array_sort"),
159    );
160    proto_obj.set(
161        ctx.intern("reverse"),
162        create_builtin_function(ctx, "array_reverse"),
163    );
164    proto_obj.set(
165        ctx.intern("fill"),
166        create_builtin_function(ctx, "array_fill"),
167    );
168    proto_obj.set(
169        ctx.intern("splice"),
170        create_builtin_function(ctx, "array_splice"),
171    );
172    proto_obj.set(
173        ctx.intern("flat"),
174        create_builtin_function(ctx, "array_flat"),
175    );
176    proto_obj.set(
177        ctx.intern("flatMap"),
178        create_builtin_function(ctx, "array_flatMap"),
179    );
180    proto_obj.set(
181        ctx.intern("findLast"),
182        create_builtin_function(ctx, "array_findLast"),
183    );
184    proto_obj.set(
185        ctx.intern("findLastIndex"),
186        create_builtin_function(ctx, "array_findLastIndex"),
187    );
188    proto_obj.set(ctx.intern("at"), create_builtin_function(ctx, "array_at"));
189    proto_obj.set(
190        ctx.intern("toSorted"),
191        create_builtin_function(ctx, "array_toSorted"),
192    );
193    proto_obj.set(
194        ctx.intern("toReversed"),
195        create_builtin_function(ctx, "array_toReversed"),
196    );
197    proto_obj.set(
198        ctx.intern("with"),
199        create_builtin_function(ctx, "array_with"),
200    );
201    proto_obj.set(
202        ctx.intern("lastIndexOf"),
203        create_builtin_function(ctx, "array_lastIndexOf"),
204    );
205    proto_obj.set(
206        ctx.intern("toSpliced"),
207        create_builtin_function(ctx, "array_toSpliced"),
208    );
209
210    proto_obj.set(ctx.common_atoms.constructor, array_value);
211
212    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
213        proto_obj.prototype = Some(obj_proto_ptr);
214    }
215
216    let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
217    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
218
219    ctx.set_array_prototype(proto_ptr);
220    let proto_value = JSValue::new_object(proto_ptr);
221
222    let array_func_ref = array_value.as_function_mut();
223    array_func_ref
224        .base
225        .set(ctx.common_atoms.prototype, proto_value);
226
227    if global.is_object() {
228        let global_obj = global.as_object_mut();
229        global_obj.set(proto_atom, proto_value);
230    }
231}
232
233pub fn register_builtins(ctx: &mut JSContext) {
234    ctx.register_builtin("array_push", HostFunction::new("push", 1, array_push));
235    ctx.register_builtin("array_pop", HostFunction::new("pop", 0, array_pop));
236    ctx.register_builtin("array_shift", HostFunction::new("shift", 0, array_shift));
237    ctx.register_builtin(
238        "array_unshift",
239        HostFunction::new("unshift", 1, array_unshift),
240    );
241    ctx.register_builtin("array_concat", HostFunction::new("concat", 1, array_concat));
242    ctx.register_builtin("array_slice", HostFunction::new("slice", 2, array_slice));
243    ctx.register_builtin(
244        "array_indexOf",
245        HostFunction::new("indexOf", 1, array_index_of),
246    );
247    ctx.register_builtin(
248        "array_includes",
249        HostFunction::new("includes", 1, array_includes),
250    );
251    ctx.register_builtin("array_join", HostFunction::new("join", 1, array_join));
252    ctx.register_builtin(
253        "array_isArray",
254        HostFunction::new("isArray", 1, array_is_array),
255    );
256    ctx.register_builtin(
257        "array_fromAsync",
258        HostFunction::new("fromAsync", 1, array_from_async),
259    );
260
261    ctx.register_builtin(
262        "array_forEach",
263        HostFunction::new("forEach", 1, array_for_each),
264    );
265    ctx.register_builtin("array_map", HostFunction::new("map", 1, array_map));
266    ctx.register_builtin("array_filter", HostFunction::new("filter", 1, array_filter));
267    ctx.register_builtin("array_reduce", HostFunction::new("reduce", 2, array_reduce));
268    ctx.register_builtin("array_every", HostFunction::new("every", 1, array_every));
269    ctx.register_builtin("array_some", HostFunction::new("some", 1, array_some));
270    ctx.register_builtin("array_find", HostFunction::new("find", 1, array_find));
271    ctx.register_builtin(
272        "array_findIndex",
273        HostFunction::new("findIndex", 1, array_find_index),
274    );
275    ctx.register_builtin(
276        "array_reduceRight",
277        HostFunction::new("reduceRight", 2, array_reduce_right),
278    );
279    ctx.register_builtin("array_sort", HostFunction::new("sort", 1, array_sort));
280    ctx.register_builtin(
281        "array_reverse",
282        HostFunction::new("reverse", 0, array_reverse),
283    );
284    ctx.register_builtin("array_fill", HostFunction::new("fill", 1, array_fill));
285    ctx.register_builtin("array_splice", HostFunction::new("splice", 2, array_splice));
286    ctx.register_builtin("array_flat", HostFunction::new("flat", 0, array_flat));
287    ctx.register_builtin(
288        "array_flatMap",
289        HostFunction::new("flatMap", 1, array_flat_map),
290    );
291    ctx.register_builtin(
292        "array_findLast",
293        HostFunction::new("findLast", 1, array_find_last),
294    );
295    ctx.register_builtin(
296        "array_findLastIndex",
297        HostFunction::new("findLastIndex", 1, array_find_last_index),
298    );
299    ctx.register_builtin("array_at", HostFunction::new("at", 1, array_at));
300    ctx.register_builtin(
301        "array_toSorted",
302        HostFunction::new("toSorted", 1, array_to_sorted),
303    );
304    ctx.register_builtin(
305        "array_toReversed",
306        HostFunction::new("toReversed", 0, array_to_reversed),
307    );
308    ctx.register_builtin("array_with", HostFunction::new("with", 2, array_with));
309    ctx.register_builtin(
310        "array_lastIndexOf",
311        HostFunction::new("lastIndexOf", 1, array_last_index_of),
312    );
313    ctx.register_builtin(
314        "array_toSpliced",
315        HostFunction::new("toSpliced", 3, array_to_spliced),
316    );
317    ctx.register_builtin(
318        "array_constructor",
319        HostFunction::new("Array", 1, array_constructor),
320    );
321}
322
323fn new_jsarray_with_proto(ctx: &mut JSContext) -> JSArrayObject {
324    let mut arr = JSArrayObject::new();
325    if let Some(proto_ptr) = ctx.get_array_prototype() {
326        arr.header.set_prototype_raw(proto_ptr);
327    }
328    arr
329}
330
331fn alloc_jsarray(arr: JSArrayObject, ctx: &mut JSContext) -> JSValue {
332    let ptr = Box::into_raw(Box::new(arr)) as usize;
333    ctx.runtime_mut().gc_heap_mut().track_array(ptr);
334    JSValue::new_object(ptr)
335}
336
337pub fn array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
338    if args.is_empty() {
339        let mut arr = new_jsarray_with_proto(ctx);
340        init_array_length(&mut arr.header, 0, ctx);
341        return alloc_jsarray(arr, ctx);
342    }
343
344    if args.len() == 1 && args[0].is_int() {
345        let len = args[0].get_int();
346        let mut arr = if len > 0 {
347            JSArrayObject::with_length(len as usize)
348        } else {
349            JSArrayObject::new()
350        };
351        if let Some(proto_ptr) = ctx.get_array_prototype() {
352            arr.header.set_prototype_raw(proto_ptr);
353        }
354        if len < 0 {
355            init_array_length(&mut arr.header, 0, ctx);
356        } else {
357            init_array_length(&mut arr.header, len, ctx);
358        }
359        return alloc_jsarray(arr, ctx);
360    }
361
362    let mut arr = new_jsarray_with_proto(ctx);
363    for arg in args.iter() {
364        arr.push(*arg);
365    }
366    init_array_length(&mut arr.header, args.len() as i64, ctx);
367    alloc_jsarray(arr, ctx)
368}
369
370fn array_push(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
371    if args.is_empty() {
372        return JSValue::new_int(0);
373    }
374    let this = &args[0];
375    if !this.is_object() {
376        return JSValue::new_int(0);
377    }
378
379    let length_atom = ctx.common_atoms.length;
380    let ptr = this.get_ptr();
381
382    if this.as_object().is_dense_array() {
383        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
384        let current_len = arr.len() as u32;
385        for arg in args.iter().skip(1) {
386            arr.push(arg.clone());
387        }
388        let new_len = current_len + (args.len() - 1) as u32;
389        arr.header.set_length_ic(
390            length_atom,
391            JSValue::new_int(new_len as i64),
392            ctx.shape_cache_mut(),
393        );
394        return JSValue::new_int(new_len as i64);
395    }
396
397    let obj = unsafe { &mut *(ptr as *mut JSObject) };
398    let current_len = if let Some(l) = obj.get(length_atom) {
399        if l.is_int() { l.get_int() as u32 } else { 0 }
400    } else {
401        0
402    };
403
404    for (i, arg) in args.iter().skip(1).enumerate() {
405        let idx = current_len as usize + i;
406        let key = ctx.int_atom_mut(idx);
407        obj.set(key, arg.clone());
408    }
409
410    let new_len = current_len + (args.len() - 1) as u32;
411    obj.set_length(length_atom, JSValue::new_int(new_len as i64));
412    JSValue::new_int(new_len as i64)
413}
414
415fn array_pop(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
416    if args.is_empty() {
417        return JSValue::undefined();
418    }
419    let this = &args[0];
420    if !this.is_object() {
421        return JSValue::undefined();
422    }
423
424    let length_atom = ctx.common_atoms.length;
425    let ptr = this.get_ptr();
426
427    if this.as_object().is_dense_array() {
428        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
429        let current_len = arr.len();
430        if current_len == 0 {
431            return JSValue::undefined();
432        }
433        let result = arr
434            .elements()
435            .last()
436            .copied()
437            .unwrap_or_else(JSValue::undefined);
438        arr.elements_mut().pop();
439        arr.header
440            .set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
441        return result;
442    }
443
444    let obj = unsafe { &mut *(ptr as *mut JSObject) };
445    let current_len = if let Some(l) = obj.get(length_atom) {
446        if l.is_int() { l.get_int() as u32 } else { 0 }
447    } else {
448        0
449    };
450
451    if current_len == 0 {
452        return JSValue::undefined();
453    }
454
455    let idx = (current_len - 1) as usize;
456    let key = ctx.int_atom_mut(idx);
457    let result = obj.get(key).unwrap_or_else(JSValue::undefined);
458    obj.delete(key);
459    obj.set_length(length_atom, JSValue::new_int(idx as i64));
460    result
461}
462
463fn array_shift(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
464    if args.is_empty() {
465        return JSValue::undefined();
466    }
467    let this = &args[0];
468    if !this.is_object() {
469        return JSValue::undefined();
470    }
471    let length_atom = ctx.common_atoms.length;
472    let ptr = this.get_ptr();
473
474    if this.as_object().is_dense_array() {
475        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
476        let current_len = arr.len();
477        if current_len == 0 {
478            return JSValue::undefined();
479        }
480        let result = arr.get(0).unwrap_or_else(JSValue::undefined);
481        arr.elements_mut().remove(0);
482        arr.header
483            .set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
484        return result;
485    }
486
487    let obj = unsafe { &mut *(ptr as *mut JSObject) };
488    let current_len = if let Some(l) = obj.get(length_atom) {
489        if l.is_int() { l.get_int() as u32 } else { 0 }
490    } else {
491        0
492    };
493
494    if current_len == 0 {
495        return JSValue::undefined();
496    }
497
498    let first_key = ctx.common_atoms.n0;
499    let result = obj.get(first_key).unwrap_or_else(JSValue::undefined);
500
501    for i in 1..current_len {
502        let old_key = ctx.int_atom_mut(i as usize);
503        let new_key = ctx.int_atom_mut(i as usize - 1);
504        if let Some(val) = obj.get(old_key) {
505            obj.set(new_key, val);
506        }
507        obj.delete(old_key);
508    }
509
510    let last_key = ctx.int_atom_mut(current_len as usize - 1);
511    obj.delete(last_key);
512    obj.set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
513    result
514}
515
516fn array_unshift(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
517    if args.is_empty() {
518        return JSValue::new_int(0);
519    }
520    let this = &args[0];
521    if !this.is_object() {
522        return JSValue::new_int(0);
523    }
524
525    let length_atom = ctx.common_atoms.length;
526    let add_count = (args.len() - 1) as usize;
527    let ptr = this.get_ptr();
528
529    if this.as_object().is_dense_array() {
530        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
531
532        let items: Vec<JSValue> = args.iter().skip(1).cloned().collect();
533        let mut new_elements = items;
534        new_elements.extend(arr.elements().iter().copied());
535        arr.set_elements(new_elements);
536        let new_len = arr.len();
537        arr.header
538            .set_length(length_atom, JSValue::new_int(new_len as i64));
539        return JSValue::new_int(new_len as i64);
540    }
541
542    let obj = unsafe { &mut *(ptr as *mut JSObject) };
543    let current_len = if let Some(l) = obj.get(length_atom) {
544        if l.is_int() { l.get_int() as u32 } else { 0 }
545    } else {
546        0
547    };
548
549    for i in (0..current_len).rev() {
550        let old_key = ctx.int_atom_mut(i as usize);
551        let new_key = ctx.int_atom_mut(i as usize + add_count);
552        if let Some(val) = obj.get(old_key) {
553            obj.set(new_key, val);
554        }
555        obj.delete(old_key);
556    }
557
558    for (i, arg) in args.iter().skip(1).enumerate() {
559        let key = ctx.int_atom_mut(i);
560        obj.set(key, arg.clone());
561    }
562
563    let new_len = current_len + add_count as u32;
564    obj.set_length(length_atom, JSValue::new_int(new_len as i64));
565    JSValue::new_int(new_len as i64)
566}
567
568fn array_concat(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
569    if args.is_empty() {
570        let mut result = new_jsarray_with_proto(ctx);
571        result
572            .header
573            .set_length(ctx.common_atoms.length, JSValue::new_int(0));
574        return alloc_jsarray(result, ctx);
575    }
576
577    let this = &args[0];
578    let mut result = new_jsarray_with_proto(ctx);
579
580    let length_atom = ctx.common_atoms.length;
581
582    if this.is_object() {
583        let obj = this.as_object();
584        let this_len = if let Some(l) = obj.get(length_atom) {
585            if l.is_int() { l.get_int() as u32 } else { 0 }
586        } else {
587            0
588        };
589
590        for i in 0..this_len {
591            if let Some(val) = array_get(obj, i as usize, ctx) {
592                result.push(val);
593            }
594        }
595    }
596
597    for arg in args.iter().skip(1) {
598        if arg.is_object() {
599            let obj = arg.as_object();
600            let arg_len = if let Some(l) = obj.get(length_atom) {
601                if l.is_int() { l.get_int() as u32 } else { 0 }
602            } else {
603                0
604            };
605
606            for i in 0..arg_len {
607                if let Some(val) = array_get(obj, i as usize, ctx) {
608                    result.push(val);
609                }
610            }
611        } else {
612            result.push(arg.clone());
613        }
614    }
615
616    let result_len = result.len();
617    result
618        .header
619        .set_length(length_atom, JSValue::new_int(result_len as i64));
620    alloc_jsarray(result, ctx)
621}
622
623fn array_slice(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
624    let mut result = new_jsarray_with_proto(ctx);
625    let length_atom = ctx.common_atoms.length;
626
627    if args.is_empty() {
628        result.header.set_length(length_atom, JSValue::new_int(0));
629        return alloc_jsarray(result, ctx);
630    }
631
632    let this = &args[0];
633    if !this.is_object() {
634        result.header.set_length(length_atom, JSValue::new_int(0));
635        return alloc_jsarray(result, ctx);
636    }
637
638    let obj = this.as_object();
639
640    let len = if let Some(l) = obj.get(length_atom) {
641        if l.is_int() { l.get_int() as i32 } else { 0 }
642    } else {
643        0
644    };
645
646    let start = if args.len() > 1 {
647        let s = args[1].get_int() as i32;
648        if s < 0 { (len + s).max(0) } else { s.min(len) }
649    } else {
650        0
651    };
652
653    let end = if args.len() > 2 {
654        let e = args[2].get_int() as i32;
655        if e < 0 { (len + e).max(0) } else { e.min(len) }
656    } else {
657        len
658    };
659
660    for i in start..end {
661        if let Some(val) = array_get(obj, i as usize, ctx) {
662            result.push(val);
663        }
664    }
665
666    result
667        .header
668        .set_length(length_atom, JSValue::new_int(result.len() as i64));
669    alloc_jsarray(result, ctx)
670}
671
672fn array_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
673    if args.len() < 2 {
674        return JSValue::new_int(-1);
675    }
676
677    let this = &args[0];
678    let search = &args[1];
679
680    if !this.is_object() {
681        return JSValue::new_int(-1);
682    }
683
684    let obj = this.as_object();
685
686    let length_atom = ctx.common_atoms.length;
687    let len = if let Some(l) = obj.get(length_atom) {
688        if l.is_int() { l.get_int() as u32 } else { 0 }
689    } else {
690        0
691    };
692
693    let from_index = if args.len() > 2 {
694        args[2].get_int() as i32
695    } else {
696        0
697    };
698
699    let start = if from_index < 0 {
700        ((len as i32) + from_index).max(0) as u32
701    } else {
702        from_index.min(len as i32) as u32
703    };
704
705    for i in start..len {
706        if let Some(val) = array_get(obj, i as usize, ctx) {
707            if val.strict_eq(search) {
708                return JSValue::new_int(i as i64);
709            }
710        }
711    }
712
713    JSValue::new_int(-1)
714}
715
716fn array_includes(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
717    if args.len() < 2 {
718        return JSValue::bool(false);
719    }
720
721    let this = &args[0];
722    let search = &args[1];
723
724    if !this.is_object() {
725        return JSValue::bool(false);
726    }
727
728    let obj = this.as_object();
729
730    let length_atom = ctx.common_atoms.length;
731    let len = if let Some(l) = obj.get(length_atom) {
732        if l.is_int() { l.get_int() as u32 } else { 0 }
733    } else {
734        0
735    };
736
737    for i in 0..len {
738        if let Some(val) = array_get(obj, i as usize, ctx) {
739            if val.strict_eq(search) {
740                return JSValue::bool(true);
741            }
742        }
743    }
744
745    JSValue::bool(false)
746}
747
748fn array_join(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
749    if args.is_empty() {
750        return JSValue::new_string(ctx.intern(""));
751    }
752
753    let this = &args[0];
754    if !this.is_object() {
755        return JSValue::new_string(ctx.intern(""));
756    }
757
758    let obj = this.as_object();
759
760    let length_atom = ctx.common_atoms.length;
761    let len = if let Some(l) = obj.get(length_atom) {
762        if l.is_int() { l.get_int() as u32 } else { 0 }
763    } else {
764        0
765    };
766
767    let separator = if args.len() > 1 && args[1].is_string() {
768        ctx.get_atom_str(args[1].get_atom()).to_string()
769    } else {
770        ",".to_string()
771    };
772
773    let mut parts = Vec::new();
774    for i in 0..len {
775        if let Some(val) = array_get(obj, i as usize, ctx) {
776            if val.is_undefined() || val.is_null() {
777                parts.push(String::new());
778            } else if val.is_string() {
779                parts.push(ctx.get_atom_str(val.get_atom()).to_string());
780            } else if val.is_int() {
781                parts.push(val.get_int().to_string());
782            } else if val.is_float() {
783                parts.push(val.get_float().to_string());
784            } else if val.is_bool() {
785                parts.push(val.get_bool().to_string());
786            } else {
787                parts.push(String::new());
788            }
789        } else {
790            parts.push(String::new());
791        }
792    }
793
794    JSValue::new_string(ctx.intern(&parts.join(&separator)))
795}
796
797fn array_is_array(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
798    if args.is_empty() {
799        return JSValue::bool(false);
800    }
801
802    let arg = &args[0];
803    if arg.is_object() {
804        let obj = arg.as_object();
805        JSValue::bool(obj.is_array())
806    } else {
807        JSValue::bool(false)
808    }
809}
810
811fn call_callback(
812    ctx: &mut JSContext,
813    callback: JSValue,
814    args: &[JSValue],
815) -> Result<JSValue, String> {
816    if let Some(ptr) = ctx.get_register_vm_ptr() {
817        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
818        vm.call_function(ctx, callback, args)
819    } else {
820        Err("call_callback: VM not available".to_string())
821    }
822}
823
824fn array_for_each(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
825    if args.len() < 2 {
826        return JSValue::undefined();
827    }
828
829    let this = &args[0];
830    let callback = &args[1];
831
832    if !this.is_object() || !callback.is_function() {
833        return JSValue::undefined();
834    }
835
836    let obj = this.as_object();
837
838    let length_atom = ctx.common_atoms.length;
839    let len = if let Some(l) = obj.get(length_atom) {
840        if l.is_int() { l.get_int() as u32 } else { 0 }
841    } else {
842        0
843    };
844
845    for i in 0..len {
846        if let Some(value) = array_get(obj, i as usize, ctx) {
847            let callback_args = vec![value, JSValue::new_int(i as i64), *this];
848            let _ = call_callback(ctx, *callback, &callback_args);
849        }
850    }
851
852    JSValue::undefined()
853}
854
855fn array_map(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
856    if args.len() < 2 {
857        let mut result = new_jsarray_with_proto(ctx);
858        result
859            .header
860            .set_length(ctx.common_atoms.length, JSValue::new_int(0));
861        return alloc_jsarray(result, ctx);
862    }
863
864    let this = &args[0];
865    let callback = &args[1];
866
867    if !this.is_object() || !callback.is_function() {
868        let mut result = new_jsarray_with_proto(ctx);
869        result
870            .header
871            .set_length(ctx.common_atoms.length, JSValue::new_int(0));
872        return alloc_jsarray(result, ctx);
873    }
874
875    let obj = this.as_object();
876
877    let length_atom = ctx.common_atoms.length;
878    let len = if let Some(l) = obj.get(length_atom) {
879        if l.is_int() { l.get_int() as u32 } else { 0 }
880    } else {
881        0
882    };
883
884    let mut result = new_jsarray_with_proto(ctx);
885
886    for i in 0..len {
887        if let Some(value) = array_get(obj, i as usize, ctx) {
888            let callback_args = vec![value];
889            if let Ok(mapped_value) = call_callback(ctx, *callback, &callback_args) {
890                result.push(mapped_value);
891            }
892        }
893    }
894
895    result
896        .header
897        .set_length(length_atom, JSValue::new_int(result.len() as i64));
898    alloc_jsarray(result, ctx)
899}
900
901fn array_filter(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
902    if args.len() < 2 {
903        let mut result = new_jsarray_with_proto(ctx);
904        result
905            .header
906            .set_length(ctx.common_atoms.length, JSValue::new_int(0));
907        return alloc_jsarray(result, ctx);
908    }
909
910    let this = &args[0];
911    let callback = &args[1];
912
913    if !this.is_object() || !callback.is_function() {
914        let mut result = new_jsarray_with_proto(ctx);
915        result
916            .header
917            .set_length(ctx.common_atoms.length, JSValue::new_int(0));
918        return alloc_jsarray(result, ctx);
919    }
920
921    let obj = this.as_object();
922
923    let length_atom = ctx.common_atoms.length;
924    let len = if let Some(l) = obj.get(length_atom) {
925        if l.is_int() { l.get_int() as u32 } else { 0 }
926    } else {
927        0
928    };
929
930    let mut result = new_jsarray_with_proto(ctx);
931
932    for i in 0..len {
933        if let Some(value) = array_get(obj, i as usize, ctx) {
934            let callback_args = vec![value, JSValue::new_int(i as i64), *this];
935            if let Ok(test_result) = call_callback(ctx, *callback, &callback_args) {
936                if test_result.is_truthy() {
937                    result.push(value);
938                }
939            }
940        }
941    }
942
943    result
944        .header
945        .set_length(length_atom, JSValue::new_int(result.len() as i64));
946    alloc_jsarray(result, ctx)
947}
948
949fn array_reduce(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
950    if args.len() < 2 {
951        return JSValue::undefined();
952    }
953
954    let this = &args[0];
955    let callback = &args[1];
956
957    if !this.is_object() || !callback.is_function() {
958        return JSValue::undefined();
959    }
960
961    let obj = this.as_object();
962
963    let length_atom = ctx.common_atoms.length;
964    let len = if let Some(l) = obj.get(length_atom) {
965        if l.is_int() { l.get_int() as u32 } else { 0 }
966    } else {
967        0
968    };
969
970    if len == 0 {
971        if args.len() > 2 {
972            return args[2];
973        }
974        return JSValue::undefined();
975    }
976
977    let mut accumulator: JSValue;
978    let mut start_index: u32 = 0;
979
980    if args.len() > 2 {
981        accumulator = args[2];
982    } else {
983        accumulator = array_get(obj, 0, ctx).unwrap_or_else(JSValue::undefined);
984        start_index = 1;
985    }
986
987    for i in start_index..len {
988        if let Some(value) = array_get(obj, i as usize, ctx) {
989            let callback_args = vec![accumulator, value, JSValue::new_int(i as i64), *this];
990            if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
991                accumulator = result;
992            }
993        }
994    }
995
996    accumulator
997}
998
999fn array_every(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1000    if args.len() < 2 {
1001        return JSValue::bool(true);
1002    }
1003
1004    let this = &args[0];
1005    let callback = &args[1];
1006
1007    if !this.is_object() || !callback.is_function() {
1008        return JSValue::bool(true);
1009    }
1010
1011    let obj = this.as_object();
1012
1013    let length_atom = ctx.common_atoms.length;
1014    let len = if let Some(l) = obj.get(length_atom) {
1015        if l.is_int() { l.get_int() as u32 } else { 0 }
1016    } else {
1017        0
1018    };
1019
1020    for i in 0..len {
1021        if let Some(value) = array_get(obj, i as usize, ctx) {
1022            let callback_args = vec![value, JSValue::new_int(i as i64), *this];
1023            if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
1024                if !result.is_truthy() {
1025                    return JSValue::bool(false);
1026                }
1027            }
1028        }
1029    }
1030
1031    JSValue::bool(true)
1032}
1033
1034fn array_some(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1035    if args.len() < 2 {
1036        return JSValue::bool(false);
1037    }
1038
1039    let this = &args[0];
1040    let callback = &args[1];
1041
1042    if !this.is_object() || !callback.is_function() {
1043        return JSValue::bool(false);
1044    }
1045
1046    let obj = this.as_object();
1047
1048    let length_atom = ctx.common_atoms.length;
1049    let len = if let Some(l) = obj.get(length_atom) {
1050        if l.is_int() { l.get_int() as u32 } else { 0 }
1051    } else {
1052        0
1053    };
1054
1055    for i in 0..len {
1056        if let Some(value) = array_get(obj, i as usize, ctx) {
1057            let callback_args = vec![value, JSValue::new_int(i as i64), *this];
1058            if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
1059                if result.is_truthy() {
1060                    return JSValue::bool(true);
1061                }
1062            }
1063        }
1064    }
1065
1066    JSValue::bool(false)
1067}
1068
1069fn array_find(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1070    if args.len() < 2 {
1071        return JSValue::undefined();
1072    }
1073
1074    let this = &args[0];
1075    let callback = &args[1];
1076
1077    if !this.is_object() || !callback.is_function() {
1078        return JSValue::undefined();
1079    }
1080
1081    let obj = this.as_object();
1082
1083    let length_atom = ctx.common_atoms.length;
1084    let len = if let Some(l) = obj.get(length_atom) {
1085        if l.is_int() { l.get_int() as u32 } else { 0 }
1086    } else {
1087        0
1088    };
1089
1090    for i in 0..len {
1091        if let Some(value) = array_get(obj, i as usize, ctx) {
1092            let callback_args = vec![value, JSValue::new_int(i as i64), *this];
1093            if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
1094                if result.is_truthy() {
1095                    return value;
1096                }
1097            }
1098        }
1099    }
1100
1101    JSValue::undefined()
1102}
1103
1104fn array_find_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1105    if args.len() < 2 {
1106        return JSValue::new_int(-1);
1107    }
1108
1109    let this = &args[0];
1110    let callback = &args[1];
1111
1112    if !this.is_object() || !callback.is_function() {
1113        return JSValue::new_int(-1);
1114    }
1115
1116    let obj = this.as_object();
1117
1118    let length_atom = ctx.common_atoms.length;
1119    let len = if let Some(l) = obj.get(length_atom) {
1120        if l.is_int() { l.get_int() as u32 } else { 0 }
1121    } else {
1122        0
1123    };
1124
1125    for i in 0..len {
1126        if let Some(value) = array_get(obj, i as usize, ctx) {
1127            let callback_args = vec![value, JSValue::new_int(i as i64), *this];
1128            if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
1129                if result.is_truthy() {
1130                    return JSValue::new_int(i as i64);
1131                }
1132            }
1133        }
1134    }
1135
1136    JSValue::new_int(-1)
1137}
1138
1139fn array_reduce_right(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1140    if args.len() < 2 {
1141        return JSValue::undefined();
1142    }
1143
1144    let this = &args[0];
1145    let callback = &args[1];
1146
1147    if !this.is_object() || !callback.is_function() {
1148        return JSValue::undefined();
1149    }
1150
1151    let obj = this.as_object();
1152
1153    let length_atom = ctx.common_atoms.length;
1154    let len = if let Some(l) = obj.get(length_atom) {
1155        if l.is_int() { l.get_int() as u32 } else { 0 }
1156    } else {
1157        0
1158    };
1159
1160    if len == 0 {
1161        if args.len() > 2 {
1162            return args[2];
1163        }
1164        return JSValue::undefined();
1165    }
1166
1167    let mut accumulator: JSValue;
1168    let mut end_index: i64;
1169
1170    if args.len() > 2 {
1171        accumulator = args[2];
1172        end_index = len as i64 - 1;
1173    } else {
1174        accumulator = array_get(obj, (len - 1) as usize, ctx).unwrap_or_else(JSValue::undefined);
1175        end_index = len as i64 - 2;
1176    }
1177
1178    while end_index >= 0 {
1179        if let Some(value) = array_get(obj, end_index as usize, ctx) {
1180            let callback_args = vec![accumulator, value, JSValue::new_int(end_index), *this];
1181            if let Ok(result) = call_callback(ctx, *callback, &callback_args) {
1182                accumulator = result;
1183            }
1184        }
1185        end_index -= 1;
1186    }
1187
1188    accumulator
1189}
1190
1191fn val_to_str(ctx: &mut JSContext, v: JSValue) -> String {
1192    if v.is_int() {
1193        format!("{}", v.get_int())
1194    } else if v.is_float() {
1195        format!("{}", v.get_float())
1196    } else if v.is_string() {
1197        ctx.get_atom_str(v.get_atom()).to_string()
1198    } else if v.is_bool() {
1199        v.get_bool().to_string()
1200    } else if v.is_null() {
1201        "null".to_string()
1202    } else {
1203        "undefined".to_string()
1204    }
1205}
1206
1207fn array_sort(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1208    if args.is_empty() {
1209        return JSValue::undefined();
1210    }
1211    let this = args[0];
1212    if !this.is_object() {
1213        return this;
1214    }
1215    let len_atom = ctx.common_atoms.length;
1216    let len = {
1217        let arr = this.as_object();
1218        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1219    };
1220    let mut elements: Vec<JSValue> = (0..len)
1221        .map(|i| {
1222            let arr = this.as_object();
1223            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1224        })
1225        .collect();
1226
1227    let cmp_fn = args.get(1).copied().filter(|v| v.is_function());
1228
1229    let n = elements.len();
1230    for i in 1..n {
1231        let mut j = i;
1232        while j > 0 {
1233            let should_swap = if let Some(func) = cmp_fn {
1234                match call_callback(ctx, func, &[elements[j - 1], elements[j]]) {
1235                    Ok(r) => r.get_float() > 0.0 || (r.is_int() && r.get_int() > 0),
1236                    Err(_) => false,
1237                }
1238            } else {
1239                let a_str = val_to_str(ctx, elements[j - 1]);
1240                let b_str = val_to_str(ctx, elements[j]);
1241                a_str > b_str
1242            };
1243            if should_swap {
1244                elements.swap(j - 1, j);
1245                j -= 1;
1246            } else {
1247                break;
1248            }
1249        }
1250    }
1251
1252    let arr = this.as_object_mut();
1253    if this.as_object().is_dense_array() {
1254        let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
1255        arr_obj.set_elements(elements);
1256    } else {
1257        if arr.has_dense_storage() {
1258            arr.set_array_elements(elements.clone());
1259        }
1260        for (i, val) in elements.into_iter().enumerate() {
1261            let key = ctx.int_atom_mut(i);
1262            arr.set(key, val);
1263        }
1264    }
1265    this
1266}
1267
1268fn array_reverse(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1269    if args.is_empty() {
1270        return JSValue::undefined();
1271    }
1272    let this = args[0];
1273    if !this.is_object() {
1274        return this;
1275    }
1276    let len_atom = ctx.common_atoms.length;
1277    let len = {
1278        let arr = this.as_object();
1279        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1280    };
1281    let mut elements: Vec<JSValue> = (0..len)
1282        .map(|i| {
1283            let arr = this.as_object();
1284            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1285        })
1286        .collect();
1287    elements.reverse();
1288    let arr = this.as_object_mut();
1289    if this.as_object().is_dense_array() {
1290        let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
1291        arr_obj.set_elements(elements);
1292    } else {
1293        if arr.has_dense_storage() {
1294            arr.set_array_elements(elements.clone());
1295        }
1296        for (i, val) in elements.into_iter().enumerate() {
1297            let key = ctx.int_atom_mut(i);
1298            arr.set(key, val);
1299        }
1300    }
1301    this
1302}
1303
1304fn array_fill(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1305    if args.is_empty() {
1306        return JSValue::undefined();
1307    }
1308    let this = args[0];
1309    if !this.is_object() {
1310        return this;
1311    }
1312    let value = args.get(1).copied().unwrap_or(JSValue::undefined());
1313    let len_atom = ctx.common_atoms.length;
1314    let len = {
1315        let arr = this.as_object();
1316        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1317    };
1318    let start = args
1319        .get(2)
1320        .map(|v| {
1321            let i = v.get_int() as isize;
1322            if i < 0 {
1323                (len as isize + i).max(0) as usize
1324            } else {
1325                (i as usize).min(len)
1326            }
1327        })
1328        .unwrap_or(0);
1329    let end = args
1330        .get(3)
1331        .map(|v| {
1332            let i = v.get_int() as isize;
1333            if i < 0 {
1334                (len as isize + i).max(0) as usize
1335            } else {
1336                (i as usize).min(len)
1337            }
1338        })
1339        .unwrap_or(len);
1340    let arr = this.as_object_mut();
1341    for i in start..end {
1342        let key = ctx.int_atom_mut(i);
1343        arr.set(key, value);
1344    }
1345    this
1346}
1347
1348fn array_splice(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1349    if args.is_empty() {
1350        return JSValue::undefined();
1351    }
1352    let this = args[0];
1353    if !this.is_object() {
1354        return JSValue::undefined();
1355    }
1356    let len_atom = ctx.common_atoms.length;
1357    let len = {
1358        let arr = this.as_object();
1359        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1360    };
1361    let start = args
1362        .get(1)
1363        .map(|v| {
1364            let i = v.get_int() as isize;
1365            if i < 0 {
1366                (len as isize + i).max(0) as usize
1367            } else {
1368                (i as usize).min(len)
1369            }
1370        })
1371        .unwrap_or(0);
1372    let delete_count = args
1373        .get(2)
1374        .map(|v| (v.get_int() as usize).min(len - start))
1375        .unwrap_or(len - start);
1376    let insert_items: Vec<JSValue> = args.iter().skip(3).copied().collect();
1377
1378    let mut elements: Vec<JSValue> = (0..len)
1379        .map(|i| {
1380            let arr = this.as_object();
1381            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1382        })
1383        .collect();
1384
1385    let removed: Vec<JSValue> = elements
1386        .splice(start..start + delete_count, insert_items)
1387        .collect();
1388
1389    let mut removed_arr = new_jsarray_with_proto(ctx);
1390    for v in removed.iter() {
1391        removed_arr.push(*v);
1392    }
1393    removed_arr
1394        .header
1395        .set_length(len_atom, JSValue::new_int(removed_arr.len() as i64));
1396
1397    let arr = this.as_object_mut();
1398    if this.as_object().is_dense_array() {
1399        let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
1400        arr_obj.set_elements(elements);
1401        arr_obj
1402            .header
1403            .set_length(len_atom, JSValue::new_int(arr_obj.len() as i64));
1404    } else {
1405        for (i, val) in elements.iter().enumerate() {
1406            let key = ctx.int_atom_mut(i);
1407            arr.set(key, *val);
1408        }
1409        arr.set_length(len_atom, JSValue::new_int(elements.len() as i64));
1410    }
1411
1412    alloc_jsarray(removed_arr, ctx)
1413}
1414
1415fn array_to_spliced(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1416    if args.is_empty() {
1417        return JSValue::undefined();
1418    }
1419    let this = args[0];
1420    if !this.is_object() {
1421        return JSValue::undefined();
1422    }
1423    let len_atom = ctx.common_atoms.length;
1424    let len = {
1425        let arr = this.as_object();
1426        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1427    };
1428    let start = args
1429        .get(1)
1430        .map(|v| {
1431            let i = v.get_int() as isize;
1432            if i < 0 {
1433                (len as isize + i).max(0) as usize
1434            } else {
1435                (i as usize).min(len)
1436            }
1437        })
1438        .unwrap_or(0);
1439    let delete_count = args
1440        .get(2)
1441        .map(|v| (v.get_int() as usize).min(len - start))
1442        .unwrap_or(len - start);
1443    let insert_items: Vec<JSValue> = args.iter().skip(3).copied().collect();
1444
1445    let mut elements: Vec<JSValue> = (0..len)
1446        .map(|i| {
1447            let arr = this.as_object();
1448            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1449        })
1450        .collect();
1451
1452    elements.splice(start..start + delete_count, insert_items);
1453
1454    let mut result_arr = new_jsarray_with_proto(ctx);
1455    for v in elements.iter() {
1456        result_arr.push(*v);
1457    }
1458    result_arr
1459        .header
1460        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
1461    alloc_jsarray(result_arr, ctx)
1462}
1463
1464fn array_flat(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1465    if args.is_empty() {
1466        return JSValue::undefined();
1467    }
1468    let this = args[0];
1469    if !this.is_object() {
1470        return JSValue::undefined();
1471    }
1472    let depth = args.get(1).map(|v| v.get_int() as usize).unwrap_or(1);
1473
1474    fn flatten(ctx: &mut JSContext, arr_val: JSValue, depth: usize, result: &mut Vec<JSValue>) {
1475        if !arr_val.is_object() {
1476            result.push(arr_val);
1477            return;
1478        }
1479        let len_atom = ctx.common_atoms.length;
1480        let arr = arr_val.as_object();
1481        if !arr.is_array() {
1482            result.push(arr_val);
1483            return;
1484        }
1485        let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
1486        let elements: Vec<JSValue> = (0..len)
1487            .map(|i| array_get(arr, i, ctx).unwrap_or(JSValue::undefined()))
1488            .collect();
1489        for el in elements {
1490            if depth > 0 && el.is_object() {
1491                let el_obj = el.as_object();
1492                if el_obj.is_array() {
1493                    flatten(ctx, el, depth - 1, result);
1494                    continue;
1495                }
1496            }
1497            result.push(el);
1498        }
1499    }
1500
1501    let mut flat_elements: Vec<JSValue> = Vec::new();
1502    flatten(ctx, this, depth, &mut flat_elements);
1503
1504    let len_atom = ctx.common_atoms.length;
1505    let mut result_arr = new_jsarray_with_proto(ctx);
1506    for v in flat_elements.iter() {
1507        result_arr.push(*v);
1508    }
1509    result_arr
1510        .header
1511        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
1512    alloc_jsarray(result_arr, ctx)
1513}
1514
1515fn array_flat_map(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1516    if args.len() < 2 {
1517        return JSValue::undefined();
1518    }
1519    let this = args[0];
1520    let callback = args[1];
1521    if !this.is_object() || !callback.is_function() {
1522        return JSValue::undefined();
1523    }
1524
1525    let len_atom = ctx.common_atoms.length;
1526    let len = {
1527        let arr = this.as_object();
1528        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1529    };
1530
1531    let mut result_elements: Vec<JSValue> = Vec::new();
1532    for i in 0..len {
1533        let el = {
1534            let arr = this.as_object();
1535            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1536        };
1537        let idx_val = JSValue::new_int(i as i64);
1538        match call_callback(ctx, callback, &[el, idx_val, this]) {
1539            Ok(mapped) => {
1540                if mapped.is_object() {
1541                    let mapped_obj = mapped.as_object();
1542                    if mapped_obj.is_array() {
1543                        let mlen = mapped_obj
1544                            .get(len_atom)
1545                            .map(|v| v.get_int() as usize)
1546                            .unwrap_or(0);
1547                        let mel_vec: Vec<JSValue> = (0..mlen)
1548                            .map(|j| array_get(mapped_obj, j, ctx).unwrap_or(JSValue::undefined()))
1549                            .collect();
1550                        result_elements.extend(mel_vec);
1551                        continue;
1552                    }
1553                }
1554                result_elements.push(mapped);
1555            }
1556            Err(_) => {}
1557        }
1558    }
1559
1560    let mut result_arr = new_jsarray_with_proto(ctx);
1561    for v in result_elements.iter() {
1562        result_arr.push(*v);
1563    }
1564    result_arr
1565        .header
1566        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
1567    alloc_jsarray(result_arr, ctx)
1568}
1569
1570fn array_find_last(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1571    if args.len() < 2 {
1572        return JSValue::undefined();
1573    }
1574    let this = args[0];
1575    let callback = args[1];
1576    if !this.is_object() {
1577        return JSValue::undefined();
1578    }
1579    let len_atom = ctx.common_atoms.length;
1580    let len = {
1581        let arr = this.as_object();
1582        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1583    };
1584    for i in (0..len).rev() {
1585        let el = {
1586            let arr = this.as_object();
1587            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1588        };
1589        let idx_val = JSValue::new_int(i as i64);
1590        if let Ok(result) = call_callback(ctx, callback, &[el, idx_val, this]) {
1591            if result.is_truthy() {
1592                return el;
1593            }
1594        }
1595    }
1596    JSValue::undefined()
1597}
1598fn array_find_last_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1599    if args.len() < 2 {
1600        return JSValue::new_int(-1);
1601    }
1602    let this = args[0];
1603    let callback = args[1];
1604    if !this.is_object() {
1605        return JSValue::new_int(-1);
1606    }
1607    let len_atom = ctx.common_atoms.length;
1608    let len = {
1609        let arr = this.as_object();
1610        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1611    };
1612    for i in (0..len).rev() {
1613        let el = {
1614            let arr = this.as_object();
1615            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1616        };
1617        let idx_val = JSValue::new_int(i as i64);
1618        if let Ok(result) = call_callback(ctx, callback, &[el, idx_val, this]) {
1619            if result.is_truthy() {
1620                return JSValue::new_int(i as i64);
1621            }
1622        }
1623    }
1624    JSValue::new_int(-1)
1625}
1626
1627fn array_at(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1628    if args.is_empty() {
1629        return JSValue::undefined();
1630    }
1631    let this = args[0];
1632    if !this.is_object() {
1633        return JSValue::undefined();
1634    }
1635    let len_atom = ctx.common_atoms.length;
1636    let arr = this.as_object();
1637    let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
1638    let idx = args.get(1).map(|v| v.get_int()).unwrap_or(0);
1639    let actual_idx = if idx < 0 { len as i64 + idx } else { idx };
1640    if actual_idx < 0 || actual_idx as usize >= len {
1641        return JSValue::undefined();
1642    }
1643    array_get(arr, actual_idx as usize, ctx).unwrap_or(JSValue::undefined())
1644}
1645
1646fn array_to_sorted(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1647    if args.is_empty() {
1648        return JSValue::undefined();
1649    }
1650    let this = args[0];
1651    if !this.is_object() {
1652        return JSValue::undefined();
1653    }
1654    let len_atom = ctx.common_atoms.length;
1655    let len = {
1656        let arr = this.as_object();
1657        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1658    };
1659    let mut elements: Vec<JSValue> = (0..len)
1660        .map(|i| {
1661            let arr = this.as_object();
1662            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1663        })
1664        .collect();
1665
1666    let cmp_fn = args.get(1).copied().filter(|v| v.is_function());
1667    let n = elements.len();
1668    for i in 1..n {
1669        let mut j = i;
1670        while j > 0 {
1671            let should_swap = if let Some(func) = cmp_fn {
1672                match call_callback(ctx, func, &[elements[j - 1], elements[j]]) {
1673                    Ok(r) => r.get_float() > 0.0 || (r.is_int() && r.get_int() > 0),
1674                    Err(_) => false,
1675                }
1676            } else {
1677                let a_str = val_to_str(ctx, elements[j - 1]);
1678                let b_str = val_to_str(ctx, elements[j]);
1679                a_str > b_str
1680            };
1681            if should_swap {
1682                elements.swap(j - 1, j);
1683                j -= 1;
1684            } else {
1685                break;
1686            }
1687        }
1688    }
1689
1690    let mut result_arr = new_jsarray_with_proto(ctx);
1691    for v in elements.iter() {
1692        result_arr.push(*v);
1693    }
1694    result_arr
1695        .header
1696        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
1697    alloc_jsarray(result_arr, ctx)
1698}
1699
1700fn array_to_reversed(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1701    if args.is_empty() {
1702        return JSValue::undefined();
1703    }
1704    let this = args[0];
1705    if !this.is_object() {
1706        return JSValue::undefined();
1707    }
1708    let len_atom = ctx.common_atoms.length;
1709    let len = {
1710        let arr = this.as_object();
1711        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1712    };
1713    let mut elements: Vec<JSValue> = (0..len)
1714        .map(|i| {
1715            let arr = this.as_object();
1716            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1717        })
1718        .collect();
1719    elements.reverse();
1720    let mut result_arr = new_jsarray_with_proto(ctx);
1721    for v in elements.iter() {
1722        result_arr.push(*v);
1723    }
1724    result_arr
1725        .header
1726        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
1727    alloc_jsarray(result_arr, ctx)
1728}
1729
1730fn array_with(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1731    if args.len() < 3 {
1732        return JSValue::undefined();
1733    }
1734    let this = args[0];
1735    let idx = args[1].get_int();
1736    let value = args[2];
1737    if !this.is_object() {
1738        return JSValue::undefined();
1739    }
1740    let len_atom = ctx.common_atoms.length;
1741    let len = {
1742        let arr = this.as_object();
1743        arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0)
1744    };
1745    let mut elements: Vec<JSValue> = (0..len)
1746        .map(|i| {
1747            let arr = this.as_object();
1748            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1749        })
1750        .collect();
1751    let actual_idx = if idx < 0 {
1752        (len as i64 + idx) as usize
1753    } else {
1754        idx as usize
1755    };
1756    if actual_idx < len {
1757        elements[actual_idx] = value;
1758    }
1759    let mut result_arr = new_jsarray_with_proto(ctx);
1760    for v in elements.iter() {
1761        result_arr.push(*v);
1762    }
1763    result_arr
1764        .header
1765        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
1766    alloc_jsarray(result_arr, ctx)
1767}
1768
1769fn array_last_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1770    if args.len() < 2 {
1771        return JSValue::new_int(-1);
1772    }
1773    let this = args[0];
1774    let search = args[1];
1775    if !this.is_object() {
1776        return JSValue::new_int(-1);
1777    }
1778    let len_atom = ctx.common_atoms.length;
1779    let arr = this.as_object();
1780    let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
1781    for i in (0..len).rev() {
1782        if let Some(el) = array_get(arr, i, ctx) {
1783            if el.strict_eq(&search) {
1784                return JSValue::new_int(i as i64);
1785            }
1786        }
1787    }
1788    JSValue::new_int(-1)
1789}
1790
1791fn array_from_async(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1792    if args.is_empty() {
1793        return create_thenable_result(ctx, Vec::new());
1794    }
1795
1796    let items = &args[0];
1797
1798    if items.is_null() || items.is_undefined() {
1799        return create_thenable_result(ctx, Vec::new());
1800    }
1801
1802    if items.is_object() {
1803        let obj = items.as_object();
1804        let then_atom = ctx.common_atoms.then;
1805        if obj.get(then_atom).is_some() {
1806            let result = vec![*items];
1807            return create_thenable_result(ctx, result);
1808        }
1809
1810        let len_atom = ctx.common_atoms.length;
1811        if let Some(len_val) = obj.get(len_atom) {
1812            let len = len_val.get_int() as usize;
1813            let mut elements: Vec<JSValue> = Vec::with_capacity(len);
1814            for i in 0..len {
1815                if let Some(val) = array_get(obj, i, ctx) {
1816                    elements.push(val);
1817                } else {
1818                    elements.push(JSValue::undefined());
1819                }
1820            }
1821            return create_thenable_result(ctx, elements);
1822        }
1823    }
1824
1825    create_thenable_result(ctx, Vec::new())
1826}
1827
1828fn create_thenable_result(ctx: &mut JSContext, elements: Vec<JSValue>) -> JSValue {
1829    use crate::object::object::JSObject;
1830
1831    let mut result_arr = new_jsarray_with_proto(ctx);
1832    let len_atom = ctx.common_atoms.length;
1833    for val in elements.iter() {
1834        result_arr.push(*val);
1835    }
1836    result_arr
1837        .header
1838        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
1839    let arr_value = alloc_jsarray(result_arr, ctx);
1840
1841    let mut promise = JSObject::new_promise();
1842
1843    if let Some(proto_ptr) = ctx.get_promise_prototype() {
1844        promise.prototype = Some(proto_ptr);
1845    }
1846
1847    let state_atom = ctx.intern("__promise_state__");
1848    promise.set(state_atom, JSValue::new_int(1));
1849
1850    let result_atom = ctx.intern("__promise_result__");
1851    promise.set(result_atom, arr_value);
1852
1853    let reactions_atom = ctx.intern("__promise_reactions__");
1854    promise.set(reactions_atom, JSValue::null());
1855
1856    let promise_ptr = Box::into_raw(Box::new(promise)) as usize;
1857    JSValue::new_object(promise_ptr)
1858}