Skip to content

Commit 4e2dbc2

Browse files
committed
add wasmparser::WasmFeatures support to wasm-compose
Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent 015b271 commit 4e2dbc2

File tree

3 files changed

+64
-30
lines changed

3 files changed

+64
-30
lines changed

crates/wasm-compose/src/composer.rs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{collections::VecDeque, ffi::OsStr, path::Path};
1414
use wasmparser::{
1515
component_types::{ComponentEntityType, ComponentInstanceTypeId},
1616
types::TypesRef,
17-
ComponentExternalKind, ComponentTypeRef,
17+
ComponentExternalKind, ComponentTypeRef, WasmFeatures,
1818
};
1919

2020
/// The root component name used in configuration.
@@ -72,9 +72,13 @@ struct CompositionGraphBuilder<'a> {
7272
}
7373

7474
impl<'a> CompositionGraphBuilder<'a> {
75-
fn new(root_path: &Path, config: &'a Config) -> Result<Self> {
75+
fn new(root_path: &Path, config: &'a Config, features: WasmFeatures) -> Result<Self> {
7676
let mut graph = CompositionGraph::new();
77-
graph.add_component(Component::from_file(ROOT_COMPONENT_NAME, root_path)?)?;
77+
graph.add_component(Component::from_file(
78+
ROOT_COMPONENT_NAME,
79+
root_path,
80+
features,
81+
)?)?;
7882

7983
let definitions = config
8084
.definitions
@@ -87,7 +91,7 @@ impl<'a> CompositionGraphBuilder<'a> {
8791
)
8892
})?;
8993

90-
let component = Component::from_file(name, config.dir.join(path))?;
94+
let component = Component::from_file(name, config.dir.join(path), features)?;
9195

9296
Ok((graph.add_component(component)?, None))
9397
})
@@ -105,19 +109,19 @@ impl<'a> CompositionGraphBuilder<'a> {
105109
///
106110
/// If a component with the given name already exists, its id is returned.
107111
/// Returns `Ok(None)` if a matching component cannot be found.
108-
fn add_component(&mut self, name: &str) -> Result<Option<ComponentId>> {
112+
fn add_component(&mut self, name: &str, features: WasmFeatures) -> Result<Option<ComponentId>> {
109113
if let Some((id, _)) = self.graph.get_component_by_name(name) {
110114
return Ok(Some(id));
111115
}
112116

113-
match self.find_component(name)? {
117+
match self.find_component(name, features)? {
114118
Some(component) => Ok(Some(self.graph.add_component(component)?)),
115119
None => Ok(None),
116120
}
117121
}
118122

119123
/// Finds the component with the given name on disk.
120-
fn find_component(&self, name: &str) -> Result<Option<Component<'a>>> {
124+
fn find_component(&self, name: &str, features: WasmFeatures) -> Result<Option<Component<'a>>> {
121125
// Check the config for an explicit path (must be a valid component)
122126
if let Some(dep) = self.config.dependencies.get(name) {
123127
log::debug!(
@@ -127,13 +131,14 @@ impl<'a> CompositionGraphBuilder<'a> {
127131
return Ok(Some(Component::from_file(
128132
name,
129133
self.config.dir.join(&dep.path),
134+
features,
130135
)?));
131136
}
132137

133138
// Otherwise, search the paths for a valid component with the same name
134139
log::info!("searching for a component with name `{name}`");
135140
for dir in std::iter::once(&self.config.dir).chain(self.config.search_paths.iter()) {
136-
if let Some(component) = Self::parse_component(dir, name)? {
141+
if let Some(component) = Self::parse_component(dir, name, features)? {
137142
return Ok(Some(component));
138143
}
139144
}
@@ -144,7 +149,11 @@ impl<'a> CompositionGraphBuilder<'a> {
144149
/// Parses a component from the given directory, if it exists.
145150
///
146151
/// Returns `Ok(None)` if the component does not exist.
147-
fn parse_component(dir: &Path, name: &str) -> Result<Option<Component<'a>>> {
152+
fn parse_component(
153+
dir: &Path,
154+
name: &str,
155+
features: WasmFeatures,
156+
) -> Result<Option<Component<'a>>> {
148157
let mut path = dir.join(name);
149158

150159
for ext in ["wasm", "wat"] {
@@ -154,7 +163,7 @@ impl<'a> CompositionGraphBuilder<'a> {
154163
continue;
155164
}
156165

157-
return Ok(Some(Component::from_file(name, &path)?));
166+
return Ok(Some(Component::from_file(name, &path, features)?));
158167
}
159168

160169
Ok(None)
@@ -165,12 +174,17 @@ impl<'a> CompositionGraphBuilder<'a> {
165174
/// Returns an index into `instances` for the instance being instantiated.
166175
///
167176
/// Returns `Ok(None)` if a component to instantiate cannot be found.
168-
fn instantiate(&mut self, name: &str, component_name: &str) -> Result<Option<(usize, bool)>> {
177+
fn instantiate(
178+
&mut self,
179+
name: &str,
180+
component_name: &str,
181+
features: WasmFeatures,
182+
) -> Result<Option<(usize, bool)>> {
169183
if let Some(index) = self.instances.get_index_of(name) {
170184
return Ok(Some((index, true)));
171185
}
172186

173-
match self.add_component(component_name)? {
187+
match self.add_component(component_name, features)? {
174188
Some(component_id) => {
175189
let (index, prev) = self
176190
.instances
@@ -301,13 +315,18 @@ impl<'a> CompositionGraphBuilder<'a> {
301315
/// Processes a dependency in the graph.
302316
///
303317
/// Returns `Ok(Some(index))` if the dependency resulted in a new dependency instance being created.
304-
fn process_dependency(&mut self, dependency: Dependency) -> Result<Option<usize>> {
318+
fn process_dependency(
319+
&mut self,
320+
dependency: Dependency,
321+
features: WasmFeatures,
322+
) -> Result<Option<usize>> {
305323
match dependency.kind {
306324
DependencyKind::Instance { instance, export } => self.process_instance_dependency(
307325
dependency.dependent,
308326
dependency.import,
309327
&instance,
310328
export.as_deref(),
329+
features,
311330
),
312331
DependencyKind::Definition { index, export } => {
313332
// The dependency is on a definition component, so we simply connect the dependent to the definition's export
@@ -348,6 +367,7 @@ impl<'a> CompositionGraphBuilder<'a> {
348367
import: InstanceImportRef,
349368
instance: &str,
350369
export: Option<&str>,
370+
features: WasmFeatures,
351371
) -> Result<Option<usize>> {
352372
let name = self.config.dependency_name(instance);
353373

@@ -356,7 +376,7 @@ impl<'a> CompositionGraphBuilder<'a> {
356376
dependent_name = self.instances.get_index(dependent_index).unwrap().0,
357377
);
358378

359-
match self.instantiate(instance, name)? {
379+
match self.instantiate(instance, name, features)? {
360380
Some((instance, existing)) => {
361381
let (dependent, import_name, import_type) = self.resolve_import_ref(import);
362382

@@ -478,12 +498,12 @@ impl<'a> CompositionGraphBuilder<'a> {
478498
}
479499

480500
/// Build the instantiation graph.
481-
fn build(mut self) -> Result<(InstanceId, CompositionGraph<'a>)> {
501+
fn build(mut self, features: WasmFeatures) -> Result<(InstanceId, CompositionGraph<'a>)> {
482502
let mut queue: VecDeque<Dependency> = VecDeque::new();
483503

484504
// Instantiate the root and push its dependencies to the queue
485505
let (root_instance, existing) = self
486-
.instantiate(ROOT_COMPONENT_NAME, ROOT_COMPONENT_NAME)?
506+
.instantiate(ROOT_COMPONENT_NAME, ROOT_COMPONENT_NAME, features)?
487507
.unwrap();
488508

489509
assert!(!existing);
@@ -492,7 +512,7 @@ impl<'a> CompositionGraphBuilder<'a> {
492512

493513
// Process all remaining dependencies in the queue
494514
while let Some(dependency) = queue.pop_front() {
495-
if let Some(instance) = self.process_dependency(dependency)? {
515+
if let Some(instance) = self.process_dependency(dependency, features)? {
496516
self.push_dependencies(instance, &mut queue)?;
497517
}
498518
}
@@ -511,6 +531,7 @@ impl<'a> CompositionGraphBuilder<'a> {
511531
pub struct ComponentComposer<'a> {
512532
component: &'a Path,
513533
config: &'a Config,
534+
features: WasmFeatures,
514535
}
515536

516537
impl<'a> ComponentComposer<'a> {
@@ -519,8 +540,12 @@ impl<'a> ComponentComposer<'a> {
519540
/// ## Arguments
520541
/// * `component` - The path to the component to compose.
521542
/// * `config` - The configuration to use for the composition.
522-
pub fn new(component: &'a Path, config: &'a Config) -> Self {
523-
Self { component, config }
543+
pub fn new(component: &'a Path, config: &'a Config, features: WasmFeatures) -> Self {
544+
Self {
545+
component,
546+
config,
547+
features,
548+
}
524549
}
525550

526551
/// Composes a WebAssembly component based on the composer's configuration.
@@ -529,7 +554,8 @@ impl<'a> ComponentComposer<'a> {
529554
/// Returns the bytes of the composed component.
530555
pub fn compose(&self) -> Result<Vec<u8>> {
531556
let (root_instance, graph) =
532-
CompositionGraphBuilder::new(self.component, self.config)?.build()?;
557+
CompositionGraphBuilder::new(self.component, self.config, self.features)?
558+
.build(self.features)?;
533559

534560
// If only the root component was instantiated, then there are no resolved dependencies
535561
if graph.instances.len() == 1 {

crates/wasm-compose/src/graph.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub struct Component<'a> {
5050

5151
impl<'a> Component<'a> {
5252
/// Constructs a new component from reading the given file.
53-
pub fn from_file(name: &str, path: impl AsRef<Path>) -> Result<Self> {
53+
pub fn from_file(name: &str, path: impl AsRef<Path>, features: WasmFeatures) -> Result<Self> {
5454
let path = path.as_ref();
5555
log::info!("parsing WebAssembly component file `{}`", path.display());
5656
let component = Self::parse(
@@ -61,6 +61,7 @@ impl<'a> Component<'a> {
6161
format!("failed to parse component `{path}`", path = path.display())
6262
})?
6363
.into(),
64+
features,
6465
)
6566
.with_context(|| format!("failed to parse component `{path}`", path = path.display()))?;
6667

@@ -73,7 +74,11 @@ impl<'a> Component<'a> {
7374
}
7475

7576
/// Constructs a new component from the given bytes.
76-
pub fn from_bytes(name: impl Into<String>, bytes: impl Into<Cow<'a, [u8]>>) -> Result<Self> {
77+
pub fn from_bytes(
78+
name: impl Into<String>,
79+
bytes: impl Into<Cow<'a, [u8]>>,
80+
features: WasmFeatures,
81+
) -> Result<Self> {
7782
let mut bytes = bytes.into();
7883

7984
match wat::parse_bytes(bytes.as_ref()).context("failed to parse component")? {
@@ -88,6 +93,7 @@ impl<'a> Component<'a> {
8893
ComponentName::new(&name.into(), 0)?.to_string(),
8994
None,
9095
bytes,
96+
features,
9197
)
9298
.context("failed to parse component")?;
9399

@@ -96,14 +102,15 @@ impl<'a> Component<'a> {
96102
Ok(component)
97103
}
98104

99-
fn parse(name: String, path: Option<PathBuf>, bytes: Cow<'a, [u8]>) -> Result<Self> {
105+
fn parse(
106+
name: String,
107+
path: Option<PathBuf>,
108+
bytes: Cow<'a, [u8]>,
109+
features: WasmFeatures,
110+
) -> Result<Self> {
100111
let mut parser = Parser::new(0);
101112
let mut parsers = Vec::new();
102-
let mut validator = Validator::new_with_features(
103-
WasmFeatures::WASM2
104-
| WasmFeatures::COMPONENT_MODEL
105-
| WasmFeatures::COMPONENT_MODEL_ASYNC,
106-
);
113+
let mut validator = Validator::new_with_features(features);
107114
let mut imports = IndexMap::new();
108115
let mut exports = IndexMap::new();
109116

src/bin/wasm-tools/compose.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use anyhow::{Context, Result};
44
use clap::Parser;
55
use std::path::{Path, PathBuf};
66
use wasm_compose::{composer::ComponentComposer, config::Config};
7-
use wasmparser::Validator;
7+
use wasmparser::{Validator, WasmFeatures};
88

99
/// WebAssembly component composer.
1010
///
@@ -59,7 +59,8 @@ impl Opts {
5959
let config = self.create_config()?;
6060
log::debug!("configuration:\n{:#?}", config);
6161

62-
let bytes = ComponentComposer::new(&self.component, &config).compose()?;
62+
let bytes =
63+
ComponentComposer::new(&self.component, &config, WasmFeatures::default()).compose()?;
6364

6465
self.output.output_wasm(&self.general, &bytes, self.wat)?;
6566

0 commit comments

Comments
 (0)