dusk_wasmtime/runtime/component/component.rs
1use crate::component::matching::InstanceType;
2use crate::component::types;
3use crate::{
4 code::CodeObject, code_memory::CodeMemory, type_registry::TypeCollection, Engine, Module,
5 ResourcesRequired,
6};
7use crate::{FuncType, ValType};
8use anyhow::Result;
9use std::mem;
10use std::ops::Range;
11use std::path::Path;
12use std::ptr::NonNull;
13use std::sync::Arc;
14use wasmtime_environ::component::{
15 AllCallFunc, CompiledComponentInfo, ComponentArtifacts, ComponentTypes, GlobalInitializer,
16 InstantiateModule, StaticModuleIndex, TrampolineIndex, TypeComponentIndex, VMComponentOffsets,
17};
18
19use wasmtime_environ::{FunctionLoc, HostPtr, ObjectKind, PrimaryMap};
20use wasmtime_runtime::component::ComponentRuntimeInfo;
21use wasmtime_runtime::{
22 VMArrayCallFunction, VMFuncRef, VMFunctionBody, VMNativeCallFunction, VMWasmCallFunction,
23};
24
25/// A compiled WebAssembly Component.
26///
27/// This structure represents a compiled component that is ready to be
28/// instantiated. This owns a region of virtual memory which contains executable
29/// code compiled from a WebAssembly binary originally. This is the analog of
30/// [`Module`](crate::Module) in the component embedding API.
31///
32/// A [`Component`] can be turned into an
33/// [`Instance`](crate::component::Instance) through a
34/// [`Linker`](crate::component::Linker). [`Component`]s are safe to share
35/// across threads. The compilation model of a component is the same as that of
36/// [a module](crate::Module) which is to say:
37///
38/// * Compilation happens synchronously during [`Component::new`].
39/// * The result of compilation can be saved into storage with
40/// [`Component::serialize`].
41/// * A previously compiled artifact can be parsed with
42/// [`Component::deserialize`].
43/// * No compilation happens at runtime for a component — everything is done
44/// by the time [`Component::new`] returns.
45///
46/// ## Components and `Clone`
47///
48/// Using `clone` on a `Component` is a cheap operation. It will not create an
49/// entirely new component, but rather just a new reference to the existing
50/// component. In other words it's a shallow copy, not a deep copy.
51///
52/// ## Examples
53///
54/// For example usage see the documentation of [`Module`](crate::Module) as
55/// [`Component`] has the same high-level API.
56#[derive(Clone)]
57pub struct Component {
58 inner: Arc<ComponentInner>,
59}
60
61struct ComponentInner {
62 /// Component type index
63 ty: TypeComponentIndex,
64
65 /// Core wasm modules that the component defined internally, indexed by the
66 /// compile-time-assigned `ModuleUpvarIndex`.
67 static_modules: PrimaryMap<StaticModuleIndex, Module>,
68
69 /// Code-related information such as the compiled artifact, type
70 /// information, etc.
71 ///
72 /// Note that the `Arc` here is used to share this allocation with internal
73 /// modules.
74 code: Arc<CodeObject>,
75
76 /// Metadata produced during compilation.
77 info: CompiledComponentInfo,
78
79 /// A cached handle to the `wasmtime::FuncType` for the canonical ABI's
80 /// `realloc`, to avoid the need to look up types in the registry and take
81 /// locks when calling `realloc` via `TypedFunc::call_raw`.
82 realloc_func_type: Arc<dyn std::any::Any + Send + Sync>,
83}
84
85pub(crate) struct AllCallFuncPointers {
86 pub wasm_call: NonNull<VMWasmCallFunction>,
87 pub array_call: VMArrayCallFunction,
88 pub native_call: NonNull<VMNativeCallFunction>,
89}
90
91impl Component {
92 /// Compiles a new WebAssembly component from the in-memory list of bytes
93 /// provided.
94 ///
95 /// The `bytes` provided can either be the binary or text format of a
96 /// [WebAssembly component]. Note that the text format requires the `wat`
97 /// feature of this crate to be enabled. This API does not support
98 /// streaming compilation.
99 ///
100 /// This function will synchronously validate the entire component,
101 /// including all core modules, and then compile all components, modules,
102 /// etc., found within the provided bytes.
103 ///
104 /// [WebAssembly component]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/Binary.md
105 ///
106 /// # Errors
107 ///
108 /// This function may fail and return an error. Errors may include
109 /// situations such as:
110 ///
111 /// * The binary provided could not be decoded because it's not a valid
112 /// WebAssembly binary
113 /// * The WebAssembly binary may not validate (e.g. contains type errors)
114 /// * Implementation-specific limits were exceeded with a valid binary (for
115 /// example too many locals)
116 /// * The wasm binary may use features that are not enabled in the
117 /// configuration of `engine`
118 /// * If the `wat` feature is enabled and the input is text, then it may be
119 /// rejected if it fails to parse.
120 ///
121 /// The error returned should contain full information about why compilation
122 /// failed.
123 ///
124 /// # Examples
125 ///
126 /// The `new` function can be invoked with a in-memory array of bytes:
127 ///
128 /// ```no_run
129 /// # use wasmtime::*;
130 /// # use wasmtime::component::Component;
131 /// # fn main() -> anyhow::Result<()> {
132 /// # let engine = Engine::default();
133 /// # let wasm_bytes: Vec<u8> = Vec::new();
134 /// let component = Component::new(&engine, &wasm_bytes)?;
135 /// # Ok(())
136 /// # }
137 /// ```
138 ///
139 /// Or you can also pass in a string to be parsed as the wasm text
140 /// format:
141 ///
142 /// ```
143 /// # use wasmtime::*;
144 /// # use wasmtime::component::Component;
145 /// # fn main() -> anyhow::Result<()> {
146 /// # let engine = Engine::default();
147 /// let component = Component::new(&engine, "(component (core module))")?;
148 /// # Ok(())
149 /// # }
150 #[cfg(any(feature = "cranelift", feature = "winch"))]
151 #[cfg_attr(docsrs, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
152 pub fn new(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Component> {
153 crate::CodeBuilder::new(engine)
154 .wasm(bytes.as_ref(), None)?
155 .compile_component()
156 }
157
158 /// Compiles a new WebAssembly component from a wasm file on disk pointed
159 /// to by `file`.
160 ///
161 /// This is a convenience function for reading the contents of `file` on
162 /// disk and then calling [`Component::new`].
163 #[cfg(any(feature = "cranelift", feature = "winch"))]
164 #[cfg_attr(docsrs, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
165 pub fn from_file(engine: &Engine, file: impl AsRef<Path>) -> Result<Component> {
166 crate::CodeBuilder::new(engine)
167 .wasm_file(file.as_ref())?
168 .compile_component()
169 }
170
171 /// Compiles a new WebAssembly component from the in-memory wasm image
172 /// provided.
173 ///
174 /// This function is the same as [`Component::new`] except that it does not
175 /// accept the text format of WebAssembly. Even if the `wat` feature
176 /// is enabled an error will be returned here if `binary` is the text
177 /// format.
178 ///
179 /// For more information on semantics and errors see [`Component::new`].
180 #[cfg(any(feature = "cranelift", feature = "winch"))]
181 #[cfg_attr(docsrs, doc(cfg(any(feature = "cranelift", feature = "winch"))))]
182 pub fn from_binary(engine: &Engine, binary: &[u8]) -> Result<Component> {
183 crate::CodeBuilder::new(engine)
184 .wasm(binary, None)?
185 .wat(false)?
186 .compile_component()
187 }
188
189 /// Same as [`Module::deserialize`], but for components.
190 ///
191 /// Note that the bytes referenced here must contain contents previously
192 /// produced by [`Engine::precompile_component`] or
193 /// [`Component::serialize`].
194 ///
195 /// For more information see the [`Module::deserialize`] method.
196 ///
197 /// # Unsafety
198 ///
199 /// The unsafety of this method is the same as that of the
200 /// [`Module::deserialize`] method.
201 ///
202 /// [`Module::deserialize`]: crate::Module::deserialize
203 pub unsafe fn deserialize(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Component> {
204 let code = engine.load_code_bytes(bytes.as_ref(), ObjectKind::Component)?;
205 Component::from_parts(engine, code, None)
206 }
207
208 /// Same as [`Module::deserialize_file`], but for components.
209 ///
210 /// Note that the file referenced here must contain contents previously
211 /// produced by [`Engine::precompile_component`] or
212 /// [`Component::serialize`].
213 ///
214 /// For more information see the [`Module::deserialize_file`] method.
215 ///
216 /// # Unsafety
217 ///
218 /// The unsafety of this method is the same as that of the
219 /// [`Module::deserialize_file`] method.
220 ///
221 /// [`Module::deserialize_file`]: crate::Module::deserialize_file
222 pub unsafe fn deserialize_file(engine: &Engine, path: impl AsRef<Path>) -> Result<Component> {
223 let code = engine.load_code_file(path.as_ref(), ObjectKind::Component)?;
224 Component::from_parts(engine, code, None)
225 }
226
227 /// Returns the type of this component as a [`types::Component`].
228 ///
229 /// This method enables runtime introspection of the type of a component
230 /// before instantiation, if necessary.
231 ///
232 /// ## Component types and Resources
233 ///
234 /// An important point to note here is that the precise type of imports and
235 /// exports of a component change when it is instantiated with respect to
236 /// resources. For example a [`Component`] represents an un-instantiated
237 /// component meaning that its imported resources are represeted as abstract
238 /// resource types. These abstract types are not equal to any other
239 /// component's types.
240 ///
241 /// For example:
242 ///
243 /// ```
244 /// # use wasmtime::Engine;
245 /// # use wasmtime::component::Component;
246 /// # use wasmtime::component::types::ComponentItem;
247 /// # fn main() -> wasmtime::Result<()> {
248 /// # let engine = Engine::default();
249 /// let a = Component::new(&engine, r#"
250 /// (component (import "x" (type (sub resource))))
251 /// "#)?;
252 /// let b = Component::new(&engine, r#"
253 /// (component (import "x" (type (sub resource))))
254 /// "#)?;
255 ///
256 /// let (_, a_ty) = a.component_type().imports(&engine).next().unwrap();
257 /// let (_, b_ty) = b.component_type().imports(&engine).next().unwrap();
258 ///
259 /// let a_ty = match a_ty {
260 /// ComponentItem::Resource(ty) => ty,
261 /// _ => unreachable!(),
262 /// };
263 /// let b_ty = match b_ty {
264 /// ComponentItem::Resource(ty) => ty,
265 /// _ => unreachable!(),
266 /// };
267 /// assert!(a_ty != b_ty);
268 /// # Ok(())
269 /// # }
270 /// ```
271 ///
272 /// Additionally, however, these abstract types are "substituted" during
273 /// instantiation meaning that a component type will appear to have changed
274 /// once it is instantiated.
275 ///
276 /// ```
277 /// # use wasmtime::{Engine, Store};
278 /// # use wasmtime::component::{Component, Linker, ResourceType};
279 /// # use wasmtime::component::types::ComponentItem;
280 /// # fn main() -> wasmtime::Result<()> {
281 /// # let engine = Engine::default();
282 /// // Here this component imports a resource and then exports it as-is
283 /// // which means that the export is equal to the import.
284 /// let a = Component::new(&engine, r#"
285 /// (component
286 /// (import "x" (type $x (sub resource)))
287 /// (export "x" (type $x))
288 /// )
289 /// "#)?;
290 ///
291 /// let (_, import) = a.component_type().imports(&engine).next().unwrap();
292 /// let (_, export) = a.component_type().exports(&engine).next().unwrap();
293 ///
294 /// let import = match import {
295 /// ComponentItem::Resource(ty) => ty,
296 /// _ => unreachable!(),
297 /// };
298 /// let export = match export {
299 /// ComponentItem::Resource(ty) => ty,
300 /// _ => unreachable!(),
301 /// };
302 /// assert_eq!(import, export);
303 ///
304 /// // However after instantiation the resource type "changes"
305 /// let mut store = Store::new(&engine, ());
306 /// let mut linker = Linker::new(&engine);
307 /// linker.root().resource("x", ResourceType::host::<()>(), |_, _| Ok(()))?;
308 /// let instance = linker.instantiate(&mut store, &a)?;
309 /// let instance_ty = instance.exports(&mut store).root().resource("x").unwrap();
310 ///
311 /// // Here `instance_ty` is not the same as either `import` or `export`,
312 /// // but it is equal to what we provided as an import.
313 /// assert!(instance_ty != import);
314 /// assert!(instance_ty != export);
315 /// assert!(instance_ty == ResourceType::host::<()>());
316 /// # Ok(())
317 /// # }
318 /// ```
319 ///
320 /// Finally, each instantiation of an exported resource from a component is
321 /// considered "fresh" for all instantiations meaning that different
322 /// instantiations will have different exported resource types:
323 ///
324 /// ```
325 /// # use wasmtime::{Engine, Store};
326 /// # use wasmtime::component::{Component, Linker};
327 /// # fn main() -> wasmtime::Result<()> {
328 /// # let engine = Engine::default();
329 /// let a = Component::new(&engine, r#"
330 /// (component
331 /// (type $x (resource (rep i32)))
332 /// (export "x" (type $x))
333 /// )
334 /// "#)?;
335 ///
336 /// let mut store = Store::new(&engine, ());
337 /// let linker = Linker::new(&engine);
338 /// let instance1 = linker.instantiate(&mut store, &a)?;
339 /// let instance2 = linker.instantiate(&mut store, &a)?;
340 ///
341 /// let x1 = instance1.exports(&mut store).root().resource("x").unwrap();
342 /// let x2 = instance2.exports(&mut store).root().resource("x").unwrap();
343 ///
344 /// // Despite these two resources being the same export of the same
345 /// // component they come from two different instances meaning that their
346 /// // types will be unique.
347 /// assert!(x1 != x2);
348 /// # Ok(())
349 /// # }
350 /// ```
351 pub fn component_type(&self) -> types::Component {
352 let resources = Arc::new(PrimaryMap::new());
353 types::Component::from(
354 self.inner.ty,
355 &InstanceType {
356 types: self.types(),
357 resources: &resources,
358 },
359 )
360 }
361
362 /// Final assembly step for a component from its in-memory representation.
363 ///
364 /// If the `artifacts` are specified as `None` here then they will be
365 /// deserialized from `code_memory`.
366 pub(crate) fn from_parts(
367 engine: &Engine,
368 code_memory: Arc<CodeMemory>,
369 artifacts: Option<ComponentArtifacts>,
370 ) -> Result<Component> {
371 let ComponentArtifacts {
372 ty,
373 info,
374 types,
375 static_modules,
376 } = match artifacts {
377 Some(artifacts) => artifacts,
378 None => bincode::deserialize(code_memory.wasmtime_info())?,
379 };
380
381 // Validate that the component can be used with the current instance
382 // allocator.
383 engine.allocator().validate_component(
384 &info.component,
385 &VMComponentOffsets::new(HostPtr, &info.component),
386 &|module_index| &static_modules[module_index].module,
387 )?;
388
389 // Create a signature registration with the `Engine` for all trampolines
390 // and core wasm types found within this component, both for the
391 // component and for all included core wasm modules.
392 let signatures = TypeCollection::new_for_module(engine, types.module_types());
393
394 // Assemble the `CodeObject` artifact which is shared by all core wasm
395 // modules as well as the final component.
396 let types = Arc::new(types);
397 let code = Arc::new(CodeObject::new(code_memory, signatures, types.into()));
398
399 // Convert all information about static core wasm modules into actual
400 // `Module` instances by converting each `CompiledModuleInfo`, the
401 // `types` type information, and the code memory to a runtime object.
402 let static_modules = static_modules
403 .into_iter()
404 .map(|(_, info)| Module::from_parts_raw(engine, code.clone(), info, false))
405 .collect::<Result<_>>()?;
406
407 let realloc_func_type = Arc::new(FuncType::new(
408 engine,
409 [ValType::I32, ValType::I32, ValType::I32, ValType::I32],
410 [ValType::I32],
411 )) as _;
412
413 Ok(Component {
414 inner: Arc::new(ComponentInner {
415 ty,
416 static_modules,
417 code,
418 info,
419 realloc_func_type,
420 }),
421 })
422 }
423
424 pub(crate) fn ty(&self) -> TypeComponentIndex {
425 self.inner.ty
426 }
427
428 pub(crate) fn env_component(&self) -> &wasmtime_environ::component::Component {
429 &self.inner.info.component
430 }
431
432 pub(crate) fn static_module(&self, idx: StaticModuleIndex) -> &Module {
433 &self.inner.static_modules[idx]
434 }
435
436 #[inline]
437 pub(crate) fn types(&self) -> &Arc<ComponentTypes> {
438 self.inner.component_types()
439 }
440
441 pub(crate) fn signatures(&self) -> &TypeCollection {
442 self.inner.code.signatures()
443 }
444
445 pub(crate) fn text(&self) -> &[u8] {
446 self.inner.code.code_memory().text()
447 }
448
449 pub(crate) fn trampoline_ptrs(&self, index: TrampolineIndex) -> AllCallFuncPointers {
450 let AllCallFunc {
451 wasm_call,
452 array_call,
453 native_call,
454 } = &self.inner.info.trampolines[index];
455 AllCallFuncPointers {
456 wasm_call: self.func(wasm_call).cast(),
457 array_call: unsafe {
458 mem::transmute::<NonNull<VMFunctionBody>, VMArrayCallFunction>(
459 self.func(array_call),
460 )
461 },
462 native_call: self.func(native_call).cast(),
463 }
464 }
465
466 fn func(&self, loc: &FunctionLoc) -> NonNull<VMFunctionBody> {
467 let text = self.text();
468 let trampoline = &text[loc.start as usize..][..loc.length as usize];
469 NonNull::new(trampoline.as_ptr() as *mut VMFunctionBody).unwrap()
470 }
471
472 pub(crate) fn code_object(&self) -> &Arc<CodeObject> {
473 &self.inner.code
474 }
475
476 /// Same as [`Module::serialize`], except for a component.
477 ///
478 /// Note that the artifact produced here must be passed to
479 /// [`Component::deserialize`] and is not compatible for use with
480 /// [`Module`].
481 ///
482 /// [`Module::serialize`]: crate::Module::serialize
483 /// [`Module`]: crate::Module
484 pub fn serialize(&self) -> Result<Vec<u8>> {
485 Ok(self.code_object().code_memory().mmap().to_vec())
486 }
487
488 pub(crate) fn runtime_info(&self) -> Arc<dyn ComponentRuntimeInfo> {
489 self.inner.clone()
490 }
491
492 /// Creates a new `VMFuncRef` with all fields filled out for the destructor
493 /// specified.
494 ///
495 /// The `dtor`'s own `VMFuncRef` won't have `wasm_call` filled out but this
496 /// component may have `resource_drop_wasm_to_native_trampoline` filled out
497 /// if necessary in which case it's filled in here.
498 pub(crate) fn resource_drop_func_ref(&self, dtor: &crate::func::HostFunc) -> VMFuncRef {
499 // Host functions never have their `wasm_call` filled in at this time.
500 assert!(dtor.func_ref().wasm_call.is_none());
501
502 // Note that if `resource_drop_wasm_to_native_trampoline` is not present
503 // then this can't be called by the component, so it's ok to leave it
504 // blank.
505 let wasm_call = self
506 .inner
507 .info
508 .resource_drop_wasm_to_native_trampoline
509 .as_ref()
510 .map(|i| self.func(i).cast());
511 VMFuncRef {
512 wasm_call,
513 ..*dtor.func_ref()
514 }
515 }
516
517 /// Returns a summary of the resources required to instantiate this
518 /// [`Component`][crate::component::Component].
519 ///
520 /// Note that when a component imports and instantiates another component or
521 /// core module, we cannot determine ahead of time how many resources
522 /// instantiating this component will require, and therefore this method
523 /// will return `None` in these scenarios.
524 ///
525 /// Potential uses of the returned information:
526 ///
527 /// * Determining whether your pooling allocator configuration supports
528 /// instantiating this component.
529 ///
530 /// * Deciding how many of which `Component` you want to instantiate within
531 /// a fixed amount of resources, e.g. determining whether to create 5
532 /// instances of component X or 10 instances of component Y.
533 ///
534 /// # Example
535 ///
536 /// ```
537 /// # fn main() -> wasmtime::Result<()> {
538 /// use wasmtime::{Config, Engine, component::Component};
539 ///
540 /// let mut config = Config::new();
541 /// config.wasm_multi_memory(true);
542 /// config.wasm_component_model(true);
543 /// let engine = Engine::new(&config)?;
544 ///
545 /// let component = Component::new(&engine, &r#"
546 /// (component
547 /// ;; Define a core module that uses two memories.
548 /// (core module $m
549 /// (memory 1)
550 /// (memory 6)
551 /// )
552 ///
553 /// ;; Instantiate that core module three times.
554 /// (core instance $i1 (instantiate (module $m)))
555 /// (core instance $i2 (instantiate (module $m)))
556 /// (core instance $i3 (instantiate (module $m)))
557 /// )
558 /// "#)?;
559 ///
560 /// let resources = component.resources_required()
561 /// .expect("this component does not import any core modules or instances");
562 ///
563 /// // Instantiating the component will require allocating two memories per
564 /// // core instance, and there are three instances, so six total memories.
565 /// assert_eq!(resources.num_memories, 6);
566 /// assert_eq!(resources.max_initial_memory_size, Some(6));
567 ///
568 /// // The component doesn't need any tables.
569 /// assert_eq!(resources.num_tables, 0);
570 /// assert_eq!(resources.max_initial_table_size, None);
571 /// # Ok(()) }
572 /// ```
573 pub fn resources_required(&self) -> Option<ResourcesRequired> {
574 let mut resources = ResourcesRequired {
575 num_memories: 0,
576 max_initial_memory_size: None,
577 num_tables: 0,
578 max_initial_table_size: None,
579 };
580 for init in &self.env_component().initializers {
581 match init {
582 GlobalInitializer::InstantiateModule(inst) => match inst {
583 InstantiateModule::Static(index, _) => {
584 let module = self.static_module(*index);
585 resources.add(&module.resources_required());
586 }
587 InstantiateModule::Import(_, _) => {
588 // We can't statically determine the resources required
589 // to instantiate this component.
590 return None;
591 }
592 },
593 GlobalInitializer::LowerImport { .. }
594 | GlobalInitializer::ExtractMemory(_)
595 | GlobalInitializer::ExtractRealloc(_)
596 | GlobalInitializer::ExtractPostReturn(_)
597 | GlobalInitializer::Resource(_) => {}
598 }
599 }
600 Some(resources)
601 }
602
603 /// Returns the range, in the host's address space, that this module's
604 /// compiled code resides at.
605 ///
606 /// For more information see
607 /// [`Module;:image_range`](crate::Module::image_range).
608 pub fn image_range(&self) -> Range<*const u8> {
609 self.inner.code.code_memory().mmap().image_range()
610 }
611}
612
613impl ComponentRuntimeInfo for ComponentInner {
614 fn component(&self) -> &wasmtime_environ::component::Component {
615 &self.info.component
616 }
617
618 fn component_types(&self) -> &Arc<ComponentTypes> {
619 match self.code.types() {
620 crate::code::Types::Component(types) => types,
621 // The only creator of a `Component` is itself which uses the other
622 // variant, so this shouldn't be possible.
623 crate::code::Types::Module(_) => unreachable!(),
624 }
625 }
626
627 fn realloc_func_type(&self) -> &Arc<dyn std::any::Any + Send + Sync> {
628 &self.realloc_func_type
629 }
630}
631
632#[cfg(test)]
633mod tests {
634 use crate::component::Component;
635 use crate::{Config, Engine};
636 use wasmtime_environ::MemoryInitialization;
637
638 #[test]
639 fn cow_on_by_default() {
640 let mut config = Config::new();
641 config.wasm_component_model(true);
642 let engine = Engine::new(&config).unwrap();
643 let component = Component::new(
644 &engine,
645 r#"
646 (component
647 (core module
648 (memory 1)
649 (data (i32.const 100) "abcd")
650 )
651 )
652 "#,
653 )
654 .unwrap();
655
656 for (_, module) in component.inner.static_modules.iter() {
657 let init = &module.env_module().memory_initialization;
658 assert!(matches!(init, MemoryInitialization::Static { .. }));
659 }
660 }
661}