#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use crate::abi::AbiVariant;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
#[cfg(feature = "std")]
use anyhow::Context;
use anyhow::{Result, bail};
use id_arena::{Arena, Id};
use semver::Version;
#[cfg(feature = "std")]
pub type IndexMap<K, V> = indexmap::IndexMap<K, V, std::hash::RandomState>;
#[cfg(feature = "std")]
pub type IndexSet<T> = indexmap::IndexSet<T, std::hash::RandomState>;
#[cfg(not(feature = "std"))]
pub type IndexMap<K, V> = indexmap::IndexMap<K, V, hashbrown::DefaultHashBuilder>;
#[cfg(not(feature = "std"))]
pub type IndexSet<T> = indexmap::IndexSet<T, hashbrown::DefaultHashBuilder>;
#[cfg(feature = "std")]
pub(crate) use std::collections::{HashMap, HashSet};
#[cfg(not(feature = "std"))]
pub(crate) use hashbrown::{HashMap, HashSet};
use alloc::borrow::Cow;
use core::fmt;
use core::hash::{Hash, Hasher};
#[cfg(feature = "std")]
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;
pub use ast::error::*;
pub 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_::*;
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>;
#[derive(Clone, PartialEq, Eq)]
pub struct UnresolvedPackage {
pub name: PackageName,
pub worlds: Arena<World>,
pub interfaces: Arena<Interface>,
pub types: Arena<TypeDef>,
pub foreign_deps: IndexMap<PackageName, IndexMap<String, (AstItem, Vec<Stability>)>>,
pub docs: Docs,
#[cfg_attr(not(feature = "std"), allow(dead_code))]
package_name_span: Span,
unknown_type_spans: Vec<Span>,
foreign_dep_spans: Vec<Span>,
required_resource_types: Vec<(TypeId, Span)>,
}
impl UnresolvedPackage {
pub(crate) fn adjust_spans(&mut self, offset: u32) {
self.package_name_span.adjust(offset);
for span in &mut self.unknown_type_spans {
span.adjust(offset);
}
for span in &mut self.foreign_dep_spans {
span.adjust(offset);
}
for (_, span) in &mut self.required_resource_types {
span.adjust(offset);
}
for (_, world) in self.worlds.iter_mut() {
world.adjust_spans(offset);
}
for (_, iface) in self.interfaces.iter_mut() {
iface.adjust_spans(offset);
}
for (_, ty) in self.types.iter_mut() {
ty.adjust_spans(offset);
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct UnresolvedPackageGroup {
pub main: UnresolvedPackage,
pub nested: Vec<UnresolvedPackage>,
pub source_map: SourceMap,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[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),
}
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(into = "String"))]
pub struct PackageName {
pub namespace: String,
pub name: String,
pub version: Option<Version>,
}
impl From<PackageName> for String {
fn from(name: PackageName) -> String {
name.to_string()
}
}
impl PackageName {
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
}
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
}
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,
}
}
fn highlight(&mut self, source_map: &ast::SourceMap) {
if self.highlighted.is_none() {
self.highlighted = source_map.highlight_span(self.span, &self.msg);
}
}
}
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 core::error::Error for Error {}
#[derive(Debug)]
struct PackageNotFoundError {
span: Span,
requested: PackageName,
known: Vec<PackageName>,
highlighted: Option<String>,
}
impl PackageNotFoundError {
pub fn new(span: Span, requested: PackageName, known: Vec<PackageName>) -> Self {
Self {
span,
requested,
known,
highlighted: None,
}
}
fn highlight(&mut self, source_map: &ast::SourceMap) {
if self.highlighted.is_none() {
self.highlighted = source_map.highlight_span(self.span, &format!("{self}"));
}
}
}
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 core::error::Error for PackageNotFoundError {}
impl UnresolvedPackageGroup {
#[cfg(feature = "std")]
pub fn parse(path: impl AsRef<Path>, contents: &str) -> Result<UnresolvedPackageGroup> {
let path = path
.as_ref()
.to_str()
.ok_or_else(|| anyhow::anyhow!("path is not valid utf-8: {:?}", path.as_ref()))?;
let mut map = SourceMap::default();
map.push_str(path, contents);
map.parse()
.map_err(|(map, e)| anyhow::anyhow!("{}", e.highlight(&map)))
}
#[cfg(feature = "std")]
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()
.map_err(|(map, e)| anyhow::anyhow!("{}", e.highlight(&map)))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct World {
pub name: String,
pub imports: IndexMap<WorldKey, WorldItem>,
pub exports: IndexMap<WorldKey, WorldItem>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))]
pub package: Option<PackageId>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
pub docs: Docs,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Stability::is_unknown")
)]
pub stability: Stability,
#[cfg_attr(feature = "serde", serde(skip))]
pub includes: Vec<WorldInclude>,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
impl World {
pub(crate) fn adjust_spans(&mut self, offset: u32) {
self.span.adjust(offset);
for item in self.imports.values_mut().chain(self.exports.values_mut()) {
item.adjust_spans(offset);
}
for include in &mut self.includes {
include.span.adjust(offset);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IncludeName {
pub name: String,
pub as_: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorldInclude {
pub stability: Stability,
pub id: WorldId,
pub names: Vec<IncludeName>,
pub span: Span,
}
#[derive(Debug, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(into = "String"))]
pub enum WorldKey {
Name(String),
Interface(InterfaceId),
}
impl Hash for WorldKey {
fn hash<H: Hasher>(&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<WorldKey> for String {
fn from(key: WorldKey) -> String {
match key {
WorldKey::Name(name) => name,
WorldKey::Interface(id) => format!("interface-{}", id.index()),
}
}
}
impl WorldKey {
#[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, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum WorldItem {
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,
#[cfg_attr(feature = "serde", serde(skip))]
span: Span,
},
Function(Function),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_ignore_span"))]
Type { id: TypeId, span: Span },
}
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,
}
}
pub fn span(&self) -> Span {
match self {
WorldItem::Interface { span, .. } => *span,
WorldItem::Function(f) => f.span,
WorldItem::Type { span, .. } => *span,
}
}
pub(crate) fn adjust_spans(&mut self, offset: u32) {
match self {
WorldItem::Function(f) => f.adjust_spans(offset),
WorldItem::Interface { span, .. } => span.adjust(offset),
WorldItem::Type { span, .. } => span.adjust(offset),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Interface {
pub name: Option<String>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))]
pub types: IndexMap<String, TypeId>,
pub functions: IndexMap<String, Function>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
pub docs: Docs,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Stability::is_unknown")
)]
pub stability: Stability,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))]
pub package: Option<PackageId>,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
serialize_with = "serialize_optional_id",
)
)]
pub clone_of: Option<InterfaceId>,
}
impl Interface {
pub(crate) fn adjust_spans(&mut self, offset: u32) {
self.span.adjust(offset);
for func in self.functions.values_mut() {
func.adjust_spans(offset);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[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,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Stability::is_unknown")
)]
pub stability: Stability,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
impl TypeDef {
pub(crate) fn adjust_spans(&mut self, offset: u32) {
self.span.adjust(offset);
match &mut self.kind {
TypeDefKind::Record(r) => {
for field in &mut r.fields {
field.span.adjust(offset);
}
}
TypeDefKind::Variant(v) => {
for case in &mut v.cases {
case.span.adjust(offset);
}
}
TypeDefKind::Enum(e) => {
for case in &mut e.cases {
case.span.adjust(offset);
}
}
TypeDefKind::Flags(f) => {
for flag in &mut f.flags {
flag.span.adjust(offset);
}
}
_ => {}
}
}
}
#[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),
FixedLengthList(Type, u32),
Future(Option<Type>),
Stream(Option<Type>),
Type(Type),
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::FixedLengthList(..) => "fixed-length 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 {
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
World(WorldId),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
Interface(InterfaceId),
#[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, PartialOrd, Ord, 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<Field>,
}
#[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,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Flags {
pub flags: Vec<Flag>,
}
#[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,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
#[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<Type>,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Variant {
pub cases: Vec<Case>,
}
#[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<Type>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
pub docs: Docs,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
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<EnumCase>,
}
#[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,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
impl Enum {
pub fn tag(&self) -> Int {
discriminant_type(self.cases.len())
}
}
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<Type>,
pub err: Option<Type>,
}
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
#[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()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Param {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "String::is_empty"))]
pub name: String,
#[cfg_attr(feature = "serde", serde(rename = "type"))]
pub ty: Type,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Function {
pub name: String,
pub kind: FunctionKind,
pub params: Vec<Param>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub result: Option<Type>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))]
pub docs: Docs,
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "Stability::is_unknown")
)]
pub stability: Stability,
#[cfg_attr(feature = "serde", serde(skip))]
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum FunctionKind {
Freestanding,
AsyncFreestanding,
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
Method(TypeId),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
AsyncMethod(TypeId),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
Static(TypeId),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
AsyncStatic(TypeId),
#[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))]
Constructor(TypeId),
}
impl FunctionKind {
pub fn is_async(&self) -> bool {
match self {
FunctionKind::Freestanding
| FunctionKind::Method(_)
| FunctionKind::Static(_)
| FunctionKind::Constructor(_) => false,
FunctionKind::AsyncFreestanding
| FunctionKind::AsyncMethod(_)
| FunctionKind::AsyncStatic(_) => true,
}
}
pub fn resource(&self) -> Option<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),
}
}
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),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Mangling {
Standard32,
Legacy,
}
impl core::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`"
)
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum LiftLowerAbi {
Sync,
AsyncCallback,
AsyncStackful,
}
impl LiftLowerAbi {
fn import_prefix(self) -> &'static str {
match self {
Self::Sync => "",
Self::AsyncCallback | Self::AsyncStackful => "[async-lower]",
}
}
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]",
}
}
pub fn export_variant(self) -> AbiVariant {
match self {
Self::Sync => AbiVariant::GuestExport,
Self::AsyncCallback => AbiVariant::GuestExportAsync,
Self::AsyncStackful => AbiVariant::GuestExportAsyncStackful,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ManglingAndAbi {
Standard32,
Legacy(LiftLowerAbi),
}
impl ManglingAndAbi {
pub fn import_variant(self) -> AbiVariant {
match self {
Self::Standard32 => AbiVariant::GuestImport,
Self::Legacy(abi) => abi.import_variant(),
}
}
pub fn export_variant(self) -> AbiVariant {
match self {
Self::Standard32 => AbiVariant::GuestExport,
Self::Legacy(abi) => abi.export_variant(),
}
}
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),
}
}
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(crate) fn adjust_spans(&mut self, offset: u32) {
self.span.adjust(offset);
for param in &mut self.params {
param.span.adjust(offset);
}
}
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",
}
}
pub fn parameter_and_result_types(&self) -> impl Iterator<Item = Type> + '_ {
self.params.iter().map(|p| p.ty).chain(self.result)
}
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)
}
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),
},
}
}
pub fn find_futures_and_streams(&self, resolve: &Resolve) -> Vec<TypeId> {
let mut results = Vec::new();
for param in self.params.iter() {
find_futures_and_streams(resolve, param.ty, &mut results);
}
if let Some(ty) = self.result {
find_futures_and_streams(resolve, ty, &mut results);
}
results
}
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;
}
pub fn task_return_import(
&self,
resolve: &Resolve,
interface: Option<&WorldKey>,
mangling: Mangling,
) -> (String, String, abi::WasmSignature) {
match mangling {
Mangling::Standard32 => todo!(),
Mangling::Legacy => {}
}
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(Param {
name: "x".to_string(),
ty,
span: Default::default(),
});
}
let sig = resolve.wasm_signature(AbiVariant::GuestImport, &func_tmp);
(module, name, sig)
}
}
fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeId>) {
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::FixedLengthList(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!(),
}
}
#[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 {
Unknown,
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>,
},
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>,
},
}
impl Stability {
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::*;
use alloc::vec;
#[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,
span: Default::default(),
});
let t1 = resolve.types.alloc(TypeDef {
name: None,
kind: TypeDefKind::Future(Some(Type::Id(t0))),
owner: TypeOwner::None,
docs: Docs::default(),
stability: Stability::Unknown,
span: Default::default(),
});
let t2 = resolve.types.alloc(TypeDef {
name: None,
kind: TypeDefKind::Stream(Some(Type::U32)),
owner: TypeOwner::None,
docs: Docs::default(),
stability: Stability::Unknown,
span: Default::default(),
});
let found = Function {
name: "foo".into(),
kind: FunctionKind::Freestanding,
params: vec![
Param {
name: "p1".into(),
ty: Type::Id(t1),
span: Default::default(),
},
Param {
name: "p2".into(),
ty: Type::U32,
span: Default::default(),
},
],
result: Some(Type::Id(t2)),
docs: Docs::default(),
stability: Stability::Unknown,
span: Default::default(),
}
.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]);
}
}