1mod install;
16pub(super) mod sort;
17
18pub(crate) use install::{array_like_type, declare_types};
19pub use install::{declare, install};
20pub(crate) use sort::merge_sort;
21
22use wasmtime::{Caller, StructRef, StructRefPre, Val};
23
24use crate::runtime::StoreData;
25use crate::runtime::array_storage::ArrayStorage;
26use crate::runtime::host::{host_boxed_number_vtable, write_submilli_array_struct};
27use crate::runtime::intrinsic_types::intrinsic_types;
28use crate::runtime::prelude::closure::Closure;
29use crate::runtime::prelude::collection::both_nan;
30use crate::runtime::prelude::iterator::{IterKind, IteratorSource, make_index_iterator};
31use crate::runtime::prelude::keep::{KeptValue, KeptValues, keep_all};
32use crate::runtime::prelude::vtable::{dispatch_vtable_slot, read_string_units};
33
34pub(super) fn read_array(
41 caller: &mut Caller<'_, StoreData>,
42 val: &Val,
43 _name: &str,
44) -> wasmtime::Result<Vec<Val>> {
45 crate::runtime::array_storage::ArrayStorage::read(caller, val)?.snapshot(caller)
46}
47
48pub(super) fn read_kept_array(
54 caller: &mut Caller<'_, StoreData>,
55 val: &Val,
56 name: &str,
57) -> wasmtime::Result<Vec<Val>> {
58 let elements = read_array(caller, val, name)?;
59 keep_all(caller, &elements)?;
60 Ok(elements)
61}
62
63fn build_array(caller: &mut Caller<'_, StoreData>, elements: &[Val]) -> wasmtime::Result<Val> {
65 let st = write_submilli_array_struct(caller, elements)?;
66 Ok(Val::AnyRef(Some(st.to_anyref())))
67}
68
69fn replace_elements(
71 caller: &mut Caller<'_, StoreData>,
72 receiver: &Val,
73 elements: &[Val],
74) -> wasmtime::Result<()> {
75 crate::runtime::array_storage::ArrayStorage::read(caller, receiver)?.replace(caller, elements)
76}
77
78fn box_number(caller: &mut Caller<'_, StoreData>, n: f64) -> wasmtime::Result<Val> {
80 let boxed = intrinsic_types(&mut *caller)?.boxed_number.clone();
81 let vtable = host_boxed_number_vtable(caller)?;
82 let pre = StructRefPre::new(&mut *caller, boxed);
83 let st = StructRef::new(&mut *caller, &pre, &[vtable, Val::F64(n.to_bits())])?;
84 Ok(Val::AnyRef(Some(st.to_anyref())))
85}
86
87pub(super) fn is_array(caller: &mut Caller<'_, StoreData>, val: &Val) -> wasmtime::Result<bool> {
89 let Val::AnyRef(Some(any)) = val else {
90 return Ok(false);
91 };
92 let Some(st) = any.as_struct(&mut *caller)? else {
93 return Ok(false);
94 };
95 let array_ty = intrinsic_types(&mut *caller)?.array.clone();
96 st.matches_ty(&*caller, &array_ty)
97}
98
99fn is_null(v: &Val) -> bool {
100 matches!(v, Val::AnyRef(None))
101}
102
103async fn element_to_string(
107 caller: &mut Caller<'_, StoreData>,
108 elem: Val,
109) -> wasmtime::Result<Vec<u16>> {
110 if is_null(&elem) {
111 return Err(wasmtime::Error::msg("array element toString: null element"));
112 }
113 let s = dispatch_vtable_slot(caller, &elem, 0, &[]).await?;
114 read_string_units(caller, &s, "array element toString result")
115}
116
117async fn join_text(caller: &mut Caller<'_, StoreData>, elem: Val) -> wasmtime::Result<Vec<u16>> {
120 if super::undefined::is_nullish(caller, &elem)? {
121 return Ok(Vec::new());
122 }
123 element_to_string(caller, elem).await
124}
125
126async fn element_matches(
130 caller: &mut Caller<'_, StoreData>,
131 slot: Val,
132 target: Val,
133) -> wasmtime::Result<bool> {
134 if is_null(&slot) || is_null(&target) {
135 return Ok(is_null(&slot) && is_null(&target));
136 }
137 let left_undefined = super::undefined::is_undefined(caller, &slot)?;
138 let right_undefined = super::undefined::is_undefined(caller, &target)?;
139 if left_undefined || right_undefined {
140 return Ok(left_undefined && right_undefined);
141 }
142 element_equals(caller, slot, target).await
143}
144
145async fn element_equals(
147 caller: &mut Caller<'_, StoreData>,
148 slot: Val,
149 target: Val,
150) -> wasmtime::Result<bool> {
151 match dispatch_vtable_slot(caller, &slot, 2, &[target]).await? {
152 Val::I32(b) => Ok(b != 0),
153 other => Err(wasmtime::Error::msg(format!(
154 "array element equals returned {other:?}, expected i32"
155 ))),
156 }
157}
158
159fn trunc_sat(x: f64) -> i32 {
165 x as i32
166}
167
168fn norm_clamp(x: f64, len: i32) -> i32 {
170 let mut i = trunc_sat(x);
171 if i < 0 {
172 i += len;
173 }
174 i.clamp(0, len)
175}
176
177fn at_index(x: f64, len: i32) -> Option<usize> {
179 let mut i = trunc_sat(x);
180 if i < 0 {
181 i += len;
182 }
183 if i < 0 || i >= len {
184 None
185 } else {
186 Some(i as usize)
187 }
188}
189
190fn fwd_from(x: f64, len: i32) -> i32 {
192 let mut fi = trunc_sat(x);
193 if fi < 0 {
194 fi += len;
195 }
196 fi.max(0)
197}
198
199fn last_from(x: f64, len: i32) -> Option<i32> {
202 let mut fi = trunc_sat(x);
203 if fi < 0 {
204 fi += len;
205 }
206 if fi < 0 {
207 return None;
208 }
209 Some(fi.min(len - 1))
210}
211
212fn at(caller: &mut Caller<'_, StoreData>, receiver: &Val, index: f64) -> wasmtime::Result<Val> {
218 let storage = ArrayStorage::read(caller, receiver)?;
219 match at_index(index, storage.len as i32) {
220 Some(i) => storage.get(caller, i as u32),
221 None => super::undefined::value(caller),
222 }
223}
224
225fn slice(
227 caller: &mut Caller<'_, StoreData>,
228 receiver: &Val,
229 start: f64,
230 end: f64,
231) -> wasmtime::Result<Vec<Val>> {
232 let storage = ArrayStorage::read(caller, receiver)?;
233 let len = storage.len as i32;
234 let si = norm_clamp(start, len);
235 let count = (norm_clamp(end, len) - si).max(0);
236 storage.range(caller, si as u32, count as u32)
237}
238
239fn concat(
242 caller: &mut Caller<'_, StoreData>,
243 mut acc: Vec<Val>,
244 others: &Val,
245) -> wasmtime::Result<Vec<Val>> {
246 for item in read_array(caller, others, "Array#concat")? {
249 if is_array(caller, &item)? {
250 acc.extend(read_array(caller, &item, "Array#concat")?);
251 } else {
252 acc.push(item);
253 }
254 }
255 Ok(acc)
256}
257
258async fn index_of(
259 caller: &mut Caller<'_, StoreData>,
260 elements: Vec<Val>,
261 target: Val,
262 from: f64,
263) -> wasmtime::Result<f64> {
264 let len = elements.len() as i32;
265 let mut i = fwd_from(from, len);
266 while i < len {
267 let e = elements[i as usize];
268 if element_matches(caller, e, target).await? {
269 return Ok(i as f64);
270 }
271 i += 1;
272 }
273 Ok(-1.0)
274}
275
276async fn last_index_of(
277 caller: &mut Caller<'_, StoreData>,
278 elements: Vec<Val>,
279 target: Val,
280 from: f64,
281) -> wasmtime::Result<f64> {
282 let len = elements.len() as i32;
283 let Some(mut i) = last_from(from, len) else {
284 return Ok(-1.0);
285 };
286 while i >= 0 {
287 let e = elements[i as usize];
288 if element_matches(caller, e, target).await? {
289 return Ok(i as f64);
290 }
291 i -= 1;
292 }
293 Ok(-1.0)
294}
295
296async fn includes(
297 caller: &mut Caller<'_, StoreData>,
298 elements: Vec<Val>,
299 target: Val,
300 from: f64,
301) -> wasmtime::Result<bool> {
302 let len = elements.len() as i32;
303 let mut i = fwd_from(from, len);
304 while i < len {
305 let e = elements[i as usize];
306 if element_matches(caller, e, target).await? || both_nan(caller, &e, &target)? {
308 return Ok(true);
309 }
310 i += 1;
311 }
312 Ok(false)
313}
314
315async fn join(
316 caller: &mut Caller<'_, StoreData>,
317 elements: Vec<Val>,
318 sep: Vec<u16>,
319) -> wasmtime::Result<Vec<u16>> {
320 if elements.is_empty() {
321 return Ok(Vec::new());
322 }
323 let mut acc = join_text(caller, elements[0]).await?;
324 for &e in &elements[1..] {
325 acc.extend_from_slice(&sep);
326 let s = join_text(caller, e).await?;
327 acc.extend_from_slice(&s);
328 }
329 Ok(acc)
330}
331
332fn push(
337 caller: &mut Caller<'_, StoreData>,
338 receiver: &Val,
339 items: Vec<Val>,
340) -> wasmtime::Result<f64> {
341 let mut storage = ArrayStorage::read(caller, receiver)?;
342 let mut length = f64::from(storage.len);
343 for item in items {
344 length = storage.push(caller, item)?;
345 }
346 Ok(length)
347}
348
349pub(crate) fn set_length(
352 caller: &mut Caller<'_, StoreData>,
353 receiver: &Val,
354 value: f64,
355) -> wasmtime::Result<()> {
356 if !(value >= 0.0 && value.fract() == 0.0 && value <= f64::from(u32::MAX)) {
357 return Err(crate::runtime::host::range_error("Invalid array length"));
358 }
359 let mut storage = ArrayStorage::read(caller, receiver)?;
360 if value > f64::from(storage.len) {
361 return Err(crate::runtime::host::range_error(format!(
362 "Cannot grow an array by writing length {value}; its length is {}",
363 storage.len
364 )));
365 }
366 storage.resize(caller, value as usize, Val::null_any_ref())
367}
368
369fn pop(caller: &mut Caller<'_, StoreData>, receiver: &Val) -> wasmtime::Result<Val> {
371 let popped = ArrayStorage::read(caller, receiver)?.pop(caller)?;
372 match popped {
373 Some(value) => Ok(value),
374 None => super::undefined::value(caller),
375 }
376}
377
378fn shift(
379 caller: &mut Caller<'_, StoreData>,
380 receiver: &Val,
381 mut elements: Vec<Val>,
382) -> wasmtime::Result<Val> {
383 if elements.is_empty() {
384 return super::undefined::value(caller);
385 }
386 let first = elements.remove(0);
387 replace_elements(caller, receiver, &elements)?;
388 Ok(first)
389}
390
391fn unshift(
392 caller: &mut Caller<'_, StoreData>,
393 receiver: &Val,
394 elements: Vec<Val>,
395 mut items: Vec<Val>,
396) -> wasmtime::Result<f64> {
397 items.extend(elements);
398 let n = items.len() as f64;
399 replace_elements(caller, receiver, &items)?;
400 Ok(n)
401}
402
403fn reverse(
404 caller: &mut Caller<'_, StoreData>,
405 receiver: &Val,
406 mut elements: Vec<Val>,
407) -> wasmtime::Result<Val> {
408 elements.reverse();
409 replace_elements(caller, receiver, &elements)?;
410 Ok(*receiver)
411}
412
413fn fill(
414 caller: &mut Caller<'_, StoreData>,
415 receiver: &Val,
416 mut elements: Vec<Val>,
417 value: Val,
418 start: f64,
419 end: f64,
420) -> wasmtime::Result<Val> {
421 let len = elements.len() as i32;
422 let si = norm_clamp(start, len);
423 let ei = norm_clamp(end, len);
424 for slot in elements.iter_mut().take(ei as usize).skip(si as usize) {
425 *slot = value;
426 }
427 replace_elements(caller, receiver, &elements)?;
428 Ok(*receiver)
429}
430
431fn copy_within(
432 caller: &mut Caller<'_, StoreData>,
433 receiver: &Val,
434 mut elements: Vec<Val>,
435 target: f64,
436 start: f64,
437 end: f64,
438) -> wasmtime::Result<Val> {
439 let len = elements.len() as i32;
440 let ti = norm_clamp(target, len);
441 let si = norm_clamp(start, len);
442 let ei = norm_clamp(end, len);
443 let mut count = (ei - si).max(0);
444 let rem = len - ti;
445 if count > rem {
446 count = rem;
447 }
448 let src: Vec<Val> = elements[si as usize..(si + count) as usize].to_vec();
450 for (k, v) in src.into_iter().enumerate() {
451 elements[ti as usize + k] = v;
452 }
453 replace_elements(caller, receiver, &elements)?;
454 Ok(*receiver)
455}
456
457fn splice_parts(
460 elements: &[Val],
461 start: f64,
462 delete_count: f64,
463 items: Vec<Val>,
464) -> (Vec<Val>, Vec<Val>) {
465 let len = elements.len() as i32;
466 let si = norm_clamp(start, len);
467 let mut dc = trunc_sat(delete_count).max(0);
468 let rem = len - si;
469 if dc > rem {
470 dc = rem;
471 }
472 let (si, dc) = (si as usize, dc as usize);
473 let removed = elements[si..si + dc].to_vec();
474 let mut result = Vec::with_capacity(elements.len() - dc + items.len());
475 result.extend_from_slice(&elements[..si]);
476 result.extend(items);
477 result.extend_from_slice(&elements[si + dc..]);
478 (removed, result)
479}
480
481fn splice(
482 caller: &mut Caller<'_, StoreData>,
483 receiver: &Val,
484 elements: Vec<Val>,
485 start: f64,
486 delete_count: f64,
487 items: Vec<Val>,
488) -> wasmtime::Result<Val> {
489 let (removed, result) = splice_parts(&elements, start, delete_count, items);
490 let removed = build_array(caller, &removed)?;
493 replace_elements(caller, receiver, &result)?;
494 Ok(removed)
495}
496
497async fn sort(
498 caller: &mut Caller<'_, StoreData>,
499 receiver: &Val,
500 mut elements: Vec<Val>,
501 cmp: Option<Closure>,
502) -> wasmtime::Result<Val> {
503 sort_elems(caller, &mut elements, cmp.as_ref()).await?;
504 ArrayStorage::read(caller, receiver)?.overwrite_prefix(caller, &elements)?;
506 Ok(*receiver)
507}
508
509fn to_reversed(mut elements: Vec<Val>) -> Vec<Val> {
514 elements.reverse();
515 elements
516}
517
518async fn to_sorted(
519 caller: &mut Caller<'_, StoreData>,
520 mut elements: Vec<Val>,
521 cmp: Option<Closure>,
522) -> wasmtime::Result<Vec<Val>> {
523 sort_elems(caller, &mut elements, cmp.as_ref()).await?;
524 Ok(elements)
525}
526
527async fn sort_elems(
530 caller: &mut Caller<'_, StoreData>,
531 elements: &mut Vec<Val>,
532 cmp: Option<&Closure>,
533) -> wasmtime::Result<()> {
534 let original_len = elements.len();
535 let mut defined_len = 0usize;
536 for index in 0..original_len {
537 let element = *elements
538 .get(index)
539 .ok_or_else(|| crate::runtime::host::fatal_host_error("sort: invalid element index"))?;
540 if !super::undefined::is_undefined(caller, &element)? {
541 let slot = elements.get_mut(defined_len).ok_or_else(|| {
542 crate::runtime::host::fatal_host_error("sort: invalid output index")
543 })?;
544 *slot = element;
545 defined_len += 1;
546 }
547 }
548 elements.truncate(defined_len);
549 match cmp {
550 Some(cmp) => merge_sort(caller, elements, cmp, |_, element| Ok(element)).await?,
551 None => sort::sort_by_string(caller, elements).await?,
552 }
553 if defined_len != original_len {
554 let undefined = super::undefined::value(caller)?;
555 elements
556 .try_reserve_exact(original_len - defined_len)
557 .map_err(crate::runtime::host::fatal_host_error)?;
558 elements.resize(original_len, undefined);
559 }
560 Ok(())
561}
562
563fn with(elements: &[Val], index: f64, value: Val) -> Option<Vec<Val>> {
566 let i = at_index(index, elements.len() as i32)?;
567 let mut new = elements.to_vec();
568 new[i] = value;
569 Some(new)
570}
571
572pub(in crate::runtime::prelude) struct ElementCallback<'a> {
581 f: &'a Closure,
582 reads: usize,
583 array: Option<Val>,
584}
585
586impl<'a> ElementCallback<'a> {
587 pub(in crate::runtime::prelude) fn new(
588 caller: &mut Caller<'_, StoreData>,
589 f: &'a Closure,
590 array: Option<Val>,
591 ) -> wasmtime::Result<Self> {
592 let reads = f.arguments_read(caller)?;
593 Ok(Self { f, reads, array })
594 }
595
596 pub(in crate::runtime::prelude) async fn call(
598 &self,
599 caller: &mut Caller<'_, StoreData>,
600 leading: Option<Val>,
601 value: Val,
602 index: usize,
603 ) -> wasmtime::Result<Val> {
604 let mut args: Vec<Val> = leading.into_iter().chain([value]).collect();
605 if args.len() < self.reads {
606 args.push(box_number(caller, index as f64)?);
607 }
608 if let Some(array) = self.array
609 && args.len() < self.reads
610 {
611 args.push(array);
612 }
613 self.f.call_dynamic(caller, &args).await
614 }
615
616 pub(in crate::runtime::prelude) async fn test(
617 &self,
618 caller: &mut Caller<'_, StoreData>,
619 value: Val,
620 index: usize,
621 ) -> wasmtime::Result<bool> {
622 let result = self.call(caller, None, value, index).await?;
623 super::value::truthy(caller, &result)
624 }
625}
626
627pub async fn for_each(
629 caller: &mut Caller<'_, StoreData>,
630 array: Val,
631 elements: Vec<Val>,
632 f: &Closure,
633) -> wasmtime::Result<()> {
634 let f = ElementCallback::new(caller, f, Some(array))?;
635 for (index, elem) in elements.into_iter().enumerate() {
636 f.call(caller, None, elem, index).await?;
637 }
638 Ok(())
639}
640
641async fn map(
642 caller: &mut Caller<'_, StoreData>,
643 array: Val,
644 elements: Vec<Val>,
645 f: &Closure,
646) -> wasmtime::Result<Vec<Val>> {
647 let f = ElementCallback::new(caller, f, Some(array))?;
648 let mut out = KeptValues::with_capacity(caller, elements.len())?;
649 for (index, elem) in elements.into_iter().enumerate() {
650 let mapped = f.call(caller, None, elem, index).await?;
651 out.push(caller, mapped)?;
652 }
653 Ok(out.values().to_vec())
654}
655
656async fn filter(
657 caller: &mut Caller<'_, StoreData>,
658 array: Val,
659 elements: Vec<Val>,
660 pred: &Closure,
661) -> wasmtime::Result<Vec<Val>> {
662 let pred = ElementCallback::new(caller, pred, Some(array))?;
663 let mut out = Vec::new();
664 for (index, elem) in elements.into_iter().enumerate() {
665 if pred.test(caller, elem, index).await? {
666 out.push(elem);
667 }
668 }
669 Ok(out)
670}
671
672async fn reduce(
675 caller: &mut Caller<'_, StoreData>,
676 array: Val,
677 elements: Vec<Val>,
678 f: &Closure,
679 mut acc: Val,
680 reverse: bool,
681) -> wasmtime::Result<Val> {
682 let f = ElementCallback::new(caller, f, Some(array))?;
683 let mut indexed: Vec<(usize, Val)> = elements.into_iter().enumerate().collect();
684 if reverse {
685 indexed.reverse();
686 }
687 let kept_acc = KeptValue::new(caller)?;
689 for (index, elem) in indexed {
690 acc = f.call(caller, Some(acc), elem, index).await?;
691 kept_acc.set(caller, acc)?;
692 }
693 Ok(acc)
694}
695
696async fn find_match(
698 caller: &mut Caller<'_, StoreData>,
699 array: Val,
700 elements: &[Val],
701 pred: &Closure,
702 reverse: bool,
703) -> wasmtime::Result<Option<usize>> {
704 let pred = ElementCallback::new(caller, pred, Some(array))?;
705 let order: Vec<usize> = if reverse {
706 (0..elements.len()).rev().collect()
707 } else {
708 (0..elements.len()).collect()
709 };
710 for i in order {
711 if pred.test(caller, elements[i], i).await? {
712 return Ok(Some(i));
713 }
714 }
715 Ok(None)
716}
717
718async fn some(
719 caller: &mut Caller<'_, StoreData>,
720 array: Val,
721 elements: Vec<Val>,
722 pred: &Closure,
723) -> wasmtime::Result<bool> {
724 let pred = ElementCallback::new(caller, pred, Some(array))?;
725 for (index, elem) in elements.into_iter().enumerate() {
726 if pred.test(caller, elem, index).await? {
727 return Ok(true);
728 }
729 }
730 Ok(false)
731}
732
733async fn every(
734 caller: &mut Caller<'_, StoreData>,
735 array: Val,
736 elements: Vec<Val>,
737 pred: &Closure,
738) -> wasmtime::Result<bool> {
739 let pred = ElementCallback::new(caller, pred, Some(array))?;
740 for (index, elem) in elements.into_iter().enumerate() {
741 if !pred.test(caller, elem, index).await? {
742 return Ok(false);
743 }
744 }
745 Ok(true)
746}
747
748fn flat_into(
751 caller: &mut Caller<'_, StoreData>,
752 elements: Vec<Val>,
753 depth: i32,
754 out: &mut Vec<Val>,
755) -> wasmtime::Result<()> {
756 let mut frames = vec![(elements.into_iter(), depth)];
757 while let Some((elements, depth)) = frames.last_mut() {
758 let Some(elem) = elements.next() else {
759 frames.pop();
760 continue;
761 };
762 let depth = *depth;
763 if depth > 0 && is_array(caller, &elem)? {
764 if frames.len() >= crate::runtime::MAX_VTABLE_WALK_DEPTH as usize {
765 return Err(crate::runtime::host::range_error(format!(
766 "Array#flat exceeds {} levels of nesting; flatten fewer levels",
767 crate::runtime::MAX_VTABLE_WALK_DEPTH,
768 )));
769 }
770 let sub = read_array(caller, &elem, "Array#flat")?;
771 frames
772 .try_reserve(1)
773 .map_err(crate::runtime::host::fatal_host_error)?;
774 frames.push((sub.into_iter(), depth - 1));
775 } else {
776 out.try_reserve(1)
777 .map_err(crate::runtime::host::fatal_host_error)?;
778 out.push(elem);
779 }
780 }
781 Ok(())
782}
783
784async fn flat_map(
785 caller: &mut Caller<'_, StoreData>,
786 array: Val,
787 elements: Vec<Val>,
788 f: &Closure,
789) -> wasmtime::Result<Vec<Val>> {
790 let f = ElementCallback::new(caller, f, Some(array))?;
791 let kept_mapped = KeptValue::new(caller)?;
792 let mut out = KeptValues::with_capacity(caller, elements.len())?;
793 for (index, elem) in elements.into_iter().enumerate() {
794 let mapped = f.call(caller, None, elem, index).await?;
795 kept_mapped.set(caller, mapped)?;
798 let flattened = read_array(caller, &mapped, "Array#flatMap callback result")?;
799 out.extend(caller, &flattened)?;
800 }
801 Ok(out.values().to_vec())
802}
803
804fn array_step(
813 caller: &mut Caller<'_, StoreData>,
814 payload: &Val,
815 pos: i32,
816) -> wasmtime::Result<Option<(Val, Val)>> {
817 let storage = crate::runtime::array_storage::ArrayStorage::read(caller, payload)?;
818 let backing = storage.backing;
819 if pos < 0 || pos as u32 >= storage.len {
820 return Ok(None);
821 }
822 let elem = backing.get(&mut *caller, pos as u32)?;
823 let idx = box_number(caller, pos as f64)?;
824 Ok(Some((idx, elem)))
825}
826
827pub fn values(caller: &mut Caller<'_, StoreData>, array: &Val) -> wasmtime::Result<Val> {
828 make_index_iterator(
829 caller,
830 IteratorSource::Array,
831 *array,
832 IterKind::Values,
833 array_step,
834 )
835}
836
837fn keys(caller: &mut Caller<'_, StoreData>, array: &Val) -> wasmtime::Result<Val> {
838 make_index_iterator(
839 caller,
840 IteratorSource::Array,
841 *array,
842 IterKind::Keys,
843 array_step,
844 )
845}
846
847fn entries(caller: &mut Caller<'_, StoreData>, array: &Val) -> wasmtime::Result<Val> {
848 make_index_iterator(
849 caller,
850 IteratorSource::Array,
851 *array,
852 IterKind::Entries,
853 array_step,
854 )
855}
856
857pub(super) async fn from(
869 caller: &mut Caller<'_, StoreData>,
870 src: &Val,
871 map_fn: &Val,
872) -> wasmtime::Result<Val> {
873 use crate::runtime::prelude::closure;
874 use crate::runtime::prelude::collection::{is_a, object_field, string_code_points, unbox_bool};
875
876 let map_closure = if super::undefined::is_undefined(caller, map_fn)? {
877 None
878 } else {
879 Some(closure::read_callback(caller, map_fn, "Array.from mapFn")?)
880 };
881 let map_fn = match &map_closure {
882 Some(c) => Some(ElementCallback::new(caller, c, None)?),
883 None => None,
884 };
885 let intr = intrinsic_types(&mut *caller)?;
886 let mut out = KeptValues::with_capacity(caller, 0)?;
887
888 if is_a(caller, src, &intr.array)? {
889 let mut pos: u32 = 0;
893 loop {
894 let storage = crate::runtime::array_storage::ArrayStorage::read(caller, src)?;
895 let backing = storage.backing;
896 if pos >= storage.len {
897 break;
898 }
899 out.reserve(caller, 1)?;
900 let elem = backing.get(&mut *caller, pos)?;
901 let mapped = apply_map(caller, &map_fn, elem, out.values().len()).await?;
902 out.push(caller, mapped)?;
903 pos += 1;
904 }
905 return build_array(caller, out.values());
906 }
907 if is_a(caller, src, &intr.string)? {
908 let cps = string_code_points(caller, src)?;
909 out.reserve(caller, cps.len())?;
910 for cp in cps {
911 let mapped = apply_map(caller, &map_fn, cp, out.values().len()).await?;
912 out.push(caller, mapped)?;
913 }
914 return build_array(caller, out.values());
915 }
916
917 let kept_iterator = KeptValue::new(caller)?;
919 let kept_result = KeptValue::new(caller)?;
920 let it = if is_a(
921 caller,
922 src,
923 &super::map::map_backing_struct(caller.engine(), &intr)?,
924 )? {
925 super::map::entries(caller, src)?
926 } else if is_a(
927 caller,
928 src,
929 &super::set::set_backing_struct(caller.engine(), &intr)?,
930 )? {
931 super::set::values(caller, src)?
932 } else if is_a(caller, src, &intr.uint8_array)? {
933 super::uint8array::values(caller, src)?
934 } else if let Some(iter_method) = object_field(caller, src, "iterator")? {
935 let c = closure::read(caller, &iter_method, "Array.from iterable")?;
936 c.call_with_receiver(caller, *src, &[]).await?
937 } else if object_field(caller, src, "next")?.is_none()
938 && let Some(length) = object_field(caller, src, "length")?
939 {
940 let length = array_like_length(caller, &length)?;
941 for index in 0..length {
942 out.reserve(caller, 1)?;
943 let hole = super::undefined::value(caller)?;
944 let mapped = apply_map(caller, &map_fn, hole, index).await?;
945 out.push(caller, mapped)?;
946 }
947 return build_array(caller, out.values());
948 } else {
949 *src
950 };
951 kept_iterator.set(caller, it)?;
952
953 let next = object_field(caller, &it, "next")?
954 .ok_or_else(|| wasmtime::Error::msg("Array.from: source is not iterable"))?;
955 let next_closure = closure::read(caller, &next, "Array.from iterator")?;
956 loop {
957 out.reserve(caller, 1)?;
958 let result = next_closure.call_with_receiver(caller, it, &[]).await?;
959 kept_result.set(caller, result)?;
960 let done = object_field(caller, &result, "done")?
961 .ok_or_else(|| wasmtime::Error::msg("Array.from: iterator result missing `done`"))?;
962 if unbox_bool(caller, &done)? {
963 break;
964 }
965 let value = object_field(caller, &result, "value")?
966 .ok_or_else(|| wasmtime::Error::msg("Array.from: iterator result missing `value`"))?;
967 let mapped = apply_map(caller, &map_fn, value, out.values().len()).await?;
968 out.push(caller, mapped)?;
969 }
970 build_array(caller, out.values())
971}
972
973fn array_like_length(caller: &mut Caller<'_, StoreData>, length: &Val) -> wasmtime::Result<usize> {
977 let not_a_number = || wasmtime::Error::msg("Array.from: `length` is not a number");
978 let Val::AnyRef(Some(any)) = length else {
979 return Err(not_a_number());
980 };
981 let st = any.as_struct(&mut *caller)?.ok_or_else(not_a_number)?;
982 let Val::F64(bits) = st.field(&mut *caller, 1)? else {
983 return Err(not_a_number());
984 };
985 let length = f64::from_bits(bits);
986 if length.is_nan() || length <= 0.0 {
987 return Ok(0);
988 }
989 let length = length.trunc();
990 let invalid = || crate::runtime::host::range_error("Invalid array length");
991 if length > f64::from(u32::MAX) {
992 return Err(invalid());
993 }
994 usize::try_from(length as u32).map_err(|_| invalid())
995}
996
997async fn apply_map(
998 caller: &mut Caller<'_, StoreData>,
999 map_fn: &Option<ElementCallback<'_>>,
1000 value: Val,
1001 index: usize,
1002) -> wasmtime::Result<Val> {
1003 match map_fn {
1004 Some(f) => f.call(caller, None, value, index).await,
1005 None => Ok(value),
1006 }
1007}