wasmtime/runtime/component/func/
options.rs

1use crate::component::matching::InstanceType;
2use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables};
3use crate::component::ResourceType;
4use crate::prelude::*;
5use crate::runtime::vm::component::{
6    CallContexts, ComponentInstance, InstanceFlags, ResourceTable, ResourceTables,
7};
8use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition};
9use crate::store::{StoreId, StoreOpaque};
10use crate::{FuncType, StoreContextMut};
11use alloc::sync::Arc;
12use core::ptr::NonNull;
13use wasmtime_environ::component::{ComponentTypes, StringEncoding, TypeResourceTableIndex};
14
15/// Runtime representation of canonical ABI options in the component model.
16///
17/// This structure packages up the runtime representation of each option from
18/// memories to reallocs to string encodings. Note that this is a "standalone"
19/// structure which has raw pointers internally. This allows it to be created
20/// out of thin air for a host function import, for example. The `store_id`
21/// field, however, is what is used to pair this set of options with a store
22/// reference to actually use the pointers.
23#[derive(Copy, Clone)]
24pub struct Options {
25    /// The store from which this options originated from.
26    store_id: StoreId,
27
28    /// An optional pointer for the memory that this set of options is referring
29    /// to. This option is not required to be specified in the canonical ABI
30    /// hence the `Option`.
31    ///
32    /// Note that this pointer cannot be safely dereferenced unless a store,
33    /// verified with `self.store_id`, has the appropriate borrow available.
34    memory: Option<NonNull<VMMemoryDefinition>>,
35
36    /// Similar to `memory` but corresponds to the `canonical_abi_realloc`
37    /// function.
38    ///
39    /// Safely using this pointer has the same restrictions as `memory` above.
40    realloc: Option<NonNull<VMFuncRef>>,
41
42    /// The encoding used for strings, if found.
43    ///
44    /// This defaults to utf-8 but can be changed if necessary.
45    string_encoding: StringEncoding,
46}
47
48// The `Options` structure stores raw pointers but they're never used unless a
49// `Store` is available so this should be threadsafe and largely inherit the
50// thread-safety story of `Store<T>` itself.
51unsafe impl Send for Options {}
52unsafe impl Sync for Options {}
53
54impl Options {
55    // FIXME(#4311): prevent a ctor where the memory is memory64
56
57    /// Creates a new set of options with the specified components.
58    ///
59    /// # Unsafety
60    ///
61    /// This is unsafety as there is no way to statically verify the validity of
62    /// the arguments. For example pointers must be valid pointers, the
63    /// `StoreId` must be valid for the pointers, etc.
64    pub unsafe fn new(
65        store_id: StoreId,
66        memory: Option<NonNull<VMMemoryDefinition>>,
67        realloc: Option<NonNull<VMFuncRef>>,
68        string_encoding: StringEncoding,
69    ) -> Options {
70        Options {
71            store_id,
72            memory,
73            realloc,
74            string_encoding,
75        }
76    }
77
78    fn realloc<'a, T>(
79        &self,
80        store: &'a mut StoreContextMut<'_, T>,
81        realloc_ty: &FuncType,
82        old: usize,
83        old_size: usize,
84        old_align: u32,
85        new_size: usize,
86    ) -> Result<(&'a mut [u8], usize)> {
87        self.store_id.assert_belongs_to(store.0.id());
88
89        let realloc = self.realloc.unwrap();
90
91        let params = (
92            u32::try_from(old)?,
93            u32::try_from(old_size)?,
94            old_align,
95            u32::try_from(new_size)?,
96        );
97
98        type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>;
99
100        // This call doesn't take any GC refs, and therefore we shouldn't ever
101        // need to GC before entering Wasm.
102        debug_assert!(!ReallocFunc::need_gc_before_call_raw(store.0, &params));
103
104        // Invoke the wasm malloc function using its raw and statically known
105        // signature.
106        let result = unsafe { ReallocFunc::call_raw(store, realloc_ty, realloc, params)? };
107
108        if result % old_align != 0 {
109            bail!("realloc return: result not aligned");
110        }
111        let result = usize::try_from(result)?;
112
113        let memory = self.memory_mut(store.0);
114
115        let result_slice = match memory.get_mut(result..).and_then(|s| s.get_mut(..new_size)) {
116            Some(end) => end,
117            None => bail!("realloc return: beyond end of memory"),
118        };
119
120        Ok((result_slice, result))
121    }
122
123    /// Asserts that this function has an associated memory attached to it and
124    /// then returns the slice of memory tied to the lifetime of the provided
125    /// store.
126    pub fn memory<'a>(&self, store: &'a StoreOpaque) -> &'a [u8] {
127        self.store_id.assert_belongs_to(store.id());
128
129        // The unsafety here is intended to be encapsulated by the two
130        // preceding assertions. Namely we assert that the `store` is the same
131        // as the original store of this `Options`, meaning that we safely have
132        // either a shared reference or a mutable reference (as below) which
133        // means it's safe to view the memory (aka it's not a different store
134        // where our original store is on some other thread or something like
135        // that).
136        //
137        // Additionally the memory itself is asserted to be present as memory
138        // is an optional configuration in canonical ABI options.
139        unsafe {
140            let memory = self.memory.unwrap().as_ref();
141            core::slice::from_raw_parts(memory.base.as_ptr(), memory.current_length())
142        }
143    }
144
145    /// Same as above, just `_mut`
146    pub fn memory_mut<'a>(&self, store: &'a mut StoreOpaque) -> &'a mut [u8] {
147        self.store_id.assert_belongs_to(store.id());
148
149        // See comments in `memory` about the unsafety
150        unsafe {
151            let memory = self.memory.unwrap().as_ref();
152            core::slice::from_raw_parts_mut(memory.base.as_ptr(), memory.current_length())
153        }
154    }
155
156    /// Returns the underlying encoding used for strings in this
157    /// lifting/lowering.
158    pub fn string_encoding(&self) -> StringEncoding {
159        self.string_encoding
160    }
161
162    /// Returns the id of the store that this `Options` is connected to.
163    pub fn store_id(&self) -> StoreId {
164        self.store_id
165    }
166}
167
168/// A helper structure which is a "package" of the context used during lowering
169/// values into a component (or storing them into memory).
170///
171/// This type is used by the `Lower` trait extensively and contains any
172/// contextual information necessary related to the context in which the
173/// lowering is happening.
174#[doc(hidden)]
175pub struct LowerContext<'a, T> {
176    /// Lowering may involve invoking memory allocation functions so part of the
177    /// context here is carrying access to the entire store that wasm is
178    /// executing within. This store serves as proof-of-ability to actually
179    /// execute wasm safely.
180    pub store: StoreContextMut<'a, T>,
181
182    /// Lowering always happens into a function that's been `canon lift`'d or
183    /// `canon lower`'d, both of which specify a set of options for the
184    /// canonical ABI. For example details like string encoding are contained
185    /// here along with which memory pointers are relative to or what the memory
186    /// allocation function is.
187    pub options: &'a Options,
188
189    /// Lowering happens within the context of a component instance and this
190    /// field stores the type information of that component instance. This is
191    /// used for type lookups and general type queries during the
192    /// lifting/lowering process.
193    pub types: &'a ComponentTypes,
194
195    /// A raw unsafe pointer to the component instance that's being lowered
196    /// into.
197    ///
198    /// This pointer is required to be owned by the `store` provided.
199    instance: *mut ComponentInstance,
200}
201
202#[doc(hidden)]
203impl<'a, T> LowerContext<'a, T> {
204    /// Creates a new lowering context from the specified parameters.
205    ///
206    /// # Unsafety
207    ///
208    /// This function is unsafe as it needs to be guaranteed by the caller that
209    /// the `instance` here is is valid within `store` and is a valid component
210    /// instance.
211    pub unsafe fn new(
212        store: StoreContextMut<'a, T>,
213        options: &'a Options,
214        types: &'a ComponentTypes,
215        instance: *mut ComponentInstance,
216    ) -> LowerContext<'a, T> {
217        LowerContext {
218            store,
219            options,
220            types,
221            instance,
222        }
223    }
224
225    /// Returns a view into memory as a mutable slice of bytes.
226    ///
227    /// # Panics
228    ///
229    /// This will panic if memory has not been configured for this lowering
230    /// (e.g. it wasn't present during the specification of canonical options).
231    pub fn as_slice_mut(&mut self) -> &mut [u8] {
232        self.options.memory_mut(self.store.0)
233    }
234
235    /// Invokes the memory allocation function (which is style after `realloc`)
236    /// with the specified parameters.
237    ///
238    /// # Panics
239    ///
240    /// This will panic if realloc hasn't been configured for this lowering via
241    /// its canonical options.
242    pub fn realloc(
243        &mut self,
244        old: usize,
245        old_size: usize,
246        old_align: u32,
247        new_size: usize,
248    ) -> Result<usize> {
249        let realloc_func_ty = Arc::clone(unsafe { (*self.instance).realloc_func_ty() });
250        let realloc_func_ty = realloc_func_ty.downcast_ref::<FuncType>().unwrap();
251        self.options
252            .realloc(
253                &mut self.store,
254                &realloc_func_ty,
255                old,
256                old_size,
257                old_align,
258                new_size,
259            )
260            .map(|(_, ptr)| ptr)
261    }
262
263    /// Returns a fixed mutable slice of memory `N` bytes large starting at
264    /// offset `N`, panicking on out-of-bounds.
265    ///
266    /// It should be previously verified that `offset` is in-bounds via
267    /// bounds-checks.
268    ///
269    /// # Panics
270    ///
271    /// This will panic if memory has not been configured for this lowering
272    /// (e.g. it wasn't present during the specification of canonical options).
273    pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] {
274        // FIXME: this bounds check shouldn't actually be necessary, all
275        // callers of `ComponentType::store` have already performed a bounds
276        // check so we're guaranteed that `offset..offset+N` is in-bounds. That
277        // being said we at least should do bounds checks in debug mode and
278        // it's not clear to me how to easily structure this so that it's
279        // "statically obvious" the bounds check isn't necessary.
280        //
281        // For now I figure we can leave in this bounds check and if it becomes
282        // an issue we can optimize further later, probably with judicious use
283        // of `unsafe`.
284        self.as_slice_mut()[offset..].first_chunk_mut().unwrap()
285    }
286
287    /// Lowers an `own` resource into the guest, converting the `rep` specified
288    /// into a guest-local index.
289    ///
290    /// The `ty` provided is which table to put this into.
291    pub fn guest_resource_lower_own(
292        &mut self,
293        ty: TypeResourceTableIndex,
294        rep: u32,
295    ) -> Result<u32> {
296        self.resource_tables().guest_resource_lower_own(rep, ty)
297    }
298
299    /// Lowers a `borrow` resource into the guest, converting the `rep` to a
300    /// guest-local index in the `ty` table specified.
301    pub fn guest_resource_lower_borrow(
302        &mut self,
303        ty: TypeResourceTableIndex,
304        rep: u32,
305    ) -> Result<u32> {
306        // Implement `lower_borrow`'s special case here where if a borrow is
307        // inserted into a table owned by the instance which implemented the
308        // original resource then no borrow tracking is employed and instead the
309        // `rep` is returned "raw".
310        //
311        // This check is performed by comparing the owning instance of `ty`
312        // against the owning instance of the resource that `ty` is working
313        // with.
314        //
315        // Note that the unsafety here should be valid given the contract of
316        // `LowerContext::new`.
317        if unsafe { (*self.instance).resource_owned_by_own_instance(ty) } {
318            return Ok(rep);
319        }
320        self.resource_tables().guest_resource_lower_borrow(rep, ty)
321    }
322
323    /// Lifts a host-owned `own` resource at the `idx` specified into the
324    /// representation of that resource.
325    pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
326        self.resource_tables().host_resource_lift_own(idx)
327    }
328
329    /// Lifts a host-owned `borrow` resource at the `idx` specified into the
330    /// representation of that resource.
331    pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
332        self.resource_tables().host_resource_lift_borrow(idx)
333    }
334
335    /// Lowers a resource into the host-owned table, returning the index it was
336    /// inserted at.
337    ///
338    /// Note that this is a special case for `Resource<T>`. Most of the time a
339    /// host value shouldn't be lowered with a lowering context.
340    pub fn host_resource_lower_own(
341        &mut self,
342        rep: u32,
343        dtor: Option<NonNull<VMFuncRef>>,
344        flags: Option<InstanceFlags>,
345    ) -> Result<HostResourceIndex> {
346        self.resource_tables()
347            .host_resource_lower_own(rep, dtor, flags)
348    }
349
350    /// Returns the underlying resource type for the `ty` table specified.
351    pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
352        self.instance_type().resource_type(ty)
353    }
354
355    /// Returns the instance type information corresponding to the instance that
356    /// this context is lowering into.
357    pub fn instance_type(&self) -> InstanceType<'_> {
358        // Note that the unsafety here should be valid given the contract of
359        // `LowerContext::new`.
360        InstanceType::new(unsafe { &*self.instance })
361    }
362
363    fn resource_tables(&mut self) -> HostResourceTables<'_> {
364        let (calls, host_table, host_resource_data) = self.store.0.component_resource_state();
365        HostResourceTables::from_parts(
366            ResourceTables {
367                host_table: Some(host_table),
368                calls,
369                // Note that the unsafety here should be valid given the contract of
370                // `LowerContext::new`.
371                tables: Some(unsafe { (*self.instance).component_resource_tables() }),
372            },
373            host_resource_data,
374        )
375    }
376
377    /// See [`HostResourceTables::enter_call`].
378    #[inline]
379    pub fn enter_call(&mut self) {
380        self.resource_tables().enter_call()
381    }
382
383    /// See [`HostResourceTables::exit_call`].
384    #[inline]
385    pub fn exit_call(&mut self) -> Result<()> {
386        self.resource_tables().exit_call()
387    }
388}
389
390/// Contextual information used when lifting a type from a component into the
391/// host.
392///
393/// This structure is the analogue of `LowerContext` except used during lifting
394/// operations (or loading from memory).
395#[doc(hidden)]
396pub struct LiftContext<'a> {
397    /// Like lowering, lifting always has options configured.
398    pub options: &'a Options,
399
400    /// Instance type information, like with lowering.
401    pub types: &'a Arc<ComponentTypes>,
402
403    memory: Option<&'a [u8]>,
404
405    instance: *mut ComponentInstance,
406
407    host_table: &'a mut ResourceTable,
408    host_resource_data: &'a mut HostResourceData,
409
410    calls: &'a mut CallContexts,
411}
412
413#[doc(hidden)]
414impl<'a> LiftContext<'a> {
415    /// Creates a new lifting context given the provided context.
416    ///
417    /// # Unsafety
418    ///
419    /// This is unsafe for the same reasons as `LowerContext::new` where the
420    /// validity of `instance` is required to be upheld by the caller.
421    #[inline]
422    pub unsafe fn new(
423        store: &'a mut StoreOpaque,
424        options: &'a Options,
425        types: &'a Arc<ComponentTypes>,
426        instance: *mut ComponentInstance,
427    ) -> LiftContext<'a> {
428        // From `&mut StoreOpaque` provided the goal here is to project out
429        // three different disjoint fields owned by the store: memory,
430        // `CallContexts`, and `ResourceTable`. There's no native API for that
431        // so it's hacked around a bit. This unsafe pointer cast could be fixed
432        // with more methods in more places, but it doesn't seem worth doing it
433        // at this time.
434        let (calls, host_table, host_resource_data) =
435            (&mut *(store as *mut StoreOpaque)).component_resource_state();
436        let memory = options.memory.map(|_| options.memory(store));
437
438        LiftContext {
439            memory,
440            options,
441            types,
442            instance,
443            calls,
444            host_table,
445            host_resource_data,
446        }
447    }
448
449    /// Returns the entire contents of linear memory for this set of lifting
450    /// options.
451    ///
452    /// # Panics
453    ///
454    /// This will panic if memory has not been configured for this lifting
455    /// operation.
456    pub fn memory(&self) -> &'a [u8] {
457        self.memory.unwrap()
458    }
459
460    /// Returns an identifier for the store from which this `LiftContext` was
461    /// created.
462    pub fn store_id(&self) -> StoreId {
463        self.options.store_id
464    }
465
466    /// Returns the component instance raw pointer that is being lifted from.
467    pub fn instance_ptr(&self) -> *mut ComponentInstance {
468        self.instance
469    }
470
471    /// Lifts an `own` resource from the guest at the `idx` specified into its
472    /// representation.
473    ///
474    /// Additionally returns a destructor/instance flags to go along with the
475    /// representation so the host knows how to destroy this resource.
476    pub fn guest_resource_lift_own(
477        &mut self,
478        ty: TypeResourceTableIndex,
479        idx: u32,
480    ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> {
481        let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?;
482        // Note that the unsafety here should be valid given the contract of
483        // `LiftContext::new`.
484        let (dtor, flags) = unsafe { (*self.instance).dtor_and_flags(ty) };
485        Ok((idx, dtor, flags))
486    }
487
488    /// Lifts a `borrow` resource from the guest at the `idx` specified.
489    pub fn guest_resource_lift_borrow(
490        &mut self,
491        ty: TypeResourceTableIndex,
492        idx: u32,
493    ) -> Result<u32> {
494        self.resource_tables().guest_resource_lift_borrow(idx, ty)
495    }
496
497    /// Lowers a resource into the host-owned table, returning the index it was
498    /// inserted at.
499    pub fn host_resource_lower_own(
500        &mut self,
501        rep: u32,
502        dtor: Option<NonNull<VMFuncRef>>,
503        flags: Option<InstanceFlags>,
504    ) -> Result<HostResourceIndex> {
505        self.resource_tables()
506            .host_resource_lower_own(rep, dtor, flags)
507    }
508
509    /// Lowers a resource into the host-owned table, returning the index it was
510    /// inserted at.
511    pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
512        self.resource_tables().host_resource_lower_borrow(rep)
513    }
514
515    /// Returns the underlying type of the resource table specified by `ty`.
516    pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
517        self.instance_type().resource_type(ty)
518    }
519
520    /// Returns instance type information for the component instance that is
521    /// being lifted from.
522    pub fn instance_type(&self) -> InstanceType<'_> {
523        // Note that the unsafety here should be valid given the contract of
524        // `LiftContext::new`.
525        InstanceType::new(unsafe { &*self.instance })
526    }
527
528    fn resource_tables(&mut self) -> HostResourceTables<'_> {
529        HostResourceTables::from_parts(
530            ResourceTables {
531                host_table: Some(self.host_table),
532                calls: self.calls,
533                // Note that the unsafety here should be valid given the contract of
534                // `LiftContext::new`.
535                tables: Some(unsafe { (*self.instance).component_resource_tables() }),
536            },
537            self.host_resource_data,
538        )
539    }
540
541    /// See [`HostResourceTables::enter_call`].
542    #[inline]
543    pub fn enter_call(&mut self) {
544        self.resource_tables().enter_call()
545    }
546
547    /// See [`HostResourceTables::exit_call`].
548    #[inline]
549    pub fn exit_call(&mut self) -> Result<()> {
550        self.resource_tables().exit_call()
551    }
552}