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}