Skip to main content

submilli_engine/runtime/prelude/array/
install.rs

1//! ABI wiring for the Rust `Array` / `ArrayConstructor` methods: marshals
2//! `$Array`/`$Closure`/`$string`, registers each method under its dispatch key,
3//! and declares the value symbols codegen routes through. The operations
4//! themselves live in the parent module.
5
6use crate::runtime::host::{abi_arg, abi_result};
7use wasmtime::{FuncType, HeapType, Linker, RefType, StructType, Val, ValType};
8
9use crate::runtime::StoreData;
10use crate::runtime::host::{
11    intrinsic_array_type, intrinsic_string_type, register_host_fn, register_host_fn_async,
12    write_submilli_string_struct_units,
13};
14use crate::runtime::intrinsic_types::{IntrinsicTypes, build_intrinsic_types};
15use crate::runtime::prelude::vtable::read_string_units;
16use crate::runtime::prelude::{MODULE_NAME, closure, declare_method};
17use crate::{MangledName, PackageDeclaration, Param, Type};
18
19/// The dispatch key codegen looks up for `Array#<method>`.
20fn method_key(method: &str) -> MangledName {
21    crate::mangle::extend(&crate::mangle::prelude("Array"), method)
22}
23
24/// The dispatch key for an `ArrayConstructor` static (`Array.of`).
25fn ctor_key(method: &str) -> MangledName {
26    crate::mangle::extend(&crate::mangle::prelude("ArrayConstructor"), method)
27}
28
29/// TypeScript's `(value: T, index: number, array: T[])` callback parameters,
30/// after `leading` (`reduce`'s accumulator). A callback may declare fewer.
31fn element_callback_params(leading: Vec<Type>) -> Vec<Type> {
32    let t = Type::TypeVar("T".to_string());
33    leading
34        .into_iter()
35        .chain([t.clone(), Type::Number, Type::Array(Box::new(t))])
36        .collect()
37}
38
39fn ref_to(struct_ty: StructType) -> ValType {
40    ValType::Ref(RefType::new(false, HeapType::ConcreteStruct(struct_ty)))
41}
42
43/// A `(ref null $Object)` — the boxed-element slot. Element args/returns,
44/// `T | undefined` returns, the (nullable) `sort` comparator, and search targets all
45/// lower to this.
46fn object_ref(intr: &IntrinsicTypes) -> ValType {
47    ValType::Ref(RefType::new(
48        true,
49        HeapType::ConcreteStruct(intr.object.clone()),
50    ))
51}
52
53fn f64v(v: &Val) -> f64 {
54    match v {
55        Val::F64(bits) => f64::from_bits(*bits),
56        _ => f64::NAN,
57    }
58}
59
60#[allow(clippy::too_many_lines)]
61pub fn install(linker: &mut Linker<StoreData>) -> wasmtime::Result<()> {
62    let engine = linker.engine().clone();
63    let intr = build_intrinsic_types(&engine)?;
64    let array = ref_to(intrinsic_array_type(&engine)?);
65    let string = ref_to(intrinsic_string_type(&engine)?);
66    let elem = object_ref(&intr);
67    let num = ValType::F64;
68    let boolean = ValType::I32;
69    // Callbacks cross erased, like `sort`'s comparator: whatever parameters a
70    // callback declares, it is called with that many (see `ElementCallback`).
71    let callback = elem.clone();
72
73    let ft = |params: Vec<ValType>, results: Vec<ValType>| FuncType::new(&engine, params, results);
74
75    // --- Accessors -------------------------------------------------------
76    register_host_fn(
77        linker,
78        MODULE_NAME,
79        method_key("at"),
80        ft(vec![array.clone(), num.clone()], vec![elem.clone()]),
81        true,
82        |caller, params, results| {
83            *abi_result(results, 0)? =
84                super::at(caller, abi_arg(params, 0)?, f64v(abi_arg(params, 1)?))?;
85            Ok(())
86        },
87    )?;
88    register_host_fn(
89        linker,
90        MODULE_NAME,
91        method_key("slice"),
92        ft(
93            vec![array.clone(), num.clone(), num.clone()],
94            vec![array.clone()],
95        ),
96        true,
97        |caller, params, results| {
98            let out = super::slice(
99                caller,
100                abi_arg(params, 0)?,
101                f64v(abi_arg(params, 1)?),
102                f64v(abi_arg(params, 2)?),
103            )?;
104            *abi_result(results, 0)? = super::build_array(caller, &out)?;
105            Ok(())
106        },
107    )?;
108    register_host_fn(
109        linker,
110        MODULE_NAME,
111        method_key("concat"),
112        ft(vec![array.clone(), array.clone()], vec![array.clone()]),
113        true,
114        |caller, params, results| {
115            let self_elems = super::read_array(caller, abi_arg(params, 0)?, "Array#concat")?;
116            let out = super::concat(caller, self_elems, abi_arg(params, 1)?)?;
117            *abi_result(results, 0)? = super::build_array(caller, &out)?;
118            Ok(())
119        },
120    )?;
121    register_host_fn_async(
122        linker,
123        MODULE_NAME,
124        method_key("join"),
125        ft(vec![array.clone(), string.clone()], vec![string.clone()]),
126        true,
127        |caller, params, results| {
128            Box::pin(async move {
129                let elements = super::read_kept_array(caller, abi_arg(params, 0)?, "Array#join")?;
130                let sep = read_string_units(caller, abi_arg(params, 1)?, "Array#join separator")?;
131                let out = super::join(caller, elements, sep).await?;
132                let st = write_submilli_string_struct_units(caller, &out)?;
133                *abi_result(results, 0)? = Val::AnyRef(Some(st.to_anyref()));
134                Ok(())
135            })
136        },
137    )?;
138    register_host_fn_async(
139        linker,
140        MODULE_NAME,
141        method_key("indexOf"),
142        ft(
143            vec![array.clone(), elem.clone(), num.clone()],
144            vec![num.clone()],
145        ),
146        true,
147        |caller, params, results| {
148            Box::pin(async move {
149                let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#indexOf")?;
150                let r = super::index_of(
151                    caller,
152                    elements,
153                    *abi_arg(params, 1)?,
154                    f64v(abi_arg(params, 2)?),
155                )
156                .await?;
157                *abi_result(results, 0)? = Val::F64(r.to_bits());
158                Ok(())
159            })
160        },
161    )?;
162    register_host_fn_async(
163        linker,
164        MODULE_NAME,
165        method_key("lastIndexOf"),
166        ft(
167            vec![array.clone(), elem.clone(), num.clone()],
168            vec![num.clone()],
169        ),
170        true,
171        |caller, params, results| {
172            Box::pin(async move {
173                let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#lastIndexOf")?;
174                let r = super::last_index_of(
175                    caller,
176                    elements,
177                    *abi_arg(params, 1)?,
178                    f64v(abi_arg(params, 2)?),
179                )
180                .await?;
181                *abi_result(results, 0)? = Val::F64(r.to_bits());
182                Ok(())
183            })
184        },
185    )?;
186    register_host_fn_async(
187        linker,
188        MODULE_NAME,
189        method_key("includes"),
190        ft(
191            vec![array.clone(), elem.clone(), num.clone()],
192            vec![boolean.clone()],
193        ),
194        true,
195        |caller, params, results| {
196            Box::pin(async move {
197                let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#includes")?;
198                let r = super::includes(
199                    caller,
200                    elements,
201                    *abi_arg(params, 1)?,
202                    f64v(abi_arg(params, 2)?),
203                )
204                .await?;
205                *abi_result(results, 0)? = Val::I32(i32::from(r));
206                Ok(())
207            })
208        },
209    )?;
210
211    // --- Mutators --------------------------------------------------------
212    register_host_fn(
213        linker,
214        MODULE_NAME,
215        method_key("push"),
216        ft(vec![array.clone(), elem.clone()], vec![num.clone()]),
217        true,
218        |caller, params, results| {
219            let [receiver, element] = params else {
220                return Err(crate::runtime::host::fatal_host_error(
221                    "invalid Array#push arguments",
222                ));
223            };
224            let [result] = results else {
225                return Err(crate::runtime::host::fatal_host_error(
226                    "invalid Array#push result slot",
227                ));
228            };
229            let n = super::push(caller, receiver, *element)?;
230            *result = Val::F64(n.to_bits());
231            Ok(())
232        },
233    )?;
234    register_host_fn(
235        linker,
236        MODULE_NAME,
237        method_key("pop"),
238        ft(vec![array.clone()], vec![elem.clone()]),
239        true,
240        |caller, params, results| {
241            *abi_result(results, 0)? = super::pop(caller, abi_arg(params, 0)?)?;
242            Ok(())
243        },
244    )?;
245    register_host_fn(
246        linker,
247        MODULE_NAME,
248        method_key("shift"),
249        ft(vec![array.clone()], vec![elem.clone()]),
250        true,
251        |caller, params, results| {
252            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#shift")?;
253            *abi_result(results, 0)? = super::shift(caller, abi_arg(params, 0)?, elements)?;
254            Ok(())
255        },
256    )?;
257    register_host_fn(
258        linker,
259        MODULE_NAME,
260        method_key("unshift"),
261        ft(vec![array.clone(), array.clone()], vec![num.clone()]),
262        true,
263        |caller, params, results| {
264            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#unshift")?;
265            let items = super::read_array(caller, abi_arg(params, 1)?, "Array#unshift")?;
266            let n = super::unshift(caller, abi_arg(params, 0)?, elements, items)?;
267            *abi_result(results, 0)? = Val::F64(n.to_bits());
268            Ok(())
269        },
270    )?;
271    register_host_fn(
272        linker,
273        MODULE_NAME,
274        method_key("reverse"),
275        ft(vec![array.clone()], vec![array.clone()]),
276        true,
277        |caller, params, results| {
278            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#reverse")?;
279            *abi_result(results, 0)? = super::reverse(caller, abi_arg(params, 0)?, elements)?;
280            Ok(())
281        },
282    )?;
283    register_host_fn(
284        linker,
285        MODULE_NAME,
286        method_key("fill"),
287        ft(
288            vec![array.clone(), elem.clone(), num.clone(), num.clone()],
289            vec![array.clone()],
290        ),
291        true,
292        |caller, params, results| {
293            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#fill")?;
294            *abi_result(results, 0)? = super::fill(
295                caller,
296                abi_arg(params, 0)?,
297                elements,
298                *abi_arg(params, 1)?,
299                f64v(abi_arg(params, 2)?),
300                f64v(abi_arg(params, 3)?),
301            )?;
302            Ok(())
303        },
304    )?;
305    register_host_fn(
306        linker,
307        MODULE_NAME,
308        method_key("copyWithin"),
309        ft(
310            vec![array.clone(), num.clone(), num.clone(), num.clone()],
311            vec![array.clone()],
312        ),
313        true,
314        |caller, params, results| {
315            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#copyWithin")?;
316            *abi_result(results, 0)? = super::copy_within(
317                caller,
318                abi_arg(params, 0)?,
319                elements,
320                f64v(abi_arg(params, 1)?),
321                f64v(abi_arg(params, 2)?),
322                f64v(abi_arg(params, 3)?),
323            )?;
324            Ok(())
325        },
326    )?;
327    register_host_fn(
328        linker,
329        MODULE_NAME,
330        method_key("splice"),
331        ft(
332            vec![array.clone(), num.clone(), num.clone(), array.clone()],
333            vec![array.clone()],
334        ),
335        true,
336        |caller, params, results| {
337            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#splice")?;
338            let items = super::read_array(caller, abi_arg(params, 3)?, "Array#splice")?;
339            *abi_result(results, 0)? = super::splice(
340                caller,
341                abi_arg(params, 0)?,
342                elements,
343                f64v(abi_arg(params, 1)?),
344                f64v(abi_arg(params, 2)?),
345                items,
346            )?;
347            Ok(())
348        },
349    )?;
350    register_host_fn_async(
351        linker,
352        MODULE_NAME,
353        method_key("sort"),
354        ft(vec![array.clone(), elem.clone()], vec![array.clone()]),
355        true,
356        |caller, params, results| {
357            Box::pin(async move {
358                let elements = super::read_kept_array(caller, abi_arg(params, 0)?, "Array#sort")?;
359                let cmp = if crate::runtime::prelude::undefined::is_undefined(
360                    caller,
361                    abi_arg(params, 1)?,
362                )? {
363                    None
364                } else {
365                    Some(closure::read_callback(
366                        caller,
367                        abi_arg(params, 1)?,
368                        "Array#sort comparator",
369                    )?)
370                };
371                *abi_result(results, 0)? =
372                    super::sort(caller, abi_arg(params, 0)?, elements, cmp).await?;
373                Ok(())
374            })
375        },
376    )?;
377
378    // --- Higher-order ----------------------------------------------------
379    register_host_fn_async(
380        linker,
381        MODULE_NAME,
382        method_key("forEach"),
383        ft(vec![array.clone(), callback.clone()], vec![]),
384        true,
385        |caller, params, _results| {
386            Box::pin(async move {
387                let elements =
388                    super::read_kept_array(caller, abi_arg(params, 0)?, "Array#forEach receiver")?;
389                let f =
390                    closure::read_callback(caller, abi_arg(params, 1)?, "Array#forEach callback")?;
391                super::for_each(caller, *abi_arg(params, 0)?, elements, &f).await
392            })
393        },
394    )?;
395    register_host_fn_async(
396        linker,
397        MODULE_NAME,
398        method_key("map"),
399        ft(vec![array.clone(), callback.clone()], vec![array.clone()]),
400        true,
401        |caller, params, results| {
402            Box::pin(async move {
403                let elements = super::read_kept_array(caller, abi_arg(params, 0)?, "Array#map")?;
404                let f = closure::read_callback(caller, abi_arg(params, 1)?, "Array#map callback")?;
405                let out = super::map(caller, *abi_arg(params, 0)?, elements, &f).await?;
406                *abi_result(results, 0)? = super::build_array(caller, &out)?;
407                Ok(())
408            })
409        },
410    )?;
411    register_host_fn_async(
412        linker,
413        MODULE_NAME,
414        method_key("filter"),
415        ft(vec![array.clone(), callback.clone()], vec![array.clone()]),
416        true,
417        |caller, params, results| {
418            Box::pin(async move {
419                let elements = super::read_kept_array(caller, abi_arg(params, 0)?, "Array#filter")?;
420                let f =
421                    closure::read_callback(caller, abi_arg(params, 1)?, "Array#filter predicate")?;
422                let out = super::filter(caller, *abi_arg(params, 0)?, elements, &f).await?;
423                *abi_result(results, 0)? = super::build_array(caller, &out)?;
424                Ok(())
425            })
426        },
427    )?;
428    register_host_fn_async(
429        linker,
430        MODULE_NAME,
431        method_key("reduce"),
432        ft(
433            vec![array.clone(), callback.clone(), elem.clone()],
434            vec![elem.clone()],
435        ),
436        true,
437        |caller, params, results| {
438            Box::pin(async move {
439                let elements = super::read_kept_array(caller, abi_arg(params, 0)?, "Array#reduce")?;
440                let f =
441                    closure::read_callback(caller, abi_arg(params, 1)?, "Array#reduce callback")?;
442                *abi_result(results, 0)? = super::reduce(
443                    caller,
444                    *abi_arg(params, 0)?,
445                    elements,
446                    &f,
447                    *abi_arg(params, 2)?,
448                    false,
449                )
450                .await?;
451                Ok(())
452            })
453        },
454    )?;
455    register_host_fn_async(
456        linker,
457        MODULE_NAME,
458        method_key("reduceRight"),
459        ft(
460            vec![array.clone(), callback.clone(), elem.clone()],
461            vec![elem.clone()],
462        ),
463        true,
464        |caller, params, results| {
465            Box::pin(async move {
466                let elements =
467                    super::read_kept_array(caller, abi_arg(params, 0)?, "Array#reduceRight")?;
468                let f = closure::read_callback(
469                    caller,
470                    abi_arg(params, 1)?,
471                    "Array#reduceRight callback",
472                )?;
473                *abi_result(results, 0)? = super::reduce(
474                    caller,
475                    *abi_arg(params, 0)?,
476                    elements,
477                    &f,
478                    *abi_arg(params, 2)?,
479                    true,
480                )
481                .await?;
482                Ok(())
483            })
484        },
485    )?;
486    for (name, reverse) in [("find", false), ("findLast", true)] {
487        register_host_fn_async(
488            linker,
489            MODULE_NAME,
490            method_key(name),
491            ft(vec![array.clone(), callback.clone()], vec![elem.clone()]),
492            true,
493            move |caller, params, results| {
494                Box::pin(async move {
495                    let elements =
496                        super::read_kept_array(caller, abi_arg(params, 0)?, "Array#find")?;
497                    let pred = closure::read_callback(
498                        caller,
499                        abi_arg(params, 1)?,
500                        "Array#find predicate",
501                    )?;
502                    *abi_result(results, 0)? = match super::find_match(
503                        caller,
504                        *abi_arg(params, 0)?,
505                        &elements,
506                        &pred,
507                        reverse,
508                    )
509                    .await?
510                    {
511                        Some(i) => *elements.get(i).ok_or_else(|| {
512                            crate::runtime::host::fatal_host_error(
513                                "Array#find: invalid result index",
514                            )
515                        })?,
516                        None => crate::runtime::prelude::undefined::value(caller)?,
517                    };
518                    Ok(())
519                })
520            },
521        )?;
522    }
523    for (name, reverse) in [("findIndex", false), ("findLastIndex", true)] {
524        register_host_fn_async(
525            linker,
526            MODULE_NAME,
527            method_key(name),
528            ft(vec![array.clone(), callback.clone()], vec![num.clone()]),
529            true,
530            move |caller, params, results| {
531                Box::pin(async move {
532                    let elements =
533                        super::read_kept_array(caller, abi_arg(params, 0)?, "Array#findIndex")?;
534                    let pred = closure::read_callback(
535                        caller,
536                        abi_arg(params, 1)?,
537                        "Array#findIndex predicate",
538                    )?;
539                    let idx =
540                        super::find_match(caller, *abi_arg(params, 0)?, &elements, &pred, reverse)
541                            .await?;
542                    let r = idx.map_or(-1.0, |i| i as f64);
543                    *abi_result(results, 0)? = Val::F64(r.to_bits());
544                    Ok(())
545                })
546            },
547        )?;
548    }
549    register_host_fn_async(
550        linker,
551        MODULE_NAME,
552        method_key("some"),
553        ft(vec![array.clone(), callback.clone()], vec![boolean.clone()]),
554        true,
555        |caller, params, results| {
556            Box::pin(async move {
557                let elements = super::read_kept_array(caller, abi_arg(params, 0)?, "Array#some")?;
558                let pred =
559                    closure::read_callback(caller, abi_arg(params, 1)?, "Array#some predicate")?;
560                let r = super::some(caller, *abi_arg(params, 0)?, elements, &pred).await?;
561                *abi_result(results, 0)? = Val::I32(i32::from(r));
562                Ok(())
563            })
564        },
565    )?;
566    register_host_fn_async(
567        linker,
568        MODULE_NAME,
569        method_key("every"),
570        ft(vec![array.clone(), callback.clone()], vec![boolean.clone()]),
571        true,
572        |caller, params, results| {
573            Box::pin(async move {
574                let elements = super::read_kept_array(caller, abi_arg(params, 0)?, "Array#every")?;
575                let pred =
576                    closure::read_callback(caller, abi_arg(params, 1)?, "Array#every predicate")?;
577                let r = super::every(caller, *abi_arg(params, 0)?, elements, &pred).await?;
578                *abi_result(results, 0)? = Val::I32(i32::from(r));
579                Ok(())
580            })
581        },
582    )?;
583    register_host_fn(
584        linker,
585        MODULE_NAME,
586        method_key("flat"),
587        ft(vec![array.clone(), num.clone()], vec![array.clone()]),
588        true,
589        |caller, params, results| {
590            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#flat")?;
591            let depth = f64v(abi_arg(params, 1)?) as i32;
592            let mut out = Vec::new();
593            super::flat_into(caller, elements, depth, &mut out)?;
594            *abi_result(results, 0)? = super::build_array(caller, &out)?;
595            Ok(())
596        },
597    )?;
598    register_host_fn_async(
599        linker,
600        MODULE_NAME,
601        method_key("flatMap"),
602        ft(vec![array.clone(), callback.clone()], vec![array.clone()]),
603        true,
604        |caller, params, results| {
605            Box::pin(async move {
606                let elements =
607                    super::read_kept_array(caller, abi_arg(params, 0)?, "Array#flatMap")?;
608                let f =
609                    closure::read_callback(caller, abi_arg(params, 1)?, "Array#flatMap callback")?;
610                let out = super::flat_map(caller, *abi_arg(params, 0)?, elements, &f).await?;
611                *abi_result(results, 0)? = super::build_array(caller, &out)?;
612                Ok(())
613            })
614        },
615    )?;
616
617    // --- Immutable variants ----------------------------------------------
618    register_host_fn(
619        linker,
620        MODULE_NAME,
621        method_key("toReversed"),
622        ft(vec![array.clone()], vec![array.clone()]),
623        true,
624        |caller, params, results| {
625            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#toReversed")?;
626            let out = super::to_reversed(elements);
627            *abi_result(results, 0)? = super::build_array(caller, &out)?;
628            Ok(())
629        },
630    )?;
631    register_host_fn_async(
632        linker,
633        MODULE_NAME,
634        method_key("toSorted"),
635        ft(vec![array.clone(), elem.clone()], vec![array.clone()]),
636        true,
637        |caller, params, results| {
638            Box::pin(async move {
639                let elements =
640                    super::read_kept_array(caller, abi_arg(params, 0)?, "Array#toSorted")?;
641                let cmp = if crate::runtime::prelude::undefined::is_undefined(
642                    caller,
643                    abi_arg(params, 1)?,
644                )? {
645                    None
646                } else {
647                    Some(closure::read_callback(
648                        caller,
649                        abi_arg(params, 1)?,
650                        "Array#toSorted comparator",
651                    )?)
652                };
653                let out = super::to_sorted(caller, elements, cmp).await?;
654                *abi_result(results, 0)? = super::build_array(caller, &out)?;
655                Ok(())
656            })
657        },
658    )?;
659    register_host_fn(
660        linker,
661        MODULE_NAME,
662        method_key("toSpliced"),
663        ft(
664            vec![array.clone(), num.clone(), num.clone(), array.clone()],
665            vec![array.clone()],
666        ),
667        true,
668        |caller, params, results| {
669            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#toSpliced")?;
670            let items = super::read_array(caller, abi_arg(params, 3)?, "Array#toSpliced")?;
671            let (_, result) = super::splice_parts(
672                &elements,
673                f64v(abi_arg(params, 1)?),
674                f64v(abi_arg(params, 2)?),
675                items,
676            );
677            *abi_result(results, 0)? = super::build_array(caller, &result)?;
678            Ok(())
679        },
680    )?;
681    register_host_fn(
682        linker,
683        MODULE_NAME,
684        method_key("with"),
685        ft(
686            vec![array.clone(), num.clone(), elem.clone()],
687            vec![array.clone()],
688        ),
689        true,
690        |caller, params, results| {
691            let elements = super::read_array(caller, abi_arg(params, 0)?, "Array#with")?;
692            match super::with(&elements, f64v(abi_arg(params, 1)?), *abi_arg(params, 2)?) {
693                Some(out) => {
694                    *abi_result(results, 0)? = super::build_array(caller, &out)?;
695                    Ok(())
696                }
697                None => Err(crate::runtime::host::range_error("index out of range")),
698            }
699        },
700    )?;
701
702    // --- Iterators -------------------------------------------------------
703    let iterator_ret = object_ref(&intr);
704    register_host_fn(
705        linker,
706        MODULE_NAME,
707        method_key("keys"),
708        ft(vec![array.clone()], vec![iterator_ret.clone()]),
709        true,
710        |caller, params, results| {
711            *abi_result(results, 0)? = super::keys(caller, abi_arg(params, 0)?)?;
712            Ok(())
713        },
714    )?;
715    register_host_fn(
716        linker,
717        MODULE_NAME,
718        method_key("values"),
719        ft(vec![array.clone()], vec![iterator_ret.clone()]),
720        true,
721        |caller, params, results| {
722            *abi_result(results, 0)? = super::values(caller, abi_arg(params, 0)?)?;
723            Ok(())
724        },
725    )?;
726    register_host_fn(
727        linker,
728        MODULE_NAME,
729        method_key("entries"),
730        ft(vec![array.clone()], vec![iterator_ret.clone()]),
731        true,
732        |caller, params, results| {
733            *abi_result(results, 0)? = super::entries(caller, abi_arg(params, 0)?)?;
734            Ok(())
735        },
736    )?;
737
738    // Serialization members — thin re-entries into the host-owned array
739    // vtable's `toString`/`toJson` slots.
740    for (name, slot) in [("toString", 0usize), ("toJson", 1usize)] {
741        register_host_fn_async(
742            linker,
743            MODULE_NAME,
744            method_key(name),
745            ft(vec![array.clone()], vec![string.clone()]),
746            true,
747            move |caller, params, results| {
748                Box::pin(async move {
749                    *abi_result(results, 0)? =
750                        crate::runtime::prelude::vtable::dispatch_vtable_slot(
751                            caller,
752                            abi_arg(params, 0)?,
753                            slot,
754                            &[],
755                        )
756                        .await?;
757                    Ok(())
758                })
759            },
760        )?;
761    }
762
763    // --- ArrayConstructor statics ----------------------------------------
764    // `Array.isArray(value)`: Dispatch::Static — a bare `$Array` ref-test.
765    register_host_fn(
766        linker,
767        MODULE_NAME,
768        ctor_key("isArray"),
769        ft(vec![elem.clone()], vec![boolean]),
770        true,
771        |caller, params, results| {
772            *abi_result(results, 0)? =
773                wasmtime::Val::I32(super::is_array(caller, abi_arg(params, 0)?)? as i32);
774            Ok(())
775        },
776    )?;
777    // `Array.of(...items)`: Dispatch::Static, so the receiver is dropped and the
778    // variadic args arrive packed into one `$Array`.
779    register_host_fn(
780        linker,
781        MODULE_NAME,
782        ctor_key("of"),
783        ft(vec![array.clone()], vec![array.clone()]),
784        true,
785        |caller, params, results| {
786            let items = super::read_array(caller, abi_arg(params, 0)?, "Array.of")?;
787            *abi_result(results, 0)? = super::build_array(caller, &items)?;
788            Ok(())
789        },
790    )?;
791    // `Array.from(src, mapFn?)`: consumes any iterable, re-entering the guest
792    // for the iterator protocol and the per-element `mapFn`.
793    register_host_fn_async(
794        linker,
795        MODULE_NAME,
796        ctor_key("from"),
797        ft(
798            vec![object_ref(&intr), object_ref(&intr)],
799            vec![array.clone()],
800        ),
801        true,
802        |caller, params, results| {
803            Box::pin(async move {
804                *abi_result(results, 0)? =
805                    super::from(caller, abi_arg(params, 0)?, abi_arg(params, 1)?).await?;
806                Ok(())
807            })
808        },
809    )?;
810    Ok(())
811}
812
813/// `{ readonly length: number }`: an array-like source with no elements to
814/// read, each of which is `undefined`.
815pub(crate) fn array_like_type() -> Type {
816    Type::Object {
817        fields: std::collections::BTreeMap::from([(
818            "length".to_string(),
819            crate::ObjectField {
820                ty: Type::Number,
821                optional: false,
822                readonly: true,
823                method: false,
824            },
825        )]),
826        index: None,
827    }
828}
829
830pub fn declare(defs: &mut PackageDeclaration) {
831    let t = || Type::TypeVar("T".to_string());
832    let u = || Type::TypeVar("U".to_string());
833    let arr_ty = || Type::Array(Box::new(t()));
834    let arr = || Param::new("arr", arr_ty());
835    let elem = || Param::new("v", t());
836    let n = |name: &str| Param::new(name, Type::Number);
837    let t_or_undefined = || Type::Union(vec![t(), Type::Undefined]);
838    let iter = |item: Type| Type::prelude_interface("Iterator".to_string(), vec![item]);
839    let func = |params: Vec<Type>, ret: Type| Type::Function {
840        optional: 0,
841        params,
842        ret: Box::new(ret),
843        predicate: None,
844        has_rest: false,
845    };
846    // An element callback's slot is erased (see `install`); its type for
847    // checking calls is in `declare_types`.
848    let callback = || Param::new("f", Type::Unknown);
849    let comparator_or_undefined =
850        || Type::Union(vec![func(vec![t(), t()], Type::Number), Type::Undefined]);
851
852    let m = |defs: &mut PackageDeclaration, name: &str, params: Vec<Param>, ret: Type| {
853        declare_method(defs, name, method_key(name), params, ret);
854    };
855
856    m(defs, "at", vec![arr(), n("index")], t_or_undefined());
857    m(defs, "slice", vec![arr(), n("start"), n("end")], arr_ty());
858    m(
859        defs,
860        "concat",
861        vec![
862            arr(),
863            Param::new(
864                "others",
865                Type::Array(Box::new(Type::Readonly(Box::new(arr_ty())))),
866            ),
867        ],
868        arr_ty(),
869    );
870    m(
871        defs,
872        "join",
873        vec![arr(), Param::new("sep", Type::String)],
874        Type::String,
875    );
876    m(
877        defs,
878        "indexOf",
879        vec![arr(), elem(), n("fromIndex")],
880        Type::Number,
881    );
882    m(
883        defs,
884        "lastIndexOf",
885        vec![arr(), elem(), n("fromIndex")],
886        Type::Number,
887    );
888    m(
889        defs,
890        "includes",
891        vec![arr(), elem(), n("fromIndex")],
892        Type::Boolean,
893    );
894
895    m(defs, "push", vec![arr(), elem()], Type::Number);
896    m(defs, "pop", vec![arr()], t_or_undefined());
897    m(defs, "shift", vec![arr()], t_or_undefined());
898    m(
899        defs,
900        "unshift",
901        vec![arr(), Param::new("items", arr_ty())],
902        Type::Number,
903    );
904    m(defs, "reverse", vec![arr()], arr_ty());
905    m(
906        defs,
907        "fill",
908        vec![arr(), elem(), n("start"), n("end")],
909        arr_ty(),
910    );
911    m(
912        defs,
913        "copyWithin",
914        vec![arr(), n("target"), n("start"), n("end")],
915        arr_ty(),
916    );
917    m(
918        defs,
919        "splice",
920        vec![
921            arr(),
922            n("start"),
923            n("deleteCount"),
924            Param::new("items", arr_ty()),
925        ],
926        arr_ty(),
927    );
928    m(
929        defs,
930        "sort",
931        vec![arr(), Param::new("compareFn", comparator_or_undefined())],
932        arr_ty(),
933    );
934
935    m(defs, "forEach", vec![arr(), callback()], Type::Void);
936    m(
937        defs,
938        "map",
939        vec![arr(), callback()],
940        Type::Array(Box::new(u())),
941    );
942    m(defs, "filter", vec![arr(), callback()], arr_ty());
943    m(
944        defs,
945        "reduce",
946        vec![arr(), callback(), Param::new("initial", u())],
947        u(),
948    );
949    m(
950        defs,
951        "reduceRight",
952        vec![arr(), callback(), Param::new("initial", u())],
953        u(),
954    );
955    for name in ["find", "findLast"] {
956        m(defs, name, vec![arr(), callback()], t_or_undefined());
957    }
958    for name in ["findIndex", "findLastIndex"] {
959        m(defs, name, vec![arr(), callback()], Type::Number);
960    }
961    m(defs, "some", vec![arr(), callback()], Type::Boolean);
962    m(defs, "every", vec![arr(), callback()], Type::Boolean);
963    m(defs, "flat", vec![arr(), n("depth")], arr_ty());
964    m(
965        defs,
966        "flatMap",
967        vec![arr(), callback()],
968        Type::Array(Box::new(u())),
969    );
970
971    m(defs, "toReversed", vec![arr()], arr_ty());
972    m(
973        defs,
974        "toSorted",
975        vec![arr(), Param::new("compareFn", comparator_or_undefined())],
976        arr_ty(),
977    );
978    m(
979        defs,
980        "toSpliced",
981        vec![
982            arr(),
983            n("start"),
984            n("deleteCount"),
985            Param::new("items", arr_ty()),
986        ],
987        arr_ty(),
988    );
989    m(defs, "with", vec![arr(), n("index"), elem()], arr_ty());
990
991    m(defs, "keys", vec![arr()], iter(Type::Number));
992    m(defs, "values", vec![arr()], iter(t()));
993    m(
994        defs,
995        "entries",
996        vec![arr()],
997        iter(Type::Tuple(vec![Type::Number, t()].into())),
998    );
999    m(defs, "toString", vec![arr()], Type::String);
1000    m(defs, "toJson", vec![arr()], Type::String);
1001    declare_method(
1002        defs,
1003        "isArray",
1004        ctor_key("isArray"),
1005        vec![Param::new("value", t())],
1006        Type::Boolean,
1007    );
1008
1009    declare_method(
1010        defs,
1011        "of",
1012        ctor_key("of"),
1013        vec![Param::new("items", arr_ty())],
1014        arr_ty(),
1015    );
1016    declare_method(
1017        defs,
1018        "from",
1019        ctor_key("from"),
1020        vec![
1021            Param::new(
1022                "src",
1023                Type::union(vec![
1024                    Type::Readonly(Box::new(arr_ty())),
1025                    iter(t()),
1026                    Type::prelude_interface("Iterable".to_string(), vec![t()]),
1027                    array_like_type(),
1028                ]),
1029            ),
1030            Param::new(
1031                "mapFn",
1032                Type::Union(vec![func(vec![t(), Type::Number], u()), Type::Undefined]),
1033            ),
1034        ],
1035        arr_ty(),
1036    );
1037}
1038
1039/// The type/interface surface this module implements — its slice of the
1040/// prelude declaration (see `declaration::prelude_package_declaration`).
1041#[allow(clippy::too_many_lines)]
1042pub(crate) fn declare_types(defs: &mut crate::PackageDeclaration) {
1043    use crate::runtime::prelude::declaration::doc;
1044    use crate::{
1045        Dispatch, MethodSig, Param, PropertySig, Span, Type, TypeKind, TypeSymbol, ValueKind,
1046        ValueSymbol,
1047    };
1048    use std::collections::BTreeMap;
1049    defs.types.insert(
1050        "Array".to_string(),
1051        TypeSymbol {
1052            name: "Array".to_string(),
1053            mangled_name: crate::mangle::prelude("Array"),
1054            declaration_span: Span::at(crate::FileId::PRELUDE),
1055            kind: TypeKind::Interface { index: None,
1056                generics: vec!["T".to_string()],
1057                methods: BTreeMap::from([
1058                    (
1059                        "toString".to_string(),
1060                        MethodSig {
1061                            optional: false,
1062                            generics: Vec::new(),
1063                            params: Vec::new(),
1064                            ret: Type::String,
1065                            predicate: None,
1066                            doc: doc("/** Returns the elements joined with commas. */"),
1067                        },
1068                    ),
1069                    (
1070                        "toJson".to_string(),
1071                        MethodSig {
1072                            optional: false,
1073                            generics: Vec::new(),
1074                            params: Vec::new(),
1075                            ret: Type::String,
1076                            predicate: None,
1077                            doc: doc(
1078                                "/** Returns the JSON representation of this array — `\"[\"` + elements' `toJson()` joined with `\",\"` + `\"]\"`. */",
1079                            ),
1080                        },
1081                    ),
1082                    (
1083                        "join".to_string(),
1084                        MethodSig {
1085                            optional: false,
1086                            generics: Vec::new(),
1087                            params: vec![Param::with_default(
1088                                "separator",
1089                                Type::String,
1090                                crate::DefaultValue::String(",".to_string()),
1091                            )],
1092                            ret: Type::String,
1093                            predicate: None,
1094                            doc: doc(
1095                                "/**\n * Joins the elements using `separator`.\n * @param separator The string inserted between elements. Defaults to `\",\"`.\n */",
1096                            ),
1097                        },
1098                    ),
1099                    (
1100                        "keys".to_string(),
1101                        MethodSig {
1102                            optional: false,
1103                            generics: Vec::new(),
1104                            params: Vec::new(),
1105                            ret: Type::prelude_interface("Iterator".to_string(), vec![Type::Number]),
1106                            predicate: None,
1107                            doc: doc(
1108                                "/** Returns a live `Iterator<number>` of the indices — length is re-read on every step. */",
1109                            ),
1110                        },
1111                    ),
1112                    (
1113                        "values".to_string(),
1114                        MethodSig {
1115                            optional: false,
1116                            generics: Vec::new(),
1117                            params: Vec::new(),
1118                            ret: Type::prelude_interface("Iterator".to_string(), vec![Type::TypeVar("T".to_string())]),
1119                            predicate: None,
1120                            doc: doc(
1121                                "/** Returns a live `Iterator<T>` over the elements — unlike Map/Set cursors it sees pushes made during iteration. */",
1122                            ),
1123                        },
1124                    ),
1125                    (
1126                        "entries".to_string(),
1127                        MethodSig {
1128                            optional: false,
1129                            generics: Vec::new(),
1130                            params: Vec::new(),
1131                            ret: Type::prelude_interface("Iterator".to_string(), vec![Type::Tuple(vec![
1132                                Type::Number,
1133                                Type::TypeVar("T".to_string()),
1134                            ].into())]),
1135                            predicate: None,
1136                            doc: doc(
1137                                "/** Returns a live `Iterator<[number, T]>` of index/element pairs. */",
1138                            ),
1139                        },
1140                    ),
1141
1142                    (
1143                        "map".to_string(),
1144                        MethodSig {
1145                            optional: false,
1146                            generics: vec!["U".to_string()],
1147                            params: vec![Param::new(
1148                                "callback",
1149                                Type::Function {
1150                                    optional: 0,
1151                                    params: element_callback_params(Vec::new()),
1152                                    ret: Box::new(Type::TypeVar("U".to_string())),
1153                                    predicate: None,
1154                                    has_rest: false,
1155                                },
1156                            )],
1157                            ret: Type::Array(Box::new(Type::TypeVar("U".to_string()))),
1158                            predicate: None,
1159                            doc: doc(
1160                                "/**\n * Returns a new array produced by applying `callback` to each element.\n * @param callback Function called with `(value, index, array)` for each element.\n * @returns A new array of length equal to this array.\n */",
1161                            ),
1162                        },
1163                    ),
1164                    (
1165                        "push".to_string(),
1166                        MethodSig {
1167                            optional: false,
1168                            generics: Vec::new(),
1169                            params: vec![Param::new("elem", Type::TypeVar("T".to_string()))],
1170                            ret: Type::Number,
1171                            predicate: None,
1172                            doc: doc(
1173                                "/**\n * Appends `elem` to the end of the array.\n * @param elem The value to add.\n * @returns The new length of the array.\n */",
1174                            ),
1175                        },
1176                    ),
1177                    // pop / at / find — each returns `T | undefined`
1178                    // for the empty / out-of-range / no-match case.
1179                    (
1180                        "pop".to_string(),
1181                        MethodSig {
1182                            optional: false,
1183                            generics: Vec::new(),
1184                            params: Vec::new(),
1185                            ret: Type::Union(vec![
1186                                Type::TypeVar("T".to_string()),
1187                                Type::Undefined,
1188                            ]),
1189                            predicate: None,
1190                            doc: doc(
1191                                "/**\n * Removes the last element and returns it, or `undefined` if the array is empty.\n * Shortens the array by one when non-empty.\n */",
1192                            ),
1193                        },
1194                    ),
1195                    (
1196                        "at".to_string(),
1197                        MethodSig {
1198                            optional: false,
1199                            generics: Vec::new(),
1200                            params: vec![Param::new("index", Type::Number)],
1201                            ret: Type::Union(vec![
1202                                Type::TypeVar("T".to_string()),
1203                                Type::Undefined,
1204                            ]),
1205                            predicate: None,
1206                            doc: doc(
1207                                "/**\n * Returns the element at `index`, or `undefined` if out of range.\n * Negative indices count from the end (`-1` is the last element).\n * @param index Zero-based index, JS negative-index semantics apply.\n */",
1208                            ),
1209                        },
1210                    ),
1211                    (
1212                        "find".to_string(),
1213                        MethodSig {
1214                            optional: false,
1215                            generics: Vec::new(),
1216                            params: vec![Param::new(
1217                                "callback",
1218                                Type::Function {
1219                                    optional: 0,
1220                                    params: element_callback_params(Vec::new()),
1221                                    ret: Box::new(Type::Unknown),
1222                                    predicate: None,
1223                                    has_rest: false,
1224                                },
1225                            )],
1226                            ret: Type::Union(vec![
1227                                Type::TypeVar("T".to_string()),
1228                                Type::Undefined,
1229                            ]),
1230                            predicate: None,
1231                            doc: doc(
1232                                "/**\n * Returns the first element for which `callback` returns a truthy value, or `undefined` if none match.\n * Short-circuits on the first match.\n * @param callback Function called with `(value, index, array)` for each element until it returns a truthy value.\n */",
1233                            ),
1234                        },
1235                    ),
1236                    (
1237                        "findLast".to_string(),
1238                        MethodSig {
1239                            optional: false,
1240                            generics: Vec::new(),
1241                            params: vec![Param::new(
1242                                "callback",
1243                                Type::Function {
1244                                    optional: 0,
1245                                    params: element_callback_params(Vec::new()),
1246                                    ret: Box::new(Type::Unknown),
1247                                    predicate: None,
1248                                    has_rest: false,
1249                                },
1250                            )],
1251                            ret: Type::Union(vec![
1252                                Type::TypeVar("T".to_string()),
1253                                Type::Undefined,
1254                            ]),
1255                            predicate: None,
1256                            doc: doc(
1257                                "/**\n * Returns the last element for which `callback` returns a truthy value, or `undefined` if none match.\n * Scans from the end; short-circuits on the first match.\n * @param callback Function called with `(value, index, array)` for each element until it returns a truthy value.\n */",
1258                            ),
1259                        },
1260                    ),
1261                    (
1262                        "findIndex".to_string(),
1263                        MethodSig {
1264                            optional: false,
1265                            generics: Vec::new(),
1266                            params: vec![Param::new(
1267                                "callback",
1268                                Type::Function {
1269                                    optional: 0,
1270                                    params: element_callback_params(Vec::new()),
1271                                    ret: Box::new(Type::Unknown),
1272                                    predicate: None,
1273                                    has_rest: false,
1274                                },
1275                            )],
1276                            ret: Type::Number,
1277                            predicate: None,
1278                            doc: doc(
1279                                "/**\n * Returns the index of the first element for which `callback` returns a truthy value, or `-1` if none match.\n * @param callback Function called with `(value, index, array)` for each element until it returns a truthy value.\n */",
1280                            ),
1281                        },
1282                    ),
1283                    (
1284                        "findLastIndex".to_string(),
1285                        MethodSig {
1286                            optional: false,
1287                            generics: Vec::new(),
1288                            params: vec![Param::new(
1289                                "callback",
1290                                Type::Function {
1291                                    optional: 0,
1292                                    params: element_callback_params(Vec::new()),
1293                                    ret: Box::new(Type::Unknown),
1294                                    predicate: None,
1295                                    has_rest: false,
1296                                },
1297                            )],
1298                            ret: Type::Number,
1299                            predicate: None,
1300                            doc: doc(
1301                                "/**\n * Returns the index of the last element for which `callback` returns a truthy value, or `-1` if none match.\n * Scans from the end.\n * @param callback Function called with `(value, index, array)` for each element until it returns a truthy value.\n */",
1302                            ),
1303                        },
1304                    ),
1305                    // `flat`'s return type un-nests `depth` array levels; the typechecker
1306                    // special-cases it (see infer::lookup) and overrides this placeholder.
1307                    (
1308                        "flat".to_string(),
1309                        MethodSig {
1310                            optional: false,
1311                            generics: Vec::new(),
1312                            params: vec![Param::with_default(
1313                                "depth",
1314                                Type::Number,
1315                                crate::DefaultValue::Number(1.0),
1316                            )],
1317                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1318                            predicate: None,
1319                            doc: doc(
1320                                "/**\n * Flattens nested arrays up to `depth` levels into a new array.\n * `depth` must be an integer literal (default `1`); for deeper flattening pass a larger literal or chain `.flat()`.\n */",
1321                            ),
1322                        },
1323                    ),
1324                    (
1325                        "flatMap".to_string(),
1326                        MethodSig {
1327                            optional: false,
1328                            generics: vec!["U".to_string()],
1329                            params: vec![Param::new(
1330                                "callback",
1331                                Type::Function {
1332                                    optional: 0,
1333                                    params: element_callback_params(Vec::new()),
1334                                    ret: Box::new(Type::Array(Box::new(Type::TypeVar(
1335                                        "U".to_string(),
1336                                    )))),
1337                                    predicate: None,
1338                                    has_rest: false,
1339                                },
1340                            )],
1341                            ret: Type::Array(Box::new(Type::TypeVar("U".to_string()))),
1342                            predicate: None,
1343                            doc: doc(
1344                                "/**\n * Maps each element to an array via `callback`, then flattens the results one level.\n * @param callback Function called with `(value, index, array)`, returning an array for each element.\n */",
1345                            ),
1346                        },
1347                    ),
1348                    (
1349                        "slice".to_string(),
1350                        MethodSig {
1351                            optional: false,
1352                            generics: Vec::new(),
1353                            params: vec![
1354                                Param::with_default(
1355                                    "start",
1356                                    Type::Number,
1357                                    crate::DefaultValue::Number(0.0),
1358                                ),
1359                                // `end` omitted means "to the end".
1360                                // `slice_body` truncates via `i32.trunc_sat_f64_s`
1361                                // then clamps to `[0, len]`, so f64::INFINITY
1362                                // saturates to i32::MAX and clamps to len —
1363                                // matching JS `slice(s)` / `slice(s, undefined)`
1364                                // without changing the body or boxing the param.
1365                                Param::with_default(
1366                                    "end",
1367                                    Type::Number,
1368                                    crate::DefaultValue::Number(f64::INFINITY),
1369                                ),
1370                            ],
1371                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1372                            predicate: None,
1373                            doc: doc(
1374                                "/**\n * Returns a new array of the elements from `start` (inclusive) to `end` (exclusive).\n * The receiver is not modified.\n * @param start First index to copy. Defaults to `0`.\n * @param end Index one past the last to copy. Defaults to the array length.\n */",
1375                            ),
1376                        },
1377                    ),
1378                    (
1379                        "indexOf".to_string(),
1380                        MethodSig {
1381                            optional: false,
1382                            generics: Vec::new(),
1383                            params: vec![
1384                                Param::new("elem", Type::TypeVar("T".to_string())),
1385                                Param::with_default(
1386                                    "fromIndex",
1387                                    Type::Number,
1388                                    crate::DefaultValue::Number(0.0),
1389                                ),
1390                            ],
1391                            ret: Type::Number,
1392                            predicate: None,
1393                            doc: doc(
1394                                "/**\n * Returns the index of the first element equal to `elem` at or after `fromIndex`, or `-1` if not present.\n * Equality dispatches through each element's `equals`.\n * @param elem The value to search for.\n * @param fromIndex Index to begin searching from. Negative counts from the end. Defaults to `0`.\n */",
1395                            ),
1396                        },
1397                    ),
1398                    (
1399                        "includes".to_string(),
1400                        MethodSig {
1401                            optional: false,
1402                            generics: Vec::new(),
1403                            params: vec![
1404                                Param::new("elem", Type::TypeVar("T".to_string())),
1405                                Param::with_default(
1406                                    "fromIndex",
1407                                    Type::Number,
1408                                    crate::DefaultValue::Number(0.0),
1409                                ),
1410                            ],
1411                            ret: Type::Boolean,
1412                            predicate: None,
1413                            doc: doc(
1414                                "/**\n * Returns `true` if some element at or after `fromIndex` equals `elem`.\n * Equality dispatches through each element's `equals`.\n * @param elem The value to search for.\n * @param fromIndex Index to begin searching from. Negative counts from the end. Defaults to `0`.\n */",
1415                            ),
1416                        },
1417                    ),
1418                    (
1419                        "concat".to_string(),
1420                        MethodSig {
1421                            optional: false,
1422                            generics: Vec::new(),
1423                            // → variadic concat —
1424                            // `concat(...others: T[][]): T[]`. The
1425                            // wrapper sees a single `$Array` carrying
1426                            // the trailing arrays; the body sums each
1427                            // input's length and one `array.copy`s
1428                            // them all into a fresh combined raw
1429                            // array.
1430                            params: vec![Param::rest(
1431                                "others",
1432                                Type::Array(Box::new(Type::Readonly(Box::new(Type::Array(
1433                                    Box::new(Type::TypeVar("T".to_string())),
1434                                ))))),
1435                            )],
1436                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1437                            predicate: None,
1438                            doc: doc(
1439                                "/**\n * Returns a new array containing this array's elements followed by every element of each `others` array, in order.\n * Neither this array nor any input is modified.\n * @param others Zero or more arrays whose elements are appended.\n */",
1440                            ),
1441                        },
1442                    ),
1443                    (
1444                        "reverse".to_string(),
1445                        MethodSig {
1446                            optional: false,
1447                            generics: Vec::new(),
1448                            params: Vec::new(),
1449                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1450                            predicate: None,
1451                            doc: doc(
1452                                "/**\n * Reverses the array in place and returns it.\n */",
1453                            ),
1454                        },
1455                    ),
1456                    // higher-order methods. Each takes a
1457                    // closure arg; under uniform erasure the closure's
1458                    // Wasm struct type is `(arity, is_void)`-keyed, so
1459                    // the prelude exports a single `Array#<method>`
1460                    // wrapper per HOF rather than emitting per
1461                    // call-site. `find` is (needs
1462                    // `T | undefined` unions).
1463                    (
1464                        "filter".to_string(),
1465                        MethodSig {
1466                            optional: false,
1467                            generics: Vec::new(),
1468                            params: vec![Param::new(
1469                                "predicate",
1470                                Type::Function {
1471                                    optional: 0,
1472                                    params: element_callback_params(Vec::new()),
1473                                    ret: Box::new(Type::Unknown),
1474                                    predicate: None,
1475                                    has_rest: false,
1476                                },
1477                            )],
1478                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1479                            predicate: None,
1480                            doc: doc(
1481                                "/**\n * Returns a new array of the elements for which `predicate` returned a truthy value.\n * Order is preserved; the receiver is not modified.\n * @param predicate Function called with `(value, index, array)` for each element.\n */",
1482                            ),
1483                        },
1484                    ),
1485                    (
1486                        "reduce".to_string(),
1487                        MethodSig {
1488                            optional: false,
1489                            generics: vec!["U".to_string()],
1490                            params: vec![
1491                                Param::new(
1492                                    "callback",
1493                                    Type::Function {
1494                                        optional: 0,
1495                                        params: element_callback_params(vec![Type::TypeVar("U".to_string())]),
1496                                        ret: Box::new(Type::TypeVar("U".to_string())),
1497                                    predicate: None,
1498                                        has_rest: false,
1499                                    },
1500                                ),
1501                                Param::new("initial", Type::TypeVar("U".to_string())),
1502                            ],
1503                            ret: Type::TypeVar("U".to_string()),
1504                            predicate: None,
1505                            doc: doc(
1506                                "/**\n * Reduces the array to a single value.\n * Calls `callback(acc, value, index, array)` for each element left-to-right, threading the result as the next `acc`.\n * @param callback Combining function; receives the accumulator, the current element, its index, and the array.\n * @param initial Starting accumulator value.\n * @returns The final accumulator.\n */",
1507                            ),
1508                        },
1509                    ),
1510                    (
1511                        "forEach".to_string(),
1512                        MethodSig {
1513                            optional: false,
1514                            generics: Vec::new(),
1515                            params: vec![Param::new(
1516                                "callback",
1517                                Type::Function {
1518                                    optional: 0,
1519                                    params: element_callback_params(Vec::new()),
1520                                    ret: Box::new(Type::Void),
1521                                    predicate: None,
1522                                    has_rest: false,
1523                                },
1524                            )],
1525                            ret: Type::Void,
1526                            predicate: None,
1527                            doc: doc(
1528                                "/**\n * Calls `callback(value, index, array)` once for each element in order.\n * @param callback Function called once per element.\n */",
1529                            ),
1530                        },
1531                    ),
1532                    (
1533                        "some".to_string(),
1534                        MethodSig {
1535                            optional: false,
1536                            generics: Vec::new(),
1537                            params: vec![Param::new(
1538                                "predicate",
1539                                Type::Function {
1540                                    optional: 0,
1541                                    params: element_callback_params(Vec::new()),
1542                                    ret: Box::new(Type::Unknown),
1543                                    predicate: None,
1544                                    has_rest: false,
1545                                },
1546                            )],
1547                            ret: Type::Boolean,
1548                            predicate: None,
1549                            doc: doc(
1550                                "/**\n * Returns `true` if `predicate` returned a truthy value for at least one element.\n * Short-circuits on the first match.\n * @param predicate Function called with `(value, index, array)` for each element until it returns a truthy value.\n */",
1551                            ),
1552                        },
1553                    ),
1554                    (
1555                        "every".to_string(),
1556                        MethodSig {
1557                            optional: false,
1558                            generics: Vec::new(),
1559                            params: vec![Param::new(
1560                                "predicate",
1561                                Type::Function {
1562                                    optional: 0,
1563                                    params: element_callback_params(Vec::new()),
1564                                    ret: Box::new(Type::Unknown),
1565                                    predicate: None,
1566                                    has_rest: false,
1567                                },
1568                            )],
1569                            ret: Type::Boolean,
1570                            predicate: None,
1571                            doc: doc(
1572                                "/**\n * Returns `true` if `predicate` returned a truthy value for every element.\n * Short-circuits on the first falsy one. An empty array returns `true`.\n * @param predicate Function called with `(value, index, array)` for each element until it returns a falsy value.\n */",
1573                            ),
1574                        },
1575                    ),
1576                    (
1577                        "sort".to_string(),
1578                        MethodSig {
1579                            optional: false,
1580                            generics: Vec::new(),
1581                            params: vec![Param::with_default(
1582                                "compareFn",
1583                                Type::Union(vec![
1584                                    Type::Function {
1585                                        optional: 0,
1586                                        params: vec![
1587                                            Type::TypeVar("T".to_string()),
1588                                            Type::TypeVar("T".to_string()),
1589                                        ],
1590                                        ret: Box::new(Type::Number),
1591                                        predicate: None,
1592                                        has_rest: false,
1593                                    },
1594                                    Type::Undefined,
1595                                ]),
1596                                crate::DefaultValue::Undefined,
1597                            )],
1598                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1599                            predicate: None,
1600                            doc: doc(
1601                                "/**\n * Sorts the array in place and returns it. With no `compareFn`, elements are\n * compared by their string form (`[1, 2, 10]` sorts to `[1, 10, 2]`) — pass\n * `(a, b) => a - b` for numeric order.\n * `compareFn(a, b)` should return a negative number when `a` sorts before `b`, positive when after, and `0` when equal.\n * @param compareFn Element-pair comparator. Omit for default string-order comparison.\n */",
1602                            ),
1603                        },
1604                    ),
1605                    (
1606                        "lastIndexOf".to_string(),
1607                        MethodSig {
1608                            optional: false,
1609                            generics: Vec::new(),
1610                            params: vec![
1611                                Param::new("elem", Type::TypeVar("T".to_string())),
1612                                Param::with_default(
1613                                    "fromIndex",
1614                                    Type::Number,
1615                                    crate::DefaultValue::OmittedNumber {
1616                                        omitted: f64::INFINITY,
1617                                        undefined: 0.0,
1618                                    },
1619                                ),
1620                            ],
1621                            ret: Type::Number,
1622                            predicate: None,
1623                            doc: doc(
1624                                "/**\n * Returns the index of the last element equal to `elem`, or `-1`.\n * Scans backwards from `fromIndex` (inclusive).\n * @param elem The value to search for.\n * @param fromIndex Index to begin the backward scan from. Negative counts from the end. Defaults to the last index.\n */",
1625                            ),
1626                        },
1627                    ),
1628                    (
1629                        "reduceRight".to_string(),
1630                        MethodSig {
1631                            optional: false,
1632                            generics: vec!["U".to_string()],
1633                            params: vec![
1634                                Param::new(
1635                                    "callback",
1636                                    Type::Function {
1637                                        optional: 0,
1638                                        params: element_callback_params(vec![Type::TypeVar("U".to_string())]),
1639                                        ret: Box::new(Type::TypeVar("U".to_string())),
1640                                        predicate: None,
1641                                        has_rest: false,
1642                                    },
1643                                ),
1644                                Param::new("initial", Type::TypeVar("U".to_string())),
1645                            ],
1646                            ret: Type::TypeVar("U".to_string()),
1647                            predicate: None,
1648                            doc: doc(
1649                                "/**\n * Reduces the array right-to-left.\n * Calls `callback(acc, value, index, array)` for each element from the last to the first, threading the result as the next `acc`.\n * @param callback Combining function; receives the accumulator, the current element, its index, and the array.\n * @param initial Starting accumulator value.\n * @returns The final accumulator.\n */",
1650                            ),
1651                        },
1652                    ),
1653                    (
1654                        "fill".to_string(),
1655                        MethodSig {
1656                            optional: false,
1657                            generics: Vec::new(),
1658                            params: vec![
1659                                Param::new("value", Type::TypeVar("T".to_string())),
1660                                Param::with_default(
1661                                    "start",
1662                                    Type::Number,
1663                                    crate::DefaultValue::Number(0.0),
1664                                ),
1665                                Param::with_default(
1666                                    "end",
1667                                    Type::Number,
1668                                    crate::DefaultValue::Number(f64::INFINITY),
1669                                ),
1670                            ],
1671                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1672                            predicate: None,
1673                            doc: doc(
1674                                "/**\n * Sets every slot in `[start, end)` to `value`, in place, and returns the array.\n * @param value The value written into each slot.\n * @param start First index to fill. Negative counts from the end. Defaults to `0`.\n * @param end Index to stop before. Negative counts from the end. Defaults to the array length.\n */",
1675                            ),
1676                        },
1677                    ),
1678                    (
1679                        "copyWithin".to_string(),
1680                        MethodSig {
1681                            optional: false,
1682                            generics: Vec::new(),
1683                            params: vec![
1684                                Param::new("target", Type::Number),
1685                                Param::with_default(
1686                                    "start",
1687                                    Type::Number,
1688                                    crate::DefaultValue::Number(0.0),
1689                                ),
1690                                Param::with_default(
1691                                    "end",
1692                                    Type::Number,
1693                                    crate::DefaultValue::Number(f64::INFINITY),
1694                                ),
1695                            ],
1696                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1697                            predicate: None,
1698                            doc: doc(
1699                                "/**\n * Copies the slots `[start, end)` to `target`, in place (overlap-safe), and returns the array.\n * The length never changes; the copy is truncated at the end of the array.\n * @param target Destination index. Negative counts from the end.\n * @param start First source index. Negative counts from the end. Defaults to `0`.\n * @param end Source index to stop before. Negative counts from the end. Defaults to the array length.\n */",
1700                            ),
1701                        },
1702                    ),
1703                    (
1704                        "toReversed".to_string(),
1705                        MethodSig {
1706                            optional: false,
1707                            generics: Vec::new(),
1708                            params: Vec::new(),
1709                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1710                            predicate: None,
1711                            doc: doc(
1712                                "/**\n * Returns a new array with the elements in reverse order; the receiver is not modified.\n */",
1713                            ),
1714                        },
1715                    ),
1716                    (
1717                        "toSorted".to_string(),
1718                        MethodSig {
1719                            optional: false,
1720                            generics: Vec::new(),
1721                            params: vec![Param::with_default(
1722                                "compareFn",
1723                                Type::Union(vec![
1724                                    Type::Function {
1725                                        optional: 0,
1726                                        params: vec![
1727                                            Type::TypeVar("T".to_string()),
1728                                            Type::TypeVar("T".to_string()),
1729                                        ],
1730                                        ret: Box::new(Type::Number),
1731                                        predicate: None,
1732                                        has_rest: false,
1733                                    },
1734                                    Type::Undefined,
1735                                ]),
1736                                crate::DefaultValue::Undefined,
1737                            )],
1738                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1739                            predicate: None,
1740                            doc: doc(
1741                                "/**\n * Returns a new sorted array; the receiver is not modified.\n * Comparator semantics match `sort`: no `compareFn` compares by string form — pass `(a, b) => a - b` for numeric order.\n * @param compareFn Element-pair comparator. Omit for default string-order comparison.\n */",
1742                            ),
1743                        },
1744                    ),
1745                    (
1746                        "toSpliced".to_string(),
1747                        MethodSig {
1748                            optional: false,
1749                            generics: Vec::new(),
1750                            params: vec![
1751                                Param::new("start", Type::Number),
1752                                Param::with_default(
1753                                    "deleteCount",
1754                                    Type::Number,
1755                                    crate::DefaultValue::OmittedNumber {
1756                                        omitted: f64::INFINITY,
1757                                        undefined: 0.0,
1758                                    },
1759                                ),
1760                                Param::rest(
1761                                    "items",
1762                                    Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1763                                ),
1764                            ],
1765                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1766                            predicate: None,
1767                            doc: doc(
1768                                "/**\n * Returns a new array with `deleteCount` elements removed at `start` and `items` inserted there; the receiver is not modified.\n * @param start Index where the change begins. Negative counts from the end.\n * @param deleteCount How many elements to drop. Defaults to all elements from `start` to the end.\n * @param items Zero or more replacement elements.\n */",
1769                            ),
1770                        },
1771                    ),
1772                    (
1773                        "with".to_string(),
1774                        MethodSig {
1775                            optional: false,
1776                            generics: Vec::new(),
1777                            params: vec![
1778                                Param::new("index", Type::Number),
1779                                Param::new("value", Type::TypeVar("T".to_string())),
1780                            ],
1781                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1782                            predicate: None,
1783                            doc: doc(
1784                                "/**\n * Returns a copy of the array with the slot at `index` replaced by `value`; the receiver is not modified.\n * Negative `index` counts from the end. An out-of-range index throws a `RangeError`.\n * @param index The slot to replace.\n * @param value The replacement value.\n */",
1785                            ),
1786                        },
1787                    ),
1788                    (
1789                        "shift".to_string(),
1790                        MethodSig {
1791                            optional: false,
1792                            generics: Vec::new(),
1793                            params: Vec::new(),
1794                            ret: Type::Union(vec![
1795                                Type::TypeVar("T".to_string()),
1796                                Type::Undefined,
1797                            ]),
1798                            predicate: None,
1799                            doc: doc(
1800                                "/**\n * Removes and returns the first element, shifting the rest forward.\n * Returns `undefined` when the array is empty.\n */",
1801                            ),
1802                        },
1803                    ),
1804                    (
1805                        "unshift".to_string(),
1806                        MethodSig {
1807                            optional: false,
1808                            generics: Vec::new(),
1809                            params: vec![Param::rest(
1810                                "items",
1811                                Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1812                            )],
1813                            ret: Type::Number,
1814                            predicate: None,
1815                            doc: doc(
1816                                "/**\n * Prepends `items` (keeping their argument order) and returns the new length.\n * @param items One or more elements to add at the front.\n */",
1817                            ),
1818                        },
1819                    ),
1820                    (
1821                        "splice".to_string(),
1822                        MethodSig {
1823                            optional: false,
1824                            generics: Vec::new(),
1825                            params: vec![
1826                                Param::new("start", Type::Number),
1827                                Param::with_default(
1828                                    "deleteCount",
1829                                    Type::Number,
1830                                    crate::DefaultValue::OmittedNumber {
1831                                        omitted: f64::INFINITY,
1832                                        undefined: 0.0,
1833                                    },
1834                                ),
1835                                Param::rest(
1836                                    "items",
1837                                    Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1838                                ),
1839                            ],
1840                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1841                            predicate: None,
1842                            doc: doc(
1843                                "/**\n * Removes `deleteCount` elements at `start`, inserts `items` there (in place), and returns the removed elements.\n * @param start Index where the change begins. Negative counts from the end.\n * @param deleteCount How many elements to remove. Defaults to all elements from `start` to the end.\n * @param items Zero or more elements to insert at `start`.\n */",
1844                            ),
1845                        },
1846                    ),
1847                ]),
1848                properties: BTreeMap::from([(
1849                    "length".to_string(),
1850                    PropertySig {
1851                        ty: Type::Number,
1852                        readonly: true,
1853                        intrinsic: true,
1854                        optional: false,
1855                        doc: doc("/** The number of elements in the array. */"),
1856                    },
1857                )]),
1858                dispatch: Dispatch::Direct,
1859                doc: doc("/** A homogeneous array of `T`. */"),
1860            },
1861        },
1862    );
1863    defs.types.insert(
1864        "ArrayConstructor".to_string(),
1865        TypeSymbol {
1866            name: "ArrayConstructor".to_string(),
1867            mangled_name: crate::mangle::prelude("ArrayConstructor"),
1868            declaration_span: Span::at(crate::FileId::PRELUDE),
1869            kind: TypeKind::Interface { index: None,
1870                generics: Vec::new(),
1871                methods: BTreeMap::from([(
1872                    "from".to_string(),
1873                    MethodSig {
1874                        optional: false,
1875                        generics: vec!["T".to_string(), "U".to_string()],
1876                        params: vec![
1877                            Param::new(
1878                                "src",
1879                                Type::union(vec![
1880                                    Type::Readonly(Box::new(Type::Array(Box::new(Type::TypeVar("T".to_string()))))),
1881                                    Type::prelude_interface(
1882                                        "Iterator".to_string(),
1883                                        vec![Type::TypeVar("T".to_string())],
1884                                    ),
1885                                    Type::prelude_interface(
1886                                        "Iterable".to_string(),
1887                                        vec![Type::TypeVar("T".to_string())],
1888                                    ),
1889                                    array_like_type(),
1890                                ]),
1891                            ),
1892                            Param::with_default(
1893                                "mapFn",
1894                                Type::union(vec![
1895                                    Type::Function {
1896                                        optional: 0,
1897                                        params: vec![Type::TypeVar("T".to_string()), Type::Number],
1898                                        ret: Box::new(Type::TypeVar("U".to_string())),
1899                                        predicate: None,
1900                                        has_rest: false,
1901                                    },
1902                                    Type::Undefined,
1903                                ]),
1904                                crate::DefaultValue::Undefined,
1905                            ),
1906                        ],
1907                        ret: Type::Array(Box::new(Type::TypeVar("U".to_string()))),
1908                        predicate: None,
1909                        doc: doc(
1910                            "/**\n * Materializes any iterable — an array, string (code points), `Iterator<T>`, or `Iterable<T>` — into a fresh array.\n * @param src The iterable source.\n * @param mapFn Optional per-element transform applied while collecting.\n */",
1911                        ),
1912                    },
1913                ), (
1914                    "of".to_string(),
1915                    MethodSig {
1916                        optional: false,
1917                        generics: vec!["T".to_string()],
1918                        params: vec![Param::rest(
1919                            "items",
1920                            Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1921                        )],
1922                        ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1923                        predicate: None,
1924                        doc: doc(
1925                            "/** Builds an array from its arguments — `Array.of(1, 2, 3)` is `[1, 2, 3]`. */",
1926                        ),
1927                    },
1928                ), (
1929                    "isArray".to_string(),
1930                    MethodSig {
1931                        optional: false,
1932                        generics: vec!["T".to_string()],
1933                        params: vec![Param::new("value", Type::TypeVar("T".to_string()))],
1934                        ret: Type::Boolean,
1935                        predicate: Some(crate::TypePredicate {
1936                            parameter_index: 0,
1937                            asserted_type: Type::Array(Box::new(Type::Unknown)),
1938                        }),
1939                        doc: doc(
1940                            "/**\n * Returns `true` when `value` is an array. Element type is erased at runtime; the static-typed true-branch sees `value` narrowed to the call site's array variant.\n * @param value The value to test.\n */",
1941                        ),
1942                    },
1943                )]),
1944                properties: BTreeMap::new(),
1945                dispatch: Dispatch::Static,
1946                doc: doc(
1947                    "/** Constructor object for `Array`. Accessed via the global `Array` binding. */",
1948                ),
1949            },
1950        },
1951    );
1952
1953    defs.values.insert(
1954        "Array".to_string(),
1955        ValueSymbol {
1956            name: "Array".to_string(),
1957            mangled_name: crate::mangle::prelude("Array"),
1958            declaration_span: Span::at(crate::FileId::PRELUDE),
1959            kind: ValueKind::Const {
1960                ty: Type::prelude_interface("ArrayConstructor".to_string(), Vec::new()),
1961                doc: doc(
1962                    "/** The `Array` constructor — call `Array.isArray(x)` to runtime-check whether a value is an array. */",
1963                ),
1964            },
1965        },
1966    );
1967}