1use crate::descriptor::VectorKind;
8use crate::js::Context;
9use crate::wit::InstructionData;
10use crate::wit::{
11 Adapter, AdapterId, AdapterKind, AdapterType, AuxFunctionArgumentData, ClosureDtor, Instruction,
12};
13use crate::OutputMode;
14use anyhow::{bail, Error};
15use std::collections::{HashMap, HashSet};
16use std::fmt::Write;
17use walrus::{Module, ValType};
18
19pub struct Builder<'a, 'b> {
22 pub cx: &'a mut Context<'b>,
24 constructor: Option<String>,
27 method: Option<bool>,
30 classless_this: bool,
32 catch: bool,
35 log_error: bool,
37}
38
39pub struct JsBuilder<'a, 'b> {
42 cx: &'a mut Context<'b>,
45
46 debug_name: &'a str,
48
49 prelude: String,
52
53 pre_try: String,
55
56 finally: String,
58
59 tmp: usize,
62
63 args: Vec<String>,
66
67 stack: Vec<String>,
73}
74
75pub struct JsFunction {
76 pub code: String,
77 pub ts_sig: String,
78 pub js_doc: String,
79 pub ts_doc: String,
80 pub ts_arg_tys: Vec<String>,
81 pub ts_ret_ty: Option<String>,
82 pub ts_refs: HashSet<TsReference>,
83 pub might_be_optional_field: bool,
87 pub catch: bool,
88 pub log_error: bool,
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Hash)]
95pub enum TsReference {
96 StringEnum(String),
97}
98
99pub fn wrap_try_catch(call: &str) -> String {
100 format!(
101 "\
102 __wbg_termination_guard();
103 try {{
104 {call};
105 }} catch(e) {{
106 __wbg_handle_catch(e);
107 }}
108 "
109 )
110}
111
112pub fn maybe_wrap_try_catch(call: &str, should_check_aborted: bool) -> String {
113 if should_check_aborted {
114 wrap_try_catch(call)
115 } else {
116 format!("{call};")
117 }
118}
119
120impl<'a, 'b> Builder<'a, 'b> {
121 pub fn new(cx: &'a mut Context<'b>) -> Builder<'a, 'b> {
122 Builder {
123 log_error: false,
124 cx,
125 constructor: None,
126 method: None,
127 classless_this: false,
128 catch: false,
129 }
130 }
131
132 pub fn method(&mut self, consumed: bool) {
133 self.method = Some(consumed);
134 }
135
136 pub fn classless_this(&mut self) {
137 self.classless_this = true;
138 }
139
140 pub fn constructor(&mut self, class: &str) {
141 self.constructor = Some(class.to_string());
142 }
143
144 pub fn catch(&mut self, catch: bool) {
145 self.catch = catch;
146 }
147
148 pub fn log_error(&mut self, log: bool) {
149 self.log_error = log;
150 }
151
152 pub fn process(
153 &mut self,
154 adapter: &Adapter,
155 instructions: &[InstructionData],
156 args_data: &Option<Vec<AuxFunctionArgumentData>>,
157 asyncness: bool,
158 variadic: bool,
159 generate_jsdoc: bool,
160 debug_name: &str,
161 ret_ty_override: &Option<String>,
162 ret_desc: &Option<String>,
163 ) -> Result<JsFunction, Error> {
164 if !self.cx.unwind_enabled
165 && self
166 .cx
167 .aux
168 .imports_with_assert_no_shim
169 .contains(&adapter.id)
170 {
171 bail!("generating a shim for something asserted to have no shim");
172 }
173
174 let mut params = adapter.params.iter();
175 let mut function_args = Vec::new();
176 let mut arg_tys = Vec::new();
177
178 let mut js = JsBuilder::new(self.cx, debug_name);
182 if let Some(consumes_self) = self.method {
183 let _ = params.next();
184 if js.cx.config.generate_reset_state {
185 js.prelude(
186 "
187 if (this.__wbg_inst !== undefined && this.__wbg_inst !== __wbg_instance_id) {
188 throw new Error('Invalid stale object from previous Wasm instance');
189 }
190 ",
191 );
192 }
193 if js.cx.config.debug {
194 js.prelude(
195 "if (this.__wbg_ptr == 0) throw new Error('Attempt to use a moved value');",
196 );
197 }
198 if consumes_self {
199 js.prelude("const ptr = this.__destroy_into_raw();");
200 js.args.push("ptr".into());
201 } else {
202 js.args.push("this.__wbg_ptr".into());
203 }
204 } else if self.classless_this {
205 let _ = params.next();
206 js.args.push("this".into());
207 }
208 for (i, param) in params.enumerate() {
209 let arg = match args_data {
210 Some(list) => list[i].clone(),
211 None => AuxFunctionArgumentData {
212 name: format!("arg{i}"),
213 ty_override: None,
214 optional: false,
215 desc: None,
216 },
217 };
218 js.args.push(arg.name.clone());
219 function_args.push(arg);
220 arg_tys.push(param);
221 }
222
223 for instr in instructions {
236 instruction(
237 &mut js,
238 &instr.instr,
239 &mut self.log_error,
240 &self.constructor,
241 )?;
242 }
243
244 assert_eq!(
245 js.stack.len(),
246 adapter.results.len(),
247 "stack size mismatch for {debug_name}"
248 );
249 match js.stack.len() {
250 0 => {}
251 1 => {
252 let val = js.pop();
253 js.prelude(&format!("return {val};"));
254 }
255
256 _ => bail!("multi-value returns from adapters not supported yet"),
262 }
268 assert!(js.stack.is_empty());
269
270 let mut code = String::new();
277 code.push('(');
278 for (i, v) in function_args.iter().enumerate() {
279 if i != 0 {
280 code.push_str(", ");
281 }
282
283 if variadic && i == function_args.len() - 1 {
284 code.push_str("...");
285 }
286
287 code.push_str(&v.name);
288 }
289 code.push_str(") {\n");
290
291 let call = if !js.finally.is_empty() {
292 format!(
293 "{}try {{\n{}}} finally {{\n{}}}\n",
294 js.pre_try, js.prelude, js.finally
295 )
296 } else {
297 js.pre_try + &js.prelude
298 };
299
300 if self.log_error {
305 js.cx.expose_log_error();
306 }
307
308 code.push_str(&call);
309 code.push('}');
310
311 let mut might_be_optional_field = false;
316 let (ts_sig, ts_arg_tys, ts_ret_ty, ts_refs) = self.typescript_signature(
317 &function_args,
318 &arg_tys,
319 &adapter.inner_results,
320 &mut might_be_optional_field,
321 asyncness,
322 variadic,
323 ret_ty_override,
324 );
325 let js_doc = if generate_jsdoc {
326 self.js_doc_comments(
327 &function_args,
328 &arg_tys,
329 &ts_ret_ty,
330 variadic,
331 ret_ty_override,
332 ret_desc,
333 )
334 } else {
335 String::new()
336 };
337
338 let ts_doc = self.ts_doc_comments(&function_args, ret_desc);
345
346 Ok(JsFunction {
347 code,
348 ts_sig,
349 js_doc,
350 ts_doc,
351 ts_arg_tys,
352 ts_ret_ty,
353 ts_refs,
354 might_be_optional_field,
355 catch: self.catch,
356 log_error: self.log_error,
357 })
358 }
359
360 fn typescript_signature(
366 &self,
367 args_data: &[AuxFunctionArgumentData],
368 arg_tys: &[&AdapterType],
369 result_tys: &[AdapterType],
370 might_be_optional_field: &mut bool,
371 asyncness: bool,
372 variadic: bool,
373 ret_ty_override: &Option<String>,
374 ) -> (String, Vec<String>, Option<String>, HashSet<TsReference>) {
375 let mut omittable = true;
377 let mut ts_args = Vec::new();
378 let mut ts_arg_tys = Vec::new();
379 let mut ts_refs = HashSet::new();
380 for (
381 AuxFunctionArgumentData {
382 name,
383 ty_override,
384 optional,
385 ..
386 },
387 ty,
388 ) in args_data.iter().zip(arg_tys).rev()
389 {
390 let mut arg = name.to_string();
396 let mut ts = String::new();
397 if let Some(v) = ty_override {
398 omittable = false;
399 if *optional {
400 arg.push('?');
401 }
402 arg.push_str(": ");
403 ts.push_str(v);
404 } else {
405 match ty {
406 AdapterType::Option(ty) if omittable => {
407 arg.push_str("?: ");
409 adapter2ts(
410 ty,
411 TypePosition::Argument,
412 &mut ts,
413 Some(&mut ts_refs),
414 &self.cx.qualified_to_js_name,
415 );
416 ts.push_str(" | null");
417 }
418 ty => {
419 adapter2ts(
420 ty,
421 TypePosition::Argument,
422 &mut ts,
423 Some(&mut ts_refs),
424 &self.cx.qualified_to_js_name,
425 );
426 omittable = false;
427 arg.push_str(": ");
428 }
429 }
430 }
431 arg.push_str(&ts);
432 ts_arg_tys.push(ts);
433 ts_args.push(arg);
434 }
435 ts_args.reverse();
436 ts_arg_tys.reverse();
437 let mut ts = String::from("(");
438 if variadic {
439 if let Some((last, non_variadic_args)) = ts_args.split_last() {
440 ts.push_str(&non_variadic_args.join(", "));
441 if !non_variadic_args.is_empty() {
442 ts.push_str(", ");
443 }
444 ts.push_str((String::from("...") + last).as_str())
445 }
446 } else {
447 ts.push_str(&ts_args.join(", "));
448 };
449 ts.push(')');
450
451 if ts_args.len() == 1 && omittable {
454 *might_be_optional_field = true;
455 }
456
457 let mut ts_ret = None;
459 if self.constructor.is_none() {
460 ts.push_str(": ");
461 let mut ret = String::new();
462 if let Some(v) = &ret_ty_override {
463 ret.push_str(v);
464 } else {
465 match result_tys.len() {
466 0 => ret.push_str("void"),
467 1 => adapter2ts(
468 &result_tys[0],
469 TypePosition::Return,
470 &mut ret,
471 Some(&mut ts_refs),
472 &self.cx.qualified_to_js_name,
473 ),
474 _ => ret.push_str("[any]"),
475 }
476 }
477 if asyncness {
478 ret = format!("Promise<{ret}>");
479 }
480 ts.push_str(&ret);
481 ts_ret = Some(ret);
482 }
483 (ts, ts_arg_tys, ts_ret, ts_refs)
484 }
485
486 fn js_doc_comments(
489 &self,
490 args_data: &[AuxFunctionArgumentData],
491 arg_tys: &[&AdapterType],
492 ts_ret: &Option<String>,
493 variadic: bool,
494 ret_ty_override: &Option<String>,
495 ret_desc: &Option<String>,
496 ) -> String {
497 let (variadic_arg, fn_arg_names) = match args_data.split_last() {
498 Some((last, args)) if variadic => (Some(last), args),
499 _ => (None, args_data),
500 };
501
502 let mut omittable = true;
503 let mut js_doc_args = Vec::new();
504
505 for (
506 AuxFunctionArgumentData {
507 name,
508 ty_override,
509 optional,
510 desc,
511 },
512 ty,
513 ) in fn_arg_names.iter().zip(arg_tys).rev()
514 {
515 let mut arg = "@param {".to_string();
516
517 if let Some(v) = ty_override {
518 omittable = false;
519 arg.push_str(v);
520 arg.push_str("} ");
521 if *optional {
522 arg.push('[');
524 arg.push_str(name);
525 arg.push(']');
526 } else {
527 arg.push_str(name);
528 }
529 } else {
530 match ty {
531 AdapterType::Option(ty) if omittable => {
532 adapter2ts(
533 ty,
534 TypePosition::Argument,
535 &mut arg,
536 None,
537 &self.cx.qualified_to_js_name,
538 );
539 arg.push_str(" | null} ");
540 arg.push('[');
541 arg.push_str(name);
542 arg.push(']');
543 }
544 _ => {
545 omittable = false;
546 adapter2ts(
547 ty,
548 TypePosition::Argument,
549 &mut arg,
550 None,
551 &self.cx.qualified_to_js_name,
552 );
553 arg.push_str("} ");
554 arg.push_str(name);
555 }
556 }
557 }
558 if let Some(v) = desc {
560 arg.push_str(" - ");
561 arg.push_str(v);
562 }
563 arg.push('\n');
564 js_doc_args.push(arg);
565 }
566
567 let mut ret: String = js_doc_args.into_iter().rev().collect();
568
569 if let (
570 Some(AuxFunctionArgumentData {
571 name,
572 ty_override,
573 optional: _,
574 desc,
575 }),
576 Some(ty),
577 ) = (variadic_arg, arg_tys.last())
578 {
579 ret.push_str("@param {...");
580 if let Some(v) = ty_override {
581 ret.push_str(v);
582 } else {
583 adapter2ts(
584 ty,
585 TypePosition::Argument,
586 &mut ret,
587 None,
588 &self.cx.qualified_to_js_name,
589 );
590 }
591 ret.push_str("} ");
592 ret.push_str(name);
593
594 if let Some(v) = desc {
596 ret.push_str(" - ");
597 ret.push_str(v);
598 }
599 ret.push('\n');
600 }
601 if let Some(ts) = ret_ty_override.as_ref().or(ts_ret.as_ref()) {
602 if ts != "void" || ret_desc.is_some() {
604 ret.push_str(&format!("@returns {{{ts}}}"));
605 }
606 if let Some(v) = ret_desc {
608 ret.push(' ');
609 ret.push_str(v);
610 }
611 }
612 ret
613 }
614
615 fn ts_doc_comments(
618 &self,
619 args_data: &[AuxFunctionArgumentData],
620 ret_desc: &Option<String>,
621 ) -> String {
622 let mut ts_doc = String::new();
623 for AuxFunctionArgumentData { name, desc, .. } in args_data.iter() {
625 ts_doc.push_str("@param ");
626 ts_doc.push_str(name);
627
628 if let Some(v) = desc {
630 ts_doc.push_str(" - ");
631 ts_doc.push_str(v);
632 }
633 ts_doc.push('\n');
634 }
635
636 if let Some(ret_desc) = ret_desc {
638 ts_doc.push_str("@returns ");
639 ts_doc.push_str(ret_desc);
640 }
641 ts_doc
642 }
643}
644
645impl<'a, 'b> JsBuilder<'a, 'b> {
646 pub fn new(cx: &'a mut Context<'b>, debug_name: &'a str) -> JsBuilder<'a, 'b> {
647 JsBuilder {
648 cx,
649 debug_name,
650 args: Vec::new(),
651 tmp: 0,
652 pre_try: String::new(),
653 finally: String::new(),
654 prelude: String::new(),
655 stack: Vec::new(),
656 }
657 }
658
659 pub fn arg(&self, idx: u32) -> &str {
660 &self.args[idx as usize]
661 }
662
663 pub fn prelude(&mut self, prelude: &str) {
664 for line in prelude.trim().lines().map(|l| l.trim()) {
665 if !line.is_empty() {
666 self.prelude.push_str(line);
667 self.prelude.push('\n');
668 }
669 }
670 }
671
672 pub fn finally(&mut self, finally: &str) {
673 for line in finally.trim().lines().map(|l| l.trim()) {
674 if !line.is_empty() {
675 self.finally.push_str(line);
676 self.finally.push('\n');
677 }
678 }
679 }
680
681 pub fn tmp(&mut self) -> usize {
682 let ret = self.tmp;
683 self.tmp += 1;
684 ret
685 }
686
687 fn pop(&mut self) -> String {
688 match self.stack.pop() {
689 Some(s) => s,
690 None => panic!("popping an empty stack in {}", self.debug_name),
691 }
692 }
693
694 fn push(&mut self, arg: String) {
695 self.stack.push(arg);
696 }
697
698 fn assert_class(&mut self, arg: &str, class: &str) {
699 self.cx.expose_assert_class();
700 let identifier = self.cx.require_class_identifier(class);
701 self.prelude(&format!("_assertClass({arg}, {identifier});"));
702 }
703
704 fn assert_number(&mut self, arg: &str) {
705 if !self.cx.config.debug {
706 return;
707 }
708 self.cx.expose_assert_num();
709 self.prelude(&format!("_assertNum({arg});"));
710 }
711
712 fn assert_bigint(&mut self, arg: &str) {
713 if !self.cx.config.debug {
714 return;
715 }
716 self.cx.expose_assert_bigint();
717 self.prelude(&format!("_assertBigInt({arg});"));
718 }
719
720 fn assert_bool(&mut self, arg: &str) {
721 if !self.cx.config.debug {
722 return;
723 }
724 self.cx.expose_assert_bool();
725 self.prelude(&format!("_assertBoolean({arg});"));
726 }
727
728 fn assert_optional_number(&mut self, arg: &str) {
729 if !self.cx.config.debug {
730 return;
731 }
732 self.cx.expose_is_like_none();
733 self.prelude(&format!("if (!isLikeNone({arg})) {{"));
734 self.assert_number(arg);
735 self.prelude("}");
736 }
737
738 fn assert_non_null(&mut self, arg: &str) {
739 self.cx.expose_assert_non_null();
740 self.prelude(&format!("_assertNonNull({arg});"));
741 }
742
743 fn assert_char(&mut self, arg: &str) {
744 self.cx.expose_assert_char();
745 self.prelude(&format!("_assertChar({arg});"));
746 }
747
748 fn assert_optional_bigint(&mut self, arg: &str) {
749 if !self.cx.config.debug {
750 return;
751 }
752 self.cx.expose_is_like_none();
753 self.prelude(&format!("if (!isLikeNone({arg})) {{"));
754 self.assert_bigint(arg);
755 self.prelude("}");
756 }
757
758 fn assert_optional_bool(&mut self, arg: &str) {
759 if !self.cx.config.debug {
760 return;
761 }
762 self.cx.expose_is_like_none();
763 self.prelude(&format!("if (!isLikeNone({arg})) {{"));
764 self.assert_bool(arg);
765 self.prelude("}");
766 }
767
768 fn assert_not_moved(&mut self, arg: &str) {
769 if self.cx.config.generate_reset_state {
770 self.prelude(&format!(
772 "\
773 if (({arg}).__wbg_inst !== undefined && ({arg}).__wbg_inst !== __wbg_instance_id) {{
774 throw new Error('Invalid stale object from previous Wasm instance');
775 }}
776 "
777 ));
778 }
779 if self.cx.config.debug {
780 self.prelude(&format!(
782 "\
783 if ({arg}.__wbg_ptr === 0) {{
784 throw new Error('Attempt to use a moved value');
785 }}
786 ",
787 ));
788 }
789 }
790
791 fn string_to_memory(
792 &mut self,
793 mem: walrus::MemoryId,
794 malloc: walrus::FunctionId,
795 realloc: Option<walrus::FunctionId>,
796 ) {
797 let pass = self.cx.expose_pass_string_to_wasm(mem);
798 let val = self.pop();
799 let malloc = self.cx.export_name_of(malloc);
800 let i = self.tmp();
801 let realloc = match realloc {
802 Some(f) => format!(", wasm.{}", self.cx.export_name_of(f)),
803 None => String::new(),
804 };
805 self.prelude(&format!(
806 "const ptr{i} = {pass}({val}, wasm.{malloc}{realloc});",
807 ));
808 self.prelude(&format!("const len{i} = WASM_VECTOR_LEN;"));
809 self.push(format!("ptr{i}"));
810 self.push(format!("len{i}"));
811 }
812}
813
814fn instruction(
815 js: &mut JsBuilder,
816 instr: &Instruction,
817 log_error: &mut bool,
818 constructor: &Option<String>,
819) -> Result<(), Error> {
820 fn wasm_to_string_enum(name: &str, index: &str) -> String {
821 format!("__wbindgen_enum_{name}[{index}]")
823 }
824 fn string_enum_to_wasm(name: &str, invalid: u32, enum_val: &str) -> String {
825 format!(
834 "(__wbindgen_enum_{name}.indexOf({enum_val}) + 1 || {invalid}) - 1",
835 invalid = invalid + 1
836 )
837 }
838
839 fn int128_to_int64x2(val: &str) -> (String, String) {
840 let low = val.to_owned();
844 let high = format!("{val} >> BigInt(64)");
845 (low, high)
846 }
847 fn int64x2_to_int128(low: String, high: String, signed: bool) -> String {
848 let low = format!("BigInt.asUintN(64, {low})");
849 if signed {
850 format!("({low} | ({high} << BigInt(64)))")
851 } else {
852 format!("({low} | (BigInt.asUintN(64, {high}) << BigInt(64)))")
853 }
854 }
855
856 match instr {
857 Instruction::ArgGet(n) => {
858 let arg = js.arg(*n).to_string();
859 js.push(arg);
860 }
861
862 Instruction::CallExport(_)
863 | Instruction::CallAdapter(_)
864 | Instruction::DeferFree { .. } => {
865 let mut should_check_aborted = js.cx.aux.wrapped_js_tag.is_some()
866 && matches!(
867 instr,
868 Instruction::CallExport(_) | Instruction::DeferFree { .. }
869 );
870 let invoc = Invocation::from(instr, js.cx.module);
871 let (mut params, results) = invoc.params_results(js.cx);
872
873 let mut args = Vec::new();
874 let tmp = js.tmp();
875 if invoc.defer() {
876 if let Instruction::DeferFree { .. } = instr {
877 params -= 1;
879 }
880 for (i, arg) in js.stack[js.stack.len() - params..].iter().enumerate() {
884 let name = format!("deferred{tmp}_{i}");
885 writeln!(js.pre_try, "let {name};").unwrap();
886 writeln!(js.prelude, "{name} = {arg};").unwrap();
887 args.push(name);
888 }
889 if let Instruction::DeferFree { align, .. } = instr {
890 args.push(align.to_string());
892 }
893 } else {
894 for _ in 0..params {
896 args.push(js.pop());
897 }
898 args.reverse();
899 }
900
901 let call = invoc.invoke(
903 js.cx,
904 &args,
905 &mut js.prelude,
906 log_error,
907 &mut should_check_aborted,
908 )?;
909
910 match (invoc.defer(), results) {
914 (true, 0) => {
915 js.finally(&maybe_wrap_try_catch(&call, should_check_aborted));
916 }
917 (true, _) => panic!("deferred calls must have no results"),
918 (false, 0) => js.prelude(&maybe_wrap_try_catch(&call, should_check_aborted)),
919 (false, n) => {
920 let body = if should_check_aborted {
921 format!(
922 "\
923 let ret;
924 {}",
925 &wrap_try_catch(&format!("ret = {call};"))
926 )
927 } else {
928 format!("const ret = {call};")
929 };
930 js.prelude(&body);
931 if n == 1 {
932 js.push("ret".to_string());
933 } else {
934 for i in 0..n {
935 js.push(format!("ret[{i}]"));
936 }
937 }
938 }
939 }
940 }
941
942 Instruction::Int32ToWasm => {
943 let val = js.pop();
944 js.assert_number(&val);
945 js.push(val);
946 }
947 Instruction::WasmToInt32 { unsigned_32 } => {
948 let val = js.pop();
949 if *unsigned_32 {
950 js.push(format!("{val} >>> 0"))
954 } else {
955 js.push(val)
956 }
957 }
958
959 Instruction::Int64ToWasm => {
960 let val = js.pop();
961 js.assert_bigint(&val);
962 js.push(val);
963 }
964 Instruction::WasmToInt64 { unsigned } => {
965 let val = js.pop();
966 if *unsigned {
967 js.push(format!("BigInt.asUintN(64, {val})"))
968 } else {
969 js.push(val)
970 }
971 }
972
973 Instruction::Int128ToWasm => {
974 let val = js.pop();
975 js.assert_bigint(&val);
976 let (low, high) = int128_to_int64x2(&val);
977 js.push(low);
978 js.push(high);
979 }
980 Instruction::WasmToInt128 { signed } => {
981 let high = js.pop();
982 let low = js.pop();
983 js.push(int64x2_to_int128(low, high, *signed));
984 }
985
986 Instruction::OptionInt128ToWasm => {
987 let val = js.pop();
988 js.cx.expose_is_like_none();
989 js.assert_optional_bigint(&val);
990 let (low, high) = int128_to_int64x2(&val);
991 js.push(format!("!isLikeNone({val})"));
992 js.push(format!("isLikeNone({val}) ? BigInt(0) : {low}"));
993 js.push(format!("isLikeNone({val}) ? BigInt(0) : {high}"));
994 }
995 Instruction::OptionWasmToInt128 { signed } => {
996 let high = js.pop();
997 let low = js.pop();
998 let present = js.pop();
999 let val = int64x2_to_int128(low, high, *signed);
1000 js.push(format!("{present} === 0 ? undefined : {val}"));
1001 }
1002
1003 Instruction::WasmToStringEnum { name } => {
1004 let index = js.pop();
1005 js.cx.expose_string_enum(name);
1006 js.push(wasm_to_string_enum(name, &index))
1007 }
1008
1009 Instruction::OptionWasmToStringEnum { name } => {
1010 let index = js.pop();
1014 js.cx.expose_string_enum(name);
1015 js.push(wasm_to_string_enum(name, &index))
1016 }
1017
1018 Instruction::StringEnumToWasm { name, invalid } => {
1019 let enum_val = js.pop();
1020 js.cx.expose_string_enum(name);
1021 js.push(string_enum_to_wasm(name, *invalid, &enum_val))
1022 }
1023
1024 Instruction::OptionStringEnumToWasm {
1025 name,
1026 invalid,
1027 hole,
1028 } => {
1029 let enum_val = js.pop();
1030 js.cx.expose_string_enum(name);
1031 let enum_val_expr = string_enum_to_wasm(name, *invalid, &enum_val);
1032 js.cx.expose_is_like_none();
1033
1034 js.push(format!(
1036 "isLikeNone({enum_val}) ? {hole} : ({enum_val_expr})"
1037 ))
1038 }
1039
1040 Instruction::MemoryToString(mem) => {
1041 let len = js.pop();
1042 let ptr = js.pop();
1043 let get = js.cx.expose_get_string_from_wasm(*mem);
1044 js.push(format!("{get}({ptr}, {len})"));
1045 }
1046
1047 Instruction::StringToMemory {
1048 mem,
1049 malloc,
1050 realloc,
1051 } => {
1052 js.string_to_memory(*mem, *malloc, *realloc);
1053 }
1054
1055 Instruction::Retptr { size } => {
1056 js.cx.inject_stack_pointer_shim()?;
1057 js.prelude(&format!(
1058 "const retptr = wasm.__wbindgen_add_to_stack_pointer(-{size});"
1059 ));
1060 js.finally(&format!("wasm.__wbindgen_add_to_stack_pointer({size});"));
1061 js.stack.push("retptr".to_string());
1062 }
1063
1064 Instruction::StoreRetptr { ty, offset, mem } => {
1065 let mem = js.cx.expose_dataview_memory(*mem);
1066 let (method, size) = match ty {
1067 AdapterType::I32 => ("setInt32", 4),
1068 AdapterType::I64 => ("setBigInt64", 8),
1069 AdapterType::F32 => ("setFloat32", 4),
1070 AdapterType::F64 => ("setFloat64", 8),
1071 other => bail!("invalid aggregate return type {other:?}"),
1072 };
1073 let val = js.pop();
1076 let mem_string = mem.access(js.cx.config.mode.emscripten());
1077 let expr = format!(
1078 "{mem_string}.{method}({} + {size} * {offset}, {val}, true);",
1079 js.arg(0),
1080 );
1081 js.prelude(&expr);
1082 }
1083
1084 Instruction::LoadRetptr { ty, offset, mem } => {
1085 let mem = js.cx.expose_dataview_memory(*mem);
1086 let (method, quads) = match ty {
1087 AdapterType::I32 => ("getInt32", 1),
1088 AdapterType::I64 => ("getBigInt64", 2),
1089 AdapterType::F32 => ("getFloat32", 1),
1090 AdapterType::F64 => ("getFloat64", 2),
1091 other => bail!("invalid aggregate return type {other:?}"),
1092 };
1093 let size = quads * 4;
1094 let scaled_offset = offset / quads;
1097 let mem_string = mem.access(js.cx.config.mode.emscripten());
1101
1102 let expr = format!("{mem_string}.{method}(retptr + {size} * {scaled_offset}, true)");
1103 js.prelude(&format!("var r{offset} = {expr};"));
1104 js.push(format!("r{offset}"));
1105 }
1106
1107 Instruction::I32FromBool => {
1108 let val = js.pop();
1109 js.assert_bool(&val);
1110 js.push(val);
1112 }
1113
1114 Instruction::I32FromStringFirstChar => {
1115 let val = js.pop();
1116 let i = js.tmp();
1117 js.prelude(&format!("const char{i} = {val}.codePointAt(0);"));
1118 let val = format!("char{i}");
1119 js.assert_char(&val);
1120 js.push(val);
1121 }
1122
1123 Instruction::I32FromExternrefOwned => {
1124 js.cx.expose_add_heap_object();
1125 let val = js.pop();
1126 js.push(format!("addHeapObject({val})"));
1127 }
1128
1129 Instruction::I32FromExternrefBorrow => {
1130 js.cx.expose_borrowed_objects();
1131 js.cx.expose_global_stack_pointer();
1132 let val = js.pop();
1133 js.push(format!("addBorrowedObject({val})"));
1134 js.finally("heap[stack_pointer++] = undefined;");
1135 }
1136
1137 Instruction::I32FromExternrefRustOwned { class } => {
1138 let val = js.pop();
1139 js.assert_class(&val, class);
1140 js.assert_not_moved(&val);
1141 let i = js.tmp();
1142 js.prelude(&format!("var ptr{i} = {val}.__destroy_into_raw();"));
1143 js.push(format!("ptr{i}"));
1144 }
1145
1146 Instruction::I32FromExternrefRustBorrow { class } => {
1147 let val = js.pop();
1148 js.assert_class(&val, class);
1149 js.assert_not_moved(&val);
1150 js.push(format!("{val}.__wbg_ptr"));
1151 }
1152
1153 Instruction::I32FromOptionRust { class } => {
1154 let val = js.pop();
1155 js.cx.expose_is_like_none();
1156 let i = js.tmp();
1157 js.prelude(&format!("let ptr{i} = 0;"));
1158 js.prelude(&format!("if (!isLikeNone({val})) {{"));
1159 js.assert_class(&val, class);
1160 js.assert_not_moved(&val);
1161 js.prelude(&format!("ptr{i} = {val}.__destroy_into_raw();"));
1162 js.prelude("}");
1163 js.push(format!("ptr{i}"));
1164 }
1165
1166 Instruction::I32FromOptionExternref { table_and_alloc } => {
1167 let val = js.pop();
1168 js.cx.expose_is_like_none();
1169 match table_and_alloc {
1170 Some((table, alloc)) => {
1171 let alloc = js.cx.expose_add_to_externref_table(*table, *alloc);
1172 js.push(format!("isLikeNone({val}) ? 0 : {alloc}({val})"));
1173 }
1174 None => {
1175 js.cx.expose_add_heap_object();
1176 js.push(format!("isLikeNone({val}) ? 0 : addHeapObject({val})"));
1177 }
1178 }
1179 }
1180
1181 Instruction::I32FromOptionU32Sentinel => {
1182 let val = js.pop();
1183 js.cx.expose_is_like_none();
1184 js.assert_optional_number(&val);
1185 js.push(format!("isLikeNone({val}) ? 0xFFFFFF : {val}"));
1186 }
1187
1188 Instruction::I32FromOptionBool => {
1189 let val = js.pop();
1190 js.cx.expose_is_like_none();
1191 js.assert_optional_bool(&val);
1192 js.push(format!("isLikeNone({val}) ? 0xFFFFFF : {val} ? 1 : 0"));
1193 }
1194
1195 Instruction::I32FromOptionChar => {
1196 let val = js.pop();
1197 let i = js.tmp();
1198 js.cx.expose_is_like_none();
1199 js.prelude(&format!(
1200 "const char{i} = isLikeNone({val}) ? 0xFFFFFF : {val}.codePointAt(0);"
1201 ));
1202 let val = format!("char{i}");
1203 js.cx.expose_assert_char();
1204 js.prelude(&format!(
1205 "if ({val} !== 0xFFFFFF) {{ _assertChar({val}); }}"
1206 ));
1207 js.push(val);
1208 }
1209
1210 Instruction::I32FromOptionEnum { hole } => {
1211 let val = js.pop();
1212 js.cx.expose_is_like_none();
1213 js.assert_optional_number(&val);
1214 js.push(format!("isLikeNone({val}) ? {hole} : {val}"));
1215 }
1216
1217 Instruction::F64FromOptionSentinelInt { signed } => {
1218 let val = js.pop();
1219 js.cx.expose_is_like_none();
1220 js.assert_optional_number(&val);
1221
1222 let op = if *signed { ">>" } else { ">>>" };
1238 js.push(format!("isLikeNone({val}) ? 0x100000001 : ({val}) {op} 0"));
1239 }
1240 Instruction::F64FromOptionSentinelF32 => {
1241 let val = js.pop();
1242 js.cx.expose_is_like_none();
1243 js.assert_optional_number(&val);
1244
1245 js.push(format!(
1251 "isLikeNone({val}) ? 0x100000001 : Math.fround({val})"
1252 ));
1253 }
1254
1255 Instruction::FromOptionNative { ty } => {
1256 let val = js.pop();
1257 js.cx.expose_is_like_none();
1258 if *ty == ValType::I64 {
1259 js.assert_optional_bigint(&val);
1260 } else {
1261 js.assert_optional_number(&val);
1262 }
1263 js.push(format!("!isLikeNone({val})"));
1264 js.push(format!(
1265 "isLikeNone({val}) ? {zero} : {val}",
1266 zero = if *ty == ValType::I64 {
1267 "BigInt(0)"
1270 } else {
1271 "0"
1272 }
1273 ));
1274 }
1275
1276 Instruction::VectorToMemory { kind, malloc, mem } => {
1277 let val = js.pop();
1278 let func = js.cx.pass_to_wasm_function(kind.clone(), *mem);
1279 let malloc = js.cx.export_name_of(*malloc);
1280 let i = js.tmp();
1281 js.prelude(&format!("const ptr{i} = {func}({val}, wasm.{malloc});",));
1282 js.prelude(&format!("const len{i} = WASM_VECTOR_LEN;"));
1283 js.push(format!("ptr{i}"));
1284 js.push(format!("len{i}"));
1285 }
1286
1287 Instruction::UnwrapResult { table_and_drop } => {
1288 let take_object = if let Some((table, drop)) = *table_and_drop {
1289 js.cx
1290 .expose_take_from_externref_table(table, drop)
1291 .to_string()
1292 } else {
1293 js.cx.expose_take_object();
1294 "takeObject".to_string()
1295 };
1296 let is_err = js.pop();
1303 let err = js.pop();
1304 js.prelude(&format!(
1305 "
1306 if ({is_err}) {{
1307 throw {take_object}({err});
1308 }}
1309 ",
1310 ));
1311 }
1312
1313 Instruction::UnwrapResultString { table_and_drop } => {
1314 let take_object = if let Some((table, drop)) = *table_and_drop {
1315 js.cx
1316 .expose_take_from_externref_table(table, drop)
1317 .to_string()
1318 } else {
1319 js.cx.expose_take_object();
1320 "takeObject".to_string()
1321 };
1322 let is_err = js.pop();
1323 let err = js.pop();
1324 let len = js.pop();
1325 let ptr = js.pop();
1326 let i = js.tmp();
1327 js.prelude(&format!(
1328 "
1329 var ptr{i} = {ptr};
1330 var len{i} = {len};
1331 if ({is_err}) {{
1332 ptr{i} = 0; len{i} = 0;
1333 throw {take_object}({err});
1334 }}
1335 ",
1336 ));
1337 js.push(format!("ptr{i}"));
1338 js.push(format!("len{i}"));
1339 }
1340
1341 Instruction::OptionString {
1342 mem,
1343 malloc,
1344 realloc,
1345 } => {
1346 let func = js.cx.expose_pass_string_to_wasm(*mem);
1347 js.cx.expose_is_like_none();
1348 let i = js.tmp();
1349 let malloc = js.cx.export_name_of(*malloc);
1350 let val = js.pop();
1351 let realloc = match realloc {
1352 Some(f) => format!(", wasm.{}", js.cx.export_name_of(*f)),
1353 None => String::new(),
1354 };
1355 js.prelude(&format!(
1356 "var ptr{i} = isLikeNone({val}) ? 0 : {func}({val}, wasm.{malloc}{realloc});",
1357 ));
1358 js.prelude(&format!("var len{i} = WASM_VECTOR_LEN;"));
1359 js.push(format!("ptr{i}"));
1360 js.push(format!("len{i}"));
1361 }
1362
1363 Instruction::OptionVector { kind, mem, malloc } => {
1364 let func = js.cx.pass_to_wasm_function(kind.clone(), *mem);
1365 js.cx.expose_is_like_none();
1366 let i = js.tmp();
1367 let malloc = js.cx.export_name_of(*malloc);
1368 let val = js.pop();
1369 js.prelude(&format!(
1370 "var ptr{i} = isLikeNone({val}) ? 0 : {func}({val}, wasm.{malloc});",
1371 ));
1372 js.prelude(&format!("var len{i} = WASM_VECTOR_LEN;"));
1373 js.push(format!("ptr{i}"));
1374 js.push(format!("len{i}"));
1375 }
1376
1377 Instruction::MutableSliceToMemory { kind, malloc, mem } => {
1378 let val = js.pop();
1380 let func = js.cx.pass_to_wasm_function(kind.clone(), *mem);
1381 let malloc = js.cx.export_name_of(*malloc);
1382 let i = js.tmp();
1383 js.prelude(&format!("var ptr{i} = {func}({val}, wasm.{malloc});",));
1384 js.prelude(&format!("var len{i} = WASM_VECTOR_LEN;"));
1385 js.push(format!("ptr{i}"));
1387 js.push(format!("len{i}"));
1388 js.push(val);
1391 }
1392
1393 Instruction::BoolFromI32 => {
1394 let val = js.pop();
1395 js.push(format!("{val} !== 0"));
1396 }
1397
1398 Instruction::ExternrefLoadOwned { table_and_drop } => {
1399 let take_object = if let Some((table, drop)) = *table_and_drop {
1400 js.cx
1401 .expose_take_from_externref_table(table, drop)
1402 .to_string()
1403 } else {
1404 js.cx.expose_take_object();
1405 "takeObject".to_string()
1406 };
1407 let val = js.pop();
1408 js.push(format!("{take_object}({val})"));
1409 }
1410
1411 Instruction::StringFromChar => {
1412 let val = js.pop();
1413 js.push(format!("String.fromCodePoint({val})"));
1414 }
1415
1416 Instruction::RustFromI32 { class } => {
1417 let val = js.pop();
1418 let resolved_class = js.cx.resolve_class_name(class);
1422 match constructor {
1423 Some(name) if js.cx.resolve_class_name(name) == resolved_class => {
1424 let identifier = js.cx.require_class_identifier(class);
1427 let (ptr_assignment, register_data) = if js.cx.config.generate_reset_state {
1428 (
1429 format!(
1430 "\
1431 this.__wbg_ptr = {val} >>> 0;
1432 this.__wbg_inst = __wbg_instance_id;
1433 "
1434 ),
1435 format!("{{ ptr: {val} >>> 0, instance: __wbg_instance_id }}"),
1436 )
1437 } else {
1438 (
1439 format!("this.__wbg_ptr = {val} >>> 0;"),
1440 "this.__wbg_ptr".to_string(),
1441 )
1442 };
1443
1444 js.prelude(&format!(
1445 "
1446 {ptr_assignment}
1447 {identifier}Finalization.register(this, {register_data}, this);
1448 "
1449 ));
1450 js.push(String::from("this"));
1451 }
1452 Some(_) | None => {
1453 let identifier = js.cx.require_class_wrap(class);
1454 js.push(format!("{identifier}.__wrap({val})"));
1455 }
1456 }
1457 }
1458
1459 Instruction::OptionRustFromI32 { class } => {
1460 assert!(constructor.is_none());
1461 let val = js.pop();
1462 let identifier = js.cx.require_class_wrap(class);
1463 js.push(format!(
1464 "{val} === 0 ? undefined : {identifier}.__wrap({val})",
1465 ));
1466 }
1467
1468 Instruction::CachedStringLoad {
1469 owned,
1470 mem,
1471 free,
1472 table,
1473 } => {
1474 let len = js.pop();
1475 let ptr = js.pop();
1476 let tmp = js.tmp();
1477
1478 let get = js.cx.expose_get_cached_string_from_wasm(*mem, *table);
1479
1480 js.prelude(&format!("var v{tmp} = {get}({ptr}, {len});"));
1481
1482 if *owned {
1483 let free = js.cx.export_name_of(*free);
1484 js.prelude(&format!(
1485 "if ({ptr} !== 0) {{ wasm.{free}({ptr}, {len}, 1); }}",
1486 ));
1487 }
1488
1489 js.push(format!("v{tmp}"));
1490 }
1491
1492 Instruction::TableGet => {
1493 let val = js.pop();
1494 js.cx.expose_get_object();
1495 js.push(format!("getObject({val})"));
1496 }
1497
1498 Instruction::Closure {
1499 adapter,
1500 nargs,
1501 mutable,
1502 dtor,
1503 } => {
1504 let b = js.pop();
1505 let a = js.pop();
1506 let wrapper = js.cx.export_adapter_name(*adapter);
1507
1508 if matches!(js.cx.config.mode, OutputMode::Emscripten) {
1509 js.cx.adapter_deps.insert(wrapper.clone());
1510 }
1511
1512 if let ClosureDtor::OwnClosure = dtor {
1516 let make_closure = if *mutable {
1518 js.cx.expose_make_mut_closure();
1519 "makeMutClosure"
1520 } else {
1521 js.cx.expose_make_closure();
1522 "makeClosure"
1523 };
1524
1525 js.push(format!("{make_closure}({a}, {b}, {wrapper})"));
1526 } else {
1527 let i = js.tmp();
1529 js.prelude(&format!("var state{i} = {{a: {a}, b: {b}}};"));
1530 let args = (0..*nargs)
1531 .map(|i| format!("arg{i}"))
1532 .collect::<Vec<_>>()
1533 .join(", ");
1534 if *mutable {
1535 js.prelude(&format!(
1539 "var cb{i} = ({args}) => {{
1540 const a = state{i}.a;
1541 state{i}.a = 0;
1542 try {{
1543 return {wrapper}(a, state{i}.b, {args});
1544 }} finally {{
1545 state{i}.a = a;
1546 }}
1547 }};",
1548 ));
1549 } else {
1550 js.prelude(&format!(
1551 "var cb{i} = ({args}) => {wrapper}(state{i}.a, state{i}.b, {args});",
1552 ));
1553 }
1554
1555 match dtor {
1556 ClosureDtor::OwnClosure => unreachable!(),
1557 ClosureDtor::Immediate => {
1558 js.finally(&format!("state{i}.a = 0;"));
1565 }
1566 ClosureDtor::Borrowed => {
1567 js.prelude(&format!("cb{i}._wbg_cb_unref = () => state{i}.a = 0;"));
1571 }
1572 }
1573 js.push(format!("cb{i}"));
1574 }
1575 }
1576
1577 Instruction::VectorLoad { kind, mem, free } => {
1578 let len = js.pop();
1579 let ptr = js.pop();
1580 let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem);
1581 let i = js.tmp();
1582 let free = js.cx.export_name_of(*free);
1583 js.prelude(&format!("var v{i} = {f}({ptr}, {len}).slice();"));
1584 js.prelude(&format!(
1585 "wasm.{free}({ptr}, {len} * {size}, {size});",
1586 size = kind.size()
1587 ));
1588 js.push(format!("v{i}"))
1589 }
1590
1591 Instruction::OptionVectorLoad { kind, mem, free } => {
1592 let len = js.pop();
1593 let ptr = js.pop();
1594 let f = js.cx.expose_get_vector_from_wasm(kind.clone(), *mem);
1595 let i = js.tmp();
1596 let free = js.cx.export_name_of(*free);
1597 js.prelude(&format!("let v{i};"));
1598 js.prelude(&format!("if ({ptr} !== 0) {{"));
1599 js.prelude(&format!("v{i} = {f}({ptr}, {len}).slice();"));
1600 js.prelude(&format!(
1601 "wasm.{free}({ptr}, {len} * {size}, {size});",
1602 size = kind.size()
1603 ));
1604 js.prelude("}");
1605 js.push(format!("v{i}"));
1606 }
1607
1608 Instruction::View { kind, mem } => {
1609 let len = js.pop();
1610 let ptr = js.pop();
1611 let f = match kind {
1613 VectorKind::Externref | VectorKind::NamedExternref(_) => {
1614 js.cx.expose_get_array_js_value_view_from_wasm(*mem)
1615 }
1616 _ => js.cx.expose_get_vector_from_wasm(kind.clone(), *mem),
1617 };
1618 js.push(format!("{f}({ptr}, {len})"));
1619 }
1620
1621 Instruction::OptionView { kind, mem } => {
1622 let len = js.pop();
1623 let ptr = js.pop();
1624 let f = match kind {
1626 VectorKind::Externref | VectorKind::NamedExternref(_) => {
1627 js.cx.expose_get_array_js_value_view_from_wasm(*mem)
1628 }
1629 _ => js.cx.expose_get_vector_from_wasm(kind.clone(), *mem),
1630 };
1631 js.push(format!("{ptr} === 0 ? undefined : {f}({ptr}, {len})"));
1632 }
1633
1634 Instruction::OptionF64Sentinel => {
1635 let val = js.pop();
1636 js.push(format!("{val} === 0x100000001 ? undefined : {val}"));
1637 }
1638
1639 Instruction::OptionU32Sentinel => {
1640 let val = js.pop();
1641 js.push(format!("{val} === 0xFFFFFF ? undefined : {val}"));
1642 }
1643
1644 Instruction::ToOptionNative { ty, signed } => {
1645 let val = js.pop();
1646 let present = js.pop();
1647 js.push(format!(
1648 "{present} === 0 ? undefined : {}",
1649 if *signed {
1650 val
1651 } else {
1652 match ty {
1653 ValType::I32 => format!("{val} >>> 0"),
1654 ValType::I64 => format!("BigInt.asUintN(64, {val})"),
1655 _ => unreachable!("unsigned non-integer"),
1656 }
1657 },
1658 ));
1659 }
1660
1661 Instruction::OptionBoolFromI32 => {
1662 let val = js.pop();
1663 js.push(format!("{val} === 0xFFFFFF ? undefined : {val} !== 0"));
1664 }
1665
1666 Instruction::OptionCharFromI32 => {
1667 let val = js.pop();
1668 js.push(format!(
1669 "{val} === 0xFFFFFF ? undefined : String.fromCodePoint({val})",
1670 ));
1671 }
1672
1673 Instruction::OptionEnumFromI32 { hole } => {
1674 let val = js.pop();
1675 js.push(format!("{val} === {hole} ? undefined : {val}"));
1676 }
1677
1678 Instruction::I32FromNonNull => {
1679 let val = js.pop();
1680 js.assert_non_null(&val);
1681 js.push(val);
1682 }
1683
1684 Instruction::I32FromOptionNonNull => {
1685 let val = js.pop();
1686 js.cx.expose_is_like_none();
1687 js.assert_optional_number(&val);
1688 js.push(format!("isLikeNone({val}) ? 0 : {val}"));
1689 }
1690
1691 Instruction::OptionNonNullFromI32 => {
1692 let val = js.pop();
1693 js.push(format!("{val} === 0 ? undefined : {val} >>> 0"));
1694 }
1695 }
1696 Ok(())
1697}
1698
1699enum Invocation {
1700 Core {
1701 id: walrus::FunctionId,
1702 export_id: Option<walrus::ExportId>,
1703 defer: bool,
1704 },
1705 Adapter(AdapterId),
1706}
1707
1708impl Invocation {
1709 fn from(instr: &Instruction, module: &Module) -> Invocation {
1710 use Instruction::*;
1711 match instr {
1712 DeferFree { free, .. } => Invocation::Core {
1713 id: *free,
1714 export_id: None,
1715 defer: true,
1716 },
1717
1718 CallExport(e) => match module.exports.get(*e).item {
1719 walrus::ExportItem::Function(id) => Invocation::Core {
1720 id,
1721 export_id: Some(*e),
1722 defer: false,
1723 },
1724 _ => panic!("can only call exported function"),
1725 },
1726
1727 CallAdapter(id) => Invocation::Adapter(*id),
1728
1729 _ => unreachable!(),
1731 }
1732 }
1733
1734 fn params_results(&self, cx: &Context) -> (usize, usize) {
1735 match self {
1736 Invocation::Core { id, .. } => {
1737 let ty = cx.module.funcs.get(*id).ty();
1738 let ty = cx.module.types.get(ty);
1739 (ty.params().len(), ty.results().len())
1740 }
1741 Invocation::Adapter(id) => {
1742 let adapter = &cx.wit.adapters[id];
1743 (adapter.params.len(), adapter.results.len())
1744 }
1745 }
1746 }
1747
1748 fn invoke(
1749 &self,
1750 cx: &mut Context,
1751 args: &[String],
1752 prelude: &mut String,
1753 log_error: &mut bool,
1754 handle_error: &mut bool,
1755 ) -> Result<String, Error> {
1756 match self {
1757 Invocation::Core { id, export_id, .. } => {
1758 let name = match export_id {
1759 Some(eid) => cx.module.exports.get(*eid).name.clone(),
1760 None => cx.export_name_of(*id),
1761 };
1762 Ok(format!("wasm.{name}({})", args.join(", ")))
1763 }
1764 Invocation::Adapter(id) => {
1765 let adapter = &cx.wit.adapters[id];
1766 let kind = match adapter.kind {
1767 AdapterKind::Import { kind, .. } => kind,
1768 AdapterKind::Local { .. } => {
1769 bail!("adapter-to-adapter calls not supported yet");
1770 }
1771 };
1772 let import = &cx.aux.import_map[id];
1773 let variadic = cx.aux.imports_with_variadic.contains(id);
1774 if cx.import_never_log_error(import) {
1775 *log_error = false;
1776 }
1777 if cx.import_never_handle_error(import) {
1778 *handle_error = false;
1779 }
1780 cx.invoke_import(import, kind, args, variadic, prelude)
1781 }
1782 }
1783 }
1784
1785 fn defer(&self) -> bool {
1786 match self {
1787 Invocation::Core { defer, .. } => *defer,
1788 _ => false,
1789 }
1790 }
1791}
1792
1793#[derive(Debug, Clone, Copy)]
1794enum TypePosition {
1795 Argument,
1796 Return,
1797}
1798
1799fn adapter2ts(
1800 ty: &AdapterType,
1801 position: TypePosition,
1802 dst: &mut String,
1803 refs: Option<&mut HashSet<TsReference>>,
1804 name_map: &HashMap<String, String>,
1805) {
1806 match ty {
1807 AdapterType::I32
1808 | AdapterType::S8
1809 | AdapterType::S16
1810 | AdapterType::S32
1811 | AdapterType::U8
1812 | AdapterType::U16
1813 | AdapterType::U32
1814 | AdapterType::F32
1815 | AdapterType::F64
1816 | AdapterType::NonNull => dst.push_str("number"),
1817 AdapterType::I64
1818 | AdapterType::S64
1819 | AdapterType::U64
1820 | AdapterType::S128
1821 | AdapterType::U128 => dst.push_str("bigint"),
1822 AdapterType::String => dst.push_str("string"),
1823 AdapterType::Externref => dst.push_str("any"),
1824 AdapterType::Bool => dst.push_str("boolean"),
1825 AdapterType::Vector(kind) => dst.push_str(&kind.js_ty()),
1826 AdapterType::Option(ty) => {
1827 adapter2ts(ty, position, dst, refs, name_map);
1828 dst.push_str(match position {
1829 TypePosition::Argument => " | null | undefined",
1830 TypePosition::Return => " | undefined",
1831 });
1832 }
1833 AdapterType::NamedExternref(name) => dst.push_str(name),
1834 AdapterType::Struct(name) => {
1835 let resolved = name_map.get(name).map(|s| s.as_str()).unwrap_or(name);
1836 dst.push_str(resolved);
1837 }
1838 AdapterType::Enum(name) => dst.push_str(name),
1839 AdapterType::StringEnum(name) => {
1840 if let Some(refs) = refs {
1841 refs.insert(TsReference::StringEnum(name.clone()));
1842 }
1843
1844 dst.push_str(name);
1845 }
1846 AdapterType::Function => dst.push_str("any"),
1847 }
1848}