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