1 //! Implementation of string transcoding required by the component model.
2 
3 use crate::component::Instance;
4 #[cfg(feature = "component-model-async")]
5 use crate::component::concurrent::WaitResult;
6 use crate::prelude::*;
7 use crate::runtime::component::RuntimeInstance;
8 #[cfg(feature = "component-model-async")]
9 use crate::runtime::component::concurrent::{ResourcePair, SuspensionTarget};
10 use crate::runtime::vm::component::{ComponentInstance, VMComponentContext};
11 use crate::runtime::vm::{HostResultHasUnwindSentinel, VMStore, VmSafe};
12 use core::cell::Cell;
13 use core::ptr::NonNull;
14 use core::slice;
15 use wasmtime_environ::component::*;
16 
17 const UTF16_TAG: usize = 1 << 31;
18 
19 macro_rules! signature {
20     (@ty size) => (usize);
21     (@ty ptr_u8) => (*mut u8);
22     (@ty ptr_u16) => (*mut u16);
23     (@ty ptr_size) => (*mut usize);
24     (@ty u8) => (u8);
25     (@ty u32) => (u32);
26     (@ty u64) => (u64);
27     (@ty bool) => (bool);
28     (@ty vmctx) => (NonNull<VMComponentContext>);
29 }
30 
31 /// Defines a `VMComponentBuiltins` structure which contains any builtins such
32 /// as resource-related intrinsics.
33 macro_rules! define_builtins {
34     (
35         $(
36             $( #[$attr:meta] )*
37             $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
38         )*
39     ) => {
40         /// An array that stores addresses of builtin functions. We translate code
41         /// to use indirect calls. This way, we don't have to patch the code.
42         #[repr(C)]
43         pub struct VMComponentBuiltins {
44             $(
45                 $name: unsafe extern "C" fn(
46                     $(signature!(@ty $param),)*
47                 ) $( -> signature!(@ty $result))?,
48             )*
49         }
50 
51         // SAFETY: the above structure is repr(C) and only contains `VmSafe`
52         // fields.
53         unsafe impl VmSafe for VMComponentBuiltins {}
54 
55         impl VMComponentBuiltins {
56             pub const INIT: VMComponentBuiltins = VMComponentBuiltins {
57                 $($name: trampolines::$name,)*
58             };
59 
60             /// Helper to call `expose_provenance()` on all contained pointers.
61             ///
62             /// This is required to be called at least once before entering wasm
63             /// to inform the compiler that these function pointers may all be
64             /// loaded/stored and used on the "other end" to reacquire
65             /// provenance in Pulley. Pulley models hostcalls with a host
66             /// pointer as the first parameter that's a function pointer under
67             /// the hood, and this call ensures that the use of the function
68             /// pointer is considered valid.
69             pub fn expose_provenance(&self) -> NonNull<Self>{
70                 $(
71                     (self.$name as *mut u8).expose_provenance();
72                 )*
73                 NonNull::from(self)
74             }
75         }
76     };
77 }
78 
79 wasmtime_environ::foreach_builtin_component_function!(define_builtins);
80 
81 /// Submodule with macro-generated constants which are the actual libcall
82 /// transcoders that are invoked by Cranelift. These functions have a specific
83 /// ABI defined by the macro itself and will defer to the actual bodies of each
84 /// implementation following this submodule.
85 mod trampolines {
86     use super::{ComponentInstance, VMComponentContext};
87     use core::ptr::NonNull;
88 
89     macro_rules! shims {
90         (
91             $(
92                 $( #[cfg($attr:meta)] )?
93                 $name:ident( vmctx: vmctx $(, $pname:ident: $param:ident )* ) $( -> $result:ident )?;
94             )*
95         ) => (
96             $(
97                 #[allow(unused_variables, reason = "macro-generated")]
98                 pub unsafe extern "C" fn $name(
99                     vmctx: NonNull<VMComponentContext>
100                     $(,$pname : signature!(@ty $param))*
101                 ) $( -> signature!(@ty $result))? {
102                     $(#[cfg($attr)])?
103                     {
104                         $(shims!(@validate_param $pname $param);)*
105 
106                         let ret = unsafe {
107                             ComponentInstance::enter_host_from_wasm(vmctx, |store, instance| {
108                                 shims!(@invoke $name(store, instance,) $($pname)*)
109                             })
110                         };
111                         shims!(@convert_ret ret $($pname: $param)*)
112                     }
113                     $(
114                         #[cfg(not($attr))]
115                         {
116                             let _ = vmctx;
117                             unreachable!();
118                         }
119                     )?
120                 }
121             )*
122         );
123 
124         // Helper macro to convert a 2-tuple automatically when the last
125         // parameter is a `ptr_size` argument.
126         (@convert_ret $ret:ident) => ($ret);
127         (@convert_ret $ret:ident $retptr:ident: ptr_size) => ({
128             let (a, b) = $ret;
129             unsafe {
130                 *$retptr = b;
131             }
132             a
133         });
134         (@convert_ret $ret:ident $name:ident: $ty:ident $($rest:tt)*) => (
135             shims!(@convert_ret $ret $($rest)*)
136         );
137 
138         (@validate_param $arg:ident ptr_u16) => ({
139             // This should already be guaranteed by the canonical ABI and our
140             // adapter modules, but double-check here to be extra-sure. If this
141             // is a perf concern it can become a `debug_assert!`.
142             assert!(($arg as usize) % 2 == 0, "unaligned 16-bit pointer");
143         });
144         (@validate_param $arg:ident $ty:ident) => ();
145 
146         // Helper macro to invoke `$m` with all of the tokens passed except for
147         // any argument named `ret2`
148         (@invoke $m:ident ($($args:tt)*)) => (super::$m($($args)*));
149 
150         // ignore `ret2`-named arguments
151         (@invoke $m:ident ($($args:tt)*) ret2 $($rest:tt)*) => (
152             shims!(@invoke $m ($($args)*) $($rest)*)
153         );
154 
155         // move all other arguments into the `$args` list
156         (@invoke $m:ident ($($args:tt)*) $param:ident $($rest:tt)*) => (
157             shims!(@invoke $m ($($args)* $param,) $($rest)*)
158         );
159     }
160 
161     wasmtime_environ::foreach_builtin_component_function!(shims);
162 }
163 
164 /// This property should already be guaranteed by construction in the component
165 /// model but assert it here to be extra sure. Nothing below is sound if regions
166 /// can overlap.
167 fn assert_no_overlap<T, U>(a: &[T], b: &[U]) {
168     let a_start = a.as_ptr() as usize;
169     let a_end = a_start + (a.len() * core::mem::size_of::<T>());
170     let b_start = b.as_ptr() as usize;
171     let b_end = b_start + (b.len() * core::mem::size_of::<U>());
172 
173     if a_start < b_start {
174         assert!(a_end < b_start);
175     } else {
176         assert!(b_end < a_start);
177     }
178 }
179 
180 /// Converts a utf8 string to a utf8 string.
181 ///
182 /// The length provided is length of both the source and the destination
183 /// buffers. No value is returned other than whether an invalid string was
184 /// found.
185 unsafe fn utf8_to_utf8(
186     _: &mut dyn VMStore,
187     _: Instance,
188     src: *mut u8,
189     len: usize,
190     dst: *mut u8,
191 ) -> Result<()> {
192     let src = unsafe { slice::from_raw_parts(src, len) };
193     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
194     assert_no_overlap(src, dst);
195     log::trace!("utf8-to-utf8 {len}");
196     let src = core::str::from_utf8(src).map_err(|_| format_err!("invalid utf8 encoding"))?;
197     dst.copy_from_slice(src.as_bytes());
198     Ok(())
199 }
200 
201 /// Converts a utf16 string to a utf16 string.
202 ///
203 /// The length provided is length of both the source and the destination
204 /// buffers. No value is returned other than whether an invalid string was
205 /// found.
206 unsafe fn utf16_to_utf16(
207     _: &mut dyn VMStore,
208     _: Instance,
209     src: *mut u16,
210     len: usize,
211     dst: *mut u16,
212 ) -> Result<()> {
213     let src = unsafe { slice::from_raw_parts(src, len) };
214     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
215     assert_no_overlap(src, dst);
216     log::trace!("utf16-to-utf16 {len}");
217     run_utf16_to_utf16(src, dst)?;
218     Ok(())
219 }
220 
221 /// Transcodes utf16 to itself, returning whether all code points were inside of
222 /// the latin1 space.
223 fn run_utf16_to_utf16(src: &[u16], mut dst: &mut [u16]) -> Result<bool> {
224     let mut all_latin1 = true;
225     for ch in core::char::decode_utf16(src.iter().map(|i| u16::from_le(*i))) {
226         let ch = ch.map_err(|_| format_err!("invalid utf16 encoding"))?;
227         all_latin1 = all_latin1 && u8::try_from(u32::from(ch)).is_ok();
228         let result = ch.encode_utf16(dst);
229         let size = result.len();
230         for item in result {
231             *item = item.to_le();
232         }
233         dst = &mut dst[size..];
234     }
235     Ok(all_latin1)
236 }
237 
238 /// Converts a latin1 string to a latin1 string.
239 ///
240 /// Given that all byte sequences are valid latin1 strings this is simply a
241 /// memory copy.
242 unsafe fn latin1_to_latin1(
243     _: &mut dyn VMStore,
244     _: Instance,
245     src: *mut u8,
246     len: usize,
247     dst: *mut u8,
248 ) -> Result<()> {
249     let src = unsafe { slice::from_raw_parts(src, len) };
250     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
251     assert_no_overlap(src, dst);
252     log::trace!("latin1-to-latin1 {len}");
253     dst.copy_from_slice(src);
254     Ok(())
255 }
256 
257 /// Converts a latin1 string to a utf16 string.
258 ///
259 /// This simply inflates the latin1 characters to the u16 code points. The
260 /// length provided is the same length of the source and destination buffers.
261 unsafe fn latin1_to_utf16(
262     _: &mut dyn VMStore,
263     _: Instance,
264     src: *mut u8,
265     len: usize,
266     dst: *mut u16,
267 ) -> Result<()> {
268     let src = unsafe { slice::from_raw_parts(src, len) };
269     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
270     assert_no_overlap(src, dst);
271     for (src, dst) in src.iter().zip(dst) {
272         *dst = u16::from(*src).to_le();
273     }
274     log::trace!("latin1-to-utf16 {len}");
275     Ok(())
276 }
277 
278 struct CopySizeReturn(usize);
279 
280 unsafe impl HostResultHasUnwindSentinel for CopySizeReturn {
281     type Abi = usize;
282     const SENTINEL: usize = usize::MAX;
283     fn into_abi(self) -> usize {
284         self.0
285     }
286 }
287 
288 /// Converts utf8 to utf16.
289 ///
290 /// The length provided is the same unit length of both buffers, and the
291 /// returned value from this function is how many u16 units were written.
292 unsafe fn utf8_to_utf16(
293     _: &mut dyn VMStore,
294     _: Instance,
295     src: *mut u8,
296     len: usize,
297     dst: *mut u16,
298 ) -> Result<CopySizeReturn> {
299     let src = unsafe { slice::from_raw_parts(src, len) };
300     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
301     assert_no_overlap(src, dst);
302 
303     let result = run_utf8_to_utf16(src, dst)?;
304     log::trace!("utf8-to-utf16 {len} => {result}");
305     Ok(CopySizeReturn(result))
306 }
307 
308 fn run_utf8_to_utf16(src: &[u8], dst: &mut [u16]) -> Result<usize> {
309     let src = core::str::from_utf8(src).map_err(|_| format_err!("invalid utf8 encoding"))?;
310     let mut amt = 0;
311     for (i, dst) in src.encode_utf16().zip(dst) {
312         *dst = i.to_le();
313         amt += 1;
314     }
315     Ok(amt)
316 }
317 
318 struct SizePair {
319     src_read: usize,
320     dst_written: usize,
321 }
322 
323 unsafe impl HostResultHasUnwindSentinel for SizePair {
324     type Abi = (usize, usize);
325     const SENTINEL: (usize, usize) = (usize::MAX, 0);
326     fn into_abi(self) -> (usize, usize) {
327         (self.src_read, self.dst_written)
328     }
329 }
330 
331 /// Converts utf16 to utf8.
332 ///
333 /// Each buffer is specified independently here and the returned value is a pair
334 /// of the number of code units read and code units written. This might perform
335 /// a partial transcode if the destination buffer is not large enough to hold
336 /// the entire contents.
337 unsafe fn utf16_to_utf8(
338     _: &mut dyn VMStore,
339     _: Instance,
340     src: *mut u16,
341     src_len: usize,
342     dst: *mut u8,
343     dst_len: usize,
344 ) -> Result<SizePair> {
345     let src = unsafe { slice::from_raw_parts(src, src_len) };
346     let mut dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) };
347     assert_no_overlap(src, dst);
348 
349     // This iterator will convert to native endianness and additionally count
350     // how many items have been read from the iterator so far. This
351     // count is used to return how many of the source code units were read.
352     let src_iter_read = Cell::new(0);
353     let src_iter = src.iter().map(|i| {
354         src_iter_read.set(src_iter_read.get() + 1);
355         u16::from_le(*i)
356     });
357 
358     let mut src_read = 0;
359     let mut dst_written = 0;
360 
361     for ch in core::char::decode_utf16(src_iter) {
362         let ch = ch.map_err(|_| format_err!("invalid utf16 encoding"))?;
363 
364         // If the destination doesn't have enough space for this character
365         // then the loop is ended and this function will be called later with a
366         // larger destination buffer.
367         if dst.len() < 4 && dst.len() < ch.len_utf8() {
368             break;
369         }
370 
371         // Record that characters were read and then convert the `char` to
372         // utf-8, advancing the destination buffer.
373         src_read = src_iter_read.get();
374         let len = ch.encode_utf8(dst).len();
375         dst_written += len;
376         dst = &mut dst[len..];
377     }
378 
379     log::trace!("utf16-to-utf8 {src_len}/{dst_len} => {src_read}/{dst_written}");
380     Ok(SizePair {
381         src_read,
382         dst_written,
383     })
384 }
385 
386 /// Converts latin1 to utf8.
387 ///
388 /// Receives the independent size of both buffers and returns the number of code
389 /// units read and code units written (both bytes in this case).
390 ///
391 /// This may perform a partial encoding if the destination is not large enough.
392 unsafe fn latin1_to_utf8(
393     _: &mut dyn VMStore,
394     _: Instance,
395     src: *mut u8,
396     src_len: usize,
397     dst: *mut u8,
398     dst_len: usize,
399 ) -> Result<SizePair> {
400     let src = unsafe { slice::from_raw_parts(src, src_len) };
401     let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) };
402     assert_no_overlap(src, dst);
403     let (read, written) = encoding_rs::mem::convert_latin1_to_utf8_partial(src, dst);
404     log::trace!("latin1-to-utf8 {src_len}/{dst_len} => ({read}, {written})");
405     Ok(SizePair {
406         src_read: read,
407         dst_written: written,
408     })
409 }
410 
411 /// Converts utf16 to "latin1+utf16", probably using a utf16 encoding.
412 ///
413 /// The length specified is the length of both the source and destination
414 /// buffers. If the source string has any characters that don't fit in the
415 /// latin1 code space (0xff and below) then a utf16-tagged length will be
416 /// returned. Otherwise the string is "deflated" from a utf16 string to a latin1
417 /// string and the latin1 length is returned.
418 unsafe fn utf16_to_compact_probably_utf16(
419     _: &mut dyn VMStore,
420     _: Instance,
421     src: *mut u16,
422     len: usize,
423     dst: *mut u16,
424 ) -> Result<CopySizeReturn> {
425     let src = unsafe { slice::from_raw_parts(src, len) };
426     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
427     assert_no_overlap(src, dst);
428     let all_latin1 = run_utf16_to_utf16(src, dst)?;
429     if all_latin1 {
430         let (left, dst, right) = unsafe { dst.align_to_mut::<u8>() };
431         assert!(left.is_empty());
432         assert!(right.is_empty());
433         for i in 0..len {
434             dst[i] = dst[2 * i];
435         }
436         log::trace!("utf16-to-compact-probably-utf16 {len} => latin1 {len}");
437         Ok(CopySizeReturn(len))
438     } else {
439         log::trace!("utf16-to-compact-probably-utf16 {len} => utf16 {len}");
440         Ok(CopySizeReturn(len | UTF16_TAG))
441     }
442 }
443 
444 /// Converts a utf8 string to latin1.
445 ///
446 /// The length specified is the same length of both the input and the output
447 /// buffers.
448 ///
449 /// Returns the number of code units read from the source and the number of code
450 /// units written to the destination.
451 ///
452 /// Note that this may not convert the entire source into the destination if the
453 /// original utf8 string has usvs not representable in latin1.
454 unsafe fn utf8_to_latin1(
455     _: &mut dyn VMStore,
456     _: Instance,
457     src: *mut u8,
458     len: usize,
459     dst: *mut u8,
460 ) -> Result<SizePair> {
461     let src = unsafe { slice::from_raw_parts(src, len) };
462     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
463     assert_no_overlap(src, dst);
464     let read = encoding_rs::mem::utf8_latin1_up_to(src);
465     let written = encoding_rs::mem::convert_utf8_to_latin1_lossy(&src[..read], dst);
466     log::trace!("utf8-to-latin1 {len} => ({read}, {written})");
467     Ok(SizePair {
468         src_read: read,
469         dst_written: written,
470     })
471 }
472 
473 /// Converts a utf16 string to latin1
474 ///
475 /// This is the same as `utf8_to_latin1` in terms of parameters/results.
476 unsafe fn utf16_to_latin1(
477     _: &mut dyn VMStore,
478     _: Instance,
479     src: *mut u16,
480     len: usize,
481     dst: *mut u8,
482 ) -> Result<SizePair> {
483     let src = unsafe { slice::from_raw_parts(src, len) };
484     let dst = unsafe { slice::from_raw_parts_mut(dst, len) };
485     assert_no_overlap(src, dst);
486 
487     let mut size = 0;
488     for (src, dst) in src.iter().zip(dst) {
489         let src = u16::from_le(*src);
490         match u8::try_from(src) {
491             Ok(src) => *dst = src,
492             Err(_) => break,
493         }
494         size += 1;
495     }
496     log::trace!("utf16-to-latin1 {len} => {size}");
497     Ok(SizePair {
498         src_read: size,
499         dst_written: size,
500     })
501 }
502 
503 /// Converts a utf8 string to a utf16 string which has been partially converted
504 /// as latin1 prior.
505 ///
506 /// The original string has already been partially transcoded with
507 /// `utf8_to_latin1` and that was determined to not be able to transcode the
508 /// entire string. The substring of the source that couldn't be encoded into
509 /// latin1 is passed here via `src` and `src_len`.
510 ///
511 /// The destination buffer is specified by `dst` and `dst_len`. The first
512 /// `latin1_bytes_so_far` bytes (not code units) of the `dst` buffer have
513 /// already been filled in with latin1 characters and need to be inflated
514 /// in-place to their utf16 equivalents.
515 ///
516 /// After the initial latin1 code units have been inflated the entirety of `src`
517 /// is then transcoded into the remaining space within `dst`.
518 unsafe fn utf8_to_compact_utf16(
519     _: &mut dyn VMStore,
520     _: Instance,
521     src: *mut u8,
522     src_len: usize,
523     dst: *mut u16,
524     dst_len: usize,
525     latin1_bytes_so_far: usize,
526 ) -> Result<CopySizeReturn> {
527     let src = unsafe { slice::from_raw_parts(src, src_len) };
528     let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) };
529     assert_no_overlap(src, dst);
530 
531     let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far);
532     let result = run_utf8_to_utf16(src, dst)?;
533     log::trace!("utf8-to-compact-utf16 {src_len}/{dst_len}/{latin1_bytes_so_far} => {result}");
534     Ok(CopySizeReturn(result + latin1_bytes_so_far))
535 }
536 
537 /// Same as `utf8_to_compact_utf16` but for utf16 source strings.
538 unsafe fn utf16_to_compact_utf16(
539     _: &mut dyn VMStore,
540     _: Instance,
541     src: *mut u16,
542     src_len: usize,
543     dst: *mut u16,
544     dst_len: usize,
545     latin1_bytes_so_far: usize,
546 ) -> Result<CopySizeReturn> {
547     let src = unsafe { slice::from_raw_parts(src, src_len) };
548     let dst = unsafe { slice::from_raw_parts_mut(dst, dst_len) };
549     assert_no_overlap(src, dst);
550 
551     let dst = inflate_latin1_bytes(dst, latin1_bytes_so_far);
552     run_utf16_to_utf16(src, dst)?;
553     let result = src.len();
554     log::trace!("utf16-to-compact-utf16 {src_len}/{dst_len}/{latin1_bytes_so_far} => {result}");
555     Ok(CopySizeReturn(result + latin1_bytes_so_far))
556 }
557 
558 /// Inflates the `latin1_bytes_so_far` number of bytes written to the beginning
559 /// of `dst` into u16 codepoints.
560 ///
561 /// Returns the remaining space in the destination that can be transcoded into,
562 /// slicing off the prefix of the string that was inflated from the latin1
563 /// bytes.
564 fn inflate_latin1_bytes(dst: &mut [u16], latin1_bytes_so_far: usize) -> &mut [u16] {
565     // Note that `latin1_bytes_so_far` is a byte measure while `dst` is a region
566     // of u16 units. This `split_at_mut` uses the byte index as an index into
567     // the u16 unit because each of the latin1 bytes will become a whole code
568     // unit in the destination which is 2 bytes large.
569     let (to_inflate, rest) = dst.split_at_mut(latin1_bytes_so_far);
570 
571     // Use a byte-oriented view to inflate the original latin1 bytes.
572     let (left, mid, right) = unsafe { to_inflate.align_to_mut::<u8>() };
573     assert!(left.is_empty());
574     assert!(right.is_empty());
575     for i in (0..latin1_bytes_so_far).rev() {
576         mid[2 * i] = mid[i];
577         mid[2 * i + 1] = 0;
578     }
579 
580     return rest;
581 }
582 
583 fn resource_new32(
584     store: &mut dyn VMStore,
585     instance: Instance,
586     _caller_instance: u32,
587     resource: u32,
588     rep: u32,
589 ) -> Result<u32> {
590     let resource = TypeResourceTableIndex::from_u32(resource);
591     instance.resource_new32(store, resource, rep)
592 }
593 
594 fn resource_rep32(
595     store: &mut dyn VMStore,
596     instance: Instance,
597     _caller_instance: u32,
598     resource: u32,
599     idx: u32,
600 ) -> Result<u32> {
601     let resource = TypeResourceTableIndex::from_u32(resource);
602     instance.resource_rep32(store, resource, idx)
603 }
604 
605 fn resource_drop(
606     store: &mut dyn VMStore,
607     instance: Instance,
608     _caller_instance: u32,
609     resource: u32,
610     idx: u32,
611 ) -> Result<ResourceDropRet> {
612     let resource = TypeResourceTableIndex::from_u32(resource);
613     Ok(ResourceDropRet(
614         instance.resource_drop(store, resource, idx)?,
615     ))
616 }
617 
618 struct ResourceDropRet(Option<u32>);
619 
620 unsafe impl HostResultHasUnwindSentinel for ResourceDropRet {
621     type Abi = u64;
622     const SENTINEL: u64 = u64::MAX;
623     fn into_abi(self) -> u64 {
624         match self.0 {
625             Some(rep) => (u64::from(rep) << 1) | 1,
626             None => 0,
627         }
628     }
629 }
630 
631 fn resource_transfer_own(
632     store: &mut dyn VMStore,
633     instance: Instance,
634     src_idx: u32,
635     src_table: u32,
636     dst_table: u32,
637 ) -> Result<u32> {
638     let src_table = TypeResourceTableIndex::from_u32(src_table);
639     let dst_table = TypeResourceTableIndex::from_u32(dst_table);
640     instance.resource_transfer_own(store, src_idx, src_table, dst_table)
641 }
642 
643 fn resource_transfer_borrow(
644     store: &mut dyn VMStore,
645     instance: Instance,
646     src_idx: u32,
647     src_table: u32,
648     dst_table: u32,
649 ) -> Result<u32> {
650     let src_table = TypeResourceTableIndex::from_u32(src_table);
651     let dst_table = TypeResourceTableIndex::from_u32(dst_table);
652     instance.resource_transfer_borrow(store, src_idx, src_table, dst_table)
653 }
654 
655 fn trap(_store: &mut dyn VMStore, _instance: Instance, code: u32) -> Result<()> {
656     Err(wasmtime_environ::Trap::from_u8(u8::try_from(code).unwrap())
657         .unwrap()
658         .into())
659 }
660 
661 fn enter_sync_call(
662     store: &mut dyn VMStore,
663     instance: Instance,
664     caller_instance: u32,
665     callee_async: u32,
666     callee_instance: u32,
667 ) -> Result<()> {
668     store.enter_guest_sync_call(
669         Some(RuntimeInstance {
670             instance: instance.id().instance(),
671             index: RuntimeComponentInstanceIndex::from_u32(caller_instance),
672         }),
673         callee_async != 0,
674         RuntimeInstance {
675             instance: instance.id().instance(),
676             index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
677         },
678     )
679 }
680 
681 fn exit_sync_call(store: &mut dyn VMStore, instance: Instance) -> Result<()> {
682     store
683         .component_resource_tables(Some(instance))
684         .validate_scope_exit()?;
685     store.exit_guest_sync_call(true)
686 }
687 
688 #[cfg(feature = "component-model-async")]
689 fn backpressure_modify(
690     store: &mut dyn VMStore,
691     instance: Instance,
692     caller_instance: u32,
693     increment: u8,
694 ) -> Result<()> {
695     store.backpressure_modify(
696         RuntimeInstance {
697             instance: instance.id().instance(),
698             index: RuntimeComponentInstanceIndex::from_u32(caller_instance),
699         },
700         |old| {
701             if increment != 0 {
702                 old.checked_add(1)
703             } else {
704                 old.checked_sub(1)
705             }
706         },
707     )
708 }
709 
710 #[cfg(feature = "component-model-async")]
711 unsafe fn task_return(
712     store: &mut dyn VMStore,
713     instance: Instance,
714     _caller_instance: u32,
715     ty: u32,
716     options: u32,
717     storage: *mut u8,
718     storage_len: usize,
719 ) -> Result<()> {
720     instance.task_return(
721         store,
722         TypeTupleIndex::from_u32(ty),
723         OptionsIndex::from_u32(options),
724         unsafe { core::slice::from_raw_parts(storage.cast(), storage_len) },
725     )
726 }
727 
728 #[cfg(feature = "component-model-async")]
729 fn task_cancel(store: &mut dyn VMStore, instance: Instance, _caller_instance: u32) -> Result<()> {
730     instance.task_cancel(store)
731 }
732 
733 #[cfg(feature = "component-model-async")]
734 fn waitable_set_new(
735     store: &mut dyn VMStore,
736     instance: Instance,
737     caller_instance: u32,
738 ) -> Result<u32> {
739     instance.waitable_set_new(
740         store,
741         RuntimeComponentInstanceIndex::from_u32(caller_instance),
742     )
743 }
744 
745 #[cfg(feature = "component-model-async")]
746 fn waitable_set_wait(
747     store: &mut dyn VMStore,
748     instance: Instance,
749     _caller: u32,
750     options: u32,
751     set: u32,
752     payload: u32,
753 ) -> Result<u32> {
754     instance.waitable_set_wait(store, OptionsIndex::from_u32(options), set, payload)
755 }
756 
757 #[cfg(feature = "component-model-async")]
758 fn waitable_set_poll(
759     store: &mut dyn VMStore,
760     instance: Instance,
761     _caller: u32,
762     options: u32,
763     set: u32,
764     payload: u32,
765 ) -> Result<u32> {
766     instance.waitable_set_poll(store, OptionsIndex::from_u32(options), set, payload)
767 }
768 
769 #[cfg(feature = "component-model-async")]
770 fn waitable_set_drop(
771     store: &mut dyn VMStore,
772     instance: Instance,
773     caller_instance: u32,
774     set: u32,
775 ) -> Result<()> {
776     instance.waitable_set_drop(
777         store,
778         RuntimeComponentInstanceIndex::from_u32(caller_instance),
779         set,
780     )
781 }
782 
783 #[cfg(feature = "component-model-async")]
784 fn waitable_join(
785     store: &mut dyn VMStore,
786     instance: Instance,
787     caller_instance: u32,
788     waitable: u32,
789     set: u32,
790 ) -> Result<()> {
791     instance.waitable_join(
792         store,
793         RuntimeComponentInstanceIndex::from_u32(caller_instance),
794         waitable,
795         set,
796     )
797 }
798 
799 #[cfg(feature = "component-model-async")]
800 fn thread_yield(
801     store: &mut dyn VMStore,
802     instance: Instance,
803     caller_instance: u32,
804     cancellable: u8,
805 ) -> Result<bool> {
806     instance
807         .suspension_intrinsic(
808             store,
809             RuntimeComponentInstanceIndex::from_u32(caller_instance),
810             cancellable != 0,
811             true,
812             SuspensionTarget::None,
813         )
814         .map(|r| r == WaitResult::Cancelled)
815 }
816 
817 #[cfg(feature = "component-model-async")]
818 fn subtask_drop(
819     store: &mut dyn VMStore,
820     instance: Instance,
821     caller_instance: u32,
822     task_id: u32,
823 ) -> Result<()> {
824     instance.subtask_drop(
825         store,
826         RuntimeComponentInstanceIndex::from_u32(caller_instance),
827         task_id,
828     )
829 }
830 
831 #[cfg(feature = "component-model-async")]
832 fn subtask_cancel(
833     store: &mut dyn VMStore,
834     instance: Instance,
835     caller_instance: u32,
836     async_: u8,
837     task_id: u32,
838 ) -> Result<u32> {
839     instance.subtask_cancel(
840         store,
841         RuntimeComponentInstanceIndex::from_u32(caller_instance),
842         async_ != 0,
843         task_id,
844     )
845 }
846 
847 #[cfg(feature = "component-model-async")]
848 unsafe fn prepare_call(
849     store: &mut dyn VMStore,
850     instance: Instance,
851     memory: *mut u8,
852     start: *mut u8,
853     return_: *mut u8,
854     caller_instance: u32,
855     callee_instance: u32,
856     task_return_type: u32,
857     callee_async: u32,
858     string_encoding: u32,
859     result_count_or_max_if_async: u32,
860     storage: *mut u8,
861     storage_len: usize,
862 ) -> Result<()> {
863     unsafe {
864         store.component_async_store().prepare_call(
865             instance,
866             memory.cast::<crate::vm::VMMemoryDefinition>(),
867             start.cast::<crate::vm::VMFuncRef>(),
868             return_.cast::<crate::vm::VMFuncRef>(),
869             RuntimeComponentInstanceIndex::from_u32(caller_instance),
870             RuntimeComponentInstanceIndex::from_u32(callee_instance),
871             TypeTupleIndex::from_u32(task_return_type),
872             callee_async != 0,
873             u8::try_from(string_encoding).unwrap(),
874             result_count_or_max_if_async,
875             storage.cast::<crate::ValRaw>(),
876             storage_len,
877         )
878     }
879 }
880 
881 #[cfg(feature = "component-model-async")]
882 unsafe fn sync_start(
883     store: &mut dyn VMStore,
884     instance: Instance,
885     callback: *mut u8,
886     storage: *mut u8,
887     storage_len: usize,
888     callee: *mut u8,
889     param_count: u32,
890 ) -> Result<()> {
891     unsafe {
892         store.component_async_store().sync_start(
893             instance,
894             callback.cast::<crate::vm::VMFuncRef>(),
895             callee.cast::<crate::vm::VMFuncRef>(),
896             param_count,
897             storage.cast::<std::mem::MaybeUninit<crate::ValRaw>>(),
898             storage_len,
899         )
900     }
901 }
902 
903 #[cfg(feature = "component-model-async")]
904 unsafe fn async_start(
905     store: &mut dyn VMStore,
906     instance: Instance,
907     callback: *mut u8,
908     post_return: *mut u8,
909     callee: *mut u8,
910     param_count: u32,
911     result_count: u32,
912     flags: u32,
913 ) -> Result<u32> {
914     unsafe {
915         store.component_async_store().async_start(
916             instance,
917             callback.cast::<crate::vm::VMFuncRef>(),
918             post_return.cast::<crate::vm::VMFuncRef>(),
919             callee.cast::<crate::vm::VMFuncRef>(),
920             param_count,
921             result_count,
922             flags,
923         )
924     }
925 }
926 
927 #[cfg(feature = "component-model-async")]
928 fn future_transfer(
929     store: &mut dyn VMStore,
930     instance: Instance,
931     src_idx: u32,
932     src_table: u32,
933     dst_table: u32,
934 ) -> Result<u32> {
935     instance.future_transfer(
936         store,
937         src_idx,
938         TypeFutureTableIndex::from_u32(src_table),
939         TypeFutureTableIndex::from_u32(dst_table),
940     )
941 }
942 
943 #[cfg(feature = "component-model-async")]
944 fn stream_transfer(
945     store: &mut dyn VMStore,
946     instance: Instance,
947     src_idx: u32,
948     src_table: u32,
949     dst_table: u32,
950 ) -> Result<u32> {
951     instance.stream_transfer(
952         store,
953         src_idx,
954         TypeStreamTableIndex::from_u32(src_table),
955         TypeStreamTableIndex::from_u32(dst_table),
956     )
957 }
958 
959 #[cfg(feature = "component-model-async")]
960 fn error_context_transfer(
961     store: &mut dyn VMStore,
962     instance: Instance,
963     src_idx: u32,
964     src_table: u32,
965     dst_table: u32,
966 ) -> Result<u32> {
967     let src_table = TypeComponentLocalErrorContextTableIndex::from_u32(src_table);
968     let dst_table = TypeComponentLocalErrorContextTableIndex::from_u32(dst_table);
969     instance.error_context_transfer(store, src_idx, src_table, dst_table)
970 }
971 
972 #[cfg(feature = "component-model-async")]
973 unsafe impl HostResultHasUnwindSentinel for ResourcePair {
974     type Abi = u64;
975     const SENTINEL: u64 = u64::MAX;
976 
977     fn into_abi(self) -> Self::Abi {
978         assert!(self.write & (1 << 31) == 0);
979         (u64::from(self.write) << 32) | u64::from(self.read)
980     }
981 }
982 
983 #[cfg(feature = "component-model-async")]
984 fn future_new(
985     store: &mut dyn VMStore,
986     instance: Instance,
987     _caller_instance: u32,
988     ty: u32,
989 ) -> Result<ResourcePair> {
990     instance.future_new(store, TypeFutureTableIndex::from_u32(ty))
991 }
992 
993 #[cfg(feature = "component-model-async")]
994 fn future_write(
995     store: &mut dyn VMStore,
996     instance: Instance,
997     caller_instance: u32,
998     ty: u32,
999     options: u32,
1000     future: u32,
1001     address: u32,
1002 ) -> Result<u32> {
1003     store.component_async_store().future_write(
1004         instance,
1005         RuntimeComponentInstanceIndex::from_u32(caller_instance),
1006         TypeFutureTableIndex::from_u32(ty),
1007         OptionsIndex::from_u32(options),
1008         future,
1009         address,
1010     )
1011 }
1012 
1013 #[cfg(feature = "component-model-async")]
1014 fn future_read(
1015     store: &mut dyn VMStore,
1016     instance: Instance,
1017     caller_instance: u32,
1018     ty: u32,
1019     options: u32,
1020     future: u32,
1021     address: u32,
1022 ) -> Result<u32> {
1023     store.component_async_store().future_read(
1024         instance,
1025         RuntimeComponentInstanceIndex::from_u32(caller_instance),
1026         TypeFutureTableIndex::from_u32(ty),
1027         OptionsIndex::from_u32(options),
1028         future,
1029         address,
1030     )
1031 }
1032 
1033 #[cfg(feature = "component-model-async")]
1034 fn future_cancel_write(
1035     store: &mut dyn VMStore,
1036     instance: Instance,
1037     _caller_instance: u32,
1038     ty: u32,
1039     async_: u8,
1040     writer: u32,
1041 ) -> Result<u32> {
1042     instance.future_cancel_write(
1043         store,
1044         TypeFutureTableIndex::from_u32(ty),
1045         async_ != 0,
1046         writer,
1047     )
1048 }
1049 
1050 #[cfg(feature = "component-model-async")]
1051 fn future_cancel_read(
1052     store: &mut dyn VMStore,
1053     instance: Instance,
1054     _caller_instance: u32,
1055     ty: u32,
1056     async_: u8,
1057     reader: u32,
1058 ) -> Result<u32> {
1059     instance.future_cancel_read(
1060         store,
1061         TypeFutureTableIndex::from_u32(ty),
1062         async_ != 0,
1063         reader,
1064     )
1065 }
1066 
1067 #[cfg(feature = "component-model-async")]
1068 fn future_drop_writable(
1069     store: &mut dyn VMStore,
1070     instance: Instance,
1071     _caller_instance: u32,
1072     ty: u32,
1073     writer: u32,
1074 ) -> Result<()> {
1075     store.component_async_store().future_drop_writable(
1076         instance,
1077         TypeFutureTableIndex::from_u32(ty),
1078         writer,
1079     )
1080 }
1081 
1082 #[cfg(feature = "component-model-async")]
1083 fn future_drop_readable(
1084     store: &mut dyn VMStore,
1085     instance: Instance,
1086     _caller_instance: u32,
1087     ty: u32,
1088     reader: u32,
1089 ) -> Result<()> {
1090     instance.future_drop_readable(store, TypeFutureTableIndex::from_u32(ty), reader)
1091 }
1092 
1093 #[cfg(feature = "component-model-async")]
1094 fn stream_new(
1095     store: &mut dyn VMStore,
1096     instance: Instance,
1097     _caller_instance: u32,
1098     ty: u32,
1099 ) -> Result<ResourcePair> {
1100     instance.stream_new(store, TypeStreamTableIndex::from_u32(ty))
1101 }
1102 
1103 #[cfg(feature = "component-model-async")]
1104 fn stream_write(
1105     store: &mut dyn VMStore,
1106     instance: Instance,
1107     caller_instance: u32,
1108     ty: u32,
1109     options: u32,
1110     stream: u32,
1111     address: u32,
1112     count: u32,
1113 ) -> Result<u32> {
1114     store.component_async_store().stream_write(
1115         instance,
1116         RuntimeComponentInstanceIndex::from_u32(caller_instance),
1117         TypeStreamTableIndex::from_u32(ty),
1118         OptionsIndex::from_u32(options),
1119         stream,
1120         address,
1121         count,
1122     )
1123 }
1124 
1125 #[cfg(feature = "component-model-async")]
1126 fn stream_read(
1127     store: &mut dyn VMStore,
1128     instance: Instance,
1129     caller_instance: u32,
1130     ty: u32,
1131     options: u32,
1132     stream: u32,
1133     address: u32,
1134     count: u32,
1135 ) -> Result<u32> {
1136     store.component_async_store().stream_read(
1137         instance,
1138         RuntimeComponentInstanceIndex::from_u32(caller_instance),
1139         TypeStreamTableIndex::from_u32(ty),
1140         OptionsIndex::from_u32(options),
1141         stream,
1142         address,
1143         count,
1144     )
1145 }
1146 
1147 #[cfg(feature = "component-model-async")]
1148 fn stream_cancel_write(
1149     store: &mut dyn VMStore,
1150     instance: Instance,
1151     _caller_instance: u32,
1152     ty: u32,
1153     async_: u8,
1154     writer: u32,
1155 ) -> Result<u32> {
1156     instance.stream_cancel_write(
1157         store,
1158         TypeStreamTableIndex::from_u32(ty),
1159         async_ != 0,
1160         writer,
1161     )
1162 }
1163 
1164 #[cfg(feature = "component-model-async")]
1165 fn stream_cancel_read(
1166     store: &mut dyn VMStore,
1167     instance: Instance,
1168     _caller_instance: u32,
1169     ty: u32,
1170     async_: u8,
1171     reader: u32,
1172 ) -> Result<u32> {
1173     instance.stream_cancel_read(
1174         store,
1175         TypeStreamTableIndex::from_u32(ty),
1176         async_ != 0,
1177         reader,
1178     )
1179 }
1180 
1181 #[cfg(feature = "component-model-async")]
1182 fn stream_drop_writable(
1183     store: &mut dyn VMStore,
1184     instance: Instance,
1185     _caller_instance: u32,
1186     ty: u32,
1187     writer: u32,
1188 ) -> Result<()> {
1189     store.component_async_store().stream_drop_writable(
1190         instance,
1191         TypeStreamTableIndex::from_u32(ty),
1192         writer,
1193     )
1194 }
1195 
1196 #[cfg(feature = "component-model-async")]
1197 fn stream_drop_readable(
1198     store: &mut dyn VMStore,
1199     instance: Instance,
1200     _caller_instance: u32,
1201     ty: u32,
1202     reader: u32,
1203 ) -> Result<()> {
1204     instance.stream_drop_readable(store, TypeStreamTableIndex::from_u32(ty), reader)
1205 }
1206 
1207 #[cfg(feature = "component-model-async")]
1208 fn flat_stream_write(
1209     store: &mut dyn VMStore,
1210     instance: Instance,
1211     caller_instance: u32,
1212     ty: u32,
1213     options: u32,
1214     payload_size: u32,
1215     payload_align: u32,
1216     stream: u32,
1217     address: u32,
1218     count: u32,
1219 ) -> Result<u32> {
1220     store.component_async_store().flat_stream_write(
1221         instance,
1222         RuntimeComponentInstanceIndex::from_u32(caller_instance),
1223         TypeStreamTableIndex::from_u32(ty),
1224         OptionsIndex::from_u32(options),
1225         payload_size,
1226         payload_align,
1227         stream,
1228         address,
1229         count,
1230     )
1231 }
1232 
1233 #[cfg(feature = "component-model-async")]
1234 fn flat_stream_read(
1235     store: &mut dyn VMStore,
1236     instance: Instance,
1237     caller_instance: u32,
1238     ty: u32,
1239     options: u32,
1240     payload_size: u32,
1241     payload_align: u32,
1242     stream: u32,
1243     address: u32,
1244     count: u32,
1245 ) -> Result<u32> {
1246     store.component_async_store().flat_stream_read(
1247         instance,
1248         RuntimeComponentInstanceIndex::from_u32(caller_instance),
1249         TypeStreamTableIndex::from_u32(ty),
1250         OptionsIndex::from_u32(options),
1251         payload_size,
1252         payload_align,
1253         stream,
1254         address,
1255         count,
1256     )
1257 }
1258 
1259 #[cfg(feature = "component-model-async")]
1260 fn error_context_new(
1261     store: &mut dyn VMStore,
1262     instance: Instance,
1263     _caller_instance: u32,
1264     ty: u32,
1265     options: u32,
1266     debug_msg_address: u32,
1267     debug_msg_len: u32,
1268 ) -> Result<u32> {
1269     instance.error_context_new(
1270         store.store_opaque_mut(),
1271         TypeComponentLocalErrorContextTableIndex::from_u32(ty),
1272         OptionsIndex::from_u32(options),
1273         debug_msg_address,
1274         debug_msg_len,
1275     )
1276 }
1277 
1278 #[cfg(feature = "component-model-async")]
1279 fn error_context_debug_message(
1280     store: &mut dyn VMStore,
1281     instance: Instance,
1282     _caller_instance: u32,
1283     ty: u32,
1284     options: u32,
1285     err_ctx_handle: u32,
1286     debug_msg_address: u32,
1287 ) -> Result<()> {
1288     store.component_async_store().error_context_debug_message(
1289         instance,
1290         TypeComponentLocalErrorContextTableIndex::from_u32(ty),
1291         OptionsIndex::from_u32(options),
1292         err_ctx_handle,
1293         debug_msg_address,
1294     )
1295 }
1296 
1297 #[cfg(feature = "component-model-async")]
1298 fn error_context_drop(
1299     store: &mut dyn VMStore,
1300     instance: Instance,
1301     _caller_instance: u32,
1302     ty: u32,
1303     err_ctx_handle: u32,
1304 ) -> Result<()> {
1305     instance.error_context_drop(
1306         store,
1307         TypeComponentLocalErrorContextTableIndex::from_u32(ty),
1308         err_ctx_handle,
1309     )
1310 }
1311 
1312 #[cfg(feature = "component-model-async")]
1313 fn context_get(
1314     store: &mut dyn VMStore,
1315     instance: Instance,
1316     _caller_instance: u32,
1317     slot: u32,
1318 ) -> Result<u32> {
1319     instance.context_get(store, slot)
1320 }
1321 
1322 #[cfg(feature = "component-model-async")]
1323 fn context_set(
1324     store: &mut dyn VMStore,
1325     instance: Instance,
1326     _caller_instance: u32,
1327     slot: u32,
1328     val: u32,
1329 ) -> Result<()> {
1330     instance.context_set(store, slot, val)
1331 }
1332 
1333 #[cfg(feature = "component-model-async")]
1334 fn thread_index(store: &mut dyn VMStore, instance: Instance) -> Result<u32> {
1335     instance.thread_index(store)
1336 }
1337 
1338 #[cfg(feature = "component-model-async")]
1339 fn thread_new_indirect(
1340     store: &mut dyn VMStore,
1341     instance: Instance,
1342     caller: u32,
1343     func_ty_id: u32,
1344     func_table_idx: u32,
1345     func_idx: u32,
1346     context: u32,
1347 ) -> Result<u32> {
1348     store.component_async_store().thread_new_indirect(
1349         instance,
1350         RuntimeComponentInstanceIndex::from_u32(caller),
1351         TypeFuncIndex::from_u32(func_ty_id),
1352         RuntimeTableIndex::from_u32(func_table_idx),
1353         func_idx,
1354         context as i32,
1355     )
1356 }
1357 
1358 #[cfg(feature = "component-model-async")]
1359 fn thread_suspend_to_suspended(
1360     store: &mut dyn VMStore,
1361     instance: Instance,
1362     caller: u32,
1363     cancellable: u8,
1364     thread_idx: u32,
1365 ) -> Result<bool> {
1366     instance
1367         .suspension_intrinsic(
1368             store,
1369             RuntimeComponentInstanceIndex::from_u32(caller),
1370             cancellable != 0,
1371             false,
1372             SuspensionTarget::SomeSuspended(thread_idx),
1373         )
1374         .map(|r| r == WaitResult::Cancelled)
1375 }
1376 
1377 #[cfg(feature = "component-model-async")]
1378 fn thread_suspend_to(
1379     store: &mut dyn VMStore,
1380     instance: Instance,
1381     caller: u32,
1382     cancellable: u8,
1383     thread_idx: u32,
1384 ) -> Result<bool> {
1385     instance
1386         .suspension_intrinsic(
1387             store,
1388             RuntimeComponentInstanceIndex::from_u32(caller),
1389             cancellable != 0,
1390             false,
1391             SuspensionTarget::Some(thread_idx),
1392         )
1393         .map(|r| r == WaitResult::Cancelled)
1394 }
1395 
1396 #[cfg(feature = "component-model-async")]
1397 fn thread_suspend(
1398     store: &mut dyn VMStore,
1399     instance: Instance,
1400     caller: u32,
1401     cancellable: u8,
1402 ) -> Result<bool> {
1403     instance
1404         .suspension_intrinsic(
1405             store,
1406             RuntimeComponentInstanceIndex::from_u32(caller),
1407             cancellable != 0,
1408             false,
1409             SuspensionTarget::None,
1410         )
1411         .map(|r| r == WaitResult::Cancelled)
1412 }
1413 
1414 #[cfg(feature = "component-model-async")]
1415 fn thread_unsuspend(
1416     store: &mut dyn VMStore,
1417     instance: Instance,
1418     caller_instance: u32,
1419     thread_idx: u32,
1420 ) -> Result<()> {
1421     instance.resume_thread(
1422         store,
1423         RuntimeComponentInstanceIndex::from_u32(caller_instance),
1424         thread_idx,
1425         false,
1426         false,
1427     )
1428 }
1429 
1430 #[cfg(feature = "component-model-async")]
1431 fn thread_yield_to_suspended(
1432     store: &mut dyn VMStore,
1433     instance: Instance,
1434     caller_instance: u32,
1435     cancellable: u8,
1436     thread_idx: u32,
1437 ) -> Result<bool> {
1438     instance
1439         .suspension_intrinsic(
1440             store,
1441             RuntimeComponentInstanceIndex::from_u32(caller_instance),
1442             cancellable != 0,
1443             true,
1444             SuspensionTarget::SomeSuspended(thread_idx),
1445         )
1446         .map(|r| r == WaitResult::Cancelled)
1447 }
1448