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}