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