wasmtime/runtime/component/func/
options.rs

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