wasmtime/runtime/component/func/
options.rs

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