Skip to main content

pipa/builtins/
array.rs

1use crate::builtins::string::js_to_length;
2use crate::host::HostFunction;
3use crate::object::array_obj::JSArrayObject;
4use crate::object::function::JSFunction;
5use crate::object::object::JSObject;
6use crate::runtime::context::JSContext;
7
8fn throw_type_error(ctx: &mut JSContext, msg: &str) {
9    let mut err = JSObject::new();
10    if let Some(proto_ptr) = ctx.get_type_error_prototype() {
11        err.prototype = Some(proto_ptr);
12    }
13    err.set(ctx.common_atoms.name, JSValue::new_string(ctx.intern("TypeError")));
14    err.set(ctx.common_atoms.message, JSValue::new_string(ctx.intern(msg)));
15    let ptr = Box::into_raw(Box::new(err)) as usize;
16    ctx.runtime_mut().gc_heap_mut().track(ptr);
17    ctx.pending_exception = Some(JSValue::new_object(ptr));
18}
19
20fn throw_range_error(ctx: &mut JSContext, msg: &str) {
21    let mut err = JSObject::new();
22    if let Some(proto_ptr) = ctx.get_range_error_prototype() {
23        err.prototype = Some(proto_ptr);
24    }
25    err.set(ctx.common_atoms.name, JSValue::new_string(ctx.intern("RangeError")));
26    err.set(ctx.common_atoms.message, JSValue::new_string(ctx.intern(msg)));
27    let ptr = Box::into_raw(Box::new(err)) as usize;
28    ctx.runtime_mut().gc_heap_mut().track(ptr);
29    ctx.pending_exception = Some(JSValue::new_object(ptr));
30}
31
32const ARRAY_LENGTH_LIMIT: u64 = 4294967295;
33
34#[inline(always)]
35fn require_object_coercible(ctx: &mut JSContext, val: &JSValue) -> bool {
36    if val.is_undefined() || val.is_null() {
37        throw_type_error(ctx, "Cannot convert undefined or null to object");
38        return false;
39    }
40    true
41}
42
43#[inline(always)]
44pub fn array_get(obj: &JSObject, index: usize, ctx: &mut JSContext) -> Option<crate::value::JSValue> {
45    if obj.is_array() {
46        let ptr = obj as *const JSObject as usize;
47        if obj.is_dense_array() {
48            let arr = unsafe { &*(ptr as *const JSArrayObject) };
49            if let Some(val) = arr.get(index) {
50                return Some(val);
51            }
52        }
53    }
54
55    if let Some(val) = obj.get_indexed(index) {
56        return Some(val);
57    }
58    let key = ctx.int_atom_mut(index);
59    obj.get(key)
60}
61
62pub fn array_set(obj: &mut JSObject, index: usize, value: crate::value::JSValue) {
63    if obj.is_array() {
64        let ptr = obj as *mut JSObject as *mut JSArrayObject;
65        if obj.is_dense_array() {
66            unsafe { (*ptr).set(index, value) };
67            return;
68        }
69    }
70    obj.set_indexed(index, value);
71}
72
73pub fn array_delete(obj: &mut JSObject, index: usize) {
74    if obj.is_array() && obj.is_dense_array() {
75        let ptr = obj as *mut JSObject as *mut JSArrayObject;
76        let elems = unsafe { &mut (*ptr).elements };
77        if index < elems.len() {
78            elems[index] = crate::value::JSValue::undefined();
79        }
80        return;
81    }
82    obj.set_indexed(index, crate::value::JSValue::undefined());
83}
84use crate::value::JSValue;
85
86fn safe_array_len_with_ctx(obj: &JSObject, len_atom: crate::runtime::atom::Atom, ctx: &mut JSContext) -> u64 {
87    let val = obj.get(len_atom);
88    match &val {
89        Some(v) => {
90            let actual = if v.is_function() {
91                let this_val = JSValue::new_object(obj as *const _ as usize);
92                match call_callback_with_this(ctx, *v, this_val, &[]) {
93                    Ok(result) => result,
94                    Err(_) => return 0,
95                }
96            } else {
97                *v
98            };
99            if actual.is_string() {
100                let s = ctx.get_atom_str(actual.get_atom());
101                let n: f64 = s.parse().unwrap_or(f64::NAN);
102                if n.is_nan() || n <= 0.0 { 0 }
103                else if n.is_infinite() { u64::MAX }
104                else { n as u64 }
105            } else {
106                js_to_length(&actual)
107            }
108        }
109        None => 0,
110    }
111}
112
113fn to_object_or_return_undefined(val: JSValue, ctx: &mut JSContext) -> JSValue {
114    if val.is_object_like() {
115        val
116    } else {
117        crate::builtins::object::object_to_object(ctx, &val)
118    }
119}
120
121fn init_array_length(obj: &mut JSObject, len: i64, ctx: &mut JSContext) {
122    use crate::object::object::PropertyDescriptor;
123    let len_atom = ctx.common_atoms.length;
124    if obj.has_own(len_atom) {
125        obj.set_length(len_atom, JSValue::new_int(len));
126    } else {
127        obj.define_property(
128            len_atom,
129            PropertyDescriptor {
130                value: Some(JSValue::new_int(len)),
131                writable: true,
132                enumerable: false,
133                configurable: false,
134                get: None,
135                set: None,
136            },
137        );
138    }
139
140    obj.assign_shape_from_existing_props(ctx.shape_cache_mut());
141}
142
143fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
144    let mut func = JSFunction::new_builtin(ctx.intern(name), 1);
145    func.set_builtin_marker(ctx, name);
146    let ptr = Box::into_raw(Box::new(func)) as usize;
147    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
148    debug_assert_ne!(
149        unsafe { (*(ptr as *const JSObject)).gc_slot },
150        u32::MAX,
151        "track_function did not set gc_slot"
152    );
153    JSValue::new_function(ptr)
154}
155
156pub fn init_array(ctx: &mut JSContext) {
157    let array_atom = ctx.common_atoms.array;
158
159    fn set_ne(obj: &mut JSObject, key: crate::runtime::atom::Atom, val: JSValue) {
160        obj.define_property(
161            key,
162            crate::object::object::PropertyDescriptor {
163                value: Some(val),
164                writable: true,
165                enumerable: false,
166                configurable: true,
167                get: None,
168                set: None,
169            },
170        );
171    }
172
173    let mut array_func = JSFunction::new_builtin(array_atom, 1);
174    array_func.set_builtin_marker(ctx, "array_constructor");
175
176    set_ne(
177        &mut array_func.base,
178        ctx.intern("isArray"),
179        create_builtin_function(ctx, "array_isArray"),
180    );
181    set_ne(
182        &mut array_func.base,
183        ctx.intern("from"),
184        create_builtin_function(ctx, "array_from"),
185    );
186    set_ne(
187        &mut array_func.base,
188        ctx.intern("of"),
189        create_builtin_function(ctx, "array_of"),
190    );
191    set_ne(
192        &mut array_func.base,
193        ctx.intern("fromAsync"),
194        create_builtin_function(ctx, "array_fromAsync"),
195    );
196
197    let array_ptr = Box::into_raw(Box::new(array_func)) as usize;
198    ctx.runtime_mut().gc_heap_mut().track_function(array_ptr);
199    let array_value = JSValue::new_function(array_ptr);
200    let global = ctx.global();
201    if global.is_object() {
202        let global_obj = global.as_object_mut();
203        crate::builtins::global::set_non_enumerable(global_obj, array_atom, array_value);
204    }
205
206    let proto_atom = ctx.intern("ArrayPrototype");
207    let mut proto_obj = JSObject::new_array();
208    set_ne(
209        &mut proto_obj,
210        ctx.intern("push"),
211        create_builtin_function(ctx, "array_push"),
212    );
213    set_ne(
214        &mut proto_obj,
215        ctx.intern("pop"),
216        create_builtin_function(ctx, "array_pop"),
217    );
218    set_ne(
219        &mut proto_obj,
220        ctx.intern("shift"),
221        create_builtin_function(ctx, "array_shift"),
222    );
223    set_ne(
224        &mut proto_obj,
225        ctx.intern("unshift"),
226        create_builtin_function(ctx, "array_unshift"),
227    );
228    set_ne(
229        &mut proto_obj,
230        ctx.intern("concat"),
231        create_builtin_function(ctx, "array_concat"),
232    );
233    set_ne(
234        &mut proto_obj,
235        ctx.intern("slice"),
236        create_builtin_function(ctx, "array_slice"),
237    );
238    set_ne(
239        &mut proto_obj,
240        ctx.intern("indexOf"),
241        create_builtin_function(ctx, "array_indexOf"),
242    );
243    set_ne(
244        &mut proto_obj,
245        ctx.intern("includes"),
246        create_builtin_function(ctx, "array_includes"),
247    );
248    set_ne(
249        &mut proto_obj,
250        ctx.intern("join"),
251        create_builtin_function(ctx, "array_join"),
252    );
253    init_array_length(&mut proto_obj, 0, ctx);
254
255    set_ne(
256        &mut proto_obj,
257        ctx.intern("forEach"),
258        create_builtin_function(ctx, "array_forEach"),
259    );
260    set_ne(
261        &mut proto_obj,
262        ctx.intern("map"),
263        create_builtin_function(ctx, "array_map"),
264    );
265    set_ne(
266        &mut proto_obj,
267        ctx.intern("filter"),
268        create_builtin_function(ctx, "array_filter"),
269    );
270    set_ne(
271        &mut proto_obj,
272        ctx.intern("reduce"),
273        create_builtin_function(ctx, "array_reduce"),
274    );
275    set_ne(
276        &mut proto_obj,
277        ctx.intern("every"),
278        create_builtin_function(ctx, "array_every"),
279    );
280    set_ne(
281        &mut proto_obj,
282        ctx.intern("some"),
283        create_builtin_function(ctx, "array_some"),
284    );
285    set_ne(
286        &mut proto_obj,
287        ctx.intern("find"),
288        create_builtin_function(ctx, "array_find"),
289    );
290    set_ne(
291        &mut proto_obj,
292        ctx.intern("findIndex"),
293        create_builtin_function(ctx, "array_findIndex"),
294    );
295    set_ne(
296        &mut proto_obj,
297        ctx.intern("reduceRight"),
298        create_builtin_function(ctx, "array_reduceRight"),
299    );
300    set_ne(
301        &mut proto_obj,
302        ctx.intern("sort"),
303        create_builtin_function(ctx, "array_sort"),
304    );
305    set_ne(
306        &mut proto_obj,
307        ctx.intern("reverse"),
308        create_builtin_function(ctx, "array_reverse"),
309    );
310    set_ne(
311        &mut proto_obj,
312        ctx.intern("fill"),
313        create_builtin_function(ctx, "array_fill"),
314    );
315    set_ne(
316        &mut proto_obj,
317        ctx.intern("splice"),
318        create_builtin_function(ctx, "array_splice"),
319    );
320    set_ne(
321        &mut proto_obj,
322        ctx.intern("flat"),
323        create_builtin_function(ctx, "array_flat"),
324    );
325    set_ne(
326        &mut proto_obj,
327        ctx.intern("flatMap"),
328        create_builtin_function(ctx, "array_flatMap"),
329    );
330    set_ne(
331        &mut proto_obj,
332        ctx.intern("findLast"),
333        create_builtin_function(ctx, "array_findLast"),
334    );
335    set_ne(
336        &mut proto_obj,
337        ctx.intern("findLastIndex"),
338        create_builtin_function(ctx, "array_findLastIndex"),
339    );
340    set_ne(
341        &mut proto_obj,
342        ctx.intern("at"),
343        create_builtin_function(ctx, "array_at"),
344    );
345    set_ne(
346        &mut proto_obj,
347        ctx.intern("toSorted"),
348        create_builtin_function(ctx, "array_toSorted"),
349    );
350    set_ne(
351        &mut proto_obj,
352        ctx.intern("toReversed"),
353        create_builtin_function(ctx, "array_toReversed"),
354    );
355    set_ne(
356        &mut proto_obj,
357        ctx.intern("with"),
358        create_builtin_function(ctx, "array_with"),
359    );
360    set_ne(
361        &mut proto_obj,
362        ctx.intern("lastIndexOf"),
363        create_builtin_function(ctx, "array_lastIndexOf"),
364    );
365    set_ne(
366        &mut proto_obj,
367        ctx.intern("toSpliced"),
368        create_builtin_function(ctx, "array_toSpliced"),
369    );
370
371    set_ne(&mut proto_obj, ctx.common_atoms.constructor, array_value);
372
373    let sym_iter_atom = crate::builtins::symbol::get_symbol_iterator_atom(ctx);
374    set_ne(
375        &mut proto_obj,
376        sym_iter_atom,
377        create_builtin_function(ctx, "array_symbol_iterator"),
378    );
379
380    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
381        proto_obj.prototype = Some(obj_proto_ptr);
382    }
383
384    let proto_ptr = Box::into_raw(Box::new(proto_obj)) as usize;
385    ctx.runtime_mut().gc_heap_mut().track(proto_ptr);
386
387    ctx.set_array_prototype(proto_ptr);
388    let proto_value = JSValue::new_object(proto_ptr);
389
390    let array_func_ref = array_value.as_function_mut();
391    array_func_ref
392        .base
393        .set(ctx.common_atoms.prototype, proto_value);
394
395    crate::builtins::symbol::install_species_accessor(ctx, &array_value);
396
397    if global.is_object() {
398        let global_obj = global.as_object_mut();
399        global_obj.set(proto_atom, proto_value);
400    }
401
402    let mut iter_proto = JSObject::new();
403    let next_builtin = create_builtin_function(ctx, "array_iterator_next");
404    set_ne(&mut iter_proto, ctx.intern("next"), next_builtin);
405    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
406        iter_proto.prototype = Some(obj_proto_ptr);
407    }
408    let iter_proto_ptr = Box::into_raw(Box::new(iter_proto)) as usize;
409    ctx.runtime_mut().gc_heap_mut().track(iter_proto_ptr);
410    ctx.set_array_iterator_prototype(iter_proto_ptr);
411}
412
413fn array_symbol_iterator(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
414    let base = args.first().copied().unwrap_or(JSValue::undefined());
415    if !require_object_coercible(ctx, &base) {
416        return JSValue::undefined();
417    }
418    let this = to_object_or_return_undefined(base, ctx);
419    let arr_atom = ctx.common_atoms.__iter_arr__;
420    let idx_atom = ctx.common_atoms.__iter_idx__;
421    let mut iter_obj = JSObject::new();
422    iter_obj.set(arr_atom, this);
423    iter_obj.set(idx_atom, JSValue::new_int(0));
424    if let Some(proto) = ctx.get_array_iterator_prototype() {
425        iter_obj.prototype = Some(proto as *mut JSObject);
426    }
427    let ptr = Box::into_raw(Box::new(iter_obj)) as usize;
428    ctx.runtime_mut().gc_heap_mut().track(ptr);
429    JSValue::new_object(ptr)
430}
431
432fn array_iterator_next(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
433    let base = args.first().copied().unwrap_or(JSValue::undefined());
434    if !require_object_coercible(ctx, &base) {
435        return JSValue::undefined();
436    }
437    let this = to_object_or_return_undefined(base, ctx);
438    let obj_ptr = this.get_ptr();
439    let obj = unsafe { &*(obj_ptr as *const JSObject) };
440    let obj_mut = unsafe { &mut *(obj_ptr as *mut JSObject) };
441    let arr_atom = ctx.common_atoms.__iter_arr__;
442    let idx_atom = ctx.common_atoms.__iter_idx__;
443    let iter_idx = obj.get(idx_atom).map(|v| if v.is_int() { v.get_int() as usize } else { 0 }).unwrap_or(0);
444    let arr_val = match obj.get(arr_atom) {
445        Some(v) => v,
446        None => {
447            throw_type_error(ctx, "Iterator has no [[IteratedObject]] slot");
448            return JSValue::undefined();
449        }
450    };
451    if !arr_val.is_object_like() {
452        throw_type_error(ctx, "Iterator [[IteratedObject]] is not an object");
453        return JSValue::undefined();
454    }
455    let arr_obj = arr_val.as_object();
456    let len = arr_obj.get(ctx.common_atoms.length).map(|v| js_to_length(&v) as usize).unwrap_or(0);
457    if iter_idx < len {
458        let value = super::array::array_get(arr_obj, iter_idx, ctx).unwrap_or(JSValue::undefined());
459        obj_mut.set(idx_atom, JSValue::new_int((iter_idx + 1) as i64));
460        let mut result = JSObject::new();
461        let done_atom = ctx.intern("done");
462        let value_atom = ctx.intern("value");
463        result.set(value_atom, value);
464        result.set(done_atom, JSValue::bool(false));
465        let ptr = Box::into_raw(Box::new(result)) as usize;
466        ctx.runtime_mut().gc_heap_mut().track(ptr);
467        JSValue::new_object(ptr)
468    } else {
469        let mut result = JSObject::new();
470        let done_atom = ctx.intern("done");
471        let value_atom = ctx.intern("value");
472        result.set(value_atom, JSValue::undefined());
473        result.set(done_atom, JSValue::bool(true));
474        let ptr = Box::into_raw(Box::new(result)) as usize;
475        ctx.runtime_mut().gc_heap_mut().track(ptr);
476        JSValue::new_object(ptr)
477    }
478}
479
480pub fn register_builtins(ctx: &mut JSContext) {
481    ctx.register_builtin("array_push", HostFunction::method("push", 1, array_push));
482    ctx.register_builtin("array_pop", HostFunction::method("pop", 0, array_pop));
483    ctx.register_builtin("array_shift", HostFunction::method("shift", 0, array_shift));
484    ctx.register_builtin(
485        "array_unshift",
486        HostFunction::method("unshift", 1, array_unshift),
487    );
488    ctx.register_builtin(
489        "array_concat",
490        HostFunction::method("concat", 1, array_concat),
491    );
492    ctx.register_builtin("array_slice", HostFunction::method("slice", 2, array_slice));
493    ctx.register_builtin(
494        "array_indexOf",
495        HostFunction::method("indexOf", 1, array_index_of),
496    );
497    ctx.register_builtin(
498        "array_includes",
499        HostFunction::method("includes", 1, array_includes),
500    );
501    ctx.register_builtin("array_join", HostFunction::method("join", 1, array_join));
502    ctx.register_builtin(
503        "array_isArray",
504        HostFunction::new("isArray", 1, array_is_array),
505    );
506    ctx.register_builtin(
507        "array_from",
508        HostFunction::method("from", 1, array_from),
509    );
510    ctx.register_builtin(
511        "array_of",
512        HostFunction::method("of", 0, array_of),
513    );
514    ctx.register_builtin(
515        "array_fromAsync",
516        HostFunction::new("fromAsync", 1, array_from_async),
517    );
518
519    ctx.register_builtin(
520        "array_forEach",
521        HostFunction::method("forEach", 1, array_for_each),
522    );
523    ctx.register_builtin("array_map", HostFunction::method("map", 1, array_map));
524    ctx.register_builtin(
525        "array_filter",
526        HostFunction::method("filter", 1, array_filter),
527    );
528    ctx.register_builtin(
529        "array_reduce",
530        HostFunction::method("reduce", 2, array_reduce),
531    );
532    ctx.register_builtin("array_every", HostFunction::method("every", 1, array_every));
533    ctx.register_builtin("array_some", HostFunction::method("some", 1, array_some));
534    ctx.register_builtin("array_find", HostFunction::method("find", 1, array_find));
535    ctx.register_builtin(
536        "array_findIndex",
537        HostFunction::method("findIndex", 1, array_find_index),
538    );
539    ctx.register_builtin(
540        "array_reduceRight",
541        HostFunction::method("reduceRight", 2, array_reduce_right),
542    );
543    ctx.register_builtin("array_sort", HostFunction::method("sort", 1, array_sort));
544    ctx.register_builtin(
545        "array_iterator_next",
546        HostFunction::method("next", 0, array_iterator_next),
547    );
548    ctx.register_builtin(
549        "array_reverse",
550        HostFunction::method("reverse", 0, array_reverse),
551    );
552    ctx.register_builtin("array_fill", HostFunction::method("fill", 1, array_fill));
553    ctx.register_builtin(
554        "array_splice",
555        HostFunction::method("splice", 2, array_splice),
556    );
557    ctx.register_builtin("array_flat", HostFunction::method("flat", 0, array_flat));
558    ctx.register_builtin(
559        "array_flatMap",
560        HostFunction::method("flatMap", 1, array_flat_map),
561    );
562    ctx.register_builtin(
563        "array_findLast",
564        HostFunction::method("findLast", 1, array_find_last),
565    );
566    ctx.register_builtin(
567        "array_findLastIndex",
568        HostFunction::method("findLastIndex", 1, array_find_last_index),
569    );
570    ctx.register_builtin("array_at", HostFunction::method("at", 1, array_at));
571    ctx.register_builtin(
572        "array_toSorted",
573        HostFunction::method("toSorted", 1, array_to_sorted),
574    );
575    ctx.register_builtin(
576        "array_toReversed",
577        HostFunction::method("toReversed", 0, array_to_reversed),
578    );
579    ctx.register_builtin("array_with", HostFunction::method("with", 2, array_with));
580    ctx.register_builtin(
581        "array_lastIndexOf",
582        HostFunction::method("lastIndexOf", 1, array_last_index_of),
583    );
584    ctx.register_builtin(
585        "array_toSpliced",
586        HostFunction::method("toSpliced", 3, array_to_spliced),
587    );
588    ctx.register_builtin(
589        "array_symbol_iterator",
590        HostFunction::method("[Symbol.iterator]", 0, array_symbol_iterator),
591    );
592    ctx.register_builtin(
593        "array_constructor",
594        HostFunction::ctor("Array", 1, array_constructor),
595    );
596}
597
598fn new_jsarray_with_proto(ctx: &mut JSContext) -> JSArrayObject {
599    let mut arr = JSArrayObject::new();
600    if let Some(proto_ptr) = ctx.get_array_prototype() {
601        arr.header.set_prototype_raw(proto_ptr);
602    }
603    arr
604}
605
606fn alloc_jsarray(arr: JSArrayObject, ctx: &mut JSContext) -> JSValue {
607    let ptr = Box::into_raw(Box::new(arr)) as usize;
608    ctx.runtime_mut().gc_heap_mut().track_array(ptr);
609    JSValue::new_object(ptr)
610}
611
612pub fn array_constructor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
613    if args.is_empty() {
614        let mut arr = new_jsarray_with_proto(ctx);
615        init_array_length(&mut arr.header, 0, ctx);
616        return alloc_jsarray(arr, ctx);
617    }
618
619    if args.len() == 1 && args[0].is_int() {
620        let len = args[0].get_int();
621        let mut arr = if len > 0 {
622            JSArrayObject::with_length(len as usize)
623        } else {
624            JSArrayObject::new()
625        };
626        if let Some(proto_ptr) = ctx.get_array_prototype() {
627            arr.header.set_prototype_raw(proto_ptr);
628        }
629        if len < 0 {
630            init_array_length(&mut arr.header, 0, ctx);
631        } else {
632            init_array_length(&mut arr.header, len, ctx);
633        }
634        return alloc_jsarray(arr, ctx);
635    }
636
637    let mut arr = new_jsarray_with_proto(ctx);
638    for arg in args.iter() {
639        arr.push(*arg);
640    }
641    init_array_length(&mut arr.header, args.len() as i64, ctx);
642    alloc_jsarray(arr, ctx)
643}
644
645fn array_push(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
646    if args.is_empty() {
647        return JSValue::new_int(0);
648    }
649    let this_val = args[0];
650    if !require_object_coercible(ctx, &this_val) {
651        return JSValue::new_int(0);
652    }
653    let this_obj = to_object_or_return_undefined(this_val, ctx);
654
655    let length_atom = ctx.common_atoms.length;
656    let ptr = this_obj.get_ptr();
657
658    if this_obj.as_object().is_dense_array() {
659        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
660        if !arr.header.is_property_writable(length_atom) {
661            throw_type_error(ctx, "Cannot assign to read only property 'length' of object");
662            return JSValue::undefined();
663        }
664        let current_len = arr.len() as u32;
665        for arg in args.iter().skip(1) {
666            arr.push(arg.clone());
667        }
668        let new_len = current_len + (args.len() - 1) as u32;
669        arr.header.set_length_ic(
670            length_atom,
671            JSValue::new_int(new_len as i64),
672            ctx.shape_cache_mut(),
673        );
674        return JSValue::new_int(new_len as i64);
675    }
676
677    let obj = unsafe { &mut *(ptr as *mut JSObject) };
678    if !obj.is_property_writable(length_atom) {
679        throw_type_error(ctx, "Cannot assign to read only property 'length' of object");
680        return JSValue::undefined();
681    }
682    let current_len = if let Some(l) = obj.get(length_atom) {
683        if l.is_int() { l.get_int() as u32 } else { 0 }
684    } else {
685        0
686    };
687
688    for (i, arg) in args.iter().skip(1).enumerate() {
689        let idx = current_len as usize + i;
690        let key = ctx.int_atom_mut(idx);
691        obj.set(key, arg.clone());
692    }
693
694    let new_len = current_len + (args.len() - 1) as u32;
695    obj.set_length(length_atom, JSValue::new_int(new_len as i64));
696    JSValue::new_int(new_len as i64)
697}
698
699fn array_pop(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
700    if args.is_empty() {
701        return JSValue::undefined();
702    }
703    let this_val = args[0];
704    if !require_object_coercible(ctx, &this_val) {
705        return JSValue::undefined();
706    }
707    let this_obj = to_object_or_return_undefined(this_val, ctx);
708
709    let length_atom = ctx.common_atoms.length;
710    let ptr = this_obj.get_ptr();
711
712    if this_obj.as_object().is_dense_array() {
713        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
714        let current_len = arr.len();
715        if current_len == 0 {
716            return JSValue::undefined();
717        }
718        let result = arr
719            .elements()
720            .last()
721            .copied()
722            .unwrap_or_else(JSValue::undefined);
723        arr.elements_mut().pop();
724        arr.header
725            .set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
726        return result;
727    }
728
729    let obj = unsafe { &mut *(ptr as *mut JSObject) };
730    let current_len = if let Some(l) = obj.get(length_atom) {
731        if l.is_int() { l.get_int() as u32 } else { 0 }
732    } else {
733        0
734    };
735
736    if current_len == 0 {
737        return JSValue::undefined();
738    }
739
740    let idx = (current_len - 1) as usize;
741    let key = ctx.int_atom_mut(idx);
742    let result = obj.get(key).unwrap_or_else(JSValue::undefined);
743    obj.delete(key);
744    obj.set_length(length_atom, JSValue::new_int(idx as i64));
745    result
746}
747
748fn array_shift(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
749    if args.is_empty() {
750        return JSValue::undefined();
751    }
752    let this_val = args[0];
753    if !require_object_coercible(ctx, &this_val) {
754        return JSValue::undefined();
755    }
756    let this_obj = to_object_or_return_undefined(this_val, ctx);
757    let length_atom = ctx.common_atoms.length;
758    let ptr = this_obj.get_ptr();
759
760    if this_obj.as_object().is_dense_array() {
761        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
762        let current_len = arr.len();
763        if current_len == 0 {
764            return JSValue::undefined();
765        }
766        let result = arr.get(0).unwrap_or_else(JSValue::undefined);
767        arr.elements_mut().remove(0);
768        arr.header
769            .set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
770        return result;
771    }
772
773    let obj = unsafe { &mut *(ptr as *mut JSObject) };
774    let current_len = if let Some(l) = obj.get(length_atom) {
775        if l.is_int() { l.get_int() as u32 } else { 0 }
776    } else {
777        0
778    };
779
780    if current_len == 0 {
781        return JSValue::undefined();
782    }
783
784    let first_key = ctx.common_atoms.n0;
785    let result = obj.get(first_key).unwrap_or_else(JSValue::undefined);
786
787    for i in 1..current_len {
788        let old_key = ctx.int_atom_mut(i as usize);
789        let new_key = ctx.int_atom_mut(i as usize - 1);
790        if let Some(val) = obj.get(old_key) {
791            obj.set(new_key, val);
792        }
793        obj.delete(old_key);
794    }
795
796    let last_key = ctx.int_atom_mut(current_len as usize - 1);
797    obj.delete(last_key);
798    obj.set_length(length_atom, JSValue::new_int((current_len - 1) as i64));
799    result
800}
801
802fn array_unshift(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
803    if args.is_empty() {
804        return JSValue::new_int(0);
805    }
806    let this_val = args[0];
807    if !require_object_coercible(ctx, &this_val) {
808        return JSValue::new_int(0);
809    }
810    let this_obj = to_object_or_return_undefined(this_val, ctx);
811
812    let length_atom = ctx.common_atoms.length;
813    let add_count = (args.len() - 1) as usize;
814    let ptr = this_obj.get_ptr();
815
816    if this_obj.as_object().is_dense_array() {
817        let arr = unsafe { &mut *(ptr as *mut JSArrayObject) };
818
819        let items: Vec<JSValue> = args.iter().skip(1).cloned().collect();
820        let mut new_elements = items;
821        new_elements.extend(arr.elements().iter().copied());
822        arr.set_elements(new_elements);
823        let new_len = arr.len();
824        arr.header
825            .set_length(length_atom, JSValue::new_int(new_len as i64));
826        return JSValue::new_int(new_len as i64);
827    }
828
829    let obj = unsafe { &mut *(ptr as *mut JSObject) };
830    let current_len = if let Some(l) = obj.get(length_atom) {
831        if l.is_int() { l.get_int() as u32 } else { 0 }
832    } else {
833        0
834    };
835
836    for i in (0..current_len).rev() {
837        let old_key = ctx.int_atom_mut(i as usize);
838        let new_key = ctx.int_atom_mut(i as usize + add_count);
839        if let Some(val) = obj.get(old_key) {
840            obj.set(new_key, val);
841        }
842        obj.delete(old_key);
843    }
844
845    for (i, arg) in args.iter().skip(1).enumerate() {
846        let key = ctx.int_atom_mut(i);
847        obj.set(key, arg.clone());
848    }
849
850    let new_len = current_len + add_count as u32;
851    obj.set_length(length_atom, JSValue::new_int(new_len as i64));
852    JSValue::new_int(new_len as i64)
853}
854
855fn array_concat(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
856    if args.is_empty() {
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_val = args[0];
865    if !require_object_coercible(ctx, &this_val) {
866        let mut result = new_jsarray_with_proto(ctx);
867        result
868            .header
869            .set_length(ctx.common_atoms.length, JSValue::new_int(0));
870        return alloc_jsarray(result, ctx);
871    }
872    let this = to_object_or_return_undefined(this_val, ctx);
873    let mut result = new_jsarray_with_proto(ctx);
874
875    let length_atom = ctx.common_atoms.length;
876
877    let concat_items: Vec<JSValue> = std::iter::once(this)
878        .chain(args.iter().skip(1).copied())
879        .collect();
880
881    for item in concat_items {
882        if item.is_object() {
883            let obj = item.as_object();
884            let spread_atom =
885                crate::builtins::symbol::get_symbol_is_concat_spreadable_atom(ctx);
886            let is_spreadable = match obj.get(spread_atom) {
887                Some(v) if !v.is_undefined() => v.is_truthy(),
888                _ => obj.is_array(),
889            };
890            if is_spreadable {
891                let item_len = if let Some(l) = obj.get(length_atom) {
892                    if l.is_int() { l.get_int() as u32 } else { 0 }
893                } else {
894                    0
895                };
896                for i in 0..item_len {
897                    if let Some(val) = array_get(obj, i as usize, ctx) {
898                        result.push(val);
899                    }
900                }
901            } else {
902                result.push(item);
903            }
904        } else {
905            result.push(item);
906        }
907    }
908
909    let result_len = result.len();
910    result
911        .header
912        .set_length(length_atom, JSValue::new_int(result_len as i64));
913    alloc_jsarray(result, ctx)
914}
915
916fn array_slice(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
917    let mut result = new_jsarray_with_proto(ctx);
918    let length_atom = ctx.common_atoms.length;
919
920    if args.is_empty() {
921        result.header.set_length(length_atom, JSValue::new_int(0));
922        return alloc_jsarray(result, ctx);
923    }
924
925    let this_val = args[0];
926    if !require_object_coercible(ctx, &this_val) {
927        result.header.set_length(length_atom, JSValue::new_int(0));
928        return alloc_jsarray(result, ctx);
929    }
930    let this_obj = to_object_or_return_undefined(this_val, ctx);
931
932    let obj = this_obj.as_object();
933
934    let len = if let Some(l) = obj.get(length_atom) {
935        if l.is_int() { l.get_int() as i32 } else { 0 }
936    } else {
937        0
938    };
939
940    let start = if args.len() > 1 {
941        let s = args[1].get_int() as i32;
942        if s < 0 { (len + s).max(0) } else { s.min(len) }
943    } else {
944        0
945    };
946
947    let end = if args.len() > 2 {
948        let e = args[2].get_int() as i32;
949        if e < 0 { (len + e).max(0) } else { e.min(len) }
950    } else {
951        len
952    };
953
954    for i in start..end {
955        if let Some(val) = array_get(obj, i as usize, ctx) {
956            result.push(val);
957        }
958    }
959
960    result
961        .header
962        .set_length(length_atom, JSValue::new_int(result.len() as i64));
963    alloc_jsarray(result, ctx)
964}
965
966fn array_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
967    if args.len() < 2 {
968        return JSValue::new_int(-1);
969    }
970
971    let this = args[0];
972    let search = &args[1];
973
974    if !require_object_coercible(ctx, &this) {
975        return JSValue::new_int(-1);
976    }
977
978    let this_obj = to_object_or_return_undefined(this, ctx);
979    let obj = this_obj.as_object();
980
981    let length_atom = ctx.common_atoms.length;
982    let len = safe_array_len_with_ctx(obj, length_atom, ctx);
983
984    let from_index_f = if args.len() > 2 {
985        args[2].to_number()
986    } else {
987        0.0
988    };
989
990    let start = if from_index_f.is_nan() {
991        0u64
992    } else if from_index_f < 0.0 {
993        ((len as f64) + from_index_f).max(0.0) as u64
994    } else {
995        (from_index_f as u64).min(len)
996    };
997
998    if start >= len {
999        return JSValue::new_int(-1);
1000    }
1001
1002    if len - start > 10_000_000 {
1003        if obj.is_dense_array() {
1004            let ptr = this.get_ptr();
1005            let dense_arr = unsafe { &*(ptr as *const crate::object::array_obj::JSArrayObject) };
1006            let dense_len = dense_arr.elements.len() as u64;
1007            for i in start..dense_len.min(len) {
1008                if let Some(val) = dense_arr.elements.get(i as usize) {
1009                    if val.strict_eq(search) {
1010                        return JSValue::new_int(i as i64);
1011                    }
1012                }
1013            }
1014        } else {
1015            for slot in obj.iter_props() {
1016                let s = ctx.get_atom_str(slot.atom);
1017                if let Ok(idx) = s.parse::<u64>() {
1018                    if idx >= start && idx < len && slot.value.strict_eq(search) {
1019                        return JSValue::new_int(idx as i64);
1020                    }
1021                }
1022            }
1023        }
1024    } else {
1025        for i in start..len {
1026            if let Some(val) = array_get(obj, i as usize, ctx) {
1027                if val.strict_eq(search) {
1028                    return JSValue::new_int(i as i64);
1029                }
1030            }
1031        }
1032    }
1033
1034    JSValue::new_int(-1)
1035}
1036
1037fn array_includes(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1038    if args.len() < 2 {
1039        return JSValue::bool(false);
1040    }
1041
1042    let this = args[0];
1043    let search = &args[1];
1044
1045    if !require_object_coercible(ctx, &this) {
1046        return JSValue::bool(false);
1047    }
1048
1049    let this_obj = to_object_or_return_undefined(this, ctx);
1050    let obj = this_obj.as_object();
1051
1052    let length_atom = ctx.common_atoms.length;
1053    let len = safe_array_len_with_ctx(obj, length_atom, ctx);
1054
1055    let from_index_f = if args.len() > 2 {
1056        args[2].to_number()
1057    } else {
1058        0.0
1059    };
1060
1061    let start = if from_index_f.is_nan() {
1062        0u64
1063    } else if from_index_f < 0.0 {
1064        ((len as f64) + from_index_f).max(0.0) as u64
1065    } else {
1066        (from_index_f as u64).min(len)
1067    };
1068
1069    if start >= len {
1070        return JSValue::bool(false);
1071    }
1072
1073    if len - start > 10_000_000 {
1074        if obj.is_dense_array() {
1075            let ptr = this.get_ptr();
1076            let dense_arr = unsafe { &*(ptr as *const crate::object::array_obj::JSArrayObject) };
1077            let dense_len = dense_arr.elements.len() as u64;
1078            for i in start..dense_len.min(len) {
1079                if let Some(val) = dense_arr.elements.get(i as usize) {
1080                    if val_same_value_zero(val, search) {
1081                        return JSValue::bool(true);
1082                    }
1083                }
1084            }
1085        } else {
1086            for slot in obj.iter_props() {
1087                let s = ctx.get_atom_str(slot.atom);
1088                if let Ok(idx) = s.parse::<u64>() {
1089                    if idx >= start && idx < len && val_same_value_zero(&slot.value, search) {
1090                        return JSValue::bool(true);
1091                    }
1092                }
1093            }
1094        }
1095    } else {
1096        for i in start..len {
1097            if let Some(val) = array_get(obj, i as usize, ctx) {
1098                if val_same_value_zero(&val, search) {
1099                    return JSValue::bool(true);
1100                }
1101            }
1102        }
1103    }
1104
1105    JSValue::bool(false)
1106}
1107
1108fn val_same_value_zero(a: &JSValue, b: &JSValue) -> bool {
1109    if a.is_float() && b.is_float() {
1110        let af = a.get_float();
1111        let bf = b.get_float();
1112        if af.is_nan() && bf.is_nan() {
1113            return true;
1114        }
1115        af.to_bits() == bf.to_bits()
1116    } else {
1117        a.strict_eq(b)
1118    }
1119}
1120
1121fn array_join(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1122    if args.is_empty() {
1123        return JSValue::new_string(ctx.intern(""));
1124    }
1125
1126    let this_val = args[0];
1127    if !require_object_coercible(ctx, &this_val) {
1128        return JSValue::new_string(ctx.intern(""));
1129    }
1130    let this_obj = to_object_or_return_undefined(this_val, ctx);
1131
1132    let obj = this_obj.as_object();
1133
1134    let length_atom = ctx.common_atoms.length;
1135    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1136
1137    let separator = if args.len() > 1 {
1138        let sep = &args[1];
1139        if sep.is_undefined() {
1140            ",".to_string()
1141        } else if sep.is_string() {
1142            ctx.get_atom_str(sep.get_atom()).to_string()
1143        } else if sep.is_bool() {
1144            sep.get_bool().to_string()
1145        } else if sep.is_int() {
1146            sep.get_int().to_string()
1147        } else if sep.is_float() {
1148            let f = sep.get_float();
1149            if f.is_nan() {
1150                "NaN".to_string()
1151            } else if f.is_infinite() {
1152                "Infinity".to_string()
1153            } else {
1154                f.to_string()
1155            }
1156        } else if sep.is_null() {
1157            "null".to_string()
1158        } else {
1159            ",".to_string()
1160        }
1161    } else {
1162        ",".to_string()
1163    };
1164
1165    let mut parts = Vec::new();
1166    for i in 0..len {
1167        if let Some(val) = array_get(obj, i as usize, ctx) {
1168            if val.is_undefined() || val.is_null() {
1169                parts.push(String::new());
1170            } else if val.is_string() {
1171                parts.push(ctx.get_atom_str(val.get_atom()).to_string());
1172            } else if val.is_int() {
1173                parts.push(val.get_int().to_string());
1174            } else if val.is_float() {
1175                parts.push(val.get_float().to_string());
1176            } else if val.is_bool() {
1177                parts.push(val.get_bool().to_string());
1178            } else {
1179                parts.push(String::new());
1180            }
1181        } else {
1182            parts.push(String::new());
1183        }
1184    }
1185
1186    JSValue::new_string(ctx.intern(&parts.join(&separator)))
1187}
1188
1189fn array_is_array(_ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1190    if args.is_empty() {
1191        return JSValue::bool(false);
1192    }
1193
1194    let arg = &args[0];
1195    if arg.is_object() {
1196        let obj = arg.as_object();
1197        JSValue::bool(obj.is_array())
1198    } else {
1199        JSValue::bool(false)
1200    }
1201}
1202
1203fn array_from(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1204    let ctor = args.first().copied().unwrap_or(JSValue::undefined());
1205    let source = args.get(1).copied().unwrap_or(JSValue::undefined());
1206    let map_fn = args.get(2).copied().filter(|v| v.is_function());
1207    let map_fn_this = args.get(3).copied().unwrap_or(JSValue::undefined());
1208
1209    if source.is_null_or_undefined() {
1210        throw_type_error(ctx, "Array.from requires an array-like object or iterable");
1211        return JSValue::undefined();
1212    }
1213
1214    let mut items: Vec<JSValue> = Vec::new();
1215
1216    if source.is_string() {
1217        let s = ctx.get_atom_str(source.get_atom()).to_string();
1218        for ch in s.chars() {
1219            items.push(JSValue::new_string(ctx.intern(&ch.to_string())));
1220        }
1221        return create_array_obj_from_ctor(ctx, ctor, &items);
1222    }
1223
1224    if !source.is_object() {
1225        return create_array_obj_from_ctor(ctx, ctor, &items);
1226    }
1227
1228    let obj = source.as_object();
1229
1230    let sym_iter_atom = crate::builtins::symbol::get_symbol_iterator_atom(ctx);
1231    if let Some(iter_val) = obj.get(sym_iter_atom) {
1232        if iter_val.is_function() {
1233            match call_callback_with_this(ctx, iter_val, source, &[]) {
1234                Ok(iterator) => {
1235                    if iterator.is_object_like() {
1236                        let iter_obj = iterator.as_object();
1237                        let next_fn = iter_obj.get(ctx.intern("next"));
1238                        if let Some(next_val) = next_fn {
1239                            if next_val.is_function() {
1240                                loop {
1241                                    match call_callback_with_this(ctx, next_val, iterator, &[]) {
1242                                        Ok(result) => {
1243                                            if result.is_object_like() {
1244                                                let robj = result.as_object();
1245                                                let done = robj
1246                                                    .get(ctx.intern("done"))
1247                                                    .map(|v| v.is_truthy())
1248                                                    .unwrap_or(false);
1249                                                if done {
1250                                                    break;
1251                                                }
1252                                                let value = robj
1253                                                    .get(ctx.intern("value"))
1254                                                    .unwrap_or(JSValue::undefined());
1255                                                if let Some(mf) = map_fn {
1256                                                    let mapped = call_callback_with_this(
1257                                                        ctx,
1258                                                        mf,
1259                                                        map_fn_this,
1260                                                        &[
1261                                                            value,
1262                                                            JSValue::new_int(items.len() as i64),
1263                                                        ],
1264                                                    );
1265                                                    match mapped {
1266                                                        Ok(v) => items.push(v),
1267                                                        Err(_) => return JSValue::undefined(),
1268                                                    }
1269                                                } else {
1270                                                    items.push(value);
1271                                                }
1272                                            } else {
1273                                                break;
1274                                            }
1275                                        }
1276                                        Err(_) => return JSValue::undefined(),
1277                                    }
1278                                }
1279                            }
1280                        }
1281                    }
1282                    return create_array_obj_from_ctor_iter(ctx, ctor, &items);
1283                }
1284                Err(_) => return JSValue::undefined(),
1285            }
1286        }
1287    }
1288
1289    let len = obj
1290        .get(ctx.common_atoms.length)
1291        .map(|v| js_to_length(&v) as usize)
1292        .unwrap_or(0);
1293
1294    for i in 0..len {
1295        let val = array_get(obj, i, ctx).unwrap_or(JSValue::undefined());
1296
1297        if let Some(mf) = map_fn {
1298            let mapped = call_callback_with_this(
1299                ctx,
1300                mf,
1301                map_fn_this,
1302                &[val, JSValue::new_int(i as i64)],
1303            );
1304            match mapped {
1305                Ok(v) => items.push(v),
1306                Err(_) => return JSValue::undefined(),
1307            }
1308        } else {
1309            items.push(val);
1310        }
1311    }
1312
1313    create_array_obj_from_ctor(ctx, ctor, &items)
1314}
1315
1316fn is_builtin_array_ctor(ctx: &mut JSContext, ctor: JSValue) -> bool {
1317    if !ctor.is_function() {
1318        return false;
1319    }
1320    if let Some(ba) = ctor.as_function().builtin_atom {
1321        ctx.get_atom_str(ba) == "array_constructor"
1322    } else {
1323        false
1324    }
1325}
1326
1327fn create_array_obj_from_ctor(ctx: &mut JSContext, ctor: JSValue, items: &[JSValue]) -> JSValue {
1328    if !ctor.is_function() || is_builtin_array_ctor(ctx, ctor) {
1329        return create_array_obj(ctx, items);
1330    }
1331
1332    if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
1333        let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
1334        match vm.construct(ctx, ctor, &[JSValue::new_int(items.len() as i64)]) {
1335            Ok(arr) => {
1336                if arr.is_object_like() {
1337                    let arr_obj = arr.as_object_mut();
1338                    for (i, &val) in items.iter().enumerate() {
1339                        let key = ctx.int_atom_mut(i);
1340                        arr_obj.set(key, val);
1341                    }
1342                    arr_obj.set(ctx.common_atoms.length, JSValue::new_int(items.len() as i64));
1343                }
1344                return arr;
1345            }
1346            Err(_) => return JSValue::undefined(),
1347        }
1348    }
1349
1350    create_array_obj(ctx, items)
1351}
1352
1353fn create_array_obj_from_ctor_iter(ctx: &mut JSContext, ctor: JSValue, items: &[JSValue]) -> JSValue {
1354    if !ctor.is_function() || is_builtin_array_ctor(ctx, ctor) {
1355        return create_array_obj(ctx, items);
1356    }
1357
1358    if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
1359        let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
1360        match vm.construct(ctx, ctor, &[]) {
1361            Ok(arr) => {
1362                if arr.is_object_like() {
1363                    let arr_obj = arr.as_object_mut();
1364                    for (i, &val) in items.iter().enumerate() {
1365                        let key = ctx.int_atom_mut(i);
1366                        arr_obj.set(key, val);
1367                    }
1368                    arr_obj.set(ctx.common_atoms.length, JSValue::new_int(items.len() as i64));
1369                }
1370                return arr;
1371            }
1372            Err(_) => return JSValue::undefined(),
1373        }
1374    }
1375
1376    create_array_obj(ctx, items)
1377}
1378
1379fn array_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1380    let ctor = args.first().copied().unwrap_or(JSValue::undefined());
1381    let items: Vec<JSValue> = args.iter().skip(1).copied().collect();
1382    create_array_obj_from_ctor(ctx, ctor, &items)
1383}
1384
1385fn create_array_obj(ctx: &mut JSContext, items: &[JSValue]) -> JSValue {
1386    let mut arr_obj = JSObject::new_array();
1387    if let Some(proto_ptr) = ctx.get_array_prototype() {
1388        arr_obj.set_prototype_raw(proto_ptr);
1389    }
1390    for (i, &val) in items.iter().enumerate() {
1391        let key = ctx.int_atom_mut(i);
1392        arr_obj.set(key, val);
1393    }
1394    let len_atom = ctx.common_atoms.length;
1395    arr_obj.set(len_atom, JSValue::new_int(items.len() as i64));
1396    let ptr = Box::into_raw(Box::new(arr_obj)) as usize;
1397    ctx.runtime_mut().gc_heap_mut().track(ptr);
1398    JSValue::new_object(ptr)
1399}
1400
1401pub fn call_callback(
1402    ctx: &mut JSContext,
1403    callback: JSValue,
1404    args: &[JSValue],
1405) -> Result<JSValue, String> {
1406    call_callback_with_this(ctx, callback, JSValue::undefined(), args)
1407}
1408
1409pub fn call_callback_with_this(
1410    ctx: &mut JSContext,
1411    callback: JSValue,
1412    this_value: JSValue,
1413    args: &[JSValue],
1414) -> Result<JSValue, String> {
1415    if let Some(ptr) = ctx.get_register_vm_ptr() {
1416        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
1417        vm.call_function_with_this(ctx, callback, this_value, args)
1418    } else {
1419        Err("call_callback: VM not available".to_string())
1420    }
1421}
1422
1423fn array_for_each(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1424    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1425
1426    if !require_object_coercible(ctx, &this) {
1427        return JSValue::undefined();
1428    }
1429
1430    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1431    if !callback.is_function() {
1432        throw_type_error(ctx, "Callback is not a function");
1433        return JSValue::undefined();
1434    }
1435
1436    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1437
1438    let this_obj = if this.is_object() {
1439        this
1440    } else {
1441        crate::builtins::object::object_to_object(ctx, &this)
1442    };
1443
1444    let obj = if this_obj.is_object_like() {
1445        this_obj.as_object()
1446    } else {
1447        return JSValue::undefined();
1448    };
1449
1450    let length_atom = ctx.common_atoms.length;
1451    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1452
1453    for i in 0..len {
1454        if let Some(value) = array_get(obj, i as usize, ctx) {
1455            let callback_args = vec![value, JSValue::new_int(i as i64), this_obj];
1456            let _ = call_callback_with_this(ctx, callback, this_arg, &callback_args);
1457        }
1458    }
1459
1460    JSValue::undefined()
1461}
1462
1463fn array_map(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1464    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1465
1466    if !require_object_coercible(ctx, &this) {
1467        return JSValue::undefined();
1468    }
1469
1470    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1471    if !callback.is_function() {
1472        throw_type_error(ctx, "Callback is not a function");
1473        return JSValue::undefined();
1474    }
1475
1476    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1477
1478    let this_obj = if this.is_object() {
1479        this
1480    } else {
1481        crate::builtins::object::object_to_object(ctx, &this)
1482    };
1483
1484    let obj = if this_obj.is_object_like() {
1485        this_obj.as_object()
1486    } else {
1487        let mut result = new_jsarray_with_proto(ctx);
1488        result
1489            .header
1490            .set_length(ctx.common_atoms.length, JSValue::new_int(0));
1491        return alloc_jsarray(result, ctx);
1492    };
1493
1494    let length_atom = ctx.common_atoms.length;
1495
1496    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1497
1498    let mut result = new_jsarray_with_proto(ctx);
1499
1500    for i in 0..len {
1501        if let Some(value) = array_get(obj, i as usize, ctx) {
1502            let callback_args = vec![value, JSValue::new_int(i as i64), this_obj];
1503            if let Ok(mapped_value) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
1504                result.push(mapped_value);
1505            }
1506        }
1507    }
1508
1509    result
1510        .header
1511        .set_length(length_atom, JSValue::new_int(result.len() as i64));
1512    alloc_jsarray(result, ctx)
1513}
1514
1515fn array_filter(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1516    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1517
1518    if !require_object_coercible(ctx, &this) {
1519        return JSValue::undefined();
1520    }
1521
1522    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1523    if !callback.is_function() {
1524        throw_type_error(ctx, "Callback is not a function");
1525        return JSValue::undefined();
1526    }
1527
1528    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1529
1530    let this_obj = to_object_or_return_undefined(this, ctx);
1531    let obj = this_obj.as_object();
1532
1533    let length_atom = ctx.common_atoms.length;
1534    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1535
1536    let mut result = new_jsarray_with_proto(ctx);
1537
1538    for i in 0..len {
1539        if let Some(value) = array_get(obj, i as usize, ctx) {
1540            let callback_args = vec![value, JSValue::new_int(i as i64), this_obj];
1541            if let Ok(test_result) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
1542                if test_result.is_truthy() {
1543                    result.push(value);
1544                }
1545            }
1546        }
1547    }
1548
1549    result
1550        .header
1551        .set_length(length_atom, JSValue::new_int(result.len() as i64));
1552    alloc_jsarray(result, ctx)
1553}
1554
1555fn array_reduce(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1556    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1557
1558    if !require_object_coercible(ctx, &this) {
1559        return JSValue::undefined();
1560    }
1561
1562    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1563    if !callback.is_function() {
1564        throw_type_error(ctx, "Callback is not a function");
1565        return JSValue::undefined();
1566    }
1567
1568    let this_obj = to_object_or_return_undefined(this, ctx);
1569    let obj = this_obj.as_object();
1570
1571    let length_atom = ctx.common_atoms.length;
1572    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1573
1574    if len == 0 {
1575        if args.len() > 2 {
1576            return args[2];
1577        }
1578        throw_type_error(ctx, "Reduce of empty array with no initial value");
1579        return JSValue::undefined();
1580    }
1581
1582    let mut accumulator: JSValue;
1583    let mut start_index: usize = 0;
1584
1585    if args.len() > 2 {
1586        accumulator = args[2];
1587        start_index = 0;
1588    } else {
1589        let mut found: Option<JSValue> = None;
1590        while start_index < len {
1591            if let Some(v) = array_get(obj, start_index, ctx) {
1592                found = Some(v);
1593                start_index += 1;
1594                break;
1595            }
1596            start_index += 1;
1597        }
1598        match found {
1599            Some(v) => accumulator = v,
1600            None => {
1601                throw_type_error(ctx, "Reduce of empty array with no initial value");
1602                return JSValue::undefined();
1603            }
1604        }
1605    }
1606
1607    for i in start_index..len {
1608        if let Some(value) = array_get(obj, i as usize, ctx) {
1609            let callback_args = vec![accumulator, value, JSValue::new_int(i as i64), this_obj];
1610            if let Ok(result) = call_callback(ctx, callback, &callback_args) {
1611                accumulator = result;
1612            }
1613        }
1614    }
1615
1616    accumulator
1617}
1618
1619fn array_every(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1620    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1621
1622    if !require_object_coercible(ctx, &this) {
1623        return JSValue::undefined();
1624    }
1625
1626    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1627    if !callback.is_function() {
1628        throw_type_error(ctx, "Callback is not a function");
1629        return JSValue::undefined();
1630    }
1631
1632    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1633
1634    let this_obj = to_object_or_return_undefined(this, ctx);
1635    let obj = this_obj.as_object();
1636
1637    let length_atom = ctx.common_atoms.length;
1638    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1639
1640    for i in 0..len {
1641        if let Some(value) = array_get(obj, i as usize, ctx) {
1642            let callback_args = vec![value, JSValue::new_int(i as i64), this_obj];
1643            if let Ok(test_result) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
1644                if !test_result.is_truthy() {
1645                    return JSValue::bool(false);
1646                }
1647            }
1648        }
1649    }
1650
1651    JSValue::bool(true)
1652}
1653
1654fn array_some(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1655    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1656
1657    if !require_object_coercible(ctx, &this) {
1658        return JSValue::undefined();
1659    }
1660
1661    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1662    if !callback.is_function() {
1663        throw_type_error(ctx, "Callback is not a function");
1664        return JSValue::undefined();
1665    }
1666
1667    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1668
1669    let this_obj = to_object_or_return_undefined(this, ctx);
1670    let obj = this_obj.as_object();
1671
1672    let length_atom = ctx.common_atoms.length;
1673    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1674
1675    for i in 0..len {
1676        if let Some(value) = array_get(obj, i as usize, ctx) {
1677            let callback_args = vec![value, JSValue::new_int(i as i64), this_obj];
1678            if let Ok(test_result) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
1679                if test_result.is_truthy() {
1680                    return JSValue::bool(true);
1681                }
1682            }
1683        }
1684    }
1685
1686    JSValue::bool(false)
1687}
1688
1689fn array_find(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1690    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1691
1692    if !require_object_coercible(ctx, &this) {
1693        return JSValue::undefined();
1694    }
1695
1696    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1697    if !callback.is_function() {
1698        throw_type_error(ctx, "Callback is not a function");
1699        return JSValue::undefined();
1700    }
1701
1702    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1703
1704    let this_obj = to_object_or_return_undefined(this, ctx);
1705    let obj = this_obj.as_object();
1706
1707    let length_atom = ctx.common_atoms.length;
1708    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1709
1710    for i in 0..len {
1711        if let Some(value) = array_get(obj, i as usize, ctx) {
1712            let callback_args = vec![value, JSValue::new_int(i as i64), this_obj];
1713            if let Ok(result) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
1714                if result.is_truthy() {
1715                    return value;
1716                }
1717            }
1718        }
1719    }
1720
1721    JSValue::undefined()
1722}
1723
1724fn array_find_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1725    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1726
1727    if !require_object_coercible(ctx, &this) {
1728        return JSValue::undefined();
1729    }
1730
1731    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1732    if !callback.is_function() {
1733        throw_type_error(ctx, "Callback is not a function");
1734        return JSValue::undefined();
1735    }
1736
1737    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
1738
1739    let this_obj = to_object_or_return_undefined(this, ctx);
1740    let obj = this_obj.as_object();
1741
1742    let length_atom = ctx.common_atoms.length;
1743    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1744
1745    for i in 0..len {
1746        if let Some(value) = array_get(obj, i as usize, ctx) {
1747            let callback_args = vec![value, JSValue::new_int(i as i64), this_obj];
1748            if let Ok(result) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
1749                if result.is_truthy() {
1750                    return JSValue::new_int(i as i64);
1751                }
1752            }
1753        }
1754    }
1755
1756    JSValue::new_int(-1)
1757}
1758
1759fn array_reduce_right(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1760    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
1761
1762    if !require_object_coercible(ctx, &this) {
1763        return JSValue::undefined();
1764    }
1765
1766    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
1767    if !callback.is_function() {
1768        throw_type_error(ctx, "Callback is not a function");
1769        return JSValue::undefined();
1770    }
1771
1772    let this_obj = to_object_or_return_undefined(this, ctx);
1773    let obj = this_obj.as_object();
1774
1775    let length_atom = ctx.common_atoms.length;
1776    let len = safe_array_len_with_ctx(obj, length_atom, ctx) as usize;
1777
1778    if len == 0 {
1779        if args.len() > 2 {
1780            return args[2];
1781        }
1782        return JSValue::undefined();
1783    }
1784
1785    let mut accumulator: JSValue;
1786    let mut end_index: i64;
1787
1788    if args.len() > 2 {
1789        accumulator = args[2];
1790        end_index = len as i64 - 1;
1791    } else {
1792        accumulator = array_get(obj, (len - 1) as usize, ctx).unwrap_or_else(JSValue::undefined);
1793        end_index = len as i64 - 2;
1794    }
1795
1796    while end_index >= 0 {
1797        if let Some(value) = array_get(obj, end_index as usize, ctx) {
1798            let callback_args = vec![accumulator, value, JSValue::new_int(end_index), this_obj];
1799            if let Ok(result) = call_callback(ctx, callback, &callback_args) {
1800                accumulator = result;
1801            }
1802        }
1803        end_index -= 1;
1804    }
1805
1806    accumulator
1807}
1808
1809fn val_to_str(ctx: &mut JSContext, v: JSValue) -> String {
1810    if v.is_int() {
1811        format!("{}", v.get_int())
1812    } else if v.is_float() {
1813        format!("{}", v.get_float())
1814    } else if v.is_string() {
1815        ctx.get_atom_str(v.get_atom()).to_string()
1816    } else if v.is_bool() {
1817        v.get_bool().to_string()
1818    } else if v.is_null() {
1819        "null".to_string()
1820    } else {
1821        "undefined".to_string()
1822    }
1823}
1824
1825fn array_sort(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1826    if args.is_empty() {
1827        return JSValue::undefined();
1828    }
1829    let this = args[0];
1830    if !require_object_coercible(ctx, &this) {
1831        return JSValue::undefined();
1832    }
1833    let this = to_object_or_return_undefined(this, ctx);
1834    let len_atom = ctx.common_atoms.length;
1835    let len = {
1836        let arr = this.as_object();
1837        safe_array_len_with_ctx(&arr, len_atom, ctx) as usize
1838    };
1839    let mut elements: Vec<JSValue> = (0..len)
1840        .map(|i| {
1841            let arr = this.as_object();
1842            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
1843        })
1844        .collect();
1845
1846    let cmp_fn = args.get(1).copied().filter(|v| v.is_function());
1847
1848    let n = elements.len();
1849    for i in 1..n {
1850        let mut j = i;
1851        while j > 0 {
1852            let should_swap = if let Some(func) = cmp_fn {
1853                match call_callback(ctx, func, &[elements[j - 1], elements[j]]) {
1854                    Ok(r) => r.get_float() > 0.0 || (r.is_int() && r.get_int() > 0),
1855                    Err(_) => false,
1856                }
1857            } else {
1858                let a_str = val_to_str(ctx, elements[j - 1]);
1859                let b_str = val_to_str(ctx, elements[j]);
1860                a_str > b_str
1861            };
1862            if should_swap {
1863                elements.swap(j - 1, j);
1864                j -= 1;
1865            } else {
1866                break;
1867            }
1868        }
1869    }
1870
1871    let arr = this.as_object_mut();
1872    if this.as_object().is_dense_array() {
1873        let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
1874        arr_obj.set_elements(elements);
1875    } else {
1876        if arr.has_dense_storage() {
1877            arr.set_array_elements(elements.clone());
1878        }
1879        for (i, val) in elements.into_iter().enumerate() {
1880            let key = ctx.int_atom_mut(i);
1881            arr.set(key, val);
1882        }
1883    }
1884    this
1885}
1886
1887fn array_reverse(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1888    if args.is_empty() {
1889        return JSValue::undefined();
1890    }
1891    let this = args[0];
1892    if !require_object_coercible(ctx, &this) {
1893        return JSValue::undefined();
1894    }
1895    let this = to_object_or_return_undefined(this, ctx);
1896    let len_atom = ctx.common_atoms.length;
1897    let len = {
1898        let arr = this.as_object();
1899        arr.get(len_atom)
1900            .as_ref()
1901            .map(|v| js_to_length(v))
1902            .unwrap_or(0)
1903    };
1904    if len > 10_000_000 {
1905        return this;
1906    }
1907    let mut elements: Vec<JSValue> = (0..len)
1908        .map(|i| {
1909            let arr = this.as_object();
1910            array_get(arr, i as usize, ctx).unwrap_or(JSValue::undefined())
1911        })
1912        .collect();
1913    elements.reverse();
1914    let arr = this.as_object_mut();
1915    if this.as_object().is_dense_array() {
1916        let arr_obj = unsafe { &mut *(this.get_ptr() as *mut JSArrayObject) };
1917        arr_obj.set_elements(elements);
1918    } else {
1919        if arr.has_dense_storage() {
1920            arr.set_array_elements(elements.clone());
1921        }
1922        for (i, val) in elements.into_iter().enumerate() {
1923            let key = ctx.int_atom_mut(i);
1924            arr.set(key, val);
1925        }
1926    }
1927    this
1928}
1929
1930fn array_fill(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1931    if args.is_empty() {
1932        return JSValue::undefined();
1933    }
1934    let this = args[0];
1935    if !require_object_coercible(ctx, &this) {
1936        return JSValue::undefined();
1937    }
1938    let this = to_object_or_return_undefined(this, ctx);
1939    let value = args.get(1).copied().unwrap_or(JSValue::undefined());
1940    let len_atom = ctx.common_atoms.length;
1941    let len = {
1942        let arr = this.as_object();
1943        safe_array_len_with_ctx(&arr, len_atom, ctx) as usize
1944    };
1945    let start = args
1946        .get(2)
1947        .map(|v| {
1948            let i = v.get_int() as isize;
1949            if i < 0 {
1950                (len as isize + i).max(0) as usize
1951            } else {
1952                (i as usize).min(len)
1953            }
1954        })
1955        .unwrap_or(0);
1956    let end = args
1957        .get(3)
1958        .map(|v| {
1959            let i = v.get_int() as isize;
1960            if i < 0 {
1961                (len as isize + i).max(0) as usize
1962            } else {
1963                (i as usize).min(len)
1964            }
1965        })
1966        .unwrap_or(len);
1967    let arr = this.as_object_mut();
1968    for i in start..end {
1969        let key = ctx.int_atom_mut(i);
1970        arr.set(key, value);
1971    }
1972    this
1973}
1974
1975fn array_splice(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
1976    if args.is_empty() {
1977        return JSValue::undefined();
1978    }
1979    let this = args[0];
1980    if !require_object_coercible(ctx, &this) {
1981        return JSValue::undefined();
1982    }
1983    let this = to_object_or_return_undefined(this, ctx);
1984    let len_atom = ctx.common_atoms.length;
1985    let len = {
1986        let arr = this.as_object();
1987        arr.get(len_atom)
1988            .as_ref()
1989            .map(|v| js_to_length(v))
1990            .unwrap_or(0)
1991    };
1992    let start = args
1993        .get(1)
1994        .map(|v| {
1995            let n = v.to_number();
1996            let i = if n < 0.0 {
1997                (len as f64 + n).max(0.0) as u64
1998            } else {
1999                n.min(len as f64) as u64
2000            };
2001            i.min(len)
2002        })
2003        .unwrap_or(0);
2004    let delete_count = args
2005        .get(2)
2006        .map(|v| (js_to_length(v)).min(len.saturating_sub(start)))
2007        .unwrap_or(len.saturating_sub(start));
2008    let insert_items: Vec<JSValue> = args.iter().skip(3).copied().collect();
2009
2010    let new_len = len - delete_count + insert_items.len() as u64;
2011    let mut removed: Vec<JSValue> = Vec::with_capacity(delete_count.min(1024) as usize);
2012
2013    for i in 0..delete_count {
2014        let arr = this.as_object();
2015        removed.push(array_get(arr, (start + i) as usize, ctx).unwrap_or(JSValue::undefined()));
2016    }
2017
2018    let shift = insert_items.len() as i64 - delete_count as i64;
2019
2020    if shift < 0 {
2021        for i in start..(len - delete_count) {
2022            let src_idx = (start + delete_count + (i - start)) as usize;
2023            let dst_idx = (start + insert_items.len() as u64 + (i - start)) as usize;
2024            let val = {
2025                let arr = this.as_object();
2026                array_get(arr, src_idx, ctx).unwrap_or(JSValue::undefined())
2027            };
2028            let arr = this.as_object_mut();
2029            array_set(arr, dst_idx, val);
2030        }
2031        for i in new_len..len {
2032            let arr = this.as_object_mut();
2033            array_delete(arr, i as usize);
2034        }
2035    } else if shift > 0 {
2036        for i in (start..(len - delete_count)).rev() {
2037            let src_idx = (start + delete_count + (i - start)) as usize;
2038            let dst_idx = (start + insert_items.len() as u64 + (i - start)) as usize;
2039            let val = {
2040                let arr = this.as_object();
2041                array_get(arr, src_idx, ctx).unwrap_or(JSValue::undefined())
2042            };
2043            let arr = this.as_object_mut();
2044            array_set(arr, dst_idx, val);
2045        }
2046    }
2047
2048    for (i, item) in insert_items.iter().enumerate() {
2049        let arr = this.as_object_mut();
2050        array_set(arr, (start + i as u64) as usize, *item);
2051    }
2052
2053    let arr = this.as_object_mut();
2054    arr.set_length(len_atom, JSValue::new_int(new_len as i64));
2055
2056    let mut removed_arr = new_jsarray_with_proto(ctx);
2057    for v in removed.iter() {
2058        removed_arr.push(*v);
2059    }
2060    removed_arr
2061        .header
2062        .set_length(len_atom, JSValue::new_int(removed_arr.len() as i64));
2063
2064    alloc_jsarray(removed_arr, ctx)
2065}
2066
2067fn array_to_spliced(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2068    if args.is_empty() {
2069        return JSValue::undefined();
2070    }
2071    let this = args[0];
2072    if !require_object_coercible(ctx, &this) {
2073        return JSValue::undefined();
2074    }
2075    let this = to_object_or_return_undefined(this, ctx);
2076    let len_atom = ctx.common_atoms.length;
2077    let len = {
2078        let arr = this.as_object();
2079        safe_array_len_with_ctx(&arr, len_atom, ctx)
2080    };
2081    if len > ARRAY_LENGTH_LIMIT {
2082        throw_range_error(ctx, "Invalid array length");
2083        return JSValue::undefined();
2084    }
2085    let len = len as usize;
2086    let start = args
2087        .get(1)
2088        .map(|v| {
2089            let i = v.get_int() as isize;
2090            if i < 0 {
2091                (len as isize + i).max(0) as usize
2092            } else {
2093                (i as usize).min(len)
2094            }
2095        })
2096        .unwrap_or(0);
2097    let delete_count = args
2098        .get(2)
2099        .map(|v| (v.get_int() as usize).min(len - start))
2100        .unwrap_or(len - start);
2101    let insert_count = args.len().saturating_sub(3);
2102    let new_len = (len + insert_count).saturating_sub(delete_count);
2103    if new_len as u64 > ARRAY_LENGTH_LIMIT {
2104        throw_range_error(ctx, "Invalid array length");
2105        return JSValue::undefined();
2106    }
2107    let insert_items: Vec<JSValue> = args.iter().skip(3).copied().collect();
2108
2109    let mut elements: Vec<JSValue> = (0..len)
2110        .map(|i| {
2111            let arr = this.as_object();
2112            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
2113        })
2114        .collect();
2115
2116    elements.splice(start..start + delete_count, insert_items);
2117
2118    let mut result_arr = new_jsarray_with_proto(ctx);
2119    for v in elements.iter() {
2120        result_arr.push(*v);
2121    }
2122    result_arr
2123        .header
2124        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
2125    alloc_jsarray(result_arr, ctx)
2126}
2127
2128fn array_flat(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2129    if args.is_empty() {
2130        return JSValue::undefined();
2131    }
2132    let this = args[0];
2133    if !require_object_coercible(ctx, &this) {
2134        return JSValue::undefined();
2135    }
2136    let this = to_object_or_return_undefined(this, ctx);
2137    let depth = args.get(1).map(|v| v.get_int() as usize).unwrap_or(1);
2138
2139    fn flatten(ctx: &mut JSContext, arr_val: JSValue, depth: usize, result: &mut Vec<JSValue>) {
2140        if !arr_val.is_object_like() {
2141            result.push(arr_val);
2142            return;
2143        }
2144        let len_atom = ctx.common_atoms.length;
2145        let arr = arr_val.as_object();
2146        if !arr.is_array() {
2147            result.push(arr_val);
2148            return;
2149        }
2150        let len = safe_array_len_with_ctx(&arr, len_atom, ctx) as usize;
2151        let elements: Vec<JSValue> = (0..len)
2152            .map(|i| array_get(arr, i, ctx).unwrap_or(JSValue::undefined()))
2153            .collect();
2154        for el in elements {
2155            if depth > 0 && el.is_object() {
2156                let el_obj = el.as_object();
2157                if el_obj.is_array() {
2158                    flatten(ctx, el, depth - 1, result);
2159                    continue;
2160                }
2161            }
2162            result.push(el);
2163        }
2164    }
2165
2166    let mut flat_elements: Vec<JSValue> = Vec::new();
2167    {
2168        let obj = this.as_object();
2169        let this_len = safe_array_len_with_ctx(&obj, ctx.common_atoms.length, ctx) as usize;
2170        for i in 0..this_len {
2171            let el = array_get(obj, i, ctx).unwrap_or(JSValue::undefined());
2172            flatten(ctx, el, depth, &mut flat_elements);
2173        }
2174    }
2175
2176    let len_atom = ctx.common_atoms.length;
2177    let mut result_arr = new_jsarray_with_proto(ctx);
2178    for v in flat_elements.iter() {
2179        result_arr.push(*v);
2180    }
2181    result_arr
2182        .header
2183        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
2184    alloc_jsarray(result_arr, ctx)
2185}
2186
2187fn array_flat_map(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2188    if args.len() < 2 {
2189        return JSValue::undefined();
2190    }
2191    let this = args[0];
2192    let callback = args[1];
2193    if !this.is_object() || !callback.is_function() {
2194        return JSValue::undefined();
2195    }
2196
2197    let len_atom = ctx.common_atoms.length;
2198    let len = {
2199        let arr = this.as_object();
2200        safe_array_len_with_ctx(&arr, len_atom, ctx) as usize
2201    };
2202
2203    let mut result_elements: Vec<JSValue> = Vec::new();
2204    for i in 0..len {
2205        let el = {
2206            let arr = this.as_object();
2207            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
2208        };
2209        let idx_val = JSValue::new_int(i as i64);
2210        match call_callback(ctx, callback, &[el, idx_val, this]) {
2211            Ok(mapped) => {
2212                if mapped.is_object() {
2213                    let mapped_obj = mapped.as_object();
2214                    if mapped_obj.is_array() {
2215                        let mlen = mapped_obj
2216                            .get(len_atom)
2217                            .map(|v| v.get_int() as usize)
2218                            .unwrap_or(0);
2219                        let mel_vec: Vec<JSValue> = (0..mlen)
2220                            .map(|j| array_get(mapped_obj, j, ctx).unwrap_or(JSValue::undefined()))
2221                            .collect();
2222                        result_elements.extend(mel_vec);
2223                        continue;
2224                    }
2225                }
2226                result_elements.push(mapped);
2227            }
2228            Err(_) => {}
2229        }
2230    }
2231
2232    let mut result_arr = new_jsarray_with_proto(ctx);
2233    for v in result_elements.iter() {
2234        result_arr.push(*v);
2235    }
2236    result_arr
2237        .header
2238        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
2239    alloc_jsarray(result_arr, ctx)
2240}
2241
2242fn array_find_last(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2243    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
2244
2245    if !require_object_coercible(ctx, &this) {
2246        return JSValue::undefined();
2247    }
2248
2249    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
2250    if !callback.is_function() {
2251        throw_type_error(ctx, "Callback is not a function");
2252        return JSValue::undefined();
2253    }
2254
2255    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
2256
2257    let this_obj = to_object_or_return_undefined(this, ctx);
2258    let obj = this_obj.as_object();
2259
2260    let length_atom = ctx.common_atoms.length;
2261    let len = safe_array_len_with_ctx(obj, length_atom, ctx).min(9007199254740991) as usize;
2262
2263    for i in (0..len).rev() {
2264        let value = array_get(obj, i, ctx).unwrap_or(JSValue::undefined());
2265        let idx_val = if i >= (1usize << 47) {
2266            JSValue::new_float(i as f64)
2267        } else {
2268            JSValue::new_int(i as i64)
2269        };
2270        let callback_args = vec![value, idx_val, this_obj];
2271        if let Ok(result) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
2272            if result.is_truthy() {
2273                return value;
2274            }
2275        }
2276    }
2277
2278    JSValue::undefined()
2279}
2280fn array_find_last_index(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2281    let this = if args.is_empty() { JSValue::undefined() } else { args[0] };
2282
2283    if !require_object_coercible(ctx, &this) {
2284        return JSValue::undefined();
2285    }
2286
2287    let callback = args.get(1).copied().unwrap_or(JSValue::undefined());
2288    if !callback.is_function() {
2289        throw_type_error(ctx, "Callback is not a function");
2290        return JSValue::undefined();
2291    }
2292
2293    let this_arg = args.get(2).copied().unwrap_or(JSValue::undefined());
2294
2295    let this_obj = to_object_or_return_undefined(this, ctx);
2296    let obj = this_obj.as_object();
2297
2298    let length_atom = ctx.common_atoms.length;
2299    let len = safe_array_len_with_ctx(obj, length_atom, ctx).min(9007199254740991) as usize;
2300
2301    for i in (0..len).rev() {
2302        let value = array_get(obj, i, ctx).unwrap_or(JSValue::undefined());
2303        let idx_val = if i >= (1usize << 47) {
2304            JSValue::new_float(i as f64)
2305        } else {
2306            JSValue::new_int(i as i64)
2307        };
2308        let callback_args = vec![value, idx_val, this_obj];
2309        if let Ok(result) = call_callback_with_this(ctx, callback, this_arg, &callback_args) {
2310            if result.is_truthy() {
2311                return idx_val;
2312            }
2313        }
2314    }
2315
2316    JSValue::new_int(-1)
2317}
2318
2319fn array_at(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2320    if args.is_empty() {
2321        return JSValue::undefined();
2322    }
2323    let this = args[0];
2324    if !require_object_coercible(ctx, &this) {
2325        return JSValue::undefined();
2326    }
2327    let this = to_object_or_return_undefined(this, ctx);
2328    let len_atom = ctx.common_atoms.length;
2329    let arr = this.as_object();
2330    let len = safe_array_len_with_ctx(&arr, len_atom, ctx) as usize;
2331    let idx_arg = args.get(1).copied().unwrap_or(JSValue::undefined());
2332    let n = match crate::builtins::global::js_to_number_value(ctx, &idx_arg) {
2333        Ok(n) => n,
2334        Err(()) => return JSValue::undefined(),
2335    };
2336    let idx = if n.is_nan() {
2337        0i64
2338    } else {
2339        n.trunc() as i64
2340    };
2341    let actual_idx = if idx < 0 { len as i64 + idx } else { idx };
2342    if actual_idx < 0 || actual_idx as usize >= len {
2343        return JSValue::undefined();
2344    }
2345    array_get(arr, actual_idx as usize, ctx).unwrap_or(JSValue::undefined())
2346}
2347
2348fn array_to_sorted(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2349    if args.is_empty() {
2350        return JSValue::undefined();
2351    }
2352    let this = args[0];
2353    if !require_object_coercible(ctx, &this) {
2354        return JSValue::undefined();
2355    }
2356    let this = to_object_or_return_undefined(this, ctx);
2357    let comparefn = args.get(1).copied().unwrap_or(JSValue::undefined());
2358    if !comparefn.is_undefined() && !comparefn.is_function() {
2359        throw_type_error(ctx, "Comparison function is not callable");
2360        return JSValue::undefined();
2361    }
2362    let len_atom = ctx.common_atoms.length;
2363    let len = {
2364        let arr = this.as_object();
2365        safe_array_len_with_ctx(&arr, len_atom, ctx)
2366    };
2367    if len > ARRAY_LENGTH_LIMIT {
2368        throw_range_error(ctx, "Invalid array length");
2369        return JSValue::undefined();
2370    }
2371    let len = len as usize;
2372    let mut elements: Vec<JSValue> = (0..len)
2373        .map(|i| {
2374            let arr = this.as_object();
2375            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
2376        })
2377        .collect();
2378
2379    let cmp_fn = if comparefn.is_function() { Some(comparefn) } else { None };
2380    let n = elements.len();
2381    for i in 1..n {
2382        let mut j = i;
2383        while j > 0 {
2384            let should_swap = if let Some(func) = cmp_fn {
2385                match call_callback(ctx, func, &[elements[j - 1], elements[j]]) {
2386                    Ok(r) => r.get_float() > 0.0 || (r.is_int() && r.get_int() > 0),
2387                    Err(_) => false,
2388                }
2389            } else {
2390                let a_str = val_to_str(ctx, elements[j - 1]);
2391                let b_str = val_to_str(ctx, elements[j]);
2392                a_str > b_str
2393            };
2394            if should_swap {
2395                elements.swap(j - 1, j);
2396                j -= 1;
2397            } else {
2398                break;
2399            }
2400        }
2401    }
2402
2403    let mut result_arr = new_jsarray_with_proto(ctx);
2404    for v in elements.iter() {
2405        result_arr.push(*v);
2406    }
2407    result_arr
2408        .header
2409        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
2410    alloc_jsarray(result_arr, ctx)
2411}
2412
2413fn array_to_reversed(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2414    if args.is_empty() {
2415        return JSValue::undefined();
2416    }
2417    let this = args[0];
2418    if !require_object_coercible(ctx, &this) {
2419        return JSValue::undefined();
2420    }
2421    let this = to_object_or_return_undefined(this, ctx);
2422    let len_atom = ctx.common_atoms.length;
2423    let len = {
2424        let arr = this.as_object();
2425        safe_array_len_with_ctx(&arr, len_atom, ctx)
2426    };
2427    if len > ARRAY_LENGTH_LIMIT {
2428        throw_range_error(ctx, "Invalid array length");
2429        return JSValue::undefined();
2430    }
2431    let len = len as usize;
2432    let mut elements: Vec<JSValue> = (0..len)
2433        .map(|i| {
2434            let arr = this.as_object();
2435            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
2436        })
2437        .collect();
2438    elements.reverse();
2439    let mut result_arr = new_jsarray_with_proto(ctx);
2440    for v in elements.iter() {
2441        result_arr.push(*v);
2442    }
2443    result_arr
2444        .header
2445        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
2446    alloc_jsarray(result_arr, ctx)
2447}
2448
2449fn array_with(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2450    if args.len() < 3 {
2451        return JSValue::undefined();
2452    }
2453    let this = args[0];
2454    let idx = args[1].get_int();
2455    let value = args[2];
2456    if !require_object_coercible(ctx, &this) {
2457        return JSValue::undefined();
2458    }
2459    let this = to_object_or_return_undefined(this, ctx);
2460    let len_atom = ctx.common_atoms.length;
2461    let len = {
2462        let arr = this.as_object();
2463        safe_array_len_with_ctx(&arr, len_atom, ctx)
2464    };
2465    if len > ARRAY_LENGTH_LIMIT {
2466        throw_range_error(ctx, "Invalid array length");
2467        return JSValue::undefined();
2468    }
2469    let len = len as usize;
2470    let mut elements: Vec<JSValue> = (0..len)
2471        .map(|i| {
2472            let arr = this.as_object();
2473            array_get(arr, i, ctx).unwrap_or(JSValue::undefined())
2474        })
2475        .collect();
2476    let actual_idx = if idx < 0 {
2477        (len as i64 + idx) as usize
2478    } else {
2479        idx as usize
2480    };
2481    if actual_idx < len {
2482        elements[actual_idx] = value;
2483    }
2484    let mut result_arr = new_jsarray_with_proto(ctx);
2485    for v in elements.iter() {
2486        result_arr.push(*v);
2487    }
2488    result_arr
2489        .header
2490        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
2491    alloc_jsarray(result_arr, ctx)
2492}
2493
2494fn array_last_index_of(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2495    if args.len() < 2 {
2496        return JSValue::new_int(-1);
2497    }
2498    let this = args[0];
2499    let search = args[1];
2500    if !require_object_coercible(ctx, &this) {
2501        return JSValue::new_int(-1);
2502    }
2503    let this = to_object_or_return_undefined(this, ctx);
2504    let len_atom = ctx.common_atoms.length;
2505    let arr = this.as_object();
2506    let len = arr.get(len_atom).map(|v| v.get_int() as u64).unwrap_or(0);
2507
2508    let from_index_f = if args.len() > 2 {
2509        args[2].to_number()
2510    } else {
2511        len.saturating_sub(1) as f64
2512    };
2513
2514    let end = if from_index_f.is_nan() {
2515        0i64
2516    } else if from_index_f < 0.0 {
2517        ((len as f64) + from_index_f).max(-1.0) as i64
2518    } else {
2519        (from_index_f as u64).min(len.saturating_sub(1)) as i64
2520    };
2521
2522    if end < 0 || len == 0 {
2523        return JSValue::new_int(-1);
2524    }
2525
2526    let end_u = end as u64;
2527
2528    if end_u > 10_000_000 {
2529        if arr.is_dense_array() {
2530            let ptr = this.get_ptr();
2531            let dense_arr = unsafe { &*(ptr as *const crate::object::array_obj::JSArrayObject) };
2532            let dense_len = dense_arr.elements.len() as u64;
2533            for i in (0..=end_u.min(dense_len.saturating_sub(1))).rev() {
2534                if let Some(val) = dense_arr.elements.get(i as usize) {
2535                    if val.strict_eq(&search) {
2536                        return JSValue::new_int(i as i64);
2537                    }
2538                }
2539            }
2540        } else {
2541            let mut candidates: Vec<u64> = Vec::new();
2542            for slot in arr.iter_props() {
2543                let s = ctx.get_atom_str(slot.atom);
2544                if let Ok(idx) = s.parse::<u64>() {
2545                    if idx <= end_u && idx < len && slot.value.strict_eq(&search) {
2546                        candidates.push(idx);
2547                    }
2548                }
2549            }
2550            if let Some(&max) = candidates.iter().max() {
2551                return JSValue::new_int(max as i64);
2552            }
2553        }
2554    } else {
2555        for i in (0..=end_u).rev() {
2556            if i < len {
2557                if let Some(el) = array_get(arr, i as usize, ctx) {
2558                    if el.strict_eq(&search) {
2559                        return JSValue::new_int(i as i64);
2560                    }
2561                }
2562            }
2563        }
2564    }
2565    JSValue::new_int(-1)
2566}
2567
2568fn array_from_async(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
2569    if args.is_empty() {
2570        return create_thenable_result(ctx, Vec::new());
2571    }
2572
2573    let items = &args[0];
2574
2575    if items.is_null() || items.is_undefined() {
2576        return create_thenable_result(ctx, Vec::new());
2577    }
2578
2579    if items.is_object() {
2580        let obj = items.as_object();
2581        let then_atom = ctx.common_atoms.then;
2582        if obj.get(then_atom).is_some() {
2583            let result = vec![*items];
2584            return create_thenable_result(ctx, result);
2585        }
2586
2587        let len_atom = ctx.common_atoms.length;
2588        if let Some(len_val) = obj.get(len_atom) {
2589            let len = len_val.get_int() as usize;
2590            let mut elements: Vec<JSValue> = Vec::with_capacity(len);
2591            for i in 0..len {
2592                if let Some(val) = array_get(obj, i, ctx) {
2593                    elements.push(val);
2594                } else {
2595                    elements.push(JSValue::undefined());
2596                }
2597            }
2598            return create_thenable_result(ctx, elements);
2599        }
2600    }
2601
2602    create_thenable_result(ctx, Vec::new())
2603}
2604
2605fn create_thenable_result(ctx: &mut JSContext, elements: Vec<JSValue>) -> JSValue {
2606    use crate::object::object::JSObject;
2607
2608    let mut result_arr = new_jsarray_with_proto(ctx);
2609    let len_atom = ctx.common_atoms.length;
2610    for val in elements.iter() {
2611        result_arr.push(*val);
2612    }
2613    result_arr
2614        .header
2615        .set_length(len_atom, JSValue::new_int(result_arr.len() as i64));
2616    let arr_value = alloc_jsarray(result_arr, ctx);
2617
2618    let mut promise = JSObject::new_promise();
2619
2620    if let Some(proto_ptr) = ctx.get_promise_prototype() {
2621        promise.prototype = Some(proto_ptr);
2622    }
2623
2624    let state_atom = ctx.intern("__promise_state__");
2625    promise.set(state_atom, JSValue::new_int(1));
2626
2627    let result_atom = ctx.intern("__promise_result__");
2628    promise.set(result_atom, arr_value);
2629
2630    let reactions_atom = ctx.intern("__promise_reactions__");
2631    promise.set(reactions_atom, JSValue::null());
2632
2633    let promise_ptr = Box::into_raw(Box::new(promise)) as usize;
2634    JSValue::new_object(promise_ptr)
2635}