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}