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