Skip to content

Commit

Permalink
Add a flag to require the new mangling scheme
Browse files Browse the repository at this point in the history
This commit adds a new `--reject-legacy-names` flag to the `wasm-tools
component new` subcommand which can be used to disable support for the
legacy naming scheme. This is intended to help with testing out the new
naming scheme for tools and to help evaluate in the future if it's
theoretically possible to remove support for the old naming scheme.
  • Loading branch information
alexcrichton committed Sep 28, 2024
1 parent 3368973 commit ed037a5
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 87 deletions.
22 changes: 18 additions & 4 deletions crates/wit-component/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1878,7 +1878,7 @@ pub struct LibraryInfo {
}

/// Represents an adapter or library to be instantiated as part of the component
struct Adapter {
pub(super) struct Adapter {
/// The wasm of the module itself, with `component-type` sections stripped
wasm: Vec<u8>,

Expand All @@ -1900,13 +1900,14 @@ struct Adapter {
#[derive(Default)]
pub struct ComponentEncoder {
module: Vec<u8>,
metadata: Bindgen,
pub(super) metadata: Bindgen,
validate: bool,
main_module_exports: IndexSet<WorldKey>,
adapters: IndexMap<String, Adapter>,
pub(super) main_module_exports: IndexSet<WorldKey>,
pub(super) adapters: IndexMap<String, Adapter>,
import_name_map: HashMap<String, String>,
realloc_via_memory_grow: bool,
merge_imports_based_on_semver: Option<bool>,
pub(super) reject_legacy_names: bool,
}

impl ComponentEncoder {
Expand Down Expand Up @@ -1959,6 +1960,19 @@ impl ComponentEncoder {
self
}

/// Sets whether to reject the historical mangling/name scheme for core wasm
/// imports/exports as they map to the component model.
///
/// The `wit-component` crate supported a different set of names prior to
/// WebAssembly/component-model#378 and this can be used to disable this
/// support.
///
/// This is disabled by default.
pub fn reject_legacy_names(mut self, reject: bool) -> Self {
self.reject_legacy_names = reject;
self
}

/// Specifies a new adapter which is used to translate from a historical
/// wasm ABI to the canonical ABI and the `interface` provided.
///
Expand Down
25 changes: 5 additions & 20 deletions crates/wit-component/src/encoding/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,7 @@ pub enum Lowering {

impl<'a> ComponentWorld<'a> {
pub fn new(encoder: &'a ComponentEncoder) -> Result<Self> {
let adapters = encoder
.adapters
.keys()
.map(|s| s.as_str())
.collect::<IndexSet<_>>();
let info = validate_module(
&encoder.module,
&encoder.metadata,
&encoder.main_module_exports,
&adapters,
)
.context("module was not valid")?;
let info = validate_module(encoder, &encoder.module).context("module was not valid")?;

let mut ret = ComponentWorld {
encoder,
Expand All @@ -85,7 +74,7 @@ impl<'a> ComponentWorld<'a> {
exports_used: HashMap::new(),
};

ret.process_adapters(&adapters)?;
ret.process_adapters()?;
ret.process_imports()?;
ret.process_exports_used();
ret.process_live_type_imports();
Expand All @@ -97,7 +86,7 @@ impl<'a> ComponentWorld<'a> {
/// adapters and figure out what functions are required from the
/// adapter itself, either because the functions are imported by the
/// main module or they're part of the adapter's exports.
fn process_adapters(&mut self, adapters: &IndexSet<&str>) -> Result<()> {
fn process_adapters(&mut self) -> Result<()> {
let resolve = &self.encoder.metadata.resolve;
let world = self.encoder.metadata.world;
for (
Expand Down Expand Up @@ -156,13 +145,11 @@ impl<'a> ComponentWorld<'a> {
// imports may have deleted some imports which means that the
// final component may not need to import as many interfaces.
let info = validate_adapter_module(
self.encoder,
&wasm,
resolve,
world,
&required_by_import,
required_exports,
library_info.as_ref(),
adapters,
)
.with_context(|| {
format!("failed to validate the imports of the adapter module `{name}`")
Expand All @@ -189,13 +176,11 @@ impl<'a> ComponentWorld<'a> {
)
};
let info = validate_adapter_module(
self.encoder,
&wasm,
resolve,
world,
&required_by_import,
required_exports,
library_info.as_ref(),
adapters,
)
.with_context(|| {
format!("failed to validate the imports of the minimized adapter module `{name}`")
Expand Down
95 changes: 35 additions & 60 deletions crates/wit-component/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::encoding::{Instance, Item, LibraryInfo, MainOrAdapter};
use crate::metadata::Bindgen;
use crate::ComponentEncoder;
use anyhow::{bail, Context, Result};
use indexmap::{map::Entry, IndexMap, IndexSet};
use std::mem;
Expand Down Expand Up @@ -50,11 +50,9 @@ pub struct ValidatedModule {

impl ValidatedModule {
fn new(
encoder: &ComponentEncoder,
bytes: &[u8],
resolve: &Resolve,
world: WorldId,
exports: &IndexSet<WorldKey>,
adapters: &IndexSet<&str>,
info: Option<&LibraryInfo>,
) -> Result<ValidatedModule> {
let mut validator = Validator::new();
Expand All @@ -75,21 +73,20 @@ impl ValidatedModule {
Payload::ImportSection(s) => {
for import in s {
let import = import?;
ret.imports
.add(import, resolve, world, adapters, info, types)?;
ret.imports.add(import, encoder, info, types)?;
}
}
Payload::ExportSection(s) => {
for export in s {
let export = export?;
ret.exports.add(export, resolve, world, &exports, types)?;
ret.exports.add(export, encoder, &exports, types)?;
}
}
_ => continue,
}
}

ret.exports.validate(resolve, world, exports)?;
ret.exports.validate(encoder, exports)?;

Ok(ret)
}
Expand Down Expand Up @@ -255,32 +252,26 @@ impl ImportMap {
fn add(
&mut self,
import: wasmparser::Import<'_>,
resolve: &Resolve,
world: WorldId,
adapters: &IndexSet<&str>,
encoder: &ComponentEncoder,
library_info: Option<&LibraryInfo>,
types: TypesRef<'_>,
) -> Result<()> {
if self.classify_import_with_library(import, library_info)? {
return Ok(());
}
let item = self
.classify(import, resolve, world, adapters, types)
.with_context(|| {
format!(
"failed to resolve import `{}::{}`",
import.module, import.name,
)
})?;
let item = self.classify(import, encoder, types).with_context(|| {
format!(
"failed to resolve import `{}::{}`",
import.module, import.name,
)
})?;
self.insert_import(import, item)
}

fn classify(
&self,
import: wasmparser::Import<'_>,
resolve: &Resolve,
world_id: WorldId,
adapters: &IndexSet<&str>,
encoder: &ComponentEncoder,
types: TypesRef<'_>,
) -> Result<Import> {
// Special-case the main module's memory imported into adapters which
Expand Down Expand Up @@ -311,15 +302,16 @@ impl ImportMap {

// Handle main module imports that match known adapters and set it up as
// an import of an adapter export.
if adapters.contains(import.module) {
if encoder.adapters.contains_key(import.module) {
return Ok(Import::AdapterExport(ty.clone()));
}

let (module, names) = match import.module.strip_prefix("cm32p2") {
Some(suffix) => (suffix, STANDARD),
None if encoder.reject_legacy_names => (import.module, STANDARD),
None => (import.module, LEGACY),
};
self.classify_component_model_import(module, import.name, resolve, world_id, ty, names)
self.classify_component_model_import(module, import.name, encoder, ty, names)
}

/// Attempts to classify the import `{module}::{name}` with the rules
Expand All @@ -328,11 +320,12 @@ impl ImportMap {
&self,
module: &str,
name: &str,
resolve: &Resolve,
world_id: WorldId,
encoder: &ComponentEncoder,
ty: &FuncType,
names: &dyn NameMangling,
) -> Result<Import> {
let resolve = &encoder.metadata.resolve;
let world_id = encoder.metadata.world;
let world = &resolve.worlds[world_id];

if module == names.import_root() {
Expand Down Expand Up @@ -408,7 +401,7 @@ impl ImportMap {
}
bail!(
"import interface `{module}` is missing function \
`{name}` that is required by the module",
`{name}` that is required by the module",
)
}

Expand Down Expand Up @@ -533,12 +526,11 @@ impl ExportMap {
fn add(
&mut self,
export: wasmparser::Export<'_>,
resolve: &Resolve,
world: WorldId,
encoder: &ComponentEncoder,
exports: &IndexSet<WorldKey>,
types: TypesRef<'_>,
) -> Result<()> {
if let Some(item) = self.classify(export, resolve, world, exports, types)? {
if let Some(item) = self.classify(export, encoder, exports, types)? {
log::debug!("classifying export `{}` as {item:?}", export.name);
let prev = self.names.insert(export.name.to_string(), item);
assert!(prev.is_none());
Expand All @@ -549,8 +541,7 @@ impl ExportMap {
fn classify(
&mut self,
export: wasmparser::Export<'_>,
resolve: &Resolve,
world: WorldId,
encoder: &ComponentEncoder,
exports: &IndexSet<WorldKey>,
types: TypesRef<'_>,
) -> Result<Option<Export>> {
Expand All @@ -559,7 +550,6 @@ impl ExportMap {
let ty = types[types.core_function_at(export.index)].unwrap_func();
self.raw_exports.insert(export.name.to_string(), ty.clone());
}
ExternalKind::Memory => return Ok(Some(Export::Memory)),
_ => return Ok(None),
}

Expand All @@ -576,10 +566,11 @@ impl ExportMap {

let (name, names) = match export.name.strip_prefix("cm32p2") {
Some(name) => (name, STANDARD),
None if encoder.reject_legacy_names => return Ok(None),
None => (export.name, LEGACY),
};
if let Some(export) = self
.classify_component_export(names, name, &export, resolve, world, exports, types)
.classify_component_export(names, name, &export, encoder, exports, types)
.with_context(|| format!("failed to classify export `{}`", export.name))?
{
return Ok(Some(export));
Expand All @@ -593,11 +584,12 @@ impl ExportMap {
names: &dyn NameMangling,
name: &str,
export: &wasmparser::Export<'_>,
resolve: &Resolve,
world: WorldId,
encoder: &ComponentEncoder,
exports: &IndexSet<WorldKey>,
types: TypesRef<'_>,
) -> Result<Option<Export>> {
let resolve = &encoder.metadata.resolve;
let world = encoder.metadata.world;
match export.kind {
ExternalKind::Func => {}
ExternalKind::Memory => {
Expand Down Expand Up @@ -748,12 +740,9 @@ impl ExportMap {
self.names.iter().map(|(n, e)| (n.as_str(), e))
}

fn validate(
&self,
resolve: &Resolve,
world: WorldId,
exports: &IndexSet<WorldKey>,
) -> Result<()> {
fn validate(&self, encoder: &ComponentEncoder, exports: &IndexSet<WorldKey>) -> Result<()> {
let resolve = &encoder.metadata.resolve;
let world = encoder.metadata.world;
// Multi-memory isn't supported because otherwise we don't know what
// memory to put things in.
if self
Expand Down Expand Up @@ -1193,20 +1182,8 @@ impl NameMangling for Legacy {
/// The `ValidatedModule` return value contains the metadata which describes the
/// input module on success. This is then further used to generate a component
/// for this module.
pub fn validate_module(
bytes: &[u8],
metadata: &Bindgen,
exports: &IndexSet<WorldKey>,
adapters: &IndexSet<&str>,
) -> Result<ValidatedModule> {
ValidatedModule::new(
bytes,
&metadata.resolve,
metadata.world,
exports,
adapters,
None,
)
pub fn validate_module(encoder: &ComponentEncoder, bytes: &[u8]) -> Result<ValidatedModule> {
ValidatedModule::new(encoder, bytes, &encoder.main_module_exports, None)
}

/// This function will validate the `bytes` provided as a wasm adapter module.
Expand All @@ -1227,15 +1204,13 @@ pub fn validate_module(
/// allowing the module to import tables and globals, as well as import
/// functions at the world level, not just at the interface level.
pub fn validate_adapter_module(
encoder: &ComponentEncoder,
bytes: &[u8],
resolve: &Resolve,
world: WorldId,
required_by_import: &IndexMap<String, FuncType>,
exports: &IndexSet<WorldKey>,
library_info: Option<&LibraryInfo>,
adapters: &IndexSet<&str>,
) -> Result<ValidatedModule> {
let ret = ValidatedModule::new(bytes, resolve, world, exports, adapters, library_info)?;
let ret = ValidatedModule::new(encoder, bytes, exports, library_info)?;

for (name, required_ty) in required_by_import {
let actual = match ret.exports.raw_exports.get(name) {
Expand Down
15 changes: 14 additions & 1 deletion src/bin/wasm-tools/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ pub struct NewOpts {
/// This is enabled by default.
#[clap(long, value_name = "MERGE")]
merge_imports_based_on_semver: Option<bool>,

/// Reject usage of the "legacy" naming scheme of `wit-component` and
/// require the new naming scheme to be used.
///
/// This flag can be used to ignore core module imports/exports that don't
/// conform to WebAssembly/component-model#378. This turns off
/// compatibility `wit-component`'s historical naming scheme. This is
/// intended to be used to test if a tool is compatible with a hypothetical
/// removal of the old scheme in the future.
#[clap(long)]
reject_legacy_names: bool,
}

impl NewOpts {
Expand All @@ -158,7 +169,9 @@ impl NewOpts {
/// Executes the application.
fn run(self) -> Result<()> {
let wasm = self.io.parse_input_wasm()?;
let mut encoder = ComponentEncoder::default().validate(!self.skip_validation);
let mut encoder = ComponentEncoder::default()
.validate(!self.skip_validation)
.reject_legacy_names(self.reject_legacy_names);

if let Some(merge) = self.merge_imports_based_on_semver {
encoder = encoder.merge_imports_based_on_semver(merge);
Expand Down
8 changes: 8 additions & 0 deletions tests/cli/reject-legacy-names.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// FAIL: component embed % --dummy-names legacy | component new --reject-legacy-names

package a:b;

world foo {
export f: func();
import y: func();
}
Loading

0 comments on commit ed037a5

Please sign in to comment.