wasmtime/runtime/component/func/typed.rs
1use crate::component::Instance;
2use crate::component::func::{Func, LiftContext, LowerContext, Options};
3use crate::component::matching::InstanceType;
4use crate::component::storage::{storage_as_slice, storage_as_slice_mut};
5use crate::prelude::*;
6use crate::{AsContextMut, StoreContext, StoreContextMut, ValRaw};
7use alloc::borrow::Cow;
8use alloc::sync::Arc;
9use core::fmt;
10use core::iter;
11use core::marker;
12use core::mem::{self, MaybeUninit};
13use core::str;
14use wasmtime_environ::component::{
15 CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_PARAMS, MAX_FLAT_RESULTS,
16 StringEncoding, VariantInfo,
17};
18
19#[cfg(feature = "component-model-async")]
20use crate::component::concurrent::{self, PreparedCall};
21#[cfg(feature = "component-model-async")]
22use core::future::Future;
23#[cfg(feature = "component-model-async")]
24use core::pin::Pin;
25
26/// A statically-typed version of [`Func`] which takes `Params` as input and
27/// returns `Return`.
28///
29/// This is an efficient way to invoke a WebAssembly component where if the
30/// inputs and output are statically known this can eschew the vast majority of
31/// machinery and checks when calling WebAssembly. This is the most optimized
32/// way to call a WebAssembly component.
33///
34/// Note that like [`Func`] this is a pointer within a [`Store`](crate::Store)
35/// and usage will panic if used with the wrong store.
36///
37/// This type is primarily created with the [`Func::typed`] API.
38///
39/// See [`ComponentType`] for more information about supported types.
40pub struct TypedFunc<Params, Return> {
41 func: Func,
42
43 // The definition of this field is somewhat subtle and may be surprising.
44 // Naively one might expect something like
45 //
46 // _marker: marker::PhantomData<fn(Params) -> Return>,
47 //
48 // Since this is a function pointer after all. The problem with this
49 // definition though is that it imposes the wrong variance on `Params` from
50 // what we want. Abstractly a `fn(Params)` is able to store `Params` within
51 // it meaning you can only give it `Params` that live longer than the
52 // function pointer.
53 //
54 // With a component model function, however, we're always copying data from
55 // the host into the guest, so we are never storing pointers to `Params`
56 // into the guest outside the duration of a `call`, meaning we can actually
57 // accept values in `TypedFunc::call` which live for a shorter duration
58 // than the `Params` argument on the struct.
59 //
60 // This all means that we don't use a phantom function pointer, but instead
61 // feign phantom storage here to get the variance desired.
62 _marker: marker::PhantomData<(Params, Return)>,
63}
64
65impl<Params, Return> Copy for TypedFunc<Params, Return> {}
66
67impl<Params, Return> Clone for TypedFunc<Params, Return> {
68 fn clone(&self) -> TypedFunc<Params, Return> {
69 *self
70 }
71}
72
73impl<Params, Return> TypedFunc<Params, Return>
74where
75 Params: ComponentNamedList + Lower,
76 Return: ComponentNamedList + Lift,
77{
78 /// Creates a new [`TypedFunc`] from the provided component [`Func`],
79 /// unsafely asserting that the underlying function takes `Params` as
80 /// input and returns `Return`.
81 ///
82 /// # Unsafety
83 ///
84 /// This is an unsafe function because it does not verify that the [`Func`]
85 /// provided actually implements this signature. It's up to the caller to
86 /// have performed some other sort of check to ensure that the signature is
87 /// correct.
88 pub unsafe fn new_unchecked(func: Func) -> TypedFunc<Params, Return> {
89 TypedFunc {
90 _marker: marker::PhantomData,
91 func,
92 }
93 }
94
95 /// Returns the underlying un-typed [`Func`] that this [`TypedFunc`]
96 /// references.
97 pub fn func(&self) -> &Func {
98 &self.func
99 }
100
101 /// Calls the underlying WebAssembly component function using the provided
102 /// `params` as input.
103 ///
104 /// This method is used to enter into a component. Execution happens within
105 /// the `store` provided. The `params` are copied into WebAssembly memory
106 /// as appropriate and a core wasm function is invoked.
107 ///
108 /// # Post-return
109 ///
110 /// In the component model each function can have a "post return" specified
111 /// which allows cleaning up the arguments returned to the host. For example
112 /// if WebAssembly returns a string to the host then it might be a uniquely
113 /// allocated string which, after the host finishes processing it, needs to
114 /// be deallocated in the wasm instance's own linear memory to prevent
115 /// memory leaks in wasm itself. The `post-return` canonical abi option is
116 /// used to configured this.
117 ///
118 /// To accommodate this feature of the component model after invoking a
119 /// function via [`TypedFunc::call`] you must next invoke
120 /// [`TypedFunc::post_return`]. Note that the return value of the function
121 /// should be processed between these two function calls. The return value
122 /// continues to be usable from an embedder's perspective after
123 /// `post_return` is called, but after `post_return` is invoked it may no
124 /// longer retain the same value that the wasm module originally returned.
125 ///
126 /// Also note that [`TypedFunc::post_return`] must be invoked irrespective
127 /// of whether the canonical ABI option `post-return` was configured or not.
128 /// This means that embedders must unconditionally call
129 /// [`TypedFunc::post_return`] when a function returns. If this function
130 /// call returns an error, however, then [`TypedFunc::post_return`] is not
131 /// required.
132 ///
133 /// # Errors
134 ///
135 /// This function can return an error for a number of reasons:
136 ///
137 /// * If the wasm itself traps during execution.
138 /// * If the wasm traps while copying arguments into memory.
139 /// * If the wasm provides bad allocation pointers when copying arguments
140 /// into memory.
141 /// * If the wasm returns a value which violates the canonical ABI.
142 /// * If this function's instances cannot be entered, for example if the
143 /// instance is currently calling a host function.
144 /// * If a previous function call occurred and the corresponding
145 /// `post_return` hasn't been invoked yet.
146 ///
147 /// In general there are many ways that things could go wrong when copying
148 /// types in and out of a wasm module with the canonical ABI, and certain
149 /// error conditions are specific to certain types. For example a
150 /// WebAssembly module can't return an invalid `char`. When allocating space
151 /// for this host to copy a string into the returned pointer must be
152 /// in-bounds in memory.
153 ///
154 /// If an error happens then the error should contain detailed enough
155 /// information to understand which part of the canonical ABI went wrong
156 /// and what to inspect.
157 ///
158 /// # Panics
159 ///
160 /// Panics if this is called on a function in an asynchronous store. This
161 /// only works with functions defined within a synchronous store. Also
162 /// panics if `store` does not own this function.
163 pub fn call(&self, store: impl AsContextMut, params: Params) -> Result<Return> {
164 assert!(
165 !store.as_context().async_support(),
166 "must use `call_async` when async support is enabled on the config"
167 );
168 self.call_impl(store, params)
169 }
170
171 /// Exactly like [`Self::call`], except for use on asynchronous stores.
172 ///
173 /// # Panics
174 ///
175 /// Panics if this is called on a function in a synchronous store. This
176 /// only works with functions defined within an asynchronous store. Also
177 /// panics if `store` does not own this function.
178 #[cfg(feature = "async")]
179 pub async fn call_async(
180 &self,
181 mut store: impl AsContextMut<Data: Send>,
182 params: Params,
183 ) -> Result<Return>
184 where
185 Return: 'static,
186 {
187 let mut store = store.as_context_mut();
188 assert!(
189 store.0.async_support(),
190 "cannot use `call_async` when async support is not enabled on the config"
191 );
192 #[cfg(feature = "component-model-async")]
193 {
194 use crate::component::concurrent::TaskId;
195 use crate::runtime::vm::SendSyncPtr;
196 use core::ptr::NonNull;
197
198 let ptr = SendSyncPtr::from(NonNull::from(¶ms).cast::<u8>());
199 let prepared =
200 self.prepare_call(store.as_context_mut(), false, false, move |cx, ty, dst| {
201 // SAFETY: The goal here is to get `Params`, a non-`'static`
202 // value, to live long enough to the lowering of the
203 // parameters. We're guaranteed that `Params` lives in the
204 // future of the outer function (we're in an `async fn`) so it'll
205 // stay alive as long as the future itself. That is distinct,
206 // for example, from the signature of `call_concurrent` below.
207 //
208 // Here a pointer to `Params` is smuggled to this location
209 // through a `SendSyncPtr<u8>` to thwart the `'static` check
210 // of rustc and the signature of `prepare_call`.
211 //
212 // Note the use of `RemoveOnDrop` in the code that follows
213 // this closure, which ensures that the task will be removed
214 // from the concurrent state to which it belongs when the
215 // containing `Future` is dropped, thereby ensuring that
216 // this closure will never be called if it hasn't already,
217 // meaning it will never see a dangling pointer.
218 let params = unsafe { ptr.cast::<Params>().as_ref() };
219 Self::lower_args(cx, ty, dst, params)
220 })?;
221
222 struct RemoveOnDrop<'a, T: 'static> {
223 store: StoreContextMut<'a, T>,
224 task: TaskId,
225 }
226
227 impl<'a, T> Drop for RemoveOnDrop<'a, T> {
228 fn drop(&mut self) {
229 self.task.remove(self.store.as_context_mut()).unwrap();
230 }
231 }
232
233 let mut wrapper = RemoveOnDrop {
234 store,
235 task: prepared.task_id(),
236 };
237
238 let result = concurrent::queue_call(wrapper.store.as_context_mut(), prepared)?;
239 self.func
240 .instance
241 .run(wrapper.store.as_context_mut(), result)
242 .await?
243 }
244 #[cfg(not(feature = "component-model-async"))]
245 {
246 store
247 .on_fiber(|store| self.call_impl(store, params))
248 .await?
249 }
250 }
251
252 /// Start a concurrent call to this function.
253 ///
254 /// Unlike [`Self::call`] and [`Self::call_async`] (both of which require
255 /// exclusive access to the store until the completion of the call), calls
256 /// made using this method may run concurrently with other calls to the same
257 /// instance. In addition, the runtime will call the `post-return` function
258 /// (if any) automatically when the guest task completes -- no need to
259 /// explicitly call `Func::post_return` afterward.
260 ///
261 /// Note that the `Future` returned by this method will panic if polled or
262 /// `.await`ed outside of the event loop of the component instance this
263 /// function belongs to; use `Instance::run`, `Instance::run_with`, or
264 /// `Instance::spawn` to poll it from within the event loop. See
265 /// [`Instance::run`] for examples.
266 //
267 // FIXME: this function should return `impl Future` but that forces
268 // capturing all type parameters in scope at this time. The future
269 // intentionally does not close over `store` but returning `impl Future`
270 // implicitly does so. In a future version of Rust maybe this limitation
271 // will be lifted? Maybe rust-lang/rust#130043. Unsure.
272 #[cfg(feature = "component-model-async")]
273 pub fn call_concurrent(
274 self,
275 mut store: impl AsContextMut<Data: Send>,
276 params: Params,
277 ) -> Pin<Box<dyn Future<Output = Result<Return>> + Send>>
278 where
279 Params: 'static,
280 Return: 'static,
281 {
282 let mut store = store.as_context_mut();
283 assert!(
284 store.0.async_support(),
285 "cannot use `call_concurrent` when async support is not enabled on the config"
286 );
287
288 let result = (|| {
289 let prepared =
290 self.prepare_call(store.as_context_mut(), true, true, move |cx, ty, dst| {
291 Self::lower_args(cx, ty, dst, ¶ms)
292 })?;
293 concurrent::queue_call(store, prepared)
294 })();
295
296 Box::pin(async move { result?.await })
297 }
298
299 fn lower_args<T>(
300 cx: &mut LowerContext<T>,
301 ty: InterfaceType,
302 dst: &mut [MaybeUninit<ValRaw>],
303 params: &Params,
304 ) -> Result<()> {
305 use crate::component::storage::slice_to_storage_mut;
306
307 if Params::flatten_count() <= MAX_FLAT_PARAMS {
308 // SAFETY: the safety of `slice_to_storage_mut` relies on
309 // `Params::Lower` being represented by a sequence of
310 // `ValRaw`, and that's a guarantee upheld by the `Lower`
311 // trait itself.
312 let dst: &mut MaybeUninit<Params::Lower> = unsafe { slice_to_storage_mut(dst) };
313 Self::lower_stack_args(cx, ¶ms, ty, dst)
314 } else {
315 Self::lower_heap_args(cx, ¶ms, ty, &mut dst[0])
316 }
317 }
318
319 /// Calls `concurrent::prepare_call` with monomorphized functions for
320 /// lowering the parameters and lifting the result according to the number
321 /// of core Wasm parameters and results in the signature of the function to
322 /// be called.
323 #[cfg(feature = "component-model-async")]
324 fn prepare_call<T>(
325 self,
326 store: StoreContextMut<'_, T>,
327 remove_task_automatically: bool,
328 call_post_return_automatically: bool,
329 lower: impl FnOnce(
330 &mut LowerContext<T>,
331 InterfaceType,
332 &mut [MaybeUninit<ValRaw>],
333 ) -> Result<()>
334 + Send
335 + Sync
336 + 'static,
337 ) -> Result<PreparedCall<Return>>
338 where
339 Return: 'static,
340 {
341 use crate::component::storage::slice_to_storage;
342
343 let param_count = if Params::flatten_count() <= MAX_FLAT_PARAMS {
344 Params::flatten_count()
345 } else {
346 1
347 };
348 let max_results = if self.func.abi_async(store.0) {
349 MAX_FLAT_PARAMS
350 } else {
351 MAX_FLAT_RESULTS
352 };
353 concurrent::prepare_call(
354 store,
355 self.func,
356 param_count,
357 remove_task_automatically,
358 call_post_return_automatically,
359 move |func, store, params_out| {
360 func.with_lower_context(store, call_post_return_automatically, |cx, ty| {
361 lower(cx, ty, params_out)
362 })
363 },
364 move |func, store, results| {
365 let result = if Return::flatten_count() <= max_results {
366 func.with_lift_context(store, |cx, ty| {
367 // SAFETY: Per the safety requiments documented for the
368 // `ComponentType` trait, `Return::Lower` must be
369 // compatible at the binary level with a `[ValRaw; N]`,
370 // where `N` is `mem::size_of::<Return::Lower>() /
371 // mem::size_of::<ValRaw>()`. And since this function
372 // is only used when `Return::flatten_count() <=
373 // MAX_FLAT_RESULTS` and `MAX_FLAT_RESULTS == 1`, `N`
374 // can only either be 0 or 1.
375 //
376 // See `ComponentInstance::exit_call` for where we use
377 // the result count passed from
378 // `wasmtime_environ::fact::trampoline`-generated code
379 // to ensure the slice has the correct length, and also
380 // `concurrent::start_call` for where we conservatively
381 // use a slice length of 1 unconditionally. Also note
382 // that, as of this writing `slice_to_storage`
383 // double-checks the slice length is sufficient.
384 let results: &Return::Lower = unsafe { slice_to_storage(results) };
385 Self::lift_stack_result(cx, ty, results)
386 })?
387 } else {
388 func.with_lift_context(store, |cx, ty| {
389 Self::lift_heap_result(cx, ty, &results[0])
390 })?
391 };
392 Ok(Box::new(result))
393 },
394 )
395 }
396
397 fn call_impl(&self, mut store: impl AsContextMut, params: Params) -> Result<Return> {
398 let store = store.as_context_mut();
399
400 if self.func.abi_async(store.0) {
401 bail!("must enable the `component-model-async` feature to call async-lifted exports")
402 }
403
404 // Note that this is in theory simpler than it might read at this time.
405 // Here we're doing a runtime dispatch on the `flatten_count` for the
406 // params/results to see whether they're inbounds. This creates 4 cases
407 // to handle. In reality this is a highly optimizable branch where LLVM
408 // will easily figure out that only one branch here is taken.
409 //
410 // Otherwise this current construction is done to ensure that the stack
411 // space reserved for the params/results is always of the appropriate
412 // size (as the params/results needed differ depending on the "flatten"
413 // count)
414 //
415 // SAFETY: the safety of these invocations of `call_raw` depends on the
416 // correctness of the ascription of the `LowerParams` and `LowerReturn`
417 // types on the `call_raw` function. That's upheld here through the
418 // safety requirements of `Lift` and `Lower` on `Params` and `Return` in
419 // combination with checking the various possible branches here and
420 // dispatching to appropriately typed functions.
421 unsafe {
422 // This type is used as `LowerParams` for `call_raw` which is either
423 // `Params::Lower` or `ValRaw` representing it's either on the stack
424 // or it's on the heap. This allocates 1 extra `ValRaw` on the stack
425 // if `Params` is empty and `Return` is also empty, but that's a
426 // reasonable enough price to pay for now given the current code
427 // organization.
428 #[derive(Copy, Clone)]
429 union Union<T: Copy, U: Copy> {
430 _a: T,
431 _b: U,
432 }
433
434 if Return::flatten_count() <= MAX_FLAT_RESULTS {
435 self.func.call_raw(
436 store,
437 |cx, ty, dst: &mut MaybeUninit<Union<Params::Lower, ValRaw>>| {
438 let dst = storage_as_slice_mut(dst);
439 Self::lower_args(cx, ty, dst, ¶ms)
440 },
441 Self::lift_stack_result,
442 )
443 } else {
444 self.func.call_raw(
445 store,
446 |cx, ty, dst: &mut MaybeUninit<Union<Params::Lower, ValRaw>>| {
447 let dst = storage_as_slice_mut(dst);
448 Self::lower_args(cx, ty, dst, ¶ms)
449 },
450 Self::lift_heap_result,
451 )
452 }
453 }
454 }
455
456 /// Lower parameters directly onto the stack specified by the `dst`
457 /// location.
458 ///
459 /// This is only valid to call when the "flatten count" is small enough, or
460 /// when the canonical ABI says arguments go through the stack rather than
461 /// the heap.
462 fn lower_stack_args<T>(
463 cx: &mut LowerContext<'_, T>,
464 params: &Params,
465 ty: InterfaceType,
466 dst: &mut MaybeUninit<Params::Lower>,
467 ) -> Result<()> {
468 assert!(Params::flatten_count() <= MAX_FLAT_PARAMS);
469 params.linear_lower_to_flat(cx, ty, dst)?;
470 Ok(())
471 }
472
473 /// Lower parameters onto a heap-allocated location.
474 ///
475 /// This is used when the stack space to be used for the arguments is above
476 /// the `MAX_FLAT_PARAMS` threshold. Here the wasm's `realloc` function is
477 /// invoked to allocate space and then parameters are stored at that heap
478 /// pointer location.
479 fn lower_heap_args<T>(
480 cx: &mut LowerContext<'_, T>,
481 params: &Params,
482 ty: InterfaceType,
483 dst: &mut MaybeUninit<ValRaw>,
484 ) -> Result<()> {
485 // Memory must exist via validation if the arguments are stored on the
486 // heap, so we can create a `MemoryMut` at this point. Afterwards
487 // `realloc` is used to allocate space for all the arguments and then
488 // they're all stored in linear memory.
489 //
490 // Note that `realloc` will bake in a check that the returned pointer is
491 // in-bounds.
492 let ptr = cx.realloc(0, 0, Params::ALIGN32, Params::SIZE32)?;
493 params.linear_lower_to_memory(cx, ty, ptr)?;
494
495 // Note that the pointer here is stored as a 64-bit integer. This allows
496 // this to work with either 32 or 64-bit memories. For a 32-bit memory
497 // it'll just ignore the upper 32 zero bits, and for 64-bit memories
498 // this'll have the full 64-bits. Note that for 32-bit memories the call
499 // to `realloc` above guarantees that the `ptr` is in-bounds meaning
500 // that we will know that the zero-extended upper bits of `ptr` are
501 // guaranteed to be zero.
502 //
503 // This comment about 64-bit integers is also referred to below with
504 // "WRITEPTR64".
505 dst.write(ValRaw::i64(ptr as i64));
506
507 Ok(())
508 }
509
510 /// Lift the result of a function directly from the stack result.
511 ///
512 /// This is only used when the result fits in the maximum number of stack
513 /// slots.
514 fn lift_stack_result(
515 cx: &mut LiftContext<'_>,
516 ty: InterfaceType,
517 dst: &Return::Lower,
518 ) -> Result<Return> {
519 Return::linear_lift_from_flat(cx, ty, dst)
520 }
521
522 /// Lift the result of a function where the result is stored indirectly on
523 /// the heap.
524 fn lift_heap_result(
525 cx: &mut LiftContext<'_>,
526 ty: InterfaceType,
527 dst: &ValRaw,
528 ) -> Result<Return> {
529 assert!(Return::flatten_count() > MAX_FLAT_RESULTS);
530 // FIXME(#4311): needs to read an i64 for memory64
531 let ptr = usize::try_from(dst.get_u32())?;
532 if ptr % usize::try_from(Return::ALIGN32)? != 0 {
533 bail!("return pointer not aligned");
534 }
535
536 let bytes = cx
537 .memory()
538 .get(ptr..)
539 .and_then(|b| b.get(..Return::SIZE32))
540 .ok_or_else(|| anyhow::anyhow!("pointer out of bounds of memory"))?;
541 Return::linear_lift_from_memory(cx, ty, bytes)
542 }
543
544 /// See [`Func::post_return`]
545 pub fn post_return(&self, store: impl AsContextMut) -> Result<()> {
546 self.func.post_return(store)
547 }
548
549 /// See [`Func::post_return_async`]
550 #[cfg(feature = "async")]
551 pub async fn post_return_async<T: Send>(
552 &self,
553 store: impl AsContextMut<Data = T>,
554 ) -> Result<()> {
555 self.func.post_return_async(store).await
556 }
557}
558
559/// A trait representing a static list of named types that can be passed to or
560/// returned from a [`TypedFunc`].
561///
562/// This trait is implemented for a number of tuple types and is not expected
563/// to be implemented externally. The contents of this trait are hidden as it's
564/// intended to be an implementation detail of Wasmtime. The contents of this
565/// trait are not covered by Wasmtime's stability guarantees.
566///
567/// For more information about this trait see [`Func::typed`] and
568/// [`TypedFunc`].
569//
570// Note that this is an `unsafe` trait, and the unsafety means that
571// implementations of this trait must be correct or otherwise [`TypedFunc`]
572// would not be memory safe. The main reason this is `unsafe` is the
573// `typecheck` function which must operate correctly relative to the `AsTuple`
574// interpretation of the implementor.
575pub unsafe trait ComponentNamedList: ComponentType {}
576
577/// A trait representing types which can be passed to and read from components
578/// with the canonical ABI.
579///
580/// This trait is implemented for Rust types which can be communicated to
581/// components. The [`Func::typed`] and [`TypedFunc`] Rust items are the main
582/// consumers of this trait.
583///
584/// Supported Rust types include:
585///
586/// | Component Model Type | Rust Type |
587/// |-----------------------------------|--------------------------------------|
588/// | `{s,u}{8,16,32,64}` | `{i,u}{8,16,32,64}` |
589/// | `f{32,64}` | `f{32,64}` |
590/// | `bool` | `bool` |
591/// | `char` | `char` |
592/// | `tuple<A, B>` | `(A, B)` |
593/// | `option<T>` | `Option<T>` |
594/// | `result` | `Result<(), ()>` |
595/// | `result<T>` | `Result<T, ()>` |
596/// | `result<_, E>` | `Result<(), E>` |
597/// | `result<T, E>` | `Result<T, E>` |
598/// | `string` | `String`, `&str`, or [`WasmStr`] |
599/// | `list<T>` | `Vec<T>`, `&[T]`, or [`WasmList`] |
600/// | `own<T>`, `borrow<T>` | [`Resource<T>`] or [`ResourceAny`] |
601/// | `record` | [`#[derive(ComponentType)]`][d-cm] |
602/// | `variant` | [`#[derive(ComponentType)]`][d-cm] |
603/// | `enum` | [`#[derive(ComponentType)]`][d-cm] |
604/// | `flags` | [`flags!`][f-m] |
605///
606/// [`Resource<T>`]: crate::component::Resource
607/// [`ResourceAny`]: crate::component::ResourceAny
608/// [d-cm]: macro@crate::component::ComponentType
609/// [f-m]: crate::component::flags
610///
611/// Rust standard library pointers such as `&T`, `Box<T>`, and `Arc<T>`
612/// additionally represent whatever type `T` represents in the component model.
613/// Note that types such as `record`, `variant`, `enum`, and `flags` are
614/// generated by the embedder at compile time. These macros derive
615/// implementation of this trait for custom types to map to custom types in the
616/// component model. Note that for `record`, `variant`, `enum`, and `flags`
617/// those types are often generated by the
618/// [`bindgen!`](crate::component::bindgen) macro from WIT definitions.
619///
620/// Types that implement [`ComponentType`] are used for `Params` and `Return`
621/// in [`TypedFunc`] and [`Func::typed`].
622///
623/// The contents of this trait are hidden as it's intended to be an
624/// implementation detail of Wasmtime. The contents of this trait are not
625/// covered by Wasmtime's stability guarantees.
626///
627/// # Safety
628///
629/// Note that this is an `unsafe` trait as `TypedFunc`'s safety heavily relies on
630/// the correctness of the implementations of this trait. Some ways in which this
631/// trait must be correct to be safe are:
632///
633/// * The `Lower` associated type must be a `ValRaw` sequence. It doesn't have to
634/// literally be `[ValRaw; N]` but when laid out in memory it must be adjacent
635/// `ValRaw` values and have a multiple of the size of `ValRaw` and the same
636/// alignment.
637///
638/// * The `lower` function must initialize the bits within `Lower` that are going
639/// to be read by the trampoline that's used to enter core wasm. A trampoline
640/// is passed `*mut Lower` and will read the canonical abi arguments in
641/// sequence, so all of the bits must be correctly initialized.
642///
643/// * The `size` and `align` functions must be correct for this value stored in
644/// the canonical ABI. The `Cursor<T>` iteration of these bytes rely on this
645/// for correctness as they otherwise eschew bounds-checking.
646///
647/// There are likely some other correctness issues which aren't documented as
648/// well, this isn't currently an exhaustive list. It suffices to say, though,
649/// that correctness bugs in this trait implementation are highly likely to
650/// lead to security bugs, which again leads to the `unsafe` in the trait.
651///
652/// Note that this trait specifically is not sealed because `bindgen!`-generated
653/// types must be able to implement this trait using a `#[derive]` macro. For
654/// users it's recommended to not implement this trait manually given the
655/// non-exhaustive list of safety requirements that must be upheld. This trait
656/// is implemented at your own risk if you do so.
657///
658/// # Send and Sync
659///
660/// While on the topic of safety it's worth discussing the `Send` and `Sync`
661/// bounds here as well. These bounds might naively seem like they shouldn't be
662/// required for all component types as they're host-level types not guest-level
663/// types persisted anywhere. Various subtleties lead to these bounds, however:
664///
665/// * Fibers require that all stack-local variables are `Send` and `Sync` for
666/// fibers themselves to be send/sync. Unfortunately we have no help from the
667/// compiler on this one so it's up to Wasmtime's discipline to maintain this.
668/// One instance of this is that return values are placed on the stack as
669/// they're lowered into guest memory. This lowering operation can involve
670/// malloc and context switches, so return values must be Send/Sync.
671///
672/// * In the implementation of component model async it's not uncommon for types
673/// to be "buffered" in the store temporarily. For example parameters might
674/// reside in a store temporarily while wasm has backpressure turned on.
675///
676/// Overall it's generally easiest to require `Send` and `Sync` for all
677/// component types. There additionally aren't known use case for non-`Send` or
678/// non-`Sync` types at this time.
679pub unsafe trait ComponentType: Send + Sync {
680 /// Representation of the "lowered" form of this component value.
681 ///
682 /// Lowerings lower into core wasm values which are represented by `ValRaw`.
683 /// This `Lower` type must be a list of `ValRaw` as either a literal array
684 /// or a struct where every field is a `ValRaw`. This must be `Copy` (as
685 /// `ValRaw` is `Copy`) and support all byte patterns. This being correct is
686 /// one reason why the trait is unsafe.
687 #[doc(hidden)]
688 type Lower: Copy;
689
690 /// The information about this type's canonical ABI (size/align/etc).
691 #[doc(hidden)]
692 const ABI: CanonicalAbiInfo;
693
694 #[doc(hidden)]
695 const SIZE32: usize = Self::ABI.size32 as usize;
696 #[doc(hidden)]
697 const ALIGN32: u32 = Self::ABI.align32;
698
699 #[doc(hidden)]
700 const IS_RUST_UNIT_TYPE: bool = false;
701
702 /// Returns the number of core wasm abi values will be used to represent
703 /// this type in its lowered form.
704 ///
705 /// This divides the size of `Self::Lower` by the size of `ValRaw`.
706 #[doc(hidden)]
707 fn flatten_count() -> usize {
708 assert!(mem::size_of::<Self::Lower>() % mem::size_of::<ValRaw>() == 0);
709 assert!(mem::align_of::<Self::Lower>() == mem::align_of::<ValRaw>());
710 mem::size_of::<Self::Lower>() / mem::size_of::<ValRaw>()
711 }
712
713 /// Performs a type-check to see whether this component value type matches
714 /// the interface type `ty` provided.
715 #[doc(hidden)]
716 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()>;
717}
718
719#[doc(hidden)]
720pub unsafe trait ComponentVariant: ComponentType {
721 const CASES: &'static [Option<CanonicalAbiInfo>];
722 const INFO: VariantInfo = VariantInfo::new_static(Self::CASES);
723 const PAYLOAD_OFFSET32: usize = Self::INFO.payload_offset32 as usize;
724}
725
726/// Host types which can be passed to WebAssembly components.
727///
728/// This trait is implemented for all types that can be passed to components
729/// either as parameters of component exports or returns of component imports.
730/// This trait represents the ability to convert from the native host
731/// representation to the canonical ABI.
732///
733/// Built-in types to Rust such as `Option<T>` implement this trait as
734/// appropriate. For a mapping of component model to Rust types see
735/// [`ComponentType`].
736///
737/// For user-defined types, for example `record` types mapped to Rust `struct`s,
738/// this crate additionally has
739/// [`#[derive(Lower)]`](macro@crate::component::Lower).
740///
741/// Note that like [`ComponentType`] the definition of this trait is intended to
742/// be an internal implementation detail of Wasmtime at this time. It's
743/// recommended to use the `#[derive(Lower)]` implementation instead.
744pub unsafe trait Lower: ComponentType {
745 /// Performs the "lower" function in the linear memory version of the
746 /// canonical ABI.
747 ///
748 /// This method will lower the current value into a component. The `lower`
749 /// function performs a "flat" lowering into the `dst` specified which is
750 /// allowed to be uninitialized entering this method but is guaranteed to be
751 /// fully initialized if the method returns `Ok(())`.
752 ///
753 /// The `cx` context provided is the context within which this lowering is
754 /// happening. This contains information such as canonical options specified
755 /// (e.g. string encodings, memories, etc), the store itself, along with
756 /// type information.
757 ///
758 /// The `ty` parameter is the destination type that is being lowered into.
759 /// For example this is the component's "view" of the type that is being
760 /// lowered. This is guaranteed to have passed a `typecheck` earlier.
761 ///
762 /// This will only be called if `typecheck` passes for `Op::Lower`.
763 #[doc(hidden)]
764 fn linear_lower_to_flat<T>(
765 &self,
766 cx: &mut LowerContext<'_, T>,
767 ty: InterfaceType,
768 dst: &mut MaybeUninit<Self::Lower>,
769 ) -> Result<()>;
770
771 /// Performs the "store" operation in the linear memory version of the
772 /// canonical ABI.
773 ///
774 /// This function will store `self` into the linear memory described by
775 /// `cx` at the `offset` provided.
776 ///
777 /// It is expected that `offset` is a valid offset in memory for
778 /// `Self::SIZE32` bytes. At this time that's not an unsafe contract as it's
779 /// always re-checked on all stores, but this is something that will need to
780 /// be improved in the future to remove extra bounds checks. For now this
781 /// function will panic if there's a bug and `offset` isn't valid within
782 /// memory.
783 ///
784 /// The `ty` type information passed here is the same as the type
785 /// information passed to `lower` above, and is the component's own view of
786 /// what the resulting type should be.
787 ///
788 /// This will only be called if `typecheck` passes for `Op::Lower`.
789 #[doc(hidden)]
790 fn linear_lower_to_memory<T>(
791 &self,
792 cx: &mut LowerContext<'_, T>,
793 ty: InterfaceType,
794 offset: usize,
795 ) -> Result<()>;
796
797 /// Provided method to lower a list of `Self` into memory.
798 ///
799 /// Requires that `offset` has already been checked for alignment and
800 /// validity in terms of being in-bounds, otherwise this may panic.
801 ///
802 /// This is primarily here to get overridden for implementations of integers
803 /// which can avoid some extra fluff and use a pattern that's more easily
804 /// optimizable by LLVM.
805 #[doc(hidden)]
806 fn linear_store_list_to_memory<T>(
807 cx: &mut LowerContext<'_, T>,
808 ty: InterfaceType,
809 mut offset: usize,
810 items: &[Self],
811 ) -> Result<()>
812 where
813 Self: Sized,
814 {
815 for item in items {
816 item.linear_lower_to_memory(cx, ty, offset)?;
817 offset += Self::SIZE32;
818 }
819 Ok(())
820 }
821}
822
823/// Host types which can be created from the canonical ABI.
824///
825/// This is the mirror of the [`Lower`] trait where it represents the capability
826/// of acquiring items from WebAssembly and passing them to the host.
827///
828/// Built-in types to Rust such as `Option<T>` implement this trait as
829/// appropriate. For a mapping of component model to Rust types see
830/// [`ComponentType`].
831///
832/// For user-defined types, for example `record` types mapped to Rust `struct`s,
833/// this crate additionally has
834/// [`#[derive(Lift)]`](macro@crate::component::Lift).
835///
836/// Note that like [`ComponentType`] the definition of this trait is intended to
837/// be an internal implementation detail of Wasmtime at this time. It's
838/// recommended to use the `#[derive(Lift)]` implementation instead.
839pub unsafe trait Lift: Sized + ComponentType {
840 /// Performs the "lift" operation in the linear memory version of the
841 /// canonical ABI.
842 ///
843 /// This function performs a "flat" lift operation from the `src` specified
844 /// which is a sequence of core wasm values. The lifting operation will
845 /// validate core wasm values and produce a `Self` on success.
846 ///
847 /// The `cx` provided contains contextual information such as the store
848 /// that's being loaded from, canonical options, and type information.
849 ///
850 /// The `ty` parameter is the origin component's specification for what the
851 /// type that is being lifted is. For example this is the record type or the
852 /// resource type that is being lifted.
853 ///
854 /// Note that this has a default implementation but if `typecheck` passes
855 /// for `Op::Lift` this needs to be overridden.
856 #[doc(hidden)]
857 fn linear_lift_from_flat(
858 cx: &mut LiftContext<'_>,
859 ty: InterfaceType,
860 src: &Self::Lower,
861 ) -> Result<Self>;
862
863 /// Performs the "load" operation in the linear memory version of the
864 /// canonical ABI.
865 ///
866 /// This will read the `bytes` provided, which are a sub-slice into the
867 /// linear memory described by `cx`. The `bytes` array provided is
868 /// guaranteed to be `Self::SIZE32` bytes large. All of memory is then also
869 /// available through `cx` for bounds-checks and such as necessary for
870 /// strings/lists.
871 ///
872 /// The `ty` argument is the type that's being loaded, as described by the
873 /// original component.
874 ///
875 /// Note that this has a default implementation but if `typecheck` passes
876 /// for `Op::Lift` this needs to be overridden.
877 #[doc(hidden)]
878 fn linear_lift_from_memory(
879 cx: &mut LiftContext<'_>,
880 ty: InterfaceType,
881 bytes: &[u8],
882 ) -> Result<Self>;
883
884 /// Converts `list` into a `Vec<T>`, used in `Lift for Vec<T>`.
885 #[doc(hidden)]
886 fn linear_lift_list_from_memory(
887 cx: &mut LiftContext<'_>,
888 list: &WasmList<Self>,
889 ) -> Result<Vec<Self>>
890 where
891 Self: Sized,
892 {
893 let mut dst = Vec::with_capacity(list.len);
894 Self::linear_lift_into_from_memory(cx, list, &mut dst)?;
895 Ok(dst)
896 }
897
898 /// Load no more than `max_count` items from `list` into `dst`.
899 ///
900 /// This is primarily here to get overridden for implementations of integers
901 /// which can avoid some extra fluff and use a pattern that's more easily
902 /// optimizable by LLVM.
903 #[doc(hidden)]
904 fn linear_lift_into_from_memory(
905 cx: &mut LiftContext<'_>,
906 list: &WasmList<Self>,
907 dst: &mut impl Extend<Self>,
908 ) -> Result<()>
909 where
910 Self: Sized,
911 {
912 for i in 0..list.len {
913 dst.extend(Some(list.get_from_store(cx, i).unwrap()?));
914 }
915 Ok(())
916 }
917}
918
919// Macro to help generate "forwarding implementations" of `ComponentType` to
920// another type, used for wrappers in Rust like `&T`, `Box<T>`, etc. Note that
921// these wrappers only implement lowering because lifting native Rust types
922// cannot be done.
923macro_rules! forward_type_impls {
924 ($(($($generics:tt)*) $a:ty => $b:ty,)*) => ($(
925 unsafe impl <$($generics)*> ComponentType for $a {
926 type Lower = <$b as ComponentType>::Lower;
927
928 const ABI: CanonicalAbiInfo = <$b as ComponentType>::ABI;
929
930 #[inline]
931 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
932 <$b as ComponentType>::typecheck(ty, types)
933 }
934 }
935 )*)
936}
937
938forward_type_impls! {
939 (T: ComponentType + ?Sized) &'_ T => T,
940 (T: ComponentType + ?Sized) Box<T> => T,
941 (T: ComponentType + ?Sized) alloc::sync::Arc<T> => T,
942 () String => str,
943 (T: ComponentType) Vec<T> => [T],
944}
945
946macro_rules! forward_lowers {
947 ($(($($generics:tt)*) $a:ty => $b:ty,)*) => ($(
948 unsafe impl <$($generics)*> Lower for $a {
949 fn linear_lower_to_flat<U>(
950 &self,
951 cx: &mut LowerContext<'_, U>,
952 ty: InterfaceType,
953 dst: &mut MaybeUninit<Self::Lower>,
954 ) -> Result<()> {
955 <$b as Lower>::linear_lower_to_flat(self, cx, ty, dst)
956 }
957
958 fn linear_lower_to_memory<U>(
959 &self,
960 cx: &mut LowerContext<'_, U>,
961 ty: InterfaceType,
962 offset: usize,
963 ) -> Result<()> {
964 <$b as Lower>::linear_lower_to_memory(self, cx, ty, offset)
965 }
966 }
967 )*)
968}
969
970forward_lowers! {
971 (T: Lower + ?Sized) &'_ T => T,
972 (T: Lower + ?Sized) Box<T> => T,
973 (T: Lower + ?Sized) alloc::sync::Arc<T> => T,
974 () String => str,
975 (T: Lower) Vec<T> => [T],
976}
977
978macro_rules! forward_string_lifts {
979 ($($a:ty,)*) => ($(
980 unsafe impl Lift for $a {
981 #[inline]
982 fn linear_lift_from_flat(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
983 Ok(<WasmStr as Lift>::linear_lift_from_flat(cx, ty, src)?.to_str_from_memory(cx.memory())?.into())
984 }
985
986 #[inline]
987 fn linear_lift_from_memory(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
988 Ok(<WasmStr as Lift>::linear_lift_from_memory(cx, ty, bytes)?.to_str_from_memory(cx.memory())?.into())
989 }
990 }
991 )*)
992}
993
994forward_string_lifts! {
995 Box<str>,
996 alloc::sync::Arc<str>,
997 String,
998}
999
1000macro_rules! forward_list_lifts {
1001 ($($a:ty,)*) => ($(
1002 unsafe impl <T: Lift> Lift for $a {
1003 fn linear_lift_from_flat(cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
1004 let list = <WasmList::<T> as Lift>::linear_lift_from_flat(cx, ty, src)?;
1005 Ok(T::linear_lift_list_from_memory(cx, &list)?.into())
1006 }
1007
1008 fn linear_lift_from_memory(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
1009 let list = <WasmList::<T> as Lift>::linear_lift_from_memory(cx, ty, bytes)?;
1010 Ok(T::linear_lift_list_from_memory(cx, &list)?.into())
1011 }
1012 }
1013 )*)
1014}
1015
1016forward_list_lifts! {
1017 Box<[T]>,
1018 alloc::sync::Arc<[T]>,
1019 Vec<T>,
1020}
1021
1022// Macro to help generate `ComponentType` implementations for primitive types
1023// such as integers, char, bool, etc.
1024macro_rules! integers {
1025 ($($primitive:ident = $ty:ident in $field:ident/$get:ident with abi:$abi:ident,)*) => ($(
1026 unsafe impl ComponentType for $primitive {
1027 type Lower = ValRaw;
1028
1029 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::$abi;
1030
1031 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1032 match ty {
1033 InterfaceType::$ty => Ok(()),
1034 other => bail!("expected `{}` found `{}`", desc(&InterfaceType::$ty), desc(other))
1035 }
1036 }
1037 }
1038
1039 unsafe impl Lower for $primitive {
1040 #[inline]
1041 #[allow(trivial_numeric_casts, reason = "macro-generated code")]
1042 fn linear_lower_to_flat<T>(
1043 &self,
1044 _cx: &mut LowerContext<'_, T>,
1045 ty: InterfaceType,
1046 dst: &mut MaybeUninit<Self::Lower>,
1047 ) -> Result<()> {
1048 debug_assert!(matches!(ty, InterfaceType::$ty));
1049 dst.write(ValRaw::$field(*self as $field));
1050 Ok(())
1051 }
1052
1053 #[inline]
1054 fn linear_lower_to_memory<T>(
1055 &self,
1056 cx: &mut LowerContext<'_, T>,
1057 ty: InterfaceType,
1058 offset: usize,
1059 ) -> Result<()> {
1060 debug_assert!(matches!(ty, InterfaceType::$ty));
1061 debug_assert!(offset % Self::SIZE32 == 0);
1062 *cx.get(offset) = self.to_le_bytes();
1063 Ok(())
1064 }
1065
1066 fn linear_store_list_to_memory<T>(
1067 cx: &mut LowerContext<'_, T>,
1068 ty: InterfaceType,
1069 offset: usize,
1070 items: &[Self],
1071 ) -> Result<()> {
1072 debug_assert!(matches!(ty, InterfaceType::$ty));
1073
1074 // Double-check that the CM alignment is at least the host's
1075 // alignment for this type which should be true for all
1076 // platforms.
1077 assert!((Self::ALIGN32 as usize) >= mem::align_of::<Self>());
1078
1079 // Slice `cx`'s memory to the window that we'll be modifying.
1080 // This should all have already been verified in terms of
1081 // alignment and sizing meaning that these assertions here are
1082 // not truly necessary but are instead double-checks.
1083 //
1084 // Note that we're casting a `[u8]` slice to `[Self]` with
1085 // `align_to_mut` which is not safe in general but is safe in
1086 // our specific case as all `u8` patterns are valid `Self`
1087 // patterns since `Self` is an integral type.
1088 let dst = &mut cx.as_slice_mut()[offset..][..items.len() * Self::SIZE32];
1089 let (before, middle, end) = unsafe { dst.align_to_mut::<Self>() };
1090 assert!(before.is_empty() && end.is_empty());
1091 assert_eq!(middle.len(), items.len());
1092
1093 // And with all that out of the way perform the copying loop.
1094 // This is not a `copy_from_slice` because endianness needs to
1095 // be handled here, but LLVM should pretty easily transform this
1096 // into a memcpy on little-endian platforms.
1097 for (dst, src) in middle.iter_mut().zip(items) {
1098 *dst = src.to_le();
1099 }
1100 Ok(())
1101 }
1102 }
1103
1104 unsafe impl Lift for $primitive {
1105 #[inline]
1106 #[allow(
1107 trivial_numeric_casts,
1108 clippy::cast_possible_truncation,
1109 reason = "macro-generated code"
1110 )]
1111 fn linear_lift_from_flat(_cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
1112 debug_assert!(matches!(ty, InterfaceType::$ty));
1113 Ok(src.$get() as $primitive)
1114 }
1115
1116 #[inline]
1117 fn linear_lift_from_memory(_cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
1118 debug_assert!(matches!(ty, InterfaceType::$ty));
1119 debug_assert!((bytes.as_ptr() as usize) % Self::SIZE32 == 0);
1120 Ok($primitive::from_le_bytes(bytes.try_into().unwrap()))
1121 }
1122
1123 fn linear_lift_into_from_memory(
1124 cx: &mut LiftContext<'_>,
1125 list: &WasmList<Self>,
1126 dst: &mut impl Extend<Self>,
1127 ) -> Result<()>
1128 where
1129 Self: Sized,
1130 {
1131 dst.extend(list._as_le_slice(cx.memory())
1132 .iter()
1133 .map(|i| Self::from_le(*i)));
1134 Ok(())
1135 }
1136 }
1137 )*)
1138}
1139
1140integers! {
1141 i8 = S8 in i32/get_i32 with abi:SCALAR1,
1142 u8 = U8 in u32/get_u32 with abi:SCALAR1,
1143 i16 = S16 in i32/get_i32 with abi:SCALAR2,
1144 u16 = U16 in u32/get_u32 with abi:SCALAR2,
1145 i32 = S32 in i32/get_i32 with abi:SCALAR4,
1146 u32 = U32 in u32/get_u32 with abi:SCALAR4,
1147 i64 = S64 in i64/get_i64 with abi:SCALAR8,
1148 u64 = U64 in u64/get_u64 with abi:SCALAR8,
1149}
1150
1151macro_rules! floats {
1152 ($($float:ident/$get_float:ident = $ty:ident with abi:$abi:ident)*) => ($(const _: () = {
1153 unsafe impl ComponentType for $float {
1154 type Lower = ValRaw;
1155
1156 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::$abi;
1157
1158 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1159 match ty {
1160 InterfaceType::$ty => Ok(()),
1161 other => bail!("expected `{}` found `{}`", desc(&InterfaceType::$ty), desc(other))
1162 }
1163 }
1164 }
1165
1166 unsafe impl Lower for $float {
1167 #[inline]
1168 fn linear_lower_to_flat<T>(
1169 &self,
1170 _cx: &mut LowerContext<'_, T>,
1171 ty: InterfaceType,
1172 dst: &mut MaybeUninit<Self::Lower>,
1173 ) -> Result<()> {
1174 debug_assert!(matches!(ty, InterfaceType::$ty));
1175 dst.write(ValRaw::$float(self.to_bits()));
1176 Ok(())
1177 }
1178
1179 #[inline]
1180 fn linear_lower_to_memory<T>(
1181 &self,
1182 cx: &mut LowerContext<'_, T>,
1183 ty: InterfaceType,
1184 offset: usize,
1185 ) -> Result<()> {
1186 debug_assert!(matches!(ty, InterfaceType::$ty));
1187 debug_assert!(offset % Self::SIZE32 == 0);
1188 let ptr = cx.get(offset);
1189 *ptr = self.to_bits().to_le_bytes();
1190 Ok(())
1191 }
1192
1193 fn linear_store_list_to_memory<T>(
1194 cx: &mut LowerContext<'_, T>,
1195 ty: InterfaceType,
1196 offset: usize,
1197 items: &[Self],
1198 ) -> Result<()> {
1199 debug_assert!(matches!(ty, InterfaceType::$ty));
1200
1201 // Double-check that the CM alignment is at least the host's
1202 // alignment for this type which should be true for all
1203 // platforms.
1204 assert!((Self::ALIGN32 as usize) >= mem::align_of::<Self>());
1205
1206 // Slice `cx`'s memory to the window that we'll be modifying.
1207 // This should all have already been verified in terms of
1208 // alignment and sizing meaning that these assertions here are
1209 // not truly necessary but are instead double-checks.
1210 let dst = &mut cx.as_slice_mut()[offset..][..items.len() * Self::SIZE32];
1211 assert!(dst.as_ptr().cast::<Self>().is_aligned());
1212
1213 // And with all that out of the way perform the copying loop.
1214 // This is not a `copy_from_slice` because endianness needs to
1215 // be handled here, but LLVM should pretty easily transform this
1216 // into a memcpy on little-endian platforms.
1217 // TODO use `as_chunks` when https://github.com/rust-lang/rust/issues/74985
1218 // is stabilized
1219 for (dst, src) in iter::zip(dst.chunks_exact_mut(Self::SIZE32), items) {
1220 let dst: &mut [u8; Self::SIZE32] = dst.try_into().unwrap();
1221 *dst = src.to_le_bytes();
1222 }
1223 Ok(())
1224 }
1225 }
1226
1227 unsafe impl Lift for $float {
1228 #[inline]
1229 fn linear_lift_from_flat(_cx: &mut LiftContext<'_>, ty: InterfaceType, src: &Self::Lower) -> Result<Self> {
1230 debug_assert!(matches!(ty, InterfaceType::$ty));
1231 Ok($float::from_bits(src.$get_float()))
1232 }
1233
1234 #[inline]
1235 fn linear_lift_from_memory(_cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
1236 debug_assert!(matches!(ty, InterfaceType::$ty));
1237 debug_assert!((bytes.as_ptr() as usize) % Self::SIZE32 == 0);
1238 Ok($float::from_le_bytes(bytes.try_into().unwrap()))
1239 }
1240
1241 fn linear_lift_list_from_memory(cx: &mut LiftContext<'_>, list: &WasmList<Self>) -> Result<Vec<Self>> where Self: Sized {
1242 // See comments in `WasmList::get` for the panicking indexing
1243 let byte_size = list.len * mem::size_of::<Self>();
1244 let bytes = &cx.memory()[list.ptr..][..byte_size];
1245
1246 // The canonical ABI requires that everything is aligned to its
1247 // own size, so this should be an aligned array.
1248 assert!(bytes.as_ptr().cast::<Self>().is_aligned());
1249
1250 // Copy the resulting slice to a new Vec, handling endianness
1251 // in the process
1252 // TODO use `as_chunks` when https://github.com/rust-lang/rust/issues/74985
1253 // is stabilized
1254 Ok(
1255 bytes
1256 .chunks_exact(Self::SIZE32)
1257 .map(|i| $float::from_le_bytes(i.try_into().unwrap()))
1258 .collect()
1259 )
1260 }
1261 }
1262 };)*)
1263}
1264
1265floats! {
1266 f32/get_f32 = Float32 with abi:SCALAR4
1267 f64/get_f64 = Float64 with abi:SCALAR8
1268}
1269
1270unsafe impl ComponentType for bool {
1271 type Lower = ValRaw;
1272
1273 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR1;
1274
1275 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1276 match ty {
1277 InterfaceType::Bool => Ok(()),
1278 other => bail!("expected `bool` found `{}`", desc(other)),
1279 }
1280 }
1281}
1282
1283unsafe impl Lower for bool {
1284 fn linear_lower_to_flat<T>(
1285 &self,
1286 _cx: &mut LowerContext<'_, T>,
1287 ty: InterfaceType,
1288 dst: &mut MaybeUninit<Self::Lower>,
1289 ) -> Result<()> {
1290 debug_assert!(matches!(ty, InterfaceType::Bool));
1291 dst.write(ValRaw::i32(*self as i32));
1292 Ok(())
1293 }
1294
1295 fn linear_lower_to_memory<T>(
1296 &self,
1297 cx: &mut LowerContext<'_, T>,
1298 ty: InterfaceType,
1299 offset: usize,
1300 ) -> Result<()> {
1301 debug_assert!(matches!(ty, InterfaceType::Bool));
1302 debug_assert!(offset % Self::SIZE32 == 0);
1303 cx.get::<1>(offset)[0] = *self as u8;
1304 Ok(())
1305 }
1306}
1307
1308unsafe impl Lift for bool {
1309 #[inline]
1310 fn linear_lift_from_flat(
1311 _cx: &mut LiftContext<'_>,
1312 ty: InterfaceType,
1313 src: &Self::Lower,
1314 ) -> Result<Self> {
1315 debug_assert!(matches!(ty, InterfaceType::Bool));
1316 match src.get_i32() {
1317 0 => Ok(false),
1318 _ => Ok(true),
1319 }
1320 }
1321
1322 #[inline]
1323 fn linear_lift_from_memory(
1324 _cx: &mut LiftContext<'_>,
1325 ty: InterfaceType,
1326 bytes: &[u8],
1327 ) -> Result<Self> {
1328 debug_assert!(matches!(ty, InterfaceType::Bool));
1329 match bytes[0] {
1330 0 => Ok(false),
1331 _ => Ok(true),
1332 }
1333 }
1334}
1335
1336unsafe impl ComponentType for char {
1337 type Lower = ValRaw;
1338
1339 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::SCALAR4;
1340
1341 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1342 match ty {
1343 InterfaceType::Char => Ok(()),
1344 other => bail!("expected `char` found `{}`", desc(other)),
1345 }
1346 }
1347}
1348
1349unsafe impl Lower for char {
1350 #[inline]
1351 fn linear_lower_to_flat<T>(
1352 &self,
1353 _cx: &mut LowerContext<'_, T>,
1354 ty: InterfaceType,
1355 dst: &mut MaybeUninit<Self::Lower>,
1356 ) -> Result<()> {
1357 debug_assert!(matches!(ty, InterfaceType::Char));
1358 dst.write(ValRaw::u32(u32::from(*self)));
1359 Ok(())
1360 }
1361
1362 #[inline]
1363 fn linear_lower_to_memory<T>(
1364 &self,
1365 cx: &mut LowerContext<'_, T>,
1366 ty: InterfaceType,
1367 offset: usize,
1368 ) -> Result<()> {
1369 debug_assert!(matches!(ty, InterfaceType::Char));
1370 debug_assert!(offset % Self::SIZE32 == 0);
1371 *cx.get::<4>(offset) = u32::from(*self).to_le_bytes();
1372 Ok(())
1373 }
1374}
1375
1376unsafe impl Lift for char {
1377 #[inline]
1378 fn linear_lift_from_flat(
1379 _cx: &mut LiftContext<'_>,
1380 ty: InterfaceType,
1381 src: &Self::Lower,
1382 ) -> Result<Self> {
1383 debug_assert!(matches!(ty, InterfaceType::Char));
1384 Ok(char::try_from(src.get_u32())?)
1385 }
1386
1387 #[inline]
1388 fn linear_lift_from_memory(
1389 _cx: &mut LiftContext<'_>,
1390 ty: InterfaceType,
1391 bytes: &[u8],
1392 ) -> Result<Self> {
1393 debug_assert!(matches!(ty, InterfaceType::Char));
1394 debug_assert!((bytes.as_ptr() as usize) % Self::SIZE32 == 0);
1395 let bits = u32::from_le_bytes(bytes.try_into().unwrap());
1396 Ok(char::try_from(bits)?)
1397 }
1398}
1399
1400// FIXME(#4311): these probably need different constants for memory64
1401const UTF16_TAG: usize = 1 << 31;
1402const MAX_STRING_BYTE_LENGTH: usize = (1 << 31) - 1;
1403
1404// Note that this is similar to `ComponentType for WasmStr` except it can only
1405// be used for lowering, not lifting.
1406unsafe impl ComponentType for str {
1407 type Lower = [ValRaw; 2];
1408
1409 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
1410
1411 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1412 match ty {
1413 InterfaceType::String => Ok(()),
1414 other => bail!("expected `string` found `{}`", desc(other)),
1415 }
1416 }
1417}
1418
1419unsafe impl Lower for str {
1420 fn linear_lower_to_flat<T>(
1421 &self,
1422 cx: &mut LowerContext<'_, T>,
1423 ty: InterfaceType,
1424 dst: &mut MaybeUninit<[ValRaw; 2]>,
1425 ) -> Result<()> {
1426 debug_assert!(matches!(ty, InterfaceType::String));
1427 let (ptr, len) = lower_string(cx, self)?;
1428 // See "WRITEPTR64" above for why this is always storing a 64-bit
1429 // integer.
1430 map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64));
1431 map_maybe_uninit!(dst[1]).write(ValRaw::i64(len as i64));
1432 Ok(())
1433 }
1434
1435 fn linear_lower_to_memory<T>(
1436 &self,
1437 cx: &mut LowerContext<'_, T>,
1438 ty: InterfaceType,
1439 offset: usize,
1440 ) -> Result<()> {
1441 debug_assert!(matches!(ty, InterfaceType::String));
1442 debug_assert!(offset % (Self::ALIGN32 as usize) == 0);
1443 let (ptr, len) = lower_string(cx, self)?;
1444 // FIXME(#4311): needs memory64 handling
1445 *cx.get(offset + 0) = u32::try_from(ptr).unwrap().to_le_bytes();
1446 *cx.get(offset + 4) = u32::try_from(len).unwrap().to_le_bytes();
1447 Ok(())
1448 }
1449}
1450
1451fn lower_string<T>(cx: &mut LowerContext<'_, T>, string: &str) -> Result<(usize, usize)> {
1452 // Note that in general the wasm module can't assume anything about what the
1453 // host strings are encoded as. Additionally hosts are allowed to have
1454 // differently-encoded strings at runtime. Finally when copying a string
1455 // into wasm it's somewhat strict in the sense that the various patterns of
1456 // allocation and such are already dictated for us.
1457 //
1458 // In general what this means is that when copying a string from the host
1459 // into the destination we need to follow one of the cases of copying into
1460 // WebAssembly. It doesn't particularly matter which case as long as it ends
1461 // up in the right encoding. For example a destination encoding of
1462 // latin1+utf16 has a number of ways to get copied into and we do something
1463 // here that isn't the default "utf8 to latin1+utf16" since we have access
1464 // to simd-accelerated helpers in the `encoding_rs` crate. This is ok though
1465 // because we can fake that the host string was already stored in latin1
1466 // format and follow that copy pattern instead.
1467 match cx.options.string_encoding() {
1468 // This corresponds to `store_string_copy` in the canonical ABI where
1469 // the host's representation is utf-8 and the wasm module wants utf-8 so
1470 // a copy is all that's needed (and the `realloc` can be precise for the
1471 // initial memory allocation).
1472 StringEncoding::Utf8 => {
1473 if string.len() > MAX_STRING_BYTE_LENGTH {
1474 bail!(
1475 "string length of {} too large to copy into wasm",
1476 string.len()
1477 );
1478 }
1479 let ptr = cx.realloc(0, 0, 1, string.len())?;
1480 cx.as_slice_mut()[ptr..][..string.len()].copy_from_slice(string.as_bytes());
1481 Ok((ptr, string.len()))
1482 }
1483
1484 // This corresponds to `store_utf8_to_utf16` in the canonical ABI. Here
1485 // an over-large allocation is performed and then shrunk afterwards if
1486 // necessary.
1487 StringEncoding::Utf16 => {
1488 let size = string.len() * 2;
1489 if size > MAX_STRING_BYTE_LENGTH {
1490 bail!(
1491 "string length of {} too large to copy into wasm",
1492 string.len()
1493 );
1494 }
1495 let mut ptr = cx.realloc(0, 0, 2, size)?;
1496 let mut copied = 0;
1497 let bytes = &mut cx.as_slice_mut()[ptr..][..size];
1498 for (u, bytes) in string.encode_utf16().zip(bytes.chunks_mut(2)) {
1499 let u_bytes = u.to_le_bytes();
1500 bytes[0] = u_bytes[0];
1501 bytes[1] = u_bytes[1];
1502 copied += 1;
1503 }
1504 if (copied * 2) < size {
1505 ptr = cx.realloc(ptr, size, 2, copied * 2)?;
1506 }
1507 Ok((ptr, copied))
1508 }
1509
1510 StringEncoding::CompactUtf16 => {
1511 // This corresponds to `store_string_to_latin1_or_utf16`
1512 let bytes = string.as_bytes();
1513 let mut iter = string.char_indices();
1514 let mut ptr = cx.realloc(0, 0, 2, bytes.len())?;
1515 let mut dst = &mut cx.as_slice_mut()[ptr..][..bytes.len()];
1516 let mut result = 0;
1517 while let Some((i, ch)) = iter.next() {
1518 // Test if this `char` fits into the latin1 encoding.
1519 if let Ok(byte) = u8::try_from(u32::from(ch)) {
1520 dst[result] = byte;
1521 result += 1;
1522 continue;
1523 }
1524
1525 // .. if utf16 is forced to be used then the allocation is
1526 // bumped up to the maximum size.
1527 let worst_case = bytes
1528 .len()
1529 .checked_mul(2)
1530 .ok_or_else(|| anyhow!("byte length overflow"))?;
1531 if worst_case > MAX_STRING_BYTE_LENGTH {
1532 bail!("byte length too large");
1533 }
1534 ptr = cx.realloc(ptr, bytes.len(), 2, worst_case)?;
1535 dst = &mut cx.as_slice_mut()[ptr..][..worst_case];
1536
1537 // Previously encoded latin1 bytes are inflated to their 16-bit
1538 // size for utf16
1539 for i in (0..result).rev() {
1540 dst[2 * i] = dst[i];
1541 dst[2 * i + 1] = 0;
1542 }
1543
1544 // and then the remainder of the string is encoded.
1545 for (u, bytes) in string[i..]
1546 .encode_utf16()
1547 .zip(dst[2 * result..].chunks_mut(2))
1548 {
1549 let u_bytes = u.to_le_bytes();
1550 bytes[0] = u_bytes[0];
1551 bytes[1] = u_bytes[1];
1552 result += 1;
1553 }
1554 if worst_case > 2 * result {
1555 ptr = cx.realloc(ptr, worst_case, 2, 2 * result)?;
1556 }
1557 return Ok((ptr, result | UTF16_TAG));
1558 }
1559 if result < bytes.len() {
1560 ptr = cx.realloc(ptr, bytes.len(), 2, result)?;
1561 }
1562 Ok((ptr, result))
1563 }
1564 }
1565}
1566
1567/// Representation of a string located in linear memory in a WebAssembly
1568/// instance.
1569///
1570/// This type can be used in place of `String` and `str` for string-taking APIs
1571/// in some situations. The purpose of this type is to represent a range of
1572/// validated bytes within a component but does not actually copy the bytes. The
1573/// primary method, [`WasmStr::to_str`], attempts to return a reference to the
1574/// string directly located in the component's memory, avoiding a copy into the
1575/// host if possible.
1576///
1577/// The downside of this type, however, is that accessing a string requires a
1578/// [`Store`](crate::Store) pointer (via [`StoreContext`]). Bindings generated
1579/// by [`bindgen!`](crate::component::bindgen), for example, do not have access
1580/// to [`StoreContext`] and thus can't use this type.
1581///
1582/// This is intended for more advanced use cases such as defining functions
1583/// directly in a [`Linker`](crate::component::Linker). It's expected that in
1584/// the future [`bindgen!`](crate::component::bindgen) will also have a way to
1585/// use this type.
1586///
1587/// This type is used with [`TypedFunc`], for example, when WebAssembly returns
1588/// a string. This type cannot be used to give a string to WebAssembly, instead
1589/// `&str` should be used for that (since it's coming from the host).
1590///
1591/// Note that this type represents an in-bounds string in linear memory, but it
1592/// does not represent a valid string (e.g. valid utf-8). Validation happens
1593/// when [`WasmStr::to_str`] is called.
1594///
1595/// Also note that this type does not implement [`Lower`], it only implements
1596/// [`Lift`].
1597pub struct WasmStr {
1598 ptr: usize,
1599 len: usize,
1600 options: Options,
1601}
1602
1603impl WasmStr {
1604 pub(crate) fn new(ptr: usize, len: usize, cx: &mut LiftContext<'_>) -> Result<WasmStr> {
1605 let byte_len = match cx.options.string_encoding() {
1606 StringEncoding::Utf8 => Some(len),
1607 StringEncoding::Utf16 => len.checked_mul(2),
1608 StringEncoding::CompactUtf16 => {
1609 if len & UTF16_TAG == 0 {
1610 Some(len)
1611 } else {
1612 (len ^ UTF16_TAG).checked_mul(2)
1613 }
1614 }
1615 };
1616 match byte_len.and_then(|len| ptr.checked_add(len)) {
1617 Some(n) if n <= cx.memory().len() => {}
1618 _ => bail!("string pointer/length out of bounds of memory"),
1619 }
1620 Ok(WasmStr {
1621 ptr,
1622 len,
1623 options: *cx.options,
1624 })
1625 }
1626
1627 /// Returns the underlying string that this cursor points to.
1628 ///
1629 /// Note that this will internally decode the string from the wasm's
1630 /// encoding to utf-8 and additionally perform validation.
1631 ///
1632 /// The `store` provided must be the store where this string lives to
1633 /// access the correct memory.
1634 ///
1635 /// # Errors
1636 ///
1637 /// Returns an error if the string wasn't encoded correctly (e.g. invalid
1638 /// utf-8).
1639 ///
1640 /// # Panics
1641 ///
1642 /// Panics if this string is not owned by `store`.
1643 //
1644 // TODO: should add accessors for specifically utf-8 and utf-16 that perhaps
1645 // in an opt-in basis don't do validation. Additionally there should be some
1646 // method that returns `[u16]` after validating to avoid the utf16-to-utf8
1647 // transcode.
1648 pub fn to_str<'a, T: 'static>(
1649 &self,
1650 store: impl Into<StoreContext<'a, T>>,
1651 ) -> Result<Cow<'a, str>> {
1652 let store = store.into().0;
1653 let memory = self.options.memory(store);
1654 self.to_str_from_memory(memory)
1655 }
1656
1657 pub(crate) fn to_str_from_memory<'a>(&self, memory: &'a [u8]) -> Result<Cow<'a, str>> {
1658 match self.options.string_encoding() {
1659 StringEncoding::Utf8 => self.decode_utf8(memory),
1660 StringEncoding::Utf16 => self.decode_utf16(memory, self.len),
1661 StringEncoding::CompactUtf16 => {
1662 if self.len & UTF16_TAG == 0 {
1663 self.decode_latin1(memory)
1664 } else {
1665 self.decode_utf16(memory, self.len ^ UTF16_TAG)
1666 }
1667 }
1668 }
1669 }
1670
1671 fn decode_utf8<'a>(&self, memory: &'a [u8]) -> Result<Cow<'a, str>> {
1672 // Note that bounds-checking already happen in construction of `WasmStr`
1673 // so this is never expected to panic. This could theoretically be
1674 // unchecked indexing if we're feeling wild enough.
1675 Ok(str::from_utf8(&memory[self.ptr..][..self.len])?.into())
1676 }
1677
1678 fn decode_utf16<'a>(&self, memory: &'a [u8], len: usize) -> Result<Cow<'a, str>> {
1679 // See notes in `decode_utf8` for why this is panicking indexing.
1680 let memory = &memory[self.ptr..][..len * 2];
1681 Ok(core::char::decode_utf16(
1682 memory
1683 .chunks(2)
1684 .map(|chunk| u16::from_le_bytes(chunk.try_into().unwrap())),
1685 )
1686 .collect::<Result<String, _>>()?
1687 .into())
1688 }
1689
1690 fn decode_latin1<'a>(&self, memory: &'a [u8]) -> Result<Cow<'a, str>> {
1691 // See notes in `decode_utf8` for why this is panicking indexing.
1692 Ok(encoding_rs::mem::decode_latin1(
1693 &memory[self.ptr..][..self.len],
1694 ))
1695 }
1696}
1697
1698// Note that this is similar to `ComponentType for str` except it can only be
1699// used for lifting, not lowering.
1700unsafe impl ComponentType for WasmStr {
1701 type Lower = <str as ComponentType>::Lower;
1702
1703 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
1704
1705 fn typecheck(ty: &InterfaceType, _types: &InstanceType<'_>) -> Result<()> {
1706 match ty {
1707 InterfaceType::String => Ok(()),
1708 other => bail!("expected `string` found `{}`", desc(other)),
1709 }
1710 }
1711}
1712
1713unsafe impl Lift for WasmStr {
1714 #[inline]
1715 fn linear_lift_from_flat(
1716 cx: &mut LiftContext<'_>,
1717 ty: InterfaceType,
1718 src: &Self::Lower,
1719 ) -> Result<Self> {
1720 debug_assert!(matches!(ty, InterfaceType::String));
1721 // FIXME(#4311): needs memory64 treatment
1722 let ptr = src[0].get_u32();
1723 let len = src[1].get_u32();
1724 let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?);
1725 WasmStr::new(ptr, len, cx)
1726 }
1727
1728 #[inline]
1729 fn linear_lift_from_memory(
1730 cx: &mut LiftContext<'_>,
1731 ty: InterfaceType,
1732 bytes: &[u8],
1733 ) -> Result<Self> {
1734 debug_assert!(matches!(ty, InterfaceType::String));
1735 debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
1736 // FIXME(#4311): needs memory64 treatment
1737 let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap());
1738 let len = u32::from_le_bytes(bytes[4..].try_into().unwrap());
1739 let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?);
1740 WasmStr::new(ptr, len, cx)
1741 }
1742}
1743
1744unsafe impl<T> ComponentType for [T]
1745where
1746 T: ComponentType,
1747{
1748 type Lower = [ValRaw; 2];
1749
1750 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
1751
1752 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
1753 match ty {
1754 InterfaceType::List(t) => T::typecheck(&types.types[*t].element, types),
1755 other => bail!("expected `list` found `{}`", desc(other)),
1756 }
1757 }
1758}
1759
1760unsafe impl<T> Lower for [T]
1761where
1762 T: Lower,
1763{
1764 fn linear_lower_to_flat<U>(
1765 &self,
1766 cx: &mut LowerContext<'_, U>,
1767 ty: InterfaceType,
1768 dst: &mut MaybeUninit<[ValRaw; 2]>,
1769 ) -> Result<()> {
1770 let elem = match ty {
1771 InterfaceType::List(i) => cx.types[i].element,
1772 _ => bad_type_info(),
1773 };
1774 let (ptr, len) = lower_list(cx, elem, self)?;
1775 // See "WRITEPTR64" above for why this is always storing a 64-bit
1776 // integer.
1777 map_maybe_uninit!(dst[0]).write(ValRaw::i64(ptr as i64));
1778 map_maybe_uninit!(dst[1]).write(ValRaw::i64(len as i64));
1779 Ok(())
1780 }
1781
1782 fn linear_lower_to_memory<U>(
1783 &self,
1784 cx: &mut LowerContext<'_, U>,
1785 ty: InterfaceType,
1786 offset: usize,
1787 ) -> Result<()> {
1788 let elem = match ty {
1789 InterfaceType::List(i) => cx.types[i].element,
1790 _ => bad_type_info(),
1791 };
1792 debug_assert!(offset % (Self::ALIGN32 as usize) == 0);
1793 let (ptr, len) = lower_list(cx, elem, self)?;
1794 *cx.get(offset + 0) = u32::try_from(ptr).unwrap().to_le_bytes();
1795 *cx.get(offset + 4) = u32::try_from(len).unwrap().to_le_bytes();
1796 Ok(())
1797 }
1798}
1799
1800// FIXME: this is not a memcpy for `T` where `T` is something like `u8`.
1801//
1802// Some attempts to fix this have proved not fruitful. In isolation an attempt
1803// was made where:
1804//
1805// * `MemoryMut` stored a `*mut [u8]` as its "last view" of memory to avoid
1806// reloading the base pointer constantly. This view is reset on `realloc`.
1807// * The bounds-checks in `MemoryMut::get` were removed (replaced with unsafe
1808// indexing)
1809//
1810// Even then though this didn't correctly vectorized for `Vec<u8>`. It's not
1811// entirely clear why but it appeared that it's related to reloading the base
1812// pointer to memory (I guess from `MemoryMut` itself?). Overall I'm not really
1813// clear on what's happening there, but this is surely going to be a performance
1814// bottleneck in the future.
1815fn lower_list<T, U>(
1816 cx: &mut LowerContext<'_, U>,
1817 ty: InterfaceType,
1818 list: &[T],
1819) -> Result<(usize, usize)>
1820where
1821 T: Lower,
1822{
1823 let elem_size = T::SIZE32;
1824 let size = list
1825 .len()
1826 .checked_mul(elem_size)
1827 .ok_or_else(|| anyhow!("size overflow copying a list"))?;
1828 let ptr = cx.realloc(0, 0, T::ALIGN32, size)?;
1829 T::linear_store_list_to_memory(cx, ty, ptr, list)?;
1830 Ok((ptr, list.len()))
1831}
1832
1833/// Representation of a list of values that are owned by a WebAssembly instance.
1834///
1835/// For some more commentary about the rationale for this type see the
1836/// documentation of [`WasmStr`]. In summary this type can avoid a copy when
1837/// passing data to the host in some situations but is additionally more
1838/// cumbersome to use by requiring a [`Store`](crate::Store) to be provided.
1839///
1840/// This type is used whenever a `(list T)` is returned from a [`TypedFunc`],
1841/// for example. This type represents a list of values that are stored in linear
1842/// memory which are waiting to be read.
1843///
1844/// Note that this type represents only a valid range of bytes for the list
1845/// itself, it does not represent validity of the elements themselves and that's
1846/// performed when they're iterated.
1847///
1848/// Note that this type does not implement the [`Lower`] trait, only [`Lift`].
1849pub struct WasmList<T> {
1850 ptr: usize,
1851 len: usize,
1852 options: Options,
1853 elem: InterfaceType,
1854 // NB: it would probably be more efficient to store a non-atomic index-style
1855 // reference to something inside a `StoreOpaque`, but that's not easily
1856 // available at this time, so it's left as a future exercise.
1857 types: Arc<ComponentTypes>,
1858 instance: Instance,
1859 _marker: marker::PhantomData<T>,
1860}
1861
1862impl<T: Lift> WasmList<T> {
1863 pub(crate) fn new(
1864 ptr: usize,
1865 len: usize,
1866 cx: &mut LiftContext<'_>,
1867 elem: InterfaceType,
1868 ) -> Result<WasmList<T>> {
1869 match len
1870 .checked_mul(T::SIZE32)
1871 .and_then(|len| ptr.checked_add(len))
1872 {
1873 Some(n) if n <= cx.memory().len() => {}
1874 _ => bail!("list pointer/length out of bounds of memory"),
1875 }
1876 if ptr % usize::try_from(T::ALIGN32)? != 0 {
1877 bail!("list pointer is not aligned")
1878 }
1879 Ok(WasmList {
1880 ptr,
1881 len,
1882 options: *cx.options,
1883 elem,
1884 types: cx.types.clone(),
1885 instance: cx.instance_handle(),
1886 _marker: marker::PhantomData,
1887 })
1888 }
1889
1890 /// Returns the item length of this vector
1891 #[inline]
1892 pub fn len(&self) -> usize {
1893 self.len
1894 }
1895
1896 /// Gets the `n`th element of this list.
1897 ///
1898 /// Returns `None` if `index` is out of bounds. Returns `Some(Err(..))` if
1899 /// the value couldn't be decoded (it was invalid). Returns `Some(Ok(..))`
1900 /// if the value is valid.
1901 ///
1902 /// # Panics
1903 ///
1904 /// This function will panic if the string did not originally come from the
1905 /// `store` specified.
1906 //
1907 // TODO: given that interface values are intended to be consumed in one go
1908 // should we even expose a random access iteration API? In theory all
1909 // consumers should be validating through the iterator.
1910 pub fn get(&self, mut store: impl AsContextMut, index: usize) -> Option<Result<T>> {
1911 let store = store.as_context_mut().0;
1912 self.options.store_id().assert_belongs_to(store.id());
1913 let mut cx = LiftContext::new(store, &self.options, &self.types, self.instance);
1914 self.get_from_store(&mut cx, index)
1915 }
1916
1917 fn get_from_store(&self, cx: &mut LiftContext<'_>, index: usize) -> Option<Result<T>> {
1918 if index >= self.len {
1919 return None;
1920 }
1921 // Note that this is using panicking indexing and this is expected to
1922 // never fail. The bounds-checking here happened during the construction
1923 // of the `WasmList` itself which means these should always be in-bounds
1924 // (and wasm memory can only grow). This could theoretically be
1925 // unchecked indexing if we're confident enough and it's actually a perf
1926 // issue one day.
1927 let bytes = &cx.memory()[self.ptr + index * T::SIZE32..][..T::SIZE32];
1928 Some(T::linear_lift_from_memory(cx, self.elem, bytes))
1929 }
1930
1931 /// Returns an iterator over the elements of this list.
1932 ///
1933 /// Each item of the list may fail to decode and is represented through the
1934 /// `Result` value of the iterator.
1935 pub fn iter<'a, U: 'static>(
1936 &'a self,
1937 store: impl Into<StoreContextMut<'a, U>>,
1938 ) -> impl ExactSizeIterator<Item = Result<T>> + 'a {
1939 let store = store.into().0;
1940 self.options.store_id().assert_belongs_to(store.id());
1941 let mut cx = LiftContext::new(store, &self.options, &self.types, self.instance);
1942 (0..self.len).map(move |i| self.get_from_store(&mut cx, i).unwrap())
1943 }
1944}
1945
1946macro_rules! raw_wasm_list_accessors {
1947 ($($i:ident)*) => ($(
1948 impl WasmList<$i> {
1949 /// Get access to the raw underlying memory for this list.
1950 ///
1951 /// This method will return a direct slice into the original wasm
1952 /// module's linear memory where the data for this slice is stored.
1953 /// This allows the embedder to have efficient access to the
1954 /// underlying memory if needed and avoid copies and such if
1955 /// desired.
1956 ///
1957 /// Note that multi-byte integers are stored in little-endian format
1958 /// so portable processing of this slice must be aware of the host's
1959 /// byte-endianness. The `from_le` constructors in the Rust standard
1960 /// library should be suitable for converting from little-endian.
1961 ///
1962 /// # Panics
1963 ///
1964 /// Panics if the `store` provided is not the one from which this
1965 /// slice originated.
1966 pub fn as_le_slice<'a, T: 'static>(&self, store: impl Into<StoreContext<'a, T>>) -> &'a [$i] {
1967 let memory = self.options.memory(store.into().0);
1968 self._as_le_slice(memory)
1969 }
1970
1971 fn _as_le_slice<'a>(&self, all_of_memory: &'a [u8]) -> &'a [$i] {
1972 // See comments in `WasmList::get` for the panicking indexing
1973 let byte_size = self.len * mem::size_of::<$i>();
1974 let bytes = &all_of_memory[self.ptr..][..byte_size];
1975
1976 // The canonical ABI requires that everything is aligned to its
1977 // own size, so this should be an aligned array. Furthermore the
1978 // alignment of primitive integers for hosts should be smaller
1979 // than or equal to the size of the primitive itself, meaning
1980 // that a wasm canonical-abi-aligned list is also aligned for
1981 // the host. That should mean that the head/tail slices here are
1982 // empty.
1983 //
1984 // Also note that the `unsafe` here is needed since the type
1985 // we're aligning to isn't guaranteed to be valid, but in our
1986 // case it's just integers and bytes so this should be safe.
1987 unsafe {
1988 let (head, body, tail) = bytes.align_to::<$i>();
1989 assert!(head.is_empty() && tail.is_empty());
1990 body
1991 }
1992 }
1993 }
1994 )*)
1995}
1996
1997raw_wasm_list_accessors! {
1998 i8 i16 i32 i64
1999 u8 u16 u32 u64
2000}
2001
2002// Note that this is similar to `ComponentType for str` except it can only be
2003// used for lifting, not lowering.
2004unsafe impl<T: ComponentType> ComponentType for WasmList<T> {
2005 type Lower = <[T] as ComponentType>::Lower;
2006
2007 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::POINTER_PAIR;
2008
2009 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
2010 <[T] as ComponentType>::typecheck(ty, types)
2011 }
2012}
2013
2014unsafe impl<T: Lift> Lift for WasmList<T> {
2015 fn linear_lift_from_flat(
2016 cx: &mut LiftContext<'_>,
2017 ty: InterfaceType,
2018 src: &Self::Lower,
2019 ) -> Result<Self> {
2020 let elem = match ty {
2021 InterfaceType::List(i) => cx.types[i].element,
2022 _ => bad_type_info(),
2023 };
2024 // FIXME(#4311): needs memory64 treatment
2025 let ptr = src[0].get_u32();
2026 let len = src[1].get_u32();
2027 let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?);
2028 WasmList::new(ptr, len, cx, elem)
2029 }
2030
2031 fn linear_lift_from_memory(
2032 cx: &mut LiftContext<'_>,
2033 ty: InterfaceType,
2034 bytes: &[u8],
2035 ) -> Result<Self> {
2036 let elem = match ty {
2037 InterfaceType::List(i) => cx.types[i].element,
2038 _ => bad_type_info(),
2039 };
2040 debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
2041 // FIXME(#4311): needs memory64 treatment
2042 let ptr = u32::from_le_bytes(bytes[..4].try_into().unwrap());
2043 let len = u32::from_le_bytes(bytes[4..].try_into().unwrap());
2044 let (ptr, len) = (usize::try_from(ptr)?, usize::try_from(len)?);
2045 WasmList::new(ptr, len, cx, elem)
2046 }
2047}
2048
2049/// Verify that the given wasm type is a tuple with the expected fields in the right order.
2050fn typecheck_tuple(
2051 ty: &InterfaceType,
2052 types: &InstanceType<'_>,
2053 expected: &[fn(&InterfaceType, &InstanceType<'_>) -> Result<()>],
2054) -> Result<()> {
2055 match ty {
2056 InterfaceType::Tuple(t) => {
2057 let tuple = &types.types[*t];
2058 if tuple.types.len() != expected.len() {
2059 bail!(
2060 "expected {}-tuple, found {}-tuple",
2061 expected.len(),
2062 tuple.types.len()
2063 );
2064 }
2065 for (ty, check) in tuple.types.iter().zip(expected) {
2066 check(ty, types)?;
2067 }
2068 Ok(())
2069 }
2070 other => bail!("expected `tuple` found `{}`", desc(other)),
2071 }
2072}
2073
2074/// Verify that the given wasm type is a record with the expected fields in the right order and with the right
2075/// names.
2076pub fn typecheck_record(
2077 ty: &InterfaceType,
2078 types: &InstanceType<'_>,
2079 expected: &[(&str, fn(&InterfaceType, &InstanceType<'_>) -> Result<()>)],
2080) -> Result<()> {
2081 match ty {
2082 InterfaceType::Record(index) => {
2083 let fields = &types.types[*index].fields;
2084
2085 if fields.len() != expected.len() {
2086 bail!(
2087 "expected record of {} fields, found {} fields",
2088 expected.len(),
2089 fields.len()
2090 );
2091 }
2092
2093 for (field, &(name, check)) in fields.iter().zip(expected) {
2094 check(&field.ty, types)
2095 .with_context(|| format!("type mismatch for field {name}"))?;
2096
2097 if field.name != name {
2098 bail!("expected record field named {}, found {}", name, field.name);
2099 }
2100 }
2101
2102 Ok(())
2103 }
2104 other => bail!("expected `record` found `{}`", desc(other)),
2105 }
2106}
2107
2108/// Verify that the given wasm type is a variant with the expected cases in the right order and with the right
2109/// names.
2110pub fn typecheck_variant(
2111 ty: &InterfaceType,
2112 types: &InstanceType<'_>,
2113 expected: &[(
2114 &str,
2115 Option<fn(&InterfaceType, &InstanceType<'_>) -> Result<()>>,
2116 )],
2117) -> Result<()> {
2118 match ty {
2119 InterfaceType::Variant(index) => {
2120 let cases = &types.types[*index].cases;
2121
2122 if cases.len() != expected.len() {
2123 bail!(
2124 "expected variant of {} cases, found {} cases",
2125 expected.len(),
2126 cases.len()
2127 );
2128 }
2129
2130 for ((case_name, case_ty), &(name, check)) in cases.iter().zip(expected) {
2131 if *case_name != name {
2132 bail!("expected variant case named {name}, found {case_name}");
2133 }
2134
2135 match (check, case_ty) {
2136 (Some(check), Some(ty)) => check(ty, types)
2137 .with_context(|| format!("type mismatch for case {name}"))?,
2138 (None, None) => {}
2139 (Some(_), None) => {
2140 bail!("case `{name}` has no type but one was expected")
2141 }
2142 (None, Some(_)) => {
2143 bail!("case `{name}` has a type but none was expected")
2144 }
2145 }
2146 }
2147
2148 Ok(())
2149 }
2150 other => bail!("expected `variant` found `{}`", desc(other)),
2151 }
2152}
2153
2154/// Verify that the given wasm type is a enum with the expected cases in the right order and with the right
2155/// names.
2156pub fn typecheck_enum(
2157 ty: &InterfaceType,
2158 types: &InstanceType<'_>,
2159 expected: &[&str],
2160) -> Result<()> {
2161 match ty {
2162 InterfaceType::Enum(index) => {
2163 let names = &types.types[*index].names;
2164
2165 if names.len() != expected.len() {
2166 bail!(
2167 "expected enum of {} names, found {} names",
2168 expected.len(),
2169 names.len()
2170 );
2171 }
2172
2173 for (name, expected) in names.iter().zip(expected) {
2174 if name != expected {
2175 bail!("expected enum case named {}, found {}", expected, name);
2176 }
2177 }
2178
2179 Ok(())
2180 }
2181 other => bail!("expected `enum` found `{}`", desc(other)),
2182 }
2183}
2184
2185/// Verify that the given wasm type is a flags type with the expected flags in the right order and with the right
2186/// names.
2187pub fn typecheck_flags(
2188 ty: &InterfaceType,
2189 types: &InstanceType<'_>,
2190 expected: &[&str],
2191) -> Result<()> {
2192 match ty {
2193 InterfaceType::Flags(index) => {
2194 let names = &types.types[*index].names;
2195
2196 if names.len() != expected.len() {
2197 bail!(
2198 "expected flags type with {} names, found {} names",
2199 expected.len(),
2200 names.len()
2201 );
2202 }
2203
2204 for (name, expected) in names.iter().zip(expected) {
2205 if name != expected {
2206 bail!("expected flag named {}, found {}", expected, name);
2207 }
2208 }
2209
2210 Ok(())
2211 }
2212 other => bail!("expected `flags` found `{}`", desc(other)),
2213 }
2214}
2215
2216/// Format the specified bitflags using the specified names for debugging
2217pub fn format_flags(bits: &[u32], names: &[&str], f: &mut fmt::Formatter) -> fmt::Result {
2218 f.write_str("(")?;
2219 let mut wrote = false;
2220 for (index, name) in names.iter().enumerate() {
2221 if ((bits[index / 32] >> (index % 32)) & 1) != 0 {
2222 if wrote {
2223 f.write_str("|")?;
2224 } else {
2225 wrote = true;
2226 }
2227
2228 f.write_str(name)?;
2229 }
2230 }
2231 f.write_str(")")
2232}
2233
2234unsafe impl<T> ComponentType for Option<T>
2235where
2236 T: ComponentType,
2237{
2238 type Lower = TupleLower<<u32 as ComponentType>::Lower, T::Lower>;
2239
2240 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[None, Some(T::ABI)]);
2241
2242 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
2243 match ty {
2244 InterfaceType::Option(t) => T::typecheck(&types.types[*t].ty, types),
2245 other => bail!("expected `option` found `{}`", desc(other)),
2246 }
2247 }
2248}
2249
2250unsafe impl<T> ComponentVariant for Option<T>
2251where
2252 T: ComponentType,
2253{
2254 const CASES: &'static [Option<CanonicalAbiInfo>] = &[None, Some(T::ABI)];
2255}
2256
2257unsafe impl<T> Lower for Option<T>
2258where
2259 T: Lower,
2260{
2261 fn linear_lower_to_flat<U>(
2262 &self,
2263 cx: &mut LowerContext<'_, U>,
2264 ty: InterfaceType,
2265 dst: &mut MaybeUninit<Self::Lower>,
2266 ) -> Result<()> {
2267 let payload = match ty {
2268 InterfaceType::Option(ty) => cx.types[ty].ty,
2269 _ => bad_type_info(),
2270 };
2271 match self {
2272 None => {
2273 map_maybe_uninit!(dst.A1).write(ValRaw::i32(0));
2274 // Note that this is unsafe as we're writing an arbitrary
2275 // bit-pattern to an arbitrary type, but part of the unsafe
2276 // contract of the `ComponentType` trait is that we can assign
2277 // any bit-pattern. By writing all zeros here we're ensuring
2278 // that the core wasm arguments this translates to will all be
2279 // zeros (as the canonical ABI requires).
2280 unsafe {
2281 map_maybe_uninit!(dst.A2).as_mut_ptr().write_bytes(0u8, 1);
2282 }
2283 }
2284 Some(val) => {
2285 map_maybe_uninit!(dst.A1).write(ValRaw::i32(1));
2286 val.linear_lower_to_flat(cx, payload, map_maybe_uninit!(dst.A2))?;
2287 }
2288 }
2289 Ok(())
2290 }
2291
2292 fn linear_lower_to_memory<U>(
2293 &self,
2294 cx: &mut LowerContext<'_, U>,
2295 ty: InterfaceType,
2296 offset: usize,
2297 ) -> Result<()> {
2298 debug_assert!(offset % (Self::ALIGN32 as usize) == 0);
2299 let payload = match ty {
2300 InterfaceType::Option(ty) => cx.types[ty].ty,
2301 _ => bad_type_info(),
2302 };
2303 match self {
2304 None => {
2305 cx.get::<1>(offset)[0] = 0;
2306 }
2307 Some(val) => {
2308 cx.get::<1>(offset)[0] = 1;
2309 val.linear_lower_to_memory(
2310 cx,
2311 payload,
2312 offset + (Self::INFO.payload_offset32 as usize),
2313 )?;
2314 }
2315 }
2316 Ok(())
2317 }
2318}
2319
2320unsafe impl<T> Lift for Option<T>
2321where
2322 T: Lift,
2323{
2324 fn linear_lift_from_flat(
2325 cx: &mut LiftContext<'_>,
2326 ty: InterfaceType,
2327 src: &Self::Lower,
2328 ) -> Result<Self> {
2329 let payload = match ty {
2330 InterfaceType::Option(ty) => cx.types[ty].ty,
2331 _ => bad_type_info(),
2332 };
2333 Ok(match src.A1.get_i32() {
2334 0 => None,
2335 1 => Some(T::linear_lift_from_flat(cx, payload, &src.A2)?),
2336 _ => bail!("invalid option discriminant"),
2337 })
2338 }
2339
2340 fn linear_lift_from_memory(
2341 cx: &mut LiftContext<'_>,
2342 ty: InterfaceType,
2343 bytes: &[u8],
2344 ) -> Result<Self> {
2345 debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
2346 let payload_ty = match ty {
2347 InterfaceType::Option(ty) => cx.types[ty].ty,
2348 _ => bad_type_info(),
2349 };
2350 let discrim = bytes[0];
2351 let payload = &bytes[Self::INFO.payload_offset32 as usize..];
2352 match discrim {
2353 0 => Ok(None),
2354 1 => Ok(Some(T::linear_lift_from_memory(cx, payload_ty, payload)?)),
2355 _ => bail!("invalid option discriminant"),
2356 }
2357 }
2358}
2359
2360#[derive(Clone, Copy)]
2361#[repr(C)]
2362pub struct ResultLower<T: Copy, E: Copy> {
2363 tag: ValRaw,
2364 payload: ResultLowerPayload<T, E>,
2365}
2366
2367#[derive(Clone, Copy)]
2368#[repr(C)]
2369union ResultLowerPayload<T: Copy, E: Copy> {
2370 ok: T,
2371 err: E,
2372}
2373
2374unsafe impl<T, E> ComponentType for Result<T, E>
2375where
2376 T: ComponentType,
2377 E: ComponentType,
2378{
2379 type Lower = ResultLower<T::Lower, E::Lower>;
2380
2381 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::variant_static(&[Some(T::ABI), Some(E::ABI)]);
2382
2383 fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> {
2384 match ty {
2385 InterfaceType::Result(r) => {
2386 let result = &types.types[*r];
2387 match &result.ok {
2388 Some(ty) => T::typecheck(ty, types)?,
2389 None if T::IS_RUST_UNIT_TYPE => {}
2390 None => bail!("expected no `ok` type"),
2391 }
2392 match &result.err {
2393 Some(ty) => E::typecheck(ty, types)?,
2394 None if E::IS_RUST_UNIT_TYPE => {}
2395 None => bail!("expected no `err` type"),
2396 }
2397 Ok(())
2398 }
2399 other => bail!("expected `result` found `{}`", desc(other)),
2400 }
2401 }
2402}
2403
2404/// Lowers the payload of a variant into the storage for the entire payload,
2405/// handling writing zeros at the end of the representation if this payload is
2406/// smaller than the entire flat representation.
2407///
2408/// * `payload` - the flat storage space for the entire payload of the variant
2409/// * `typed_payload` - projection from the payload storage space to the
2410/// individual storage space for this variant.
2411/// * `lower` - lowering operation used to initialize the `typed_payload` return
2412/// value.
2413///
2414/// For more information on this se the comments in the `Lower for Result`
2415/// implementation below.
2416pub unsafe fn lower_payload<P, T>(
2417 payload: &mut MaybeUninit<P>,
2418 typed_payload: impl FnOnce(&mut MaybeUninit<P>) -> &mut MaybeUninit<T>,
2419 lower: impl FnOnce(&mut MaybeUninit<T>) -> Result<()>,
2420) -> Result<()> {
2421 let typed = typed_payload(payload);
2422 lower(typed)?;
2423
2424 let typed_len = storage_as_slice(typed).len();
2425 let payload = storage_as_slice_mut(payload);
2426 for slot in payload[typed_len..].iter_mut() {
2427 slot.write(ValRaw::u64(0));
2428 }
2429 Ok(())
2430}
2431
2432unsafe impl<T, E> ComponentVariant for Result<T, E>
2433where
2434 T: ComponentType,
2435 E: ComponentType,
2436{
2437 const CASES: &'static [Option<CanonicalAbiInfo>] = &[Some(T::ABI), Some(E::ABI)];
2438}
2439
2440unsafe impl<T, E> Lower for Result<T, E>
2441where
2442 T: Lower,
2443 E: Lower,
2444{
2445 fn linear_lower_to_flat<U>(
2446 &self,
2447 cx: &mut LowerContext<'_, U>,
2448 ty: InterfaceType,
2449 dst: &mut MaybeUninit<Self::Lower>,
2450 ) -> Result<()> {
2451 let (ok, err) = match ty {
2452 InterfaceType::Result(ty) => {
2453 let ty = &cx.types[ty];
2454 (ty.ok, ty.err)
2455 }
2456 _ => bad_type_info(),
2457 };
2458
2459 // This implementation of `Lower::lower`, if you're reading these from
2460 // the top of this file, is the first location that the "join" logic of
2461 // the component model's canonical ABI encountered. The rough problem is
2462 // that let's say we have a component model type of the form:
2463 //
2464 // (result u64 (error (tuple f32 u16)))
2465 //
2466 // The flat representation of this is actually pretty tricky. Currently
2467 // it is:
2468 //
2469 // i32 i64 i32
2470 //
2471 // The first `i32` is the discriminant for the `result`, and the payload
2472 // is represented by `i64 i32`. The "ok" variant will only use the `i64`
2473 // and the "err" variant will use both `i64` and `i32`.
2474 //
2475 // In the "ok" variant the first issue is encountered. The size of one
2476 // variant may not match the size of the other variants. All variants
2477 // start at the "front" but when lowering a type we need to be sure to
2478 // initialize the later variants (lest we leak random host memory into
2479 // the guest module). Due to how the `Lower` type is represented as a
2480 // `union` of all the variants what ends up happening here is that
2481 // internally within the `lower_payload` after the typed payload is
2482 // lowered the remaining bits of the payload that weren't initialized
2483 // are all set to zero. This will guarantee that we'll write to all the
2484 // slots for each variant.
2485 //
2486 // The "err" variant encounters the second issue, however, which is that
2487 // the flat representation for each type may differ between payloads. In
2488 // the "ok" arm an `i64` is written, but the `lower` implementation for
2489 // the "err" arm will write an `f32` and then an `i32`. For this
2490 // implementation of `lower` to be valid the `f32` needs to get inflated
2491 // to an `i64` with zero-padding in the upper bits. What may be
2492 // surprising, however, is that none of this is handled in this file.
2493 // This implementation looks like it's blindly deferring to `E::lower`
2494 // and hoping it does the right thing.
2495 //
2496 // In reality, however, the correctness of variant lowering relies on
2497 // two subtle details of the `ValRaw` implementation in Wasmtime:
2498 //
2499 // 1. First the `ValRaw` value always contains little-endian values.
2500 // This means that if a `u32` is written, a `u64` is read, and then
2501 // the `u64` has its upper bits truncated the original value will
2502 // always be retained. This is primarily here for big-endian
2503 // platforms where if it weren't little endian then the opposite
2504 // would occur and the wrong value would be read.
2505 //
2506 // 2. Second, and perhaps even more subtly, the `ValRaw` constructors
2507 // for 32-bit types actually always initialize 64-bits of the
2508 // `ValRaw`. In the component model flat ABI only 32 and 64-bit types
2509 // are used so 64-bits is big enough to contain everything. This
2510 // means that when a `ValRaw` is written into the destination it will
2511 // always, whether it's needed or not, be "ready" to get extended up
2512 // to 64-bits.
2513 //
2514 // Put together these two subtle guarantees means that all `Lower`
2515 // implementations can be written "naturally" as one might naively
2516 // expect. Variants will, on each arm, zero out remaining fields and all
2517 // writes to the flat representation will automatically be 64-bit writes
2518 // meaning that if the value is read as a 64-bit value, which isn't
2519 // known at the time of the write, it'll still be correct.
2520 match self {
2521 Ok(e) => {
2522 map_maybe_uninit!(dst.tag).write(ValRaw::i32(0));
2523 unsafe {
2524 lower_payload(
2525 map_maybe_uninit!(dst.payload),
2526 |payload| map_maybe_uninit!(payload.ok),
2527 |dst| match ok {
2528 Some(ok) => e.linear_lower_to_flat(cx, ok, dst),
2529 None => Ok(()),
2530 },
2531 )
2532 }
2533 }
2534 Err(e) => {
2535 map_maybe_uninit!(dst.tag).write(ValRaw::i32(1));
2536 unsafe {
2537 lower_payload(
2538 map_maybe_uninit!(dst.payload),
2539 |payload| map_maybe_uninit!(payload.err),
2540 |dst| match err {
2541 Some(err) => e.linear_lower_to_flat(cx, err, dst),
2542 None => Ok(()),
2543 },
2544 )
2545 }
2546 }
2547 }
2548 }
2549
2550 fn linear_lower_to_memory<U>(
2551 &self,
2552 cx: &mut LowerContext<'_, U>,
2553 ty: InterfaceType,
2554 offset: usize,
2555 ) -> Result<()> {
2556 let (ok, err) = match ty {
2557 InterfaceType::Result(ty) => {
2558 let ty = &cx.types[ty];
2559 (ty.ok, ty.err)
2560 }
2561 _ => bad_type_info(),
2562 };
2563 debug_assert!(offset % (Self::ALIGN32 as usize) == 0);
2564 let payload_offset = Self::INFO.payload_offset32 as usize;
2565 match self {
2566 Ok(e) => {
2567 cx.get::<1>(offset)[0] = 0;
2568 if let Some(ok) = ok {
2569 e.linear_lower_to_memory(cx, ok, offset + payload_offset)?;
2570 }
2571 }
2572 Err(e) => {
2573 cx.get::<1>(offset)[0] = 1;
2574 if let Some(err) = err {
2575 e.linear_lower_to_memory(cx, err, offset + payload_offset)?;
2576 }
2577 }
2578 }
2579 Ok(())
2580 }
2581}
2582
2583unsafe impl<T, E> Lift for Result<T, E>
2584where
2585 T: Lift,
2586 E: Lift,
2587{
2588 #[inline]
2589 fn linear_lift_from_flat(
2590 cx: &mut LiftContext<'_>,
2591 ty: InterfaceType,
2592 src: &Self::Lower,
2593 ) -> Result<Self> {
2594 let (ok, err) = match ty {
2595 InterfaceType::Result(ty) => {
2596 let ty = &cx.types[ty];
2597 (ty.ok, ty.err)
2598 }
2599 _ => bad_type_info(),
2600 };
2601 // Note that this implementation specifically isn't trying to actually
2602 // reinterpret or alter the bits of `lower` depending on which variant
2603 // we're lifting. This ends up all working out because the value is
2604 // stored in little-endian format.
2605 //
2606 // When stored in little-endian format the `{T,E}::Lower`, when each
2607 // individual `ValRaw` is read, means that if an i64 value, extended
2608 // from an i32 value, was stored then when the i32 value is read it'll
2609 // automatically ignore the upper bits.
2610 //
2611 // This "trick" allows us to seamlessly pass through the `Self::Lower`
2612 // representation into the lifting/lowering without trying to handle
2613 // "join"ed types as per the canonical ABI. It just so happens that i64
2614 // bits will naturally be reinterpreted as f64. Additionally if the
2615 // joined type is i64 but only the lower bits are read that's ok and we
2616 // don't need to validate the upper bits.
2617 //
2618 // This is largely enabled by WebAssembly/component-model#35 where no
2619 // validation needs to be performed for ignored bits and bytes here.
2620 Ok(match src.tag.get_i32() {
2621 0 => Ok(unsafe { lift_option(cx, ok, &src.payload.ok)? }),
2622 1 => Err(unsafe { lift_option(cx, err, &src.payload.err)? }),
2623 _ => bail!("invalid expected discriminant"),
2624 })
2625 }
2626
2627 #[inline]
2628 fn linear_lift_from_memory(
2629 cx: &mut LiftContext<'_>,
2630 ty: InterfaceType,
2631 bytes: &[u8],
2632 ) -> Result<Self> {
2633 debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
2634 let discrim = bytes[0];
2635 let payload = &bytes[Self::INFO.payload_offset32 as usize..];
2636 let (ok, err) = match ty {
2637 InterfaceType::Result(ty) => {
2638 let ty = &cx.types[ty];
2639 (ty.ok, ty.err)
2640 }
2641 _ => bad_type_info(),
2642 };
2643 match discrim {
2644 0 => Ok(Ok(load_option(cx, ok, &payload[..T::SIZE32])?)),
2645 1 => Ok(Err(load_option(cx, err, &payload[..E::SIZE32])?)),
2646 _ => bail!("invalid expected discriminant"),
2647 }
2648 }
2649}
2650
2651fn lift_option<T>(cx: &mut LiftContext<'_>, ty: Option<InterfaceType>, src: &T::Lower) -> Result<T>
2652where
2653 T: Lift,
2654{
2655 match ty {
2656 Some(ty) => T::linear_lift_from_flat(cx, ty, src),
2657 None => Ok(empty_lift()),
2658 }
2659}
2660
2661fn load_option<T>(cx: &mut LiftContext<'_>, ty: Option<InterfaceType>, bytes: &[u8]) -> Result<T>
2662where
2663 T: Lift,
2664{
2665 match ty {
2666 Some(ty) => T::linear_lift_from_memory(cx, ty, bytes),
2667 None => Ok(empty_lift()),
2668 }
2669}
2670
2671fn empty_lift<T>() -> T
2672where
2673 T: Lift,
2674{
2675 assert!(T::IS_RUST_UNIT_TYPE);
2676 assert_eq!(mem::size_of::<T>(), 0);
2677 unsafe { MaybeUninit::uninit().assume_init() }
2678}
2679
2680/// Helper structure to define `Lower` for tuples below.
2681///
2682/// Uses default type parameters to have fields be zero-sized and not present
2683/// in memory for smaller tuple values.
2684#[expect(non_snake_case, reason = "more amenable to macro-generated code")]
2685#[doc(hidden)]
2686#[derive(Clone, Copy)]
2687#[repr(C)]
2688pub struct TupleLower<
2689 T1 = (),
2690 T2 = (),
2691 T3 = (),
2692 T4 = (),
2693 T5 = (),
2694 T6 = (),
2695 T7 = (),
2696 T8 = (),
2697 T9 = (),
2698 T10 = (),
2699 T11 = (),
2700 T12 = (),
2701 T13 = (),
2702 T14 = (),
2703 T15 = (),
2704 T16 = (),
2705 T17 = (),
2706> {
2707 // NB: these names match the names in `for_each_function_signature!`
2708 A1: T1,
2709 A2: T2,
2710 A3: T3,
2711 A4: T4,
2712 A5: T5,
2713 A6: T6,
2714 A7: T7,
2715 A8: T8,
2716 A9: T9,
2717 A10: T10,
2718 A11: T11,
2719 A12: T12,
2720 A13: T13,
2721 A14: T14,
2722 A15: T15,
2723 A16: T16,
2724 A17: T17,
2725 _align_tuple_lower0_correctly: [ValRaw; 0],
2726}
2727
2728macro_rules! impl_component_ty_for_tuples {
2729 ($n:tt $($t:ident)*) => {
2730 #[allow(non_snake_case, reason = "macro-generated code")]
2731 unsafe impl<$($t,)*> ComponentType for ($($t,)*)
2732 where $($t: ComponentType),*
2733 {
2734 type Lower = TupleLower<$($t::Lower),*>;
2735
2736 const ABI: CanonicalAbiInfo = CanonicalAbiInfo::record_static(&[
2737 $($t::ABI),*
2738 ]);
2739
2740 const IS_RUST_UNIT_TYPE: bool = {
2741 let mut _is_unit = true;
2742 $(
2743 let _anything_to_bind_the_macro_variable = $t::IS_RUST_UNIT_TYPE;
2744 _is_unit = false;
2745 )*
2746 _is_unit
2747 };
2748
2749 fn typecheck(
2750 ty: &InterfaceType,
2751 types: &InstanceType<'_>,
2752 ) -> Result<()> {
2753 typecheck_tuple(ty, types, &[$($t::typecheck),*])
2754 }
2755 }
2756
2757 #[allow(non_snake_case, reason = "macro-generated code")]
2758 unsafe impl<$($t,)*> Lower for ($($t,)*)
2759 where $($t: Lower),*
2760 {
2761 fn linear_lower_to_flat<U>(
2762 &self,
2763 cx: &mut LowerContext<'_, U>,
2764 ty: InterfaceType,
2765 _dst: &mut MaybeUninit<Self::Lower>,
2766 ) -> Result<()> {
2767 let types = match ty {
2768 InterfaceType::Tuple(t) => &cx.types[t].types,
2769 _ => bad_type_info(),
2770 };
2771 let ($($t,)*) = self;
2772 let mut _types = types.iter();
2773 $(
2774 let ty = *_types.next().unwrap_or_else(bad_type_info);
2775 $t.linear_lower_to_flat(cx, ty, map_maybe_uninit!(_dst.$t))?;
2776 )*
2777 Ok(())
2778 }
2779
2780 fn linear_lower_to_memory<U>(
2781 &self,
2782 cx: &mut LowerContext<'_, U>,
2783 ty: InterfaceType,
2784 mut _offset: usize,
2785 ) -> Result<()> {
2786 debug_assert!(_offset % (Self::ALIGN32 as usize) == 0);
2787 let types = match ty {
2788 InterfaceType::Tuple(t) => &cx.types[t].types,
2789 _ => bad_type_info(),
2790 };
2791 let ($($t,)*) = self;
2792 let mut _types = types.iter();
2793 $(
2794 let ty = *_types.next().unwrap_or_else(bad_type_info);
2795 $t.linear_lower_to_memory(cx, ty, $t::ABI.next_field32_size(&mut _offset))?;
2796 )*
2797 Ok(())
2798 }
2799 }
2800
2801 #[allow(non_snake_case, reason = "macro-generated code")]
2802 unsafe impl<$($t,)*> Lift for ($($t,)*)
2803 where $($t: Lift),*
2804 {
2805 #[inline]
2806 fn linear_lift_from_flat(cx: &mut LiftContext<'_>, ty: InterfaceType, _src: &Self::Lower) -> Result<Self> {
2807 let types = match ty {
2808 InterfaceType::Tuple(t) => &cx.types[t].types,
2809 _ => bad_type_info(),
2810 };
2811 let mut _types = types.iter();
2812 Ok(($(
2813 $t::linear_lift_from_flat(
2814 cx,
2815 *_types.next().unwrap_or_else(bad_type_info),
2816 &_src.$t,
2817 )?,
2818 )*))
2819 }
2820
2821 #[inline]
2822 fn linear_lift_from_memory(cx: &mut LiftContext<'_>, ty: InterfaceType, bytes: &[u8]) -> Result<Self> {
2823 debug_assert!((bytes.as_ptr() as usize) % (Self::ALIGN32 as usize) == 0);
2824 let types = match ty {
2825 InterfaceType::Tuple(t) => &cx.types[t].types,
2826 _ => bad_type_info(),
2827 };
2828 let mut _types = types.iter();
2829 let mut _offset = 0;
2830 $(
2831 let ty = *_types.next().unwrap_or_else(bad_type_info);
2832 let $t = $t::linear_lift_from_memory(cx, ty, &bytes[$t::ABI.next_field32_size(&mut _offset)..][..$t::SIZE32])?;
2833 )*
2834 Ok(($($t,)*))
2835 }
2836 }
2837
2838 #[allow(non_snake_case, reason = "macro-generated code")]
2839 unsafe impl<$($t,)*> ComponentNamedList for ($($t,)*)
2840 where $($t: ComponentType),*
2841 {}
2842 };
2843}
2844
2845for_each_function_signature!(impl_component_ty_for_tuples);
2846
2847pub fn desc(ty: &InterfaceType) -> &'static str {
2848 match ty {
2849 InterfaceType::U8 => "u8",
2850 InterfaceType::S8 => "s8",
2851 InterfaceType::U16 => "u16",
2852 InterfaceType::S16 => "s16",
2853 InterfaceType::U32 => "u32",
2854 InterfaceType::S32 => "s32",
2855 InterfaceType::U64 => "u64",
2856 InterfaceType::S64 => "s64",
2857 InterfaceType::Float32 => "f32",
2858 InterfaceType::Float64 => "f64",
2859 InterfaceType::Bool => "bool",
2860 InterfaceType::Char => "char",
2861 InterfaceType::String => "string",
2862 InterfaceType::List(_) => "list",
2863 InterfaceType::Tuple(_) => "tuple",
2864 InterfaceType::Option(_) => "option",
2865 InterfaceType::Result(_) => "result",
2866
2867 InterfaceType::Record(_) => "record",
2868 InterfaceType::Variant(_) => "variant",
2869 InterfaceType::Flags(_) => "flags",
2870 InterfaceType::Enum(_) => "enum",
2871 InterfaceType::Own(_) => "owned resource",
2872 InterfaceType::Borrow(_) => "borrowed resource",
2873 InterfaceType::Future(_) => "future",
2874 InterfaceType::Stream(_) => "stream",
2875 InterfaceType::ErrorContext(_) => "error-context",
2876 }
2877}
2878
2879#[cold]
2880#[doc(hidden)]
2881pub fn bad_type_info<T>() -> T {
2882 // NB: should consider something like `unreachable_unchecked` here if this
2883 // becomes a performance bottleneck at some point, but that also comes with
2884 // a tradeoff of propagating a lot of unsafety, so it may not be worth it.
2885 panic!("bad type information detected");
2886}