From 0539c3bacddb7237dee8ce6c239cb6d3f8c34907 Mon Sep 17 00:00:00 2001 From: Brian Hardock Date: Fri, 15 Aug 2025 13:05:14 -0600 Subject: [PATCH] Enable semver-compatible matching of import/export lookups in target validation Signed-off-by: Brian Hardock --- crates/wac-types/src/lib.rs | 2 + crates/wac-types/src/names.rs | 276 ++++++++++++++++++ crates/wac-types/src/targets.rs | 48 ++- crates/wac-types/tests/README.md | 6 + .../tests/dummy_wasi_http@0.2.0.wasm | Bin 0 -> 26977 bytes .../tests/dummy_wasi_http@0.2.3.wasm | Bin 0 -> 26993 bytes crates/wac-types/tests/targets.rs | 45 +++ 7 files changed, 368 insertions(+), 9 deletions(-) create mode 100644 crates/wac-types/src/names.rs create mode 100644 crates/wac-types/tests/README.md create mode 100644 crates/wac-types/tests/dummy_wasi_http@0.2.0.wasm create mode 100644 crates/wac-types/tests/dummy_wasi_http@0.2.3.wasm create mode 100644 crates/wac-types/tests/targets.rs diff --git a/crates/wac-types/src/lib.rs b/crates/wac-types/src/lib.rs index 35808dc..84ccc5e 100644 --- a/crates/wac-types/src/lib.rs +++ b/crates/wac-types/src/lib.rs @@ -6,6 +6,7 @@ mod aggregator; mod checker; mod component; mod core; +mod names; mod package; mod targets; @@ -13,5 +14,6 @@ pub use aggregator::*; pub use checker::*; pub use component::*; pub use core::*; +pub use names::*; pub use package::*; pub use targets::*; diff --git a/crates/wac-types/src/names.rs b/crates/wac-types/src/names.rs new file mode 100644 index 0000000..13f4808 --- /dev/null +++ b/crates/wac-types/src/names.rs @@ -0,0 +1,276 @@ +// Adapted from https://github.com/bytecodealliance/wasmtime/blob/main/crates/environ/src/component/names.rs + +use anyhow::{bail, Result}; +use core::hash::Hash; +use indexmap::IndexMap; +use semver::Version; + +/// A semver-aware map for imports/exports of a component. +/// +/// This data structure is used when looking up the names of imports/exports of +/// a component to enable semver-compatible matching of lookups. This will +/// enable lookups of `a:b/c@0.2.0` to match entries defined as `a:b/c@0.2.1` +/// which is currently considered a key feature of WASI's compatibility story. +/// +/// On the outside this looks like a map of `K` to `V`. +#[derive(Clone, Debug)] +pub struct NameMap { + /// A map of keys to the value that they define. + /// + /// Note that this map is "exact" where the name here is the exact name that + /// was specified when the `insert` was called. This doesn't have any + /// semver-mangling or anything like that. + /// + /// This map is always consulted first during lookups. + definitions: IndexMap, + + /// An auxiliary map tracking semver-compatible names. This is a map from + /// "semver compatible alternate name" to a name present in `definitions` + /// and the semver version it was registered at. + /// + /// An example map would be: + /// + /// ```text + /// { + /// "a:b/c@0.2": ("a:b/c@0.2.1", 0.2.1), + /// "a:b/c@2": ("a:b/c@2.0.0+abc", 2.0.0+abc), + /// } + /// ``` + /// + /// As names are inserted into `definitions` each name may have up to one + /// semver-compatible name with extra numbers/info chopped off which is + /// inserted into this map. This map is the lookup table from `@0.2` to + /// `@0.2.x` where `x` is what was inserted manually. + /// + /// The `Version` here is tracked to ensure that when multiple versions on + /// one track are defined that only the maximal version here is retained. + alternate_lookups: IndexMap, +} + +impl NameMap +where + K: Clone + Hash + Eq + Ord, +{ + /// Inserts the `name` specified into this map. + /// + /// The name is intern'd through the `cx` argument and shadowing is + /// controlled by the `allow_shadowing` variable. + /// + /// This function will automatically insert an entry in + /// `self.alternate_lookups` if `name` is a semver-looking name. + /// + /// Returns an error if `allow_shadowing` is `false` and the `name` is + /// already present in this map (by exact match). Otherwise returns the + /// intern'd version of `name`. + pub fn insert(&mut self, name: &str, cx: &mut I, allow_shadowing: bool, item: V) -> Result + where + I: NameMapIntern, + { + // Always insert `name` and `item` as an exact definition. + let key = cx.intern(name); + if let Some(prev) = self.definitions.insert(key.clone(), item) { + if !allow_shadowing { + self.definitions.insert(key, prev); + bail!("map entry `{name}` defined twice") + } + } + + // If `name` is a semver-looking thing, like `a:b/c@1.0.0`, then also + // insert an entry in the semver-compatible map under a key such as + // `a:b/c@1`. + // + // This key is used during `get` later on. + if let Some((alternate_key, version)) = alternate_lookup_key(name) { + let alternate_key = cx.intern(alternate_key); + if let Some((prev_key, prev_version)) = self + .alternate_lookups + .insert(alternate_key.clone(), (key.clone(), version.clone())) + { + // Prefer the latest version, so only do this if we're + // greater than the prior version. + if version < prev_version { + self.alternate_lookups + .insert(alternate_key, (prev_key, prev_version)); + } + } + } + Ok(key) + } + + /// Looks up `name` within this map, using the interning specified by + /// `cx`. + /// + /// This may return a definition even if `name` wasn't exactly defined in + /// this map, such as looking up `a:b/c@0.2.0` when the map only has + /// `a:b/c@0.2.1` defined. + pub fn get(&self, name: &str, cx: &I) -> Option<&V> + where + I: NameMapIntern, + { + // First look up an exact match and if that's found return that. This + // enables defining multiple versions in the map and the requested + // version is returned if it matches exactly. + let candidate = cx.lookup(name).and_then(|k| self.definitions.get(&k)); + if let Some(def) = candidate { + return Some(def); + } + + // Failing that, then try to look for a semver-compatible alternative. + // This looks up the key based on `name`, if any, and then looks to see + // if that was intern'd in `strings`. Given all that look to see if it + // was defined in `alternate_lookups` and finally at the end that exact + // key is then used to look up again in `self.definitions`. + let (alternate_name, _version) = alternate_lookup_key(name)?; + let alternate_key = cx.lookup(alternate_name)?; + let (exact_key, _version) = self.alternate_lookups.get(&alternate_key)?; + self.definitions.get(exact_key) + } + + /// Returns an iterator over inserted values in this map. + /// + /// Note that the iterator return yields intern'd keys and additionally does + /// not do anything special with semver names and such, it only literally + /// yields what's been inserted with [`NameMap::insert`]. + pub fn raw_iter(&self) -> impl Iterator { + self.definitions.iter() + } + + /// TODO + pub fn raw_get_mut(&mut self, key: &K) -> Option<&mut V> { + self.definitions.get_mut(key) + } +} + +impl Default for NameMap +where + K: Clone + Hash + Eq + Ord, +{ + fn default() -> NameMap { + NameMap { + definitions: Default::default(), + alternate_lookups: Default::default(), + } + } +} + +/// A helper trait used in conjunction with [`NameMap`] to optionally intern +/// keys to non-strings. +pub trait NameMapIntern { + /// The key that this interning context generates. + type Key; + + /// Inserts `s` into `self` and returns the intern'd key `Self::Key`. + fn intern(&mut self, s: &str) -> Self::Key; + + /// Looks up `s` in `self` returning `Some` if it was found or `None` if + /// it's not present. + fn lookup(&self, s: &str) -> Option; +} + +/// For use with [`NameMap`] when no interning should happen and instead string +/// keys are copied as-is. +pub struct NameMapNoIntern; + +impl NameMapIntern for NameMapNoIntern { + type Key = String; + + fn intern(&mut self, s: &str) -> String { + s.to_string() + } + + fn lookup(&self, s: &str) -> Option { + Some(s.to_string()) + } +} + +/// Determines a version-based "alternate lookup key" for the `name` specified. +/// +/// Some examples are: +/// +/// * `foo` => `None` +/// * `foo:bar/baz` => `None` +/// * `foo:bar/baz@1.1.2` => `Some(foo:bar/baz@1)` +/// * `foo:bar/baz@0.1.0` => `Some(foo:bar/baz@0.1)` +/// * `foo:bar/baz@0.0.1` => `None` +/// * `foo:bar/baz@0.1.0-rc.2` => `None` +/// +/// This alternate lookup key is intended to serve the purpose where a +/// semver-compatible definition can be located, if one is defined, at perhaps +/// either a newer or an older version. +fn alternate_lookup_key(name: &str) -> Option<(&str, Version)> { + let at = name.find('@')?; + let version_string = &name[at + 1..]; + let version = Version::parse(version_string).ok()?; + if !version.pre.is_empty() { + // If there's a prerelease then don't consider that compatible with any + // other version number. + None + } else if version.major != 0 { + // If the major number is nonzero then compatibility is up to the major + // version number, so return up to the first decimal. + let first_dot = version_string.find('.')? + at + 1; + Some((&name[..first_dot], version)) + } else if version.minor != 0 { + // Like the major version if the minor is nonzero then patch releases + // are all considered to be on a "compatible track". + let first_dot = version_string.find('.')? + at + 1; + let second_dot = name[first_dot + 1..].find('.')? + first_dot + 1; + Some((&name[..second_dot], version)) + } else { + // If the patch number is the first nonzero entry then nothing can be + // compatible with this patch, e.g. 0.0.1 isn't' compatible with + // any other version inherently. + None + } +} + +#[cfg(test)] +mod tests { + use super::{NameMap, NameMapNoIntern}; + + #[test] + fn alternate_lookup_key() { + fn alt(s: &str) -> Option<&str> { + super::alternate_lookup_key(s).map(|(s, _)| s) + } + + assert_eq!(alt("x"), None); + assert_eq!(alt("x:y/z"), None); + assert_eq!(alt("x:y/z@1.0.0"), Some("x:y/z@1")); + assert_eq!(alt("x:y/z@1.1.0"), Some("x:y/z@1")); + assert_eq!(alt("x:y/z@1.1.2"), Some("x:y/z@1")); + assert_eq!(alt("x:y/z@2.1.2"), Some("x:y/z@2")); + assert_eq!(alt("x:y/z@2.1.2+abc"), Some("x:y/z@2")); + assert_eq!(alt("x:y/z@0.1.2"), Some("x:y/z@0.1")); + assert_eq!(alt("x:y/z@0.1.3"), Some("x:y/z@0.1")); + assert_eq!(alt("x:y/z@0.2.3"), Some("x:y/z@0.2")); + assert_eq!(alt("x:y/z@0.2.3+abc"), Some("x:y/z@0.2")); + assert_eq!(alt("x:y/z@0.0.1"), None); + assert_eq!(alt("x:y/z@0.0.1-pre"), None); + assert_eq!(alt("x:y/z@0.1.0-pre"), None); + assert_eq!(alt("x:y/z@1.0.0-pre"), None); + } + + #[test] + fn name_map_smoke() { + let mut map = NameMap::default(); + let mut intern = NameMapNoIntern; + + map.insert("a", &mut intern, false, 0).unwrap(); + map.insert("b", &mut intern, false, 1).unwrap(); + + assert!(map.insert("a", &mut intern, false, 0).is_err()); + assert!(map.insert("a", &mut intern, true, 0).is_ok()); + + assert_eq!(map.get("a", &intern), Some(&0)); + assert_eq!(map.get("b", &intern), Some(&1)); + assert_eq!(map.get("c", &intern), None); + + map.insert("a:b/c@1.0.0", &mut intern, false, 2).unwrap(); + map.insert("a:b/c@1.0.1", &mut intern, false, 3).unwrap(); + assert_eq!(map.get("a:b/c@1.0.0", &intern), Some(&2)); + assert_eq!(map.get("a:b/c@1.0.1", &intern), Some(&3)); + assert_eq!(map.get("a:b/c@1.0.2", &intern), Some(&3)); + assert_eq!(map.get("a:b/c@1.1.0", &intern), Some(&3)); + } +} diff --git a/crates/wac-types/src/targets.rs b/crates/wac-types/src/targets.rs index 8e34378..f97164b 100644 --- a/crates/wac-types/src/targets.rs +++ b/crates/wac-types/src/targets.rs @@ -1,4 +1,6 @@ -use crate::{ExternKind, ItemKind, SubtypeChecker, Types, WorldId}; +use crate::{ + ExternKind, ItemKind, NameMap, NameMapNoIntern, SubtypeChecker, Types, World, WorldId, +}; use std::collections::{BTreeMap, BTreeSet}; use std::fmt; @@ -113,20 +115,17 @@ pub fn validate_target( ) -> TargetValidationResult { let component_world = &types[component_world_id]; let wit_world = &types[wit_world_id]; - // The interfaces imported implicitly through uses. - let implicit_imported_interfaces = wit_world.implicit_imported_interfaces(types); let mut cache = Default::default(); let mut checker = SubtypeChecker::new(&mut cache); - let mut report = TargetValidationReport::default(); // The output is allowed to import a subset of the world's imports checker.invert(); + + let world_imports = wit_world.all_imports(types); + for (import, item_kind) in component_world.imports.iter() { - let Some(expected) = implicit_imported_interfaces - .get(import.as_str()) - .or_else(|| wit_world.imports.get(import)) - else { + let Some(expected) = world_imports.get(import.as_str(), &NameMapNoIntern) else { report.imports_not_in_target.insert(import.to_owned()); continue; }; @@ -140,9 +139,19 @@ pub fn validate_target( checker.revert(); + let component_exports = + component_world + .exports + .iter() + .fold(NameMap::default(), |mut map, (name, item)| { + // The unwrap here is safe because we allow shaddowing + map.insert(name, &mut NameMapNoIntern, true, *item).unwrap(); + map + }); + // The output must export every export in the world for (name, expected) in &wit_world.exports { - let Some(export) = component_world.exports.get(name).copied() else { + let Some(export) = component_exports.get(name, &NameMapNoIntern).copied() else { report.missing_exports.insert(name.to_owned(), *expected); continue; }; @@ -156,3 +165,24 @@ pub fn validate_target( report.into() } + +impl World { + /// This returns all implicit and explicit imports of the world as a mapping + /// of names to item kinds. The `NameMap` here is used to enable + /// semver-compatible matching of lookups + fn all_imports(&self, types: &Types) -> NameMap { + let mut map = NameMap::default(); + let mut intern = NameMapNoIntern; + // Add implicit imports from the world + for (name, kind) in self.implicit_imported_interfaces(types) { + // The unwrap here is safe because we allow shaddowing + map.insert(name, &mut intern, true, kind).unwrap(); + } + // Add explicit imports from the world + for (name, item) in &self.imports { + // The unwrap here is safe because we allow shaddowing. + map.insert(name, &mut intern, true, *item).unwrap(); + } + map + } +} diff --git a/crates/wac-types/tests/README.md b/crates/wac-types/tests/README.md new file mode 100644 index 0000000..13f14f3 --- /dev/null +++ b/crates/wac-types/tests/README.md @@ -0,0 +1,6 @@ +The `dummy_wasi_http@*.wasm`'s here were generated via the follow: +```console +$ wkg get wasi:http@* --format wasm +$ wasm-tools component wit wit/wasi_http@*.wasm -o wasi_http@*.wit +$ wasm-tools component embed --dummy wasi_http@*.wit | wasm-tools component new - -o dummy_wasi_http@*.wasm +``` \ No newline at end of file diff --git a/crates/wac-types/tests/dummy_wasi_http@0.2.0.wasm b/crates/wac-types/tests/dummy_wasi_http@0.2.0.wasm new file mode 100644 index 0000000000000000000000000000000000000000..41c4d3331ca244a724e2437c032fb6c398954e8e GIT binary patch literal 26977 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 literal 0 HcmV?d00001 diff --git a/crates/wac-types/tests/dummy_wasi_http@0.2.3.wasm b/crates/wac-types/tests/dummy_wasi_http@0.2.3.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b62d972f60d305e07c3adb7f798ccafc2bb91916 GIT binary patch literal 26993 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