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