Skip to content

Commit

Permalink
Update embed --dummy with new ABI names
Browse files Browse the repository at this point in the history
This commit updates the `wasm-tools component embed` subcommand,
specifically the `--dummy` flag. This flag now uses the new "standard32"
names for the core module that is generated. Additionally a new
`--dummy-names $FOO` option has been added to enable generating the old
names as well as the new names.

Utilities have also been added to `Resolve` for bindings generators to
avoid hardcoding ABI names and instead use the add categories of
imports/exports to name items.
  • Loading branch information
alexcrichton committed Sep 28, 2024
1 parent 22eaa40 commit 3368973
Show file tree
Hide file tree
Showing 12 changed files with 453 additions and 56 deletions.
148 changes: 112 additions & 36 deletions crates/wit-component/src/dummy.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use wit_parser::abi::{AbiVariant, WasmType};
use wit_parser::{Function, Resolve, TypeDefKind, TypeId, WorldId, WorldItem};
use wit_parser::{
Function, Mangling, Resolve, ResourceIntrinsic, TypeDefKind, TypeId, WasmExport, WasmImport,
WorldId, WorldItem, WorldKey,
};

/// Generate a dummy implementation core Wasm module for a given WIT document
pub fn dummy_module(resolve: &Resolve, world: WorldId) -> Vec<u8> {
pub fn dummy_module(resolve: &Resolve, world: WorldId, mangling: Mangling) -> Vec<u8> {
let world = &resolve.worlds[world];
let mut wat = String::new();
wat.push_str("(module\n");
Expand All @@ -11,27 +14,41 @@ pub fn dummy_module(resolve: &Resolve, world: WorldId) -> Vec<u8> {
WorldItem::Function(func) => {
let sig = resolve.wasm_signature(AbiVariant::GuestImport, func);

wat.push_str(&format!("(import \"$root\" \"{}\" (func", func.name));
let (module, name) = resolve.wasm_import_name(
mangling,
WasmImport::Func {
interface: None,
func,
},
);

wat.push_str(&format!("(import {module:?} {name:?} (func"));
push_tys(&mut wat, "param", &sig.params);
push_tys(&mut wat, "result", &sig.results);
wat.push_str("))\n");
}
WorldItem::Interface { id: import, .. } => {
let name = resolve.name_world_key(name);
for (_, func) in resolve.interfaces[*import].functions.iter() {
let sig = resolve.wasm_signature(AbiVariant::GuestImport, func);

wat.push_str(&format!("(import \"{name}\" \"{}\" (func", func.name));
let (module, name) = resolve.wasm_import_name(
mangling,
WasmImport::Func {
interface: Some(name),
func,
},
);
wat.push_str(&format!("(import {module:?} {name:?} (func"));
push_tys(&mut wat, "param", &sig.params);
push_tys(&mut wat, "result", &sig.results);
wat.push_str("))\n");
}
for (_, ty) in resolve.interfaces[*import].types.iter() {
push_resource_func_imports(&mut wat, resolve, &name, *ty);
push_resource_func_imports(&mut wat, resolve, Some(name), *ty, mangling);
}
}
WorldItem::Type(id) => {
push_resource_func_imports(&mut wat, resolve, "$root", *id);
push_resource_func_imports(&mut wat, resolve, None, *id, mangling);
}
}
}
Expand All @@ -42,77 +59,136 @@ pub fn dummy_module(resolve: &Resolve, world: WorldId) -> Vec<u8> {
WorldItem::Interface { id, .. } => *id,
_ => continue,
};
let module = format!("[export]{}", resolve.name_world_key(name));
for (name, ty) in resolve.interfaces[export].types.iter() {
let ty = &resolve.types[*ty];
for resource in resolve.interfaces[export].types.values().copied() {
let ty = &resolve.types[resource];
match ty.kind {
TypeDefKind::Resource => {}
_ => continue,
}
wat.push_str(&format!(
"\
(import \"{module}\" \"[resource-drop]{name}\" (func (param i32)))
(import \"{module}\" \"[resource-new]{name}\" (func (param i32) (result i32)))
(import \"{module}\" \"[resource-rep]{name}\" (func (param i32) (result i32)))
"
));
let intrinsics = [
(ResourceIntrinsic::ExportedDrop, "(func (param i32))"),
(
ResourceIntrinsic::ExportedNew,
"(func (param i32) (result i32))",
),
(
ResourceIntrinsic::ExportedRep,
"(func (param i32) (result i32))",
),
];
for (intrinsic, sig) in intrinsics {
let (module, name) = resolve.wasm_import_name(
mangling,
WasmImport::ResourceIntrinsic {
interface: Some(name),
resource,
intrinsic,
},
);
wat.push_str(&format!("(import {module:?} {name:?} {sig})\n"));
}
}
}

for (name, export) in world.exports.iter() {
match export {
WorldItem::Function(func) => {
push_func(&mut wat, &func.name, resolve, func);
push_func_export(&mut wat, resolve, None, func, mangling);
}
WorldItem::Interface { id: export, .. } => {
let name = resolve.name_world_key(name);
for (_, func) in resolve.interfaces[*export].functions.iter() {
let name = func.legacy_core_export_name(Some(&name));
push_func(&mut wat, &name, resolve, func);
push_func_export(&mut wat, resolve, Some(name), func, mangling);
}

// Feign destructors for any resource that this interface
// exports
for (resource_name, ty) in resolve.interfaces[*export].types.iter() {
let ty = &resolve.types[*ty];
for resource in resolve.interfaces[*export].types.values().copied() {
let ty = &resolve.types[resource];
match ty.kind {
TypeDefKind::Resource => {}
_ => continue,
}
wat.push_str(&format!(
"(func (export \"{name}#[dtor]{resource_name}\") (param i32))"
));
let name = resolve.wasm_export_name(
mangling,
WasmExport::ResourceDtor {
interface: name,
resource,
},
);
wat.push_str(&format!("(func (export {name:?}) (param i32))"));
}
}
WorldItem::Type(_) => {}
}
}

wat.push_str("(memory (export \"memory\") 0)\n");
wat.push_str(
"(func (export \"cabi_realloc\") (param i32 i32 i32 i32) (result i32) unreachable)\n",
);
let memory = resolve.wasm_export_name(mangling, WasmExport::Memory);
wat.push_str(&format!("(memory (export {memory:?}) 0)\n"));
let realloc = resolve.wasm_export_name(mangling, WasmExport::Realloc);
wat.push_str(&format!(
"(func (export {realloc:?}) (param i32 i32 i32 i32) (result i32) unreachable)\n"
));
let initialize = resolve.wasm_export_name(mangling, WasmExport::Initialize);
wat.push_str(&format!("(func (export {initialize:?}))"));
wat.push_str(")\n");

return wat::parse_str(&wat).unwrap();

fn push_resource_func_imports(wat: &mut String, resolve: &Resolve, module: &str, ty: TypeId) {
let ty = &resolve.types[ty];
fn push_resource_func_imports(
wat: &mut String,
resolve: &Resolve,
interface: Option<&WorldKey>,
resource: TypeId,
mangling: Mangling,
) {
let ty = &resolve.types[resource];
match ty.kind {
TypeDefKind::Resource => {}
_ => return,
}
let name = ty.name.as_ref().unwrap();
wat.push_str(&format!("(import \"{module}\" \"[resource-drop]{name}\""));
wat.push_str(" (func (param i32)))\n");
let (module, name) = resolve.wasm_import_name(
mangling,
WasmImport::ResourceIntrinsic {
interface,
resource,
intrinsic: ResourceIntrinsic::ImportedDrop,
},
);
wat.push_str(&format!("(import {module:?} {name:?} (func (param i32)))"));
}

fn push_func(wat: &mut String, name: &str, resolve: &Resolve, func: &Function) {
fn push_func_export(
wat: &mut String,
resolve: &Resolve,
interface: Option<&WorldKey>,
func: &Function,
mangling: Mangling,
) {
let sig = resolve.wasm_signature(AbiVariant::GuestExport, func);
let name = resolve.wasm_export_name(
mangling,
WasmExport::Func {
interface,
func,
post_return: false,
},
);
wat.push_str(&format!("(func (export \"{name}\")"));
push_tys(wat, "param", &sig.params);
push_tys(wat, "result", &sig.results);
wat.push_str(" unreachable)\n");

let name = resolve.wasm_export_name(
mangling,
WasmExport::Func {
interface,
func,
post_return: true,
},
);
wat.push_str(&format!("(func (export \"{name}\")"));
push_tys(wat, "param", &sig.results);
wat.push_str(")\n");
}

fn push_tys(dst: &mut String, desc: &str, params: &[WasmType]) {
Expand Down
3 changes: 2 additions & 1 deletion crates/wit-component/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2158,6 +2158,7 @@ impl ComponentWorld<'_> {
mod test {
use super::*;
use crate::{dummy_module, embed_component_metadata};
use wit_parser::Mangling;

#[test]
fn it_renames_imports() {
Expand All @@ -2183,7 +2184,7 @@ world test {
.unwrap();
let world = resolve.select_world(pkg, None).unwrap();

let mut module = dummy_module(&resolve, world);
let mut module = dummy_module(&resolve, world, Mangling::Standard32);

embed_component_metadata(&mut module, &resolve, world, StringEncoding::UTF8).unwrap();

Expand Down
4 changes: 2 additions & 2 deletions crates/wit-component/src/semver_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
use anyhow::{bail, Context, Result};
use wasm_encoder::{ComponentBuilder, ComponentExportKind, ComponentTypeRef};
use wasmparser::Validator;
use wit_parser::{Resolve, WorldId};
use wit_parser::{Mangling, Resolve, WorldId};

/// Tests whether `new` is a semver-compatible upgrade from the world `prev`.
///
Expand Down Expand Up @@ -63,7 +63,7 @@ pub fn semver_check(mut resolve: Resolve, prev: WorldId, new: WorldId) -> Result
let mut root_component = ComponentBuilder::default();

// (1) above - create a dummy component which has the shape of `prev`.
let mut prev_as_module = dummy_module(&resolve, prev);
let mut prev_as_module = dummy_module(&resolve, prev, Mangling::Standard32);
embed_component_metadata(&mut prev_as_module, &resolve, prev, StringEncoding::UTF8)
.context("failed to embed component metadata")?;
let prev_as_component = ComponentEncoder::default()
Expand Down
21 changes: 19 additions & 2 deletions crates/wit-parser/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{Context, Result};
use anyhow::{bail, Context, Result};
use id_arena::{Arena, Id};
use indexmap::IndexMap;
use semver::Version;
Expand All @@ -21,7 +21,7 @@ pub use ast::{parse_use_path, ParsedUsePath};
mod sizealign;
pub use sizealign::*;
mod resolve;
pub use resolve::{InvalidTransitiveDependency, Package, PackageId, Remap, Resolve};
pub use resolve::*;
mod live;
pub use live::{LiveTypes, TypeIdVisitor};

Expand Down Expand Up @@ -881,6 +881,23 @@ pub enum Mangling {
Legacy,
}

impl std::str::FromStr for Mangling {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Mangling> {
match s {
"legacy" => Ok(Mangling::Legacy),
"standard32" => Ok(Mangling::Standard32),
_ => {
bail!(
"unknown name mangling `{s}`, \
supported values are `legacy` or `standard32`"
)
}
}
}
}

impl Function {
pub fn item_name(&self) -> &str {
match &self.kind {
Expand Down
Loading

0 comments on commit 3368973

Please sign in to comment.