wai_bindgen_gen_js/
lib.rs

1use heck::*;
2use std::collections::{BTreeMap, BTreeSet, HashMap};
3use std::fmt::Write;
4use std::mem;
5use wai_bindgen_gen_core::wai_parser::abi::{
6    AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType,
7};
8use wai_bindgen_gen_core::{wai_parser::*, Direction, Files, Generator};
9
10#[derive(Default)]
11pub struct Js {
12    src: Source,
13    in_import: bool,
14    opts: Opts,
15    guest_imports: HashMap<String, Imports>,
16    guest_exports: HashMap<String, Exports>,
17    sizes: SizeAlign,
18    intrinsics: BTreeMap<Intrinsic, String>,
19    all_intrinsics: BTreeSet<Intrinsic>,
20    needs_get_export: bool,
21    imported_resources: BTreeSet<ResourceId>,
22    exported_resources: BTreeSet<ResourceId>,
23    needs_ty_option: bool,
24    needs_ty_result: bool,
25}
26
27#[derive(Default)]
28struct Imports {
29    freestanding_funcs: Vec<(String, Source)>,
30    resource_funcs: BTreeMap<ResourceId, Vec<(String, Source)>>,
31}
32
33#[derive(Default)]
34struct Exports {
35    freestanding_funcs: Vec<Source>,
36    resource_funcs: BTreeMap<ResourceId, Vec<Source>>,
37}
38
39#[derive(Default, Debug, Clone)]
40#[cfg_attr(feature = "structopt", derive(structopt::StructOpt))]
41pub struct Opts {
42    #[cfg_attr(feature = "structopt", structopt(long = "no-typescript"))]
43    pub no_typescript: bool,
44}
45
46impl Opts {
47    pub fn build(self) -> Js {
48        let mut r = Js::new();
49        r.opts = self;
50        r
51    }
52}
53
54#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
55enum Intrinsic {
56    ClampGuest,
57    ClampHost,
58    ClampHost64,
59    DataView,
60    ValidateGuestChar,
61    ValidateHostChar,
62    ValidateFlags,
63    ValidateFlags64,
64    /// Implementation of https://tc39.es/ecma262/#sec-tostring.
65    ToString,
66    I32ToF32,
67    F32ToI32,
68    I64ToF64,
69    F64ToI64,
70    Utf8Decoder,
71    Utf8Encode,
72    Utf8EncodedLen,
73    Slab,
74    Promises,
75    WithCurrentPromise,
76    ThrowInvalidBool,
77}
78
79impl Intrinsic {
80    fn name(&self) -> &'static str {
81        match self {
82            Intrinsic::ClampGuest => "clamp_guest",
83            Intrinsic::ClampHost => "clamp_host",
84            Intrinsic::ClampHost64 => "clamp_host64",
85            Intrinsic::DataView => "data_view",
86            Intrinsic::ValidateGuestChar => "validate_guest_char",
87            Intrinsic::ValidateHostChar => "validate_host_char",
88            Intrinsic::ValidateFlags => "validate_flags",
89            Intrinsic::ValidateFlags64 => "validate_flags64",
90            Intrinsic::ToString => "to_string",
91            Intrinsic::F32ToI32 => "f32ToI32",
92            Intrinsic::I32ToF32 => "i32ToF32",
93            Intrinsic::F64ToI64 => "f64ToI64",
94            Intrinsic::I64ToF64 => "i64ToF64",
95            Intrinsic::Utf8Decoder => "UTF8_DECODER",
96            Intrinsic::Utf8Encode => "utf8_encode",
97            Intrinsic::Utf8EncodedLen => "utf8_encoded_len",
98            Intrinsic::Slab => "Slab",
99            Intrinsic::Promises => "PROMISES",
100            Intrinsic::WithCurrentPromise => "with_current_promise",
101            Intrinsic::ThrowInvalidBool => "throw_invalid_bool",
102        }
103    }
104}
105
106impl Js {
107    pub fn new() -> Js {
108        Js::default()
109    }
110
111    fn abi_variant(dir: Direction) -> AbiVariant {
112        // This generator uses a reversed mapping! In the JS host-side
113        // bindings, we don't use any extra adapter layer between guest wasm
114        // modules and the host. When the guest imports functions using the
115        // `GuestImport` ABI, the host directly implements the `GuestImport`
116        // ABI, even though the host is *exporting* functions. Similarly, when
117        // the guest exports functions using the `GuestExport` ABI, the host
118        // directly imports them with the `GuestExport` ABI, even though the
119        // host is *importing* functions.
120        match dir {
121            Direction::Import => AbiVariant::GuestExport,
122            Direction::Export => AbiVariant::GuestImport,
123        }
124    }
125
126    fn array_ty(&self, iface: &Interface, ty: &Type) -> Option<&'static str> {
127        match ty {
128            Type::Unit | Type::Bool => None,
129            Type::U8 => Some("Uint8Array"),
130            Type::S8 => Some("Int8Array"),
131            Type::U16 => Some("Uint16Array"),
132            Type::S16 => Some("Int16Array"),
133            Type::U32 => Some("Uint32Array"),
134            Type::S32 => Some("Int32Array"),
135            Type::U64 => Some("BigUint64Array"),
136            Type::S64 => Some("BigInt64Array"),
137            Type::Float32 => Some("Float32Array"),
138            Type::Float64 => Some("Float64Array"),
139            Type::Char => None,
140            Type::Handle(_) => None,
141            Type::String => None,
142            Type::Id(id) => match &iface.types[*id].kind {
143                TypeDefKind::Type(t) => self.array_ty(iface, t),
144                _ => None,
145            },
146        }
147    }
148
149    fn print_ty(&mut self, iface: &Interface, ty: &Type) {
150        match ty {
151            Type::Unit => self.src.ts("void"),
152            Type::Bool => self.src.ts("boolean"),
153            Type::U8
154            | Type::S8
155            | Type::U16
156            | Type::S16
157            | Type::U32
158            | Type::S32
159            | Type::Float32
160            | Type::Float64 => self.src.ts("number"),
161            Type::U64 | Type::S64 => self.src.ts("bigint"),
162            Type::Char => self.src.ts("string"),
163            Type::Handle(id) => self.src.ts(&iface.resources[*id].name.to_camel_case()),
164            Type::String => self.src.ts("string"),
165            Type::Id(id) => {
166                let ty = &iface.types[*id];
167                if let Some(name) = &ty.name {
168                    return self.src.ts(&name.to_camel_case());
169                }
170                match &ty.kind {
171                    TypeDefKind::Type(t) => self.print_ty(iface, t),
172                    TypeDefKind::Tuple(t) => self.print_tuple(iface, t),
173                    TypeDefKind::Record(_) => panic!("anonymous record"),
174                    TypeDefKind::Flags(_) => panic!("anonymous flags"),
175                    TypeDefKind::Enum(_) => panic!("anonymous enum"),
176                    TypeDefKind::Union(_) => panic!("anonymous union"),
177                    TypeDefKind::Option(t) => {
178                        if self.maybe_null(iface, t) {
179                            self.needs_ty_option = true;
180                            self.src.ts("Option<");
181                            self.print_ty(iface, t);
182                            self.src.ts(">");
183                        } else {
184                            self.print_ty(iface, t);
185                            self.src.ts(" | null");
186                        }
187                    }
188                    TypeDefKind::Expected(e) => {
189                        self.needs_ty_result = true;
190                        self.src.ts("Result<");
191                        self.print_ty(iface, &e.ok);
192                        self.src.ts(", ");
193                        self.print_ty(iface, &e.err);
194                        self.src.ts(">");
195                    }
196                    TypeDefKind::Variant(_) => panic!("anonymous variant"),
197                    TypeDefKind::List(v) => self.print_list(iface, v),
198                    TypeDefKind::Future(_) => todo!("anonymous future"),
199                    TypeDefKind::Stream(_) => todo!("anonymous stream"),
200                }
201            }
202        }
203    }
204
205    fn print_list(&mut self, iface: &Interface, ty: &Type) {
206        match self.array_ty(iface, ty) {
207            Some(ty) => self.src.ts(ty),
208            None => {
209                self.print_ty(iface, ty);
210                self.src.ts("[]");
211            }
212        }
213    }
214
215    fn print_tuple(&mut self, iface: &Interface, tuple: &Tuple) {
216        self.src.ts("[");
217        for (i, ty) in tuple.types.iter().enumerate() {
218            if i > 0 {
219                self.src.ts(", ");
220            }
221            self.print_ty(iface, ty);
222        }
223        self.src.ts("]");
224    }
225
226    fn docs_raw(&mut self, docs: &str) {
227        self.src.ts("/**\n");
228        for line in docs.lines() {
229            self.src.ts(&format!(" * {}\n", line));
230        }
231        self.src.ts(" */\n");
232    }
233
234    fn docs(&mut self, docs: &Docs) {
235        match &docs.contents {
236            Some(docs) => self.docs_raw(docs),
237            None => (),
238        }
239    }
240
241    fn ts_func(&mut self, iface: &Interface, func: &Function) {
242        self.docs(&func.docs);
243
244        let mut name_printed = false;
245        if let FunctionKind::Static { .. } = &func.kind {
246            // static methods in imports are still wired up to an imported host
247            // object, but static methods on exports are actually static
248            // methods on the resource object.
249            if self.in_import {
250                name_printed = true;
251                self.src.ts(&func.name.to_mixed_case());
252            } else {
253                self.src.ts("static ");
254            }
255        }
256        if !name_printed {
257            self.src.ts(&func.item_name().to_mixed_case());
258        }
259        self.src.ts("(");
260
261        let param_start = match &func.kind {
262            FunctionKind::Freestanding => 0,
263            FunctionKind::Static { .. } if self.in_import => 0,
264            FunctionKind::Static { .. } => {
265                // the 0th argument for exported static methods will be the
266                // instantiated interface
267                self.src.ts(&iface.name.to_mixed_case());
268                self.src.ts(": ");
269                self.src.ts(&iface.name.to_camel_case());
270                if !func.params.is_empty() {
271                    self.src.ts(", ");
272                }
273                0
274            }
275            // skip the first parameter on methods which is `this`
276            FunctionKind::Method { .. } => 1,
277        };
278
279        for (i, (name, ty)) in func.params[param_start..].iter().enumerate() {
280            if i > 0 {
281                self.src.ts(", ");
282            }
283            self.src.ts(to_js_ident(&name.to_mixed_case()));
284            self.src.ts(": ");
285            self.print_ty(iface, ty);
286        }
287        self.src.ts("): ");
288        if func.is_async {
289            self.src.ts("Promise<");
290        }
291        self.print_ty(iface, &func.result);
292        if func.is_async {
293            self.src.ts(">");
294        }
295        self.src.ts(";\n");
296    }
297
298    fn intrinsic(&mut self, i: Intrinsic) -> String {
299        if let Some(name) = self.intrinsics.get(&i) {
300            return name.clone();
301        }
302        // TODO: should select a name that automatically doesn't conflict with
303        // anything else being generated.
304        self.intrinsics.insert(i, i.name().to_string());
305        return i.name().to_string();
306    }
307
308    /// Returns whether `null` is a valid value of type `ty`
309    fn maybe_null(&self, iface: &Interface, ty: &Type) -> bool {
310        self.as_nullable(iface, ty).is_some()
311    }
312
313    /// Tests whether `ty` can be represented with `null`, and if it can then
314    /// the "other type" is returned. If `Some` is returned that means that `ty`
315    /// is `null | <return>`. If `None` is returned that means that `null` can't
316    /// be used to represent `ty`.
317    fn as_nullable<'a>(&self, iface: &'a Interface, ty: &'a Type) -> Option<&'a Type> {
318        let id = match ty {
319            Type::Id(id) => *id,
320            _ => return None,
321        };
322        match &iface.types[id].kind {
323            // If `ty` points to an `option<T>`, then `ty` can be represented
324            // with `null` if `t` itself can't be represented with null. For
325            // example `option<option<u32>>` can't be represented with `null`
326            // since that's ambiguous if it's `none` or `some(none)`.
327            //
328            // Note, oddly enough, that `option<option<option<u32>>>` can be
329            // represented as `null` since:
330            //
331            // * `null` => `none`
332            // * `{ tag: "none" }` => `some(none)`
333            // * `{ tag: "some", val: null }` => `some(some(none))`
334            // * `{ tag: "some", val: 1 }` => `some(some(some(1)))`
335            //
336            // It's doubtful anyone would actually rely on that though due to
337            // how confusing it is.
338            TypeDefKind::Option(t) => {
339                if !self.maybe_null(iface, t) {
340                    Some(t)
341                } else {
342                    None
343                }
344            }
345            TypeDefKind::Type(t) => self.as_nullable(iface, t),
346            _ => None,
347        }
348    }
349}
350
351impl Generator for Js {
352    fn preprocess_one(&mut self, iface: &Interface, dir: Direction) {
353        let variant = Self::abi_variant(dir);
354        self.sizes.fill(iface);
355        self.in_import = variant == AbiVariant::GuestImport;
356    }
357
358    fn type_record(
359        &mut self,
360        iface: &Interface,
361        _id: TypeId,
362        name: &str,
363        record: &Record,
364        docs: &Docs,
365    ) {
366        self.docs(docs);
367        self.src
368            .ts(&format!("export interface {} {{\n", name.to_camel_case()));
369        for field in record.fields.iter() {
370            self.docs(&field.docs);
371            let (option_str, ty) = self
372                .as_nullable(iface, &field.ty)
373                .map_or(("", &field.ty), |ty| ("?", ty));
374            self.src
375                .ts(&format!("{}{}: ", field.name.to_mixed_case(), option_str));
376            self.print_ty(iface, ty);
377            self.src.ts(",\n");
378        }
379        self.src.ts("}\n");
380    }
381
382    fn type_tuple(
383        &mut self,
384        iface: &Interface,
385        _id: TypeId,
386        name: &str,
387        tuple: &Tuple,
388        docs: &Docs,
389    ) {
390        self.docs(docs);
391        self.src
392            .ts(&format!("export type {} = ", name.to_camel_case()));
393        self.print_tuple(iface, tuple);
394        self.src.ts(";\n");
395    }
396
397    fn type_flags(
398        &mut self,
399        _iface: &Interface,
400        _id: TypeId,
401        name: &str,
402        flags: &Flags,
403        docs: &Docs,
404    ) {
405        self.docs(docs);
406        let repr = js_flags_repr(flags);
407        let ty = repr.ty();
408        let suffix = repr.suffix();
409        self.src
410            .ts(&format!("export type {} = {ty};\n", name.to_camel_case()));
411        let name = name.to_shouty_snake_case();
412        for (i, flag) in flags.flags.iter().enumerate() {
413            let flag = flag.name.to_shouty_snake_case();
414            let ident = format!("{name}_{flag}");
415            self.src
416                .js(&format!("const {ident} = {}{suffix};\n", 1u128 << i));
417            self.src
418                .ts(&format!("export const {ident} = {}{suffix};\n", 1u128 << i));
419            self.src.export(ident);
420        }
421    }
422
423    fn type_variant(
424        &mut self,
425        iface: &Interface,
426        _id: TypeId,
427        name: &str,
428        variant: &Variant,
429        docs: &Docs,
430    ) {
431        self.docs(docs);
432        self.src
433            .ts(&format!("export type {} = ", name.to_camel_case()));
434        for (i, case) in variant.cases.iter().enumerate() {
435            if i > 0 {
436                self.src.ts(" | ");
437            }
438            self.src
439                .ts(&format!("{}_{}", name, case.name).to_camel_case());
440        }
441        self.src.ts(";\n");
442        for case in variant.cases.iter() {
443            self.docs(&case.docs);
444            self.src.ts(&format!(
445                "export interface {} {{\n",
446                format!("{}_{}", name, case.name).to_camel_case()
447            ));
448            self.src.ts("tag: \"");
449            self.src.ts(&case.name);
450            self.src.ts("\",\n");
451            if case.ty != Type::Unit {
452                self.src.ts("val: ");
453                self.print_ty(iface, &case.ty);
454                self.src.ts(",\n");
455            }
456            self.src.ts("}\n");
457        }
458    }
459
460    fn type_union(
461        &mut self,
462        iface: &Interface,
463        _id: TypeId,
464        name: &str,
465        union: &Union,
466        docs: &Docs,
467    ) {
468        self.docs(docs);
469        let name = name.to_camel_case();
470        self.src.ts(&format!("export type {name} = "));
471        for i in 0..union.cases.len() {
472            if i > 0 {
473                self.src.ts(" | ");
474            }
475            self.src.ts(&format!("{name}{i}"));
476        }
477        self.src.ts(";\n");
478        for (i, case) in union.cases.iter().enumerate() {
479            self.docs(&case.docs);
480            self.src.ts(&format!("export interface {name}{i} {{\n"));
481            self.src.ts(&format!("tag: {i},\n"));
482            self.src.ts("val: ");
483            self.print_ty(iface, &case.ty);
484            self.src.ts(",\n");
485            self.src.ts("}\n");
486        }
487    }
488
489    fn type_option(
490        &mut self,
491        iface: &Interface,
492        _id: TypeId,
493        name: &str,
494        payload: &Type,
495        docs: &Docs,
496    ) {
497        self.docs(docs);
498        let name = name.to_camel_case();
499        self.src.ts(&format!("export type {name} = "));
500        if self.maybe_null(iface, payload) {
501            self.needs_ty_option = true;
502            self.src.ts("Option<");
503            self.print_ty(iface, payload);
504            self.src.ts(">");
505        } else {
506            self.print_ty(iface, payload);
507            self.src.ts(" | null");
508        }
509        self.src.ts(";\n");
510    }
511
512    fn type_expected(
513        &mut self,
514        iface: &Interface,
515        _id: TypeId,
516        name: &str,
517        expected: &Expected,
518        docs: &Docs,
519    ) {
520        self.docs(docs);
521        let name = name.to_camel_case();
522        self.needs_ty_result = true;
523        self.src.ts(&format!("export type {name} = Result<"));
524        self.print_ty(iface, &expected.ok);
525        self.src.ts(", ");
526        self.print_ty(iface, &expected.err);
527        self.src.ts(">;\n");
528    }
529
530    fn type_enum(
531        &mut self,
532        _iface: &Interface,
533        _id: TypeId,
534        name: &str,
535        enum_: &Enum,
536        docs: &Docs,
537    ) {
538        // The complete documentation for this enum, including documentation for variants.
539        let mut complete_docs = String::new();
540
541        if let Some(docs) = &docs.contents {
542            complete_docs.push_str(docs);
543            // Add a gap before the `# Variants` section.
544            complete_docs.push('\n');
545        }
546
547        writeln!(complete_docs, "# Variants").unwrap();
548
549        for case in enum_.cases.iter() {
550            writeln!(complete_docs).unwrap();
551            writeln!(complete_docs, "## `\"{}\"`", case.name).unwrap();
552
553            if let Some(docs) = &case.docs.contents {
554                writeln!(complete_docs).unwrap();
555                complete_docs.push_str(docs);
556            }
557        }
558
559        self.docs_raw(&complete_docs);
560
561        self.src
562            .ts(&format!("export type {} = ", name.to_camel_case()));
563        for (i, case) in enum_.cases.iter().enumerate() {
564            if i != 0 {
565                self.src.ts(" | ");
566            }
567            self.src.ts(&format!("\"{}\"", case.name));
568        }
569        self.src.ts(";\n");
570    }
571
572    fn type_resource(&mut self, _iface: &Interface, ty: ResourceId) {
573        if !self.in_import {
574            self.exported_resources.insert(ty);
575        }
576    }
577
578    fn type_alias(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
579        self.docs(docs);
580        self.src
581            .ts(&format!("export type {} = ", name.to_camel_case()));
582        self.print_ty(iface, ty);
583        self.src.ts(";\n");
584    }
585
586    fn type_list(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
587        self.docs(docs);
588        self.src
589            .ts(&format!("export type {} = ", name.to_camel_case()));
590        self.print_list(iface, ty);
591        self.src.ts(";\n");
592    }
593
594    fn type_builtin(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
595        drop((iface, _id, name, ty, docs));
596    }
597
598    // As with `abi_variant` above, we're generating host-side bindings here
599    // so a user "export" uses the "guest import" ABI variant on the inside of
600    // this `Generator` implementation.
601    fn export(&mut self, iface: &Interface, func: &Function) {
602        let prev = mem::take(&mut self.src);
603
604        let sig = iface.wasm_signature(AbiVariant::GuestImport, func);
605        let params = (0..sig.params.len())
606            .map(|i| format!("arg{}", i))
607            .collect::<Vec<_>>();
608        self.src
609            .js(&format!("function({}) {{\n", params.join(", ")));
610        self.ts_func(iface, func);
611
612        let mut f = FunctionBindgen::new(self, false, params);
613        iface.call(
614            AbiVariant::GuestImport,
615            LiftLower::LiftArgsLowerResults,
616            func,
617            &mut f,
618        );
619
620        let FunctionBindgen {
621            src,
622            needs_memory,
623            needs_realloc,
624            needs_free,
625            ..
626        } = f;
627
628        if needs_memory {
629            self.needs_get_export = true;
630            // TODO: hardcoding "memory"
631            self.src.js("const memory = get_export(\"memory\");\n");
632        }
633
634        if let Some(name) = needs_realloc {
635            self.needs_get_export = true;
636            self.src
637                .js(&format!("const realloc = get_export(\"{}\");\n", name));
638        }
639
640        if let Some(name) = needs_free {
641            self.needs_get_export = true;
642            self.src
643                .js(&format!("const free = get_export(\"{}\");\n", name));
644        }
645        self.src.js(&src.js);
646
647        if func.is_async {
648            // Note that `catch_closure` here is defined by the `CallInterface`
649            // instruction.
650            self.src.js("}, catch_closure);\n"); // `.then` block
651            self.src.js("});\n"); // `with_current_promise` block.
652        }
653        self.src.js("}");
654
655        let src = mem::replace(&mut self.src, prev);
656        let imports = self
657            .guest_imports
658            .entry(iface.name.to_string())
659            .or_default();
660        let dst = match &func.kind {
661            FunctionKind::Freestanding | FunctionKind::Static { .. } => {
662                &mut imports.freestanding_funcs
663            }
664            FunctionKind::Method { resource, .. } => {
665                imports.resource_funcs.entry(*resource).or_default()
666            }
667        };
668        dst.push((func.name.to_string(), src));
669    }
670
671    // As with `abi_variant` above, we're generating host-side bindings here
672    // so a user "import" uses the "export" ABI variant on the inside of
673    // this `Generator` implementation.
674    fn import(&mut self, iface: &Interface, func: &Function) {
675        let prev = mem::take(&mut self.src);
676
677        let mut params = func
678            .params
679            .iter()
680            .enumerate()
681            .map(|(i, _)| format!("arg{}", i))
682            .collect::<Vec<_>>();
683        let mut sig_start = 0;
684        let mut first_is_operand = true;
685        let src_object = match &func.kind {
686            FunctionKind::Freestanding => "this".to_string(),
687            FunctionKind::Static { .. } => {
688                self.src.js("static ");
689                params.insert(0, iface.name.to_mixed_case());
690                first_is_operand = false;
691                iface.name.to_mixed_case()
692            }
693            FunctionKind::Method { .. } => {
694                params[0] = "this".to_string();
695                sig_start = 1;
696                "this._obj".to_string()
697            }
698        };
699        if func.is_async {
700            self.src.js("async ");
701        }
702        self.src.js(&format!(
703            "{}({}) {{\n",
704            func.item_name().to_mixed_case(),
705            params[sig_start..].join(", ")
706        ));
707        self.ts_func(iface, func);
708
709        if !first_is_operand {
710            params.remove(0);
711        }
712        let mut f = FunctionBindgen::new(self, false, params);
713        f.src_object = src_object;
714        iface.call(
715            AbiVariant::GuestExport,
716            LiftLower::LowerArgsLiftResults,
717            func,
718            &mut f,
719        );
720
721        let FunctionBindgen {
722            src,
723            needs_memory,
724            needs_realloc,
725            needs_free,
726            src_object,
727            ..
728        } = f;
729        if needs_memory {
730            // TODO: hardcoding "memory"
731            self.src
732                .js(&format!("const memory = {}._exports.memory;\n", src_object));
733        }
734
735        if let Some(name) = needs_realloc {
736            self.src.js(&format!(
737                "const realloc = {}._exports[\"{}\"];\n",
738                src_object, name
739            ));
740        }
741
742        if let Some(name) = needs_free {
743            self.src.js(&format!(
744                "const free = {}._exports[\"{}\"];\n",
745                src_object, name
746            ));
747        }
748        self.src.js(&src.js);
749        self.src.js("}\n");
750
751        let exports = self
752            .guest_exports
753            .entry(iface.name.to_string())
754            .or_insert_with(Exports::default);
755
756        let func_body = mem::replace(&mut self.src, prev);
757        match &func.kind {
758            FunctionKind::Freestanding => {
759                exports.freestanding_funcs.push(func_body);
760            }
761            FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => {
762                exports
763                    .resource_funcs
764                    .entry(*resource)
765                    .or_default()
766                    .push(func_body);
767            }
768        }
769    }
770
771    fn finish_one(&mut self, iface: &Interface, files: &mut Files) {
772        for (module, funcs) in mem::take(&mut self.guest_imports) {
773            // TODO: `module.exports` vs `export function`
774            let function_name = format!("add{}ToImports", module.to_camel_case());
775            self.src.js(&format!(
776                "function {function_name}(imports, obj{}) {{\n",
777                if self.needs_get_export {
778                    ", get_export"
779                } else {
780                    ""
781                },
782            ));
783            self.src.ts(&format!(
784                "export function add{}ToImports(imports: any, obj: {0}{}): void;\n",
785                module.to_camel_case(),
786                if self.needs_get_export {
787                    ", get_export: (name: string) => WebAssembly.ExportValue"
788                } else {
789                    ""
790                },
791            ));
792            self.src.export(function_name);
793            self.src.js(&format!(
794                "if (!(\"{0}\" in imports)) imports[\"{0}\"] = {{}};\n",
795                module,
796            ));
797
798            self.src
799                .ts(&format!("export interface {} {{\n", module.to_camel_case()));
800
801            for (name, src) in funcs
802                .freestanding_funcs
803                .iter()
804                .chain(funcs.resource_funcs.values().flatten())
805            {
806                self.src.js(&format!(
807                    "imports[\"{}\"][\"{}\"] = {};\n",
808                    module,
809                    name,
810                    src.js.trim(),
811                ));
812            }
813
814            for (_, src) in funcs.freestanding_funcs.iter() {
815                self.src.ts(&src.ts);
816            }
817
818            if !self.imported_resources.is_empty() {
819                self.src
820                    .js("if (!(\"canonical_abi\" in imports)) imports[\"canonical_abi\"] = {};\n");
821            }
822            for resource in self.imported_resources.clone() {
823                let slab = self.intrinsic(Intrinsic::Slab);
824                self.src.js(&format!(
825                    "
826                        const resources{idx} = new {slab}();
827                        imports.canonical_abi[\"resource_drop_{name}\"] = (i) => {{
828                            const val = resources{idx}.remove(i);
829                            if (obj.drop{camel})
830                                obj.drop{camel}(val);
831                        }};
832                    ",
833                    name = iface.resources[resource].name,
834                    camel = iface.resources[resource].name.to_camel_case(),
835                    idx = resource.index(),
836                    slab = slab,
837                ));
838                self.src.ts(&format!(
839                    "drop{}?: (val: {0}) => void;\n",
840                    iface.resources[resource].name.to_camel_case()
841                ));
842            }
843            self.src.js("}");
844            self.src.ts("}\n");
845
846            for (resource, _) in iface.resources.iter() {
847                self.src.ts(&format!(
848                    "export interface {} {{\n",
849                    iface.resources[resource].name.to_camel_case()
850                ));
851                if let Some(funcs) = funcs.resource_funcs.get(&resource) {
852                    for (_, src) in funcs {
853                        self.src.ts(&src.ts);
854                    }
855                }
856                self.src.ts("}\n");
857            }
858        }
859        let imports = mem::take(&mut self.src);
860
861        for (module, exports) in mem::take(&mut self.guest_exports) {
862            let module = module.to_camel_case();
863            self.src.ts(&format!("export class {} {{\n", module));
864            self.src.js(&format!("class {} {{\n", module));
865            self.src.export(module);
866
867            self.src.ts("
868               /**
869                * The WebAssembly instance that this class is operating with.
870                * This is only available after the `instantiate` method has
871                * been called.
872                */
873                instance: WebAssembly.Instance;
874            ");
875
876            self.src.ts("
877               /**
878                * Constructs a new instance with internal state necessary to
879                * manage a wasm instance.
880                *
881                * Note that this does not actually instantiate the WebAssembly
882                * instance or module, you'll need to call the `instantiate`
883                * method below to \"activate\" this class.
884                */
885                constructor();
886            ");
887            if !self.exported_resources.is_empty() {
888                self.src.js("constructor() {\n");
889                let slab = self.intrinsic(Intrinsic::Slab);
890                for r in self.exported_resources.iter() {
891                    self.src.js(&format!(
892                        "this._resource{}_slab = new {}();\n",
893                        r.index(),
894                        slab
895                    ));
896                }
897                self.src.js("}\n");
898            }
899
900            self.src.ts("
901               /**
902                * This is a low-level method which can be used to add any
903                * intrinsics necessary for this instance to operate to an
904                * import object.
905                *
906                * The `import` object given here is expected to be used later
907                * to actually instantiate the module this class corresponds to.
908                * If the `instantiate` method below actually does the
909                * instantiation then there's no need to call this method, but
910                * if you're instantiating manually elsewhere then this can be
911                * used to prepare the import object for external instantiation.
912                */
913                addToImports(imports: any): void;
914            ");
915            self.src.js("addToImports(imports) {\n");
916            let any_async = iface.functions.iter().any(|f| f.is_async);
917            if !self.exported_resources.is_empty() || any_async {
918                self.src
919                    .js("if (!(\"canonical_abi\" in imports)) imports[\"canonical_abi\"] = {};\n");
920            }
921            for r in self.exported_resources.iter() {
922                self.src.js(&format!(
923                    "
924                        imports.canonical_abi['resource_drop_{name}'] = i => {{
925                            this._resource{idx}_slab.remove(i).drop();
926                        }};
927                        imports.canonical_abi['resource_clone_{name}'] = i => {{
928                            const obj = this._resource{idx}_slab.get(i);
929                            return this._resource{idx}_slab.insert(obj.clone())
930                        }};
931                        imports.canonical_abi['resource_get_{name}'] = i => {{
932                            return this._resource{idx}_slab.get(i)._wasm_val;
933                        }};
934                        imports.canonical_abi['resource_new_{name}'] = i => {{
935                            const registry = this._registry{idx};
936                            return this._resource{idx}_slab.insert(new {class}(i, this));
937                        }};
938                    ",
939                    name = iface.resources[*r].name,
940                    idx = r.index(),
941                    class = iface.resources[*r].name.to_camel_case(),
942                ));
943            }
944            if any_async {
945                let promises = self.intrinsic(Intrinsic::Promises);
946                self.src.js(&format!(
947                    "
948                        imports.canonical_abi['async_export_done'] = (ctx, ptr) => {{
949                            {}.remove(ctx)(ptr >>> 0)
950                        }};
951                    ",
952                    promises
953                ));
954            }
955            self.src.js("}\n");
956
957            self.src.ts("
958                   /**
959                    * Initializes this object with the provided WebAssembly
960                    * module/instance.
961                    *
962                    * This is intended to be a flexible method of instantiating
963                    * and completion of the initialization of this class. This
964                    * method must be called before interacting with the
965                    * WebAssembly object.
966                    *
967                    * The first argument to this method is where to get the
968                    * wasm from. This can be a whole bunch of different types,
969                    * for example:
970                    *
971                    * * A precompiled `WebAssembly.Module`
972                    * * A typed array buffer containing the wasm bytecode.
973                    * * A `Promise` of a `Response` which is used with
974                    *   `instantiateStreaming`
975                    * * A `Response` itself used with `instantiateStreaming`.
976                    * * An already instantiated `WebAssembly.Instance`
977                    *
978                    * If necessary the module is compiled, and if necessary the
979                    * module is instantiated. Whether or not it's necessary
980                    * depends on the type of argument provided to
981                    * instantiation.
982                    *
983                    * If instantiation is performed then the `imports` object
984                    * passed here is the list of imports used to instantiate
985                    * the instance. This method may add its own intrinsics to
986                    * this `imports` object too.
987                    */
988                    instantiate(
989                        module: WebAssembly.Module | BufferSource | Promise<Response> | Response | WebAssembly.Instance,
990                        imports?: any,
991                    ): Promise<void>;
992                ");
993            self.src.js("
994                async instantiate(module, imports) {
995                    imports = imports || {};
996                    this.addToImports(imports);
997            ");
998
999            // With intrinsics prep'd we can now instantiate the module. JS has
1000            // a ... variety of methods of instantiation, so we basically just
1001            // try to be flexible here.
1002            self.src.js("
1003                if (module instanceof WebAssembly.Instance) {
1004                    this.instance = module;
1005                } else if (module instanceof WebAssembly.Module) {
1006                    this.instance = await WebAssembly.instantiate(module, imports);
1007                } else if (module instanceof ArrayBuffer || module instanceof Uint8Array) {
1008                    const { instance } = await WebAssembly.instantiate(module, imports);
1009                    this.instance = instance;
1010                } else {
1011                    const { instance } = await WebAssembly.instantiateStreaming(module, imports);
1012                    this.instance = instance;
1013                }
1014                this._exports = this.instance.exports;
1015            ");
1016
1017            // Exported resources all get a finalization registry, and we
1018            // created them after instantiation so we can pass the raw wasm
1019            // export as the destructor callback.
1020            for r in self.exported_resources.iter() {
1021                self.src.js(&format!(
1022                    "this._registry{} = new FinalizationRegistry(this._exports['canonical_abi_drop_{}']);\n",
1023                    r.index(),
1024                    iface.resources[*r].name,
1025                ));
1026            }
1027            self.src.js("}\n");
1028
1029            for func in exports.freestanding_funcs.iter() {
1030                self.src.js(&func.js);
1031                self.src.ts(&func.ts);
1032            }
1033            self.src.ts("}\n");
1034            self.src.js("}\n");
1035
1036            for &ty in self.exported_resources.iter() {
1037                let class_name = iface.resources[ty].name.to_camel_case();
1038                self.src.js(&format!(
1039                    "
1040                        class {class_name} {{
1041                            constructor(wasm_val, obj) {{
1042                                this._wasm_val = wasm_val;
1043                                this._obj = obj;
1044                                this._refcnt = 1;
1045                                obj._registry{idx}.register(this, wasm_val, this);
1046                            }}
1047
1048                            clone() {{
1049                                this._refcnt += 1;
1050                                return this;
1051                            }}
1052
1053                            drop() {{
1054                                this._refcnt -= 1;
1055                                if (this._refcnt !== 0)
1056                                    return;
1057                                this._obj._registry{idx}.unregister(this);
1058                                const dtor = this._obj._exports['canonical_abi_drop_{}'];
1059                                const wasm_val = this._wasm_val;
1060                                delete this._obj;
1061                                delete this._refcnt;
1062                                delete this._wasm_val;
1063                                dtor(wasm_val);
1064                            }}
1065                    ",
1066                    iface.resources[ty].name,
1067                    idx = ty.index(),
1068                ));
1069                self.src.ts(&format!(
1070                    "
1071                        export class {class_name} {{
1072                            // Creates a new strong reference count as a new
1073                            // object.  This is only required if you're also
1074                            // calling `drop` below and want to manually manage
1075                            // the reference count from JS.
1076                            //
1077                            // If you don't call `drop`, you don't need to call
1078                            // this and can simply use the object from JS.
1079                            clone(): {class_name};
1080
1081                            // Explicitly indicate that this JS object will no
1082                            // longer be used. If the internal reference count
1083                            // reaches zero then this will deterministically
1084                            // destroy the underlying wasm object.
1085                            //
1086                            // This is not required to be called from JS. Wasm
1087                            // destructors will be automatically called for you
1088                            // if this is not called using the JS
1089                            // `FinalizationRegistry`.
1090                            //
1091                            // Calling this method does not guarantee that the
1092                            // underlying wasm object is deallocated. Something
1093                            // else (including wasm) may be holding onto a
1094                            // strong reference count.
1095                            drop(): void;
1096                    ",
1097                ));
1098                self.src.export(class_name);
1099
1100                if let Some(funcs) = exports.resource_funcs.get(&ty) {
1101                    for func in funcs {
1102                        self.src.js(&func.js);
1103                        self.src.ts(&func.ts);
1104                    }
1105                }
1106
1107                self.src.ts("}\n");
1108                self.src.js("}\n");
1109            }
1110        }
1111
1112        let exports = mem::take(&mut self.src);
1113
1114        if mem::take(&mut self.needs_ty_option) {
1115            self.src
1116                .ts("export type Option<T> = { tag: \"none\" } | { tag: \"some\", val; T };\n");
1117        }
1118        if mem::take(&mut self.needs_ty_result) {
1119            self.src.ts(
1120                "export type Result<T, E> = { tag: \"ok\", val: T } | { tag: \"err\", val: E };\n",
1121            );
1122        }
1123
1124        if !self.intrinsics.is_empty() {
1125            self.src.js("const { ");
1126            for (i, (intrinsic, name)) in mem::take(&mut self.intrinsics).into_iter().enumerate() {
1127                if i > 0 {
1128                    self.src.js(", ");
1129                }
1130                self.src.js(intrinsic.name());
1131                if intrinsic.name() != name {
1132                    self.src.js(" as ");
1133                    self.src.js(&name);
1134                }
1135                self.all_intrinsics.insert(intrinsic);
1136            }
1137            self.src.js(" } = require('./intrinsics.js');\n");
1138        }
1139
1140        self.src.js(&imports.js);
1141        self.src.ts(&imports.ts);
1142        self.src.js(&exports.js);
1143        self.src.ts(&exports.ts);
1144
1145        let mut exported_items = Vec::new();
1146        exported_items.extend(exports.exported_items);
1147        exported_items.extend(imports.exported_items);
1148
1149        self.src.js(&format!(
1150            "\nmodule.exports = {{ {} }};\n",
1151            exported_items.join(", ")
1152        ));
1153
1154        let src = mem::take(&mut self.src);
1155        let name = iface.name.to_kebab_case();
1156        files.push(&format!("{}.js", name), src.js.as_bytes());
1157        if !self.opts.no_typescript {
1158            files.push(&format!("{}.d.ts", name), src.ts.as_bytes());
1159        }
1160    }
1161
1162    fn finish_all(&mut self, files: &mut Files) {
1163        assert!(self.src.ts.is_empty());
1164        assert!(self.src.js.is_empty());
1165        self.print_intrinsics();
1166        assert!(self.src.ts.is_empty());
1167        files.push("intrinsics.js", self.src.js.as_bytes());
1168    }
1169}
1170
1171struct FunctionBindgen<'a> {
1172    gen: &'a mut Js,
1173    tmp: usize,
1174    src: Source,
1175    block_storage: Vec<wai_bindgen_gen_core::Source>,
1176    blocks: Vec<(String, Vec<String>)>,
1177    in_import: bool,
1178    needs_memory: bool,
1179    needs_realloc: Option<String>,
1180    needs_free: Option<String>,
1181    params: Vec<String>,
1182    src_object: String,
1183}
1184
1185impl FunctionBindgen<'_> {
1186    fn new(gen: &mut Js, in_import: bool, params: Vec<String>) -> FunctionBindgen<'_> {
1187        FunctionBindgen {
1188            gen,
1189            tmp: 0,
1190            src: Source::default(),
1191            block_storage: Vec::new(),
1192            blocks: Vec::new(),
1193            in_import,
1194            needs_memory: false,
1195            needs_realloc: None,
1196            needs_free: None,
1197            params,
1198            src_object: "this".to_string(),
1199        }
1200    }
1201
1202    fn tmp(&mut self) -> usize {
1203        let ret = self.tmp;
1204        self.tmp += 1;
1205        ret
1206    }
1207
1208    fn clamp_guest<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
1209    where
1210        T: std::fmt::Display,
1211    {
1212        let clamp = self.gen.intrinsic(Intrinsic::ClampGuest);
1213        results.push(format!("{}({}, {}, {})", clamp, operands[0], min, max));
1214    }
1215
1216    fn clamp_host<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
1217    where
1218        T: std::fmt::Display,
1219    {
1220        let clamp = self.gen.intrinsic(Intrinsic::ClampHost);
1221        results.push(format!("{}({}, {}, {})", clamp, operands[0], min, max));
1222    }
1223
1224    fn clamp_host64<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
1225    where
1226        T: std::fmt::Display,
1227    {
1228        let clamp = self.gen.intrinsic(Intrinsic::ClampHost64);
1229        results.push(format!("{}({}, {}n, {}n)", clamp, operands[0], min, max));
1230    }
1231
1232    fn load(&mut self, method: &str, offset: i32, operands: &[String], results: &mut Vec<String>) {
1233        self.needs_memory = true;
1234        let view = self.gen.intrinsic(Intrinsic::DataView);
1235        results.push(format!(
1236            "{}(memory).{}({} + {}, true)",
1237            view, method, operands[0], offset,
1238        ));
1239    }
1240
1241    fn store(&mut self, method: &str, offset: i32, operands: &[String]) {
1242        self.needs_memory = true;
1243        let view = self.gen.intrinsic(Intrinsic::DataView);
1244        self.src.js(&format!(
1245            "{}(memory).{}({} + {}, {}, true);\n",
1246            view, method, operands[1], offset, operands[0]
1247        ));
1248    }
1249
1250    fn bind_results(&mut self, amt: usize, results: &mut Vec<String>) {
1251        match amt {
1252            0 => {}
1253            1 => {
1254                self.src.js("const ret = ");
1255                results.push("ret".to_string());
1256            }
1257            n => {
1258                self.src.js("const [");
1259                for i in 0..n {
1260                    if i > 0 {
1261                        self.src.js(", ");
1262                    }
1263                    self.src.js(&format!("ret{}", i));
1264                    results.push(format!("ret{}", i));
1265                }
1266                self.src.js("] = ");
1267            }
1268        }
1269    }
1270}
1271
1272impl Bindgen for FunctionBindgen<'_> {
1273    type Operand = String;
1274
1275    fn sizes(&self) -> &SizeAlign {
1276        &self.gen.sizes
1277    }
1278
1279    fn push_block(&mut self) {
1280        let prev = mem::take(&mut self.src.js);
1281        self.block_storage.push(prev);
1282    }
1283
1284    fn finish_block(&mut self, operands: &mut Vec<String>) {
1285        let to_restore = self.block_storage.pop().unwrap();
1286        let src = mem::replace(&mut self.src.js, to_restore);
1287        self.blocks.push((src.into(), mem::take(operands)));
1288    }
1289
1290    fn return_pointer(&mut self, _iface: &Interface, _size: usize, _align: usize) -> String {
1291        unimplemented!()
1292    }
1293
1294    fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool {
1295        self.gen.array_ty(iface, ty).is_some()
1296    }
1297
1298    fn emit(
1299        &mut self,
1300        iface: &Interface,
1301        inst: &Instruction<'_>,
1302        operands: &mut Vec<String>,
1303        results: &mut Vec<String>,
1304    ) {
1305        match inst {
1306            Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
1307            Instruction::I32Const { val } => results.push(val.to_string()),
1308            Instruction::ConstZero { tys } => {
1309                for t in tys.iter() {
1310                    match t {
1311                        WasmType::I64 => results.push("0n".to_string()),
1312                        WasmType::I32 | WasmType::F32 | WasmType::F64 => {
1313                            results.push("0".to_string());
1314                        }
1315                    }
1316                }
1317            }
1318
1319            // The representation of i32 in JS is a number, so 8/16-bit values
1320            // get further clamped to ensure that the upper bits aren't set when
1321            // we pass the value, ensuring that only the right number of bits
1322            // are transferred.
1323            Instruction::U8FromI32 => self.clamp_guest(results, operands, u8::MIN, u8::MAX),
1324            Instruction::S8FromI32 => self.clamp_guest(results, operands, i8::MIN, i8::MAX),
1325            Instruction::U16FromI32 => self.clamp_guest(results, operands, u16::MIN, u16::MAX),
1326            Instruction::S16FromI32 => self.clamp_guest(results, operands, i16::MIN, i16::MAX),
1327            // Use `>>>0` to ensure the bits of the number are treated as
1328            // unsigned.
1329            Instruction::U32FromI32 => {
1330                results.push(format!("{} >>> 0", operands[0]));
1331            }
1332            // All bigints coming from wasm are treated as signed, so convert
1333            // it to ensure it's treated as unsigned.
1334            Instruction::U64FromI64 => results.push(format!("BigInt.asUintN(64, {})", operands[0])),
1335            // Nothing to do signed->signed where the representations are the
1336            // same.
1337            Instruction::S32FromI32 | Instruction::S64FromI64 => {
1338                results.push(operands.pop().unwrap())
1339            }
1340
1341            // All values coming from the host and going to wasm need to have
1342            // their ranges validated, since the host could give us any value.
1343            Instruction::I32FromU8 => self.clamp_host(results, operands, u8::MIN, u8::MAX),
1344            Instruction::I32FromS8 => self.clamp_host(results, operands, i8::MIN, i8::MAX),
1345            Instruction::I32FromU16 => self.clamp_host(results, operands, u16::MIN, u16::MAX),
1346            Instruction::I32FromS16 => self.clamp_host(results, operands, i16::MIN, i16::MAX),
1347            Instruction::I32FromU32 => {
1348                self.clamp_host(results, operands, u32::MIN, u32::MAX);
1349            }
1350            Instruction::I32FromS32 => self.clamp_host(results, operands, i32::MIN, i32::MAX),
1351            Instruction::I64FromU64 => self.clamp_host64(results, operands, u64::MIN, u64::MAX),
1352            Instruction::I64FromS64 => self.clamp_host64(results, operands, i64::MIN, i64::MAX),
1353
1354            // The native representation in JS of f32 and f64 is just a number,
1355            // so there's nothing to do here. Everything wasm gives us is
1356            // representable in JS.
1357            Instruction::Float32FromF32 | Instruction::Float64FromF64 => {
1358                results.push(operands.pop().unwrap())
1359            }
1360
1361            Instruction::F32FromFloat32 | Instruction::F64FromFloat64 => {
1362                // Use a unary `+` to cast to a float.
1363                results.push(format!("+{}", operands[0]));
1364            }
1365
1366            // Validate that i32 values coming from wasm are indeed valid code
1367            // points.
1368            Instruction::CharFromI32 => {
1369                let validate = self.gen.intrinsic(Intrinsic::ValidateGuestChar);
1370                results.push(format!("{}({})", validate, operands[0]));
1371            }
1372
1373            // Validate that strings are indeed 1 character long and valid
1374            // unicode.
1375            Instruction::I32FromChar => {
1376                let validate = self.gen.intrinsic(Intrinsic::ValidateHostChar);
1377                results.push(format!("{}({})", validate, operands[0]));
1378            }
1379
1380            Instruction::Bitcasts { casts } => {
1381                for (cast, op) in casts.iter().zip(operands) {
1382                    match cast {
1383                        Bitcast::I32ToF32 => {
1384                            let cvt = self.gen.intrinsic(Intrinsic::I32ToF32);
1385                            results.push(format!("{}({})", cvt, op));
1386                        }
1387                        Bitcast::F32ToI32 => {
1388                            let cvt = self.gen.intrinsic(Intrinsic::F32ToI32);
1389                            results.push(format!("{}({})", cvt, op));
1390                        }
1391                        Bitcast::I64ToF64 => {
1392                            let cvt = self.gen.intrinsic(Intrinsic::I64ToF64);
1393                            results.push(format!("{}({})", cvt, op));
1394                        }
1395                        Bitcast::F64ToI64 => {
1396                            let cvt = self.gen.intrinsic(Intrinsic::F64ToI64);
1397                            results.push(format!("{}({})", cvt, op));
1398                        }
1399                        Bitcast::I32ToI64 => results.push(format!("BigInt({})", op)),
1400                        Bitcast::I64ToI32 => results.push(format!("Number({})", op)),
1401                        Bitcast::I64ToF32 => {
1402                            let cvt = self.gen.intrinsic(Intrinsic::I32ToF32);
1403                            results.push(format!("{}(Number({}))", cvt, op));
1404                        }
1405                        Bitcast::F32ToI64 => {
1406                            let cvt = self.gen.intrinsic(Intrinsic::F32ToI32);
1407                            results.push(format!("BigInt({}({}))", cvt, op));
1408                        }
1409                        Bitcast::None => results.push(op.clone()),
1410                    }
1411                }
1412            }
1413
1414            Instruction::UnitLower => {}
1415            Instruction::UnitLift => {
1416                results.push("undefined".to_string());
1417            }
1418
1419            Instruction::BoolFromI32 => {
1420                let tmp = self.tmp();
1421                self.src
1422                    .js(&format!("const bool{} = {};\n", tmp, operands[0]));
1423                let throw = self.gen.intrinsic(Intrinsic::ThrowInvalidBool);
1424                results.push(format!(
1425                    "bool{tmp} == 0 ? false : (bool{tmp} == 1 ? true : {throw}())"
1426                ));
1427            }
1428            Instruction::I32FromBool => {
1429                results.push(format!("{} ? 1 : 0", operands[0]));
1430            }
1431
1432            // These instructions are used with handles when we're implementing
1433            // imports. This means we interact with the `resources` slabs to
1434            // translate the wasm-provided index into a JS value.
1435            Instruction::I32FromOwnedHandle { ty } => {
1436                self.gen.imported_resources.insert(*ty);
1437                results.push(format!("resources{}.insert({})", ty.index(), operands[0]));
1438            }
1439            Instruction::HandleBorrowedFromI32 { ty } => {
1440                self.gen.imported_resources.insert(*ty);
1441                results.push(format!("resources{}.get({})", ty.index(), operands[0]));
1442            }
1443
1444            // These instructions are used for handles to objects owned in wasm.
1445            // This means that they're interacting with a wrapper class defined
1446            // in JS.
1447            Instruction::I32FromBorrowedHandle { ty } => {
1448                let tmp = self.tmp();
1449                self.src
1450                    .js(&format!("const obj{} = {};\n", tmp, operands[0]));
1451
1452                // If this is the `this` argument then it's implicitly already valid
1453                if operands[0] != "this" {
1454                    self.src.js(&format!(
1455                        "if (!(obj{} instanceof {})) ",
1456                        tmp,
1457                        iface.resources[*ty].name.to_camel_case()
1458                    ));
1459                    self.src.js(&format!(
1460                        "throw new TypeError('expected instance of {}');\n",
1461                        iface.resources[*ty].name.to_camel_case()
1462                    ));
1463                }
1464                results.push(format!(
1465                    "{}._resource{}_slab.insert(obj{}.clone())",
1466                    self.src_object,
1467                    ty.index(),
1468                    tmp,
1469                ));
1470            }
1471            Instruction::HandleOwnedFromI32 { ty } => {
1472                results.push(format!(
1473                    "{}._resource{}_slab.remove({})",
1474                    self.src_object,
1475                    ty.index(),
1476                    operands[0],
1477                ));
1478            }
1479
1480            Instruction::RecordLower { record, .. } => {
1481                // use destructuring field access to get each
1482                // field individually.
1483                let tmp = self.tmp();
1484                let mut expr = "const {".to_string();
1485                for (i, field) in record.fields.iter().enumerate() {
1486                    if i > 0 {
1487                        expr.push_str(", ");
1488                    }
1489                    let name = format!("v{}_{}", tmp, i);
1490                    expr.push_str(&field.name.to_mixed_case());
1491                    expr.push_str(": ");
1492                    expr.push_str(&name);
1493                    results.push(name);
1494                }
1495                self.src.js(&format!("{} }} = {};\n", expr, operands[0]));
1496            }
1497
1498            Instruction::RecordLift { record, .. } => {
1499                // records are represented as plain objects, so we
1500                // make a new object and set all the fields with an object
1501                // literal.
1502                let mut result = "{\n".to_string();
1503                for (field, op) in record.fields.iter().zip(operands) {
1504                    result.push_str(&format!("{}: {},\n", field.name.to_mixed_case(), op));
1505                }
1506                result.push('}');
1507                results.push(result);
1508            }
1509
1510            Instruction::TupleLower { tuple, .. } => {
1511                // Tuples are represented as an array, sowe can use
1512                // destructuring assignment to lower the tuple into its
1513                // components.
1514                let tmp = self.tmp();
1515                let mut expr = "const [".to_string();
1516                for i in 0..tuple.types.len() {
1517                    if i > 0 {
1518                        expr.push_str(", ");
1519                    }
1520                    let name = format!("tuple{}_{}", tmp, i);
1521                    expr.push_str(&name);
1522                    results.push(name);
1523                }
1524                self.src.js(&format!("{}] = {};\n", expr, operands[0]));
1525            }
1526
1527            Instruction::TupleLift { .. } => {
1528                // Tuples are represented as an array, so we just shove all
1529                // the operands into an array.
1530                results.push(format!("[{}]", operands.join(", ")));
1531            }
1532
1533            Instruction::FlagsLower { flags, .. } => {
1534                let repr = js_flags_repr(flags);
1535                let validate = match repr {
1536                    JsFlagsRepr::Number => self.gen.intrinsic(Intrinsic::ValidateFlags),
1537                    JsFlagsRepr::Bigint => self.gen.intrinsic(Intrinsic::ValidateFlags64),
1538                };
1539                let op0 = &operands[0];
1540                let len = flags.flags.len();
1541                let n = repr.suffix();
1542                let tmp = self.tmp();
1543                let mask = (1u128 << len) - 1;
1544                self.src.js(&format!(
1545                    "const flags{tmp} = {validate}({op0}, {mask}{n});\n"
1546                ));
1547                match repr {
1548                    JsFlagsRepr::Number => {
1549                        results.push(format!("flags{}", tmp));
1550                    }
1551                    JsFlagsRepr::Bigint => {
1552                        for i in 0..flags.repr().count() {
1553                            let i = 32 * i;
1554                            results.push(format!("Number((flags{tmp} >> {i}n) & 0xffffffffn)",));
1555                        }
1556                    }
1557                }
1558            }
1559
1560            Instruction::FlagsLift { flags, .. } => {
1561                let repr = js_flags_repr(flags);
1562                let n = repr.suffix();
1563                let tmp = self.tmp();
1564                let operand = match repr {
1565                    JsFlagsRepr::Number => operands[0].clone(),
1566                    JsFlagsRepr::Bigint => {
1567                        self.src.js(&format!("let flags{tmp} = 0n;\n"));
1568                        for (i, op) in operands.iter().enumerate() {
1569                            let i = 32 * i;
1570                            self.src
1571                                .js(&format!("flags{tmp} |= BigInt({op}) << {i}n;\n",));
1572                        }
1573                        format!("flags{tmp}")
1574                    }
1575                };
1576                let validate = match repr {
1577                    JsFlagsRepr::Number => self.gen.intrinsic(Intrinsic::ValidateFlags),
1578                    JsFlagsRepr::Bigint => self.gen.intrinsic(Intrinsic::ValidateFlags64),
1579                };
1580                let len = flags.flags.len();
1581                let mask = (1u128 << len) - 1;
1582                results.push(format!("{validate}({operand}, {mask}{n})"));
1583            }
1584
1585            Instruction::VariantPayloadName => results.push("e".to_string()),
1586
1587            Instruction::VariantLower {
1588                variant,
1589                results: result_types,
1590                name,
1591                ..
1592            } => {
1593                let blocks = self
1594                    .blocks
1595                    .drain(self.blocks.len() - variant.cases.len()..)
1596                    .collect::<Vec<_>>();
1597                let tmp = self.tmp();
1598                self.src
1599                    .js(&format!("const variant{} = {};\n", tmp, operands[0]));
1600
1601                for i in 0..result_types.len() {
1602                    self.src.js(&format!("let variant{}_{};\n", tmp, i));
1603                    results.push(format!("variant{}_{}", tmp, i));
1604                }
1605
1606                let expr_to_match = format!("variant{}.tag", tmp);
1607
1608                self.src.js(&format!("switch ({}) {{\n", expr_to_match));
1609                for (case, (block, block_results)) in variant.cases.iter().zip(blocks) {
1610                    self.src
1611                        .js(&format!("case \"{}\": {{\n", case.name.as_str()));
1612                    if case.ty != Type::Unit {
1613                        self.src.js(&format!("const e = variant{}.val;\n", tmp));
1614                    }
1615                    self.src.js(&block);
1616
1617                    for (i, result) in block_results.iter().enumerate() {
1618                        self.src
1619                            .js(&format!("variant{}_{} = {};\n", tmp, i, result));
1620                    }
1621                    self.src.js("break;\n}\n");
1622                }
1623                let variant_name = name.to_camel_case();
1624                self.src.js("default:\n");
1625                self.src.js(&format!(
1626                    "throw new RangeError(\"invalid variant specified for {}\");\n",
1627                    variant_name
1628                ));
1629                self.src.js("}\n");
1630            }
1631
1632            Instruction::VariantLift { variant, name, .. } => {
1633                let blocks = self
1634                    .blocks
1635                    .drain(self.blocks.len() - variant.cases.len()..)
1636                    .collect::<Vec<_>>();
1637
1638                let tmp = self.tmp();
1639
1640                self.src.js(&format!("let variant{};\n", tmp));
1641                self.src.js(&format!("switch ({}) {{\n", operands[0]));
1642                for (i, (case, (block, block_results))) in
1643                    variant.cases.iter().zip(blocks).enumerate()
1644                {
1645                    self.src.js(&format!("case {}: {{\n", i));
1646                    self.src.js(&block);
1647
1648                    self.src.js(&format!("variant{} = {{\n", tmp));
1649                    self.src.js(&format!("tag: \"{}\",\n", case.name.as_str()));
1650                    assert!(block_results.len() == 1);
1651                    if case.ty != Type::Unit {
1652                        self.src.js(&format!("val: {},\n", block_results[0]));
1653                    } else {
1654                        assert_eq!(block_results[0], "undefined");
1655                    }
1656                    self.src.js("};\n");
1657                    self.src.js("break;\n}\n");
1658                }
1659                let variant_name = name.to_camel_case();
1660                self.src.js("default:\n");
1661                self.src.js(&format!(
1662                    "throw new RangeError(\"invalid variant discriminant for {}\");\n",
1663                    variant_name
1664                ));
1665                self.src.js("}\n");
1666                results.push(format!("variant{}", tmp));
1667            }
1668
1669            Instruction::UnionLower {
1670                union,
1671                results: result_types,
1672                name,
1673                ..
1674            } => {
1675                let blocks = self
1676                    .blocks
1677                    .drain(self.blocks.len() - union.cases.len()..)
1678                    .collect::<Vec<_>>();
1679                let tmp = self.tmp();
1680                let op0 = &operands[0];
1681                self.src.js(&format!("const union{tmp} = {op0};\n"));
1682
1683                for i in 0..result_types.len() {
1684                    self.src.js(&format!("let union{tmp}_{i};\n"));
1685                    results.push(format!("union{tmp}_{i}"));
1686                }
1687
1688                self.src.js(&format!("switch (union{tmp}.tag) {{\n"));
1689                for (i, (_case, (block, block_results))) in
1690                    union.cases.iter().zip(blocks).enumerate()
1691                {
1692                    self.src.js(&format!("case {i}: {{\n"));
1693                    self.src.js(&format!("const e = union{tmp}.val;\n"));
1694                    self.src.js(&block);
1695                    for (i, result) in block_results.iter().enumerate() {
1696                        self.src.js(&format!("union{tmp}_{i} = {result};\n"));
1697                    }
1698                    self.src.js("break;\n}\n");
1699                }
1700                let name = name.to_camel_case();
1701                self.src.js("default:\n");
1702                self.src.js(&format!(
1703                    "throw new RangeError(\"invalid union specified for {name}\");\n",
1704                ));
1705                self.src.js("}\n");
1706            }
1707
1708            Instruction::UnionLift { union, name, .. } => {
1709                let blocks = self
1710                    .blocks
1711                    .drain(self.blocks.len() - union.cases.len()..)
1712                    .collect::<Vec<_>>();
1713
1714                let tmp = self.tmp();
1715
1716                self.src.js(&format!("let union{tmp};\n"));
1717                self.src.js(&format!("switch ({}) {{\n", operands[0]));
1718                for (i, (_case, (block, block_results))) in
1719                    union.cases.iter().zip(blocks).enumerate()
1720                {
1721                    assert!(block_results.len() == 1);
1722                    let block_result = &block_results[0];
1723                    self.src.js(&format!(
1724                        "case {i}: {{
1725                            {block}
1726                            union{tmp} = {{
1727                                tag: {i},
1728                                val: {block_result},
1729                            }};
1730                            break;
1731                        }}\n"
1732                    ));
1733                }
1734                let name = name.to_camel_case();
1735                self.src.js("default:\n");
1736                self.src.js(&format!(
1737                    "throw new RangeError(\"invalid union discriminant for {name}\");\n",
1738                ));
1739                self.src.js("}\n");
1740                results.push(format!("union{tmp}"));
1741            }
1742
1743            Instruction::OptionLower {
1744                payload,
1745                results: result_types,
1746                ..
1747            } => {
1748                let (mut some, some_results) = self.blocks.pop().unwrap();
1749                let (mut none, none_results) = self.blocks.pop().unwrap();
1750
1751                let tmp = self.tmp();
1752                self.src
1753                    .js(&format!("const variant{tmp} = {};\n", operands[0]));
1754
1755                for i in 0..result_types.len() {
1756                    self.src.js(&format!("let variant{tmp}_{i};\n"));
1757                    results.push(format!("variant{tmp}_{i}"));
1758
1759                    let some_result = &some_results[i];
1760                    let none_result = &none_results[i];
1761                    some.push_str(&format!("variant{tmp}_{i} = {some_result};\n"));
1762                    none.push_str(&format!("variant{tmp}_{i} = {none_result};\n"));
1763                }
1764
1765                if self.gen.maybe_null(iface, payload) {
1766                    self.src.js(&format!(
1767                        "
1768                        switch (variant{tmp}.tag) {{
1769                            case \"none\": {{
1770                                {none}
1771                                break;
1772                            }}
1773                            case \"some\": {{
1774                                const e = variant{tmp}.val;
1775                                {some}
1776                                break;
1777                            }}
1778                            default: {{
1779                                throw new RangeError(\"invalid variant specified for option\");
1780                            }}
1781                        }}
1782                        "
1783                    ));
1784                } else {
1785                    self.src.js(&format!(
1786                        "
1787                        switch (variant{tmp}) {{
1788                            case null: {{
1789                                {none}
1790                                break;
1791                            }}
1792                            default: {{
1793                                const e = variant{tmp};
1794                                {some}
1795                                break;
1796                            }}
1797                        }}
1798                        "
1799                    ));
1800                }
1801            }
1802
1803            Instruction::OptionLift { payload, .. } => {
1804                let (some, some_results) = self.blocks.pop().unwrap();
1805                let (none, none_results) = self.blocks.pop().unwrap();
1806                assert!(none_results.len() == 1);
1807                assert!(some_results.len() == 1);
1808                let some_result = &some_results[0];
1809                assert_eq!(none_results[0], "undefined");
1810
1811                let tmp = self.tmp();
1812
1813                self.src.js(&format!("let variant{tmp};\n"));
1814                self.src.js(&format!("switch ({}) {{\n", operands[0]));
1815
1816                if self.gen.maybe_null(iface, payload) {
1817                    self.src.js(&format!(
1818                        "
1819                            case 0: {{
1820                                {none}
1821                                variant{tmp} = {{ tag: \"none\" }};
1822                                break;
1823                            }}
1824                            case 1: {{
1825                                {some}
1826                                variant{tmp} = {{ tag: \"some\", val: {some_result} }};
1827                                break;
1828                            }}
1829                        ",
1830                    ));
1831                } else {
1832                    self.src.js(&format!(
1833                        "
1834                            case 0: {{
1835                                {none}
1836                                variant{tmp} = null;
1837                                break;
1838                            }}
1839                            case 1: {{
1840                                {some}
1841                                variant{tmp} = {some_result};
1842                                break;
1843                            }}
1844                        ",
1845                    ));
1846                }
1847                self.src.js("
1848                    default:
1849                        throw new RangeError(\"invalid variant discriminant for option\");
1850                ");
1851                self.src.js("}\n");
1852                results.push(format!("variant{tmp}"));
1853            }
1854
1855            Instruction::ExpectedLower {
1856                results: result_types,
1857                ..
1858            } => {
1859                let (mut err, err_results) = self.blocks.pop().unwrap();
1860                let (mut ok, ok_results) = self.blocks.pop().unwrap();
1861
1862                let tmp = self.tmp();
1863                self.src
1864                    .js(&format!("const variant{tmp} = {};\n", operands[0]));
1865
1866                for i in 0..result_types.len() {
1867                    self.src.js(&format!("let variant{tmp}_{i};\n"));
1868                    results.push(format!("variant{tmp}_{i}"));
1869
1870                    let ok_result = &ok_results[i];
1871                    let err_result = &err_results[i];
1872                    ok.push_str(&format!("variant{tmp}_{i} = {ok_result};\n"));
1873                    err.push_str(&format!("variant{tmp}_{i} = {err_result};\n"));
1874                }
1875
1876                self.src.js(&format!(
1877                    "
1878                    switch (variant{tmp}.tag) {{
1879                        case \"ok\": {{
1880                            const e = variant{tmp}.val;
1881                            {ok}
1882                            break;
1883                        }}
1884                        case \"err\": {{
1885                            const e = variant{tmp}.val;
1886                            {err}
1887                            break;
1888                        }}
1889                        default: {{
1890                            throw new RangeError(\"invalid variant specified for expected\");
1891                        }}
1892                    }}
1893                    "
1894                ));
1895            }
1896
1897            Instruction::ExpectedLift { .. } => {
1898                let (err, err_results) = self.blocks.pop().unwrap();
1899                let (ok, ok_results) = self.blocks.pop().unwrap();
1900                let err_result = &err_results[0];
1901                let ok_result = &ok_results[0];
1902                let tmp = self.tmp();
1903                let op0 = &operands[0];
1904                self.src.js(&format!(
1905                    "
1906                    let variant{tmp};
1907                    switch ({op0}) {{
1908                        case 0: {{
1909                            {ok}
1910                            variant{tmp} = {{ tag: \"ok\", val: {ok_result} }};
1911                            break;
1912                        }}
1913                        case 1: {{
1914                            {err}
1915                            variant{tmp} = {{ tag: \"err\", val: {err_result} }};
1916                            break;
1917                        }}
1918                        default: {{
1919                            throw new RangeError(\"invalid variant discriminant for expected\");
1920                        }}
1921                    }}
1922                    ",
1923                ));
1924                results.push(format!("variant{tmp}"));
1925            }
1926
1927            // Lowers an enum in accordance with https://webidl.spec.whatwg.org/#es-enumeration.
1928            Instruction::EnumLower { name, enum_, .. } => {
1929                let tmp = self.tmp();
1930
1931                let to_string = self.gen.intrinsic(Intrinsic::ToString);
1932                self.src
1933                    .js(&format!("const val{tmp} = {to_string}({});\n", operands[0]));
1934
1935                // Declare a variable to hold the result.
1936                self.src.js(&format!("let enum{tmp};\n"));
1937
1938                self.src.js(&format!("switch (val{tmp}) {{\n"));
1939                for (i, case) in enum_.cases.iter().enumerate() {
1940                    self.src.js(&format!(
1941                        "\
1942                        case \"{case}\": {{
1943                            enum{tmp} = {i};
1944                            break;
1945                        }}
1946                        ",
1947                        case = case.name
1948                    ));
1949                }
1950                self.src.js(&format!("\
1951                        default: {{
1952                            throw new TypeError(`\"${{val{tmp}}}\" is not one of the cases of {name}`);
1953                        }}
1954                    }}
1955                "));
1956
1957                results.push(format!("enum{tmp}"));
1958            }
1959
1960            Instruction::EnumLift { name, enum_, .. } => {
1961                let tmp = self.tmp();
1962
1963                self.src.js(&format!("let enum{tmp};\n"));
1964
1965                self.src.js(&format!("switch ({}) {{\n", operands[0]));
1966                for (i, case) in enum_.cases.iter().enumerate() {
1967                    self.src.js(&format!(
1968                        "\
1969                        case {i}: {{
1970                            enum{tmp} = \"{case}\";
1971                            break;
1972                        }}
1973                        ",
1974                        case = case.name
1975                    ));
1976                }
1977                self.src.js(&format!(
1978                    "\
1979                        default: {{
1980                            throw new RangeError(\"invalid discriminant specified for {name}\");
1981                        }}
1982                    }}
1983                    ",
1984                    name = name.to_camel_case()
1985                ));
1986
1987                results.push(format!("enum{tmp}"));
1988            }
1989
1990            Instruction::ListCanonLower { element, realloc } => {
1991                // Lowering only happens when we're passing lists into wasm,
1992                // which forces us to always allocate, so this should always be
1993                // `Some`.
1994                let realloc = realloc.unwrap();
1995                self.gen.needs_get_export = true;
1996                self.needs_memory = true;
1997                self.needs_realloc = Some(realloc.to_string());
1998                let tmp = self.tmp();
1999
2000                let size = self.gen.sizes.size(element);
2001                let align = self.gen.sizes.align(element);
2002                self.src
2003                    .js(&format!("const val{} = {};\n", tmp, operands[0]));
2004                self.src.js(&format!("const len{} = val{0}.length;\n", tmp));
2005                self.src.js(&format!(
2006                    "const ptr{} = realloc(0, 0, {}, len{0} * {});\n",
2007                    tmp, align, size,
2008                ));
2009                // TODO: this is the wrong endianness
2010                self.src.js(&format!(
2011                    "(new Uint8Array(memory.buffer, ptr{0}, len{0} * {1})).set(new Uint8Array(val{0}.buffer, val{0}.byteOffset, len{0} * {1}));\n",
2012                    tmp, size,
2013                ));
2014                results.push(format!("ptr{}", tmp));
2015                results.push(format!("len{}", tmp));
2016            }
2017            Instruction::ListCanonLift { element, free, .. } => {
2018                self.needs_memory = true;
2019                let tmp = self.tmp();
2020                self.src
2021                    .js(&format!("const ptr{} = {};\n", tmp, operands[0]));
2022                self.src
2023                    .js(&format!("const len{} = {};\n", tmp, operands[1]));
2024                // TODO: this is the wrong endianness
2025                let array_ty = self.gen.array_ty(iface, element).unwrap();
2026                let result = format!(
2027                    "new {}(memory.buffer.slice(ptr{}, ptr{1} + len{1} * {}))",
2028                    array_ty,
2029                    tmp,
2030                    self.gen.sizes.size(element),
2031                );
2032                let align = self.gen.sizes.align(element);
2033                match free {
2034                    Some(free) => {
2035                        self.needs_free = Some(free.to_string());
2036                        self.src.js(&format!("const list{} = {};\n", tmp, result));
2037                        self.src
2038                            .js(&format!("free(ptr{}, len{0}, {});\n", tmp, align));
2039                        results.push(format!("list{}", tmp));
2040                    }
2041                    None => results.push(result),
2042                }
2043            }
2044            Instruction::StringLower { realloc } => {
2045                // Lowering only happens when we're passing strings into wasm,
2046                // which forces us to always allocate, so this should always be
2047                // `Some`.
2048                let realloc = realloc.unwrap();
2049                self.gen.needs_get_export = true;
2050                self.needs_memory = true;
2051                self.needs_realloc = Some(realloc.to_string());
2052                let tmp = self.tmp();
2053
2054                let encode = self.gen.intrinsic(Intrinsic::Utf8Encode);
2055                self.src.js(&format!(
2056                    "const ptr{} = {}({}, realloc, memory);\n",
2057                    tmp, encode, operands[0],
2058                ));
2059                let encoded_len = self.gen.intrinsic(Intrinsic::Utf8EncodedLen);
2060                self.src.js(&format!("const len{tmp} = {encoded_len}();\n"));
2061                results.push(format!("ptr{}", tmp));
2062                results.push(format!("len{}", tmp));
2063            }
2064            Instruction::StringLift { free } => {
2065                self.needs_memory = true;
2066                let tmp = self.tmp();
2067                self.src
2068                    .js(&format!("const ptr{} = {};\n", tmp, operands[0]));
2069                self.src
2070                    .js(&format!("const len{} = {};\n", tmp, operands[1]));
2071                let decoder = self.gen.intrinsic(Intrinsic::Utf8Decoder);
2072                let result = format!(
2073                    "{}.decode(new Uint8Array(memory.buffer, ptr{}, len{1}))",
2074                    decoder, tmp,
2075                );
2076                match free {
2077                    Some(free) => {
2078                        self.needs_free = Some(free.to_string());
2079                        self.src.js(&format!("const list{} = {};\n", tmp, result));
2080                        self.src.js(&format!("free(ptr{}, len{0}, 1);\n", tmp));
2081                        results.push(format!("list{}", tmp));
2082                    }
2083                    None => results.push(result),
2084                }
2085            }
2086
2087            Instruction::ListLower { element, realloc } => {
2088                let realloc = realloc.unwrap();
2089                let (body, body_results) = self.blocks.pop().unwrap();
2090                assert!(body_results.is_empty());
2091                let tmp = self.tmp();
2092                let vec = format!("vec{}", tmp);
2093                let result = format!("result{}", tmp);
2094                let len = format!("len{}", tmp);
2095                self.needs_realloc = Some(realloc.to_string());
2096                let size = self.gen.sizes.size(element);
2097                let align = self.gen.sizes.align(element);
2098
2099                // first store our vec-to-lower in a temporary since we'll
2100                // reference it multiple times.
2101                self.src.js(&format!("const {} = {};\n", vec, operands[0]));
2102                self.src.js(&format!("const {} = {}.length;\n", len, vec));
2103
2104                // ... then realloc space for the result in the guest module
2105                self.src.js(&format!(
2106                    "const {} = realloc(0, 0, {}, {} * {});\n",
2107                    result, align, len, size,
2108                ));
2109
2110                // ... then consume the vector and use the block to lower the
2111                // result.
2112                self.src
2113                    .js(&format!("for (let i = 0; i < {}.length; i++) {{\n", vec));
2114                self.src.js(&format!("const e = {}[i];\n", vec));
2115                self.src
2116                    .js(&format!("const base = {} + i * {};\n", result, size));
2117                self.src.js(&body);
2118                self.src.js("}\n");
2119
2120                results.push(result);
2121                results.push(len);
2122            }
2123
2124            Instruction::ListLift { element, free, .. } => {
2125                let (body, body_results) = self.blocks.pop().unwrap();
2126                let tmp = self.tmp();
2127                let size = self.gen.sizes.size(element);
2128                let align = self.gen.sizes.align(element);
2129                let len = format!("len{}", tmp);
2130                self.src.js(&format!("const {} = {};\n", len, operands[1]));
2131                let base = format!("base{}", tmp);
2132                self.src.js(&format!("const {} = {};\n", base, operands[0]));
2133                let result = format!("result{}", tmp);
2134                self.src.js(&format!("const {} = [];\n", result));
2135                results.push(result.clone());
2136
2137                self.src
2138                    .js(&format!("for (let i = 0; i < {}; i++) {{\n", len));
2139                self.src
2140                    .js(&format!("const base = {} + i * {};\n", base, size));
2141                self.src.js(&body);
2142                assert_eq!(body_results.len(), 1);
2143                self.src
2144                    .js(&format!("{}.push({});\n", result, body_results[0]));
2145                self.src.js("}\n");
2146
2147                if let Some(free) = free {
2148                    self.needs_free = Some(free.to_string());
2149                    self.src
2150                        .js(&format!("free({}, {} * {}, {});\n", base, len, size, align,));
2151                }
2152            }
2153
2154            Instruction::IterElem { .. } => results.push("e".to_string()),
2155
2156            Instruction::IterBasePointer => results.push("base".to_string()),
2157
2158            Instruction::CallWasm {
2159                iface: _,
2160                name,
2161                sig,
2162            } => {
2163                self.bind_results(sig.results.len(), results);
2164                self.src.js(&self.src_object);
2165                self.src.js("._exports['");
2166                self.src.js(name);
2167                self.src.js("'](");
2168                self.src.js(&operands.join(", "));
2169                self.src.js(");\n");
2170            }
2171
2172            Instruction::CallWasmAsyncExport {
2173                module: _,
2174                name,
2175                params: _,
2176                results: wasm_results,
2177            } => {
2178                self.bind_results(wasm_results.len(), results);
2179                let promises = self.gen.intrinsic(Intrinsic::Promises);
2180                self.src.js(&format!(
2181                    "\
2182                        await new Promise((resolve, reject) => {{
2183                            const promise_ctx = {promises}.insert(val => {{
2184                                if (typeof val !== 'number')
2185                                    return reject(val);
2186                                resolve(\
2187                    ",
2188                    promises = promises
2189                ));
2190
2191                if !wasm_results.is_empty() {
2192                    self.src.js("[");
2193                    let operands = &["val".to_string()];
2194                    let mut results = Vec::new();
2195                    for (i, result) in wasm_results.iter().enumerate() {
2196                        if i > 0 {
2197                            self.src.js(", ");
2198                        }
2199                        let method = match result {
2200                            WasmType::I32 => "getInt32",
2201                            WasmType::I64 => "getBigInt64",
2202                            WasmType::F32 => "getFloat32",
2203                            WasmType::F64 => "getFloat64",
2204                        };
2205                        self.load(method, (i * 8) as i32, operands, &mut results);
2206                        self.src.js(&results.pop().unwrap());
2207                    }
2208                    self.src.js("]");
2209                }
2210
2211                // Finish the blocks from above
2212                self.src.js(");\n"); // `resolve(...)`
2213                self.src.js("});\n"); // `promises.insert(...)`
2214
2215                let with = self.gen.intrinsic(Intrinsic::WithCurrentPromise);
2216                self.src.js(&with);
2217                self.src.js("(promise_ctx, _prev => {\n");
2218                self.src.js(&self.src_object);
2219                self.src.js("._exports['");
2220                self.src.js(name);
2221                self.src.js("'](");
2222                for op in operands {
2223                    self.src.js(op);
2224                    self.src.js(", ");
2225                }
2226                self.src.js("promise_ctx);\n");
2227                self.src.js("});\n"); // call to `with`
2228                self.src.js("});\n"); // `await new Promise(...)`
2229            }
2230
2231            Instruction::CallInterface { module: _, func } => {
2232                let call = |me: &mut FunctionBindgen<'_>| match &func.kind {
2233                    FunctionKind::Freestanding | FunctionKind::Static { .. } => {
2234                        me.src.js(&format!(
2235                            "obj.{}({})",
2236                            func.name.to_mixed_case(),
2237                            operands.join(", "),
2238                        ));
2239                    }
2240                    FunctionKind::Method { name, .. } => {
2241                        me.src.js(&format!(
2242                            "{}.{}({})",
2243                            operands[0],
2244                            name.to_mixed_case(),
2245                            operands[1..].join(", "),
2246                        ));
2247                    }
2248                };
2249                let mut bind_results = |me: &mut FunctionBindgen<'_>| match &func.result {
2250                    Type::Unit => {
2251                        results.push("".to_string());
2252                    }
2253                    _ => {
2254                        me.src.js("const ret = ");
2255                        results.push("ret".to_string());
2256                    }
2257                };
2258
2259                if func.is_async {
2260                    let with = self.gen.intrinsic(Intrinsic::WithCurrentPromise);
2261                    let promises = self.gen.intrinsic(Intrinsic::Promises);
2262                    self.src.js(&with);
2263                    self.src.js("(null, cur_promise => {\n");
2264                    self.src.js(&format!(
2265                        "const catch_closure = e => {}.remove(cur_promise)(e);\n",
2266                        promises
2267                    ));
2268                    call(self);
2269                    self.src.js(".then(e => {\n");
2270                    match &func.result {
2271                        Type::Unit => {
2272                            results.push("".to_string());
2273                        }
2274                        _ => {
2275                            bind_results(self);
2276                            self.src.js("e;\n");
2277                        }
2278                    }
2279                } else {
2280                    bind_results(self);
2281                    call(self);
2282                    self.src.js(";\n");
2283                }
2284            }
2285
2286            Instruction::Return { amt, func: _ } => match amt {
2287                0 => {}
2288                1 => self.src.js(&format!("return {};\n", operands[0])),
2289                _ => {
2290                    assert!(self.in_import);
2291                    self.src.js(&format!("return [{}];\n", operands.join(", ")));
2292                }
2293            },
2294
2295            Instruction::ReturnAsyncImport { .. } => {
2296                // When we reenter webassembly successfully that means that the
2297                // host's promise resolved without exception. Take the current
2298                // promise index saved as part of `CallInterface` and update the
2299                // `CUR_PROMISE` global with what's currently being executed.
2300                // This'll get reset once the wasm returns again.
2301                //
2302                // Note that the name `cur_promise` used here is introduced in
2303                // the `CallInterface` codegen above in the closure for
2304                // `with_current_promise` which we're using here.
2305                //
2306                // TODO: hardcoding `__indirect_function_table` and no help if
2307                // it's not actually defined.
2308                self.gen.needs_get_export = true;
2309                let with = self.gen.intrinsic(Intrinsic::WithCurrentPromise);
2310                self.src.js(&format!(
2311                    "\
2312                        {with}(cur_promise, _prev => {{
2313                            get_export(\"__indirect_function_table\").get({})({});
2314                        }});
2315                    ",
2316                    operands[0],
2317                    operands[1..].join(", "),
2318                    with = with,
2319                ));
2320            }
2321
2322            Instruction::I32Load { offset } => self.load("getInt32", *offset, operands, results),
2323            Instruction::I64Load { offset } => self.load("getBigInt64", *offset, operands, results),
2324            Instruction::F32Load { offset } => self.load("getFloat32", *offset, operands, results),
2325            Instruction::F64Load { offset } => self.load("getFloat64", *offset, operands, results),
2326            Instruction::I32Load8U { offset } => self.load("getUint8", *offset, operands, results),
2327            Instruction::I32Load8S { offset } => self.load("getInt8", *offset, operands, results),
2328            Instruction::I32Load16U { offset } => {
2329                self.load("getUint16", *offset, operands, results)
2330            }
2331            Instruction::I32Load16S { offset } => self.load("getInt16", *offset, operands, results),
2332            Instruction::I32Store { offset } => self.store("setInt32", *offset, operands),
2333            Instruction::I64Store { offset } => self.store("setBigInt64", *offset, operands),
2334            Instruction::F32Store { offset } => self.store("setFloat32", *offset, operands),
2335            Instruction::F64Store { offset } => self.store("setFloat64", *offset, operands),
2336            Instruction::I32Store8 { offset } => self.store("setInt8", *offset, operands),
2337            Instruction::I32Store16 { offset } => self.store("setInt16", *offset, operands),
2338
2339            Instruction::Malloc {
2340                realloc,
2341                size,
2342                align,
2343            } => {
2344                self.needs_realloc = Some(realloc.to_string());
2345                let tmp = self.tmp();
2346                let ptr = format!("ptr{}", tmp);
2347                self.src.js(&format!(
2348                    "const {} = realloc(0, 0, {}, {});\n",
2349                    ptr, align, size
2350                ));
2351                results.push(ptr);
2352            }
2353
2354            i => unimplemented!("{:?}", i),
2355        }
2356    }
2357}
2358
2359impl Js {
2360    fn print_intrinsics(&mut self) {
2361        if self.all_intrinsics.contains(&Intrinsic::I32ToF32)
2362            || self.all_intrinsics.contains(&Intrinsic::F32ToI32)
2363        {
2364            self.src.js("
2365                const I32_TO_F32_I = new Int32Array(1);
2366                const I32_TO_F32_F = new Float32Array(I32_TO_F32_I.buffer);
2367            ");
2368        }
2369        if self.all_intrinsics.contains(&Intrinsic::I64ToF64)
2370            || self.all_intrinsics.contains(&Intrinsic::F64ToI64)
2371        {
2372            self.src.js("
2373                const I64_TO_F64_I = new BigInt64Array(1);
2374                const I64_TO_F64_F = new Float64Array(I64_TO_F64_I.buffer);
2375            ");
2376        }
2377
2378        if self.all_intrinsics.contains(&Intrinsic::Promises) {
2379            self.all_intrinsics.insert(Intrinsic::Slab);
2380        }
2381
2382        for i in mem::take(&mut self.all_intrinsics) {
2383            self.print_intrinsic(i);
2384        }
2385
2386        self.src.js(&format!(
2387            "\nmodule.exports = {{ {} }};\n",
2388            self.src.exported_items.join(", ")
2389        ));
2390    }
2391
2392    fn print_intrinsic(&mut self, i: Intrinsic) {
2393        self.src.export(i.name());
2394        match i {
2395            Intrinsic::ClampGuest => self.src.js("
2396                function clamp_guest(i, min, max) {
2397                    if (i < min || i > max) \
2398                        throw new RangeError(`must be between ${min} and ${max}`);
2399                    return i;
2400                }
2401            "),
2402            Intrinsic::ClampHost => self.src.js("
2403                function clamp_host(i, min, max) {
2404                    if (!Number.isInteger(i)) \
2405                        throw new TypeError(`must be an integer`);
2406                    if (i < min || i > max) \
2407                        throw new RangeError(`must be between ${min} and ${max}`);
2408                    return i;
2409                }
2410            "),
2411
2412            Intrinsic::DataView => self.src.js("
2413                let DATA_VIEW = new DataView(new ArrayBuffer());
2414
2415                function data_view(mem) {
2416                    if (DATA_VIEW.buffer !== mem.buffer) \
2417                        DATA_VIEW = new DataView(mem.buffer);
2418                    return DATA_VIEW;
2419                }
2420            "),
2421
2422            Intrinsic::ClampHost64 => self.src.js("
2423                function clamp_host64(i, min, max) {
2424                    if (typeof i !== 'bigint') \
2425                        throw new TypeError(`must be a bigint`);
2426                    if (i < min || i > max) \
2427                        throw new RangeError(`must be between ${min} and ${max}`);
2428                    return i;
2429                }
2430            "),
2431
2432            Intrinsic::ValidateGuestChar => self.src.js("
2433                function validate_guest_char(i) {
2434                    if ((i > 0x10ffff) || (i >= 0xd800 && i <= 0xdfff)) \
2435                        throw new RangeError(`not a valid char`);
2436                    return String.fromCodePoint(i);
2437                }
2438            "),
2439
2440            // TODO: this is incorrect. It at least allows strings of length > 0
2441            // but it probably doesn't do the right thing for unicode or invalid
2442            // utf16 strings either.
2443            Intrinsic::ValidateHostChar => self.src.js("
2444                function validate_host_char(s) {
2445                    if (typeof s !== 'string') \
2446                        throw new TypeError(`must be a string`);
2447                    return s.codePointAt(0);
2448                }
2449            "),
2450
2451            Intrinsic::ValidateFlags => self.src.js("
2452                function validate_flags(flags, mask) {
2453                    if (!Number.isInteger(flags)) \
2454                        throw new TypeError('flags were not an integer');
2455                    if ((flags & ~mask) != 0)
2456                        throw new TypeError('flags have extraneous bits set');
2457                    return flags;
2458                }
2459            "),
2460
2461            Intrinsic::ValidateFlags64 => self.src.js("
2462                function validate_flags64(flags, mask) {
2463                    if (typeof flags !== 'bigint')
2464                        throw new TypeError('flags were not a bigint');
2465                    if ((flags & ~mask) != 0n)
2466                        throw new TypeError('flags have extraneous bits set');
2467                    return flags;
2468                }
2469            "),
2470
2471            Intrinsic::ToString => self.src.js("
2472                function to_string(val) {
2473                    if (typeof val === 'symbol') {
2474                        throw new TypeError('symbols cannot be converted to strings');
2475                    } else {
2476                        // Calling `String` almost directly calls `ToString`, except that it also allows symbols,
2477                        // which is why we have the symbol-rejecting branch above.
2478                        //
2479                        // Definition of `String`: https://tc39.es/ecma262/#sec-string-constructor-string-value
2480                        return String(val);
2481                    }
2482                }
2483            "),
2484
2485            Intrinsic::I32ToF32 => self.src.js("
2486                function i32ToF32(i) {
2487                    I32_TO_F32_I[0] = i;
2488                    return I32_TO_F32_F[0];
2489                }
2490            "),
2491            Intrinsic::F32ToI32 => self.src.js("
2492                function f32ToI32(f) {
2493                    I32_TO_F32_F[0] = f;
2494                    return I32_TO_F32_I[0];
2495                }
2496            "),
2497            Intrinsic::I64ToF64 => self.src.js("
2498                function i64ToF64(i) {
2499                    I64_TO_F64_I[0] = i;
2500                    return I64_TO_F64_F[0];
2501                }
2502            "),
2503            Intrinsic::F64ToI64 => self.src.js("
2504                function f64ToI64(f) {
2505                    I64_TO_F64_F[0] = f;
2506                    return I64_TO_F64_I[0];
2507                }
2508            "),
2509
2510            Intrinsic::Utf8Decoder => self
2511                .src
2512                .js("const UTF8_DECODER = new TextDecoder('utf-8');\n"),
2513
2514            Intrinsic::Utf8EncodedLen => self.src.js("
2515                let UTF8_ENCODED_LEN = 0;
2516
2517                function utf8_encoded_len() {
2518                    return UTF8_ENCODED_LEN;
2519                }
2520            "),
2521
2522            Intrinsic::Utf8Encode => self.src.js("
2523                const UTF8_ENCODER = new TextEncoder('utf-8');
2524
2525                function utf8_encode(s, realloc, memory) {
2526                    if (typeof s !== 'string') \
2527                        throw new TypeError('expected a string');
2528
2529                    if (s.length === 0) {
2530                        UTF8_ENCODED_LEN = 0;
2531                        return 1;
2532                    }
2533
2534                    let alloc_len = 0;
2535                    let ptr = 0;
2536                    let writtenTotal = 0;
2537                    while (s.length > 0) {
2538                        ptr = realloc(ptr, alloc_len, 1, alloc_len + s.length);
2539                        alloc_len += s.length;
2540                        const { read, written } = UTF8_ENCODER.encodeInto(
2541                            s,
2542                            new Uint8Array(memory.buffer, ptr + writtenTotal, alloc_len - writtenTotal),
2543                        );
2544                        writtenTotal += written;
2545                        s = s.slice(read);
2546                    }
2547                    if (alloc_len > writtenTotal)
2548                        ptr = realloc(ptr, alloc_len, 1, writtenTotal);
2549                    UTF8_ENCODED_LEN = writtenTotal;
2550                    return ptr;
2551                }
2552            "),
2553
2554            Intrinsic::Slab => self.src.js("
2555                class Slab {
2556                    constructor() {
2557                        this.list = [];
2558                        this.head = 0;
2559                    }
2560
2561                    insert(val) {
2562                        if (this.head >= this.list.length) {
2563                            this.list.push({
2564                                next: this.list.length + 1,
2565                                val: undefined,
2566                            });
2567                        }
2568                        const ret = this.head;
2569                        const slot = this.list[ret];
2570                        this.head = slot.next;
2571                        slot.next = -1;
2572                        slot.val = val;
2573                        return ret;
2574                    }
2575
2576                    get(idx) {
2577                        if (idx >= this.list.length)
2578                            throw new RangeError('handle index not valid');
2579                        const slot = this.list[idx];
2580                        if (slot.next === -1)
2581                            return slot.val;
2582                        throw new RangeError('handle index not valid');
2583                    }
2584
2585                    remove(idx) {
2586                        const ret = this.get(idx); // validate the slot
2587                        const slot = this.list[idx];
2588                        slot.val = undefined;
2589                        slot.next = this.head;
2590                        this.head = idx;
2591                        return ret;
2592                    }
2593                }
2594            "),
2595
2596            Intrinsic::Promises => self.src.js("const PROMISES = new Slab();\n"),
2597            Intrinsic::WithCurrentPromise => self.src.js("
2598                let CUR_PROMISE = null;
2599                function with_current_promise(val, closure) {
2600                    const prev = CUR_PROMISE;
2601                    CUR_PROMISE = val;
2602                    try {
2603                        closure(prev);
2604                    } finally {
2605                        CUR_PROMISE = prev;
2606                    }
2607                }
2608            "),
2609            Intrinsic::ThrowInvalidBool => self.src.js("
2610                function throw_invalid_bool() {
2611                    throw new RangeError(\"invalid variant discriminant for bool\");
2612                }
2613            "),
2614        }
2615    }
2616}
2617
2618pub fn to_js_ident(name: &str) -> &str {
2619    match name {
2620        "in" => "in_",
2621        "import" => "import_",
2622        "export" => "export_",
2623        "module" => "module_",
2624        s => s,
2625    }
2626}
2627
2628#[derive(Default)]
2629struct Source {
2630    js: wai_bindgen_gen_core::Source,
2631    ts: wai_bindgen_gen_core::Source,
2632    exported_items: Vec<String>,
2633}
2634
2635impl Source {
2636    fn js(&mut self, s: &str) {
2637        self.js.push_str(s);
2638    }
2639    fn ts(&mut self, s: &str) {
2640        self.ts.push_str(s);
2641    }
2642    fn export(&mut self, name: impl Into<String>) {
2643        self.exported_items.push(name.into());
2644    }
2645}
2646
2647enum JsFlagsRepr {
2648    Number,
2649    Bigint,
2650}
2651
2652impl JsFlagsRepr {
2653    fn ty(&self) -> &'static str {
2654        match self {
2655            JsFlagsRepr::Number => "number",
2656            JsFlagsRepr::Bigint => "bigint",
2657        }
2658    }
2659    fn suffix(&self) -> &'static str {
2660        match self {
2661            JsFlagsRepr::Number => "",
2662            JsFlagsRepr::Bigint => "n",
2663        }
2664    }
2665}
2666
2667fn js_flags_repr(f: &Flags) -> JsFlagsRepr {
2668    match f.repr() {
2669        FlagsRepr::U8 | FlagsRepr::U16 | FlagsRepr::U32(1) => JsFlagsRepr::Number,
2670        FlagsRepr::U32(_) => JsFlagsRepr::Bigint,
2671    }
2672}