wasmtime/runtime/component/func/options.rs
1use crate::component::matching::InstanceType;
2use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables};
3use crate::component::{Instance, ResourceType};
4use crate::prelude::*;
5use crate::runtime::vm::component::{
6 CallContexts, ComponentInstance, HandleTable, InstanceFlags, ResourceTables,
7};
8use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition};
9use crate::store::{StoreId, StoreOpaque};
10use crate::{FuncType, StoreContextMut};
11use alloc::sync::Arc;
12use core::pin::Pin;
13use core::ptr::NonNull;
14use wasmtime_environ::component::{
15 CanonicalOptions, CanonicalOptionsDataModel, ComponentTypes, OptionsIndex, StringEncoding,
16 TypeResourceTableIndex,
17};
18
19/// Runtime representation of canonical ABI options in the component model.
20///
21/// This structure packages up the runtime representation of each option from
22/// memories to reallocs to string encodings. Note that this is a "standalone"
23/// structure which has raw pointers internally. This allows it to be created
24/// out of thin air for a host function import, for example. The `store_id`
25/// field, however, is what is used to pair this set of options with a store
26/// reference to actually use the pointers.
27#[derive(Copy, Clone)]
28pub struct Options {
29 /// The store from which this options originated.
30 store_id: StoreId,
31
32 /// An optional pointer for the memory that this set of options is referring
33 /// to. This option is not required to be specified in the canonical ABI
34 /// hence the `Option`.
35 ///
36 /// Note that this pointer cannot be safely dereferenced unless a store,
37 /// verified with `self.store_id`, has the appropriate borrow available.
38 memory: Option<NonNull<VMMemoryDefinition>>,
39
40 /// Similar to `memory` but corresponds to the `canonical_abi_realloc`
41 /// function.
42 ///
43 /// Safely using this pointer has the same restrictions as `memory` above.
44 realloc: Option<NonNull<VMFuncRef>>,
45
46 /// The encoding used for strings, if found.
47 ///
48 /// This defaults to utf-8 but can be changed if necessary.
49 string_encoding: StringEncoding,
50
51 /// Whether or not this the async option was set when lowering.
52 async_: bool,
53
54 #[cfg(feature = "component-model-async")]
55 callback: Option<NonNull<VMFuncRef>>,
56}
57
58// The `Options` structure stores raw pointers but they're never used unless a
59// `Store` is available so this should be threadsafe and largely inherit the
60// thread-safety story of `Store<T>` itself.
61unsafe impl Send for Options {}
62unsafe impl Sync for Options {}
63
64impl Options {
65 // FIXME(#4311): prevent a ctor where the memory is memory64
66
67 /// Creates a new [`Options`] from the given [`OptionsIndex`] belonging to
68 /// the specified [`Instance`]
69 ///
70 /// # Panics
71 ///
72 /// Panics if `instance` is not owned by `store` or if `index` is not valid
73 /// for `instance`'s component.
74 pub fn new_index(store: &StoreOpaque, instance: Instance, index: OptionsIndex) -> Options {
75 let instance = instance.id().get(store);
76 let CanonicalOptions {
77 string_encoding,
78 async_,
79 callback,
80 ref data_model,
81 ..
82 } = instance.component().env_component().options[index];
83 let (memory, realloc) = match data_model {
84 CanonicalOptionsDataModel::Gc { .. } => (None, None),
85 CanonicalOptionsDataModel::LinearMemory(o) => (o.memory, o.realloc),
86 };
87 let memory = memory.map(|i| NonNull::new(instance.runtime_memory(i)).unwrap());
88 let realloc = realloc.map(|i| instance.runtime_realloc(i));
89 let callback = callback.map(|i| instance.runtime_callback(i));
90 let _ = callback;
91
92 Options {
93 store_id: store.id(),
94 memory,
95 realloc,
96 string_encoding,
97 async_,
98 #[cfg(feature = "component-model-async")]
99 callback,
100 }
101 }
102
103 fn realloc<'a, T>(
104 &self,
105 store: &'a mut StoreContextMut<'_, T>,
106 realloc_ty: &FuncType,
107 old: usize,
108 old_size: usize,
109 old_align: u32,
110 new_size: usize,
111 ) -> Result<(&'a mut [u8], usize)> {
112 self.store_id.assert_belongs_to(store.0.id());
113
114 let realloc = self.realloc.unwrap();
115
116 let params = (
117 u32::try_from(old)?,
118 u32::try_from(old_size)?,
119 old_align,
120 u32::try_from(new_size)?,
121 );
122
123 type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>;
124
125 // Invoke the wasm malloc function using its raw and statically known
126 // signature.
127 let result = unsafe { ReallocFunc::call_raw(store, realloc_ty, realloc, params)? };
128
129 if result % old_align != 0 {
130 bail!("realloc return: result not aligned");
131 }
132 let result = usize::try_from(result)?;
133
134 let memory = self.memory_mut(store.0);
135
136 let result_slice = match memory.get_mut(result..).and_then(|s| s.get_mut(..new_size)) {
137 Some(end) => end,
138 None => bail!("realloc return: beyond end of memory"),
139 };
140
141 Ok((result_slice, result))
142 }
143
144 /// Asserts that this function has an associated memory attached to it and
145 /// then returns the slice of memory tied to the lifetime of the provided
146 /// store.
147 pub fn memory<'a>(&self, store: &'a StoreOpaque) -> &'a [u8] {
148 self.store_id.assert_belongs_to(store.id());
149
150 // The unsafety here is intended to be encapsulated by the two
151 // preceding assertions. Namely we assert that the `store` is the same
152 // as the original store of this `Options`, meaning that we safely have
153 // either a shared reference or a mutable reference (as below) which
154 // means it's safe to view the memory (aka it's not a different store
155 // where our original store is on some other thread or something like
156 // that).
157 //
158 // Additionally the memory itself is asserted to be present as memory
159 // is an optional configuration in canonical ABI options.
160 unsafe {
161 let memory = self.memory.unwrap().as_ref();
162 core::slice::from_raw_parts(memory.base.as_ptr(), memory.current_length())
163 }
164 }
165
166 /// Same as above, just `_mut`
167 pub fn memory_mut<'a>(&self, store: &'a mut StoreOpaque) -> &'a mut [u8] {
168 self.store_id.assert_belongs_to(store.id());
169
170 // See comments in `memory` about the unsafety
171 unsafe {
172 let memory = self.memory.unwrap().as_ref();
173 core::slice::from_raw_parts_mut(memory.base.as_ptr(), memory.current_length())
174 }
175 }
176
177 /// Returns the underlying encoding used for strings in this
178 /// lifting/lowering.
179 pub fn string_encoding(&self) -> StringEncoding {
180 self.string_encoding
181 }
182
183 /// Returns the id of the store that this `Options` is connected to.
184 pub fn store_id(&self) -> StoreId {
185 self.store_id
186 }
187
188 /// Returns whether this lifting or lowering uses the async ABI.
189 pub fn async_(&self) -> bool {
190 self.async_
191 }
192
193 #[cfg(feature = "component-model-async")]
194 pub(crate) fn callback(&self) -> Option<NonNull<VMFuncRef>> {
195 self.callback
196 }
197
198 #[cfg(feature = "component-model-async")]
199 pub(crate) fn memory_raw(&self) -> Option<NonNull<VMMemoryDefinition>> {
200 self.memory
201 }
202}
203
204/// A helper structure which is a "package" of the context used during lowering
205/// values into a component (or storing them into memory).
206///
207/// This type is used by the `Lower` trait extensively and contains any
208/// contextual information necessary related to the context in which the
209/// lowering is happening.
210#[doc(hidden)]
211pub struct LowerContext<'a, T: 'static> {
212 /// Lowering may involve invoking memory allocation functions so part of the
213 /// context here is carrying access to the entire store that wasm is
214 /// executing within. This store serves as proof-of-ability to actually
215 /// execute wasm safely.
216 pub store: StoreContextMut<'a, T>,
217
218 /// Lowering always happens into a function that's been `canon lift`'d or
219 /// `canon lower`'d, both of which specify a set of options for the
220 /// canonical ABI. For example details like string encoding are contained
221 /// here along with which memory pointers are relative to or what the memory
222 /// allocation function is.
223 pub options: &'a Options,
224
225 /// Lowering happens within the context of a component instance and this
226 /// field stores the type information of that component instance. This is
227 /// used for type lookups and general type queries during the
228 /// lifting/lowering process.
229 pub types: &'a ComponentTypes,
230
231 /// Index of the component instance that's being lowered into.
232 instance: Instance,
233
234 /// Whether to allow `options.realloc` to be used when lowering.
235 allow_realloc: bool,
236}
237
238#[doc(hidden)]
239impl<'a, T: 'static> LowerContext<'a, T> {
240 /// Creates a new lowering context from the specified parameters.
241 pub fn new(
242 store: StoreContextMut<'a, T>,
243 options: &'a Options,
244 types: &'a ComponentTypes,
245 instance: Instance,
246 ) -> LowerContext<'a, T> {
247 #[cfg(all(debug_assertions, feature = "component-model-async"))]
248 if store.engine().config().async_support {
249 // Assert that we're running on a fiber, which is necessary in
250 // case we call the guest's realloc function.
251 store.0.with_blocking(|_, _| {});
252 }
253 LowerContext {
254 store,
255 options,
256 types,
257 instance,
258 allow_realloc: true,
259 }
260 }
261
262 /// Like `new`, except disallows use of `options.realloc`.
263 ///
264 /// The returned object will panic if its `realloc` method is called.
265 ///
266 /// This is meant for use when lowering "flat" values (i.e. values which
267 /// require no allocations) into already-allocated memory or into stack
268 /// slots, in which case the lowering may safely be done outside of a fiber
269 /// since there is no need to make any guest calls.
270 #[cfg(feature = "component-model-async")]
271 pub(crate) fn new_without_realloc(
272 store: StoreContextMut<'a, T>,
273 options: &'a Options,
274 types: &'a ComponentTypes,
275 instance: Instance,
276 ) -> LowerContext<'a, T> {
277 LowerContext {
278 store,
279 options,
280 types,
281 instance,
282 allow_realloc: false,
283 }
284 }
285
286 /// Returns the `&ComponentInstance` that's being lowered into.
287 pub fn instance(&self) -> &ComponentInstance {
288 self.instance.id().get(self.store.0)
289 }
290
291 /// Returns the `&mut ComponentInstance` that's being lowered into.
292 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> {
293 self.instance.id().get_mut(self.store.0)
294 }
295
296 /// Returns a view into memory as a mutable slice of bytes.
297 ///
298 /// # Panics
299 ///
300 /// This will panic if memory has not been configured for this lowering
301 /// (e.g. it wasn't present during the specification of canonical options).
302 pub fn as_slice_mut(&mut self) -> &mut [u8] {
303 self.options.memory_mut(self.store.0)
304 }
305
306 /// Invokes the memory allocation function (which is style after `realloc`)
307 /// with the specified parameters.
308 ///
309 /// # Panics
310 ///
311 /// This will panic if realloc hasn't been configured for this lowering via
312 /// its canonical options.
313 pub fn realloc(
314 &mut self,
315 old: usize,
316 old_size: usize,
317 old_align: u32,
318 new_size: usize,
319 ) -> Result<usize> {
320 assert!(self.allow_realloc);
321
322 let realloc_func_ty = Arc::clone(self.instance().component().realloc_func_ty());
323 self.options
324 .realloc(
325 &mut self.store,
326 &realloc_func_ty,
327 old,
328 old_size,
329 old_align,
330 new_size,
331 )
332 .map(|(_, ptr)| ptr)
333 }
334
335 /// Returns a fixed mutable slice of memory `N` bytes large starting at
336 /// offset `N`, panicking on out-of-bounds.
337 ///
338 /// It should be previously verified that `offset` is in-bounds via
339 /// bounds-checks.
340 ///
341 /// # Panics
342 ///
343 /// This will panic if memory has not been configured for this lowering
344 /// (e.g. it wasn't present during the specification of canonical options).
345 pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] {
346 // FIXME: this bounds check shouldn't actually be necessary, all
347 // callers of `ComponentType::store` have already performed a bounds
348 // check so we're guaranteed that `offset..offset+N` is in-bounds. That
349 // being said we at least should do bounds checks in debug mode and
350 // it's not clear to me how to easily structure this so that it's
351 // "statically obvious" the bounds check isn't necessary.
352 //
353 // For now I figure we can leave in this bounds check and if it becomes
354 // an issue we can optimize further later, probably with judicious use
355 // of `unsafe`.
356 self.as_slice_mut()[offset..].first_chunk_mut().unwrap()
357 }
358
359 /// Lowers an `own` resource into the guest, converting the `rep` specified
360 /// into a guest-local index.
361 ///
362 /// The `ty` provided is which table to put this into.
363 pub fn guest_resource_lower_own(
364 &mut self,
365 ty: TypeResourceTableIndex,
366 rep: u32,
367 ) -> Result<u32> {
368 self.resource_tables().guest_resource_lower_own(rep, ty)
369 }
370
371 /// Lowers a `borrow` resource into the guest, converting the `rep` to a
372 /// guest-local index in the `ty` table specified.
373 pub fn guest_resource_lower_borrow(
374 &mut self,
375 ty: TypeResourceTableIndex,
376 rep: u32,
377 ) -> Result<u32> {
378 // Implement `lower_borrow`'s special case here where if a borrow is
379 // inserted into a table owned by the instance which implemented the
380 // original resource then no borrow tracking is employed and instead the
381 // `rep` is returned "raw".
382 //
383 // This check is performed by comparing the owning instance of `ty`
384 // against the owning instance of the resource that `ty` is working
385 // with.
386 if self.instance().resource_owned_by_own_instance(ty) {
387 return Ok(rep);
388 }
389 self.resource_tables().guest_resource_lower_borrow(rep, ty)
390 }
391
392 /// Lifts a host-owned `own` resource at the `idx` specified into the
393 /// representation of that resource.
394 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
395 self.resource_tables().host_resource_lift_own(idx)
396 }
397
398 /// Lifts a host-owned `borrow` resource at the `idx` specified into the
399 /// representation of that resource.
400 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
401 self.resource_tables().host_resource_lift_borrow(idx)
402 }
403
404 /// Lowers a resource into the host-owned table, returning the index it was
405 /// inserted at.
406 ///
407 /// Note that this is a special case for `Resource<T>`. Most of the time a
408 /// host value shouldn't be lowered with a lowering context.
409 pub fn host_resource_lower_own(
410 &mut self,
411 rep: u32,
412 dtor: Option<NonNull<VMFuncRef>>,
413 flags: Option<InstanceFlags>,
414 ) -> Result<HostResourceIndex> {
415 self.resource_tables()
416 .host_resource_lower_own(rep, dtor, flags)
417 }
418
419 /// Returns the underlying resource type for the `ty` table specified.
420 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
421 self.instance_type().resource_type(ty)
422 }
423
424 /// Returns the instance type information corresponding to the instance that
425 /// this context is lowering into.
426 pub fn instance_type(&self) -> InstanceType<'_> {
427 InstanceType::new(self.instance())
428 }
429
430 fn resource_tables(&mut self) -> HostResourceTables<'_> {
431 let (calls, host_table, host_resource_data, instance) = self
432 .store
433 .0
434 .component_resource_state_with_instance(self.instance);
435 HostResourceTables::from_parts(
436 ResourceTables {
437 host_table: Some(host_table),
438 calls,
439 guest: Some(instance.guest_tables()),
440 },
441 host_resource_data,
442 )
443 }
444
445 /// See [`HostResourceTables::enter_call`].
446 #[inline]
447 pub fn enter_call(&mut self) {
448 self.resource_tables().enter_call()
449 }
450
451 /// See [`HostResourceTables::exit_call`].
452 #[inline]
453 pub fn exit_call(&mut self) -> Result<()> {
454 self.resource_tables().exit_call()
455 }
456}
457
458/// Contextual information used when lifting a type from a component into the
459/// host.
460///
461/// This structure is the analogue of `LowerContext` except used during lifting
462/// operations (or loading from memory).
463#[doc(hidden)]
464pub struct LiftContext<'a> {
465 /// Like lowering, lifting always has options configured.
466 pub options: &'a Options,
467
468 /// Instance type information, like with lowering.
469 pub types: &'a Arc<ComponentTypes>,
470
471 memory: Option<&'a [u8]>,
472
473 instance: Pin<&'a mut ComponentInstance>,
474 instance_handle: Instance,
475
476 host_table: &'a mut HandleTable,
477 host_resource_data: &'a mut HostResourceData,
478
479 calls: &'a mut CallContexts,
480}
481
482#[doc(hidden)]
483impl<'a> LiftContext<'a> {
484 /// Creates a new lifting context given the provided context.
485 #[inline]
486 pub fn new(
487 store: &'a mut StoreOpaque,
488 options: &'a Options,
489 instance_handle: Instance,
490 ) -> LiftContext<'a> {
491 // From `&mut StoreOpaque` provided the goal here is to project out
492 // three different disjoint fields owned by the store: memory,
493 // `CallContexts`, and `HandleTable`. There's no native API for that
494 // so it's hacked around a bit. This unsafe pointer cast could be fixed
495 // with more methods in more places, but it doesn't seem worth doing it
496 // at this time.
497 let memory = options
498 .memory
499 .map(|_| options.memory(unsafe { &*(store as *const StoreOpaque) }));
500 let (calls, host_table, host_resource_data, instance) =
501 store.component_resource_state_with_instance(instance_handle);
502 let (component, instance) = instance.component_and_self();
503
504 LiftContext {
505 memory,
506 options,
507 types: component.types(),
508 instance,
509 instance_handle,
510 calls,
511 host_table,
512 host_resource_data,
513 }
514 }
515
516 /// Returns the entire contents of linear memory for this set of lifting
517 /// options.
518 ///
519 /// # Panics
520 ///
521 /// This will panic if memory has not been configured for this lifting
522 /// operation.
523 pub fn memory(&self) -> &'a [u8] {
524 self.memory.unwrap()
525 }
526
527 /// Returns an identifier for the store from which this `LiftContext` was
528 /// created.
529 pub fn store_id(&self) -> StoreId {
530 self.options.store_id
531 }
532
533 /// Returns the component instance that is being lifted from.
534 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> {
535 self.instance.as_mut()
536 }
537 /// Returns the component instance that is being lifted from.
538 pub fn instance_handle(&self) -> Instance {
539 self.instance_handle
540 }
541
542 /// Lifts an `own` resource from the guest at the `idx` specified into its
543 /// representation.
544 ///
545 /// Additionally returns a destructor/instance flags to go along with the
546 /// representation so the host knows how to destroy this resource.
547 pub fn guest_resource_lift_own(
548 &mut self,
549 ty: TypeResourceTableIndex,
550 idx: u32,
551 ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> {
552 let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?;
553 let (dtor, flags) = self.instance.dtor_and_flags(ty);
554 Ok((idx, dtor, flags))
555 }
556
557 /// Lifts a `borrow` resource from the guest at the `idx` specified.
558 pub fn guest_resource_lift_borrow(
559 &mut self,
560 ty: TypeResourceTableIndex,
561 idx: u32,
562 ) -> Result<u32> {
563 self.resource_tables().guest_resource_lift_borrow(idx, ty)
564 }
565
566 /// Lowers a resource into the host-owned table, returning the index it was
567 /// inserted at.
568 pub fn host_resource_lower_own(
569 &mut self,
570 rep: u32,
571 dtor: Option<NonNull<VMFuncRef>>,
572 flags: Option<InstanceFlags>,
573 ) -> Result<HostResourceIndex> {
574 self.resource_tables()
575 .host_resource_lower_own(rep, dtor, flags)
576 }
577
578 /// Lowers a resource into the host-owned table, returning the index it was
579 /// inserted at.
580 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
581 self.resource_tables().host_resource_lower_borrow(rep)
582 }
583
584 /// Returns the underlying type of the resource table specified by `ty`.
585 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
586 self.instance_type().resource_type(ty)
587 }
588
589 /// Returns instance type information for the component instance that is
590 /// being lifted from.
591 pub fn instance_type(&self) -> InstanceType<'_> {
592 InstanceType::new(&self.instance)
593 }
594
595 fn resource_tables(&mut self) -> HostResourceTables<'_> {
596 HostResourceTables::from_parts(
597 ResourceTables {
598 host_table: Some(self.host_table),
599 calls: self.calls,
600 // Note that the unsafety here should be valid given the contract of
601 // `LiftContext::new`.
602 guest: Some(self.instance.as_mut().guest_tables()),
603 },
604 self.host_resource_data,
605 )
606 }
607
608 /// See [`HostResourceTables::enter_call`].
609 #[inline]
610 pub fn enter_call(&mut self) {
611 self.resource_tables().enter_call()
612 }
613
614 /// See [`HostResourceTables::exit_call`].
615 #[inline]
616 pub fn exit_call(&mut self) -> Result<()> {
617 self.resource_tables().exit_call()
618 }
619}