diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 1e9441035b..9a010ca3ff 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -4538,6 +4538,7 @@ impl ComponentNameContext { | ComponentNameKind::Method(_) | ComponentNameKind::Static(_) | ComponentNameKind::Constructor(_) + | ComponentNameKind::Implements(_) | ComponentNameKind::Interface(_) => {} ComponentNameKind::Hash(_) @@ -4608,6 +4609,17 @@ impl ComponentNameContext { | ComponentNameKind::Dependency(_) | ComponentNameKind::Hash(_) => {} + // `[implements=]L` may only appear on instance imports/exports. + ComponentNameKind::Implements(imp) => match ty { + ComponentEntityType::Instance(_) => {} + _ => bail!( + offset, + "`implements` name `[implements=<{}>]{}` can only be used with instance types", + imp.interface(), + imp.label(), + ), + }, + // Constructors must return `(own $resource)` or // `(result (own $Tresource))` and the `$resource` must be named // within this context to match `rname`. diff --git a/crates/wasmparser/src/validator/names.rs b/crates/wasmparser/src/validator/names.rs index 5ec899634e..d8594557a0 100644 --- a/crates/wasmparser/src/validator/names.rs +++ b/crates/wasmparser/src/validator/names.rs @@ -242,6 +242,7 @@ impl From for String { /// * a plain method name : `[method]a-b.c-d` /// * a plain static method name : `[static]a-b.c-d` /// * a plain constructor: `[constructor]a-b` +/// * an implements name: `[implements=]label` /// * an interface name: `wasi:cli/reactor@0.1.0` /// * a dependency name: `locked-dep=foo:bar/baz` /// * a URL name: `url=https://..` @@ -252,6 +253,10 @@ impl From for String { /// Note that this type the `[method]...` and `[static]...` variants are /// considered equal and hash to the same value. This enables disallowing /// clashes between the two where method name overlap cannot happen. +/// +/// Similarly, `[implements=]L` is considered equal to a bare label `L` +/// (they conflict on the same label). Two `[implements=<...>]` names with +/// the same label also conflict, regardless of the interface name. #[derive(Clone)] pub struct ComponentName { raw: String, @@ -264,6 +269,7 @@ enum ParsedComponentNameKind { Constructor, Method, Static, + Implements, Interface, Dependency, Url, @@ -283,6 +289,9 @@ pub enum ComponentNameKind<'a> { /// `[static]a-b.c-d` #[allow(missing_docs)] Static(ResourceFunc<'a>), + /// A plain-named instance that implements a named interface, e.g. + /// `[implements=]label`. + Implements(ImplementsName<'a>), /// `wasi:http/types@2.0` #[allow(missing_docs)] Interface(InterfaceName<'a>), @@ -300,6 +309,7 @@ pub enum ComponentNameKind<'a> { const CONSTRUCTOR: &str = "[constructor]"; const METHOD: &str = "[method]"; const STATIC: &str = "[static]"; +const IMPLEMENTS_PREFIX: &str = "[implements=<"; impl ComponentName { /// Attempts to parse `name` as a valid component name, returning `Err` if @@ -338,6 +348,7 @@ impl ComponentName { PK::Constructor => Constructor(KebabStr::new_unchecked(&self.raw[CONSTRUCTOR.len()..])), PK::Method => Method(ResourceFunc(&self.raw[METHOD.len()..])), PK::Static => Static(ResourceFunc(&self.raw[STATIC.len()..])), + PK::Implements => Implements(ImplementsName(&self.raw[IMPLEMENTS_PREFIX.len()..])), PK::Interface => Interface(InterfaceName(&self.raw)), PK::Dependency => Dependency(DependencyName(&self.raw)), PK::Url => Url(UrlName(&self.raw)), @@ -379,7 +390,7 @@ impl Ord for ComponentName { impl PartialOrd for ComponentName { fn partial_cmp(&self, other: &Self) -> Option { - self.kind.partial_cmp(&other.kind) + Some(self.kind().cmp(&other.kind())) } } @@ -397,9 +408,13 @@ impl fmt::Debug for ComponentName { impl ComponentNameKind<'_> { /// Returns the [`ParsedComponentNameKind`] of the [`ComponentNameKind`]. + /// + /// Note that `Implements` maps to `Label` because they share the same + /// uniqueness namespace (an `[implements=]L` name conflicts with a + /// bare label `L`). fn kind(&self) -> ParsedComponentNameKind { match self { - Self::Label(_) => ParsedComponentNameKind::Label, + Self::Label(_) | Self::Implements(_) => ParsedComponentNameKind::Label, Self::Constructor(_) => ParsedComponentNameKind::Constructor, Self::Method(_) => ParsedComponentNameKind::Method, Self::Static(_) => ParsedComponentNameKind::Static, @@ -428,12 +443,29 @@ impl Ord for ComponentNameKind<'_> { Ordering::Equal } + // `[implements=]L` compares by label only, and is equivalent + // to a bare label `L`. + (Implements(lhs), Implements(rhs)) => lhs.label().cmp(rhs.label()), + (Label(lhs), Implements(rhs)) => (*lhs).cmp(rhs.label()), + (Implements(lhs), Label(rhs)) => lhs.label().cmp(*rhs), + + // `[implements=]l` is equivalent to `[method]l.l` / `[static]l.l` + // when resource == method (the `l.l` edge case that also equals + // bare label `l`). + (Implements(imp), Method(method) | Static(method)) + | (Method(method) | Static(method), Implements(imp)) + if imp.label() == method.resource() && imp.label() == method.method() => + { + Ordering::Equal + } + (Interface(lhs), Interface(rhs)) => lhs.cmp(rhs), (Dependency(lhs), Dependency(rhs)) => lhs.cmp(rhs), (Url(lhs), Url(rhs)) => lhs.cmp(rhs), (Hash(lhs), Hash(rhs)) => lhs.cmp(rhs), (Label(_), _) + | (Implements(_), _) | (Constructor(_), _) | (Method(_), _) | (Static(_), _) @@ -455,7 +487,11 @@ impl Hash for ComponentNameKind<'_> { fn hash(&self, hasher: &mut H) { use ComponentNameKind::*; match self { + // `[implements=]L` hashes the same as bare label `L` since + // they conflict on the same label. Label(name) => (0u8, name).hash(hasher), + Implements(name) => (0u8, name.label()).hash(hasher), + Constructor(name) => (1u8, name).hash(hasher), Method(name) | Static(name) => { @@ -508,6 +544,37 @@ impl<'a> ResourceFunc<'a> { } } +/// An implements name, representing `[implements=]L`. +/// +/// The internal string starts after `[implements=<` and contains +/// `interface_name>]label`. +#[derive(Debug, Clone)] +pub struct ImplementsName<'a>(&'a str); + +impl<'a> ImplementsName<'a> { + /// Returns the full raw string of the implements name (everything after + /// the `[implements=<` prefix). + pub fn as_str(&self) -> &'a str { + self.0 + } + + /// Returns the index of `>]` in the internal string, which separates the + /// interface name from the label. + fn split_point(&self) -> usize { + self.0.find(">]").unwrap() + } + + /// Returns the interface name (the `I` in `[implements=]L`). + pub fn interface(&self) -> &'a str { + &self.0[..self.split_point()] + } + + /// Returns the label (the `L` in `[implements=]L`). + pub fn label(&self) -> &'a KebabStr { + KebabStr::new_unchecked(&self.0[self.split_point() + 2..]) + } +} + /// An interface name, stored as `a:b/c@1.2.3` #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct InterfaceName<'a>(&'a str); @@ -616,6 +683,29 @@ impl<'a> ComponentNameParser<'a> { return Ok(ParsedComponentNameKind::Static); } + // '[implements=<' '>]'