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