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            match super::with(&elements, f64v(abi_arg(params, 1)?), *abi_arg(params, 2)?) {
662                Some(out) => {
663                    *abi_result(results, 0)? = super::build_array(caller, &out)?;
664                    Ok(())
665                }
666                None => Err(crate::runtime::host::range_error("index out of range")),
667            }
668        },
669    )?;
670
671    // --- Iterators -------------------------------------------------------
672    let iterator_ret = object_ref(&intr);
673    register_host_fn(
674        linker,
675        MODULE_NAME,
676        method_key("keys"),
677        ft(vec![array.clone()], vec![iterator_ret.clone()]),
678        |caller, params, results| {
679            *abi_result(results, 0)? = super::keys(caller, abi_arg(params, 0)?)?;
680            Ok(())
681        },
682    )?;
683    register_host_fn(
684        linker,
685        MODULE_NAME,
686        method_key("values"),
687        ft(vec![array.clone()], vec![iterator_ret.clone()]),
688        |caller, params, results| {
689            *abi_result(results, 0)? = super::values(caller, abi_arg(params, 0)?)?;
690            Ok(())
691        },
692    )?;
693    register_host_fn(
694        linker,
695        MODULE_NAME,
696        method_key("entries"),
697        ft(vec![array.clone()], vec![iterator_ret.clone()]),
698        |caller, params, results| {
699            *abi_result(results, 0)? = super::entries(caller, abi_arg(params, 0)?)?;
700            Ok(())
701        },
702    )?;
703
704    // Serialization members — thin re-entries into the host-owned array
705    // vtable's `toString`/`toJson` slots.
706    for (name, slot) in [("toString", 0usize), ("toJson", 1usize)] {
707        register_host_fn_async(
708            linker,
709            MODULE_NAME,
710            method_key(name),
711            ft(vec![array.clone()], vec![string.clone()]),
712            move |caller, params, results| {
713                Box::pin(async move {
714                    *abi_result(results, 0)? =
715                        crate::runtime::prelude::vtable::dispatch_vtable_slot(
716                            caller,
717                            abi_arg(params, 0)?,
718                            slot,
719                            &[],
720                        )
721                        .await?;
722                    Ok(())
723                })
724            },
725        )?;
726    }
727
728    // --- ArrayConstructor statics ----------------------------------------
729    // `Array.isArray(value)`: Dispatch::Static — a bare `$Array` ref-test.
730    register_host_fn(
731        linker,
732        MODULE_NAME,
733        ctor_key("isArray"),
734        ft(vec![elem.clone()], vec![boolean]),
735        |caller, params, results| {
736            *abi_result(results, 0)? =
737                wasmtime::Val::I32(super::is_array(caller, abi_arg(params, 0)?)? as i32);
738            Ok(())
739        },
740    )?;
741    // `Array.of(...items)`: Dispatch::Static, so the receiver is dropped and the
742    // variadic args arrive packed into one `$Array`.
743    register_host_fn(
744        linker,
745        MODULE_NAME,
746        ctor_key("of"),
747        ft(vec![array.clone()], vec![array.clone()]),
748        |caller, params, results| {
749            let items = super::read_array(caller, abi_arg(params, 0)?, "Array.of")?;
750            *abi_result(results, 0)? = super::build_array(caller, &items)?;
751            Ok(())
752        },
753    )?;
754    // `Array.from(src, mapFn?)`: consumes any iterable, re-entering the guest
755    // for the iterator protocol and the per-element `mapFn`.
756    register_host_fn_async(
757        linker,
758        MODULE_NAME,
759        ctor_key("from"),
760        ft(
761            vec![object_ref(&intr), object_ref(&intr)],
762            vec![array.clone()],
763        ),
764        |caller, params, results| {
765            Box::pin(async move {
766                *abi_result(results, 0)? =
767                    super::from(caller, abi_arg(params, 0)?, abi_arg(params, 1)?).await?;
768                Ok(())
769            })
770        },
771    )?;
772    Ok(())
773}
774
775/// `{ readonly length: number }`: an array-like source with no elements to
776/// read, each of which is `undefined`.
777pub(crate) fn array_like_type() -> Type {
778    Type::Object {
779        fields: std::collections::BTreeMap::from([(
780            "length".to_string(),
781            crate::ObjectField {
782                ty: Type::Number,
783                optional: false,
784                readonly: true,
785                method: false,
786            },
787        )]),
788        index: None,
789    }
790}
791
792pub fn declare(defs: &mut PackageDeclaration) {
793    let t = || Type::TypeVar("T".to_string());
794    let u = || Type::TypeVar("U".to_string());
795    let arr_ty = || Type::Array(Box::new(t()));
796    let arr = || Param::new("arr", arr_ty());
797    let elem = || Param::new("v", t());
798    let n = |name: &str| Param::new(name, Type::Number);
799    let t_or_undefined = || Type::Union(vec![t(), Type::Undefined]);
800    let iter = |item: Type| Type::prelude_interface("Iterator".to_string(), vec![item]);
801    let func = |params: Vec<Type>, ret: Type| Type::Function {
802        optional: 0,
803        params,
804        ret: Box::new(ret),
805        predicate: None,
806        has_rest: false,
807    };
808    // An element callback's slot is erased (see `install`); its type for
809    // checking calls is in `declare_types`.
810    let callback = || Param::new("f", Type::Unknown);
811    let comparator_or_undefined =
812        || Type::Union(vec![func(vec![t(), t()], Type::Number), Type::Undefined]);
813
814    let m = |defs: &mut PackageDeclaration, name: &str, params: Vec<Param>, ret: Type| {
815        declare_method(defs, name, method_key(name), params, ret);
816    };
817
818    m(defs, "at", vec![arr(), n("index")], t_or_undefined());
819    m(defs, "slice", vec![arr(), n("start"), n("end")], arr_ty());
820    m(
821        defs,
822        "concat",
823        vec![
824            arr(),
825            Param::new(
826                "items",
827                Type::Array(Box::new(Type::union(vec![
828                    t(),
829                    Type::Readonly(Box::new(arr_ty())),
830                ]))),
831            ),
832        ],
833        arr_ty(),
834    );
835    m(
836        defs,
837        "join",
838        vec![arr(), Param::new("sep", Type::String)],
839        Type::String,
840    );
841    m(
842        defs,
843        "indexOf",
844        vec![arr(), elem(), n("fromIndex")],
845        Type::Number,
846    );
847    m(
848        defs,
849        "lastIndexOf",
850        vec![arr(), elem(), n("fromIndex")],
851        Type::Number,
852    );
853    m(
854        defs,
855        "includes",
856        vec![arr(), elem(), n("fromIndex")],
857        Type::Boolean,
858    );
859
860    m(
861        defs,
862        "push",
863        vec![arr(), Param::new("items", arr_ty())],
864        Type::Number,
865    );
866    m(defs, "pop", vec![arr()], t_or_undefined());
867    m(
868        defs,
869        &crate::mangle::property_setter("length"),
870        vec![arr(), Param::new("value", Type::Number)],
871        Type::Void,
872    );
873    m(defs, "shift", vec![arr()], t_or_undefined());
874    m(
875        defs,
876        "unshift",
877        vec![arr(), Param::new("items", arr_ty())],
878        Type::Number,
879    );
880    m(defs, "reverse", vec![arr()], arr_ty());
881    m(
882        defs,
883        "fill",
884        vec![arr(), elem(), n("start"), n("end")],
885        arr_ty(),
886    );
887    m(
888        defs,
889        "copyWithin",
890        vec![arr(), n("target"), n("start"), n("end")],
891        arr_ty(),
892    );
893    m(
894        defs,
895        "splice",
896        vec![
897            arr(),
898            n("start"),
899            n("deleteCount"),
900            Param::new("items", arr_ty()),
901        ],
902        arr_ty(),
903    );
904    m(
905        defs,
906        "sort",
907        vec![arr(), Param::new("compareFn", comparator_or_undefined())],
908        arr_ty(),
909    );
910
911    m(defs, "forEach", vec![arr(), callback()], Type::Void);
912    m(
913        defs,
914        "map",
915        vec![arr(), callback()],
916        Type::Array(Box::new(u())),
917    );
918    m(defs, "filter", vec![arr(), callback()], arr_ty());
919    m(
920        defs,
921        "reduce",
922        vec![arr(), callback(), Param::new("initial", u())],
923        u(),
924    );
925    m(
926        defs,
927        "reduceRight",
928        vec![arr(), callback(), Param::new("initial", u())],
929        u(),
930    );
931    for name in ["find", "findLast"] {
932        m(defs, name, vec![arr(), callback()], t_or_undefined());
933    }
934    for name in ["findIndex", "findLastIndex"] {
935        m(defs, name, vec![arr(), callback()], Type::Number);
936    }
937    m(defs, "some", vec![arr(), callback()], Type::Boolean);
938    m(defs, "every", vec![arr(), callback()], Type::Boolean);
939    m(defs, "flat", vec![arr(), n("depth")], arr_ty());
940    m(
941        defs,
942        "flatMap",
943        vec![arr(), callback()],
944        Type::Array(Box::new(u())),
945    );
946
947    m(defs, "toReversed", vec![arr()], arr_ty());
948    m(
949        defs,
950        "toSorted",
951        vec![arr(), Param::new("compareFn", comparator_or_undefined())],
952        arr_ty(),
953    );
954    m(
955        defs,
956        "toSpliced",
957        vec![
958            arr(),
959            n("start"),
960            n("deleteCount"),
961            Param::new("items", arr_ty()),
962        ],
963        arr_ty(),
964    );
965    m(defs, "with", vec![arr(), n("index"), elem()], arr_ty());
966
967    m(defs, "keys", vec![arr()], iter(Type::Number));
968    m(defs, "values", vec![arr()], iter(t()));
969    m(
970        defs,
971        "entries",
972        vec![arr()],
973        iter(Type::Tuple(vec![Type::Number, t()].into())),
974    );
975    m(defs, "toString", vec![arr()], Type::String);
976    m(defs, "toJson", vec![arr()], Type::String);
977    declare_method(
978        defs,
979        "isArray",
980        ctor_key("isArray"),
981        vec![Param::new("value", t())],
982        Type::Boolean,
983    );
984
985    declare_method(
986        defs,
987        "of",
988        ctor_key("of"),
989        vec![Param::new("items", arr_ty())],
990        arr_ty(),
991    );
992    declare_method(
993        defs,
994        "from",
995        ctor_key("from"),
996        vec![
997            Param::new(
998                "src",
999                Type::union(vec![
1000                    Type::Readonly(Box::new(arr_ty())),
1001                    iter(t()),
1002                    Type::prelude_interface("Iterable".to_string(), vec![t()]),
1003                    array_like_type(),
1004                ]),
1005            ),
1006            Param::new(
1007                "mapFn",
1008                Type::Union(vec![func(vec![t(), Type::Number], u()), Type::Undefined]),
1009            ),
1010        ],
1011        arr_ty(),
1012    );
1013}
1014
1015/// The type/interface surface this module implements — its slice of the
1016/// prelude declaration (see `declaration::prelude_package_declaration`).
1017#[allow(clippy::too_many_lines)]
1018pub(crate) fn declare_types(defs: &mut crate::PackageDeclaration) {
1019    use crate::runtime::prelude::declaration::doc;
1020    use crate::{
1021        Dispatch, MethodSig, Param, PropertySig, Span, Type, TypeKind, TypeSymbol, ValueKind,
1022        ValueSymbol,
1023    };
1024    use std::collections::BTreeMap;
1025    defs.types.insert(
1026        "Array".to_string(),
1027        TypeSymbol {
1028            name: "Array".to_string(),
1029            mangled_name: crate::mangle::prelude("Array"),
1030            declaration_span: Span::at(crate::FileId::PRELUDE),
1031            kind: TypeKind::Interface { index: None,
1032                generics: vec!["T".to_string()],
1033                methods: BTreeMap::from([
1034                    (
1035                        "toString".to_string(),
1036                        MethodSig {
1037                            optional: false,
1038                            generics: Vec::new(),
1039                            params: Vec::new(),
1040                            ret: Type::String,
1041                            predicate: None,
1042                            doc: doc("/** Returns the elements joined with commas. */"),
1043                        },
1044                    ),
1045                    (
1046                        "toJson".to_string(),
1047                        MethodSig {
1048                            optional: false,
1049                            generics: Vec::new(),
1050                            params: Vec::new(),
1051                            ret: Type::String,
1052                            predicate: None,
1053                            doc: doc(
1054                                "/** Returns the JSON representation of this array — `\"[\"` + elements' `toJson()` joined with `\",\"` + `\"]\"`. */",
1055                            ),
1056                        },
1057                    ),
1058                    (
1059                        "join".to_string(),
1060                        MethodSig {
1061                            optional: false,
1062                            generics: Vec::new(),
1063                            params: vec![Param::with_default(
1064                                "separator",
1065                                Type::String,
1066                                crate::DefaultValue::String(",".to_string()),
1067                            )],
1068                            ret: Type::String,
1069                            predicate: None,
1070                            doc: doc(
1071                                "/**\n * Joins the elements using `separator`.\n * @param separator The string inserted between elements. Defaults to `\",\"`.\n */",
1072                            ),
1073                        },
1074                    ),
1075                    (
1076                        "keys".to_string(),
1077                        MethodSig {
1078                            optional: false,
1079                            generics: Vec::new(),
1080                            params: Vec::new(),
1081                            ret: Type::prelude_interface("Iterator".to_string(), vec![Type::Number]),
1082                            predicate: None,
1083                            doc: doc(
1084                                "/** Returns a live `Iterator<number>` of the indices — length is re-read on every step. */",
1085                            ),
1086                        },
1087                    ),
1088                    (
1089                        "values".to_string(),
1090                        MethodSig {
1091                            optional: false,
1092                            generics: Vec::new(),
1093                            params: Vec::new(),
1094                            ret: Type::prelude_interface("Iterator".to_string(), vec![Type::TypeVar("T".to_string())]),
1095                            predicate: None,
1096                            doc: doc(
1097                                "/** Returns a live `Iterator<T>` over the elements — unlike Map/Set cursors it sees pushes made during iteration. */",
1098                            ),
1099                        },
1100                    ),
1101                    (
1102                        "entries".to_string(),
1103                        MethodSig {
1104                            optional: false,
1105                            generics: Vec::new(),
1106                            params: Vec::new(),
1107                            ret: Type::prelude_interface("Iterator".to_string(), vec![Type::Tuple(vec![
1108                                Type::Number,
1109                                Type::TypeVar("T".to_string()),
1110                            ].into())]),
1111                            predicate: None,
1112                            doc: doc(
1113                                "/** Returns a live `Iterator<[number, T]>` of index/element pairs. */",
1114                            ),
1115                        },
1116                    ),
1117
1118                    (
1119                        "map".to_string(),
1120                        MethodSig {
1121                            optional: false,
1122                            generics: vec!["U".to_string()],
1123                            params: vec![Param::new(
1124                                "callback",
1125                                Type::Function {
1126                                    optional: 0,
1127                                    params: element_callback_params(Vec::new()),
1128                                    ret: Box::new(Type::TypeVar("U".to_string())),
1129                                    predicate: None,
1130                                    has_rest: false,
1131                                },
1132                            )],
1133                            ret: Type::Array(Box::new(Type::TypeVar("U".to_string()))),
1134                            predicate: None,
1135                            doc: doc(
1136                                "/**\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 */",
1137                            ),
1138                        },
1139                    ),
1140                    (
1141                        "push".to_string(),
1142                        MethodSig {
1143                            optional: false,
1144                            generics: Vec::new(),
1145                            params: vec![Param::rest(
1146                                "items",
1147                                Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1148                            )],
1149                            ret: Type::Number,
1150                            predicate: None,
1151                            doc: doc(
1152                                "/**\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 */",
1153                            ),
1154                        },
1155                    ),
1156                    // pop / at / find — each returns `T | undefined`
1157                    // for the empty / out-of-range / no-match case.
1158                    (
1159                        "pop".to_string(),
1160                        MethodSig {
1161                            optional: false,
1162                            generics: Vec::new(),
1163                            params: Vec::new(),
1164                            ret: Type::Union(vec![
1165                                Type::TypeVar("T".to_string()),
1166                                Type::Undefined,
1167                            ]),
1168                            predicate: None,
1169                            doc: doc(
1170                                "/**\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 */",
1171                            ),
1172                        },
1173                    ),
1174                    (
1175                        "at".to_string(),
1176                        MethodSig {
1177                            optional: false,
1178                            generics: Vec::new(),
1179                            params: vec![Param::new("index", Type::Number)],
1180                            ret: Type::Union(vec![
1181                                Type::TypeVar("T".to_string()),
1182                                Type::Undefined,
1183                            ]),
1184                            predicate: None,
1185                            doc: doc(
1186                                "/**\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 */",
1187                            ),
1188                        },
1189                    ),
1190                    (
1191                        "find".to_string(),
1192                        MethodSig {
1193                            optional: false,
1194                            generics: Vec::new(),
1195                            params: vec![Param::new(
1196                                "callback",
1197                                Type::Function {
1198                                    optional: 0,
1199                                    params: element_callback_params(Vec::new()),
1200                                    ret: Box::new(Type::Unknown),
1201                                    predicate: None,
1202                                    has_rest: false,
1203                                },
1204                            )],
1205                            ret: Type::Union(vec![
1206                                Type::TypeVar("T".to_string()),
1207                                Type::Undefined,
1208                            ]),
1209                            predicate: None,
1210                            doc: doc(
1211                                "/**\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 */",
1212                            ),
1213                        },
1214                    ),
1215                    (
1216                        "findLast".to_string(),
1217                        MethodSig {
1218                            optional: false,
1219                            generics: Vec::new(),
1220                            params: vec![Param::new(
1221                                "callback",
1222                                Type::Function {
1223                                    optional: 0,
1224                                    params: element_callback_params(Vec::new()),
1225                                    ret: Box::new(Type::Unknown),
1226                                    predicate: None,
1227                                    has_rest: false,
1228                                },
1229                            )],
1230                            ret: Type::Union(vec![
1231                                Type::TypeVar("T".to_string()),
1232                                Type::Undefined,
1233                            ]),
1234                            predicate: None,
1235                            doc: doc(
1236                                "/**\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 */",
1237                            ),
1238                        },
1239                    ),
1240                    (
1241                        "findIndex".to_string(),
1242                        MethodSig {
1243                            optional: false,
1244                            generics: Vec::new(),
1245                            params: vec![Param::new(
1246                                "callback",
1247                                Type::Function {
1248                                    optional: 0,
1249                                    params: element_callback_params(Vec::new()),
1250                                    ret: Box::new(Type::Unknown),
1251                                    predicate: None,
1252                                    has_rest: false,
1253                                },
1254                            )],
1255                            ret: Type::Number,
1256                            predicate: None,
1257                            doc: doc(
1258                                "/**\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 */",
1259                            ),
1260                        },
1261                    ),
1262                    (
1263                        "findLastIndex".to_string(),
1264                        MethodSig {
1265                            optional: false,
1266                            generics: Vec::new(),
1267                            params: vec![Param::new(
1268                                "callback",
1269                                Type::Function {
1270                                    optional: 0,
1271                                    params: element_callback_params(Vec::new()),
1272                                    ret: Box::new(Type::Unknown),
1273                                    predicate: None,
1274                                    has_rest: false,
1275                                },
1276                            )],
1277                            ret: Type::Number,
1278                            predicate: None,
1279                            doc: doc(
1280                                "/**\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 */",
1281                            ),
1282                        },
1283                    ),
1284                    // `flat`'s return type un-nests `depth` array levels; the typechecker
1285                    // special-cases it (see infer::lookup) and overrides this placeholder.
1286                    (
1287                        "flat".to_string(),
1288                        MethodSig {
1289                            optional: false,
1290                            generics: Vec::new(),
1291                            params: vec![Param::with_default(
1292                                "depth",
1293                                Type::Number,
1294                                crate::DefaultValue::Number(1.0),
1295                            )],
1296                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1297                            predicate: None,
1298                            doc: doc(
1299                                "/**\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 */",
1300                            ),
1301                        },
1302                    ),
1303                    (
1304                        "flatMap".to_string(),
1305                        MethodSig {
1306                            optional: false,
1307                            generics: vec!["U".to_string()],
1308                            params: vec![Param::new(
1309                                "callback",
1310                                Type::Function {
1311                                    optional: 0,
1312                                    params: element_callback_params(Vec::new()),
1313                                    ret: Box::new(Type::Array(Box::new(Type::TypeVar(
1314                                        "U".to_string(),
1315                                    )))),
1316                                    predicate: None,
1317                                    has_rest: false,
1318                                },
1319                            )],
1320                            ret: Type::Array(Box::new(Type::TypeVar("U".to_string()))),
1321                            predicate: None,
1322                            doc: doc(
1323                                "/**\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 */",
1324                            ),
1325                        },
1326                    ),
1327                    (
1328                        "slice".to_string(),
1329                        MethodSig {
1330                            optional: false,
1331                            generics: Vec::new(),
1332                            params: vec![
1333                                Param::with_default(
1334                                    "start",
1335                                    Type::Number,
1336                                    crate::DefaultValue::Number(0.0),
1337                                ),
1338                                // `end` omitted means "to the end".
1339                                // `slice_body` truncates via `i32.trunc_sat_f64_s`
1340                                // then clamps to `[0, len]`, so f64::INFINITY
1341                                // saturates to i32::MAX and clamps to len —
1342                                // matching JS `slice(s)` / `slice(s, undefined)`
1343                                // without changing the body or boxing the param.
1344                                Param::with_default(
1345                                    "end",
1346                                    Type::Number,
1347                                    crate::DefaultValue::Number(f64::INFINITY),
1348                                ),
1349                            ],
1350                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1351                            predicate: None,
1352                            doc: doc(
1353                                "/**\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 */",
1354                            ),
1355                        },
1356                    ),
1357                    (
1358                        "indexOf".to_string(),
1359                        MethodSig {
1360                            optional: false,
1361                            generics: Vec::new(),
1362                            params: vec![
1363                                Param::new("elem", Type::TypeVar("T".to_string())),
1364                                Param::with_default(
1365                                    "fromIndex",
1366                                    Type::Number,
1367                                    crate::DefaultValue::Number(0.0),
1368                                ),
1369                            ],
1370                            ret: Type::Number,
1371                            predicate: None,
1372                            doc: doc(
1373                                "/**\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 */",
1374                            ),
1375                        },
1376                    ),
1377                    (
1378                        "includes".to_string(),
1379                        MethodSig {
1380                            optional: false,
1381                            generics: Vec::new(),
1382                            params: vec![
1383                                Param::new("elem", Type::TypeVar("T".to_string())),
1384                                Param::with_default(
1385                                    "fromIndex",
1386                                    Type::Number,
1387                                    crate::DefaultValue::Number(0.0),
1388                                ),
1389                            ],
1390                            ret: Type::Boolean,
1391                            predicate: None,
1392                            doc: doc(
1393                                "/**\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 */",
1394                            ),
1395                        },
1396                    ),
1397                    (
1398                        "concat".to_string(),
1399                        MethodSig {
1400                            optional: false,
1401                            generics: Vec::new(),
1402                            // `concat(...items: (T | readonly T[])[]): T[]`,
1403                            // tsc's `ConcatArray` overload. The wrapper sees one
1404                            // `$Array` carrying the trailing arguments.
1405                            params: vec![Param::rest(
1406                                "items",
1407                                Type::Array(Box::new(Type::union(vec![
1408                                    Type::TypeVar("T".to_string()),
1409                                    Type::Readonly(Box::new(Type::Array(Box::new(
1410                                        Type::TypeVar("T".to_string()),
1411                                    )))),
1412                                ]))),
1413                            )],
1414                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1415                            predicate: None,
1416                            doc: doc(
1417                                "/**\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 */",
1418                            ),
1419                        },
1420                    ),
1421                    (
1422                        "reverse".to_string(),
1423                        MethodSig {
1424                            optional: false,
1425                            generics: Vec::new(),
1426                            params: Vec::new(),
1427                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1428                            predicate: None,
1429                            doc: doc(
1430                                "/**\n * Reverses the array in place and returns it.\n */",
1431                            ),
1432                        },
1433                    ),
1434                    // higher-order methods. Each takes a
1435                    // closure arg; under uniform erasure the closure's
1436                    // Wasm struct type is `(arity, is_void)`-keyed, so
1437                    // the prelude exports a single `Array#<method>`
1438                    // wrapper per HOF rather than emitting per
1439                    // call-site. `find` is (needs
1440                    // `T | undefined` unions).
1441                    (
1442                        "filter".to_string(),
1443                        MethodSig {
1444                            optional: false,
1445                            generics: Vec::new(),
1446                            params: vec![Param::new(
1447                                "predicate",
1448                                Type::Function {
1449                                    optional: 0,
1450                                    params: element_callback_params(Vec::new()),
1451                                    ret: Box::new(Type::Unknown),
1452                                    predicate: None,
1453                                    has_rest: false,
1454                                },
1455                            )],
1456                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1457                            predicate: None,
1458                            doc: doc(
1459                                "/**\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 */",
1460                            ),
1461                        },
1462                    ),
1463                    (
1464                        "reduce".to_string(),
1465                        MethodSig {
1466                            optional: false,
1467                            generics: vec!["U".to_string()],
1468                            params: vec![
1469                                Param::new(
1470                                    "callback",
1471                                    Type::Function {
1472                                        optional: 0,
1473                                        params: element_callback_params(vec![Type::TypeVar("U".to_string())]),
1474                                        ret: Box::new(Type::TypeVar("U".to_string())),
1475                                    predicate: None,
1476                                        has_rest: false,
1477                                    },
1478                                ),
1479                                Param::new("initial", Type::TypeVar("U".to_string())),
1480                            ],
1481                            ret: Type::TypeVar("U".to_string()),
1482                            predicate: None,
1483                            doc: doc(
1484                                "/**\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 */",
1485                            ),
1486                        },
1487                    ),
1488                    (
1489                        "forEach".to_string(),
1490                        MethodSig {
1491                            optional: false,
1492                            generics: Vec::new(),
1493                            params: vec![Param::new(
1494                                "callback",
1495                                Type::Function {
1496                                    optional: 0,
1497                                    params: element_callback_params(Vec::new()),
1498                                    ret: Box::new(Type::Void),
1499                                    predicate: None,
1500                                    has_rest: false,
1501                                },
1502                            )],
1503                            ret: Type::Void,
1504                            predicate: None,
1505                            doc: doc(
1506                                "/**\n * Calls `callback(value, index, array)` once for each element in order.\n * @param callback Function called once per element.\n */",
1507                            ),
1508                        },
1509                    ),
1510                    (
1511                        "some".to_string(),
1512                        MethodSig {
1513                            optional: false,
1514                            generics: Vec::new(),
1515                            params: vec![Param::new(
1516                                "predicate",
1517                                Type::Function {
1518                                    optional: 0,
1519                                    params: element_callback_params(Vec::new()),
1520                                    ret: Box::new(Type::Unknown),
1521                                    predicate: None,
1522                                    has_rest: false,
1523                                },
1524                            )],
1525                            ret: Type::Boolean,
1526                            predicate: None,
1527                            doc: doc(
1528                                "/**\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 */",
1529                            ),
1530                        },
1531                    ),
1532                    (
1533                        "every".to_string(),
1534                        MethodSig {
1535                            optional: false,
1536                            generics: Vec::new(),
1537                            params: vec![Param::new(
1538                                "predicate",
1539                                Type::Function {
1540                                    optional: 0,
1541                                    params: element_callback_params(Vec::new()),
1542                                    ret: Box::new(Type::Unknown),
1543                                    predicate: None,
1544                                    has_rest: false,
1545                                },
1546                            )],
1547                            ret: Type::Boolean,
1548                            predicate: None,
1549                            doc: doc(
1550                                "/**\n * Returns `true` if `predicate` returned a truthy value for 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 */",
1551                            ),
1552                        },
1553                    ),
1554                    (
1555                        "sort".to_string(),
1556                        MethodSig {
1557                            optional: false,
1558                            generics: Vec::new(),
1559                            params: vec![Param::with_default(
1560                                "compareFn",
1561                                Type::Union(vec![
1562                                    Type::Function {
1563                                        optional: 0,
1564                                        params: vec![
1565                                            Type::TypeVar("T".to_string()),
1566                                            Type::TypeVar("T".to_string()),
1567                                        ],
1568                                        ret: Box::new(Type::Number),
1569                                        predicate: None,
1570                                        has_rest: false,
1571                                    },
1572                                    Type::Undefined,
1573                                ]),
1574                                crate::DefaultValue::Undefined,
1575                            )],
1576                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1577                            predicate: None,
1578                            doc: doc(
1579                                "/**\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 */",
1580                            ),
1581                        },
1582                    ),
1583                    (
1584                        "lastIndexOf".to_string(),
1585                        MethodSig {
1586                            optional: false,
1587                            generics: Vec::new(),
1588                            params: vec![
1589                                Param::new("elem", Type::TypeVar("T".to_string())),
1590                                Param::with_default(
1591                                    "fromIndex",
1592                                    Type::Number,
1593                                    crate::DefaultValue::OmittedNumber {
1594                                        omitted: f64::INFINITY,
1595                                        undefined: 0.0,
1596                                    },
1597                                ),
1598                            ],
1599                            ret: Type::Number,
1600                            predicate: None,
1601                            doc: doc(
1602                                "/**\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 */",
1603                            ),
1604                        },
1605                    ),
1606                    (
1607                        "reduceRight".to_string(),
1608                        MethodSig {
1609                            optional: false,
1610                            generics: vec!["U".to_string()],
1611                            params: vec![
1612                                Param::new(
1613                                    "callback",
1614                                    Type::Function {
1615                                        optional: 0,
1616                                        params: element_callback_params(vec![Type::TypeVar("U".to_string())]),
1617                                        ret: Box::new(Type::TypeVar("U".to_string())),
1618                                        predicate: None,
1619                                        has_rest: false,
1620                                    },
1621                                ),
1622                                Param::new("initial", Type::TypeVar("U".to_string())),
1623                            ],
1624                            ret: Type::TypeVar("U".to_string()),
1625                            predicate: None,
1626                            doc: doc(
1627                                "/**\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 */",
1628                            ),
1629                        },
1630                    ),
1631                    (
1632                        "fill".to_string(),
1633                        MethodSig {
1634                            optional: false,
1635                            generics: Vec::new(),
1636                            params: vec![
1637                                Param::new("value", Type::TypeVar("T".to_string())),
1638                                Param::with_default(
1639                                    "start",
1640                                    Type::Number,
1641                                    crate::DefaultValue::Number(0.0),
1642                                ),
1643                                Param::with_default(
1644                                    "end",
1645                                    Type::Number,
1646                                    crate::DefaultValue::Number(f64::INFINITY),
1647                                ),
1648                            ],
1649                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1650                            predicate: None,
1651                            doc: doc(
1652                                "/**\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 */",
1653                            ),
1654                        },
1655                    ),
1656                    (
1657                        "copyWithin".to_string(),
1658                        MethodSig {
1659                            optional: false,
1660                            generics: Vec::new(),
1661                            params: vec![
1662                                Param::new("target", Type::Number),
1663                                Param::with_default(
1664                                    "start",
1665                                    Type::Number,
1666                                    crate::DefaultValue::Number(0.0),
1667                                ),
1668                                Param::with_default(
1669                                    "end",
1670                                    Type::Number,
1671                                    crate::DefaultValue::Number(f64::INFINITY),
1672                                ),
1673                            ],
1674                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1675                            predicate: None,
1676                            doc: doc(
1677                                "/**\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 */",
1678                            ),
1679                        },
1680                    ),
1681                    (
1682                        "toReversed".to_string(),
1683                        MethodSig {
1684                            optional: false,
1685                            generics: Vec::new(),
1686                            params: Vec::new(),
1687                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1688                            predicate: None,
1689                            doc: doc(
1690                                "/**\n * Returns a new array with the elements in reverse order; the receiver is not modified.\n */",
1691                            ),
1692                        },
1693                    ),
1694                    (
1695                        "toSorted".to_string(),
1696                        MethodSig {
1697                            optional: false,
1698                            generics: Vec::new(),
1699                            params: vec![Param::with_default(
1700                                "compareFn",
1701                                Type::Union(vec![
1702                                    Type::Function {
1703                                        optional: 0,
1704                                        params: vec![
1705                                            Type::TypeVar("T".to_string()),
1706                                            Type::TypeVar("T".to_string()),
1707                                        ],
1708                                        ret: Box::new(Type::Number),
1709                                        predicate: None,
1710                                        has_rest: false,
1711                                    },
1712                                    Type::Undefined,
1713                                ]),
1714                                crate::DefaultValue::Undefined,
1715                            )],
1716                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1717                            predicate: None,
1718                            doc: doc(
1719                                "/**\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 */",
1720                            ),
1721                        },
1722                    ),
1723                    (
1724                        "toSpliced".to_string(),
1725                        MethodSig {
1726                            optional: false,
1727                            generics: Vec::new(),
1728                            params: vec![
1729                                Param::new("start", Type::Number),
1730                                Param::with_default(
1731                                    "deleteCount",
1732                                    Type::Number,
1733                                    crate::DefaultValue::OmittedNumber {
1734                                        omitted: f64::INFINITY,
1735                                        undefined: 0.0,
1736                                    },
1737                                ),
1738                                Param::rest(
1739                                    "items",
1740                                    Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1741                                ),
1742                            ],
1743                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1744                            predicate: None,
1745                            doc: doc(
1746                                "/**\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 */",
1747                            ),
1748                        },
1749                    ),
1750                    (
1751                        "with".to_string(),
1752                        MethodSig {
1753                            optional: false,
1754                            generics: Vec::new(),
1755                            params: vec![
1756                                Param::new("index", Type::Number),
1757                                Param::new("value", Type::TypeVar("T".to_string())),
1758                            ],
1759                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1760                            predicate: None,
1761                            doc: doc(
1762                                "/**\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 */",
1763                            ),
1764                        },
1765                    ),
1766                    (
1767                        "shift".to_string(),
1768                        MethodSig {
1769                            optional: false,
1770                            generics: Vec::new(),
1771                            params: Vec::new(),
1772                            ret: Type::Union(vec![
1773                                Type::TypeVar("T".to_string()),
1774                                Type::Undefined,
1775                            ]),
1776                            predicate: None,
1777                            doc: doc(
1778                                "/**\n * Removes and returns the first element, shifting the rest forward.\n * Returns `undefined` when the array is empty.\n */",
1779                            ),
1780                        },
1781                    ),
1782                    (
1783                        "unshift".to_string(),
1784                        MethodSig {
1785                            optional: false,
1786                            generics: Vec::new(),
1787                            params: vec![Param::rest(
1788                                "items",
1789                                Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1790                            )],
1791                            ret: Type::Number,
1792                            predicate: None,
1793                            doc: doc(
1794                                "/**\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 */",
1795                            ),
1796                        },
1797                    ),
1798                    (
1799                        "splice".to_string(),
1800                        MethodSig {
1801                            optional: false,
1802                            generics: Vec::new(),
1803                            params: vec![
1804                                Param::new("start", Type::Number),
1805                                Param::with_default(
1806                                    "deleteCount",
1807                                    Type::Number,
1808                                    crate::DefaultValue::OmittedNumber {
1809                                        omitted: f64::INFINITY,
1810                                        undefined: 0.0,
1811                                    },
1812                                ),
1813                                Param::rest(
1814                                    "items",
1815                                    Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1816                                ),
1817                            ],
1818                            ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1819                            predicate: None,
1820                            doc: doc(
1821                                "/**\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 */",
1822                            ),
1823                        },
1824                    ),
1825                ]),
1826                properties: BTreeMap::from([(
1827                    "length".to_string(),
1828                    PropertySig {
1829                        ty: Type::Number,
1830                        readonly: false,
1831                        intrinsic: true,
1832                        optional: false,
1833                        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. */"),
1834                    },
1835                )]),
1836                dispatch: Dispatch::Direct,
1837                doc: doc("/** A homogeneous array of `T`. */"),
1838            },
1839        },
1840    );
1841    defs.types.insert(
1842        "ArrayConstructor".to_string(),
1843        TypeSymbol {
1844            name: "ArrayConstructor".to_string(),
1845            mangled_name: crate::mangle::prelude("ArrayConstructor"),
1846            declaration_span: Span::at(crate::FileId::PRELUDE),
1847            kind: TypeKind::Interface { index: None,
1848                generics: Vec::new(),
1849                methods: BTreeMap::from([(
1850                    "from".to_string(),
1851                    MethodSig {
1852                        optional: false,
1853                        generics: vec!["T".to_string(), "U".to_string()],
1854                        params: vec![
1855                            Param::new(
1856                                "src",
1857                                Type::union(vec![
1858                                    Type::Readonly(Box::new(Type::Array(Box::new(Type::TypeVar("T".to_string()))))),
1859                                    Type::prelude_interface(
1860                                        "Iterator".to_string(),
1861                                        vec![Type::TypeVar("T".to_string())],
1862                                    ),
1863                                    Type::prelude_interface(
1864                                        "Iterable".to_string(),
1865                                        vec![Type::TypeVar("T".to_string())],
1866                                    ),
1867                                    array_like_type(),
1868                                ]),
1869                            ),
1870                            Param::with_default(
1871                                "mapFn",
1872                                Type::union(vec![
1873                                    Type::Function {
1874                                        optional: 0,
1875                                        params: vec![Type::TypeVar("T".to_string()), Type::Number],
1876                                        ret: Box::new(Type::TypeVar("U".to_string())),
1877                                        predicate: None,
1878                                        has_rest: false,
1879                                    },
1880                                    Type::Undefined,
1881                                ]),
1882                                crate::DefaultValue::Undefined,
1883                            ),
1884                        ],
1885                        ret: Type::Array(Box::new(Type::TypeVar("U".to_string()))),
1886                        predicate: None,
1887                        doc: doc(
1888                            "/**\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 */",
1889                        ),
1890                    },
1891                ), (
1892                    "of".to_string(),
1893                    MethodSig {
1894                        optional: false,
1895                        generics: vec!["T".to_string()],
1896                        params: vec![Param::rest(
1897                            "items",
1898                            Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1899                        )],
1900                        ret: Type::Array(Box::new(Type::TypeVar("T".to_string()))),
1901                        predicate: None,
1902                        doc: doc(
1903                            "/** Builds an array from its arguments — `Array.of(1, 2, 3)` is `[1, 2, 3]`. */",
1904                        ),
1905                    },
1906                ), (
1907                    "isArray".to_string(),
1908                    MethodSig {
1909                        optional: false,
1910                        generics: vec!["T".to_string()],
1911                        params: vec![Param::new("value", Type::TypeVar("T".to_string()))],
1912                        ret: Type::Boolean,
1913                        predicate: Some(crate::TypePredicate {
1914                            parameter_index: 0,
1915                            asserted_type: Type::Array(Box::new(Type::Unknown)),
1916                        }),
1917                        doc: doc(
1918                            "/**\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 */",
1919                        ),
1920                    },
1921                )]),
1922                properties: BTreeMap::new(),
1923                dispatch: Dispatch::Static,
1924                doc: doc(
1925                    "/** Constructor object for `Array`. Accessed via the global `Array` binding. */",
1926                ),
1927            },
1928        },
1929    );
1930
1931    defs.values.insert(
1932        "Array".to_string(),
1933        ValueSymbol {
1934            name: "Array".to_string(),
1935            mangled_name: crate::mangle::prelude("Array"),
1936            declaration_span: Span::at(crate::FileId::PRELUDE),
1937            kind: ValueKind::Const {
1938                ty: Type::prelude_interface("ArrayConstructor".to_string(), Vec::new()),
1939                doc: doc(
1940                    "/** The `Array` constructor — call `Array.isArray(x)` to runtime-check whether a value is an array. */",
1941                ),
1942            },
1943        },
1944    );
1945}