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