1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt::Write;
3use std::mem;
4
5use heck::{ToLowerCamelCase, ToUpperCamelCase};
6use wasmtime_environ::component::{CanonicalOptions, 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::component::ComponentIntrinsic;
15use crate::intrinsics::conversion::ConversionIntrinsic;
16use crate::intrinsics::js_helper::JsHelperIntrinsic;
17use crate::intrinsics::p3::async_task::AsyncTaskIntrinsic;
18use crate::intrinsics::resource::ResourceIntrinsic;
19use crate::intrinsics::string::StringIntrinsic;
20use crate::intrinsics::Intrinsic;
21use crate::{get_thrown_type, source};
22use crate::{uwrite, uwriteln};
23
24#[derive(PartialEq)]
26pub enum ErrHandling {
27 None,
30 ThrowResultErr,
32 ResultCatchHandler,
34}
35
36#[derive(Clone, Debug, PartialEq)]
38pub enum ResourceData {
39 Host {
40 tid: TypeResourceTableIndex,
41 rid: ResourceIndex,
42 local_name: String,
43 dtor_name: Option<String>,
44 },
45 Guest {
46 resource_name: String,
47 prefix: Option<String>,
48 },
49}
50
51#[derive(Clone, Debug, PartialEq)]
76pub struct ResourceTable {
77 pub imported: bool,
81
82 pub data: ResourceData,
84}
85
86pub type ResourceMap = BTreeMap<TypeId, ResourceTable>;
88
89pub type RemoteResourceMap = BTreeMap<u32, ResourceTable>;
91
92pub struct FunctionBindgen<'a> {
93 pub resource_map: &'a ResourceMap,
95
96 pub remote_resource_map: &'a RemoteResourceMap,
99
100 pub clear_resource_borrows: bool,
102
103 pub intrinsics: &'a mut BTreeSet<Intrinsic>,
105
106 pub valid_lifting_optimization: bool,
108
109 pub sizes: &'a SizeAlign,
111
112 pub err: ErrHandling,
114
115 pub tmp: usize,
117
118 pub src: source::Source,
120
121 pub block_storage: Vec<source::Source>,
123
124 pub blocks: Vec<(String, Vec<String>)>,
126
127 pub params: Vec<String>,
129
130 pub memory: Option<&'a String>,
132
133 pub realloc: Option<&'a String>,
135
136 pub post_return: Option<&'a String>,
138
139 pub tracing_prefix: &'a String,
141
142 pub tracing_enabled: bool,
144
145 pub encoding: StringEncoding,
147
148 pub callee: &'a str,
150
151 pub callee_resource_dynamic: bool,
153
154 pub resolve: &'a Resolve,
156
157 pub requires_async_porcelain: bool,
162
163 pub is_guest_async_lifted: bool,
165
166 pub canon_opts: &'a CanonicalOptions,
168
169 pub iface_name: Option<&'a str>,
171}
172
173impl FunctionBindgen<'_> {
174 fn tmp(&mut self) -> usize {
175 let ret = self.tmp;
176 self.tmp += 1;
177 ret
178 }
179
180 fn intrinsic(&mut self, intrinsic: Intrinsic) -> String {
181 self.intrinsics.insert(intrinsic);
182 intrinsic.name().to_string()
183 }
184
185 fn clamp_guest<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
186 where
187 T: std::fmt::Display,
188 {
189 let clamp = self.intrinsic(Intrinsic::ClampGuest);
190 results.push(format!("{}({}, {}, {})", clamp, operands[0], min, max));
191 }
192
193 fn load(
194 &mut self,
195 method: &str,
196 offset: ArchitectureSize,
197 operands: &[String],
198 results: &mut Vec<String>,
199 ) {
200 let view = self.intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::DataView));
201 let memory = self.memory.as_ref().unwrap();
202 results.push(format!(
203 "{view}({memory}).{method}({} + {offset}, true)",
204 operands[0],
205 offset = offset.size_wasm32()
206 ));
207 }
208
209 fn store(&mut self, method: &str, offset: ArchitectureSize, operands: &[String]) {
210 let view = self.intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::DataView));
211 let memory = self.memory.as_ref().unwrap();
212 uwriteln!(
213 self.src,
214 "{view}({memory}).{method}({} + {offset}, {}, true);",
215 operands[1],
216 operands[0],
217 offset = offset.size_wasm32()
218 );
219 }
220
221 fn write_result_assignment(&mut self, amt: usize, results: &mut Vec<String>) {
232 match amt {
233 0 => {}
234 1 => {
235 uwrite!(self.src, "const ret = ");
236 results.push("ret".to_string());
237 }
238 n => {
239 uwrite!(self.src, "var [");
240 for i in 0..n {
241 if i > 0 {
242 uwrite!(self.src, ", ");
243 }
244 uwrite!(self.src, "ret{}", i);
245 results.push(format!("ret{i}"));
246 }
247 uwrite!(self.src, "] = ");
248 }
249 }
250 }
251
252 fn bitcast(&mut self, cast: &Bitcast, op: &str) -> String {
253 match cast {
254 Bitcast::I32ToF32 => {
255 let cvt = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::I32ToF32));
256 format!("{cvt}({op})")
257 }
258 Bitcast::F32ToI32 => {
259 let cvt = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::F32ToI32));
260 format!("{cvt}({op})")
261 }
262 Bitcast::I64ToF64 => {
263 let cvt = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::I64ToF64));
264 format!("{cvt}({op})")
265 }
266 Bitcast::F64ToI64 => {
267 let cvt = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::F64ToI64));
268 format!("{cvt}({op})")
269 }
270 Bitcast::I32ToI64 => format!("BigInt({op})"),
271 Bitcast::I64ToI32 => format!("Number({op})"),
272 Bitcast::I64ToF32 => {
273 let cvt = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::I32ToF32));
274 format!("{cvt}(Number({op}))")
275 }
276 Bitcast::F32ToI64 => {
277 let cvt = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::F32ToI32));
278 format!("BigInt({cvt}({op}))")
279 }
280 Bitcast::None
281 | Bitcast::P64ToI64
282 | Bitcast::LToI32
283 | Bitcast::I32ToL
284 | Bitcast::LToP
285 | Bitcast::PToL
286 | Bitcast::PToI32
287 | Bitcast::I32ToP => op.to_string(),
288 Bitcast::PToP64 | Bitcast::I64ToP64 | Bitcast::LToI64 => format!("BigInt({op})"),
289 Bitcast::P64ToP | Bitcast::I64ToL => format!("Number({op})"),
290 Bitcast::Sequence(casts) => {
291 let mut statement = op.to_string();
292 for cast in casts.iter() {
293 statement = self.bitcast(cast, &statement);
294 }
295 statement
296 }
297 }
298 }
299
300 fn start_current_task(&mut self, instr: &Instruction, is_async: bool, fn_name: &str) {
302 let prefix = match instr {
303 Instruction::CallWasm { .. } => "_wasm_call_",
304 Instruction::CallInterface { .. } => "_interface_call_",
305 _ => unreachable!(
306 "unrecognized instruction triggering start of current task: [{instr:?}]"
307 ),
308 };
309 let start_current_task_fn =
310 self.intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::StartCurrentTask));
311 let component_instance_idx = self.canon_opts.instance.as_u32();
312 uwriteln!(
313 self.src,
314 "const {prefix}currentTaskID = {start_current_task_fn}({component_instance_idx}, {is_async}, '{fn_name}');"
315 );
316 }
317
318 fn end_current_task(&mut self) {
325 let end_current_task_fn =
326 self.intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::EndCurrentTask));
327 let component_instance_idx = self.canon_opts.instance.as_u32();
328 uwriteln!(self.src, "{end_current_task_fn}({component_instance_idx});",);
329 }
330}
331
332impl Bindgen for FunctionBindgen<'_> {
333 type Operand = String;
334
335 fn sizes(&self) -> &SizeAlign {
336 self.sizes
337 }
338
339 fn push_block(&mut self) {
340 let prev = mem::take(&mut self.src);
341 self.block_storage.push(prev);
342 }
343
344 fn finish_block(&mut self, operands: &mut Vec<String>) {
345 let to_restore = self.block_storage.pop().unwrap();
346 let src = mem::replace(&mut self.src, to_restore);
347 self.blocks.push((src.into(), mem::take(operands)));
348 }
349
350 fn return_pointer(&mut self, _size: ArchitectureSize, _align: Alignment) -> String {
351 unimplemented!();
352 }
353
354 fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool {
355 array_ty(resolve, ty).is_some()
356 }
357
358 fn emit(
359 &mut self,
360 resolve: &Resolve,
361 inst: &Instruction<'_>,
362 operands: &mut Vec<String>,
363 results: &mut Vec<String>,
364 ) {
365 match inst {
366 Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
367
368 Instruction::I32Const { val } => results.push(val.to_string()),
369
370 Instruction::ConstZero { tys } => {
371 for t in tys.iter() {
372 match t {
373 WasmType::I64 | WasmType::PointerOrI64 => results.push("0n".to_string()),
374 WasmType::I32
375 | WasmType::F32
376 | WasmType::F64
377 | WasmType::Pointer
378 | WasmType::Length => results.push("0".to_string()),
379 }
380 }
381 }
382
383 Instruction::U8FromI32 => self.clamp_guest(results, operands, u8::MIN, u8::MAX),
384
385 Instruction::S8FromI32 => self.clamp_guest(results, operands, i8::MIN, i8::MAX),
386
387 Instruction::U16FromI32 => self.clamp_guest(results, operands, u16::MIN, u16::MAX),
388
389 Instruction::S16FromI32 => self.clamp_guest(results, operands, i16::MIN, i16::MAX),
390
391 Instruction::U32FromI32 => results.push(format!("{} >>> 0", operands[0])),
392
393 Instruction::U64FromI64 => results.push(format!("BigInt.asUintN(64, {})", operands[0])),
394
395 Instruction::S32FromI32 | Instruction::S64FromI64 => {
396 results.push(operands.pop().unwrap())
397 }
398
399 Instruction::I32FromU8 => {
400 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToUint8));
401 results.push(format!("{conv}({op})", op = operands[0]))
402 }
403
404 Instruction::I32FromS8 => {
405 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToInt8));
406 results.push(format!("{conv}({op})", op = operands[0]))
407 }
408
409 Instruction::I32FromU16 => {
410 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToUint16));
411 results.push(format!("{conv}({op})", op = operands[0]))
412 }
413
414 Instruction::I32FromS16 => {
415 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToInt16));
416 results.push(format!("{conv}({op})", op = operands[0]))
417 }
418
419 Instruction::I32FromU32 => {
420 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToUint32));
421 results.push(format!("{conv}({op})", op = operands[0]))
422 }
423
424 Instruction::I32FromS32 => {
425 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToInt32));
426 results.push(format!("{conv}({op})", op = operands[0]))
427 }
428
429 Instruction::I64FromU64 => {
430 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToBigUint64));
431 results.push(format!("{conv}({op})", op = operands[0]))
432 }
433
434 Instruction::I64FromS64 => {
435 let conv = self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToBigInt64));
436 results.push(format!("{conv}({op})", op = operands[0]))
437 }
438
439 Instruction::F32FromCoreF32 | Instruction::F64FromCoreF64 => {
440 results.push(operands.pop().unwrap())
441 }
442
443 Instruction::CoreF32FromF32 | Instruction::CoreF64FromF64 => {
444 results.push(format!("+{}", operands[0]))
445 }
446
447 Instruction::CharFromI32 => {
448 let validate =
449 self.intrinsic(Intrinsic::String(StringIntrinsic::ValidateGuestChar));
450 results.push(format!("{}({})", validate, operands[0]));
451 }
452
453 Instruction::I32FromChar => {
454 let validate = self.intrinsic(Intrinsic::String(StringIntrinsic::ValidateHostChar));
455 results.push(format!("{}({})", validate, operands[0]));
456 }
457
458 Instruction::Bitcasts { casts } => {
459 for (cast, op) in casts.iter().zip(operands) {
460 results.push(self.bitcast(cast, op));
461 }
462 }
463
464 Instruction::BoolFromI32 => {
465 let tmp = self.tmp();
466 uwrite!(self.src, "var bool{} = {};\n", tmp, operands[0]);
467 if self.valid_lifting_optimization {
468 results.push(format!("!!bool{tmp}"));
469 } else {
470 let throw = self.intrinsic(Intrinsic::ThrowInvalidBool);
471 results.push(format!(
472 "bool{tmp} == 0 ? false : (bool{tmp} == 1 ? true : {throw}())"
473 ));
474 }
475 }
476
477 Instruction::I32FromBool => {
478 results.push(format!("{} ? 1 : 0", operands[0]));
479 }
480
481 Instruction::RecordLower { record, .. } => {
482 let tmp = self.tmp();
485 let mut expr = "var {".to_string();
486 for (i, field) in record.fields.iter().enumerate() {
487 if i > 0 {
488 expr.push_str(", ");
489 }
490 let name = format!("v{tmp}_{i}");
491 expr.push_str(&field.name.to_lower_camel_case());
492 expr.push_str(": ");
493 expr.push_str(&name);
494 results.push(name);
495 }
496 uwrite!(self.src, "{} }} = {};\n", expr, operands[0]);
497 }
498
499 Instruction::RecordLift { record, .. } => {
500 let mut result = "{\n".to_string();
504 for (field, op) in record.fields.iter().zip(operands) {
505 result.push_str(&format!("{}: {},\n", field.name.to_lower_camel_case(), op));
506 }
507 result.push('}');
508 results.push(result);
509 }
510
511 Instruction::TupleLower { tuple, .. } => {
512 let tmp = self.tmp();
516 let mut expr = "var [".to_string();
517 for i in 0..tuple.types.len() {
518 if i > 0 {
519 expr.push_str(", ");
520 }
521 let name = format!("tuple{tmp}_{i}");
522 expr.push_str(&name);
523 results.push(name);
524 }
525 uwrite!(self.src, "{}] = {};\n", expr, operands[0]);
526 }
527
528 Instruction::TupleLift { .. } => {
529 results.push(format!("[{}]", operands.join(", ")));
532 }
533
534 Instruction::FlagsLower { flags, .. } => {
535 let op0 = &operands[0];
536
537 for _ in 0..flags.repr().count() {
539 let tmp = self.tmp();
540 let name = format!("flags{tmp}");
541 uwrite!(self.src, "let {name} = 0;\n");
544 results.push(name);
545 }
546
547 uwrite!(
548 self.src,
549 "if (typeof {op0} === 'object' && {op0} !== null) {{\n"
550 );
551
552 for (i, chunk) in flags.flags.chunks(32).enumerate() {
553 let result_name = &results[i];
554
555 uwrite!(self.src, "{result_name} = ");
556 for (i, flag) in chunk.iter().enumerate() {
557 if i != 0 {
558 uwrite!(self.src, " | ");
559 }
560
561 let flag = flag.name.to_lower_camel_case();
562 uwrite!(self.src, "Boolean({op0}.{flag}) << {i}");
563 }
564 uwrite!(self.src, ";\n");
565 }
566
567 uwrite!(
568 self.src,
569 "\
570 }} else if ({op0} !== null && {op0} !== undefined) {{
571 throw new TypeError('only an object, undefined or null can be converted to flags');
572 }}
573 ");
574
575 }
579
580 Instruction::FlagsLift { flags, .. } => {
581 let tmp = self.tmp();
582 results.push(format!("flags{tmp}"));
583
584 if let Some(op) = operands.last() {
585 if flags.flags.len() % 32 != 0 && !self.valid_lifting_optimization {
589 let mask: u32 = 0xffffffff << (flags.flags.len() % 32);
590 uwriteln!(
591 self.src,
592 "if (({op} & {mask}) !== 0) {{
593 throw new TypeError('flags have extraneous bits set');
594 }}"
595 );
596 }
597 }
598
599 uwriteln!(self.src, "var flags{tmp} = {{");
600
601 for (i, flag) in flags.flags.iter().enumerate() {
602 let flag = flag.name.to_lower_camel_case();
603 let op = &operands[i / 32];
604 let mask: u32 = 1 << (i % 32);
605 uwriteln!(self.src, "{flag}: Boolean({op} & {mask}),");
606 }
607
608 uwriteln!(self.src, "}};");
609 }
610
611 Instruction::VariantPayloadName => results.push("e".to_string()),
612
613 Instruction::VariantLower {
614 variant,
615 results: result_types,
616 name,
617 ..
618 } => {
619 let blocks = self
620 .blocks
621 .drain(self.blocks.len() - variant.cases.len()..)
622 .collect::<Vec<_>>();
623 let tmp = self.tmp();
624 let op = &operands[0];
625 uwriteln!(self.src, "var variant{tmp} = {op};");
626
627 for i in 0..result_types.len() {
628 uwriteln!(self.src, "let variant{tmp}_{i};");
629 results.push(format!("variant{tmp}_{i}"));
630 }
631
632 let expr_to_match = format!("variant{tmp}.tag");
633
634 uwriteln!(self.src, "switch ({expr_to_match}) {{");
635 for (case, (block, block_results)) in variant.cases.iter().zip(blocks) {
636 uwriteln!(self.src, "case '{}': {{", case.name.as_str());
637 if case.ty.is_some() {
638 uwriteln!(self.src, "const e = variant{tmp}.val;");
639 }
640 self.src.push_str(&block);
641
642 for (i, result) in block_results.iter().enumerate() {
643 uwriteln!(self.src, "variant{tmp}_{i} = {result};");
644 }
645 uwriteln!(
646 self.src,
647 "break;
648 }}"
649 );
650 }
651 let variant_name = name.to_upper_camel_case();
652 uwriteln!(
653 self.src,
654 r#"default: {{
655 throw new TypeError(`invalid variant tag value \`${{JSON.stringify({expr_to_match})}}\` (received \`${{variant{tmp}}}\`) specified for \`{variant_name}\``);
656 }}"#,
657 );
658 uwriteln!(self.src, "}}");
659 }
660
661 Instruction::VariantLift { variant, name, .. } => {
662 let blocks = self
663 .blocks
664 .drain(self.blocks.len() - variant.cases.len()..)
665 .collect::<Vec<_>>();
666
667 let tmp = self.tmp();
668 let op = &operands[0];
669
670 uwriteln!(
671 self.src,
672 "let variant{tmp};
673 switch ({op}) {{"
674 );
675
676 for (i, (case, (block, block_results))) in
677 variant.cases.iter().zip(blocks).enumerate()
678 {
679 let tag = case.name.as_str();
680 uwriteln!(
681 self.src,
682 "case {i}: {{
683 {block}\
684 variant{tmp} = {{
685 tag: '{tag}',"
686 );
687 if case.ty.is_some() {
688 assert!(block_results.len() == 1);
689 uwriteln!(self.src, " val: {}", block_results[0]);
690 } else {
691 assert!(block_results.is_empty());
692 }
693 uwriteln!(
694 self.src,
695 " }};
696 break;
697 }}"
698 );
699 }
700 let variant_name = name.to_upper_camel_case();
701 if !self.valid_lifting_optimization {
702 uwriteln!(
703 self.src,
704 "default: {{
705 throw new TypeError('invalid variant discriminant for {variant_name}');
706 }}",
707 );
708 }
709 uwriteln!(self.src, "}}");
710 results.push(format!("variant{tmp}"));
711 }
712
713 Instruction::OptionLower {
714 payload,
715 results: result_types,
716 ..
717 } => {
718 let (mut some, some_results) = self.blocks.pop().unwrap();
719 let (mut none, none_results) = self.blocks.pop().unwrap();
720
721 let tmp = self.tmp();
722 let op = &operands[0];
723 uwriteln!(self.src, "var variant{tmp} = {op};");
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 some_result = &some_results[i];
730 let none_result = &none_results[i];
731 uwriteln!(some, "variant{tmp}_{i} = {some_result};");
732 uwriteln!(none, "variant{tmp}_{i} = {none_result};");
733 }
734
735 if maybe_null(resolve, payload) {
736 uwriteln!(
737 self.src,
738 "switch (variant{tmp}.tag) {{
739 case 'none': {{
740 {none}\
741 break;
742 }}
743 case 'some': {{
744 const e = variant{tmp}.val;
745 {some}\
746 break;
747 }}
748 default: {{
749 throw new TypeError('invalid variant specified for option');
750 }}
751 }}",
752 );
753 } else {
754 uwriteln!(
755 self.src,
756 "if (variant{tmp} === null || variant{tmp} === undefined) {{
757 {none}\
758 }} else {{
759 const e = variant{tmp};
760 {some}\
761 }}"
762 );
763 }
764 }
765
766 Instruction::OptionLift { payload, .. } => {
767 let (some, some_results) = self.blocks.pop().unwrap();
768 let (none, none_results) = self.blocks.pop().unwrap();
769 assert!(none_results.is_empty());
770 assert!(some_results.len() == 1);
771 let some_result = &some_results[0];
772
773 let tmp = self.tmp();
774 let op = &operands[0];
775
776 let (v_none, v_some) = if maybe_null(resolve, payload) {
777 (
778 "{ tag: 'none' }",
779 format!(
780 "{{
781 tag: 'some',
782 val: {some_result}
783 }}"
784 ),
785 )
786 } else {
787 ("undefined", some_result.into())
788 };
789
790 if !self.valid_lifting_optimization {
791 uwriteln!(
792 self.src,
793 "let variant{tmp};
794 switch ({op}) {{
795 case 0: {{
796 {none}\
797 variant{tmp} = {v_none};
798 break;
799 }}
800 case 1: {{
801 {some}\
802 variant{tmp} = {v_some};
803 break;
804 }}
805 default: {{
806 throw new TypeError('invalid variant discriminant for option');
807 }}
808 }}",
809 );
810 } else {
811 uwriteln!(
812 self.src,
813 "let variant{tmp};
814 if ({op}) {{
815 {some}\
816 variant{tmp} = {v_some};
817 }} else {{
818 {none}\
819 variant{tmp} = {v_none};
820 }}"
821 );
822 }
823
824 results.push(format!("variant{tmp}"));
825 }
826
827 Instruction::ResultLower {
828 results: result_types,
829 ..
830 } => {
831 let (mut err, err_results) = self.blocks.pop().unwrap();
832 let (mut ok, ok_results) = self.blocks.pop().unwrap();
833
834 let tmp = self.tmp();
835 let op = &operands[0];
836 uwriteln!(self.src, "var variant{tmp} = {op};");
837
838 for i in 0..result_types.len() {
839 uwriteln!(self.src, "let variant{tmp}_{i};");
840 results.push(format!("variant{tmp}_{i}"));
841
842 let ok_result = &ok_results[i];
843 let err_result = &err_results[i];
844 uwriteln!(ok, "variant{tmp}_{i} = {ok_result};");
845 uwriteln!(err, "variant{tmp}_{i} = {err_result};");
846 }
847
848 uwriteln!(
849 self.src,
850 "switch (variant{tmp}.tag) {{
851 case 'ok': {{
852 const e = variant{tmp}.val;
853 {ok}\
854 break;
855 }}
856 case 'err': {{
857 const e = variant{tmp}.val;
858 {err}\
859 break;
860 }}
861 default: {{
862 throw new TypeError('invalid variant specified for result');
863 }}
864 }}",
865 );
866 }
867
868 Instruction::ResultLift { result, .. } => {
869 let (err, err_results) = self.blocks.pop().unwrap();
870 let (ok, ok_results) = self.blocks.pop().unwrap();
871 let ok_result = if result.ok.is_some() {
872 assert_eq!(ok_results.len(), 1);
873 ok_results[0].to_string()
874 } else {
875 assert_eq!(ok_results.len(), 0);
876 String::from("undefined")
877 };
878 let err_result = if result.err.is_some() {
879 assert_eq!(err_results.len(), 1);
880 err_results[0].to_string()
881 } else {
882 assert_eq!(err_results.len(), 0);
883 String::from("undefined")
884 };
885 let tmp = self.tmp();
886 let op0 = &operands[0];
887
888 if !self.valid_lifting_optimization {
889 uwriteln!(
890 self.src,
891 "let variant{tmp};
892 switch ({op0}) {{
893 case 0: {{
894 {ok}\
895 variant{tmp} = {{
896 tag: 'ok',
897 val: {ok_result}
898 }};
899 break;
900 }}
901 case 1: {{
902 {err}\
903 variant{tmp} = {{
904 tag: 'err',
905 val: {err_result}
906 }};
907 break;
908 }}
909 default: {{
910 throw new TypeError('invalid variant discriminant for expected');
911 }}
912 }}",
913 );
914 } else {
915 uwriteln!(
916 self.src,
917 "let variant{tmp};
918 if ({op0}) {{
919 {err}\
920 variant{tmp} = {{
921 tag: 'err',
922 val: {err_result}
923 }};
924 }} else {{
925 {ok}\
926 variant{tmp} = {{
927 tag: 'ok',
928 val: {ok_result}
929 }};
930 }}"
931 );
932 }
933 results.push(format!("variant{tmp}"));
934 }
935
936 Instruction::EnumLower { name, enum_, .. } => {
937 let tmp = self.tmp();
938
939 let op = &operands[0];
940 uwriteln!(self.src, "var val{tmp} = {op};");
941
942 uwriteln!(
944 self.src,
945 "let enum{tmp};
946 switch (val{tmp}) {{"
947 );
948 for (i, case) in enum_.cases.iter().enumerate() {
949 uwriteln!(
950 self.src,
951 "case '{case}': {{
952 enum{tmp} = {i};
953 break;
954 }}",
955 case = case.name
956 );
957 }
958 uwriteln!(self.src, "default: {{");
959 if !self.valid_lifting_optimization {
960 uwriteln!(
961 self.src,
962 "if (({op}) instanceof Error) {{
963 console.error({op});
964 }}"
965 );
966 }
967 uwriteln!(
968 self.src,
969 "
970 throw new TypeError(`\"${{val{tmp}}}\" is not one of the cases of {name}`);
971 }}
972 }}",
973 );
974
975 results.push(format!("enum{tmp}"));
976 }
977
978 Instruction::EnumLift { name, enum_, .. } => {
979 let tmp = self.tmp();
980
981 uwriteln!(
982 self.src,
983 "let enum{tmp};
984 switch ({}) {{",
985 operands[0]
986 );
987 for (i, case) in enum_.cases.iter().enumerate() {
988 uwriteln!(
989 self.src,
990 "case {i}: {{
991 enum{tmp} = '{case}';
992 break;
993 }}",
994 case = case.name
995 );
996 }
997 if !self.valid_lifting_optimization {
998 let name = name.to_upper_camel_case();
999 uwriteln!(
1000 self.src,
1001 "default: {{
1002 throw new TypeError('invalid discriminant specified for {name}');
1003 }}",
1004 );
1005 }
1006 uwriteln!(self.src, "}}");
1007
1008 results.push(format!("enum{tmp}"));
1009 }
1010
1011 Instruction::ListCanonLower { element, .. } => {
1012 let tmp = self.tmp();
1013 let memory = self.memory.as_ref().unwrap();
1014 let realloc = self.realloc.unwrap();
1015
1016 let size = self.sizes.size(element).size_wasm32();
1017 let align = ArchitectureSize::from(self.sizes.align(element)).size_wasm32();
1018 uwriteln!(self.src, "var val{tmp} = {};", operands[0]);
1019 if matches!(element, Type::U8) {
1020 uwriteln!(self.src, "var len{tmp} = val{tmp}.byteLength;");
1021 } else {
1022 uwriteln!(self.src, "var len{tmp} = val{tmp}.length;");
1023 }
1024
1025 uwriteln!(
1026 self.src,
1027 "var ptr{tmp} = {realloc}(0, 0, {align}, len{tmp} * {size});",
1028 );
1029 if matches!(element, Type::U8) {
1031 uwriteln!(
1032 self.src,
1033 "var src{tmp} = new Uint8Array(val{tmp}.buffer || val{tmp}, val{tmp}.byteOffset, len{tmp} * {size});",
1034 );
1035 } else {
1036 uwriteln!(
1037 self.src,
1038 "var src{tmp} = new Uint8Array(val{tmp}.buffer, val{tmp}.byteOffset, len{tmp} * {size});",
1039 );
1040 }
1041 uwriteln!(
1042 self.src,
1043 "(new Uint8Array({memory}.buffer, ptr{tmp}, len{tmp} * {size})).set(src{tmp});",
1044 );
1045 results.push(format!("ptr{tmp}"));
1046 results.push(format!("len{tmp}"));
1047 }
1048
1049 Instruction::ListCanonLift { element, .. } => {
1050 let tmp = self.tmp();
1051 let memory = self.memory.as_ref().unwrap();
1052 uwriteln!(self.src, "var ptr{tmp} = {};", operands[0]);
1053 uwriteln!(self.src, "var len{tmp} = {};", operands[1]);
1054 let array_ty = array_ty(resolve, element).unwrap();
1056 uwriteln!(
1057 self.src,
1058 "var result{tmp} = new {array_ty}({memory}.buffer.slice(ptr{tmp}, ptr{tmp} + len{tmp} * {}));",
1059 self.sizes.size(element).size_wasm32(),
1060 );
1061 results.push(format!("result{tmp}"));
1062 }
1063
1064 Instruction::StringLower { .. } => {
1065 assert!(matches!(
1067 self.encoding,
1068 StringEncoding::UTF8 | StringEncoding::UTF16
1069 ));
1070 let intrinsic = if self.encoding == StringEncoding::UTF16 {
1071 Intrinsic::String(StringIntrinsic::Utf16Encode)
1072 } else {
1073 Intrinsic::String(StringIntrinsic::Utf8Encode)
1074 };
1075 let encode = self.intrinsic(intrinsic);
1076 let tmp = self.tmp();
1077 let memory = self.memory.as_ref().unwrap();
1078 let str = String::from("cabi_realloc");
1079 let realloc = self.realloc.unwrap_or(&str);
1080 uwriteln!(
1081 self.src,
1082 "var ptr{tmp} = {encode}({}, {realloc}, {memory});",
1083 operands[0],
1084 );
1085 if self.encoding == StringEncoding::UTF8 {
1086 let encoded_len =
1087 self.intrinsic(Intrinsic::String(StringIntrinsic::Utf8EncodedLen));
1088 uwriteln!(self.src, "var len{tmp} = {encoded_len};");
1089 } else {
1090 uwriteln!(self.src, "var len{tmp} = {}.length;", operands[0]);
1091 }
1092 results.push(format!("ptr{tmp}"));
1093 results.push(format!("len{tmp}"));
1094 }
1095
1096 Instruction::StringLift => {
1097 assert!(matches!(
1099 self.encoding,
1100 StringEncoding::UTF8 | StringEncoding::UTF16
1101 ));
1102 let intrinsic = if self.encoding == StringEncoding::UTF16 {
1103 Intrinsic::String(StringIntrinsic::Utf16Decoder)
1104 } else {
1105 Intrinsic::String(StringIntrinsic::Utf8Decoder)
1106 };
1107 let decoder = self.intrinsic(intrinsic);
1108 let tmp = self.tmp();
1109 let memory = self.memory.as_ref().unwrap();
1110 uwriteln!(self.src, "var ptr{tmp} = {};", operands[0]);
1111 uwriteln!(self.src, "var len{tmp} = {};", operands[1]);
1112 uwriteln!(
1113 self.src,
1114 "var result{tmp} = {decoder}.decode(new Uint{}Array({memory}.buffer, ptr{tmp}, len{tmp}));",
1115 if self.encoding == StringEncoding::UTF16 { "16" } else { "8" }
1116 );
1117 results.push(format!("result{tmp}"));
1118 }
1119
1120 Instruction::ListLower { element, .. } => {
1121 let (body, body_results) = self.blocks.pop().unwrap();
1122 assert!(body_results.is_empty());
1123 let tmp = self.tmp();
1124 let vec = format!("vec{tmp}");
1125 let result = format!("result{tmp}");
1126 let len = format!("len{tmp}");
1127 let size = self.sizes.size(element).size_wasm32();
1128 let align = ArchitectureSize::from(self.sizes.align(element)).size_wasm32();
1129
1130 uwriteln!(self.src, "var {vec} = {};", operands[0]);
1133 uwriteln!(self.src, "var {len} = {vec}.length;");
1134
1135 let realloc = self.realloc.as_ref().unwrap();
1137 uwriteln!(
1138 self.src,
1139 "var {result} = {realloc}(0, 0, {align}, {len} * {size});"
1140 );
1141
1142 uwriteln!(self.src, "for (let i = 0; i < {vec}.length; i++) {{");
1145 uwriteln!(self.src, "const e = {vec}[i];");
1146 uwrite!(self.src, "const base = {result} + i * {size};");
1147 self.src.push_str(&body);
1148 uwrite!(self.src, "}}\n");
1149
1150 results.push(result);
1151 results.push(len);
1152 }
1153
1154 Instruction::ListLift { element, .. } => {
1155 let (body, body_results) = self.blocks.pop().unwrap();
1156 let tmp = self.tmp();
1157 let size = self.sizes.size(element).size_wasm32();
1158 let len = format!("len{tmp}");
1159 uwriteln!(self.src, "var {len} = {};", operands[1]);
1160 let base = format!("base{tmp}");
1161 uwriteln!(self.src, "var {base} = {};", operands[0]);
1162 let result = format!("result{tmp}");
1163 uwriteln!(self.src, "var {result} = [];");
1164 results.push(result.clone());
1165
1166 uwriteln!(self.src, "for (let i = 0; i < {len}; i++) {{");
1167 uwriteln!(self.src, "const base = {base} + i * {size};");
1168 self.src.push_str(&body);
1169 assert_eq!(body_results.len(), 1);
1170 uwriteln!(self.src, "{result}.push({});", body_results[0]);
1171 uwrite!(self.src, "}}\n");
1172 }
1173
1174 Instruction::IterElem { .. } => results.push("e".to_string()),
1175
1176 Instruction::IterBasePointer => results.push("base".to_string()),
1177
1178 Instruction::CallWasm { sig, .. } => {
1179 let debug_log_fn = self.intrinsic(Intrinsic::DebugLog);
1180 uwriteln!(
1181 self.src,
1182 "{debug_log_fn}('{prefix} [Instruction::CallWasm] (async? {async_}, @ enter)');",
1183 prefix = self.tracing_prefix,
1184 async_ = self.is_guest_async_lifted,
1185 );
1186
1187 self.start_current_task(inst, self.is_guest_async_lifted, self.callee);
1189
1190 let sig_results_length = sig.results.len();
1192 self.write_result_assignment(sig_results_length, results);
1193
1194 let maybe_async_await = if self.requires_async_porcelain {
1196 "await "
1197 } else {
1198 ""
1199 };
1200 uwriteln!(
1201 self.src,
1202 "{maybe_async_await}{callee}({args});",
1203 callee = self.callee,
1204 args = operands.join(", ")
1205 );
1206
1207 if self.tracing_enabled {
1209 let prefix = self.tracing_prefix;
1210 let to_result_string =
1211 self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToResultString));
1212 uwriteln!(
1213 self.src,
1214 "console.error(`{prefix} return {}`);",
1215 if sig_results_length > 0 || !results.is_empty() {
1216 format!("result=${{{to_result_string}(ret)}}")
1217 } else {
1218 "".to_string()
1219 }
1220 );
1221 }
1222
1223 self.end_current_task();
1226 }
1227
1228 Instruction::CallInterface { func, async_ } => {
1230 let debug_log_fn = self.intrinsic(Intrinsic::DebugLog);
1231 uwriteln!(
1232 self.src,
1233 "{debug_log_fn}('{prefix} [Instruction::CallInterface] (async? {async_}, @ enter)');",
1234 prefix = self.tracing_prefix,
1235 async_ = async_.then_some("async").unwrap_or("sync"),
1236 );
1237
1238 self.start_current_task(inst, *async_, &func.name);
1240
1241 let results_length = if func.result.is_none() { 0 } else { 1 };
1242 let maybe_async_await = if self.requires_async_porcelain {
1243 "await "
1244 } else {
1245 ""
1246 };
1247 let call = if self.callee_resource_dynamic {
1248 format!(
1249 "{maybe_async_await}{}.{}({})",
1250 operands[0],
1251 self.callee,
1252 operands[1..].join(", ")
1253 )
1254 } else {
1255 format!(
1256 "{maybe_async_await}{}({})",
1257 self.callee,
1258 operands.join(", ")
1259 )
1260 };
1261
1262 if self.err == ErrHandling::ResultCatchHandler {
1263 let err_payload = if let (_, Some(Type::Id(err_ty))) =
1266 get_thrown_type(self.resolve, func.result).unwrap()
1267 {
1268 match &self.resolve.types[*err_ty].kind {
1269 TypeDefKind::Type(Type::String) => {
1270 self.intrinsic(Intrinsic::GetErrorPayloadString)
1271 }
1272 _ => self.intrinsic(Intrinsic::GetErrorPayload),
1273 }
1274 } else {
1275 self.intrinsic(Intrinsic::GetErrorPayload)
1276 };
1277 uwriteln!(
1278 self.src,
1279 "let ret;
1280 try {{
1281 ret = {{ tag: 'ok', val: {call} }};
1282 }} catch (e) {{
1283 ret = {{ tag: 'err', val: {err_payload}(e) }};
1284 }}",
1285 );
1286 results.push("ret".to_string());
1287 } else {
1288 self.write_result_assignment(results_length, results);
1289 uwriteln!(self.src, "{call};");
1290 }
1291
1292 uwriteln!(
1293 self.src,
1294 "{debug_log_fn}('{prefix} [Instruction::CallInterface] ({async_}, @ post-call)');",
1295 prefix = self.tracing_prefix,
1296 async_ = async_.then_some("async").unwrap_or("sync"),
1297 );
1298
1299 if self.tracing_enabled {
1300 let prefix = self.tracing_prefix;
1301 let to_result_string =
1302 self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToResultString));
1303 uwriteln!(
1304 self.src,
1305 "console.error(`{prefix} return {}`);",
1306 if results_length > 0 || !results.is_empty() {
1307 format!("result=${{{to_result_string}(ret)}}")
1308 } else {
1309 "".to_string()
1310 }
1311 );
1312 }
1313
1314 if self.clear_resource_borrows {
1319 let symbol_resource_handle = self.intrinsic(Intrinsic::SymbolResourceHandle);
1320 let cur_resource_borrows =
1321 self.intrinsic(Intrinsic::Resource(ResourceIntrinsic::CurResourceBorrows));
1322 let is_host = matches!(
1323 self.resource_map.iter().nth(0).unwrap().1.data,
1324 ResourceData::Host { .. }
1325 );
1326
1327 if is_host {
1328 uwriteln!(
1329 self.src,
1330 "for (const rsc of {cur_resource_borrows}) {{
1331 rsc[{symbol_resource_handle}] = undefined;
1332 }}
1333 {cur_resource_borrows} = [];"
1334 );
1335 } else {
1336 uwriteln!(
1337 self.src,
1338 "for (const {{ rsc, drop }} of {cur_resource_borrows}) {{
1339 if (rsc[{symbol_resource_handle}]) {{
1340 drop(rsc[{symbol_resource_handle}]);
1341 rsc[{symbol_resource_handle}] = undefined;
1342 }}
1343 }}
1344 {cur_resource_borrows} = [];"
1345 );
1346 }
1347 self.clear_resource_borrows = false;
1348 }
1349
1350 if !async_ {
1352 self.end_current_task();
1353 }
1354 }
1355
1356 Instruction::Return { func, amt } => {
1357 let debug_log_fn = self.intrinsic(Intrinsic::DebugLog);
1358 uwriteln!(
1359 self.src,
1360 "{debug_log_fn}('{prefix} [Instruction::Return]', {{
1361 funcName: '{func_name}',
1362 paramCount: {amt},
1363 postReturn: {post_return_present}
1364 }});",
1365 func_name = func.name,
1366 post_return_present = self.post_return.is_some(),
1367 prefix = self.tracing_prefix,
1368 );
1369
1370 let get_or_create_async_state_fn = self.intrinsic(Intrinsic::Component(
1373 ComponentIntrinsic::GetOrCreateAsyncState,
1374 ));
1375 let gen_post_return_js = |(invocation_stmt, ret_stmt): (String, Option<String>)| {
1376 format!(
1377 "
1378 let cstate = {get_or_create_async_state_fn}({component_idx});
1379 cstate.mayLeave = false;
1380 {invocation_stmt}
1381 cstate.mayLeave = true;
1382 {ret_stmt}
1383 ",
1384 component_idx = self.canon_opts.instance.as_u32(),
1385 ret_stmt = ret_stmt.unwrap_or_default(),
1386 )
1387 };
1388
1389 match amt {
1391 0 => {
1393 assert!(
1394 !self.is_guest_async_lifted,
1395 "async lifted guest functions must return a single i32"
1396 );
1397 if let Some(f) = &self.post_return {
1398 uwriteln!(
1399 self.src,
1400 "{}",
1401 gen_post_return_js((format!("{f}();"), None))
1402 );
1403 }
1404 }
1405
1406 1 if self.err == ErrHandling::ThrowResultErr => {
1408 assert!(
1409 !self.is_guest_async_lifted,
1410 "async lifted guest functions must return a single i32"
1411 );
1412 let component_err = self.intrinsic(Intrinsic::ComponentError);
1413 let op = &operands[0];
1414 uwriteln!(self.src, "const retCopy = {op};");
1415 if let Some(f) = &self.post_return {
1416 uwriteln!(
1417 self.src,
1418 "{}",
1419 gen_post_return_js((format!("{f}(ret);"), None))
1420 );
1421 }
1422 uwriteln!(
1423 self.src,
1424 "
1425 if (typeof retCopy === 'object' && retCopy.tag === 'err') {{
1426 throw new {component_err}(retCopy.val);
1427 }}
1428 return retCopy.val;
1429 "
1430 );
1431 }
1432
1433 amt => {
1435 let ret_val = match amt {
1436 0 => unreachable!(
1437 "unexpectedly zero return values for synchronous return"
1438 ),
1439 1 => operands[0].to_string(),
1440 _ => format!("[{}]", operands.join(", ")),
1441 };
1442
1443 match (self.post_return, self.is_guest_async_lifted) {
1444 (Some(_), true) => unreachable!(
1445 "async lifted guest functions cannot have post returns"
1446 ),
1447 (Some(post_return_fn), _) => {
1448 uwriteln!(self.src, "const retCopy = {ret_val};");
1453
1454 uwriteln!(
1457 self.src,
1458 "{}",
1459 gen_post_return_js((
1460 format!("{post_return_fn}(ret);"),
1461 Some("return retCopy;".into())
1462 ))
1463 );
1464 }
1465 (None, _is_guest_async_lifted @ false) => {
1467 uwriteln!(self.src, "return {ret_val};",)
1468 }
1469 (None, _is_guest_async_lifted @ true) => {
1471 uwriteln!(self.src, r#"throw new Error("not yet implemented!");"#,);
1472 }
1473 }
1474 }
1475 }
1476 }
1477
1478 Instruction::I32Load { offset } => self.load("getInt32", *offset, operands, results),
1479
1480 Instruction::I64Load { offset } => self.load("getBigInt64", *offset, operands, results),
1481
1482 Instruction::F32Load { offset } => self.load("getFloat32", *offset, operands, results),
1483
1484 Instruction::F64Load { offset } => self.load("getFloat64", *offset, operands, results),
1485
1486 Instruction::I32Load8U { offset } => self.load("getUint8", *offset, operands, results),
1487
1488 Instruction::I32Load8S { offset } => self.load("getInt8", *offset, operands, results),
1489
1490 Instruction::I32Load16U { offset } => {
1491 self.load("getUint16", *offset, operands, results)
1492 }
1493
1494 Instruction::I32Load16S { offset } => self.load("getInt16", *offset, operands, results),
1495
1496 Instruction::I32Store { offset } => self.store("setInt32", *offset, operands),
1497
1498 Instruction::I64Store { offset } => self.store("setBigInt64", *offset, operands),
1499
1500 Instruction::F32Store { offset } => self.store("setFloat32", *offset, operands),
1501
1502 Instruction::F64Store { offset } => self.store("setFloat64", *offset, operands),
1503
1504 Instruction::I32Store8 { offset } => self.store("setInt8", *offset, operands),
1505
1506 Instruction::I32Store16 { offset } => self.store("setInt16", *offset, operands),
1507
1508 Instruction::LengthStore { offset } => self.store("setUint32", *offset, operands),
1509
1510 Instruction::LengthLoad { offset } => {
1511 self.load("getUint32", *offset, operands, results)
1512 }
1513
1514 Instruction::PointerStore { offset } => self.store("setUint32", *offset, operands),
1515
1516 Instruction::PointerLoad { offset } => {
1517 self.load("getUint32", *offset, operands, results)
1518 }
1519
1520 Instruction::Malloc { size, align, .. } => {
1521 let tmp = self.tmp();
1522 let realloc = self.realloc.as_ref().unwrap();
1523 let ptr = format!("ptr{tmp}");
1524 uwriteln!(
1525 self.src,
1526 "var {ptr} = {realloc}(0, 0, {align}, {size});",
1527 align = align.align_wasm32(),
1528 size = size.size_wasm32()
1529 );
1530 results.push(ptr);
1531 }
1532
1533 Instruction::HandleLift { handle, .. } => {
1534 let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
1535 let resource_ty = &crate::dealias(self.resolve, *ty);
1536 let ResourceTable { imported, data } = &self.resource_map[resource_ty];
1537
1538 let is_own = matches!(handle, Handle::Own(_));
1539 let rsc = format!("rsc{}", self.tmp());
1540 let handle = format!("handle{}", self.tmp());
1541 uwriteln!(self.src, "var {handle} = {};", &operands[0]);
1542
1543 match data {
1544 ResourceData::Host {
1545 tid,
1546 rid,
1547 local_name,
1548 dtor_name,
1549 } => {
1550 let tid = tid.as_u32();
1551 let rid = rid.as_u32();
1552 let symbol_dispose = self.intrinsic(Intrinsic::SymbolDispose);
1553 let rsc_table_remove = self
1554 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableRemove));
1555 let rsc_flag = self
1556 .intrinsic(Intrinsic::Resource(ResourceIntrinsic::ResourceTableFlag));
1557 if !imported {
1558 let symbol_resource_handle =
1559 self.intrinsic(Intrinsic::SymbolResourceHandle);
1560 uwriteln!(self.src, "var {rsc} = new.target === {local_name} ? this : Object.create({local_name}.prototype);");
1561 if is_own {
1562 let empty_func = self
1564 .intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
1565 uwriteln!(self.src,
1566 "Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});
1567 finalizationRegistry{tid}.register({rsc}, {handle}, {rsc});");
1568 if let Some(dtor) = dtor_name {
1569 uwriteln!(
1571 self.src,
1572 "Object.defineProperty({rsc}, {symbol_dispose}, {{ writable: true, value: function () {{
1573 finalizationRegistry{tid}.unregister({rsc});
1574 {rsc_table_remove}(handleTable{tid}, {handle});
1575 {rsc}[{symbol_dispose}] = {empty_func};
1576 {rsc}[{symbol_resource_handle}] = undefined;
1577 {dtor}(handleTable{tid}[({handle} << 1) + 1] & ~{rsc_flag});
1578 }}}});"
1579 );
1580 } else {
1581 uwriteln!(
1583 self.src,
1584 "Object.defineProperty({rsc}, {symbol_dispose}, {{ writable: true, value: {empty_func} }});",
1585 );
1586 }
1587 } else {
1588 uwriteln!(self.src, "Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});");
1590 }
1591 } else {
1592 let rep = format!("rep{}", self.tmp());
1593 let symbol_resource_rep = self.intrinsic(Intrinsic::SymbolResourceRep);
1596 let symbol_resource_handle =
1597 self.intrinsic(Intrinsic::SymbolResourceHandle);
1598 uwriteln!(self.src,
1599 "var {rep} = handleTable{tid}[({handle} << 1) + 1] & ~{rsc_flag};
1600 var {rsc} = captureTable{rid}.get({rep});
1601 if (!{rsc}) {{
1602 {rsc} = Object.create({local_name}.prototype);
1603 Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});
1604 Object.defineProperty({rsc}, {symbol_resource_rep}, {{ writable: true, value: {rep} }});
1605 }}"
1606 );
1607 if is_own {
1608 uwriteln!(
1610 self.src,
1611 "else {{
1612 captureTable{rid}.delete({rep});
1613 }}
1614 {rsc_table_remove}(handleTable{tid}, {handle});"
1615 );
1616 }
1617 }
1618
1619 if !is_own {
1621 let cur_resource_borrows = self.intrinsic(Intrinsic::Resource(
1622 ResourceIntrinsic::CurResourceBorrows,
1623 ));
1624 uwriteln!(self.src, "{cur_resource_borrows}.push({rsc});");
1625 self.clear_resource_borrows = true;
1626 }
1627 }
1628
1629 ResourceData::Guest {
1630 resource_name,
1631 prefix,
1632 } => {
1633 let symbol_resource_handle =
1634 self.intrinsic(Intrinsic::SymbolResourceHandle);
1635 let prefix = prefix.as_deref().unwrap_or("");
1636 let lower_camel = resource_name.to_lower_camel_case();
1637
1638 if !imported {
1639 if is_own {
1640 uwriteln!(self.src, "var {rsc} = repTable.get($resource_{prefix}rep${lower_camel}({handle})).rep;");
1641 uwrite!(
1642 self.src,
1643 "repTable.delete({handle});
1644 delete {rsc}[{symbol_resource_handle}];
1645 finalizationRegistry_export${prefix}{lower_camel}.unregister({rsc});
1646 "
1647 );
1648 } else {
1649 uwriteln!(self.src, "var {rsc} = repTable.get({handle}).rep;");
1650 }
1651 } else {
1652 let upper_camel = resource_name.to_upper_camel_case();
1653
1654 uwrite!(
1655 self.src,
1656 "var {rsc} = new.target === import_{prefix}{upper_camel} ? this : Object.create(import_{prefix}{upper_camel}.prototype);
1657 Object.defineProperty({rsc}, {symbol_resource_handle}, {{ writable: true, value: {handle} }});
1658 "
1659 );
1660
1661 uwriteln!(
1662 self.src,
1663 "finalizationRegistry_import${prefix}{lower_camel}.register({rsc}, {handle}, {rsc});",
1664 );
1665
1666 if !is_own {
1667 let cur_resource_borrows = self.intrinsic(Intrinsic::Resource(
1668 ResourceIntrinsic::CurResourceBorrows,
1669 ));
1670 uwriteln!(self.src, "{cur_resource_borrows}.push({{ rsc: {rsc}, drop: $resource_import${prefix}drop${lower_camel} }});");
1671 self.clear_resource_borrows = true;
1672 }
1673 }
1674 }
1675 }
1676 results.push(rsc);
1677 }
1678
1679 Instruction::HandleLower { handle, name, .. } => {
1680 let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
1681 let is_own = matches!(handle, Handle::Own(_));
1682 let ResourceTable { imported, data } =
1683 &self.resource_map[&crate::dealias(self.resolve, *ty)];
1684
1685 let class_name = name.to_upper_camel_case();
1686 let handle = format!("handle{}", self.tmp());
1687 let symbol_resource_handle = self.intrinsic(Intrinsic::SymbolResourceHandle);
1688 let symbol_dispose = self.intrinsic(Intrinsic::SymbolDispose);
1689 let op = &operands[0];
1690
1691 match data {
1692 ResourceData::Host {
1693 tid,
1694 rid,
1695 local_name,
1696 ..
1697 } => {
1698 let tid = tid.as_u32();
1699 let rid = rid.as_u32();
1700 if !imported {
1701 if is_own {
1702 let empty_func = self
1703 .intrinsic(Intrinsic::JsHelper(JsHelperIntrinsic::EmptyFunc));
1704 uwriteln!(
1705 self.src,
1706 "var {handle} = {op}[{symbol_resource_handle}];
1707 if (!{handle}) {{
1708 throw new TypeError('Resource error: Not a valid \"{class_name}\" resource.');
1709 }}
1710 finalizationRegistry{tid}.unregister({op});
1711 {op}[{symbol_dispose}] = {empty_func};
1712 {op}[{symbol_resource_handle}] = undefined;",
1713 );
1714 } else {
1715 let rsc_flag = self.intrinsic(Intrinsic::Resource(
1720 ResourceIntrinsic::ResourceTableFlag,
1721 ));
1722 let own_handle = format!("handle{}", self.tmp());
1723 uwriteln!(self.src,
1724 "var {own_handle} = {op}[{symbol_resource_handle}];
1725 if (!{own_handle} || (handleTable{tid}[({own_handle} << 1) + 1] & {rsc_flag}) === 0) {{
1726 throw new TypeError('Resource error: Not a valid \"{class_name}\" resource.');
1727 }}
1728 var {handle} = handleTable{tid}[({own_handle} << 1) + 1] & ~{rsc_flag};",
1729 );
1730 }
1731 } else {
1732 uwriteln!(
1735 self.src,
1736 "if (!({op} instanceof {local_name})) {{
1737 throw new TypeError('Resource error: Not a valid \"{class_name}\" resource.');
1738 }}
1739 var {handle} = {op}[{symbol_resource_handle}];",
1740 );
1741 let symbol_resource_rep = self.intrinsic(Intrinsic::SymbolResourceRep);
1746 let rsc_table_create = if is_own {
1747 self.intrinsic(Intrinsic::Resource(
1748 ResourceIntrinsic::ResourceTableCreateOwn,
1749 ))
1750 } else {
1751 self.intrinsic(Intrinsic::ScopeId);
1752 self.intrinsic(Intrinsic::Resource(
1753 ResourceIntrinsic::ResourceTableCreateBorrow,
1754 ))
1755 };
1756 uwriteln!(
1757 self.src,
1758 "if (!{handle}) {{
1759 const rep = {op}[{symbol_resource_rep}] || ++captureCnt{rid};
1760 captureTable{rid}.set(rep, {op});
1761 {handle} = {rsc_table_create}(handleTable{tid}, rep);
1762 }}"
1763 );
1764 }
1765 }
1766
1767 ResourceData::Guest {
1768 resource_name,
1769 prefix,
1770 } => {
1771 let upper_camel = resource_name.to_upper_camel_case();
1772 let lower_camel = resource_name.to_lower_camel_case();
1773 let prefix = prefix.as_deref().unwrap_or("");
1774
1775 if !imported {
1776 let local_rep = format!("localRep{}", self.tmp());
1777 uwriteln!(
1778 self.src,
1779 "if (!({op} instanceof {upper_camel})) {{
1780 throw new TypeError('Resource error: Not a valid \"{upper_camel}\" resource.');
1781 }}
1782 let {handle} = {op}[{symbol_resource_handle}];",
1783 );
1784
1785 if is_own {
1786 uwriteln!(
1787 self.src,
1788 "if ({handle} === undefined) {{
1789 var {local_rep} = repCnt++;
1790 repTable.set({local_rep}, {{ rep: {op}, own: true }});
1791 {handle} = $resource_{prefix}new${lower_camel}({local_rep});
1792 {op}[{symbol_resource_handle}] = {handle};
1793 finalizationRegistry_export${prefix}{lower_camel}.register({op}, {handle}, {op});
1794 }}
1795 "
1796 );
1797 } else {
1798 uwriteln!(
1799 self.src,
1800 "if ({handle} === undefined) {{
1801 var {local_rep} = repCnt++;
1802 repTable.set({local_rep}, {{ rep: {op}, own: false }});
1803 {op}[{symbol_resource_handle}] = {local_rep};
1804 }}
1805 "
1806 );
1807 }
1808 } else {
1809 let symbol_resource_handle =
1810 self.intrinsic(Intrinsic::SymbolResourceHandle);
1811 uwrite!(
1812 self.src,
1813 "var {handle} = {op}[{symbol_resource_handle}];
1814 finalizationRegistry_import${prefix}{lower_camel}.unregister({op});
1815 "
1816 );
1817 }
1818 }
1819 }
1820 results.push(handle);
1821 }
1822
1823 Instruction::DropHandle { ty } => {
1824 let _ = ty;
1825 todo!("[Instruction::DropHandle] not yet implemented")
1826 }
1827
1828 Instruction::Flush { amt } => {
1829 for item in operands.iter().take(*amt) {
1830 results.push(item.clone());
1831 }
1832 }
1833
1834 Instruction::ErrorContextLift { .. } => {
1835 uwrite!(self.src, "throw new Error('[Instruction::ErrorContextLift] async is not yet implemented');");
1836 }
1837 Instruction::ErrorContextLower { .. } => {
1838 uwrite!(self.src, "throw new Error('[Instruction::ErrorContextLower] async is not yet implemented');");
1839 }
1840
1841 Instruction::FutureLower { .. } => {
1842 uwrite!(
1843 self.src,
1844 "throw new Error('[Instruction::FutureLower] async is not yet implemented');"
1845 );
1846 }
1847 Instruction::FutureLift { .. } => {
1848 uwrite!(
1849 self.src,
1850 "throw new Error('[Instruction::FutureLift] async is not yet implemented');"
1851 );
1852 }
1853
1854 Instruction::StreamLower { .. } => {
1855 uwrite!(
1856 self.src,
1857 "throw new Error('[Instruction::StreamLower] async is not yet implemented');"
1858 );
1859 }
1860
1861 Instruction::StreamLift { .. } => {
1862 uwrite!(
1863 self.src,
1864 "throw new Error('[Instruction::StreamLift] async is not yet implemented');"
1865 );
1866 }
1867
1868 Instruction::AsyncTaskReturn { name, params } => {
1878 let debug_log_fn = self.intrinsic(Intrinsic::DebugLog);
1879 uwriteln!(
1880 self.src,
1881 "{debug_log_fn}('{prefix} [Instruction::AsyncTaskReturn]', {{
1882 funcName: '{name}',
1883 paramCount: {param_count},
1884 postReturn: {post_return_present}
1885 }});",
1886 param_count = params.len(),
1887 post_return_present = self.post_return.is_some(),
1888 prefix = self.tracing_prefix,
1889 );
1890
1891 assert!(
1892 self.is_guest_async_lifted,
1893 "non-async functions should not be performing async returns (func {name})",
1894 );
1895 assert!(
1896 self.post_return.is_none(),
1897 "async fns cannot have post_return specified (func {name})"
1898 );
1899
1900 let get_current_task_fn =
1901 self.intrinsic(Intrinsic::AsyncTask(AsyncTaskIntrinsic::GetCurrentTask));
1902 let component_idx = self.canon_opts.instance.as_u32();
1903
1904 let i32_typecheck = self.intrinsic(Intrinsic::TypeCheckValidI32);
1905 let to_int32_fn =
1906 self.intrinsic(Intrinsic::Conversion(ConversionIntrinsic::ToInt32));
1907 let unpack_callback_result_fn = self.intrinsic(Intrinsic::AsyncTask(
1908 AsyncTaskIntrinsic::UnpackCallbackResult,
1909 ));
1910 let callback_fn_name = self
1911 .canon_opts
1912 .callback
1913 .map(|v| format!("callback_{}", v.as_u32()))
1915 .expect("callback function name missing");
1916
1917 let (
1920 task_fn_call_prefix,
1921 task_yield_fn,
1922 task_wait_for_event_fn,
1923 task_poll_for_event_fn,
1924 ) = if self.requires_async_porcelain {
1925 (
1926 "await ",
1927 "task.yield",
1928 "task.waitForEvent",
1929 "task.pollForEvent",
1930 )
1931 } else {
1932 (
1933 "",
1934 "task.yieldSync",
1935 "task.waitForEventSync",
1936 "task.pollForEventSync",
1937 )
1938 };
1939
1940 uwriteln!(
1941 self.src,
1942 r#"
1943 const retCopy = {first_op};
1944 if (retCopy !== undefined) {{
1945 if (!({i32_typecheck}(retCopy))) {{ throw new Error('invalid async return value [' + retCopy + '], not a number'); }}
1946 if (retCopy < 0 || retCopy > 3) {{
1947 throw new Error('invalid async return value, outside callback code range');
1948 }}
1949 }}
1950
1951 const taskMeta = {get_current_task_fn}({component_idx});
1952 if (!taskMeta) {{ throw new Error('missing/invalid current task metadata'); }}
1953
1954 const task = taskMeta.task;
1955 if (!task) {{ throw new Error('missing/invalid current task in metadata'); }}
1956
1957 let currentRes = retCopy;
1958 let taskRes, eventCode, index, result;
1959 if (currentRes !== undefined) {{
1960 while (true) {{
1961 let [code, waitableSetIdx] = {unpack_callback_result_fn}(currentRes);
1962 switch (code) {{
1963 case 0: // EXIT
1964 {debug_log_fn}('{prefix} [Instruction::AsyncTaskReturn] exit', {{ fn: '{name}' }});
1965 task.exit();
1966 const results = task.takeResults();
1967 if (!results || !Array.isArray(results)) {{
1968 throw new Error('missing/invalid results, non-array');
1969 }}
1970 switch (results.length) {{
1971 case 0: return undefined;
1972 case 1: return results[0];
1973 default:
1974 throw new Error('multi-return is not supported');
1975 }}
1976 case 1: // YIELD
1977 {debug_log_fn}('{prefix} [Instruction::AsyncTaskReturn] yield', {{ fn: '{name}' }});
1978 taskRes = {task_fn_call_prefix}{task_yield_fn}({{ isCancellable: true, forCallback: true }});
1979 break;
1980 case 2: // WAIT for a given waitable set
1981 {debug_log_fn}('{prefix} [Instruction::AsyncTaskReturn] waiting for event', {{ waitableSetIdx }});
1982 taskRes = {task_fn_call_prefix}{task_wait_for_event_fn}({{ isAsync: true, waitableSetIdx }});
1983 break;
1984 case 3: // POLL
1985 {debug_log_fn}('{prefix} [Instruction::AsyncTaskReturn] polling for event', {{ waitableSetIdx }});
1986 taskRes = {task_fn_call_prefix}{task_poll_for_event_fn}({{ isAsync: true, waitableSetIdx }});
1987 break;
1988 default:
1989 throw new Error('invalid async return value [' + retCopy + ']');
1990 }}
1991
1992 eventCode = taskRes[0];
1993 index = taskRes[1];
1994 result = taskRes[2];
1995 {debug_log_fn}('performing callback', {{ fn: "{callback_fn_name}", eventCode, index, result }});
1996 currentRes = {callback_fn_name}(
1997 {to_int32_fn}(eventCode),
1998 {to_int32_fn}(index),
1999 {to_int32_fn}(result),
2000 );
2001 }}
2002 }}
2003 "#,
2004 first_op = operands.first().map(|s| s.as_str()).unwrap_or("undefined"),
2005 prefix = self.tracing_prefix,
2006 );
2007
2008 self.end_current_task();
2011 }
2012
2013 Instruction::GuestDeallocate { .. }
2014 | Instruction::GuestDeallocateString
2015 | Instruction::GuestDeallocateList { .. }
2016 | Instruction::GuestDeallocateVariant { .. } => unimplemented!("Guest deallocation"),
2017 }
2018 }
2019}
2020
2021pub fn as_nullable<'a>(resolve: &'a Resolve, ty: &'a Type) -> Option<&'a Type> {
2026 let id = match ty {
2027 Type::Id(id) => *id,
2028 _ => return None,
2029 };
2030 match &resolve.types[id].kind {
2031 TypeDefKind::Option(t) => {
2047 if !maybe_null(resolve, t) {
2048 Some(t)
2049 } else {
2050 None
2051 }
2052 }
2053 TypeDefKind::Type(t) => as_nullable(resolve, t),
2054 _ => None,
2055 }
2056}
2057
2058pub fn maybe_null(resolve: &Resolve, ty: &Type) -> bool {
2059 as_nullable(resolve, ty).is_some()
2060}
2061
2062pub fn array_ty(resolve: &Resolve, ty: &Type) -> Option<&'static str> {
2063 match ty {
2064 Type::Bool => None,
2065 Type::U8 => Some("Uint8Array"),
2066 Type::S8 => Some("Int8Array"),
2067 Type::U16 => Some("Uint16Array"),
2068 Type::S16 => Some("Int16Array"),
2069 Type::U32 => Some("Uint32Array"),
2070 Type::S32 => Some("Int32Array"),
2071 Type::U64 => Some("BigUint64Array"),
2072 Type::S64 => Some("BigInt64Array"),
2073 Type::F32 => Some("Float32Array"),
2074 Type::F64 => Some("Float64Array"),
2075 Type::Char => None,
2076 Type::String => None,
2077 Type::ErrorContext => None,
2078 Type::Id(id) => match &resolve.types[*id].kind {
2079 TypeDefKind::Type(t) => array_ty(resolve, t),
2080 _ => None,
2081 },
2082 }
2083}