wasmtime/runtime/component/func/
options.rs

1use crate::StoreContextMut;
2use crate::component::concurrent::ConcurrentState;
3use crate::component::matching::InstanceType;
4use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables};
5use crate::component::{Instance, ResourceType, RuntimeInstance};
6use crate::prelude::*;
7use crate::runtime::vm::VMFuncRef;
8use crate::runtime::vm::component::{CallContexts, ComponentInstance, HandleTable, ResourceTables};
9use crate::store::{StoreId, StoreOpaque};
10use alloc::sync::Arc;
11use core::pin::Pin;
12use core::ptr::NonNull;
13use wasmtime_environ::component::{
14    CanonicalOptions, CanonicalOptionsDataModel, ComponentTypes, OptionsIndex,
15    TypeResourceTableIndex,
16};
17
18/// A helper structure which is a "package" of the context used during lowering
19/// values into a component (or storing them into memory).
20///
21/// This type is used by the `Lower` trait extensively and contains any
22/// contextual information necessary related to the context in which the
23/// lowering is happening.
24#[doc(hidden)]
25pub struct LowerContext<'a, T: 'static> {
26    /// Lowering may involve invoking memory allocation functions so part of the
27    /// context here is carrying access to the entire store that wasm is
28    /// executing within. This store serves as proof-of-ability to actually
29    /// execute wasm safely.
30    pub store: StoreContextMut<'a, T>,
31
32    /// Lowering always happens into a function that's been `canon lift`'d or
33    /// `canon lower`'d, both of which specify a set of options for the
34    /// canonical ABI. For example details like string encoding are contained
35    /// here along with which memory pointers are relative to or what the memory
36    /// allocation function is.
37    options: OptionsIndex,
38
39    /// Lowering happens within the context of a component instance and this
40    /// field stores the type information of that component instance. This is
41    /// used for type lookups and general type queries during the
42    /// lifting/lowering process.
43    pub types: &'a ComponentTypes,
44
45    /// Index of the component instance that's being lowered into.
46    instance: Instance,
47
48    /// Whether to allow `options.realloc` to be used when lowering.
49    allow_realloc: bool,
50}
51
52#[doc(hidden)]
53impl<'a, T: 'static> LowerContext<'a, T> {
54    /// Creates a new lowering context from the specified parameters.
55    pub fn new(
56        store: StoreContextMut<'a, T>,
57        options: OptionsIndex,
58        instance: Instance,
59    ) -> LowerContext<'a, T> {
60        #[cfg(all(debug_assertions, feature = "component-model-async"))]
61        if store.engine().config().async_support {
62            // Assert that we're running on a fiber, which is necessary in
63            // case we call the guest's realloc function.
64            store.0.with_blocking(|_, _| {});
65        }
66        let (component, store) = instance.component_and_store_mut(store.0);
67        LowerContext {
68            store: StoreContextMut(store),
69            options,
70            types: component.types(),
71            instance,
72            allow_realloc: true,
73        }
74    }
75
76    /// Like `new`, except disallows use of `options.realloc`.
77    ///
78    /// The returned object will panic if its `realloc` method is called.
79    ///
80    /// This is meant for use when lowering "flat" values (i.e. values which
81    /// require no allocations) into already-allocated memory or into stack
82    /// slots, in which case the lowering may safely be done outside of a fiber
83    /// since there is no need to make any guest calls.
84    #[cfg(feature = "component-model-async")]
85    pub(crate) fn new_without_realloc(
86        store: StoreContextMut<'a, T>,
87        options: OptionsIndex,
88        instance: Instance,
89    ) -> LowerContext<'a, T> {
90        let (component, store) = instance.component_and_store_mut(store.0);
91        LowerContext {
92            store: StoreContextMut(store),
93            options,
94            types: component.types(),
95            instance,
96            allow_realloc: false,
97        }
98    }
99
100    /// Returns the `&ComponentInstance` that's being lowered into.
101    pub fn instance(&self) -> &ComponentInstance {
102        self.instance.id().get(self.store.0)
103    }
104
105    /// Returns the `&mut ComponentInstance` that's being lowered into.
106    pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> {
107        self.instance.id().get_mut(self.store.0)
108    }
109
110    /// Returns the canonical options that are being used during lifting.
111    pub fn options(&self) -> &CanonicalOptions {
112        &self.instance().component().env_component().options[self.options]
113    }
114
115    /// Returns a view into memory as a mutable slice of bytes.
116    ///
117    /// # Panics
118    ///
119    /// This will panic if memory has not been configured for this lowering
120    /// (e.g. it wasn't present during the specification of canonical options).
121    pub fn as_slice_mut(&mut self) -> &mut [u8] {
122        self.instance.options_memory_mut(self.store.0, self.options)
123    }
124
125    /// Invokes the memory allocation function (which is style after `realloc`)
126    /// with the specified parameters.
127    ///
128    /// # Panics
129    ///
130    /// This will panic if realloc hasn't been configured for this lowering via
131    /// its canonical options.
132    pub fn realloc(
133        &mut self,
134        old: usize,
135        old_size: usize,
136        old_align: u32,
137        new_size: usize,
138    ) -> Result<usize> {
139        assert!(self.allow_realloc);
140
141        let (component, store) = self.instance.component_and_store_mut(self.store.0);
142        let instance = self.instance.id().get(store);
143        let options = &component.env_component().options[self.options];
144        let realloc_ty = component.realloc_func_ty();
145        let realloc = match options.data_model {
146            CanonicalOptionsDataModel::Gc {} => unreachable!(),
147            CanonicalOptionsDataModel::LinearMemory(m) => m.realloc.unwrap(),
148        };
149        let realloc = instance.runtime_realloc(realloc);
150
151        let params = (
152            u32::try_from(old)?,
153            u32::try_from(old_size)?,
154            old_align,
155            u32::try_from(new_size)?,
156        );
157
158        type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>;
159
160        // Invoke the wasm malloc function using its raw and statically known
161        // signature.
162        let result = unsafe {
163            ReallocFunc::call_raw(&mut StoreContextMut(store), &realloc_ty, realloc, params)?
164        };
165
166        if result % old_align != 0 {
167            bail!("realloc return: result not aligned");
168        }
169        let result = usize::try_from(result)?;
170
171        if self
172            .as_slice_mut()
173            .get_mut(result..)
174            .and_then(|s| s.get_mut(..new_size))
175            .is_none()
176        {
177            bail!("realloc return: beyond end of memory")
178        }
179
180        Ok(result)
181    }
182
183    /// Returns a fixed mutable slice of memory `N` bytes large starting at
184    /// offset `N`, panicking on out-of-bounds.
185    ///
186    /// It should be previously verified that `offset` is in-bounds via
187    /// bounds-checks.
188    ///
189    /// # Panics
190    ///
191    /// This will panic if memory has not been configured for this lowering
192    /// (e.g. it wasn't present during the specification of canonical options).
193    pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] {
194        // FIXME: this bounds check shouldn't actually be necessary, all
195        // callers of `ComponentType::store` have already performed a bounds
196        // check so we're guaranteed that `offset..offset+N` is in-bounds. That
197        // being said we at least should do bounds checks in debug mode and
198        // it's not clear to me how to easily structure this so that it's
199        // "statically obvious" the bounds check isn't necessary.
200        //
201        // For now I figure we can leave in this bounds check and if it becomes
202        // an issue we can optimize further later, probably with judicious use
203        // of `unsafe`.
204        self.as_slice_mut()[offset..].first_chunk_mut().unwrap()
205    }
206
207    /// Lowers an `own` resource into the guest, converting the `rep` specified
208    /// into a guest-local index.
209    ///
210    /// The `ty` provided is which table to put this into.
211    pub fn guest_resource_lower_own(
212        &mut self,
213        ty: TypeResourceTableIndex,
214        rep: u32,
215    ) -> Result<u32> {
216        self.resource_tables().guest_resource_lower_own(rep, ty)
217    }
218
219    /// Lowers a `borrow` resource into the guest, converting the `rep` to a
220    /// guest-local index in the `ty` table specified.
221    pub fn guest_resource_lower_borrow(
222        &mut self,
223        ty: TypeResourceTableIndex,
224        rep: u32,
225    ) -> Result<u32> {
226        // Implement `lower_borrow`'s special case here where if a borrow is
227        // inserted into a table owned by the instance which implemented the
228        // original resource then no borrow tracking is employed and instead the
229        // `rep` is returned "raw".
230        //
231        // This check is performed by comparing the owning instance of `ty`
232        // against the owning instance of the resource that `ty` is working
233        // with.
234        if self.instance().resource_owned_by_own_instance(ty) {
235            return Ok(rep);
236        }
237        self.resource_tables().guest_resource_lower_borrow(rep, ty)
238    }
239
240    /// Lifts a host-owned `own` resource at the `idx` specified into the
241    /// representation of that resource.
242    pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
243        self.resource_tables().host_resource_lift_own(idx)
244    }
245
246    /// Lifts a host-owned `borrow` resource at the `idx` specified into the
247    /// representation of that resource.
248    pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
249        self.resource_tables().host_resource_lift_borrow(idx)
250    }
251
252    /// Lowers a resource into the host-owned table, returning the index it was
253    /// inserted at.
254    ///
255    /// Note that this is a special case for `Resource<T>`. Most of the time a
256    /// host value shouldn't be lowered with a lowering context.
257    pub fn host_resource_lower_own(
258        &mut self,
259        rep: u32,
260        dtor: Option<NonNull<VMFuncRef>>,
261        instance: Option<RuntimeInstance>,
262    ) -> Result<HostResourceIndex> {
263        self.resource_tables()
264            .host_resource_lower_own(rep, dtor, instance)
265    }
266
267    /// Returns the underlying resource type for the `ty` table specified.
268    pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
269        self.instance_type().resource_type(ty)
270    }
271
272    /// Returns the instance type information corresponding to the instance that
273    /// this context is lowering into.
274    pub fn instance_type(&self) -> InstanceType<'_> {
275        InstanceType::new(self.instance())
276    }
277
278    fn resource_tables(&mut self) -> HostResourceTables<'_> {
279        let (calls, host_table, host_resource_data, instance) = self
280            .store
281            .0
282            .component_resource_state_with_instance(self.instance);
283        HostResourceTables::from_parts(
284            ResourceTables {
285                host_table: Some(host_table),
286                calls,
287                guest: Some(instance.instance_states()),
288            },
289            host_resource_data,
290        )
291    }
292
293    /// See [`HostResourceTables::enter_call`].
294    #[inline]
295    pub fn enter_call(&mut self) {
296        self.resource_tables().enter_call()
297    }
298
299    /// See [`HostResourceTables::exit_call`].
300    #[inline]
301    pub fn exit_call(&mut self) -> Result<()> {
302        self.resource_tables().exit_call()
303    }
304}
305
306/// Contextual information used when lifting a type from a component into the
307/// host.
308///
309/// This structure is the analogue of `LowerContext` except used during lifting
310/// operations (or loading from memory).
311#[doc(hidden)]
312pub struct LiftContext<'a> {
313    store_id: StoreId,
314    /// Like lowering, lifting always has options configured.
315    options: OptionsIndex,
316
317    /// Instance type information, like with lowering.
318    pub types: &'a Arc<ComponentTypes>,
319
320    memory: &'a [u8],
321
322    instance: Pin<&'a mut ComponentInstance>,
323    instance_handle: Instance,
324
325    host_table: &'a mut HandleTable,
326    host_resource_data: &'a mut HostResourceData,
327
328    calls: &'a mut CallContexts,
329
330    #[cfg_attr(
331        not(feature = "component-model-async"),
332        allow(unused, reason = "easier to not #[cfg] away")
333    )]
334    concurrent_state: &'a mut ConcurrentState,
335}
336
337#[doc(hidden)]
338impl<'a> LiftContext<'a> {
339    /// Creates a new lifting context given the provided context.
340    #[inline]
341    pub fn new(
342        store: &'a mut StoreOpaque,
343        options: OptionsIndex,
344        instance_handle: Instance,
345    ) -> LiftContext<'a> {
346        let store_id = store.id();
347        // From `&mut StoreOpaque` provided the goal here is to project out
348        // three different disjoint fields owned by the store: memory,
349        // `CallContexts`, and `HandleTable`. There's no native API for that
350        // so it's hacked around a bit. This unsafe pointer cast could be fixed
351        // with more methods in more places, but it doesn't seem worth doing it
352        // at this time.
353        let memory =
354            instance_handle.options_memory(unsafe { &*(store as *const StoreOpaque) }, options);
355        let (calls, host_table, host_resource_data, instance, concurrent_state) =
356            store.component_resource_state_with_instance_and_concurrent_state(instance_handle);
357        let (component, instance) = instance.component_and_self();
358
359        LiftContext {
360            store_id,
361            memory,
362            options,
363            types: component.types(),
364            instance,
365            instance_handle,
366            calls,
367            host_table,
368            host_resource_data,
369            concurrent_state,
370        }
371    }
372
373    /// Returns the canonical options that are being used during lifting.
374    pub fn options(&self) -> &CanonicalOptions {
375        &self.instance.component().env_component().options[self.options]
376    }
377
378    /// Returns the `OptionsIndex` being used during lifting.
379    pub fn options_index(&self) -> OptionsIndex {
380        self.options
381    }
382
383    /// Returns the entire contents of linear memory for this set of lifting
384    /// options.
385    ///
386    /// # Panics
387    ///
388    /// This will panic if memory has not been configured for this lifting
389    /// operation.
390    pub fn memory(&self) -> &'a [u8] {
391        self.memory
392    }
393
394    /// Returns an identifier for the store from which this `LiftContext` was
395    /// created.
396    pub fn store_id(&self) -> StoreId {
397        self.store_id
398    }
399
400    /// Returns the component instance that is being lifted from.
401    pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> {
402        self.instance.as_mut()
403    }
404    /// Returns the component instance that is being lifted from.
405    pub fn instance_handle(&self) -> Instance {
406        self.instance_handle
407    }
408
409    #[cfg(feature = "component-model-async")]
410    pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState {
411        self.concurrent_state
412    }
413
414    /// Lifts an `own` resource from the guest at the `idx` specified into its
415    /// representation.
416    ///
417    /// Additionally returns a destructor/instance flags to go along with the
418    /// representation so the host knows how to destroy this resource.
419    pub fn guest_resource_lift_own(
420        &mut self,
421        ty: TypeResourceTableIndex,
422        idx: u32,
423    ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<RuntimeInstance>)> {
424        let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?;
425        let (dtor, instance) = self.instance.dtor_and_instance(ty);
426        Ok((idx, dtor, instance))
427    }
428
429    /// Lifts a `borrow` resource from the guest at the `idx` specified.
430    pub fn guest_resource_lift_borrow(
431        &mut self,
432        ty: TypeResourceTableIndex,
433        idx: u32,
434    ) -> Result<u32> {
435        self.resource_tables().guest_resource_lift_borrow(idx, ty)
436    }
437
438    /// Lowers a resource into the host-owned table, returning the index it was
439    /// inserted at.
440    pub fn host_resource_lower_own(
441        &mut self,
442        rep: u32,
443        dtor: Option<NonNull<VMFuncRef>>,
444        instance: Option<RuntimeInstance>,
445    ) -> Result<HostResourceIndex> {
446        self.resource_tables()
447            .host_resource_lower_own(rep, dtor, instance)
448    }
449
450    /// Lowers a resource into the host-owned table, returning the index it was
451    /// inserted at.
452    pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
453        self.resource_tables().host_resource_lower_borrow(rep)
454    }
455
456    /// Returns the underlying type of the resource table specified by `ty`.
457    pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
458        self.instance_type().resource_type(ty)
459    }
460
461    /// Returns instance type information for the component instance that is
462    /// being lifted from.
463    pub fn instance_type(&self) -> InstanceType<'_> {
464        InstanceType::new(&self.instance)
465    }
466
467    fn resource_tables(&mut self) -> HostResourceTables<'_> {
468        HostResourceTables::from_parts(
469            ResourceTables {
470                host_table: Some(self.host_table),
471                calls: self.calls,
472                // Note that the unsafety here should be valid given the contract of
473                // `LiftContext::new`.
474                guest: Some(self.instance.as_mut().instance_states()),
475            },
476            self.host_resource_data,
477        )
478    }
479
480    /// See [`HostResourceTables::enter_call`].
481    #[inline]
482    pub fn enter_call(&mut self) {
483        self.resource_tables().enter_call()
484    }
485
486    /// See [`HostResourceTables::exit_call`].
487    #[inline]
488    pub fn exit_call(&mut self) -> Result<()> {
489        self.resource_tables().exit_call()
490    }
491}