1use crate::intrinsics::Intrinsic;
2use crate::source;
3use crate::{uwrite, uwriteln};
4use heck::*;
5use std::collections::BTreeSet;
6use std::fmt::Write;
7use std::mem;
8use wasmtime_environ::component::StringEncoding;
9use wit_parser::abi::{Bindgen, Bitcast, Instruction, WasmType};
10use wit_parser::*;
11
12#[derive(PartialEq)]
13pub enum ErrHandling {
14 None,
15 ThrowResultErr,
16 ResultCatchHandler,
17}
18
19pub struct FunctionBindgen<'a> {
20 pub intrinsics: &'a mut BTreeSet<Intrinsic>,
21 pub valid_lifting_optimization: bool,
22 pub sizes: &'a SizeAlign,
23 pub err: ErrHandling,
24 pub tmp: usize,
25 pub src: source::Source,
26 pub block_storage: Vec<source::Source>,
27 pub blocks: Vec<(String, Vec<String>)>,
28 pub params: Vec<String>,
29 pub memory: Option<&'a String>,
30 pub realloc: Option<&'a String>,
31 pub post_return: Option<&'a String>,
32 pub encoding: StringEncoding,
33 pub callee: &'a str,
34}
35
36impl FunctionBindgen<'_> {
37 fn tmp(&mut self) -> usize {
38 let ret = self.tmp;
39 self.tmp += 1;
40 ret
41 }
42 fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
43 self.intrinsics.insert(intrinsic);
44 return intrinsic.name().to_string();
45 }
46
47 fn clamp_guest<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
48 where
49 T: std::fmt::Display,
50 {
51 let clamp = self.intrinsic(Intrinsic::ClampGuest);
52 results.push(format!("{}({}, {}, {})", clamp, operands[0], min, max));
53 }
54
55 fn load(&mut self, method: &str, offset: i32, operands: &[String], results: &mut Vec<String>) {
56 let view = self.intrinsic(Intrinsic::DataView);
57 let memory = self.memory.as_ref().unwrap();
58 results.push(format!(
59 "{view}({memory}).{method}({} + {offset}, true)",
60 operands[0],
61 ));
62 }
63
64 fn store(&mut self, method: &str, offset: i32, operands: &[String]) {
65 let view = self.intrinsic(Intrinsic::DataView);
66 let memory = self.memory.as_ref().unwrap();
67 uwriteln!(
68 self.src,
69 "{view}({memory}).{method}({} + {offset}, {}, true);",
70 operands[1],
71 operands[0]
72 );
73 }
74
75 fn bind_results(&mut self, amt: usize, results: &mut Vec<String>) {
76 match amt {
77 0 => {}
78 1 => {
79 uwrite!(self.src, "const ret = ");
80 results.push("ret".to_string());
81 }
82 n => {
83 uwrite!(self.src, "const [");
84 for i in 0..n {
85 if i > 0 {
86 uwrite!(self.src, ", ");
87 }
88 uwrite!(self.src, "ret{}", i);
89 results.push(format!("ret{}", i));
90 }
91 uwrite!(self.src, "] = ");
92 }
93 }
94 }
95}
96
97impl Bindgen for FunctionBindgen<'_> {
98 type Operand = String;
99
100 fn sizes(&self) -> &SizeAlign {
101 &self.sizes
102 }
103
104 fn push_block(&mut self) {
105 let prev = mem::take(&mut self.src);
106 self.block_storage.push(prev);
107 }
108
109 fn finish_block(&mut self, operands: &mut Vec<String>) {
110 let to_restore = self.block_storage.pop().unwrap();
111 let src = mem::replace(&mut self.src, to_restore);
112 self.blocks.push((src.into(), mem::take(operands)));
113 }
114
115 fn return_pointer(&mut self, _size: usize, _align: usize) -> String {
116 unimplemented!();
117 }
118
119 fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool {
120 array_ty(resolve, ty).is_some()
121 }
122
123 fn emit(
124 &mut self,
125 resolve: &Resolve,
126 inst: &Instruction<'_>,
127 operands: &mut Vec<String>,
128 results: &mut Vec<String>,
129 ) {
130 match inst {
131 Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
132 Instruction::I32Const { val } => results.push(val.to_string()),
133 Instruction::ConstZero { tys } => {
134 for t in tys.iter() {
135 match t {
136 WasmType::I64 => results.push("0n".to_string()),
137 WasmType::I32 | WasmType::F32 | WasmType::F64 => {
138 results.push("0".to_string());
139 }
140 }
141 }
142 }
143
144 Instruction::U8FromI32 => self.clamp_guest(results, operands, u8::MIN, u8::MAX),
149 Instruction::S8FromI32 => self.clamp_guest(results, operands, i8::MIN, i8::MAX),
150 Instruction::U16FromI32 => self.clamp_guest(results, operands, u16::MIN, u16::MAX),
151 Instruction::S16FromI32 => self.clamp_guest(results, operands, i16::MIN, i16::MAX),
152 Instruction::U32FromI32 => {
155 results.push(format!("{} >>> 0", operands[0]));
156 }
157 Instruction::U64FromI64 => results.push(format!("BigInt.asUintN(64, {})", operands[0])),
160 Instruction::S32FromI32 | Instruction::S64FromI64 => {
163 results.push(operands.pop().unwrap())
164 }
165
166 Instruction::I32FromU8 => {
169 let conv = self.intrinsic(Intrinsic::ToUint8);
170 results.push(format!("{conv}({op})", op = operands[0]))
171 }
172 Instruction::I32FromS8 => {
173 let conv = self.intrinsic(Intrinsic::ToInt8);
174 results.push(format!("{conv}({op})", op = operands[0]))
175 }
176 Instruction::I32FromU16 => {
177 let conv = self.intrinsic(Intrinsic::ToUint16);
178 results.push(format!("{conv}({op})", op = operands[0]))
179 }
180 Instruction::I32FromS16 => {
181 let conv = self.intrinsic(Intrinsic::ToInt16);
182 results.push(format!("{conv}({op})", op = operands[0]))
183 }
184 Instruction::I32FromU32 => {
185 let conv = self.intrinsic(Intrinsic::ToUint32);
186 results.push(format!("{conv}({op})", op = operands[0]))
187 }
188 Instruction::I32FromS32 => {
189 let conv = self.intrinsic(Intrinsic::ToInt32);
190 results.push(format!("{conv}({op})", op = operands[0]))
191 }
192 Instruction::I64FromU64 => {
193 let conv = self.intrinsic(Intrinsic::ToBigUint64);
194 results.push(format!("{conv}({op})", op = operands[0]))
195 }
196 Instruction::I64FromS64 => {
197 let conv = self.intrinsic(Intrinsic::ToBigInt64);
198 results.push(format!("{conv}({op})", op = operands[0]))
199 }
200
201 Instruction::Float32FromF32 | Instruction::Float64FromF64 => {
205 results.push(operands.pop().unwrap())
206 }
207
208 Instruction::F32FromFloat32 | Instruction::F64FromFloat64 => {
209 results.push(format!("+{}", operands[0]));
211 }
212
213 Instruction::CharFromI32 => {
216 let validate = self.intrinsic(Intrinsic::ValidateGuestChar);
217 results.push(format!("{}({})", validate, operands[0]));
218 }
219
220 Instruction::I32FromChar => {
223 let validate = self.intrinsic(Intrinsic::ValidateHostChar);
224 results.push(format!("{}({})", validate, operands[0]));
225 }
226
227 Instruction::Bitcasts { casts } => {
228 for (cast, op) in casts.iter().zip(operands) {
229 match cast {
230 Bitcast::I32ToF32 => {
231 let cvt = self.intrinsic(Intrinsic::I32ToF32);
232 results.push(format!("{}({})", cvt, op));
233 }
234 Bitcast::F32ToI32 => {
235 let cvt = self.intrinsic(Intrinsic::F32ToI32);
236 results.push(format!("{}({})", cvt, op));
237 }
238 Bitcast::I64ToF64 => {
239 let cvt = self.intrinsic(Intrinsic::I64ToF64);
240 results.push(format!("{}({})", cvt, op));
241 }
242 Bitcast::F64ToI64 => {
243 let cvt = self.intrinsic(Intrinsic::F64ToI64);
244 results.push(format!("{}({})", cvt, op));
245 }
246 Bitcast::I32ToI64 => results.push(format!("BigInt({})", op)),
247 Bitcast::I64ToI32 => results.push(format!("Number({})", op)),
248 Bitcast::I64ToF32 => {
249 let cvt = self.intrinsic(Intrinsic::I32ToF32);
250 results.push(format!("{}(Number({}))", cvt, op));
251 }
252 Bitcast::F32ToI64 => {
253 let cvt = self.intrinsic(Intrinsic::F32ToI32);
254 results.push(format!("BigInt({}({}))", cvt, op));
255 }
256 Bitcast::None => results.push(op.clone()),
257 }
258 }
259 }
260
261 Instruction::BoolFromI32 => {
262 let tmp = self.tmp();
263 uwrite!(self.src, "const bool{} = {};\n", tmp, operands[0]);
264 if self.valid_lifting_optimization {
265 results.push(format!("!!bool{tmp}"));
266 } else {
267 let throw = self.intrinsic(Intrinsic::ThrowInvalidBool);
268 results.push(format!(
269 "bool{tmp} == 0 ? false : (bool{tmp} == 1 ? true : {throw}())"
270 ));
271 }
272 }
273 Instruction::I32FromBool => {
274 results.push(format!("{} ? 1 : 0", operands[0]));
275 }
276
277 Instruction::RecordLower { record, .. } => {
278 let tmp = self.tmp();
281 let mut expr = "const {".to_string();
282 for (i, field) in record.fields.iter().enumerate() {
283 if i > 0 {
284 expr.push_str(", ");
285 }
286 let name = format!("v{}_{}", tmp, i);
287 expr.push_str(&field.name.to_lower_camel_case());
288 expr.push_str(": ");
289 expr.push_str(&name);
290 results.push(name);
291 }
292 uwrite!(self.src, "{} }} = {};\n", expr, operands[0]);
293 }
294
295 Instruction::RecordLift { record, .. } => {
296 let mut result = "{\n".to_string();
300 for (field, op) in record.fields.iter().zip(operands) {
301 result.push_str(&format!("{}: {},\n", field.name.to_lower_camel_case(), op));
302 }
303 result.push_str("}");
304 results.push(result);
305 }
306
307 Instruction::TupleLower { tuple, .. } => {
308 let tmp = self.tmp();
312 let mut expr = "const [".to_string();
313 for i in 0..tuple.types.len() {
314 if i > 0 {
315 expr.push_str(", ");
316 }
317 let name = format!("tuple{}_{}", tmp, i);
318 expr.push_str(&name);
319 results.push(name);
320 }
321 uwrite!(self.src, "{}] = {};\n", expr, operands[0]);
322 }
323
324 Instruction::TupleLift { .. } => {
325 results.push(format!("[{}]", operands.join(", ")));
328 }
329
330 Instruction::FlagsLower { flags, .. } => {
332 let op0 = &operands[0];
333
334 for _ in 0..flags.repr().count() {
336 let tmp = self.tmp();
337 let name = format!("flags{tmp}");
338 uwrite!(self.src, "let {name} = 0;\n");
341 results.push(name);
342 }
343
344 uwrite!(
345 self.src,
346 "if (typeof {op0} === 'object' && {op0} !== null) {{\n"
347 );
348
349 for (i, chunk) in flags.flags.chunks(32).enumerate() {
350 let result_name = &results[i];
351
352 uwrite!(self.src, "{result_name} = ");
353 for (i, flag) in chunk.iter().enumerate() {
354 if i != 0 {
355 uwrite!(self.src, " | ");
356 }
357
358 let flag = flag.name.to_lower_camel_case();
359 uwrite!(self.src, "Boolean({op0}.{flag}) << {i}");
360 }
361 uwrite!(self.src, ";\n");
362 }
363
364 uwrite!(self.src, "\
365 }} else if ({op0} !== null && {op0} !== undefined) {{
366 throw new TypeError('only an object, undefined or null can be converted to flags');
367 }}
368 ");
369
370 }
374
375 Instruction::FlagsLift { flags, .. } => {
376 let tmp = self.tmp();
377 results.push(format!("flags{tmp}"));
378
379 if let Some(op) = operands.last() {
380 if flags.flags.len() % 32 != 0 && !self.valid_lifting_optimization {
384 let mask: u32 = 0xffffffff << (flags.flags.len() % 32);
385 uwriteln!(
386 self.src,
387 "if (({op} & {mask}) !== 0) {{
388 throw new TypeError('flags have extraneous bits set');
389 }}"
390 );
391 }
392 }
393
394 uwriteln!(self.src, "const flags{tmp} = {{");
395
396 for (i, flag) in flags.flags.iter().enumerate() {
397 let flag = flag.name.to_lower_camel_case();
398 let op = &operands[i / 32];
399 let mask: u32 = 1 << (i % 32);
400 uwriteln!(self.src, "{flag}: Boolean({op} & {mask}),");
401 }
402
403 uwriteln!(self.src, "}};");
404 }
405
406 Instruction::VariantPayloadName => results.push("e".to_string()),
407
408 Instruction::VariantLower {
409 variant,
410 results: result_types,
411 name,
412 ..
413 } => {
414 let blocks = self
415 .blocks
416 .drain(self.blocks.len() - variant.cases.len()..)
417 .collect::<Vec<_>>();
418 let tmp = self.tmp();
419 let operand = &operands[0];
420 uwriteln!(self.src, "const variant{tmp} = {operand};");
421
422 for i in 0..result_types.len() {
423 uwriteln!(self.src, "let variant{tmp}_{i};");
424 results.push(format!("variant{}_{}", tmp, i));
425 }
426
427 let expr_to_match = format!("variant{}.tag", tmp);
428
429 uwriteln!(self.src, "switch ({expr_to_match}) {{");
430 for (case, (block, block_results)) in variant.cases.iter().zip(blocks) {
431 uwriteln!(self.src, "case '{}': {{", case.name.as_str());
432 if case.ty.is_some() {
433 uwriteln!(self.src, "const e = variant{tmp}.val;");
434 }
435 self.src.push_str(&block);
436
437 for (i, result) in block_results.iter().enumerate() {
438 uwriteln!(self.src, "variant{tmp}_{i} = {result};");
439 }
440 uwriteln!(
441 self.src,
442 "break;
443 }}"
444 );
445 }
446 let variant_name = name.to_upper_camel_case();
447 uwriteln!(
448 self.src,
449 "default: {{
450 throw new TypeError('invalid variant specified for {variant_name}');
451 }}"
452 );
453 uwriteln!(self.src, "}}");
454 }
455
456 Instruction::VariantLift { variant, name, .. } => {
457 let blocks = self
458 .blocks
459 .drain(self.blocks.len() - variant.cases.len()..)
460 .collect::<Vec<_>>();
461
462 let tmp = self.tmp();
463 let operand = &operands[0];
464
465 uwriteln!(
466 self.src,
467 "let variant{tmp};
468 switch ({operand}) {{"
469 );
470
471 for (i, (case, (block, block_results))) in
472 variant.cases.iter().zip(blocks).enumerate()
473 {
474 let tag = case.name.as_str();
475 uwriteln!(
476 self.src,
477 "case {i}: {{
478 {block}\
479 variant{tmp} = {{
480 tag: '{tag}',"
481 );
482 if case.ty.is_some() {
483 assert!(block_results.len() == 1);
484 uwriteln!(self.src, " val: {}", block_results[0]);
485 } else {
486 assert!(block_results.len() == 0);
487 }
488 uwriteln!(
489 self.src,
490 " }};
491 break;
492 }}"
493 );
494 }
495 let variant_name = name.to_upper_camel_case();
496 if !self.valid_lifting_optimization {
497 uwriteln!(
498 self.src,
499 "default: {{
500 throw new TypeError('invalid variant discriminant for {variant_name}');
501 }}"
502 );
503 }
504 uwriteln!(self.src, "}}");
505 results.push(format!("variant{}", tmp));
506 }
507
508 Instruction::UnionLower {
509 union,
510 results: result_types,
511 name,
512 ..
513 } => {
514 let blocks = self
515 .blocks
516 .drain(self.blocks.len() - union.cases.len()..)
517 .collect::<Vec<_>>();
518 let tmp = self.tmp();
519 let op0 = &operands[0];
520 uwriteln!(self.src, "const union{tmp} = {op0};");
521
522 for i in 0..result_types.len() {
523 uwriteln!(self.src, "let union{tmp}_{i};");
524 results.push(format!("union{tmp}_{i}"));
525 }
526
527 uwriteln!(self.src, "switch (union{tmp}.tag) {{");
528 for (i, (_case, (block, block_results))) in
529 union.cases.iter().zip(blocks).enumerate()
530 {
531 uwriteln!(
532 self.src,
533 "case {i}: {{
534 const e = union{tmp}.val;
535 {block}"
536 );
537 for (i, result) in block_results.iter().enumerate() {
538 uwriteln!(self.src, "union{tmp}_{i} = {result};");
539 }
540 uwriteln!(
541 self.src,
542 "break;
543 }}"
544 );
545 }
546 let name = name.to_upper_camel_case();
547 uwriteln!(
548 self.src,
549 "default: {{
550 throw new TypeError('invalid union specified for {name}');
551 }}"
552 );
553 uwriteln!(self.src, "}}");
554 }
555
556 Instruction::UnionLift { union, name, .. } => {
557 let blocks = self
558 .blocks
559 .drain(self.blocks.len() - union.cases.len()..)
560 .collect::<Vec<_>>();
561
562 let tmp = self.tmp();
563 let operand = &operands[0];
564
565 uwriteln!(
566 self.src,
567 "let union{tmp};
568 switch ({operand}) {{"
569 );
570 for (i, (_case, (block, block_results))) in
571 union.cases.iter().zip(blocks).enumerate()
572 {
573 assert!(block_results.len() == 1);
574 let block_result = &block_results[0];
575 uwriteln!(
576 self.src,
577 "case {i}: {{
578 {block}\
579 union{tmp} = {{
580 tag: {i},
581 val: {block_result},
582 }};
583 break;
584 }}"
585 );
586 }
587 let name = name.to_upper_camel_case();
588 if !self.valid_lifting_optimization {
589 uwriteln!(
590 self.src,
591 "default: {{
592 throw new TypeError('invalid union discriminant for {name}');
593 }}"
594 );
595 }
596 uwriteln!(self.src, "}}");
597 results.push(format!("union{tmp}"));
598 }
599
600 Instruction::OptionLower {
601 payload,
602 results: result_types,
603 ..
604 } => {
605 let (mut some, some_results) = self.blocks.pop().unwrap();
606 let (mut none, none_results) = self.blocks.pop().unwrap();
607
608 let tmp = self.tmp();
609 let operand = &operands[0];
610 uwriteln!(self.src, "const variant{tmp} = {operand};");
611
612 for i in 0..result_types.len() {
613 uwriteln!(self.src, "let variant{tmp}_{i};");
614 results.push(format!("variant{tmp}_{i}"));
615
616 let some_result = &some_results[i];
617 let none_result = &none_results[i];
618 uwriteln!(some, "variant{tmp}_{i} = {some_result};");
619 uwriteln!(none, "variant{tmp}_{i} = {none_result};");
620 }
621
622 if maybe_null(resolve, payload) {
623 uwriteln!(
624 self.src,
625 "switch (variant{tmp}.tag) {{
626 case 'none': {{
627 {none}\
628 break;
629 }}
630 case 'some': {{
631 const e = variant{tmp}.val;
632 {some}\
633 break;
634 }}
635 default: {{
636 throw new TypeError('invalid variant specified for option');
637 }}
638 }}"
639 );
640 } else {
641 uwriteln!(
642 self.src,
643 "if (variant{tmp} === null || variant{tmp} === undefined) {{
644 {none}\
645 }} else {{
646 const e = variant{tmp};
647 {some}\
648 }}"
649 );
650 }
651 }
652
653 Instruction::OptionLift { payload, .. } => {
654 let (some, some_results) = self.blocks.pop().unwrap();
655 let (none, none_results) = self.blocks.pop().unwrap();
656 assert!(none_results.len() == 0);
657 assert!(some_results.len() == 1);
658 let some_result = &some_results[0];
659
660 let tmp = self.tmp();
661 let operand = &operands[0];
662
663 let (v_none, v_some) = if maybe_null(resolve, payload) {
664 (
665 "{ tag: 'none' }",
666 format!(
667 "{{
668 tag: 'some',
669 val: {some_result}
670 }}"
671 ),
672 )
673 } else {
674 ("null", some_result.into())
675 };
676
677 if !self.valid_lifting_optimization {
678 uwriteln!(
679 self.src,
680 "let variant{tmp};
681 switch ({operand}) {{
682 case 0: {{
683 {none}\
684 variant{tmp} = {v_none};
685 break;
686 }}
687 case 1: {{
688 {some}\
689 variant{tmp} = {v_some};
690 break;
691 }}
692 default: {{
693 throw new TypeError('invalid variant discriminant for option');
694 }}
695 }}"
696 );
697 } else {
698 uwriteln!(
699 self.src,
700 "let variant{tmp};
701 if ({operand}) {{
702 {some}\
703 variant{tmp} = {v_some};
704 }} else {{
705 {none}\
706 variant{tmp} = {v_none};
707 }}"
708 );
709 }
710
711 results.push(format!("variant{tmp}"));
712 }
713
714 Instruction::ResultLower {
715 results: result_types,
716 ..
717 } => {
718 let (mut err, err_results) = self.blocks.pop().unwrap();
719 let (mut ok, ok_results) = self.blocks.pop().unwrap();
720
721 let tmp = self.tmp();
722 let operand = &operands[0];
723 uwriteln!(self.src, "const variant{tmp} = {operand};");
724
725 for i in 0..result_types.len() {
726 uwriteln!(self.src, "let variant{tmp}_{i};");
727 results.push(format!("variant{tmp}_{i}"));
728
729 let ok_result = &ok_results[i];
730 let err_result = &err_results[i];
731 uwriteln!(ok, "variant{tmp}_{i} = {ok_result};");
732 uwriteln!(err, "variant{tmp}_{i} = {err_result};");
733 }
734
735 uwriteln!(
736 self.src,
737 "switch (variant{tmp}.tag) {{
738 case 'ok': {{
739 const e = variant{tmp}.val;
740 {ok}\
741 break;
742 }}
743 case 'err': {{
744 const e = variant{tmp}.val;
745 {err}\
746 break;
747 }}
748 default: {{
749 throw new TypeError('invalid variant specified for result');
750 }}
751 }}"
752 );
753 }
754
755 Instruction::ResultLift { result, .. } => {
756 let (err, err_results) = self.blocks.pop().unwrap();
757 let (ok, ok_results) = self.blocks.pop().unwrap();
758 let ok_result = if result.ok.is_some() {
759 assert_eq!(ok_results.len(), 1);
760 format!("{}", ok_results[0])
761 } else {
762 assert_eq!(ok_results.len(), 0);
763 String::from("undefined")
764 };
765 let err_result = if result.err.is_some() {
766 assert_eq!(err_results.len(), 1);
767 format!("{}", err_results[0])
768 } else {
769 assert_eq!(err_results.len(), 0);
770 String::from("undefined")
771 };
772 let tmp = self.tmp();
773 let op0 = &operands[0];
774
775 if !self.valid_lifting_optimization {
776 uwriteln!(
777 self.src,
778 "let variant{tmp};
779 switch ({op0}) {{
780 case 0: {{
781 {ok}\
782 variant{tmp} = {{
783 tag: 'ok',
784 val: {ok_result}
785 }};
786 break;
787 }}
788 case 1: {{
789 {err}\
790 variant{tmp} = {{
791 tag: 'err',
792 val: {err_result}
793 }};
794 break;
795 }}
796 default: {{
797 throw new TypeError('invalid variant discriminant for expected');
798 }}
799 }}"
800 );
801 } else {
802 uwriteln!(
803 self.src,
804 "let variant{tmp};
805 if ({op0}) {{
806 {err}\
807 variant{tmp} = {{
808 tag: 'err',
809 val: {err_result}
810 }};
811 }} else {{
812 {ok}\
813 variant{tmp} = {{
814 tag: 'ok',
815 val: {ok_result}
816 }};
817 }}"
818 );
819 }
820 results.push(format!("variant{tmp}"));
821 }
822
823 Instruction::EnumLower { name, enum_, .. } => {
825 let tmp = self.tmp();
826
827 let operand = &operands[0];
828 uwriteln!(self.src, "const val{tmp} = {operand};");
829
830 uwriteln!(
832 self.src,
833 "let enum{tmp};
834 switch (val{tmp}) {{"
835 );
836 for (i, case) in enum_.cases.iter().enumerate() {
837 uwriteln!(
838 self.src,
839 "case '{case}': {{
840 enum{tmp} = {i};
841 break;
842 }}",
843 case = case.name
844 );
845 }
846 uwriteln!(self.src, "default: {{");
847 if !self.valid_lifting_optimization {
848 uwriteln!(
849 self.src,
850 "if (({operand}) instanceof Error) {{
851 console.error({operand});
852 }}"
853 );
854 }
855 uwriteln!(
856 self.src,
857 "
858 throw new TypeError(`\"${{val{tmp}}}\" is not one of the cases of {name}`);
859 }}
860 }}"
861 );
862
863 results.push(format!("enum{tmp}"));
864 }
865
866 Instruction::EnumLift { name, enum_, .. } => {
867 let tmp = self.tmp();
868
869 uwriteln!(
870 self.src,
871 "let enum{tmp};
872 switch ({}) {{",
873 operands[0]
874 );
875 for (i, case) in enum_.cases.iter().enumerate() {
876 uwriteln!(
877 self.src,
878 "case {i}: {{
879 enum{tmp} = '{case}';
880 break;
881 }}",
882 case = case.name
883 );
884 }
885 if !self.valid_lifting_optimization {
886 let name = name.to_upper_camel_case();
887 uwriteln!(
888 self.src,
889 "default: {{
890 throw new TypeError('invalid discriminant specified for {name}');
891 }}",
892 );
893 }
894 uwriteln!(self.src, "}}");
895
896 results.push(format!("enum{tmp}"));
897 }
898
899 Instruction::ListCanonLower { element, .. } => {
900 let tmp = self.tmp();
901 let memory = self.memory.as_ref().unwrap();
902 let realloc = self.realloc.unwrap();
903
904 let size = self.sizes.size(element);
905 let align = self.sizes.align(element);
906 uwriteln!(self.src, "const val{tmp} = {};", operands[0]);
907 if matches!(element, Type::U8) {
908 uwriteln!(self.src, "const len{tmp} = val{tmp}.byteLength;");
909 } else {
910 uwriteln!(self.src, "const len{tmp} = val{tmp}.length;");
911 }
912 uwriteln!(
913 self.src,
914 "const ptr{tmp} = {realloc}(0, 0, {align}, len{tmp} * {size});"
915 );
916 if matches!(element, Type::U8) {
918 uwriteln!(
919 self.src,
920 "const src{tmp} = new Uint8Array(val{tmp}.buffer || val{tmp}, val{tmp}.byteOffset, len{tmp} * {size});",
921 );
922 } else {
923 uwriteln!(
924 self.src,
925 "const src{tmp} = new Uint8Array(val{tmp}.buffer, val{tmp}.byteOffset, len{tmp} * {size});",
926 );
927 }
928 uwriteln!(
929 self.src,
930 "(new Uint8Array({memory}.buffer, ptr{tmp}, len{tmp} * {size})).set(src{tmp});",
931 );
932 results.push(format!("ptr{}", tmp));
933 results.push(format!("len{}", tmp));
934 }
935 Instruction::ListCanonLift { element, .. } => {
936 let tmp = self.tmp();
937 let memory = self.memory.as_ref().unwrap();
938 uwriteln!(self.src, "const ptr{tmp} = {};", operands[0]);
939 uwriteln!(self.src, "const len{tmp} = {};", operands[1]);
940 let array_ty = array_ty(resolve, element).unwrap();
942 uwriteln!(
943 self.src,
944 "const result{tmp} = new {array_ty}({memory}.buffer.slice(ptr{tmp}, ptr{tmp} + len{tmp} * {}));",
945 self.sizes.size(element),
946 );
947 results.push(format!("result{tmp}"));
948 }
949 Instruction::StringLower { .. } => {
950 assert!(matches!(
952 self.encoding,
953 StringEncoding::Utf8 | StringEncoding::Utf16
954 ));
955 let intrinsic = if self.encoding == StringEncoding::Utf16 {
956 Intrinsic::Utf16Encode
957 } else {
958 Intrinsic::Utf8Encode
959 };
960 let encode = self.intrinsic(intrinsic);
961 let tmp = self.tmp();
962 let memory = self.memory.as_ref().unwrap();
963 let str = String::from("cabi_realloc");
964 let realloc = self.realloc.unwrap_or(&str);
965 uwriteln!(
966 self.src,
967 "const ptr{tmp} = {encode}({}, {realloc}, {memory});",
968 operands[0],
969 );
970 if self.encoding == StringEncoding::Utf8 {
971 let encoded_len = self.intrinsic(Intrinsic::Utf8EncodedLen);
972 uwriteln!(self.src, "const len{tmp} = {encoded_len};");
973 } else {
974 uwriteln!(self.src, "const len{tmp} = {}.length;", operands[0]);
975 }
976 results.push(format!("ptr{}", tmp));
977 results.push(format!("len{}", tmp));
978 }
979 Instruction::StringLift => {
980 assert!(matches!(
982 self.encoding,
983 StringEncoding::Utf8 | StringEncoding::Utf16
984 ));
985 let intrinsic = if self.encoding == StringEncoding::Utf16 {
986 Intrinsic::Utf16Decoder
987 } else {
988 Intrinsic::Utf8Decoder
989 };
990 let decoder = self.intrinsic(intrinsic);
991 let tmp = self.tmp();
992 let memory = self.memory.as_ref().unwrap();
993 uwriteln!(self.src, "const ptr{tmp} = {};", operands[0]);
994 uwriteln!(self.src, "const len{tmp} = {};", operands[1]);
995 uwriteln!(
996 self.src,
997 "const result{tmp} = {decoder}.decode(new Uint{}Array({memory}.buffer, ptr{tmp}, len{tmp}));",
998 if self.encoding == StringEncoding::Utf16 { "16" } else { "8" }
999 );
1000 results.push(format!("result{tmp}"));
1001 }
1002
1003 Instruction::ListLower { element, .. } => {
1004 let (body, body_results) = self.blocks.pop().unwrap();
1005 assert!(body_results.is_empty());
1006 let tmp = self.tmp();
1007 let vec = format!("vec{}", tmp);
1008 let result = format!("result{}", tmp);
1009 let len = format!("len{}", tmp);
1010 let size = self.sizes.size(element);
1011 let align = self.sizes.align(element);
1012
1013 uwriteln!(self.src, "const {vec} = {};", operands[0]);
1016 uwriteln!(self.src, "const {len} = {vec}.length;");
1017
1018 let realloc = self.realloc.as_ref().unwrap();
1020 uwriteln!(
1021 self.src,
1022 "const {result} = {realloc}(0, 0, {align}, {len} * {size});"
1023 );
1024
1025 uwriteln!(self.src, "for (let i = 0; i < {vec}.length; i++) {{");
1028 uwriteln!(self.src, "const e = {vec}[i];");
1029 uwrite!(self.src, "const base = {result} + i * {size};");
1030 self.src.push_str(&body);
1031 uwrite!(self.src, "}}\n");
1032
1033 results.push(result);
1034 results.push(len);
1035 }
1036
1037 Instruction::ListLift { element, .. } => {
1038 let (body, body_results) = self.blocks.pop().unwrap();
1039 let tmp = self.tmp();
1040 let size = self.sizes.size(element);
1041 let len = format!("len{tmp}");
1042 uwriteln!(self.src, "const {len} = {};", operands[1]);
1043 let base = format!("base{tmp}");
1044 uwriteln!(self.src, "const {base} = {};", operands[0]);
1045 let result = format!("result{tmp}");
1046 uwriteln!(self.src, "const {result} = [];");
1047 results.push(result.clone());
1048
1049 uwriteln!(self.src, "for (let i = 0; i < {len}; i++) {{");
1050 uwriteln!(self.src, "const base = {base} + i * {size};");
1051 self.src.push_str(&body);
1052 assert_eq!(body_results.len(), 1);
1053 uwriteln!(self.src, "{result}.push({});", body_results[0]);
1054 uwrite!(self.src, "}}\n");
1055 }
1056
1057 Instruction::IterElem { .. } => results.push("e".to_string()),
1058
1059 Instruction::IterBasePointer => results.push("base".to_string()),
1060
1061 Instruction::CallWasm { sig, .. } => {
1062 self.bind_results(sig.results.len(), results);
1063 uwriteln!(self.src, "{}({});", self.callee, operands.join(", "));
1064 }
1065
1066 Instruction::CallInterface { func } => {
1067 if self.err == ErrHandling::ResultCatchHandler {
1068 let err_payload = self.intrinsic(Intrinsic::GetErrorPayload);
1069 uwriteln!(
1070 self.src,
1071 "let ret;
1072 try {{
1073 ret = {{ tag: 'ok', val: {}({}) }};
1074 }} catch (e) {{
1075 ret = {{ tag: 'err', val: {}(e) }};
1076 }}",
1077 self.callee,
1078 operands.join(", "),
1079 err_payload,
1080 );
1081 results.push("ret".to_string());
1082 } else {
1083 self.bind_results(func.results.len(), results);
1084 uwriteln!(self.src, "{}({});", self.callee, operands.join(", "));
1085 }
1086 }
1087
1088 Instruction::Return { amt, .. } => {
1089 if let Some(f) = &self.post_return {
1090 uwriteln!(self.src, "{f}(ret);");
1091 }
1092
1093 if self.err == ErrHandling::ThrowResultErr {
1094 let component_err = self.intrinsic(Intrinsic::ComponentError);
1095 let operand = &operands[0];
1096 uwriteln!(
1097 self.src,
1098 "if ({operand}.tag === 'err') {{
1099 throw new {component_err}({operand}.val);
1100 }}
1101 return {operand}.val;"
1102 );
1103 } else {
1104 match amt {
1105 0 => {}
1106 1 => uwriteln!(self.src, "return {};", operands[0]),
1107 _ => uwriteln!(self.src, "return [{}];", operands.join(", ")),
1108 }
1109 }
1110 }
1111
1112 Instruction::I32Load { offset } => self.load("getInt32", *offset, operands, results),
1113 Instruction::I64Load { offset } => self.load("getBigInt64", *offset, operands, results),
1114 Instruction::F32Load { offset } => self.load("getFloat32", *offset, operands, results),
1115 Instruction::F64Load { offset } => self.load("getFloat64", *offset, operands, results),
1116 Instruction::I32Load8U { offset } => self.load("getUint8", *offset, operands, results),
1117 Instruction::I32Load8S { offset } => self.load("getInt8", *offset, operands, results),
1118 Instruction::I32Load16U { offset } => {
1119 self.load("getUint16", *offset, operands, results)
1120 }
1121 Instruction::I32Load16S { offset } => self.load("getInt16", *offset, operands, results),
1122 Instruction::I32Store { offset } => self.store("setInt32", *offset, operands),
1123 Instruction::I64Store { offset } => self.store("setBigInt64", *offset, operands),
1124 Instruction::F32Store { offset } => self.store("setFloat32", *offset, operands),
1125 Instruction::F64Store { offset } => self.store("setFloat64", *offset, operands),
1126 Instruction::I32Store8 { offset } => self.store("setInt8", *offset, operands),
1127 Instruction::I32Store16 { offset } => self.store("setInt16", *offset, operands),
1128
1129 Instruction::Malloc { size, align, .. } => {
1130 let tmp = self.tmp();
1131 let realloc = self.realloc.as_ref().unwrap();
1132 let ptr = format!("ptr{tmp}");
1133 uwriteln!(self.src, "const {ptr} = {realloc}(0, 0, {align}, {size});",);
1134 results.push(ptr);
1135 }
1136
1137 i => unimplemented!("{:?}", i),
1138 }
1139 }
1140}
1141
1142pub fn as_nullable<'a>(resolve: &'a Resolve, ty: &'a Type) -> Option<&'a Type> {
1147 let id = match ty {
1148 Type::Id(id) => *id,
1149 _ => return None,
1150 };
1151 match &resolve.types[id].kind {
1152 TypeDefKind::Option(t) => {
1168 if !maybe_null(resolve, t) {
1169 Some(t)
1170 } else {
1171 None
1172 }
1173 }
1174 TypeDefKind::Type(t) => as_nullable(resolve, t),
1175 _ => None,
1176 }
1177}
1178
1179pub fn maybe_null(resolve: &Resolve, ty: &Type) -> bool {
1180 as_nullable(resolve, ty).is_some()
1181}
1182
1183pub fn array_ty(resolve: &Resolve, ty: &Type) -> Option<&'static str> {
1184 match ty {
1185 Type::Bool => None,
1186 Type::U8 => Some("Uint8Array"),
1187 Type::S8 => Some("Int8Array"),
1188 Type::U16 => Some("Uint16Array"),
1189 Type::S16 => Some("Int16Array"),
1190 Type::U32 => Some("Uint32Array"),
1191 Type::S32 => Some("Int32Array"),
1192 Type::U64 => Some("BigUint64Array"),
1193 Type::S64 => Some("BigInt64Array"),
1194 Type::Float32 => Some("Float32Array"),
1195 Type::Float64 => Some("Float64Array"),
1196 Type::Char => None,
1197 Type::String => None,
1198 Type::Id(id) => match &resolve.types[*id].kind {
1199 TypeDefKind::Type(t) => array_ty(resolve, t),
1200 _ => None,
1201 },
1202 }
1203}