Skip to content

Commit 108c37d

Browse files
authored
Recognize _initialize in wasm-tools component new (#1747)
This commit implements recognition of the `_initialize` function from WASIp1 in the componentization process of `wasm-tools component new`. This additionally corresponds to the same function in the proposed [BuildTargets.md](WebAssembly/component-model#378). This is implemented by having a small core wasm module which is just an import and a `start` section get instantiated at the end of a component to run `_initialize` before all other exports.
1 parent 0d8caf3 commit 108c37d

File tree

7 files changed

+134
-1
lines changed

7 files changed

+134
-1
lines changed

crates/wasm-encoder/src/component/builder.rs

+14
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,20 @@ impl ComponentBuilder {
206206
})
207207
}
208208

209+
/// Creates an alias to a previous core instance's exported item.
210+
///
211+
/// The `instance` provided is the instance to access and the `name` is the
212+
/// item to access.
213+
///
214+
/// Returns the index of the new item defined.
215+
pub fn alias_core_export(&mut self, instance: u32, name: &str, kind: ExportKind) -> u32 {
216+
self.alias(Alias::CoreInstanceExport {
217+
instance,
218+
kind,
219+
name,
220+
})
221+
}
222+
209223
fn inc_kind(&mut self, kind: ComponentExportKind) -> u32 {
210224
match kind {
211225
ComponentExportKind::Func => inc(&mut self.funcs),

crates/wit-component/src/encoding.rs

+56
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ impl<'a> EncodingState<'a> {
631631
self.instantiate_adapter_module(&shims, name, adapter);
632632
}
633633

634+
self.encode_initialize_with_start()?;
635+
634636
Ok(())
635637
}
636638

@@ -1689,6 +1691,60 @@ impl<'a> EncodingState<'a> {
16891691
});
16901692
self.adapter_import_reallocs.insert(name, realloc);
16911693
}
1694+
1695+
/// Generates component bits that are responsible for executing
1696+
/// `_initialize`, if found, in the original component.
1697+
///
1698+
/// The `_initialize` function was a part of WASIp1 where it generally is
1699+
/// intended to run after imports and memory and such are all "hooked up"
1700+
/// and performs other various initialization tasks. This is additionally
1701+
/// specified in https://github.com/WebAssembly/component-model/pull/378
1702+
/// to be part of the component model lowerings as well.
1703+
///
1704+
/// This implements this functionality by encoding a core module that
1705+
/// imports a function and then registers a `start` section with that
1706+
/// imported function. This is all encoded after the
1707+
/// imports/lowerings/tables/etc are all filled in above meaning that this
1708+
/// is the last piece to run. That means that when this is running
1709+
/// everything should be hooked up for all imported functions to work.
1710+
///
1711+
/// Note that at this time `_initialize` is only detected in the "main
1712+
/// module", not adapters/libraries.
1713+
fn encode_initialize_with_start(&mut self) -> Result<()> {
1714+
let initialize = match self.info.info.initialize {
1715+
Some(name) => name,
1716+
// If this core module didn't have `_initialize` or similar, then
1717+
// there's nothing to do here.
1718+
None => return Ok(()),
1719+
};
1720+
let initialize_index = self.component.alias_core_export(
1721+
self.instance_index.unwrap(),
1722+
initialize,
1723+
ExportKind::Func,
1724+
);
1725+
let mut shim = Module::default();
1726+
let mut section = TypeSection::new();
1727+
section.function([], []);
1728+
shim.section(&section);
1729+
let mut section = ImportSection::new();
1730+
section.import("", "", EntityType::Function(0));
1731+
shim.section(&section);
1732+
shim.section(&StartSection { function_index: 0 });
1733+
1734+
// Declare the core module within the component, create a dummy core
1735+
// instance with one export of our `_initialize` function, and then use
1736+
// that to instantiate the module we emit to run the `start` function in
1737+
// core wasm to run `_initialize`.
1738+
let shim_module_index = self.component.core_module(&shim);
1739+
let shim_args_instance_index =
1740+
self.component
1741+
.core_instantiate_exports([("", ExportKind::Func, initialize_index)]);
1742+
self.component.core_instantiate(
1743+
shim_module_index,
1744+
[("", ModuleArg::Instance(shim_args_instance_index))],
1745+
);
1746+
Ok(())
1747+
}
16921748
}
16931749

16941750
/// A list of "shims" which start out during the component instantiation process

crates/wit-component/src/validation.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ pub struct ValidatedModule<'a> {
100100
/// Post-return functions annotated with `cabi_post_*` in their function
101101
/// name.
102102
pub post_returns: IndexSet<String>,
103+
104+
/// Exported function like `_initialize` which needs to be run after
105+
/// everything else has been instantiated.
106+
pub initialize: Option<&'a str>,
103107
}
104108

105109
#[derive(Default)]
@@ -146,6 +150,7 @@ pub fn validate_module<'a>(
146150
metadata: &metadata.metadata,
147151
required_resource_funcs: Default::default(),
148152
post_returns: Default::default(),
153+
initialize: None,
149154
};
150155

151156
for payload in Parser::new(0).parse_all(bytes) {
@@ -194,7 +199,11 @@ pub fn validate_module<'a>(
194199
}
195200
}
196201

197-
assert!(export_funcs.insert(export.name, export.index).is_none())
202+
if export.name == "_initialize" {
203+
ret.initialize = Some(export.name);
204+
} else {
205+
assert!(export_funcs.insert(export.name, export.index).is_none())
206+
}
198207
}
199208
ExternalKind::Memory => {
200209
if export.name == "memory" {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(component
2+
(core module (;0;)
3+
(type (;0;) (func))
4+
(func (;0;) (type 0)
5+
unreachable
6+
)
7+
(func (;1;) (type 0)
8+
unreachable
9+
)
10+
(export "a" (func 0))
11+
(export "_initialize" (func 1))
12+
(@producers
13+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
14+
(processed-by "my-fake-bindgen" "123.45")
15+
)
16+
)
17+
(core instance (;0;) (instantiate 0))
18+
(alias core export 0 "_initialize" (core func (;0;)))
19+
(core module (;1;)
20+
(type (;0;) (func))
21+
(import "" "" (func (;0;) (type 0)))
22+
(start 0)
23+
)
24+
(core instance (;1;)
25+
(export "" (func 0))
26+
)
27+
(core instance (;2;) (instantiate 1
28+
(with "" (instance 1))
29+
)
30+
)
31+
(type (;0;) (func))
32+
(alias core export 0 "a" (core func (;1;)))
33+
(func (;0;) (type 0) (canon lift (core func 1)))
34+
(export (;1;) "a" (func 0))
35+
(@producers
36+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
37+
)
38+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package root:component;
2+
3+
world root {
4+
export a: func();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
;; This test ensures that the `_initialize` function is hooked up to execute
2+
;; when the component is instantiated.
3+
(module
4+
(func (export "a") unreachable)
5+
(func (export "_initialize") unreachable)
6+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package foo:foo;
2+
3+
world module {
4+
export a: func();
5+
}

0 commit comments

Comments
 (0)