use crate::component::*; use crate::{ExportKind, Module, NameMap, RawSection, ValType}; use alloc::format; use alloc::vec::Vec; use core::mem; /// Convenience type to build a component incrementally and automatically keep /// track of index spaces. /// /// This type is intended to be a wrapper around the [`Component`] encoding type /// which is useful for building it up incrementally over time. This type will /// automatically collect definitions into sections and reports the index of all /// items added by keeping track of indices internally. #[derive(Debug, Default)] pub struct ComponentBuilder { /// The binary component that's being built. component: Component, /// The last section which was appended to during encoding. This type is /// generated by the `section_accessors` macro below. /// /// When something is encoded this is used if it matches the kind of item /// being encoded, otherwise it's "flushed" to the output component and a /// new section is started. last_section: LastSection, // Core index spaces core_modules: Namespace, core_funcs: Namespace, core_types: Namespace, core_memories: Namespace, core_tables: Namespace, core_instances: Namespace, core_tags: Namespace, core_globals: Namespace, // Component index spaces funcs: Namespace, instances: Namespace, types: Namespace, components: Namespace, values: Namespace, } impl ComponentBuilder { /// Returns the current number of core modules. pub fn core_module_count(&self) -> u32 { self.core_modules.count } /// Returns the current number of core funcs. pub fn core_func_count(&self) -> u32 { self.core_funcs.count } /// Returns the current number of core types. pub fn core_type_count(&self) -> u32 { self.core_types.count } /// Returns the current number of core memories. pub fn core_memory_count(&self) -> u32 { self.core_memories.count } /// Returns the current number of core tables. pub fn core_table_count(&self) -> u32 { self.core_tables.count } /// Returns the current number of core instances. pub fn core_instance_count(&self) -> u32 { self.core_instances.count } /// Returns the current number of core tags. pub fn core_tag_count(&self) -> u32 { self.core_tags.count } /// Returns the current number of core globals. pub fn core_global_count(&self) -> u32 { self.core_globals.count } /// Returns the current number of component funcs. pub fn func_count(&self) -> u32 { self.funcs.count } /// Returns the current number of component instances. pub fn instance_count(&self) -> u32 { self.instances.count } /// Returns the current number of component values. pub fn value_count(&self) -> u32 { self.values.count } /// Returns the current number of components. pub fn component_count(&self) -> u32 { self.components.count } /// Returns the current number of component types. pub fn type_count(&self) -> u32 { self.types.count } /// Add all debug names registered to the component. pub fn append_names(&mut self) { let mut names = ComponentNameSection::new(); if !self.core_funcs.names.is_empty() { names.core_funcs(&self.core_funcs.names); } if !self.core_tables.names.is_empty() { names.core_tables(&self.core_tables.names); } if !self.core_memories.names.is_empty() { names.core_memories(&self.core_memories.names); } if !self.core_globals.names.is_empty() { names.core_globals(&self.core_globals.names); } if !self.core_tags.names.is_empty() { names.core_tags(&self.core_tags.names); } if !self.core_types.names.is_empty() { names.core_types(&self.core_types.names); } if !self.core_modules.names.is_empty() { names.core_modules(&self.core_modules.names); } if !self.core_instances.names.is_empty() { names.core_instances(&self.core_instances.names); } if !self.instances.names.is_empty() { names.instances(&self.instances.names); } if !self.funcs.names.is_empty() { names.funcs(&self.funcs.names); } if !self.types.names.is_empty() { names.types(&self.types.names); } if !self.components.names.is_empty() { names.components(&self.components.names); } if !self.instances.names.is_empty() { names.instances(&self.instances.names); } if !names.is_empty() { self.custom_section(&names.as_custom()); } } /// Completes this component and returns the binary encoding of the entire /// component. pub fn finish(mut self) -> Vec { self.flush(); self.component.finish() } /// Encodes a core wasm `Module` into this component, returning its index. pub fn core_module(&mut self, debug_name: Option<&str>, module: &Module) -> u32 { self.flush(); self.component.section(&ModuleSection(module)); self.core_modules.add(debug_name) } /// Encodes a core wasm `module` into this component, returning its index. pub fn core_module_raw(&mut self, debug_name: Option<&str>, module: &[u8]) -> u32 { self.flush(); self.component.section(&RawSection { id: ComponentSectionId::CoreModule.into(), data: module, }); self.core_modules.add(debug_name) } /// Instantiates a core wasm module at `module_index` with the `args` /// provided. /// /// Returns the index of the core wasm instance created. pub fn core_instantiate<'a, A>( &mut self, debug_name: Option<&str>, module_index: u32, args: A, ) -> u32 where A: IntoIterator, A::IntoIter: ExactSizeIterator, { self.instances().instantiate(module_index, args); self.core_instances.add(debug_name) } /// Creates a new core wasm instance from the `exports` provided. /// /// Returns the index of the core wasm instance created. pub fn core_instantiate_exports<'a, E>(&mut self, debug_name: Option<&str>, exports: E) -> u32 where E: IntoIterator, E::IntoIter: ExactSizeIterator, { self.instances().export_items(exports); self.core_instances.add(debug_name) } /// Creates a new aliased item where the core `instance` specified has its /// export `name` aliased out with the `kind` specified. /// /// Returns the index of the item created. pub fn core_alias_export( &mut self, debug_name: Option<&str>, instance: u32, name: &str, kind: ExportKind, ) -> u32 { self.alias( debug_name, Alias::CoreInstanceExport { instance, kind, name, }, ) } /// Adds a new alias to this component pub fn alias(&mut self, debug_name: Option<&str>, alias: Alias<'_>) -> u32 { self.aliases().alias(alias); match alias { Alias::InstanceExport { kind, .. } => self.inc_kind(debug_name, kind), Alias::CoreInstanceExport { kind, .. } => self.inc_core_kind(debug_name, kind), Alias::Outer { kind: ComponentOuterAliasKind::Type, .. } => self.types.add(debug_name), Alias::Outer { kind: ComponentOuterAliasKind::CoreModule, .. } => self.core_modules.add(debug_name), Alias::Outer { kind: ComponentOuterAliasKind::Component, .. } => self.components.add(debug_name), Alias::Outer { kind: ComponentOuterAliasKind::CoreType, .. } => self.core_types.add(debug_name), } } /// Creates an alias to a previous component instance's exported item. /// /// The `instance` provided is the instance to access and the `name` is the /// item to access. /// /// Returns the index of the new item defined. pub fn alias_export(&mut self, instance: u32, name: &str, kind: ComponentExportKind) -> u32 { self.alias( Some(name), Alias::InstanceExport { instance, kind, name, }, ) } fn inc_kind(&mut self, debug_name: Option<&str>, kind: ComponentExportKind) -> u32 { match kind { ComponentExportKind::Func => self.funcs.add(debug_name), ComponentExportKind::Module => self.core_modules.add(debug_name), ComponentExportKind::Type => self.types.add(debug_name), ComponentExportKind::Component => self.components.add(debug_name), ComponentExportKind::Instance => self.instances.add(debug_name), ComponentExportKind::Value => self.values.add(debug_name), } } fn inc_core_kind(&mut self, debug_name: Option<&str>, kind: ExportKind) -> u32 { match kind { ExportKind::Func => self.core_funcs.add(debug_name), ExportKind::Table => self.core_tables.add(debug_name), ExportKind::Memory => self.core_memories.add(debug_name), ExportKind::Global => self.core_globals.add(debug_name), ExportKind::Tag => self.core_tags.add(debug_name), } } /// Lowers the `func_index` component function into a core wasm function /// using the `options` provided. /// /// Returns the index of the core wasm function created. pub fn lower_func(&mut self, debug_name: Option<&str>, func_index: u32, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions().lower(func_index, options); self.core_funcs.add(debug_name) } /// Lifts the core wasm `core_func_index` function with the component /// function type `type_index` and `options`. /// /// Returns the index of the component function created. pub fn lift_func( &mut self, debug_name: Option<&str>, core_func_index: u32, type_index: u32, options: O, ) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions() .lift(core_func_index, type_index, options); self.funcs.add(debug_name) } /// Imports a new item into this component with the `name` and `ty` specified. pub fn import(&mut self, name: &str, ty: ComponentTypeRef) -> u32 { let ret = match &ty { ComponentTypeRef::Instance(_) => self.instances.add(Some(name)), ComponentTypeRef::Func(_) => self.funcs.add(Some(name)), ComponentTypeRef::Type(..) => self.types.add(Some(name)), ComponentTypeRef::Component(_) => self.components.add(Some(name)), ComponentTypeRef::Module(_) => self.core_modules.add(Some(name)), ComponentTypeRef::Value(_) => self.values.add(Some(name)), }; self.imports().import(name, ty); ret } /// Exports a new item from this component with the `name` and `kind` /// specified. /// /// The `idx` is the item to export and the `ty` is an optional type to /// ascribe to the export. pub fn export( &mut self, name: &str, kind: ComponentExportKind, idx: u32, ty: Option, ) -> u32 { self.exports().export(name, kind, idx, ty); self.inc_kind(Some(name), kind) } /// Creates a new encoder for the next core type in this component. pub fn core_type(&mut self, debug_name: Option<&str>) -> (u32, ComponentCoreTypeEncoder<'_>) { (self.core_types.add(debug_name), self.core_types().ty()) } /// Creates a new encoder for the next type in this component. pub fn ty(&mut self, debug_name: Option<&str>) -> (u32, ComponentTypeEncoder<'_>) { (self.types.add(debug_name), self.types().ty()) } /// Creates a new instance type within this component. pub fn type_instance(&mut self, debug_name: Option<&str>, ty: &InstanceType) -> u32 { self.types().instance(ty); self.types.add(debug_name) } /// Creates a new component type within this component. pub fn type_component(&mut self, debug_name: Option<&str>, ty: &ComponentType) -> u32 { self.types().component(ty); self.types.add(debug_name) } /// Creates a new defined component type within this component. pub fn type_defined( &mut self, debug_name: Option<&str>, ) -> (u32, ComponentDefinedTypeEncoder<'_>) { (self.types.add(debug_name), self.types().defined_type()) } /// Creates a new component function type within this component. pub fn type_function( &mut self, debug_name: Option<&str>, ) -> (u32, ComponentFuncTypeEncoder<'_>) { (self.types.add(debug_name), self.types().function()) } /// Declares a new resource type within this component. pub fn type_resource( &mut self, debug_name: Option<&str>, rep: ValType, dtor: Option, ) -> u32 { self.types().resource(rep, dtor); self.types.add(debug_name) } /// Defines a new subcomponent of this component. pub fn component(&mut self, debug_name: Option<&str>, mut builder: ComponentBuilder) -> u32 { builder.flush(); self.flush(); self.component .section(&NestedComponentSection(&builder.component)); self.components.add(debug_name) } /// Defines a new subcomponent of this component. pub fn component_raw(&mut self, debug_name: Option<&str>, data: &[u8]) -> u32 { let raw_section = RawSection { id: ComponentSectionId::Component.into(), data, }; self.flush(); self.component.section(&raw_section); self.components.add(debug_name) } /// Instantiates the `component_index` specified with the `args` specified. pub fn instantiate( &mut self, debug_name: Option<&str>, component_index: u32, args: A, ) -> u32 where A: IntoIterator, A::IntoIter: ExactSizeIterator, S: AsRef, { self.component_instances() .instantiate(component_index, args); self.instances.add(debug_name) } /// Declares a new `resource.drop` intrinsic. pub fn resource_drop(&mut self, ty: u32) -> u32 { self.canonical_functions().resource_drop(ty); self.core_funcs.add(Some("resource.drop")) } /// Declares a new `resource.drop` intrinsic. pub fn resource_drop_async(&mut self, ty: u32) -> u32 { self.canonical_functions().resource_drop_async(ty); self.core_funcs.add(Some("resource.drop async")) } /// Declares a new `resource.new` intrinsic. pub fn resource_new(&mut self, ty: u32) -> u32 { self.canonical_functions().resource_new(ty); self.core_funcs.add(Some("resource.new")) } /// Declares a new `resource.rep` intrinsic. pub fn resource_rep(&mut self, ty: u32) -> u32 { self.canonical_functions().resource_rep(ty); self.core_funcs.add(Some("resource.rep")) } /// Declares a new `thread.spawn_ref` intrinsic. pub fn thread_spawn_ref(&mut self, ty: u32) -> u32 { self.canonical_functions().thread_spawn_ref(ty); self.core_funcs.add(Some("thread.spawn-ref")) } /// Declares a new `thread.available_parallelism` intrinsic. pub fn thread_available_parallelism(&mut self) -> u32 { self.canonical_functions().thread_available_parallelism(); self.core_funcs.add(Some("thread.available-parallelism")) } /// Declares a new `backpressure.inc` intrinsic. pub fn backpressure_inc(&mut self) -> u32 { self.canonical_functions().backpressure_inc(); self.core_funcs.add(Some("backpressure.inc")) } /// Declares a new `backpressure.dec` intrinsic. pub fn backpressure_dec(&mut self) -> u32 { self.canonical_functions().backpressure_dec(); self.core_funcs.add(Some("backpressure.dec")) } /// Declares a new `task.return` intrinsic. pub fn task_return(&mut self, ty: Option, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions().task_return(ty, options); self.core_funcs.add(Some("task.return")) } /// Declares a new `task.cancel` intrinsic. pub fn task_cancel(&mut self) -> u32 { self.canonical_functions().task_cancel(); self.core_funcs.add(Some("task.cancel")) } /// Declares a new `context.get` intrinsic. pub fn context_get(&mut self, i: u32) -> u32 { self.canonical_functions().context_get(i); self.core_funcs.add(Some(&format!("context.get {i}"))) } /// Declares a new `context.set` intrinsic. pub fn context_set(&mut self, i: u32) -> u32 { self.canonical_functions().context_set(i); self.core_funcs.add(Some(&format!("context.set {i}"))) } /// Declares a new `thread.yield` intrinsic. pub fn thread_yield(&mut self, cancellable: bool) -> u32 { self.canonical_functions().thread_yield(cancellable); self.core_funcs.add(Some("thread.yield")) } /// Declares a new `subtask.drop` intrinsic. pub fn subtask_drop(&mut self) -> u32 { self.canonical_functions().subtask_drop(); self.core_funcs.add(Some("subtask.drop")) } /// Declares a new `subtask.cancel` intrinsic. pub fn subtask_cancel(&mut self, async_: bool) -> u32 { self.canonical_functions().subtask_cancel(async_); self.core_funcs.add(Some("subtask.cancel")) } /// Declares a new `stream.new` intrinsic. pub fn stream_new(&mut self, ty: u32) -> u32 { self.canonical_functions().stream_new(ty); self.core_funcs.add(Some("stream.new")) } /// Declares a new `stream.read` intrinsic. pub fn stream_read(&mut self, ty: u32, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions().stream_read(ty, options); self.core_funcs.add(Some("stream.read")) } /// Declares a new `stream.write` intrinsic. pub fn stream_write(&mut self, ty: u32, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions().stream_write(ty, options); self.core_funcs.add(Some("stream.write")) } /// Declares a new `stream.cancel-read` intrinsic. pub fn stream_cancel_read(&mut self, ty: u32, async_: bool) -> u32 { self.canonical_functions().stream_cancel_read(ty, async_); self.core_funcs.add(Some("stream.cancel-read")) } /// Declares a new `stream.cancel-write` intrinsic. pub fn stream_cancel_write(&mut self, ty: u32, async_: bool) -> u32 { self.canonical_functions().stream_cancel_write(ty, async_); self.core_funcs.add(Some("stream.cancel-write")) } /// Declares a new `stream.drop-readable` intrinsic. pub fn stream_drop_readable(&mut self, ty: u32) -> u32 { self.canonical_functions().stream_drop_readable(ty); self.core_funcs.add(Some("stream.drop-readable")) } /// Declares a new `stream.drop-writable` intrinsic. pub fn stream_drop_writable(&mut self, ty: u32) -> u32 { self.canonical_functions().stream_drop_writable(ty); self.core_funcs.add(Some("stream.drop-writable")) } /// Declares a new `future.new` intrinsic. pub fn future_new(&mut self, ty: u32) -> u32 { self.canonical_functions().future_new(ty); self.core_funcs.add(Some("future.new")) } /// Declares a new `future.read` intrinsic. pub fn future_read(&mut self, ty: u32, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions().future_read(ty, options); self.core_funcs.add(Some("future.read")) } /// Declares a new `future.write` intrinsic. pub fn future_write(&mut self, ty: u32, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions().future_write(ty, options); self.core_funcs.add(Some("future.write")) } /// Declares a new `future.cancel-read` intrinsic. pub fn future_cancel_read(&mut self, ty: u32, async_: bool) -> u32 { self.canonical_functions().future_cancel_read(ty, async_); self.core_funcs.add(Some("future.cancel-read")) } /// Declares a new `future.cancel-write` intrinsic. pub fn future_cancel_write(&mut self, ty: u32, async_: bool) -> u32 { self.canonical_functions().future_cancel_write(ty, async_); self.core_funcs.add(Some("future.cancel-write")) } /// Declares a new `future.drop-readable` intrinsic. pub fn future_drop_readable(&mut self, ty: u32) -> u32 { self.canonical_functions().future_drop_readable(ty); self.core_funcs.add(Some("future.drop-readable")) } /// Declares a new `future.drop-writable` intrinsic. pub fn future_drop_writable(&mut self, ty: u32) -> u32 { self.canonical_functions().future_drop_writable(ty); self.core_funcs.add(Some("future.drop-writable")) } /// Declares a new `error-context.new` intrinsic. pub fn error_context_new(&mut self, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions().error_context_new(options); self.core_funcs.add(Some("error-context.new")) } /// Declares a new `error-context.debug-message` intrinsic. pub fn error_context_debug_message(&mut self, options: O) -> u32 where O: IntoIterator, O::IntoIter: ExactSizeIterator, { self.canonical_functions() .error_context_debug_message(options); self.core_funcs.add(Some("error-context.debug-message")) } /// Declares a new `error-context.drop` intrinsic. pub fn error_context_drop(&mut self) -> u32 { self.canonical_functions().error_context_drop(); self.core_funcs.add(Some("error-context.drop")) } /// Declares a new `waitable-set.new` intrinsic. pub fn waitable_set_new(&mut self) -> u32 { self.canonical_functions().waitable_set_new(); self.core_funcs.add(Some("waitable-set.new")) } /// Declares a new `waitable-set.wait` intrinsic. pub fn waitable_set_wait(&mut self, cancellable: bool, memory: u32) -> u32 { self.canonical_functions() .waitable_set_wait(cancellable, memory); self.core_funcs.add(Some("waitable-set.wait")) } /// Declares a new `waitable-set.poll` intrinsic. pub fn waitable_set_poll(&mut self, cancellable: bool, memory: u32) -> u32 { self.canonical_functions() .waitable_set_poll(cancellable, memory); self.core_funcs.add(Some("waitable-set.poll")) } /// Declares a new `waitable-set.drop` intrinsic. pub fn waitable_set_drop(&mut self) -> u32 { self.canonical_functions().waitable_set_drop(); self.core_funcs.add(Some("waitable-set.drop")) } /// Declares a new `waitable.join` intrinsic. pub fn waitable_join(&mut self) -> u32 { self.canonical_functions().waitable_join(); self.core_funcs.add(Some("waitable.join")) } /// Declares a new `thread.index` intrinsic. pub fn thread_index(&mut self) -> u32 { self.canonical_functions().thread_index(); self.core_funcs.add(Some("thread.index")) } /// Declares a new `thread.new-indirect` intrinsic. pub fn thread_new_indirect(&mut self, func_ty_idx: u32, table_index: u32) -> u32 { self.canonical_functions() .thread_new_indirect(func_ty_idx, table_index); self.core_funcs.add(Some("thread.new-indirect")) } /// Declares a new `thread.switch-to` intrinsic. pub fn thread_switch_to(&mut self, cancellable: bool) -> u32 { self.canonical_functions().thread_switch_to(cancellable); self.core_funcs.add(Some("thread.switch-to")) } /// Declares a new `thread.suspend` intrinsic. pub fn thread_suspend(&mut self, cancellable: bool) -> u32 { self.canonical_functions().thread_suspend(cancellable); self.core_funcs.add(Some("thread.suspend")) } /// Declares a new `thread.resume-later` intrinsic. pub fn thread_resume_later(&mut self) -> u32 { self.canonical_functions().thread_resume_later(); self.core_funcs.add(Some("thread.resume-later")) } /// Declares a new `thread.yield-to` intrinsic. pub fn thread_yield_to(&mut self, cancellable: bool) -> u32 { self.canonical_functions().thread_yield_to(cancellable); self.core_funcs.add(Some("thread.yield-to")) } /// Adds a new custom section to this component. pub fn custom_section(&mut self, section: &CustomSection<'_>) { self.flush(); self.component.section(section); } /// Adds a new custom section to this component. pub fn raw_custom_section(&mut self, section: &[u8]) { self.flush(); self.component.section(&RawCustomSection(section)); } } // Helper macro to generate methods on `ComponentBuilder` to get specific // section encoders that automatically flush and write out prior sections as // necessary. macro_rules! section_accessors { ($($method:ident => $section:ident)*) => ( #[derive(Debug, Default)] enum LastSection { #[default] None, $($section($section),)* } impl ComponentBuilder { $( fn $method(&mut self) -> &mut $section { match &self.last_section { // The last encoded section matches the section that's // being requested, so no change is necessary. LastSection::$section(_) => {} // Otherwise the last section didn't match this section, // so flush any prior section if needed and start // encoding the desired section of this method. _ => { self.flush(); self.last_section = LastSection::$section($section::new()); } } match &mut self.last_section { LastSection::$section(ret) => ret, _ => unreachable!() } } )* /// Writes out the last section into the final component binary if /// there is a section specified, otherwise does nothing. fn flush(&mut self) { match mem::take(&mut self.last_section) { LastSection::None => {} $( LastSection::$section(section) => { self.component.section(§ion); } )* } } } ) } section_accessors! { component_instances => ComponentInstanceSection instances => InstanceSection canonical_functions => CanonicalFunctionSection aliases => ComponentAliasSection exports => ComponentExportSection imports => ComponentImportSection types => ComponentTypeSection core_types => CoreTypeSection } #[derive(Debug, Default)] struct Namespace { count: u32, names: NameMap, } impl Namespace { fn add(&mut self, name: Option<&str>) -> u32 { let ret = self.count; self.count += 1; if let Some(name) = name { self.names.append(ret, name); } ret } }