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