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