1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt::Write;
3use std::mem;
4
5use heck::{ToLowerCamelCase, ToUpperCamelCase};
6use wasmtime_environ::component::{ResourceIndex, TypeResourceTableIndex};
7use wit_bindgen_core::abi::{Bindgen, Bitcast, Instruction};
8use wit_component::StringEncoding;
9use wit_parser::abi::WasmType;
10use wit_parser::{ArchitectureSize, Handle, Resolve, SizeAlign, Type, TypeDefKind, TypeId};
11
12use crate::intrinsics::Intrinsic;
13use crate::source;
14use crate::{uwrite, uwriteln};
15
16#[derive(PartialEq)]
17pub enum ErrHandling {
18 None,
19 ThrowResultErr,
20 ResultCatchHandler,
21}
22
23#[derive(Clone, Debug, PartialEq)]
24pub enum ResourceData {
25 Host {
26 tid: TypeResourceTableIndex,
27 rid: ResourceIndex,
28 local_name: String,
29 dtor_name: Option<String>,
30 },
31 Guest {
32 resource_name: String,
33 prefix: Option<String>,
34 },
35}
36
37#[derive(Clone, Debug, PartialEq)]
65pub struct ResourceTable {
66 pub imported: bool,
67 pub data: ResourceData,
68}
69pub type ResourceMap = BTreeMap<TypeId, ResourceTable>;
70
71pub struct FunctionBindgen<'a> {
72 pub resource_map: &'a ResourceMap,
73 pub cur_resource_borrows: bool,
74 pub intrinsics: &'a mut BTreeSet<Intrinsic>,
75 pub valid_lifting_optimization: bool,
76 pub sizes: &'a SizeAlign,
77 pub err: ErrHandling,
78 pub tmp: usize,
79 pub src: source::Source,
80 pub block_storage: Vec<source::Source>,
81 pub blocks: Vec<(String, Vec<String>)>,
82 pub params: Vec<String>,
83 pub memory: Option<&'a String>,
84 pub realloc: Option<&'a String>,
85 pub post_return: Option<&'a String>,
86 pub tracing_prefix: Option<&'a String>,
87 pub encoding: StringEncoding,
88 pub callee: &'a str,
89 pub callee_resource_dynamic: bool,
90 pub resolve: &'a Resolve,
91 pub is_async: bool,
92}
93
94impl FunctionBindgen<'_> {
95 fn tmp(&mut self) -> usize {
96 let ret = self.tmp;
97 self.tmp += 1;
98 ret
99 }
100
101 fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
102 self.intrinsics.insert(intrinsic);
103 intrinsic.name().to_string()
104 }
105
106 fn clamp_guest<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
107 where
108 T: std::fmt::Display,
109 {
110 let clamp = self.intrinsic(Intrinsic::ClampGuest);
111 results.push(format!("{}({}, {}, {})", clamp, operands[0], min, max));
112 }
113
114 fn load(&mut self, method: &str, offset: i32, operands: &[String], results: &mut Vec<String>) {
115 let view = self.intrinsic(Intrinsic::DataView);
116 let memory = self.memory.as_ref().unwrap();
117 results.push(format!(
118 "{view}({memory}).{method}({} + {offset}, true)",
119 operands[0],
120 ));
121 }
122
123 fn store(&mut self, method: &str, offset: i32, operands: &[String]) {
124 let view = self.intrinsic(Intrinsic::DataView);
125 let memory = self.memory.as_ref().unwrap();
126 uwriteln!(
127 self.src,
128 "{view}({memory}).{method}({} + {offset}, {}, true);",
129 operands[1],
130 operands[0]
131 );
132 }
133
134 fn bind_results(&mut self, amt: usize, results: &mut Vec<String>) {
135 match amt {
136 0 => {}
137 1 => {
138 uwrite!(self.src, "const ret = ");
139 results.push("ret".to_string());
140 }
141 n => {
142 uwrite!(self.src, "var [");
143 for i in 0..n {
144 if i > 0 {
145 uwrite!(self.src, ", ");
146 }
147 uwrite!(self.src, "ret{}", i);
148 results.push(format!("ret{}", i));
149 }
150 uwrite!(self.src, "] = ");
151 }
152 }
153 }
154
155 fn bitcast(&mut self, cast: &Bitcast, op: &str) -> String {
156 match cast {
157 Bitcast::I32ToF32 => {
158 let cvt = self.intrinsic(Intrinsic::I32ToF32);
159 format!("{cvt}({op})")
160 }
161 Bitcast::F32ToI32 => {
162 let cvt = self.intrinsic(Intrinsic::F32ToI32);
163 format!("{cvt}({op})")
164 }
165 Bitcast::I64ToF64 => {
166 let cvt = self.intrinsic(Intrinsic::I64ToF64);
167 format!("{cvt}({op})")
168 }
169 Bitcast::F64ToI64 => {
170 let cvt = self.intrinsic(Intrinsic::F64ToI64);
171 format!("{}({})", cvt, op)
172 }
173 Bitcast::I32ToI64 => format!("BigInt({op})"),
174 Bitcast::I64ToI32 => format!("Number({op})"),
175 Bitcast::I64ToF32 => {
176 let cvt = self.intrinsic(Intrinsic::I32ToF32);
177 format!("{cvt}(Number({op}))")
178 }
179 Bitcast::F32ToI64 => {
180 let cvt = self.intrinsic(Intrinsic::F32ToI32);
181 format!("BigInt({cvt}({op}))")
182 }
183 Bitcast::None
184 | Bitcast::P64ToI64
185 | Bitcast::LToI32
186 | Bitcast::I32ToL
187 | Bitcast::LToP
188 | Bitcast::PToL
189 | Bitcast::PToI32
190 | Bitcast::I32ToP => op.to_string(),
191 Bitcast::PToP64 | Bitcast::I64ToP64 | Bitcast::LToI64 => format!("BigInt({op})"),
192 Bitcast::P64ToP | Bitcast::I64ToL => format!("Number({op})"),
193 Bitcast::Sequence(casts) => {
194 let mut statement = op.to_string();
195 for cast in casts.iter() {
196 statement = self.bitcast(cast, &statement);
197 }
198 statement
199 }
200 }
201 }
202}
203
204impl Bindgen for FunctionBindgen<'_> {
205 type Operand = String;
206
207 fn sizes(&self) -> &SizeAlign {
208 self.sizes
209 }
210
211 fn push_block(&mut self) {
212 let prev = mem::take(&mut self.src);
213 self.block_storage.push(prev);
214 }
215
216 fn finish_block(&mut self, operands: &mut Vec<String>) {
217 let to_restore = self.block_storage.pop().unwrap();
218 let src = mem::replace(&mut self.src, to_restore);
219 self.blocks.push((src.into(), mem::take(operands)));
220 }
221
222 fn return_pointer(&mut self, _size: usize, _align: usize) -> String {
223 unimplemented!();
224 }
225
226 fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool {
227 array_ty(resolve, ty).is_some()
228 }
229
230 fn emit(
231 &mut self,
232 resolve: &Resolve,
233 inst: &Instruction<'_>,
234 operands: &mut Vec<String>,
235 results: &mut Vec<String>,
236 ) {
237 match inst {
238 Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
239 Instruction::I32Const { val } => results.push(val.to_string()),
240 Instruction::ConstZero { tys } => {
241 for t in tys.iter() {
242 match t {
243 WasmType::I64 | WasmType::PointerOrI64 => results.push("0n".to_string()),
244 WasmType::I32
245 | WasmType::F32
246 | WasmType::F64
247 | WasmType::Pointer
248 | WasmType::Length => results.push("0".to_string()),
249 }
250 }
251 }
252
253 Instruction::U8FromI32 => self.clamp_guest(results, operands, u8::MIN, u8::MAX),
258 Instruction::S8FromI32 => self.clamp_guest(results, operands, i8::MIN, i8::MAX),
259 Instruction::U16FromI32 => self.clamp_guest(results, operands, u16::MIN, u16::MAX),
260 Instruction::S16FromI32 => self.clamp_guest(results, operands, i16::MIN, i16::MAX),
261 Instruction::U32FromI32 => results.push(format!("{} >>> 0", operands[0])),
264 Instruction::U64FromI64 => results.push(format!("BigInt.asUintN(64, {})", operands[0])),
267 Instruction::S32FromI32 | Instruction::S64FromI64 => {
270 results.push(operands.pop().unwrap())
271 }
272
273 Instruction::I32FromU8 => {
276 let conv = self.intrinsic(Intrinsic::ToUint8);
277 results.push(format!("{conv}({op})", op = operands[0]))
278 }
279 Instruction::I32FromS8 => {
280 let conv = self.intrinsic(Intrinsic::ToInt8);
281 results.push(format!("{conv}({op})", op = operands[0]))
282 }
283 Instruction::I32FromU16 => {
284 let conv = self.intrinsic(Intrinsic::ToUint16);
285 results.push(format!("{conv}({op})", op = operands[0]))
286 }
287 Instruction::I32FromS16 => {
288 let conv = self.intrinsic(Intrinsic::ToInt16);
289 results.push(format!("{conv}({op})", op = operands[0]))
290 }
291 Instruction::I32FromU32 => {
292 let conv = self.intrinsic(Intrinsic::ToUint32);
293 results.push(format!("{conv}({op})", op = operands[0]))
294 }
295 Instruction::I32FromS32 => {
296 let conv = self.intrinsic(Intrinsic::ToInt32);
297 results.push(format!("{conv}({op})", op = operands[0]))
298 }
299 Instruction::I64FromU64 => {
300 let conv = self.intrinsic(Intrinsic::ToBigUint64);
301 results.push(format!("{conv}({op})", op = operands[0]))
302 }
303 Instruction::I64FromS64 => {
304 let conv = self.intrinsic(Intrinsic::ToBigInt64);
305 results.push(format!("{conv}({op})", op = operands[0]))
306 }
307
308 Instruction::F32FromCoreF32 | Instruction::F64FromCoreF64 => {
312 results.push(operands.pop().unwrap())
313 }
314
315 Instruction::CoreF32FromF32 | Instruction::CoreF64FromF64 => {
317 results.push(format!("+{}", operands[0]))
318 }
319
320 Instruction::CharFromI32 => {
323 let validate = self.intrinsic(Intrinsic::ValidateGuestChar);
324 results.push(format!("{}({})", validate, operands[0]));
325 }
326
327 Instruction::I32FromChar => {
330 let validate = self.intrinsic(Intrinsic::ValidateHostChar);
331 results.push(format!("{}({})", validate, operands[0]));
332 }
333
334 Instruction::Bitcasts { casts } => {
335 for (cast, op) in casts.iter().zip(operands) {
336 results.push(self.bitcast(cast, op));
337 }
338 }
339
340 Instruction::BoolFromI32 => {
341 let tmp = self.tmp();
342 uwrite!(self.src, "var bool{} = {};\n", tmp, operands[0]);
343 if self.valid_lifting_optimization {
344 results.push(format!("!!bool{tmp}"));
345 } else {
346 let throw = self.intrinsic(Intrinsic::ThrowInvalidBool);
347 results.push(format!(
348 "bool{tmp} == 0 ? false : (bool{tmp} == 1 ? true : {throw}())"
349 ));
350 }
351 }
352 Instruction::I32FromBool => {
353 results.push(format!("{} ? 1 : 0", operands[0]));
354 }
355
356 Instruction::RecordLower { record, .. } => {
357 let tmp = self.tmp();
360 let mut expr = "var {".to_string();
361 for (i, field) in record.fields.iter().enumerate() {
362 if i > 0 {
363 expr.push_str(", ");
364 }
365 let name = format!("v{}_{}", tmp, i);
366 expr.push_str(&field.name.to_lower_camel_case());
367 expr.push_str(": ");
368 expr.push_str(&name);
369 results.push(name);
370 }
371 uwrite!(self.src, "{} }} = {};\n", expr, operands[0]);
372 }
373
374 Instruction::RecordLift { record, .. } => {
375 let mut result = "{\n".to_string();
379 for (field, op) in record.fields.iter().zip(operands) {
380 result.push_str(&format!("{}: {},\n", field.name.to_lower_camel_case(), op));
381 }
382 result.push('}');
383 results.push(result);
384 }
385
386 Instruction::TupleLower { tuple, .. } => {
387 let tmp = self.tmp();
391 let mut expr = "var [".to_string();
392 for i in 0..tuple.types.len() {
393 if i > 0 {
394 expr.push_str(", ");
395 }
396 let name = format!("tuple{}_{}", tmp, i);
397 expr.push_str(&name);
398 results.push(name);
399 }
400 uwrite!(self.src, "{}] = {};\n", expr, operands[0]);
401 }
402
403 Instruction::TupleLift { .. } => {
404 results.push(format!("[{}]", operands.join(", ")));
407 }
408
409 Instruction::FlagsLower { flags, .. } => {
411 let op0 = &operands[0];
412
413 for _ in 0..flags.repr().count() {
415 let tmp = self.tmp();
416 let name = format!("flags{tmp}");
417 uwrite!(self.src, "let {name} = 0;\n");
420 results.push(name);
421 }
422
423 uwrite!(
424 self.src,
425 "if (typeof {op0} === 'object' && {op0} !== null) {{\n"
426 );
427
428 for (i, chunk) in flags.flags.chunks(32).enumerate() {
429 let result_name = &results[i];
430
431 uwrite!(self.src, "{result_name} = ");
432 for (i, flag) in chunk.iter().enumerate() {
433 if i != 0 {
434 uwrite!(self.src, " | ");
435 }
436
437 let flag = flag.name.to_lower_camel_case();
438 uwrite!(self.src, "Boolean({op0}.{flag}) << {i}");
439 }
440 uwrite!(self.src, ";\n");
441 }
442
443 uwrite!(
444 self.src,
445 "\
446 }} else if ({op0} !== null && {op0} !== undefined) {{
447 throw new TypeError('only an object, undefined or null can be converted to flags');
448 }}
449 ");
450
451 }
455
456 Instruction::FlagsLift { flags, .. } => {
457 let tmp = self.tmp();
458 results.push(format!("flags{tmp}"));
459
460 if let Some(op) = operands.last() {
461 if flags.flags.len() % 32 != 0 && !self.valid_lifting_optimization {
465 let mask: u32 = 0xffffffff << (flags.flags.len() % 32);
466 uwriteln!(
467 self.src,
468 "if (({op} & {mask}) !== 0) {{
469 throw new TypeError('flags have extraneous bits set');
470 }}"
471 );
472 }
473 }
474
475 uwriteln!(self.src, "var flags{tmp} = {{");
476
477 for (i, flag) in flags.flags.iter().enumerate() {
478 let flag = flag.name.to_lower_camel_case();
479 let op = &operands[i / 32];
480 let mask: u32 = 1 << (i % 32);
481 uwriteln!(self.src, "{flag}: Boolean({op} & {mask}),");
482 }
483
484 uwriteln!(self.src, "}};");
485 }
486
487 Instruction::VariantPayloadName => results.push("e".to_string()),
488
489 Instruction::VariantLower {
490 variant,
491 results: result_types,
492 name,
493 ..
494 } => {
495 let blocks = self
496 .blocks
497 .drain(self.blocks.len() - variant.cases.len()..)
498 .collect::<Vec<_>>();
499 let tmp = self.tmp();
500 let op = &operands[0];
501 uwriteln!(self.src, "var variant{tmp} = {op};");
502
503 for i in 0..result_types.len() {
504 uwriteln!(self.src, "let variant{tmp}_{i};");
505 results.push(format!("variant{}_{}", tmp, i));
506 }
507
508 let expr_to_match = format!("variant{tmp}.tag");
509
510 uwriteln!(self.src, "switch ({expr_to_match}) {{");
511 for (case, (block, block_results)) in variant.cases.iter().zip(blocks) {
512 uwriteln!(self.src, "case '{}': {{", case.name.as_str());
513 if case.ty.is_some() {
514 uwriteln!(self.src, "const e = variant{tmp}.val;");
515 }
516 self.src.push_str(&block);
517
518 for (i, result) in block_results.iter().enumerate() {
519 uwriteln!(self.src, "variant{tmp}_{i} = {result};");
520 }
521 uwriteln!(
522 self.src,
523 "break;
524 }}"
525 );
526 }
527 let variant_name = name.to_upper_camel_case();
528 uwriteln!(
529 self.src,
530 r#"default: {{
531 throw new TypeError(`invalid variant tag value \`${{JSON.stringify({expr_to_match})}}\` (received \`${{variant{tmp}}}\`) specified for \`{variant_name}\``);
532 }}"#,
533 );
534 uwriteln!(self.src, "}}");
535 }
536
537 Instruction::VariantLift { variant, name, .. } => {
538 let blocks = self
539 .blocks
540 .drain(self.blocks.len() - variant.cases.len()..)
541 .collect::<Vec<_>>();
542
543 let tmp = self.tmp();
544 let op = &operands[0];
545
546 uwriteln!(
547 self.src,
548 "let variant{tmp};
549 switch ({op}) {{"
550 );
551
552 for (i, (case, (block, block_results))) in
553 variant.cases.iter().zip(blocks).enumerate()
554 {
555 let tag = case.name.as_str();
556 uwriteln!(
557 self.src,
558 "case {i}: {{
559 {block}\
560 variant{tmp} = {{
561 tag: '{tag}',"
562 );
563 if case.ty.is_some() {
564 assert!(block_results.len() == 1);
565 uwriteln!(self.src, " val: {}", block_results[0]);
566 } else {
567 assert!(block_results.is_empty());
568 }
569 uwriteln!(
570 self.src,
571 " }};
572 break;
573 }}"
574 );
575 }
576 let variant_name = name.to_upper_camel_case();
577 if !self.valid_lifting_optimization {
578 uwriteln!(
579 self.src,
580 "default: {{
581 throw new TypeError('invalid variant discriminant for {variant_name}');
582 }}",
583 );
584 }
585 uwriteln!(self.src, "}}");
586 results.push(format!("variant{}", tmp));
587 }
588
589 Instruction::OptionLower {
590 payload,
591 results: result_types,
592 ..
593 } => {
594 let (mut some, some_results) = self.blocks.pop().unwrap();
595 let (mut none, none_results) = self.blocks.pop().unwrap();
596
597 let tmp = self.tmp();
598 let op = &operands[0];
599 uwriteln!(self.src, "var variant{tmp} = {op};");
600
601 for i in 0..result_types.len() {
602 uwriteln!(self.src, "let variant{tmp}_{i};");
603 results.push(format!("variant{tmp}_{i}"));
604
605 let some_result = &some_results[i];
606 let none_result = &none_results[i];
607 uwriteln!(some, "variant{tmp}_{i} = {some_result};");
608 uwriteln!(none, "variant{tmp}_{i} = {none_result};");
609 }
610
611 if maybe_null(resolve, payload) {
612 uwriteln!(
613 self.src,
614 "switch (variant{tmp}.tag) {{
615 case 'none': {{
616 {none}\
617 break;
618 }}
619 case 'some': {{
620 const e = variant{tmp}.val;
621 {some}\
622 break;
623 }}
624 default: {{
625 throw new TypeError('invalid variant specified for option');
626 }}
627 }}",
628 );
629 } else {
630 uwriteln!(
631 self.src,
632 "if (variant{tmp} === null || variant{tmp} === undefined) {{
633 {none}\
634 }} else {{
635 const e = variant{tmp};
636 {some}\
637 }}"
638 );
639 }
640 }
641
642 Instruction::OptionLift { payload, .. } => {
643 let (some, some_results) = self.blocks.pop().unwrap();
644 let (none, none_results) = self.blocks.pop().unwrap();
645 assert!(none_results.is_empty());
646 assert!(some_results.len() == 1);
647 let some_result = &some_results[0];
648
649 let tmp = self.tmp();
650 let op = &operands[0];
651
652 let (v_none, v_some) = if maybe_null(resolve, payload) {
653 (
654 "{ tag: 'none' }",
655 format!(
656 "{{
657 tag: 'some',
658 val: {some_result}
659 }}"
660 ),
661 )
662 } else {
663 ("undefined", some_result.into())
664 };
665
666 if !self.valid_lifting_optimization {
667 uwriteln!(
668 self.src,
669 "let variant{tmp};
670 switch ({op}) {{
671 case 0: {{
672 {none}\
673 variant{tmp} = {v_none};
674 break;
675 }}
676 case 1: {{
677 {some}\
678 variant{tmp} = {v_some};
679 break;
680 }}
681 default: {{
682 throw new TypeError('invalid variant discriminant for option');
683 }}
684 }}",
685 );
686 } else {
687 uwriteln!(
688 self.src,
689 "let variant{tmp};
690 if ({op}) {{
691 {some}\
692 variant{tmp} = {v_some};
693 }} else {{
694 {none}\
695 variant{tmp} = {v_none};
696 }}"
697 );
698 }
699
700 results.push(format!("variant{tmp}"));
701 }
702
703 Instruction::ResultLower {
704 results: result_types,
705 ..
706 } => {
707 let (mut err, err_results) = self.blocks.pop().unwrap();
708 let (mut ok, ok_results) = self.blocks.pop().unwrap();
709
710 let tmp = self.tmp();
711 let op = &operands[0];
712 uwriteln!(self.src, "var variant{tmp} = {op};");
713
714 for i in 0..result_types.len() {
715 uwriteln!(self.src, "let variant{tmp}_{i};");
716 results.push(format!("variant{tmp}_{i}"));
717
718 let ok_result = &ok_results[i];
719 let err_result = &err_results[i];
720 uwriteln!(ok, "variant{tmp}_{i} = {ok_result};");
721 uwriteln!(err, "variant{tmp}_{i} = {err_result};");
722 }
723
724 uwriteln!(
725 self.src,
726 "switch (variant{tmp}.tag) {{
727 case 'ok': {{
728 const e = variant{tmp}.val;
729 {ok}\
730 break;
731 }}
732 case 'err': {{
733 const e = variant{tmp}.val;
734 {err}\
735 break;
736 }}
737 default: {{
738 throw new TypeError('invalid variant specified for result');
739 }}
740 }}",
741 );
742 }
743
744 Instruction::ResultLift { result, .. } => {
745 let (err, err_results) = self.blocks.pop().unwrap();
746 let (ok, ok_results) = self.blocks.pop().unwrap();
747 let ok_result = if result.ok.is_some() {
748 assert_eq!(ok_results.len(), 1);
749 ok_results[0].to_string()
750 } else {
751 assert_eq!(ok_results.len(), 0);
752 String::from("undefined")
753 };
754 let err_result = if result.err.is_some() {
755 assert_eq!(err_results.len(), 1);
756 err_results[0].to_string()
757 } else {
758 assert_eq!(err_results.len(), 0);
759 String::from("undefined")
760 };
761 let tmp = self.tmp();
762 let op0 = &operands[0];
763
764 if !self.valid_lifting_optimization {
765 uwriteln!(
766 self.src,
767 "let variant{tmp};
768 switch ({op0}) {{
769 case 0: {{
770 {ok}\
771 variant{tmp} = {{
772 tag: 'ok',
773 val: {ok_result}
774 }};
775 break;
776 }}
777 case 1: {{
778 {err}\
779 variant{tmp} = {{
780 tag: 'err',
781 val: {err_result}
782 }};
783 break;
784 }}
785 default: {{
786 throw new TypeError('invalid variant discriminant for expected');
787 }}
788 }}",
789 );
790 } else {
791 uwriteln!(
792 self.src,
793 "let variant{tmp};
794 if ({op0}) {{
795 {err}\
796 variant{tmp} = {{
797 tag: 'err',
798 val: {err_result}
799 }};
800 }} else {{
801 {ok}\
802 variant{tmp} = {{
803 tag: 'ok',
804 val: {ok_result}
805 }};
806 }}"
807 );
808 }
809 results.push(format!("variant{tmp}"));
810 }
811
812 Instruction::EnumLower { name, enum_, .. } => {
814 let tmp = self.tmp();
815
816 let op = &operands[0];
817 uwriteln!(self.src, "var val{tmp} = {op};");
818
819 uwriteln!(
821 self.src,
822 "let enum{tmp};
823 switch (val{tmp}) {{"
824 );
825 for (i, case) in enum_.cases.iter().enumerate() {
826 uwriteln!(
827 self.src,
828 "case '{case}': {{
829 enum{tmp} = {i};
830 break;
831 }}",
832 case = case.name
833 );
834 }
835 uwriteln!(self.src, "default: {{");
836 if !self.valid_lifting_optimization {
837 uwriteln!(
838 self.src,
839 "if (({op}) instanceof Error) {{
840 console.error({op});
841 }}"
842 );
843 }
844 uwriteln!(
845 self.src,
846 "
847 throw new TypeError(`\"${{val{tmp}}}\" is not one of the cases of {name}`);
848 }}
849 }}",
850 );
851
852 results.push(format!("enum{tmp}"));
853 }
854
855 Instruction::EnumLift { name, enum_, .. } => {
856 let tmp = self.tmp();
857
858 uwriteln!(
859 self.src,
860 "let enum{tmp};
861 switch ({}) {{",
862 operands[0]
863 );
864 for (i, case) in enum_.cases.iter().enumerate() {
865 uwriteln!(
866 self.src,
867 "case {i}: {{
868 enum{tmp} = '{case}';
869 break;
870 }}",
871 case = case.name
872 );
873 }
874 if !self.valid_lifting_optimization {
875 let name = name.to_upper_camel_case();
876 uwriteln!(
877 self.src,
878 "default: {{
879 throw new TypeError('invalid discriminant specified for {name}');
880 }}",
881 );
882 }
883 uwriteln!(self.src, "}}");
884
885 results.push(format!("enum{tmp}"));
886 }
887
888 Instruction::ListCanonLower { element, .. } => {
889 let tmp = self.tmp();
890 let memory = self.memory.as_ref().unwrap();
891 let realloc = self.realloc.unwrap();
892
893 let size = self.sizes.size(element).size_wasm32();
894 let align = ArchitectureSize::from(self.sizes.align(element)).size_wasm32();
895 uwriteln!(self.src, "var val{tmp} = {};", operands[0]);
896 if matches!(element, Type::U8) {
897 uwriteln!(self.src, "var len{tmp} = val{tmp}.byteLength;");
898 } else {
899 uwriteln!(self.src, "var len{tmp} = val{tmp}.length;");
900 }
901
902 uwriteln!(
903 self.src,
904 "var ptr{tmp} = {realloc}(0, 0, {align}, len{tmp} * {size});",
905 );
906 if matches!(element, Type::U8) {
908 uwriteln!(
909 self.src,
910 "var src{tmp} = new Uint8Array(val{tmp}.buffer || val{tmp}, val{tmp}.byteOffset, len{tmp} * {size});",
911 );
912 } else {
913 uwriteln!(
914 self.src,
915 "var src{tmp} = new Uint8Array(val{tmp}.buffer, val{tmp}.byteOffset, len{tmp} * {size});",
916 );
917 }
918 uwriteln!(
919 self.src,
920 "(new Uint8Array({memory}.buffer, ptr{tmp}, len{tmp} * {size})).set(src{tmp});",
921 );
922 results.push(format!("ptr{}", tmp));
923 results.push(format!("len{}", tmp));
924 }
925 Instruction::ListCanonLift { element, .. } => {
926 let tmp = self.tmp();
927 let memory = self.memory.as_ref().unwrap();
928 uwriteln!(self.src, "var ptr{tmp} = {};", operands[0]);
929 uwriteln!(self.src, "var len{tmp} = {};", operands[1]);
930 let array_ty = array_ty(resolve, element).unwrap();
932 uwriteln!(
933 self.src,
934 "var result{tmp} = new {array_ty}({memory}.buffer.slice(ptr{tmp}, ptr{tmp} + len{tmp} * {}));",
935 self.sizes.size(element).size_wasm32(),
936 );
937 results.push(format!("result{tmp}"));
938 }
939 Instruction::StringLower { .. } => {
940 assert!(matches!(
942 self.encoding,
943 StringEncoding::UTF8 | StringEncoding::UTF16
944 ));
945 let intrinsic = if self.encoding == StringEncoding::UTF16 {
946 Intrinsic::Utf16Encode
947 } else {
948 Intrinsic::Utf8Encode
949 };
950 let encode = self.intrinsic(intrinsic);
951 let tmp = self.tmp();
952 let memory = self.memory.as_ref().unwrap();
953 let str = String::from("cabi_realloc");
954 let realloc = self.realloc.unwrap_or(&str);
955 uwriteln!(
956 self.src,
957 "var ptr{tmp} = {encode}({}, {realloc}, {memory});",
958 operands[0],
959 );
960 if self.encoding == StringEncoding::UTF8 {
961 let encoded_len = self.intrinsic(Intrinsic::Utf8EncodedLen);
962 uwriteln!(self.src, "var len{tmp} = {encoded_len};");
963 } else {
964 uwriteln!(self.src, "var len{tmp} = {}.length;", operands[0]);
965 }
966 results.push(format!("ptr{}", tmp));
967 results.push(format!("len{}", tmp));
968 }
969 Instruction::StringLift => {
970 assert!(matches!(
972 self.encoding,
973 StringEncoding::UTF8 | StringEncoding::UTF16
974 ));
975 let intrinsic = if self.encoding == StringEncoding::UTF16 {
976 Intrinsic::Utf16Decoder
977 } else {
978 Intrinsic::Utf8Decoder
979 };
980 let decoder = self.intrinsic(intrinsic);
981 let tmp = self.tmp();
982 let memory = self.memory.as_ref().unwrap();
983 uwriteln!(self.src, "var ptr{tmp} = {};", operands[0]);
984 uwriteln!(self.src, "var len{tmp} = {};", operands[1]);
985 uwriteln!(
986 self.src,
987 "var result{tmp} = {decoder}.decode(new Uint{}Array({memory}.buffer, ptr{tmp}, len{tmp}));",
988 if self.encoding == StringEncoding::UTF16 { "16" } else { "8" }
989 );
990 results.push(format!("result{tmp}"));
991 }
992
993 Instruction::ListLower { element, .. } => {
994 let (body, body_results) = self.blocks.pop().unwrap();
995 assert!(body_results.is_empty());
996 let tmp = self.tmp();
997 let vec = format!("vec{}", tmp);
998 let result = format!("result{}", tmp);
999 let len = format!("len{}", tmp);
1000 let size = self.sizes.size(element).size_wasm32();
1001 let align = ArchitectureSize::from(self.sizes.align(element)).size_wasm32();
1002
1003 uwriteln!(self.src, "var {vec} = {};", operands[0]);
1006 uwriteln!(self.src, "var {len} = {vec}.length;");
1007
1008 let realloc = self.realloc.as_ref().unwrap();
1010 uwriteln!(
1011 self.src,
1012 "var {result} = {realloc}(0, 0, {align}, {len} * {size});"
1013 );
1014
1015 uwriteln!(self.src, "for (let i = 0; i < {vec}.length; i++) {{");
1018 uwriteln!(self.src, "const e = {vec}[i];");
1019 uwrite!(self.src, "const base = {result} + i * {size};");
1020 self.src.push_str(&body);
1021 uwrite!(self.src, "}}\n");
1022
1023 results.push(result);
1024 results.push(len);
1025 }
1026
1027 Instruction::ListLift { element, .. } => {
1028 let (body, body_results) = self.blocks.pop().unwrap();
1029 let tmp = self.tmp();
1030 let size = self.sizes.size(element).size_wasm32();
1031 let len = format!("len{tmp}");
1032 uwriteln!(self.src, "var {len} = {};", operands[1]);
1033 let base = format!("base{tmp}");
1034 uwriteln!(self.src, "var {base} = {};", operands[0]);
1035 let result = format!("result{tmp}");
1036 uwriteln!(self.src, "var {result} = [];");
1037 results.push(result.clone());
1038
1039 uwriteln!(self.src, "for (let i = 0; i < {len}; i++) {{");
1040 uwriteln!(self.src, "const base = {base} + i * {size};");
1041 self.src.push_str(&body);
1042 assert_eq!(body_results.len(), 1);
1043 uwriteln!(self.src, "{result}.push({});", body_results[0]);
1044 uwrite!(self.src, "}}\n");
1045 }
1046
1047 Instruction::IterElem { .. } => results.push("e".to_string()),
1048
1049 Instruction::IterBasePointer => results.push("base".to_string()),
1050
1051 Instruction::CallWasm { sig, .. } => {
1052 let sig_results_length = sig.results.len();
1053 self.bind_results(sig_results_length, results);
1054 let maybe_async_await = if self.is_async { "await " } else { "" };
1055 uwriteln!(
1056 self.src,
1057 "{maybe_async_await}{}({});",
1058 self.callee,
1059 operands.join(", ")
1060 );
1061
1062 if let Some(prefix) = self.tracing_prefix {
1063 let to_result_string = self.intrinsic(Intrinsic::ToResultString);
1064 uwriteln!(
1065 self.src,
1066 "console.error(`{prefix} return {}`);",
1067 if sig_results_length > 0 || !results.is_empty() {
1068 format!("result=${{{to_result_string}(ret)}}")
1069 } else {
1070 "".to_string()
1071 }
1072 );
1073 }
1074 }
1075
1076 Instruction::CallInterface { func, .. } => {
1077 let results_length = func.results.len();
1078 let maybe_async_await = if self.is_async { "await " } else { "" };
1079 let call = if self.callee_resource_dynamic {
1080 format!(
1081 "{maybe_async_await}{}.{}({})",
1082 operands[0],
1083 self.callee,
1084 operands[1..].join(", ")
1085 )
1086 } else {
1087 format!(
1088 "{maybe_async_await}{}({})",
1089 self.callee,
1090 operands.join(", ")
1091 )
1092 };
1093 if self.err == ErrHandling::ResultCatchHandler {
1094 let err_payload = if let (_, Some(Type::Id(err_ty))) =
1097 func.results.throws(self.resolve).unwrap()
1098 {
1099 match &self.resolve.types[*err_ty].kind {
1100 TypeDefKind::Type(Type::String) => {
1101 self.intrinsic(Intrinsic::GetErrorPayloadString)
1102 }
1103 _ => self.intrinsic(Intrinsic::GetErrorPayload),
1104 }
1105 } else {
1106 self.intrinsic(Intrinsic::GetErrorPayload)
1107 };
1108 uwriteln!(
1109 self.src,
1110 "let ret;
1111 try {{
1112 ret = {{ tag: 'ok', val: {call} }};
1113 }} catch (e) {{
1114 ret = {{ tag: 'err', val: {err_payload}(e) }};
1115 }}",
1116 );
1117 results.push("ret".to_string());
1118 } else {
1119 self.bind_results(results_length, results);
1120 uwriteln!(self.src, "{call};");
1121 }
1122
1123 if let Some(prefix) = self.tracing_prefix {
1124 let to_result_string = self.intrinsic(Intrinsic::ToResultString);
1125 uwriteln!(
1126 self.src,
1127 "console.error(`{prefix} return {}`);",
1128 if results_length > 0 || !results.is_empty() {
1129 format!("result=${{{to_result_string}(ret)}}")
1130 } else {
1131 "".to_string()
1132 }
1133 );
1134 }
1135
1136 if self.cur_resource_borrows {
1138 let symbol_resource_handle = self.intrinsic(Intrinsic::SymbolResourceHandle);
1139 let cur_resource_borrows = self.intrinsic(Intrinsic::CurResourceBorrows);
1140 let host = matches!(
1141 self.resource_map.iter().nth(0).unwrap().1.data,
1142 ResourceData::Host { .. }
1143 );
1144 if host {
1145 uwriteln!(
1146 self.src,
1147 "for (const rsc of {cur_resource_borrows}) {{
1148 rsc[{symbol_resource_handle}] = undefined;
1149 }}
1150 {cur_resource_borrows} = [];"
1151 );
1152 } else {
1153 uwriteln!(
1154 self.src,
1155 "for (const {{ rsc, drop }} of {cur_resource_borrows}) {{
1156 if (rsc[{symbol_resource_handle}]) {{
1157 drop(rsc[{symbol_resource_handle}]);
1158 rsc[{symbol_resource_handle}] = undefined;
1159 }}
1160 }}
1161 {cur_resource_borrows} = [];"
1162 );
1163 }
1164 self.cur_resource_borrows = false;
1165 }
1166 }
1167
1168 Instruction::Return { amt, .. } => {
1169 if *amt == 0 {
1170 if let Some(f) = &self.post_return {
1171 uwriteln!(self.src, "{f}();");
1172 }
1173 } else if *amt == 1 && self.err == ErrHandling::ThrowResultErr {
1174 let component_err = self.intrinsic(Intrinsic::ComponentError);
1175 let op = &operands[0];
1176 uwriteln!(self.src, "const retVal = {op};");
1177 if let Some(f) = &self.post_return {
1178 uwriteln!(self.src, "{f}(ret);");
1179 }
1180 uwriteln!(
1181 self.src,
1182 "if (typeof retVal === 'object' && retVal.tag === 'err') {{
1183 throw new {component_err}(retVal.val);
1184 }}
1185 return retVal.val;"
1186 );
1187 } else {
1188 let ret_assign = if self.post_return.is_some() {
1189 "const retVal ="
1190 } else {
1191 "return"
1192 };
1193 if *amt == 1 {
1194 uwriteln!(self.src, "{ret_assign} {};", operands[0]);
1195 } else {
1196 uwriteln!(self.src, "{ret_assign} [{}];", operands.join(", "));
1197 }
1198 if let Some(f) = &self.post_return {
1199 uwriteln!(
1200 self.src,
1201 "{f}(ret);
1202 return retVal;"
1203 );
1204 }
1205 }
1206 }
1207
1208 Instruction::I32Load { offset } => self.load("getInt32", *offset, operands, results),
1209 Instruction::I64Load { offset } => self.load("getBigInt64", *offset, operands, results),
1210 Instruction::F32Load { offset } => self.load("getFloat32", *offset, operands, results),
1211 Instruction::F64Load { offset } => self.load("getFloat64", *offset, operands, results),
1212 Instruction::I32Load8U { offset } => self.load("getUint8", *offset, operands, results),
1213 Instruction::I32Load8S { offset } => self.load("getInt8", *offset, operands, results),
1214 Instruction::I32Load16U { offset } => {
1215 self.load("getUint16", *offset, operands, results)
1216 }
1217 Instruction::I32Load16S { offset } => self.load("getInt16", *offset, operands, results),
1218 Instruction::I32Store { offset } => self.store("setInt32", *offset, operands),
1219 Instruction::I64Store { offset } => self.store("setBigInt64", *offset, operands),
1220 Instruction::F32Store { offset } => self.store("setFloat32", *offset, operands),
1221 Instruction::F64Store { offset } => self.store("setFloat64", *offset, operands),
1222 Instruction::I32Store8 { offset } => self.store("setInt8", *offset, operands),
1223 Instruction::I32Store16 { offset } => self.store("setInt16", *offset, operands),
1224
1225 Instruction::LengthStore { offset } => self.store("setInt32", *offset, operands),
1226 Instruction::LengthLoad { offset } => self.load("getInt32", *offset, operands, results),
1227 Instruction::PointerStore { offset } => self.store("setInt32", *offset, operands),
1228 Instruction::PointerLoad { offset } => {
1229 self.load("getInt32", *offset, operands, results)
1230 }
1231
1232 Instruction::Malloc { size, align, .. } => {
1233 let tmp = self.tmp();
1234 let realloc = self.realloc.as_ref().unwrap();
1235 let ptr = format!("ptr{tmp}");
1236 uwriteln!(self.src, "var {ptr} = {realloc}(0, 0, {align}, {size});",);
1237 results.push(ptr);
1238 }
1239
1240 Instruction::HandleLift { handle, .. } => {
1241 let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
1242 let resource_ty = &crate::dealias(self.resolve, *ty);
1243 let ResourceTable { imported, data } = &self.resource_map[resource_ty];
1244
1245 let is_own = matches!(handle, Handle::Own(_));
1246 let rsc = format!("rsc{}", self.tmp());
1247 let handle = format!("handle{}", self.tmp());
1248 uwriteln!(self.src, "var {handle} = {};", &operands[0]);
1249
1250 match data {
1251 ResourceData::Host {
1252 tid,
1253 rid,
1254 local_name,
1255 dtor_name,
1256 } => {
1257 let tid = tid.as_u32();
1258 let rid = rid.as_u32();
1259 let symbol_dispose = self.intrinsic(Intrinsic::SymbolDispose);
1260 let rsc_table_remove = self.intrinsic(Intrinsic::ResourceTableRemove);
1261 let rsc_flag = self.intrinsic(Intrinsic::ResourceTableFlag);
1262 if !imported {
1263 let symbol_resource_handle =
1264 self.intrinsic(Intrinsic::SymbolResourceHandle);
1265 uwriteln!(self.src, "var {rsc} = new.target === {local_name} ? this : Object.create({local_name}.prototype);");
1266 if is_own {
1267 let empty_func = self.intrinsic(Intrinsic::EmptyFunc);
1269 uwriteln!(self.src,
1270 "Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});
1271 finalizationRegistry{tid}.register({rsc}, {handle}, {rsc});");
1272 if let Some(dtor) = dtor_name {
1273 uwriteln!(
1275 self.src,
1276 "Object.defineProperty({rsc}, {symbol_dispose}, {{ writable: true, value: function () {{
1277 finalizationRegistry{tid}.unregister({rsc});
1278 {rsc_table_remove}(handleTable{tid}, {handle});
1279 {rsc}[{symbol_dispose}] = {empty_func};
1280 {rsc}[{symbol_resource_handle}] = undefined;
1281 {dtor}(handleTable{tid}[({handle} << 1) + 1] & ~{rsc_flag});
1282 }}}});"
1283 );
1284 } else {
1285 uwriteln!(
1287 self.src,
1288 "Object.defineProperty({rsc}, {symbol_dispose}, {{ writable: true, value: {empty_func} }});",
1289 );
1290 }
1291 } else {
1292 uwriteln!(self.src, "Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});");
1294 }
1295 } else {
1296 let rep = format!("rep{}", self.tmp());
1297 let symbol_resource_rep = self.intrinsic(Intrinsic::SymbolResourceRep);
1300 let symbol_resource_handle =
1301 self.intrinsic(Intrinsic::SymbolResourceHandle);
1302 uwriteln!(self.src,
1303 "var {rep} = handleTable{tid}[({handle} << 1) + 1] & ~{rsc_flag};
1304 var {rsc} = captureTable{rid}.get({rep});
1305 if (!{rsc}) {{
1306 {rsc} = Object.create({local_name}.prototype);
1307 Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});
1308 Object.defineProperty({rsc}, {symbol_resource_rep}, {{ writable: true, value: {rep} }});
1309 }}"
1310 );
1311 if is_own {
1312 uwriteln!(
1314 self.src,
1315 "else {{
1316 captureTable{rid}.delete({rep});
1317 }}
1318 {rsc_table_remove}(handleTable{tid}, {handle});"
1319 );
1320 }
1321 }
1322
1323 if !is_own {
1325 let cur_resource_borrows =
1326 self.intrinsic(Intrinsic::CurResourceBorrows);
1327 uwriteln!(self.src, "{cur_resource_borrows}.push({rsc});");
1328 self.cur_resource_borrows = true;
1329 }
1330 }
1331
1332 ResourceData::Guest {
1333 resource_name,
1334 prefix,
1335 } => {
1336 let symbol_resource_handle =
1337 self.intrinsic(Intrinsic::SymbolResourceHandle);
1338 let prefix = prefix.as_deref().unwrap_or("");
1339 let lower_camel = resource_name.to_lower_camel_case();
1340
1341 if !imported {
1342 if is_own {
1343 uwriteln!(self.src, "var {rsc} = repTable.get($resource_{prefix}rep${lower_camel}({handle})).rep;");
1344 uwrite!(
1345 self.src,
1346 "repTable.delete({handle});
1347 delete {rsc}[{symbol_resource_handle}];
1348 finalizationRegistry_export${prefix}{lower_camel}.unregister({rsc});
1349 "
1350 );
1351 } else {
1352 uwriteln!(self.src, "var {rsc} = repTable.get({handle}).rep;");
1353 }
1354 } else {
1355 let upper_camel = resource_name.to_upper_camel_case();
1356
1357 uwrite!(
1358 self.src,
1359 "var {rsc} = new.target === import_{prefix}{upper_camel} ? this : Object.create(import_{prefix}{upper_camel}.prototype);
1360 Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});
1361 "
1362 );
1363
1364 uwriteln!(
1365 self.src,
1366 "finalizationRegistry_import${prefix}{lower_camel}.register({rsc}, {handle}, {rsc});",
1367 );
1368
1369 if !is_own {
1370 let cur_resource_borrows =
1371 self.intrinsic(Intrinsic::CurResourceBorrows);
1372 uwriteln!(self.src, "{cur_resource_borrows}.push({{ rsc: {rsc}, drop: $resource_import${prefix}drop${lower_camel} }});");
1373 self.cur_resource_borrows = true;
1374 }
1375 }
1376 }
1377 }
1378 results.push(rsc);
1379 }
1380
1381 Instruction::HandleLower { handle, name, .. } => {
1382 let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
1383 let is_own = matches!(handle, Handle::Own(_));
1384 let ResourceTable { imported, data } =
1385 &self.resource_map[&crate::dealias(self.resolve, *ty)];
1386
1387 let class_name = name.to_upper_camel_case();
1388 let handle = format!("handle{}", self.tmp());
1389 let symbol_resource_handle = self.intrinsic(Intrinsic::SymbolResourceHandle);
1390 let symbol_dispose = self.intrinsic(Intrinsic::SymbolDispose);
1391 let op = &operands[0];
1392
1393 match data {
1394 ResourceData::Host {
1395 tid,
1396 rid,
1397 local_name,
1398 ..
1399 } => {
1400 let tid = tid.as_u32();
1401 let rid = rid.as_u32();
1402 if !imported {
1403 if is_own {
1404 let empty_func = self.intrinsic(Intrinsic::EmptyFunc);
1405 uwriteln!(
1406 self.src,
1407 "var {handle} = {op}[{symbol_resource_handle}];
1408 if (!{handle}) {{
1409 throw new TypeError('Resource error: Not a valid \"{class_name}\" resource.');
1410 }}
1411 finalizationRegistry{tid}.unregister({op});
1412 {op}[{symbol_dispose}] = {empty_func};
1413 {op}[{symbol_resource_handle}] = undefined;",
1414 );
1415 } else {
1416 let rsc_flag = self.intrinsic(Intrinsic::ResourceTableFlag);
1421 let own_handle = format!("handle{}", self.tmp());
1422 uwriteln!(self.src,
1423 "var {own_handle} = {op}[{symbol_resource_handle}];
1424 if (!{own_handle} || (handleTable{tid}[({own_handle} << 1) + 1] & {rsc_flag}) === 0) {{
1425 throw new TypeError('Resource error: Not a valid \"{class_name}\" resource.');
1426 }}
1427 var {handle} = handleTable{tid}[({own_handle} << 1) + 1] & ~{rsc_flag};",
1428 );
1429 }
1430 } else {
1431 uwriteln!(
1434 self.src,
1435 "if (!({op} instanceof {local_name})) {{
1436 throw new TypeError('Resource error: Not a valid \"{class_name}\" resource.');
1437 }}
1438 var {handle} = {op}[{symbol_resource_handle}];",
1439 );
1440 let symbol_resource_rep = self.intrinsic(Intrinsic::SymbolResourceRep);
1445 let rsc_table_create = if is_own {
1446 self.intrinsic(Intrinsic::ResourceTableCreateOwn)
1447 } else {
1448 self.intrinsic(Intrinsic::ScopeId);
1449 self.intrinsic(Intrinsic::ResourceTableCreateBorrow)
1450 };
1451 uwriteln!(
1452 self.src,
1453 "if (!{handle}) {{
1454 const rep = {op}[{symbol_resource_rep}] || ++captureCnt{rid};
1455 captureTable{rid}.set(rep, {op});
1456 {handle} = {rsc_table_create}(handleTable{tid}, rep);
1457 }}"
1458 );
1459 }
1460 }
1461
1462 ResourceData::Guest {
1463 resource_name,
1464 prefix,
1465 } => {
1466 let upper_camel = resource_name.to_upper_camel_case();
1467 let lower_camel = resource_name.to_lower_camel_case();
1468 let prefix = prefix.as_deref().unwrap_or("");
1469
1470 if !imported {
1471 let local_rep = format!("localRep{}", self.tmp());
1472 uwriteln!(
1473 self.src,
1474 "if (!({op} instanceof {upper_camel})) {{
1475 throw new TypeError('Resource error: Not a valid \"{upper_camel}\" resource.');
1476 }}
1477 let {handle} = {op}[{symbol_resource_handle}];",
1478 );
1479
1480 if is_own {
1481 uwriteln!(
1482 self.src,
1483 "if ({handle} === undefined) {{
1484 var {local_rep} = repCnt++;
1485 repTable.set({local_rep}, {{ rep: {op}, own: true }});
1486 {handle} = $resource_{prefix}new${lower_camel}({local_rep});
1487 {op}[{symbol_resource_handle}] = {handle};
1488 finalizationRegistry_export${prefix}{lower_camel}.register({op}, {handle}, {op});
1489 }}
1490 "
1491 );
1492 } else {
1493 uwriteln!(
1494 self.src,
1495 "if ({handle} === undefined) {{
1496 var {local_rep} = repCnt++;
1497 repTable.set({local_rep}, {{ rep: {op}, own: false }});
1498 {op}[{symbol_resource_handle}] = {local_rep};
1499 }}
1500 "
1501 );
1502 }
1503 } else {
1504 let symbol_resource_handle =
1505 self.intrinsic(Intrinsic::SymbolResourceHandle);
1506 uwrite!(
1507 self.src,
1508 "var {handle} = {op}[{symbol_resource_handle}];
1509 finalizationRegistry_import${prefix}{lower_camel}.unregister({op});
1510 "
1511 );
1512 }
1513 }
1514 }
1515 results.push(handle);
1516 }
1517
1518 Instruction::Flush { amt } => {
1524 for n in 0..*amt {
1525 results.push(operands[n].clone());
1526 }
1527 }
1528
1529 Instruction::FutureLower { .. }
1531 | Instruction::FutureLift { .. }
1532 | Instruction::StreamLower { .. }
1533 | Instruction::StreamLift { .. }
1534 | Instruction::AsyncMalloc { .. }
1535 | Instruction::AsyncCallWasm { .. }
1536 | Instruction::AsyncPostCallInterface { .. }
1537 | Instruction::AsyncCallReturn { .. }
1538 | Instruction::ErrorContextLift { .. }
1539 | Instruction::ErrorContextLower { .. } => {
1540 uwrite!(self.src, "throw new Error('async is not yet implemented');");
1541 }
1542
1543 Instruction::GuestDeallocate { .. }
1544 | Instruction::GuestDeallocateString
1545 | Instruction::GuestDeallocateList { .. }
1546 | Instruction::GuestDeallocateVariant { .. } => unimplemented!("Guest deallocation"),
1547 }
1548 }
1549}
1550
1551pub fn as_nullable<'a>(resolve: &'a Resolve, ty: &'a Type) -> Option<&'a Type> {
1556 let id = match ty {
1557 Type::Id(id) => *id,
1558 _ => return None,
1559 };
1560 match &resolve.types[id].kind {
1561 TypeDefKind::Option(t) => {
1577 if !maybe_null(resolve, t) {
1578 Some(t)
1579 } else {
1580 None
1581 }
1582 }
1583 TypeDefKind::Type(t) => as_nullable(resolve, t),
1584 _ => None,
1585 }
1586}
1587
1588pub fn maybe_null(resolve: &Resolve, ty: &Type) -> bool {
1589 as_nullable(resolve, ty).is_some()
1590}
1591
1592pub fn array_ty(resolve: &Resolve, ty: &Type) -> Option<&'static str> {
1593 match ty {
1594 Type::Bool => None,
1595 Type::U8 => Some("Uint8Array"),
1596 Type::S8 => Some("Int8Array"),
1597 Type::U16 => Some("Uint16Array"),
1598 Type::S16 => Some("Int16Array"),
1599 Type::U32 => Some("Uint32Array"),
1600 Type::S32 => Some("Int32Array"),
1601 Type::U64 => Some("BigUint64Array"),
1602 Type::S64 => Some("BigInt64Array"),
1603 Type::F32 => Some("Float32Array"),
1604 Type::F64 => Some("Float64Array"),
1605 Type::Char => None,
1606 Type::String => None,
1607 Type::Id(id) => match &resolve.types[*id].kind {
1608 TypeDefKind::Type(t) => array_ty(resolve, t),
1609 _ => None,
1610 },
1611 }
1612}