use crate::abi::AbiVariant; use anyhow::{Context, Result, bail}; use id_arena::{Arena, Id}; use indexmap::IndexMap; use semver::Version; use std::borrow::Cow; use std::fmt; use std::hash::{Hash, Hasher}; use std::path::Path; #[cfg(feature = "decoding")] pub mod decoding; #[cfg(feature = "decoding")] mod metadata; #[cfg(feature = "decoding")] pub use metadata::PackageMetadata; pub mod abi; mod ast; pub use ast::SourceMap; use ast::lex::Span; pub use ast::{ParsedUsePath, parse_use_path}; mod sizealign; pub use sizealign::*; mod resolve; pub use resolve::*; mod live; pub use live::{LiveTypes, TypeIdVisitor}; #[cfg(feature = "serde")] use serde_derive::Serialize; #[cfg(feature = "serde")] mod serde_; #[cfg(feature = "serde")] use serde_::*; /// Checks if the given string is a legal identifier in wit. pub fn validate_id(s: &str) -> Result<()> { ast::validate_id(0, s)?; Ok(()) } pub type WorldId = Id; pub type InterfaceId = Id; pub type TypeId = Id; /// Representation of a parsed WIT package which has not resolved external /// dependencies yet. /// /// This representation has performed internal resolution of the WIT package /// itself, ensuring that all references internally are valid and the WIT was /// syntactically valid and such. /// /// The fields of this structure represent a flat list of arrays unioned from /// all documents within the WIT package. This means, for example, that all /// types from all documents are located in `self.types`. The fields of each /// item can help splitting back out into packages/interfaces/etc as necessary. /// /// Note that an `UnresolvedPackage` cannot be queried in general about /// information such as size or alignment as that would require resolution of /// foreign dependencies. Translations such as to-binary additionally are not /// supported on an `UnresolvedPackage` due to the lack of knowledge about the /// foreign types. This is intended to be an intermediate state which can be /// inspected by embedders, if necessary, before quickly transforming to a /// [`Resolve`] to fully work with a WIT package. /// /// After an [`UnresolvedPackage`] is parsed it can be fully resolved with /// [`Resolve::push`]. During this operation a dependency map is specified which /// will connect the `foreign_deps` field of this structure to packages /// previously inserted within the [`Resolve`]. Embedders are responsible for /// performing this resolution themselves. #[derive(Clone)] pub struct UnresolvedPackage { /// The namespace, name, and version information for this package. pub name: PackageName, /// All worlds from all documents within this package. /// /// Each world lists the document that it is from. pub worlds: Arena, /// All interfaces from all documents within this package. /// /// Each interface lists the document that it is from. Interfaces are listed /// in topological order as well so iteration through this arena will only /// reference prior elements already visited when working with recursive /// references. pub interfaces: Arena, /// All types from all documents within this package. /// /// Each type lists the interface or world that defined it, or nothing if /// it's an anonymous type. Types are listed in this arena in topological /// order to ensure that iteration through this arena will only reference /// other types transitively that are already iterated over. pub types: Arena, /// All foreign dependencies that this package depends on. /// /// These foreign dependencies must be resolved to convert this unresolved /// package into a `Resolve`. The map here is keyed by the name of the /// foreign package that this depends on, and the sub-map is keyed by an /// interface name followed by the identifier within `self.interfaces`. The /// fields of `self.interfaces` describes the required types that are from /// each foreign interface. pub foreign_deps: IndexMap)>>, /// Doc comments for this package. pub docs: Docs, package_name_span: Span, unknown_type_spans: Vec, interface_spans: Vec, world_spans: Vec, type_spans: Vec, foreign_dep_spans: Vec, required_resource_types: Vec<(TypeId, Span)>, } /// Tracks a set of packages, all pulled from the same group of WIT source files. #[derive(Clone)] pub struct UnresolvedPackageGroup { /// The "main" package in this package group which was found at the root of /// the WIT files. /// /// Note that this is required to be present in all WIT files. pub main: UnresolvedPackage, /// Nested packages found while parsing `main`, if any. pub nested: Vec, /// A set of processed source files from which these packages have been parsed. pub source_map: SourceMap, } #[derive(Clone)] struct WorldSpan { span: Span, imports: Vec, exports: Vec, includes: Vec, } #[derive(Clone)] struct InterfaceSpan { span: Span, funcs: Vec, } #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum AstItem { #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Interface(InterfaceId), #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] World(WorldId), } /// A structure used to keep track of the name of a package, containing optional /// information such as a namespace and version information. /// /// This is directly encoded as an "ID" in the binary component representation /// with an interfaced tacked on as well. #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(into = "String"))] pub struct PackageName { /// A namespace such as `wasi` in `wasi:foo/bar` pub namespace: String, /// The kebab-name of this package, which is always specified. pub name: String, /// Optional major/minor version information. pub version: Option, } impl From for String { fn from(name: PackageName) -> String { name.to_string() } } impl PackageName { /// Returns the ID that this package name would assign the `interface` name /// specified. pub fn interface_id(&self, interface: &str) -> String { let mut s = String::new(); s.push_str(&format!("{}:{}/{interface}", self.namespace, self.name)); if let Some(version) = &self.version { s.push_str(&format!("@{version}")); } s } /// Determines the "semver compatible track" for the given version. /// /// This method implements the logic from the component model where semver /// versions can be compatible with one another. For example versions 1.2.0 /// and 1.2.1 would be considered both compatible with one another because /// they're on the same semver compatible track. /// /// This predicate is used during /// [`Resolve::merge_world_imports_based_on_semver`] for example to /// determine whether two imports can be merged together. This is /// additionally used when creating components to match up imports in /// core wasm to imports in worlds. pub fn version_compat_track(version: &Version) -> Version { let mut version = version.clone(); version.build = semver::BuildMetadata::EMPTY; if !version.pre.is_empty() { return version; } if version.major != 0 { version.minor = 0; version.patch = 0; return version; } if version.minor != 0 { version.patch = 0; return version; } version } /// Returns the string corresponding to /// [`PackageName::version_compat_track`]. This is done to match the /// component model's expected naming scheme of imports and exports. pub fn version_compat_track_string(version: &Version) -> String { let version = Self::version_compat_track(version); if !version.pre.is_empty() { return version.to_string(); } if version.major != 0 { return format!("{}", version.major); } if version.minor != 0 { return format!("{}.{}", version.major, version.minor); } version.to_string() } } impl fmt::Display for PackageName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.namespace, self.name)?; if let Some(version) = &self.version { write!(f, "@{version}")?; } Ok(()) } } #[derive(Debug)] struct Error { span: Span, msg: String, highlighted: Option, } impl Error { fn new(span: Span, msg: impl Into) -> Error { Error { span, msg: msg.into(), highlighted: None, } } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.highlighted.as_ref().unwrap_or(&self.msg).fmt(f) } } impl std::error::Error for Error {} #[derive(Debug)] struct PackageNotFoundError { span: Span, requested: PackageName, known: Vec, highlighted: Option, } impl PackageNotFoundError { pub fn new(span: Span, requested: PackageName, known: Vec) -> Self { Self { span, requested, known, highlighted: None, } } } impl fmt::Display for PackageNotFoundError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(highlighted) = &self.highlighted { return highlighted.fmt(f); } if self.known.is_empty() { write!( f, "package '{}' not found. no known packages.", self.requested )?; } else { write!( f, "package '{}' not found. known packages:\n", self.requested )?; for known in self.known.iter() { write!(f, " {known}\n")?; } } Ok(()) } } impl std::error::Error for PackageNotFoundError {} impl UnresolvedPackageGroup { /// Parses the given string as a wit document. /// /// The `path` argument is used for error reporting. The `contents` provided /// are considered to be the contents of `path`. This function does not read /// the filesystem. pub fn parse(path: impl AsRef, contents: &str) -> Result { let mut map = SourceMap::default(); map.push(path.as_ref(), contents); map.parse() } /// Parse a WIT package at the provided path. /// /// The path provided is inferred whether it's a file or a directory. A file /// is parsed with [`UnresolvedPackageGroup::parse_file`] and a directory is /// parsed with [`UnresolvedPackageGroup::parse_dir`]. pub fn parse_path(path: impl AsRef) -> Result { let path = path.as_ref(); if path.is_dir() { UnresolvedPackageGroup::parse_dir(path) } else { UnresolvedPackageGroup::parse_file(path) } } /// Parses a WIT package from the file provided. /// /// The return value represents all packages found in the WIT file which /// might be either one or multiple depending on the syntax used. pub fn parse_file(path: impl AsRef) -> Result { let path = path.as_ref(); let contents = std::fs::read_to_string(path) .with_context(|| format!("failed to read file {path:?}"))?; Self::parse(path, &contents) } /// Parses a WIT package from the directory provided. /// /// This method will look at all files under the `path` specified. All /// `*.wit` files are parsed and assumed to be part of the same package /// grouping. This is useful when a WIT package is split across multiple /// files. pub fn parse_dir(path: impl AsRef) -> Result { let path = path.as_ref(); let mut map = SourceMap::default(); let cx = || format!("failed to read directory {path:?}"); for entry in path.read_dir().with_context(&cx)? { let entry = entry.with_context(&cx)?; let path = entry.path(); let ty = entry.file_type().with_context(&cx)?; if ty.is_dir() { continue; } if ty.is_symlink() { if path.is_dir() { continue; } } let filename = match path.file_name().and_then(|s| s.to_str()) { Some(name) => name, None => continue, }; if !filename.ends_with(".wit") { continue; } map.push_file(&path)?; } map.parse() } } #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct World { /// The WIT identifier name of this world. pub name: String, /// All imported items into this interface, both worlds and functions. pub imports: IndexMap, /// All exported items from this interface, both worlds and functions. pub exports: IndexMap, /// The package that owns this world. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))] pub package: Option, /// Documentation associated with this world declaration. #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, /// Stability annotation for this world itself. #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Stability::is_unknown") )] pub stability: Stability, /// All the included worlds from this world. Empty if this is fully resolved #[cfg_attr(feature = "serde", serde(skip))] pub includes: Vec<(Stability, WorldId)>, /// All the included worlds names. Empty if this is fully resolved #[cfg_attr(feature = "serde", serde(skip))] pub include_names: Vec>, } #[derive(Debug, Clone)] pub struct IncludeName { /// The name of the item pub name: String, /// The name to be replaced with pub as_: String, } /// The key to the import/export maps of a world. Either a kebab-name or a /// unique interface. #[derive(Debug, Clone, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(into = "String"))] pub enum WorldKey { /// A kebab-name. Name(String), /// An interface which is assigned no kebab-name. Interface(InterfaceId), } impl Hash for WorldKey { fn hash(&self, hasher: &mut H) { match self { WorldKey::Name(s) => { 0u8.hash(hasher); s.as_str().hash(hasher); } WorldKey::Interface(i) => { 1u8.hash(hasher); i.hash(hasher); } } } } impl PartialEq for WorldKey { fn eq(&self, other: &WorldKey) -> bool { match (self, other) { (WorldKey::Name(a), WorldKey::Name(b)) => a.as_str() == b.as_str(), (WorldKey::Name(_), _) => false, (WorldKey::Interface(a), WorldKey::Interface(b)) => a == b, (WorldKey::Interface(_), _) => false, } } } impl From for String { fn from(key: WorldKey) -> String { match key { WorldKey::Name(name) => name, WorldKey::Interface(id) => format!("interface-{}", id.index()), } } } impl WorldKey { /// Asserts that this is `WorldKey::Name` and returns the name. #[track_caller] pub fn unwrap_name(self) -> String { match self { WorldKey::Name(name) => name, WorldKey::Interface(_) => panic!("expected a name, found interface"), } } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum WorldItem { /// An interface is being imported or exported from a world, indicating that /// it's a namespace of functions. Interface { #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] id: InterfaceId, #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Stability::is_unknown") )] stability: Stability, }, /// A function is being directly imported or exported from this world. Function(Function), /// A type is being exported from this world. /// /// Note that types are never imported into worlds at this time. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Type(TypeId), } impl WorldItem { pub fn stability<'a>(&'a self, resolve: &'a Resolve) -> &'a Stability { match self { WorldItem::Interface { stability, .. } => stability, WorldItem::Function(f) => &f.stability, WorldItem::Type(id) => &resolve.types[*id].stability, } } } #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Interface { /// Optionally listed name of this interface. /// /// This is `None` for inline interfaces in worlds. pub name: Option, /// Exported types from this interface. /// /// Export names are listed within the types themselves. Note that the /// export name here matches the name listed in the `TypeDef`. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] pub types: IndexMap, /// Exported functions from this interface. pub functions: IndexMap, /// Documentation associated with this interface. #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, /// Stability attribute for this interface. #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Stability::is_unknown") )] pub stability: Stability, /// The package that owns this interface. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))] pub package: Option, } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct TypeDef { pub name: Option, pub kind: TypeDefKind, pub owner: TypeOwner, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, /// Stability attribute for this type. #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Stability::is_unknown") )] pub stability: Stability, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum TypeDefKind { Record(Record), Resource, Handle(Handle), Flags(Flags), Tuple(Tuple), Variant(Variant), Enum(Enum), Option(Type), Result(Result_), List(Type), Map(Type, Type), FixedSizeList(Type, u32), Future(Option), Stream(Option), Type(Type), /// This represents a type of unknown structure imported from a foreign /// interface. /// /// This variant is only used during the creation of `UnresolvedPackage` but /// by the time a `Resolve` is created then this will not exist. Unknown, } impl TypeDefKind { pub fn as_str(&self) -> &'static str { match self { TypeDefKind::Record(_) => "record", TypeDefKind::Resource => "resource", TypeDefKind::Handle(handle) => match handle { Handle::Own(_) => "own", Handle::Borrow(_) => "borrow", }, TypeDefKind::Flags(_) => "flags", TypeDefKind::Tuple(_) => "tuple", TypeDefKind::Variant(_) => "variant", TypeDefKind::Enum(_) => "enum", TypeDefKind::Option(_) => "option", TypeDefKind::Result(_) => "result", TypeDefKind::List(_) => "list", TypeDefKind::Map(_, _) => "map", TypeDefKind::FixedSizeList(..) => "fixed size list", TypeDefKind::Future(_) => "future", TypeDefKind::Stream(_) => "stream", TypeDefKind::Type(_) => "type", TypeDefKind::Unknown => "unknown", } } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum TypeOwner { /// This type was defined within a `world` block. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] World(WorldId), /// This type was defined within an `interface` block. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Interface(InterfaceId), /// This type wasn't inherently defined anywhere, such as a `list`, which /// doesn't need an owner. #[cfg_attr(feature = "serde", serde(untagged, serialize_with = "serialize_none"))] None, } #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Handle { #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Own(TypeId), #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Borrow(TypeId), } #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum Type { Bool, U8, U16, U32, U64, S8, S16, S32, S64, F32, F64, Char, String, ErrorContext, Id(TypeId), } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Int { U8, U16, U32, U64, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Record { pub fields: Vec, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Field { pub name: String, #[cfg_attr(feature = "serde", serde(rename = "type"))] pub ty: Type, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Flags { pub flags: Vec, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Flag { pub name: String, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, } #[derive(Debug, Clone, PartialEq)] pub enum FlagsRepr { U8, U16, U32(usize), } impl Flags { pub fn repr(&self) -> FlagsRepr { match self.flags.len() { 0 => FlagsRepr::U32(0), n if n <= 8 => FlagsRepr::U8, n if n <= 16 => FlagsRepr::U16, n => FlagsRepr::U32(sizealign::align_to(n, 32) / 32), } } } impl FlagsRepr { pub fn count(&self) -> usize { match self { FlagsRepr::U8 => 1, FlagsRepr::U16 => 1, FlagsRepr::U32(n) => *n, } } } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Tuple { pub types: Vec, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Variant { pub cases: Vec, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Case { pub name: String, #[cfg_attr(feature = "serde", serde(rename = "type"))] pub ty: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, } impl Variant { pub fn tag(&self) -> Int { discriminant_type(self.cases.len()) } } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Enum { pub cases: Vec, } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct EnumCase { pub name: String, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, } impl Enum { pub fn tag(&self) -> Int { discriminant_type(self.cases.len()) } } /// This corresponds to the `discriminant_type` function in the Canonical ABI. fn discriminant_type(num_cases: usize) -> Int { match num_cases.checked_sub(1) { None => Int::U8, Some(n) if n <= u8::max_value() as usize => Int::U8, Some(n) if n <= u16::max_value() as usize => Int::U16, Some(n) if n <= u32::max_value() as usize => Int::U32, _ => panic!("too many cases to fit in a repr"), } } #[derive(Debug, Clone, PartialEq, Hash, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Result_ { pub ok: Option, pub err: Option, } #[derive(Clone, Default, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Docs { pub contents: Option, } impl Docs { pub fn is_empty(&self) -> bool { self.contents.is_none() } } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Function { pub name: String, pub kind: FunctionKind, #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_params"))] pub params: Vec<(String, Type)>, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub result: Option, #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] pub docs: Docs, /// Stability attribute for this function. #[cfg_attr( feature = "serde", serde(skip_serializing_if = "Stability::is_unknown") )] pub stability: Stability, } #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum FunctionKind { /// A freestanding function. /// /// ```wit /// interface foo { /// the-func: func(); /// } /// ``` Freestanding, /// An async freestanding function. /// /// ```wit /// interface foo { /// the-func: async func(); /// } /// ``` AsyncFreestanding, /// A resource method where the first parameter is implicitly /// `borrow`. /// /// ```wit /// interface foo { /// resource r { /// the-func: func(); /// } /// } /// ``` #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Method(TypeId), /// An async resource method where the first parameter is implicitly /// `borrow`. /// /// ```wit /// interface foo { /// resource r { /// the-func: async func(); /// } /// } /// ``` #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] AsyncMethod(TypeId), /// A static resource method. /// /// ```wit /// interface foo { /// resource r { /// the-func: static func(); /// } /// } /// ``` #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Static(TypeId), /// An async static resource method. /// /// ```wit /// interface foo { /// resource r { /// the-func: static async func(); /// } /// } /// ``` #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] AsyncStatic(TypeId), /// A resource constructor where the return value is implicitly `own`. /// /// ```wit /// interface foo { /// resource r { /// constructor(); /// } /// } /// ``` #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] Constructor(TypeId), } impl FunctionKind { /// Returns whether this is an async function or not. pub fn is_async(&self) -> bool { match self { FunctionKind::Freestanding | FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => false, FunctionKind::AsyncFreestanding | FunctionKind::AsyncMethod(_) | FunctionKind::AsyncStatic(_) => true, } } /// Returns the resource, if present, that this function kind refers to. pub fn resource(&self) -> Option { match self { FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None, FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) | FunctionKind::AsyncMethod(id) | FunctionKind::AsyncStatic(id) => Some(*id), } } /// Returns the resource, if present, that this function kind refers to. pub fn resource_mut(&mut self) -> Option<&mut TypeId> { match self { FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None, FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) | FunctionKind::AsyncMethod(id) | FunctionKind::AsyncStatic(id) => Some(id), } } } /// Possible forms of name mangling that are supported by this crate. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Mangling { /// The "standard" component model mangling format for 32-bit linear /// memories. This is specified in WebAssembly/component-model#378 Standard32, /// The "legacy" name mangling supported in versions 218-and-prior for this /// crate. This is the original support for how components were created from /// core wasm modules and this does not correspond to any standard. This is /// preserved for now while tools transition to the new scheme. Legacy, } impl std::str::FromStr for Mangling { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "legacy" => Ok(Mangling::Legacy), "standard32" => Ok(Mangling::Standard32), _ => { bail!( "unknown name mangling `{s}`, \ supported values are `legacy` or `standard32`" ) } } } } /// Possible lift/lower ABI choices supported when mangling names. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum LiftLowerAbi { /// Both imports and exports will use the synchronous ABI. Sync, /// Both imports and exports will use the async ABI (with a callback for /// each export). AsyncCallback, /// Both imports and exports will use the async ABI (with no callbacks for /// exports). AsyncStackful, } impl LiftLowerAbi { fn import_prefix(self) -> &'static str { match self { Self::Sync => "", Self::AsyncCallback | Self::AsyncStackful => "[async-lower]", } } /// Get the import [`AbiVariant`] corresponding to this [`LiftLowerAbi`] pub fn import_variant(self) -> AbiVariant { match self { Self::Sync => AbiVariant::GuestImport, Self::AsyncCallback | Self::AsyncStackful => AbiVariant::GuestImportAsync, } } fn export_prefix(self) -> &'static str { match self { Self::Sync => "", Self::AsyncCallback => "[async-lift]", Self::AsyncStackful => "[async-lift-stackful]", } } /// Get the export [`AbiVariant`] corresponding to this [`LiftLowerAbi`] pub fn export_variant(self) -> AbiVariant { match self { Self::Sync => AbiVariant::GuestExport, Self::AsyncCallback => AbiVariant::GuestExportAsync, Self::AsyncStackful => AbiVariant::GuestExportAsyncStackful, } } } /// Combination of [`Mangling`] and [`LiftLowerAbi`]. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum ManglingAndAbi { /// See [`Mangling::Standard32`]. /// /// As of this writing, the standard name mangling only supports the /// synchronous ABI. Standard32, /// See [`Mangling::Legacy`] and [`LiftLowerAbi`]. Legacy(LiftLowerAbi), } impl ManglingAndAbi { /// Get the import [`AbiVariant`] corresponding to this [`ManglingAndAbi`] pub fn import_variant(self) -> AbiVariant { match self { Self::Standard32 => AbiVariant::GuestImport, Self::Legacy(abi) => abi.import_variant(), } } /// Get the export [`AbiVariant`] corresponding to this [`ManglingAndAbi`] pub fn export_variant(self) -> AbiVariant { match self { Self::Standard32 => AbiVariant::GuestExport, Self::Legacy(abi) => abi.export_variant(), } } /// Switch the ABI to be sync if it's async. pub fn sync(self) -> Self { match self { Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => self, Self::Legacy(LiftLowerAbi::AsyncCallback) | Self::Legacy(LiftLowerAbi::AsyncStackful) => Self::Legacy(LiftLowerAbi::Sync), } } /// Returns whether this is an async ABI pub fn is_async(&self) -> bool { match self { Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => false, Self::Legacy(LiftLowerAbi::AsyncCallback) | Self::Legacy(LiftLowerAbi::AsyncStackful) => true, } } pub fn mangling(&self) -> Mangling { match self { Self::Standard32 => Mangling::Standard32, Self::Legacy(_) => Mangling::Legacy, } } } impl Function { pub fn item_name(&self) -> &str { match &self.kind { FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &self.name, FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::AsyncMethod(_) | FunctionKind::AsyncStatic(_) => &self.name[self.name.find('.').unwrap() + 1..], FunctionKind::Constructor(_) => "constructor", } } /// Returns an iterator over the types used in parameters and results. /// /// Note that this iterator is not transitive, it only iterates over the /// direct references to types that this function has. pub fn parameter_and_result_types(&self) -> impl Iterator + '_ { self.params.iter().map(|(_, t)| *t).chain(self.result) } /// Gets the core export name for this function. pub fn standard32_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { self.core_export_name(interface, Mangling::Standard32) } pub fn legacy_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { self.core_export_name(interface, Mangling::Legacy) } /// Gets the core export name for this function. pub fn core_export_name<'a>( &'a self, interface: Option<&str>, mangling: Mangling, ) -> Cow<'a, str> { match interface { Some(interface) => match mangling { Mangling::Standard32 => Cow::Owned(format!("cm32p2|{interface}|{}", self.name)), Mangling::Legacy => Cow::Owned(format!("{interface}#{}", self.name)), }, None => match mangling { Mangling::Standard32 => Cow::Owned(format!("cm32p2||{}", self.name)), Mangling::Legacy => Cow::Borrowed(&self.name), }, } } /// Collect any future and stream types appearing in the signature of this /// function by doing a depth-first search over the parameter types and then /// the result types. /// /// For example, given the WIT function `foo: func(x: future>, /// y: u32) -> stream`, we would return `[future, /// future>, stream]`. /// /// This may be used by binding generators to refer to specific `future` and /// `stream` types when importing canonical built-ins such as `stream.new`, /// `future.read`, etc. Using the example above, the import /// `[future-new-0]foo` would indicate a call to `future.new` for the type /// `future`. Likewise, `[future-new-1]foo` would indicate a call to /// `future.new` for `future>`, and `[stream-new-2]foo` would /// indicate a call to `stream.new` for `stream`. pub fn find_futures_and_streams(&self, resolve: &Resolve) -> Vec { let mut results = Vec::new(); for (_, ty) in self.params.iter() { find_futures_and_streams(resolve, *ty, &mut results); } if let Some(ty) = self.result { find_futures_and_streams(resolve, ty, &mut results); } results } /// Check if this function is a resource constructor in shorthand form. /// I.e. without an explicit return type annotation. pub fn is_constructor_shorthand(&self, resolve: &Resolve) -> bool { let FunctionKind::Constructor(containing_resource_id) = self.kind else { return false; }; let Some(Type::Id(id)) = &self.result else { return false; }; let TypeDefKind::Handle(Handle::Own(returned_resource_id)) = resolve.types[*id].kind else { return false; }; return containing_resource_id == returned_resource_id; } /// Returns the `module`, `name`, and signature to use when importing this /// function's `task.return` intrinsic using the `mangling` specified. pub fn task_return_import( &self, resolve: &Resolve, interface: Option<&WorldKey>, mangling: Mangling, ) -> (String, String, abi::WasmSignature) { match mangling { Mangling::Standard32 => todo!(), Mangling::Legacy => {} } // For exported async functions, generate a `task.return` intrinsic. let module = match interface { Some(key) => format!("[export]{}", resolve.name_world_key(key)), None => "[export]$root".to_string(), }; let name = format!("[task-return]{}", self.name); let mut func_tmp = self.clone(); func_tmp.params = Vec::new(); func_tmp.result = None; if let Some(ty) = self.result { func_tmp.params.push(("x".to_string(), ty)); } let sig = resolve.wasm_signature(AbiVariant::GuestImport, &func_tmp); (module, name, sig) } // push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func); } fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec) { let Type::Id(id) = ty else { return; }; match &resolve.types[id].kind { TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => {} TypeDefKind::Record(r) => { for Field { ty, .. } in &r.fields { find_futures_and_streams(resolve, *ty, results); } } TypeDefKind::Tuple(t) => { for ty in &t.types { find_futures_and_streams(resolve, *ty, results); } } TypeDefKind::Variant(v) => { for Case { ty, .. } in &v.cases { if let Some(ty) = ty { find_futures_and_streams(resolve, *ty, results); } } } TypeDefKind::Option(ty) | TypeDefKind::List(ty) | TypeDefKind::FixedSizeList(ty, ..) | TypeDefKind::Type(ty) => { find_futures_and_streams(resolve, *ty, results); } TypeDefKind::Map(k, v) => { find_futures_and_streams(resolve, *k, results); find_futures_and_streams(resolve, *v, results); } TypeDefKind::Result(r) => { if let Some(ty) = r.ok { find_futures_and_streams(resolve, ty, results); } if let Some(ty) = r.err { find_futures_and_streams(resolve, ty, results); } } TypeDefKind::Future(ty) => { if let Some(ty) = ty { find_futures_and_streams(resolve, *ty, results); } results.push(id); } TypeDefKind::Stream(ty) => { if let Some(ty) = ty { find_futures_and_streams(resolve, *ty, results); } results.push(id); } TypeDefKind::Unknown => unreachable!(), } } /// Representation of the stability attributes associated with a world, /// interface, function, or type. /// /// This is added for WebAssembly/component-model#332 where @since and @unstable /// annotations were added to WIT. /// /// The order of the of enum values is significant since it is used with Ord and PartialOrd #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(serde_derive::Deserialize, Serialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum Stability { /// This item does not have either `@since` or `@unstable`. Unknown, /// `@unstable(feature = foo)` /// /// This item is explicitly tagged `@unstable`. A feature name is listed and /// this item is excluded by default in `Resolve` unless explicitly enabled. Unstable { feature: String, #[cfg_attr( feature = "serde", serde( skip_serializing_if = "Option::is_none", default, serialize_with = "serialize_optional_version", deserialize_with = "deserialize_optional_version" ) )] deprecated: Option, }, /// `@since(version = 1.2.3)` /// /// This item is explicitly tagged with `@since` as stable since the /// specified version. This may optionally have a feature listed as well. Stable { #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_version"))] #[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_version"))] since: Version, #[cfg_attr( feature = "serde", serde( skip_serializing_if = "Option::is_none", default, serialize_with = "serialize_optional_version", deserialize_with = "deserialize_optional_version" ) )] deprecated: Option, }, } impl Stability { /// Returns whether this is `Stability::Unknown`. pub fn is_unknown(&self) -> bool { matches!(self, Stability::Unknown) } pub fn is_stable(&self) -> bool { matches!(self, Stability::Stable { .. }) } } impl Default for Stability { fn default() -> Stability { Stability::Unknown } } #[cfg(test)] mod test { use super::*; #[test] fn test_discriminant_type() { assert_eq!(discriminant_type(1), Int::U8); assert_eq!(discriminant_type(0x100), Int::U8); assert_eq!(discriminant_type(0x101), Int::U16); assert_eq!(discriminant_type(0x10000), Int::U16); assert_eq!(discriminant_type(0x10001), Int::U32); if let Ok(num_cases) = usize::try_from(0x100000000_u64) { assert_eq!(discriminant_type(num_cases), Int::U32); } } #[test] fn test_find_futures_and_streams() { let mut resolve = Resolve::default(); let t0 = resolve.types.alloc(TypeDef { name: None, kind: TypeDefKind::Future(Some(Type::U32)), owner: TypeOwner::None, docs: Docs::default(), stability: Stability::Unknown, }); let t1 = resolve.types.alloc(TypeDef { name: None, kind: TypeDefKind::Future(Some(Type::Id(t0))), owner: TypeOwner::None, docs: Docs::default(), stability: Stability::Unknown, }); let t2 = resolve.types.alloc(TypeDef { name: None, kind: TypeDefKind::Stream(Some(Type::U32)), owner: TypeOwner::None, docs: Docs::default(), stability: Stability::Unknown, }); let found = Function { name: "foo".into(), kind: FunctionKind::Freestanding, params: vec![("p1".into(), Type::Id(t1)), ("p2".into(), Type::U32)], result: Some(Type::Id(t2)), docs: Docs::default(), stability: Stability::Unknown, } .find_futures_and_streams(&resolve); assert_eq!(3, found.len()); assert_eq!(t0, found[0]); assert_eq!(t1, found[1]); assert_eq!(t2, found[2]); } }