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