blob: 906f6a7a136c897d30a85d0beffdee8242d9fe24 [file] [log] [blame] [edit]
use anyhow::{bail, Context, Result};
use id_arena::{Arena, Id};
use indexmap::IndexMap;
use semver::Version;
use std::borrow::Cow;
use std::fmt;
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;
use ast::lex::Span;
pub use ast::SourceMap;
pub use ast::{parse_use_path, ParsedUsePath};
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<World>;
pub type InterfaceId = Id<Interface>;
pub type TypeId = Id<TypeDef>;
/// 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<World>,
/// 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<Interface>,
/// 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<TypeDef>,
/// 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<PackageName, IndexMap<String, AstItem>>,
/// Doc comments for this package.
pub docs: Docs,
package_name_span: Span,
unknown_type_spans: Vec<Span>,
interface_spans: Vec<InterfaceSpan>,
world_spans: Vec<WorldSpan>,
type_spans: Vec<Span>,
foreign_dep_spans: Vec<Span>,
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<UnresolvedPackage>,
/// 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<Span>,
exports: Vec<Span>,
includes: Vec<Span>,
}
#[derive(Clone)]
struct InterfaceSpan {
span: Span,
funcs: Vec<Span>,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
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<Version>,
}
impl From<PackageName> 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<String>,
}
impl Error {
fn new(span: Span, msg: impl Into<String>) -> 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 {}
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<Path>, contents: &str) -> Result<UnresolvedPackageGroup> {
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<Path>) -> Result<UnresolvedPackageGroup> {
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<Path>) -> Result<UnresolvedPackageGroup> {
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<Path>) -> Result<UnresolvedPackageGroup> {
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<WorldKey, WorldItem>,
/// All exported items from this interface, both worlds and functions.
pub exports: IndexMap<WorldKey, WorldItem>,
/// The package that owns this world.
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))]
pub package: Option<PackageId>,
/// 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<Vec<IncludeName>>,
}
#[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, PartialEq, Eq, Hash)]
#[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 From<WorldKey> 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 = "lowercase"))]
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<String>,
/// 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<String, TypeId>,
/// Exported functions from this interface.
pub functions: IndexMap<String, Function>,
/// 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<PackageId>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct TypeDef {
pub name: Option<String>,
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)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
pub enum TypeDefKind {
Record(Record),
Resource,
Handle(Handle),
Flags(Flags),
Tuple(Tuple),
Variant(Variant),
Enum(Enum),
Option(Type),
Result(Result_),
List(Type),
Future(Option<Type>),
Stream(Stream),
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::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 = "lowercase"))]
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<T>`, 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 = "lowercase"))]
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,
Id(TypeId),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Int {
U8,
U16,
U32,
U64,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Record {
pub fields: Vec<Field>,
}
#[derive(Debug, Clone, PartialEq)]
#[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)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Flags {
pub flags: Vec<Flag>,
}
#[derive(Debug, Clone, PartialEq)]
#[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)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Tuple {
pub types: Vec<Type>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Variant {
pub cases: Vec<Case>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Case {
pub name: String,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: Option<Type>,
#[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)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Enum {
pub cases: Vec<EnumCase>,
}
#[derive(Debug, Clone, PartialEq)]
#[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)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Result_ {
pub ok: Option<Type>,
pub err: Option<Type>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Stream {
pub element: Option<Type>,
pub end: Option<Type>,
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Docs {
pub contents: Option<String>,
}
impl Docs {
pub fn is_empty(&self) -> bool {
self.contents.is_none()
}
}
pub type Params = Vec<(String, Type)>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Results {
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_params"))]
Named(Params),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_anon_result"))]
Anon(Type),
}
pub enum ResultsTypeIter<'a> {
Named(std::slice::Iter<'a, (String, Type)>),
Anon(std::iter::Once<&'a Type>),
}
impl<'a> Iterator for ResultsTypeIter<'a> {
type Item = &'a Type;
fn next(&mut self) -> Option<&'a Type> {
match self {
ResultsTypeIter::Named(ps) => ps.next().map(|p| &p.1),
ResultsTypeIter::Anon(ty) => ty.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
ResultsTypeIter::Named(ps) => ps.size_hint(),
ResultsTypeIter::Anon(ty) => ty.size_hint(),
}
}
}
impl<'a> ExactSizeIterator for ResultsTypeIter<'a> {}
impl Results {
// For the common case of an empty results list.
pub fn empty() -> Results {
Results::Named(Vec::new())
}
pub fn len(&self) -> usize {
match self {
Results::Named(params) => params.len(),
Results::Anon(_) => 1,
}
}
pub fn throws<'a>(&self, resolve: &'a Resolve) -> Option<(Option<&'a Type>, Option<&'a Type>)> {
if self.len() != 1 {
return None;
}
match self.iter_types().next().unwrap() {
Type::Id(id) => match &resolve.types[*id].kind {
TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())),
_ => None,
},
_ => None,
}
}
pub fn iter_types(&self) -> ResultsTypeIter {
match self {
Results::Named(ps) => ResultsTypeIter::Named(ps.iter()),
Results::Anon(ty) => ResultsTypeIter::Anon(std::iter::once(ty)),
}
}
}
#[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: Params,
pub results: Results,
#[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 = "lowercase"))]
pub enum FunctionKind {
Freestanding,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
Method(TypeId),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
Static(TypeId),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
Constructor(TypeId),
}
impl FunctionKind {
/// Returns the resource, if present, that this function kind refers to.
pub fn resource(&self) -> Option<TypeId> {
match self {
FunctionKind::Freestanding => None,
FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(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<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 {
FunctionKind::Freestanding => &self.name,
FunctionKind::Method(_) | FunctionKind::Static(_) => {
&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<Item = Type> + '_ {
self.params
.iter()
.map(|(_, t)| *t)
.chain(self.results.iter_types().copied())
}
/// 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),
},
}
}
}
/// 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.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde_derive::Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
pub enum Stability {
/// `@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<Version>,
},
/// `@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<Version>,
},
/// This item does not have either `@since` or `@unstable`.
Unknown,
}
impl Stability {
/// Returns whether this is `Stability::Unknown`.
pub fn is_unknown(&self) -> bool {
matches!(self, Stability::Unknown)
}
}
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);
}
}
}