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}