1 use crate::StoreContextMut;
2 use crate::component::concurrent::ConcurrentState;
3 use crate::component::matching::InstanceType;
4 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables};
5 use crate::component::{Instance, ResourceType, RuntimeInstance};
6 use crate::prelude::*;
7 use crate::runtime::vm::VMFuncRef;
8 use crate::runtime::vm::component::{CallContexts, ComponentInstance, HandleTable, ResourceTables};
9 use crate::store::{StoreId, StoreOpaque};
10 use alloc::sync::Arc;
11 use core::pin::Pin;
12 use core::ptr::NonNull;
13 use 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)]
25 pub 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)]
53 impl<'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)]
312 pub 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: Option<&'a mut ConcurrentState>,
335 }
336 
337 #[doc(hidden)]
338 impl<'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.as_deref_mut().unwrap()
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 }
492