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 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 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 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 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 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 self.intrinsics.insert(i, i.name().to_string());
305 return i.name().to_string();
306 }
307
308 fn maybe_null(&self, iface: &Interface, ty: &Type) -> bool {
310 self.as_nullable(iface, ty).is_some()
311 }
312
313 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 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 let mut complete_docs = String::new();
540
541 if let Some(docs) = &docs.contents {
542 complete_docs.push_str(docs);
543 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 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 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 self.src.js("}, catch_closure);\n"); self.src.js("});\n"); }
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 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 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 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 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 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 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 Instruction::U32FromI32 => {
1330 results.push(format!("{} >>> 0", operands[0]));
1331 }
1332 Instruction::U64FromI64 => results.push(format!("BigInt.asUintN(64, {})", operands[0])),
1335 Instruction::S32FromI32 | Instruction::S64FromI64 => {
1338 results.push(operands.pop().unwrap())
1339 }
1340
1341 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 Instruction::Float32FromF32 | Instruction::Float64FromF64 => {
1358 results.push(operands.pop().unwrap())
1359 }
1360
1361 Instruction::F32FromFloat32 | Instruction::F64FromFloat64 => {
1362 results.push(format!("+{}", operands[0]));
1364 }
1365
1366 Instruction::CharFromI32 => {
1369 let validate = self.gen.intrinsic(Intrinsic::ValidateGuestChar);
1370 results.push(format!("{}({})", validate, operands[0]));
1371 }
1372
1373 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 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 Instruction::I32FromBorrowedHandle { ty } => {
1448 let tmp = self.tmp();
1449 self.src
1450 .js(&format!("const obj{} = {};\n", tmp, operands[0]));
1451
1452 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 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 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 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 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 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 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 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 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 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 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 self.src.js(&format!("const {} = {};\n", vec, operands[0]));
2102 self.src.js(&format!("const {} = {}.length;\n", len, vec));
2103
2104 self.src.js(&format!(
2106 "const {} = realloc(0, 0, {}, {} * {});\n",
2107 result, align, len, size,
2108 ));
2109
2110 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 self.src.js(");\n"); self.src.js("});\n"); 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"); self.src.js("});\n"); }
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 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 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}