feat: add a histrical wit-bindgen

This commit is contained in:
2023-01-01 00:25:48 +08:00
parent 01e8f5a959
commit aa50d63aec
419 changed files with 45283 additions and 1 deletions

View File

@@ -0,0 +1,40 @@
[package]
name = "wit-component"
version = "0.1.0"
authors = ["Peter Huene <peter@huene.dev>"]
edition = "2021"
[[bin]]
name = "wit-component"
path = "src/bin/wit-component.rs"
required-features = ["cli"]
[[bin]]
name = "wit2wasm"
path = "src/bin/wit2wasm.rs"
required-features = ["cli"]
[[bin]]
name = "wasm2wit"
path = "src/bin/wasm2wit.rs"
required-features = ["cli"]
[dependencies]
wasmparser = "0.86.0"
wasm-encoder = "0.13.0"
wat = "1.0.44"
wit-parser = { path = "../parser" }
anyhow = "1.0.55"
indexmap = "1.8.0"
clap = { version = "3.1.0", features = ["derive"], optional = true }
env_logger = { version = "0.9.0", optional = true }
log = { version = "0.4.14", optional = true }
[dev-dependencies]
wasmprinter = "0.2.36"
glob = "0.3.0"
pretty_assertions = "1.2.0"
[features]
default = ["cli"]
cli = ["clap", "env_logger", "log"]

View File

@@ -0,0 +1,29 @@
<div align="center">
<h1><code>wit-component</code></h1>
<p>
<strong>WebAssembly component tooling based on the component model proposal and <em>wit-bindgen</em>.</strong>
</p>
<strong>A <a href="https://bytecodealliance.org/">Bytecode Alliance</a> project</strong>
<p>
<a href="https://github.com/bytecodealliance/wit-bindgen/actions?query=workflow%3ACI"><img src="https://github.com/bytecodealliance/wit-bindgen/workflows/CI/badge.svg" alt="build status" /></a>
<img src="https://img.shields.io/badge/rustc-stable+-green.svg" alt="supported rustc stable" />
</p>
</div>
# `wit-component`
`wit-component` is a crate and a set of CLI tools for creating and interacting with WebAssembly components based on the [component model proposal](https://github.com/WebAssembly/component-model/).
## Tools
* `wit-component` - creates a WebAssembly component from a core WebAssembly module and a set of
`.wit` files representing the component's imported and exported interfaces.
* `wit2wasm` - encodes an interface definition (in `wit`) as an "interface-only" WebAssembly component.
A `.wasm` component file will be generated that stores a full description of the original interface.
* `wasm2wit` - decodes an "interface-only" WebAssembly component to an interface definition (in `wit`).
A `.wit` file will be generated that represents the interface described by the component.

View File

@@ -0,0 +1,13 @@
use clap::Parser;
use wit_component::cli::WasmToWitApp;
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
.format_target(false)
.init();
if let Err(e) = WasmToWitApp::parse().execute() {
log::error!("{:?}", e);
std::process::exit(1);
}
}

View File

@@ -0,0 +1,13 @@
use clap::Parser;
use wit_component::cli::WitComponentApp;
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
.format_target(false)
.init();
if let Err(e) = WitComponentApp::parse().execute() {
log::error!("{:?}", e);
std::process::exit(1);
}
}

View File

@@ -0,0 +1,13 @@
use clap::Parser;
use wit_component::cli::WitToWasmApp;
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
.format_target(false)
.init();
if let Err(e) = WitToWasmApp::parse().execute() {
log::error!("{:?}", e);
std::process::exit(1);
}
}

View File

@@ -0,0 +1,215 @@
//! The WebAssembly component tool command line interface.
#![deny(missing_docs)]
use crate::{
decode_interface_component, ComponentEncoder, InterfaceEncoder, InterfacePrinter,
StringEncoding,
};
use anyhow::{bail, Context, Result};
use clap::Parser;
use std::path::{Path, PathBuf};
use wit_parser::Interface;
fn parse_named_interface(s: &str) -> Result<Interface> {
let (name, path) = s
.split_once('=')
.ok_or_else(|| anyhow::anyhow!("expected a value with format `NAME=INTERFACE`"))?;
parse_interface(Some(name.to_string()), Path::new(path))
}
fn parse_unnamed_interface(s: &str) -> Result<Interface> {
parse_interface(None, Path::new(s))
}
fn parse_interface(name: Option<String>, path: &Path) -> Result<Interface> {
if !path.is_file() {
bail!("interface file `{}` does not exist", path.display(),);
}
let mut interface = Interface::parse_file(&path)
.with_context(|| format!("failed to parse interface file `{}`", path.display()))?;
interface.name = name.unwrap_or_else(|| "".to_string());
Ok(interface)
}
/// WebAssembly component encoder.
///
/// Encodes a WebAssembly component from a core WebAssembly module.
#[derive(Debug, Parser)]
#[clap(name = "component-encoder", version = env!("CARGO_PKG_VERSION"))]
pub struct WitComponentApp {
/// The path to an interface definition file the component imports.
#[clap(long = "import", value_name = "NAME=INTERFACE", parse(try_from_str = parse_named_interface))]
pub imports: Vec<Interface>,
/// The path to an interface definition file the component exports.
#[clap(long = "export", value_name = "NAME=INTERFACE", parse(try_from_str = parse_named_interface))]
pub exports: Vec<Interface>,
/// The path of the output WebAssembly component.
#[clap(long, short = 'o', value_name = "OUTPUT")]
pub output: Option<PathBuf>,
/// The default interface the component exports.
#[clap(long, short = 'i', value_name = "INTERFACE", parse(try_from_str = parse_unnamed_interface))]
pub interface: Option<Interface>,
/// Skip validation of the output component.
#[clap(long)]
pub skip_validation: bool,
/// The expected string encoding format for the component.
/// Supported values are: `utf8` (default), `utf16`, and `compact-utf16`.
#[clap(long, value_name = "ENCODING")]
pub encoding: Option<StringEncoding>,
/// Path to the WebAssembly module to encode.
#[clap(index = 1, value_name = "MODULE")]
pub module: PathBuf,
}
impl WitComponentApp {
/// Executes the application.
pub fn execute(self) -> Result<()> {
if !self.module.is_file() {
bail!(
"module `{}` does not exist as a file",
self.module.display()
);
}
let output = self.output.unwrap_or_else(|| {
let mut stem: PathBuf = self.module.file_stem().unwrap().into();
stem.set_extension("wasm");
stem
});
let module = wat::parse_file(&self.module)
.with_context(|| format!("failed to parse module `{}`", self.module.display()))?;
let mut encoder = ComponentEncoder::default()
.module(&module)
.imports(&self.imports)
.exports(&self.exports)
.validate(!self.skip_validation);
if let Some(interface) = &self.interface {
encoder = encoder.interface(interface);
}
if let Some(encoding) = &self.encoding {
encoder = encoder.encoding(*encoding);
}
let bytes = encoder.encode().with_context(|| {
format!(
"failed to encode a component from module `{}`",
self.module.display()
)
})?;
std::fs::write(&output, bytes)
.with_context(|| format!("failed to write output file `{}`", output.display()))?;
println!("encoded component `{}`", output.display());
Ok(())
}
}
/// WebAssembly interface encoder.
///
/// Encodes a WebAssembly interface as a WebAssembly component.
#[derive(Debug, Parser)]
#[clap(name = "wit2wasm", version = env!("CARGO_PKG_VERSION"))]
pub struct WitToWasmApp {
/// The path of the output WebAssembly component.
#[clap(long, short = 'o', value_name = "OUTPUT")]
pub output: Option<PathBuf>,
/// The path to the WebAssembly interface file to encode.
#[clap(index = 1, value_name = "INTERFACE")]
pub interface: PathBuf,
}
impl WitToWasmApp {
/// Executes the application.
pub fn execute(self) -> Result<()> {
let output = self.output.unwrap_or_else(|| {
let mut stem: PathBuf = self.interface.file_stem().unwrap().into();
stem.set_extension("wasm");
stem
});
let interface = parse_interface(None, &self.interface)?;
let encoder = InterfaceEncoder::new(&interface).validate(true);
let bytes = encoder.encode().with_context(|| {
format!(
"failed to encode a component from interface `{}`",
self.interface.display()
)
})?;
std::fs::write(&output, bytes)
.with_context(|| format!("failed to write output file `{}`", output.display()))?;
println!("encoded interface as component `{}`", output.display());
Ok(())
}
}
/// WebAssembly interface decoder.
///
/// Decodes a WebAssembly interface from a WebAssembly component.
#[derive(Debug, Parser)]
#[clap(name = "wit2wasm", version = env!("CARGO_PKG_VERSION"))]
pub struct WasmToWitApp {
/// The path of the output WebAssembly interface file.
#[clap(long, short = 'o', value_name = "OUTPUT")]
pub output: Option<PathBuf>,
/// The path to the WebAssembly component to decode.
#[clap(index = 1, value_name = "COMPONENT")]
pub component: PathBuf,
}
impl WasmToWitApp {
/// Executes the application.
pub fn execute(self) -> Result<()> {
let output = self.output.unwrap_or_else(|| {
let mut stem: PathBuf = self.component.file_stem().unwrap().into();
stem.set_extension("wit");
stem
});
if !self.component.is_file() {
bail!(
"component `{}` does not exist as a file",
self.component.display()
);
}
let bytes = wat::parse_file(&self.component)
.with_context(|| format!("failed to parse component `{}`", self.component.display()))?;
let interface = decode_interface_component(&bytes).with_context(|| {
format!("failed to decode component `{}`", self.component.display())
})?;
let mut printer = InterfacePrinter::default();
std::fs::write(&output, printer.print(&interface)?)
.with_context(|| format!("failed to write output file `{}`", output.display()))?;
println!("decoded interface to `{}`", output.display());
Ok(())
}
}

View File

@@ -0,0 +1,549 @@
use anyhow::{anyhow, bail, Context, Result};
use indexmap::IndexMap;
use wasmparser::{
types, Chunk, ComponentExternalKind, Encoding, Parser, Payload, PrimitiveValType, Validator,
WasmFeatures,
};
use wit_parser::*;
/// Represents information about a decoded WebAssembly component.
pub struct ComponentInfo<'a> {
/// The types defined in the component.
pub types: types::Types,
/// The exported types in the component.
pub exported_types: Vec<(&'a str, u32)>,
/// The exported functions in the component.
pub exported_functions: Vec<(&'a str, u32)>,
}
impl<'a> ComponentInfo<'a> {
/// Creates a new component info by parsing the given WebAssembly component bytes.
pub fn new(mut bytes: &'a [u8]) -> Result<Self> {
let mut parser = Parser::new(0);
let mut parsers = Vec::new();
let mut validator = Validator::new_with_features(WasmFeatures {
component_model: true,
..Default::default()
});
let mut exported_types = Vec::new();
let mut exported_functions = Vec::new();
loop {
match parser.parse(bytes, true)? {
Chunk::Parsed { payload, consumed } => {
bytes = &bytes[consumed..];
match payload {
Payload::Version {
num,
encoding,
range,
} => {
if parsers.is_empty() && encoding != Encoding::Component {
bail!("file is not a WebAssembly component");
}
validator.version(num, encoding, &range)?;
}
Payload::TypeSection(s) => {
validator.type_section(&s)?;
}
Payload::ImportSection(s) => {
validator.import_section(&s)?;
}
Payload::FunctionSection(s) => {
validator.function_section(&s)?;
}
Payload::TableSection(s) => {
validator.table_section(&s)?;
}
Payload::MemorySection(s) => {
validator.memory_section(&s)?;
}
Payload::TagSection(s) => {
validator.tag_section(&s)?;
}
Payload::GlobalSection(s) => {
validator.global_section(&s)?;
}
Payload::ExportSection(s) => {
validator.export_section(&s)?;
}
Payload::StartSection { func, range } => {
validator.start_section(func, &range)?;
}
Payload::ElementSection(s) => {
validator.element_section(&s)?;
}
Payload::DataCountSection { count, range } => {
validator.data_count_section(count, &range)?;
}
Payload::DataSection(s) => {
validator.data_section(&s)?;
}
Payload::CodeSectionStart { count, range, .. } => {
validator.code_section_start(count, &range)?;
}
Payload::CodeSectionEntry(f) => {
validator.code_section_entry(&f)?;
}
Payload::ModuleSection {
parser: inner,
range,
} => {
validator.module_section(&range)?;
parsers.push(parser);
parser = inner;
}
Payload::InstanceSection(s) => {
validator.instance_section(&s)?;
}
Payload::AliasSection(s) => {
validator.alias_section(&s)?;
}
Payload::CoreTypeSection(s) => {
validator.core_type_section(&s)?;
}
Payload::ComponentSection {
parser: inner,
range,
} => {
validator.component_section(&range)?;
parsers.push(parser);
parser = inner;
}
Payload::ComponentInstanceSection(s) => {
validator.component_instance_section(&s)?;
}
Payload::ComponentAliasSection(s) => {
validator.component_alias_section(&s)?;
}
Payload::ComponentTypeSection(s) => {
validator.component_type_section(&s)?;
}
Payload::ComponentCanonicalSection(s) => {
validator.component_canonical_section(&s)?;
}
Payload::ComponentStartSection(s) => {
validator.component_start_section(&s)?;
}
Payload::ComponentImportSection(s) => {
validator.component_import_section(&s)?;
}
Payload::ComponentExportSection(s) => {
validator.component_export_section(&s)?;
if parsers.is_empty() {
for export in s {
let export = export?;
match export.kind {
ComponentExternalKind::Func => {
exported_functions.push((export.name, export.index));
}
ComponentExternalKind::Type => {
exported_types.push((export.name, export.index));
}
_ => {}
}
}
}
}
Payload::CustomSection { .. } => {
// Skip custom sections
}
Payload::UnknownSection { id, range, .. } => {
validator.unknown_section(id, &range)?;
}
Payload::End(offset) => {
let types = validator.end(offset)?;
match parsers.pop() {
Some(parent) => parser = parent,
None => {
return Ok(Self {
types,
exported_types,
exported_functions,
});
}
}
}
}
}
Chunk::NeedMoreData(_) => unreachable!(),
}
}
}
}
/// Represents an interface decoder for WebAssembly components.
pub struct InterfaceDecoder<'a> {
info: &'a ComponentInfo<'a>,
interface: Interface,
type_map: IndexMap<types::TypeId, Type>,
name_map: IndexMap<types::TypeId, &'a str>,
}
impl<'a> InterfaceDecoder<'a> {
/// Creates a new interface decoder for the given component information.
pub fn new(info: &'a ComponentInfo<'a>) -> Self {
Self {
info,
interface: Interface::default(),
name_map: IndexMap::new(),
type_map: IndexMap::new(),
}
}
/// Consumes the decoder and returns the interface representation.
pub fn decode(mut self) -> Result<Interface> {
// Populate names in the name map first
for (name, index) in &self.info.exported_types {
if let types::Type::Defined(_) = self.info.types.type_at(*index, false).unwrap() {
self.name_map.insert(
self.info.types.id_from_type_index(*index, false).unwrap(),
name,
);
}
}
for (name, index) in &self.info.exported_types {
let ty = match self.info.types.type_at(*index, false).unwrap() {
types::Type::ComponentFunc(ty) => ty,
_ => continue,
};
self.add_function(name, ty)?;
}
for (name, index) in &self.info.exported_functions {
let ty = self.info.types.component_function_at(*index).unwrap();
self.add_function(name, ty)?;
}
Ok(self.interface)
}
fn add_function(&mut self, func_name: &str, ty: &types::ComponentFuncType) -> Result<()> {
validate_id(func_name)
.with_context(|| format!("function name `{}` is not a valid identifier", func_name))?;
let mut params = Vec::new();
for (name, ty) in ty.params.iter() {
let name = name
.as_ref()
.ok_or_else(|| anyhow!("function `{}` has a parameter without a name", func_name))?
.clone();
validate_id(&name).with_context(|| {
format!(
"function `{}` has a parameter `{}` that is not a valid identifier",
func_name, name
)
})?;
params.push((name, self.decode_type(ty)?));
}
let result = self.decode_type(&ty.result)?;
self.interface.functions.push(Function {
is_async: false,
docs: Docs::default(),
name: func_name.to_string(),
kind: FunctionKind::Freestanding,
params,
result,
});
Ok(())
}
fn decode_type(&mut self, ty: &types::ComponentValType) -> Result<Type> {
Ok(match ty {
types::ComponentValType::Primitive(ty) => self.decode_primitive(*ty)?,
types::ComponentValType::Type(id) => {
if let Some(ty) = self.type_map.get(id) {
return Ok(*ty);
}
let name = self.name_map.get(id).map(ToString::to_string);
if let Some(name) = name.as_deref() {
validate_id(name).with_context(|| {
format!("type name `{}` is not a valid identifier", name)
})?;
}
let ty = match &self.info.types.type_from_id(*id).unwrap() {
types::Type::Defined(ty) => match ty {
types::ComponentDefinedType::Primitive(ty) => {
self.decode_named_primitive(name, ty)?
}
types::ComponentDefinedType::Record(r) => {
self.decode_record(name, r.fields.iter())?
}
types::ComponentDefinedType::Variant(v) => {
self.decode_variant(name, v.cases.iter())?
}
types::ComponentDefinedType::List(ty) => {
let inner = self.decode_type(ty)?;
Type::Id(self.alloc_type(name, TypeDefKind::List(inner)))
}
types::ComponentDefinedType::Tuple(t) => {
self.decode_tuple(name, &t.types)?
}
types::ComponentDefinedType::Flags(names) => {
self.decode_flags(name, names.iter())?
}
types::ComponentDefinedType::Enum(names) => {
self.decode_enum(name, names.iter())?
}
types::ComponentDefinedType::Union(u) => {
self.decode_union(name, &u.types)?
}
types::ComponentDefinedType::Option(ty) => self.decode_option(name, ty)?,
types::ComponentDefinedType::Expected(ok, err) => {
self.decode_expected(name, ok, err)?
}
},
_ => unreachable!(),
};
self.type_map.insert(*id, ty);
ty
}
})
}
fn decode_named_primitive(
&mut self,
name: Option<String>,
ty: &PrimitiveValType,
) -> Result<Type> {
let mut ty = self.decode_primitive(*ty)?;
if let Some(name) = name {
validate_id(&name)
.with_context(|| format!("type name `{}` is not a valid identifier", name))?;
ty = Type::Id(self.alloc_type(Some(name), TypeDefKind::Type(ty)));
}
Ok(ty)
}
fn decode_primitive(&mut self, ty: PrimitiveValType) -> Result<Type> {
Ok(match ty {
PrimitiveValType::Unit => Type::Unit,
PrimitiveValType::Bool => Type::Bool,
PrimitiveValType::S8 => Type::S8,
PrimitiveValType::U8 => Type::U8,
PrimitiveValType::S16 => Type::S16,
PrimitiveValType::U16 => Type::U16,
PrimitiveValType::S32 => Type::S32,
PrimitiveValType::U32 => Type::U32,
PrimitiveValType::S64 => Type::S64,
PrimitiveValType::U64 => Type::U64,
PrimitiveValType::Float32 => Type::Float32,
PrimitiveValType::Float64 => Type::Float64,
PrimitiveValType::Char => Type::Char,
PrimitiveValType::String => Type::String,
})
}
fn decode_record(
&mut self,
record_name: Option<String>,
fields: impl ExactSizeIterator<Item = (&'a String, &'a types::ComponentValType)>,
) -> Result<Type> {
let record_name =
record_name.ok_or_else(|| anyhow!("interface has an unnamed record type"))?;
let record = Record {
fields: fields
.map(|(name, ty)| {
validate_id(name).with_context(|| {
format!(
"record `{}` has a field `{}` that is not a valid identifier",
record_name, name
)
})?;
Ok(Field {
docs: Docs::default(),
name: name.to_string(),
ty: self.decode_type(ty)?,
})
})
.collect::<Result<_>>()?,
};
Ok(Type::Id(self.alloc_type(
Some(record_name),
TypeDefKind::Record(record),
)))
}
fn decode_variant(
&mut self,
variant_name: Option<String>,
cases: impl ExactSizeIterator<Item = (&'a String, &'a types::VariantCase)>,
) -> Result<Type> {
let variant_name =
variant_name.ok_or_else(|| anyhow!("interface has an unnamed variant type"))?;
let variant = Variant {
cases: cases
.map(|(name, case)| {
validate_id(name).with_context(|| {
format!(
"variant `{}` has a case `{}` that is not a valid identifier",
variant_name, name
)
})?;
Ok(Case {
docs: Docs::default(),
name: name.to_string(),
ty: self.decode_type(&case.ty)?,
})
})
.collect::<Result<_>>()?,
};
Ok(Type::Id(self.alloc_type(
Some(variant_name),
TypeDefKind::Variant(variant),
)))
}
fn decode_tuple(
&mut self,
name: Option<String>,
tys: &[types::ComponentValType],
) -> Result<Type> {
let tuple = Tuple {
types: tys
.iter()
.map(|ty| self.decode_type(ty))
.collect::<Result<_>>()?,
};
Ok(Type::Id(self.alloc_type(name, TypeDefKind::Tuple(tuple))))
}
fn decode_flags(
&mut self,
flags_name: Option<String>,
names: impl ExactSizeIterator<Item = &'a String>,
) -> Result<Type> {
let flags_name =
flags_name.ok_or_else(|| anyhow!("interface has an unnamed flags type"))?;
let flags = Flags {
flags: names
.map(|name| {
validate_id(name).with_context(|| {
format!(
"flags `{}` has a flag named `{}` that is not a valid identifier",
flags_name, name
)
})?;
Ok(Flag {
docs: Docs::default(),
name: name.clone(),
})
})
.collect::<Result<_>>()?,
};
Ok(Type::Id(
self.alloc_type(Some(flags_name), TypeDefKind::Flags(flags)),
))
}
fn decode_enum(
&mut self,
enum_name: Option<String>,
names: impl ExactSizeIterator<Item = &'a String>,
) -> Result<Type> {
let enum_name = enum_name.ok_or_else(|| anyhow!("interface has an unnamed enum type"))?;
let enum_ = Enum {
cases: names
.map(|name| {
validate_id(name).with_context(|| {
format!(
"enum `{}` has a value `{}` that is not a valid identifier",
enum_name, name
)
})?;
Ok(EnumCase {
docs: Docs::default(),
name: name.to_string(),
})
})
.collect::<Result<_>>()?,
};
Ok(Type::Id(
self.alloc_type(Some(enum_name), TypeDefKind::Enum(enum_)),
))
}
fn decode_union(
&mut self,
name: Option<String>,
tys: &[types::ComponentValType],
) -> Result<Type> {
let union = Union {
cases: tys
.iter()
.map(|ty| {
Ok(UnionCase {
docs: Docs::default(),
ty: self.decode_type(ty)?,
})
})
.collect::<Result<_>>()?,
};
Ok(Type::Id(self.alloc_type(name, TypeDefKind::Union(union))))
}
fn decode_option(
&mut self,
name: Option<String>,
payload: &types::ComponentValType,
) -> Result<Type> {
let payload = self.decode_type(payload)?;
Ok(Type::Id(
self.alloc_type(name, TypeDefKind::Option(payload)),
))
}
fn decode_expected(
&mut self,
name: Option<String>,
ok: &types::ComponentValType,
err: &types::ComponentValType,
) -> Result<Type> {
let ok = self.decode_type(ok)?;
let err = self.decode_type(err)?;
Ok(Type::Id(self.alloc_type(
name,
TypeDefKind::Expected(Expected { ok, err }),
)))
}
fn alloc_type(&mut self, name: Option<String>, kind: TypeDefKind) -> TypeId {
self.interface.types.alloc(TypeDef {
docs: Docs::default(),
kind,
name,
foreign_module: None,
})
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
//! The WebAssembly component tooling.
#![deny(missing_docs)]
use anyhow::{bail, Result};
use std::str::FromStr;
use wasm_encoder::CanonicalOption;
use wit_parser::Interface;
#[cfg(feature = "cli")]
pub mod cli;
mod decoding;
mod encoding;
mod printing;
mod validation;
pub use encoding::*;
pub use printing::*;
/// Supported string encoding formats.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum StringEncoding {
/// Strings are encoded with UTF-8.
UTF8,
/// Strings are encoded with UTF-16.
UTF16,
/// Strings are encoded with compact UTF-16 (i.e. Latin1+UTF-16).
CompactUTF16,
}
impl Default for StringEncoding {
fn default() -> Self {
StringEncoding::UTF8
}
}
impl FromStr for StringEncoding {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"utf8" => Ok(StringEncoding::UTF8),
"utf16" => Ok(StringEncoding::UTF16),
"compact-utf16" => Ok(StringEncoding::CompactUTF16),
_ => bail!("unknown string encoding `{}`", s),
}
}
}
impl From<StringEncoding> for wasm_encoder::CanonicalOption {
fn from(e: StringEncoding) -> wasm_encoder::CanonicalOption {
match e {
StringEncoding::UTF8 => CanonicalOption::UTF8,
StringEncoding::UTF16 => CanonicalOption::UTF16,
StringEncoding::CompactUTF16 => CanonicalOption::CompactUTF16,
}
}
}
/// Decode an "interface-only" component to a wit `Interface`.
pub fn decode_interface_component(bytes: &[u8]) -> Result<Interface> {
decoding::InterfaceDecoder::new(&decoding::ComponentInfo::new(bytes)?).decode()
}

View File

@@ -0,0 +1,381 @@
use anyhow::{bail, Result};
use indexmap::IndexSet;
use std::fmt::Write;
use wit_parser::{
Enum, Expected, Flags, Interface, Record, Tuple, Type, TypeDefKind, TypeId, Union, Variant,
};
/// A utility for printing WebAssembly interface definitions to a string.
#[derive(Default)]
pub struct InterfacePrinter {
output: String,
declared: IndexSet<TypeId>,
}
impl InterfacePrinter {
/// Print the given WebAssembly interface to a string.
pub fn print(&mut self, interface: &Interface) -> Result<String> {
for func in &interface.functions {
for ty in func.params.iter().map(|p| &p.1).chain([&func.result]) {
self.declare_type(interface, ty)?;
}
}
for func in &interface.functions {
write!(&mut self.output, "{}: func(", func.name)?;
for (i, (name, ty)) in func.params.iter().enumerate() {
if i > 0 {
self.output.push_str(", ");
}
write!(&mut self.output, "{}: ", name)?;
self.print_type_name(interface, ty)?;
}
self.output.push(')');
match &func.result {
Type::Unit => {}
other => {
self.output.push_str(" -> ");
self.print_type_name(interface, other)?;
}
}
self.output.push_str("\n\n");
}
self.declared.clear();
Ok(std::mem::take(&mut self.output))
}
fn print_type_name(&mut self, interface: &Interface, ty: &Type) -> Result<()> {
match ty {
Type::Unit => self.output.push_str("unit"),
Type::Bool => self.output.push_str("bool"),
Type::U8 => self.output.push_str("u8"),
Type::U16 => self.output.push_str("u16"),
Type::U32 => self.output.push_str("u32"),
Type::U64 => self.output.push_str("u64"),
Type::S8 => self.output.push_str("s8"),
Type::S16 => self.output.push_str("s16"),
Type::S32 => self.output.push_str("s32"),
Type::S64 => self.output.push_str("s64"),
Type::Float32 => self.output.push_str("float32"),
Type::Float64 => self.output.push_str("float64"),
Type::Char => self.output.push_str("char"),
Type::String => self.output.push_str("string"),
Type::Id(id) => {
let ty = &interface.types[*id];
if let Some(name) = &ty.name {
self.output.push_str(name);
return Ok(());
}
match &ty.kind {
TypeDefKind::Tuple(t) => {
self.print_tuple_type(interface, t)?;
}
TypeDefKind::Option(t) => {
self.print_option_type(interface, t)?;
}
TypeDefKind::Expected(t) => {
self.print_expected_type(interface, t)?;
}
TypeDefKind::Record(_) => {
bail!("interface has an unnamed record type");
}
TypeDefKind::Flags(_) => {
bail!("interface has unnamed flags type")
}
TypeDefKind::Enum(_) => {
bail!("interface has unnamed enum type")
}
TypeDefKind::Variant(_) => {
bail!("interface has unnamed variant type")
}
TypeDefKind::Union(_) => {
bail!("interface has unnamed union type")
}
TypeDefKind::List(ty) => {
self.output.push_str("list<");
self.print_type_name(interface, ty)?;
self.output.push('>');
}
TypeDefKind::Type(ty) => self.print_type_name(interface, ty)?,
TypeDefKind::Future(_) => {
todo!("interface has an unnamed future type")
}
TypeDefKind::Stream(_) => {
todo!("interface has an unnamed stream type")
}
}
}
Type::Handle(_) => bail!("interface has unsupported type"),
}
Ok(())
}
fn print_tuple_type(&mut self, interface: &Interface, tuple: &Tuple) -> Result<()> {
self.output.push_str("tuple<");
for (i, ty) in tuple.types.iter().enumerate() {
if i > 0 {
self.output.push_str(", ");
}
self.print_type_name(interface, ty)?;
}
self.output.push('>');
Ok(())
}
fn print_option_type(&mut self, interface: &Interface, payload: &Type) -> Result<()> {
self.output.push_str("option<");
self.print_type_name(interface, payload)?;
self.output.push('>');
Ok(())
}
fn print_expected_type(&mut self, interface: &Interface, expected: &Expected) -> Result<()> {
self.output.push_str("expected<");
self.print_type_name(interface, &expected.ok)?;
self.output.push_str(", ");
self.print_type_name(interface, &expected.err)?;
self.output.push('>');
Ok(())
}
fn declare_type(&mut self, interface: &Interface, ty: &Type) -> Result<()> {
match ty {
Type::Unit
| Type::Bool
| Type::U8
| Type::U16
| Type::U32
| Type::U64
| Type::S8
| Type::S16
| Type::S32
| Type::S64
| Type::Float32
| Type::Float64
| Type::Char
| Type::String => return Ok(()),
Type::Id(id) => {
if !self.declared.insert(*id) {
return Ok(());
}
let ty = &interface.types[*id];
match &ty.kind {
TypeDefKind::Record(r) => {
self.declare_record(interface, ty.name.as_deref(), r)?
}
TypeDefKind::Tuple(t) => {
self.declare_tuple(interface, ty.name.as_deref(), t)?
}
TypeDefKind::Flags(f) => self.declare_flags(ty.name.as_deref(), f)?,
TypeDefKind::Variant(v) => {
self.declare_variant(interface, ty.name.as_deref(), v)?
}
TypeDefKind::Union(u) => {
self.declare_union(interface, ty.name.as_deref(), u)?
}
TypeDefKind::Option(t) => {
self.declare_option(interface, ty.name.as_deref(), t)?
}
TypeDefKind::Expected(e) => {
self.declare_expected(interface, ty.name.as_deref(), e)?
}
TypeDefKind::Enum(e) => self.declare_enum(ty.name.as_deref(), e)?,
TypeDefKind::List(inner) => {
self.declare_list(interface, ty.name.as_deref(), inner)?
}
TypeDefKind::Type(inner) => match ty.name.as_deref() {
Some(name) => {
write!(&mut self.output, "type {} = ", name)?;
self.print_type_name(interface, inner)?;
self.output.push_str("\n\n");
}
None => bail!("unnamed type in interface"),
},
TypeDefKind::Future(_) => todo!("declare future"),
TypeDefKind::Stream(_) => todo!("declare stream"),
}
}
Type::Handle(_) => bail!("interface has unsupported type"),
}
Ok(())
}
fn declare_record(
&mut self,
interface: &Interface,
name: Option<&str>,
record: &Record,
) -> Result<()> {
for field in record.fields.iter() {
self.declare_type(interface, &field.ty)?;
}
match name {
Some(name) => {
writeln!(&mut self.output, "record {} {{", name)?;
for field in &record.fields {
write!(&mut self.output, " {}: ", field.name)?;
self.declare_type(interface, &field.ty)?;
self.print_type_name(interface, &field.ty)?;
self.output.push_str(",\n");
}
self.output.push_str("}\n\n");
Ok(())
}
None => bail!("interface has unnamed record type"),
}
}
fn declare_tuple(
&mut self,
interface: &Interface,
name: Option<&str>,
tuple: &Tuple,
) -> Result<()> {
for ty in tuple.types.iter() {
self.declare_type(interface, ty)?;
}
if let Some(name) = name {
write!(&mut self.output, "type {} = ", name)?;
self.print_tuple_type(interface, tuple)?;
self.output.push_str("\n\n");
}
Ok(())
}
fn declare_flags(&mut self, name: Option<&str>, flags: &Flags) -> Result<()> {
match name {
Some(name) => {
writeln!(&mut self.output, "flags {} {{", name)?;
for flag in &flags.flags {
writeln!(&mut self.output, " {},", flag.name)?;
}
self.output.push_str("}\n\n");
}
None => bail!("interface has unnamed flags type"),
}
Ok(())
}
fn declare_variant(
&mut self,
interface: &Interface,
name: Option<&str>,
variant: &Variant,
) -> Result<()> {
for case in variant.cases.iter() {
self.declare_type(interface, &case.ty)?;
}
let name = match name {
Some(name) => name,
None => bail!("interface has unnamed union type"),
};
writeln!(&mut self.output, "variant {} {{", name)?;
for case in &variant.cases {
write!(&mut self.output, " {}", case.name)?;
if case.ty != Type::Unit {
self.output.push('(');
self.print_type_name(interface, &case.ty)?;
self.output.push(')');
}
self.output.push_str(",\n");
}
self.output.push_str("}\n\n");
Ok(())
}
fn declare_union(
&mut self,
interface: &Interface,
name: Option<&str>,
union: &Union,
) -> Result<()> {
for case in union.cases.iter() {
self.declare_type(interface, &case.ty)?;
}
let name = match name {
Some(name) => name,
None => bail!("interface has unnamed union type"),
};
writeln!(&mut self.output, "union {} {{", name)?;
for case in &union.cases {
self.output.push_str(" ");
self.print_type_name(interface, &case.ty)?;
self.output.push_str(",\n");
}
self.output.push_str("}\n\n");
Ok(())
}
fn declare_option(
&mut self,
interface: &Interface,
name: Option<&str>,
payload: &Type,
) -> Result<()> {
self.declare_type(interface, payload)?;
if let Some(name) = name {
write!(&mut self.output, "type {} = ", name)?;
self.print_option_type(interface, payload)?;
self.output.push_str("\n\n");
}
Ok(())
}
fn declare_expected(
&mut self,
interface: &Interface,
name: Option<&str>,
expected: &Expected,
) -> Result<()> {
self.declare_type(interface, &expected.ok)?;
self.declare_type(interface, &expected.err)?;
if let Some(name) = name {
write!(&mut self.output, "type {} = ", name)?;
self.print_expected_type(interface, expected)?;
self.output.push_str("\n\n");
}
Ok(())
}
fn declare_enum(&mut self, name: Option<&str>, enum_: &Enum) -> Result<()> {
let name = match name {
Some(name) => name,
None => bail!("interface has unnamed enum type"),
};
writeln!(&mut self.output, "enum {} {{", name)?;
for case in &enum_.cases {
writeln!(&mut self.output, " {},", case.name)?;
}
self.output.push_str("}\n\n");
Ok(())
}
fn declare_list(&mut self, interface: &Interface, name: Option<&str>, ty: &Type) -> Result<()> {
self.declare_type(interface, ty)?;
if let Some(name) = name {
write!(&mut self.output, "type {} = list<", name)?;
self.print_type_name(interface, ty)?;
self.output.push_str(">\n\n");
return Ok(());
}
Ok(())
}
}

View File

@@ -0,0 +1,254 @@
use anyhow::{anyhow, bail, Result};
use indexmap::{map::Entry, IndexMap, IndexSet};
use std::borrow::Cow;
use wasmparser::{
types::Types, Encoding, ExternalKind, FuncType, Parser, Payload, TypeRef, ValType,
ValidPayload, Validator,
};
use wit_parser::{
abi::{AbiVariant, WasmSignature, WasmType},
Interface,
};
fn is_wasi(name: &str) -> bool {
name == "wasi_unstable" || name == "wasi_snapshot_preview1"
}
fn is_canonical_function(name: &str) -> bool {
name.starts_with("canonical_abi_")
}
pub fn expected_export_name<'a>(interface: Option<&str>, func: &'a str) -> Cow<'a, str> {
// TODO: wit-bindgen currently doesn't mangle its export names, so this
// only works with the default (i.e. `None`) interface.
match interface {
Some(interface) => format!("{}#{}", interface, func).into(),
None => func.into(),
}
}
fn wasm_sig_to_func_type(signature: WasmSignature) -> FuncType {
fn from_wasm_type(ty: &WasmType) -> ValType {
match ty {
WasmType::I32 => ValType::I32,
WasmType::I64 => ValType::I64,
WasmType::F32 => ValType::F32,
WasmType::F64 => ValType::F64,
}
}
FuncType {
params: signature
.params
.iter()
.map(from_wasm_type)
.collect::<Vec<_>>()
.into_boxed_slice(),
returns: signature
.results
.iter()
.map(from_wasm_type)
.collect::<Vec<_>>()
.into_boxed_slice(),
}
}
/// This function validates the following:
/// * The bytes represent a core WebAssembly module.
/// * The module's imports are all satisfied by the given import interfaces.
/// * The given default and exported interfaces are satisfied by the module's exports.
///
/// Returns a tuple of the set of imported interfaces required by the module, whether
/// the module exports a memory, and whether the module exports a realloc function.
pub fn validate_module<'a>(
bytes: &'a [u8],
interface: &Option<&Interface>,
imports: &[Interface],
exports: &[Interface],
) -> Result<(IndexSet<&'a str>, bool, bool)> {
let imports: IndexMap<&str, &Interface> =
imports.iter().map(|i| (i.name.as_str(), i)).collect();
let exports: IndexMap<&str, &Interface> =
exports.iter().map(|i| (i.name.as_str(), i)).collect();
let mut validator = Validator::new();
let mut types = None;
let mut import_funcs = IndexMap::new();
let mut export_funcs = IndexMap::new();
let mut has_memory = false;
let mut has_realloc = false;
for payload in Parser::new(0).parse_all(bytes) {
let payload = payload?;
if let ValidPayload::End(tys) = validator.payload(&payload)? {
types = Some(tys);
break;
}
match payload {
Payload::Version { encoding, .. } if encoding != Encoding::Module => {
bail!("data is not a WebAssembly module");
}
Payload::ImportSection(s) => {
for import in s {
let import = import?;
if is_wasi(import.module) {
continue;
}
match import.ty {
TypeRef::Func(ty) => {
let map = match import_funcs.entry(import.module) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => e.insert(IndexMap::new()),
};
assert!(map.insert(import.name, ty).is_none());
}
_ => bail!("module is only allowed to import functions"),
}
}
}
Payload::ExportSection(s) => {
for export in s {
let export = export?;
match export.kind {
ExternalKind::Func => {
if is_canonical_function(export.name) {
if export.name == "canonical_abi_realloc" {
// TODO: validate that the canonical_abi_realloc function is [i32, i32, i32, i32] -> [i32]
has_realloc = true;
}
continue;
}
assert!(export_funcs.insert(export.name, export.index).is_none())
}
ExternalKind::Memory => {
if export.name == "memory" {
has_memory = true;
}
}
_ => continue,
}
}
}
_ => continue,
}
}
let types = types.unwrap();
for (name, funcs) in &import_funcs {
if name.is_empty() {
bail!("module imports from an empty module name");
}
match imports.get(name) {
Some(interface) => {
validate_imported_interface(interface, name, funcs, &types)?;
}
None => bail!("module requires an import interface named `{}`", name),
}
}
if let Some(interface) = interface {
validate_exported_interface(interface, None, &export_funcs, &types)?;
}
for (name, interface) in exports {
if name.is_empty() {
bail!("cannot export an interface with an empty name");
}
validate_exported_interface(interface, Some(name), &export_funcs, &types)?;
}
Ok((
import_funcs.keys().cloned().collect(),
has_memory,
has_realloc,
))
}
fn validate_imported_interface(
interface: &Interface,
name: &str,
imports: &IndexMap<&str, u32>,
types: &Types,
) -> Result<()> {
for (func_name, ty) in imports {
let f = interface
.functions
.iter()
.find(|f| f.name == *func_name)
.ok_or_else(|| {
anyhow!(
"import interface `{}` is missing function `{}` that is required by the module",
name,
func_name,
)
})?;
let expected = wasm_sig_to_func_type(interface.wasm_signature(AbiVariant::GuestImport, f));
let ty = types.func_type_at(*ty).unwrap();
if ty != &expected {
bail!(
"type mismatch for function `{}` on imported interface `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`",
func_name,
name,
expected.params,
expected.returns,
ty.params,
ty.returns
);
}
}
Ok(())
}
fn validate_exported_interface(
interface: &Interface,
name: Option<&str>,
exports: &IndexMap<&str, u32>,
types: &Types,
) -> Result<()> {
for f in &interface.functions {
let expected_export = expected_export_name(name, &f.name);
match exports.get(expected_export.as_ref()) {
Some(func_index) => {
let expected_ty =
wasm_sig_to_func_type(interface.wasm_signature(AbiVariant::GuestExport, f));
let ty = types.function_at(*func_index).unwrap();
if ty != &expected_ty {
match name {
Some(name) => bail!(
"type mismatch for function `{}` from exported interface `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`",
f.name,
name,
expected_ty.params,
expected_ty.returns,
ty.params,
ty.returns
),
None => bail!(
"type mismatch for default interface function `{}`: expected `{:?} -> {:?}` but found `{:?} -> {:?}`",
f.name,
expected_ty.params,
expected_ty.returns,
ty.params,
ty.returns
)
}
}
}
None => bail!(
"module does not export required function `{}`",
expected_export
),
}
}
Ok(())
}

View File

@@ -0,0 +1,123 @@
use anyhow::{bail, Context, Result};
use pretty_assertions::assert_eq;
use std::{fs, path::Path};
use wit_component::ComponentEncoder;
use wit_parser::Interface;
fn read_interface(path: &Path) -> Result<Interface> {
wit_parser::Interface::parse_file(&path)
.with_context(|| format!("failed to parse interface file `{}`", path.display()))
}
fn read_interfaces(dir: &Path, pattern: &str) -> Result<Vec<Interface>> {
glob::glob(dir.join(pattern).to_str().unwrap())?
.map(|p| {
let p = p?;
let mut i = read_interface(&p)?;
i.name = p
.file_stem()
.unwrap()
.to_str()
.unwrap()
.trim_start_matches("import-")
.trim_start_matches("export-")
.to_string();
Ok(i)
})
.collect::<Result<_>>()
}
/// Tests the encoding of components.
///
/// This test looks in the `components/` directory for test cases.
///
/// The expected input files for a test case are:
///
/// * [required] `module.wat` - contains the core module definition to be encoded
/// as a component.
/// * [optional] `default.wit` - represents the component's default interface.
/// * [optional] `export-<name>.wit` - represents an interface exported by the component.
/// * [optional] `import-<name>.wit` - represents an interface imported by the component.
///
/// And the output files are one of the following:
///
/// * `component.wat` - the expected encoded component in text format if the encoding
/// is expected to succeed.
/// * `error.txt` - the expected error message if the encoding is expected to fail.
///
/// The test encodes a component based on the input files. If the encoding succeeds,
/// it expects the output to match `component.wat`. If the encoding fails, it expects
/// the output to match `error.txt`.
///
/// Run the test with the environment variable `BLESS` set to update
/// either `component.wat` or `error.txt` depending on the outcome of the encoding.
#[test]
fn component_encoding() -> Result<()> {
for entry in fs::read_dir("tests/components")? {
let path = entry?.path();
if !path.is_dir() {
continue;
}
let test_case = path.file_stem().unwrap().to_str().unwrap();
let module_path = path.join("module.wat");
let interface_path = path.join("default.wit");
let component_path = path.join("component.wat");
let error_path = path.join("error.txt");
let module = wat::parse_file(&module_path)
.with_context(|| format!("expected file `{}`", module_path.display()))?;
let interface = interface_path
.is_file()
.then(|| read_interface(&interface_path))
.transpose()?;
let imports = read_interfaces(&path, "import-*.wit")?;
let exports = read_interfaces(&path, "export-*.wit")?;
let mut encoder = ComponentEncoder::default()
.module(&module)
.imports(&imports)
.exports(&exports)
.validate(true);
if let Some(interface) = &interface {
encoder = encoder.interface(interface);
}
let r = encoder.encode();
let (output, baseline_path) = if error_path.is_file() {
match r {
Ok(_) => bail!("encoding should fail for test case `{}`", test_case),
Err(e) => (e.to_string(), &error_path),
}
} else {
(
wasmprinter::print_bytes(
&r.with_context(|| format!("failed to encode for test case `{}`", test_case))?,
)
.with_context(|| {
format!(
"failed to print component bytes for test case `{}`",
test_case
)
})?,
&component_path,
)
};
if std::env::var_os("BLESS").is_some() {
fs::write(&baseline_path, output)?;
} else {
assert_eq!(
fs::read_to_string(&baseline_path)?.replace("\r\n", "\n"),
output,
"failed baseline comparison for test case `{}` ({})",
test_case,
baseline_path.display(),
);
}
}
Ok(())
}

View File

@@ -0,0 +1 @@
type mismatch for default interface function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`

View File

@@ -0,0 +1,3 @@
(module
(func (export "a") unreachable)
)

View File

@@ -0,0 +1 @@
module imports from an empty module name

View File

@@ -0,0 +1,3 @@
(module
(import "" "foo" (func))
)

View File

@@ -0,0 +1,4 @@
(component
(core module (;0;))
(core instance (;0;) (instantiate 0))
)

View File

@@ -0,0 +1 @@
type mismatch for function `a` from exported interface `foo`: expected `[I32, I32] -> [I32]` but found `[] -> []`

View File

@@ -0,0 +1,3 @@
(module
(func (export "foo#a") unreachable)
)

View File

@@ -0,0 +1,84 @@
(component
(type (;0;) (func))
(type (;1;) (func (param "a" s8) (param "b" s16) (param "c" s32) (param "d" s64) (result string)))
(type (;2;) (tuple s8 s16 s32 s64))
(type (;3;) (func (result 2)))
(type (;4;) (flags "a" "b" "c"))
(type (;5;) (func (param "x" 4)))
(type (;6;) (variant (case $c0 "a" unit) (case $c1 "b" string) (case $c2 "c" s64)))
(type (;7;) (func (param "x" string) (result 6)))
(type (;8;) (func (param "x" 6) (result string)))
(core module (;0;)
(type (;0;) (func (param i32 i32 i32 i32) (result i32)))
(type (;1;) (func))
(type (;2;) (func (param i32 i32 i32 i64) (result i32)))
(type (;3;) (func (result i32)))
(type (;4;) (func (param i32 i32) (result i32)))
(type (;5;) (func (param i32 i64 i32) (result i32)))
(type (;6;) (func (param i32)))
(func (;0;) (type 0) (param i32 i32 i32 i32) (result i32)
unreachable
)
(func (;1;) (type 1)
unreachable
)
(func (;2;) (type 2) (param i32 i32 i32 i64) (result i32)
unreachable
)
(func (;3;) (type 3) (result i32)
unreachable
)
(func (;4;) (type 1)
unreachable
)
(func (;5;) (type 4) (param i32 i32) (result i32)
unreachable
)
(func (;6;) (type 5) (param i32 i64 i32) (result i32)
unreachable
)
(func (;7;) (type 6) (param i32)
unreachable
)
(memory (;0;) 1)
(export "memory" (memory 0))
(export "canonical_abi_realloc" (func 0))
(export "a" (func 1))
(export "b" (func 2))
(export "c" (func 3))
(export "foo#a" (func 4))
(export "foo#b" (func 5))
(export "foo#c" (func 6))
(export "bar#a" (func 7))
)
(core instance (;0;) (instantiate 0))
(core alias export 0 "memory" (memory (;0;)))
(core alias export 0 "canonical_abi_realloc" (func (;0;)))
(core alias export 0 "a" (func (;1;)))
(core alias export 0 "b" (func (;2;)))
(core alias export 0 "c" (func (;3;)))
(func (;0;) (type 0) (canon lift (core func 1)))
(func (;1;) (type 1) (canon lift (core func 2) (memory 0) (realloc 0) string-encoding=utf8))
(func (;2;) (type 3) (canon lift (core func 3) (memory 0)))
(core alias export 0 "bar#a" (func (;4;)))
(func (;3;) (type 5) (canon lift (core func 4)))
(core alias export 0 "foo#a" (func (;5;)))
(core alias export 0 "foo#b" (func (;6;)))
(core alias export 0 "foo#c" (func (;7;)))
(func (;4;) (type 0) (canon lift (core func 5)))
(func (;5;) (type 7) (canon lift (core func 6) (memory 0) (realloc 0) string-encoding=utf8))
(func (;6;) (type 8) (canon lift (core func 7) (memory 0) (realloc 0) string-encoding=utf8))
(instance (;0;)
(export "a" (func 3))
)
(instance (;1;)
(export "a" (func 4))
(export "b" (func 5))
(export "c" (func 6))
)
(export "a" (func 0))
(export "b" (func 1))
(export "c" (func 2))
(export "bar" (instance 0))
(export "foo" (instance 1))
)

View File

@@ -0,0 +1,3 @@
a: func()
b: func(a: s8, b: s16, c: s32, d: s64) -> string
c: func() -> tuple<s8, s16, s32, s64>

View File

@@ -0,0 +1,7 @@
flags x {
a,
b,
c
}
a: func(x: x)

View File

@@ -0,0 +1,9 @@
variant x {
a,
b(string),
c(s64)
}
a: func()
b: func(x: string) -> x
c: func(x: x) -> string

View File

@@ -0,0 +1,11 @@
(module
(memory (export "memory") 1)
(func (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
(func (export "a") unreachable)
(func (export "b") (param i32 i32 i32 i64) (result i32) unreachable)
(func (export "c") (result i32) unreachable)
(func (export "foo#a") unreachable)
(func (export "foo#b") (param i32 i32) (result i32) unreachable)
(func (export "foo#c") (param i32 i64 i32) (result i32) unreachable)
(func (export "bar#a") (param i32) unreachable)
)

View File

@@ -0,0 +1,108 @@
(component
(type (;0;) (func (param "x" u64) (param "y" string)))
(type (;1;)
(instance
(alias outer 1 0 (type (;0;)))
(export "a" (func (type 0)))
)
)
(type (;2;) (list u8))
(type (;3;) (func (param "x" 2) (result 2)))
(type (;4;)
(instance
(alias outer 1 3 (type (;0;)))
(export "baz" (func (type 0)))
)
)
(type (;5;) (func))
(type (;6;)
(instance
(alias outer 1 5 (type (;0;)))
(export "a" (func (type 0)))
)
)
(import "bar" (instance (;0;) (type 1)))
(import "baz" (instance (;1;) (type 4)))
(import "foo" (instance (;2;) (type 6)))
(core module (;0;)
(type (;0;) (func))
(type (;1;) (func (param i64 i32 i32)))
(type (;2;) (func (param i32 i32 i32)))
(type (;3;) (func (param i32 i32 i32 i32) (result i32)))
(import "foo" "a" (func (;0;) (type 0)))
(import "bar" "a" (func (;1;) (type 1)))
(import "baz" "baz" (func (;2;) (type 2)))
(func (;3;) (type 3) (param i32 i32 i32 i32) (result i32)
unreachable
)
(memory (;0;) 1)
(export "memory" (memory 0))
(export "canonical_abi_realloc" (func 3))
)
(core module (;1;)
(type (;0;) (func (param i64 i32 i32)))
(type (;1;) (func (param i32 i32 i32)))
(func (;0;) (type 0) (param i64 i32 i32)
local.get 0
local.get 1
local.get 2
i32.const 0
call_indirect (type 0)
)
(func (;1;) (type 1) (param i32 i32 i32)
local.get 0
local.get 1
local.get 2
i32.const 1
call_indirect (type 1)
)
(table (;0;) 2 2 funcref)
(export "0" (func 0))
(export "1" (func 1))
(export "$imports" (table 0))
)
(core module (;2;)
(type (;0;) (func (param i64 i32 i32)))
(type (;1;) (func (param i32 i32 i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 1)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
)
(core instance (;0;) (instantiate 1))
(core alias export 0 "0" (func (;0;)))
(core alias export 0 "1" (func (;1;)))
(alias export 2 "a" (func (;0;)))
(core func (;2;) (canon lower (func 0)))
(core instance (;1;)
(export "a" (func 0))
)
(core instance (;2;)
(export "baz" (func 1))
)
(core instance (;3;)
(export "a" (func 2))
)
(core instance (;4;) (instantiate 0
(with "bar" (instance 1))
(with "baz" (instance 2))
(with "foo" (instance 3))
)
)
(core alias export 4 "memory" (memory (;0;)))
(core alias export 4 "canonical_abi_realloc" (func (;3;)))
(core alias export 0 "$imports" (table (;0;)))
(alias export 0 "a" (func (;1;)))
(alias export 1 "baz" (func (;2;)))
(core func (;4;) (canon lower (func 1) (memory 0) (realloc 3) string-encoding=utf8))
(core func (;5;) (canon lower (func 2) (memory 0) (realloc 3)))
(core instance (;5;)
(export "$imports" (table 0))
(export "0" (func 4))
(export "1" (func 5))
)
(core instance (;6;) (instantiate 2
(with "" (instance 5))
)
)
)

View File

@@ -0,0 +1 @@
baz: func(x: list<u8>) -> list<u8>

View File

@@ -0,0 +1,7 @@
(module
(import "foo" "a" (func))
(import "bar" "a" (func (param i64 i32 i32)))
(import "baz" "baz" (func (param i32 i32 i32)))
(memory (export "memory") 1)
(func (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
)

View File

@@ -0,0 +1,90 @@
(component
(type (;0;) (func (result string)))
(type (;1;)
(instance
(alias outer 1 0 (type (;0;)))
(export "a" (func (type 0)))
)
)
(type (;2;) (tuple string u32 string))
(type (;3;) (func (param "x" string) (result 2)))
(type (;4;) (func))
(import "foo" (instance (;0;) (type 1)))
(core module (;0;)
(type (;0;) (func (param i32)))
(type (;1;) (func (param i32 i32 i32 i32) (result i32)))
(type (;2;) (func (param i32 i32) (result i32)))
(type (;3;) (func))
(type (;4;) (func (result i32)))
(import "foo" "a" (func (;0;) (type 0)))
(func (;1;) (type 1) (param i32 i32 i32 i32) (result i32)
unreachable
)
(func (;2;) (type 2) (param i32 i32) (result i32)
unreachable
)
(func (;3;) (type 3)
unreachable
)
(func (;4;) (type 4) (result i32)
unreachable
)
(memory (;0;) 1)
(export "memory" (memory 0))
(export "canonical_abi_realloc" (func 1))
(export "a" (func 2))
(export "bar#a" (func 3))
(export "bar#b" (func 4))
)
(core module (;1;)
(type (;0;) (func (param i32)))
(func (;0;) (type 0) (param i32)
local.get 0
i32.const 0
call_indirect (type 0)
)
(table (;0;) 1 1 funcref)
(export "0" (func 0))
(export "$imports" (table 0))
)
(core module (;2;)
(type (;0;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "$imports" (table (;0;) 1 1 funcref))
(elem (;0;) (i32.const 0) func 0)
)
(core instance (;0;) (instantiate 1))
(core alias export 0 "0" (func (;0;)))
(core instance (;1;)
(export "a" (func 0))
)
(core instance (;2;) (instantiate 0
(with "foo" (instance 1))
)
)
(core alias export 2 "memory" (memory (;0;)))
(core alias export 2 "canonical_abi_realloc" (func (;1;)))
(core alias export 0 "$imports" (table (;0;)))
(alias export 0 "a" (func (;0;)))
(core func (;2;) (canon lower (func 0) (memory 0) (realloc 1) string-encoding=utf8))
(core instance (;3;)
(export "$imports" (table 0))
(export "0" (func 2))
)
(core instance (;4;) (instantiate 2
(with "" (instance 3))
)
)
(core alias export 2 "a" (func (;3;)))
(func (;1;) (type 3) (canon lift (core func 3) (memory 0) (realloc 1) string-encoding=utf8))
(core alias export 2 "bar#a" (func (;4;)))
(core alias export 2 "bar#b" (func (;5;)))
(func (;2;) (type 4) (canon lift (core func 4)))
(func (;3;) (type 0) (canon lift (core func 5) (memory 0) (realloc 1) string-encoding=utf8))
(instance (;1;)
(export "a" (func 2))
(export "b" (func 3))
)
(export "a" (func 1))
(export "bar" (instance 1))
)

View File

@@ -0,0 +1 @@
a: func(x: string) -> tuple<string, u32, string>

View File

@@ -0,0 +1,2 @@
a: func()
b: func() -> string

View File

@@ -0,0 +1,8 @@
(module
(import "foo" "a" (func (param i32)))
(memory (export "memory") 1)
(func (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
(func (export "a") (param i32 i32) (result i32) unreachable)
(func (export "bar#a") unreachable)
(func (export "bar#b") (result i32) unreachable)
)

View File

@@ -0,0 +1 @@
type mismatch for function `bar` on imported interface `foo`: expected `[I32, I32] -> []` but found `[] -> []`

View File

@@ -0,0 +1,3 @@
(module
(import "foo" "bar" (func))
)

View File

@@ -0,0 +1,141 @@
(component
(type (;0;) (func (param "x" string)))
(type (;1;) (record (field "a" u8)))
(type (;2;) (func (param "x" 1)))
(type (;3;)
(instance
(alias outer 1 0 (type (;0;)))
(export "bar1" (func (type 0)))
(alias outer 1 2 (type (;1;)))
(export "bar2" (func (type 1)))
)
)
(type (;4;) (list string))
(type (;5;) (func (param "x" 4)))
(type (;6;) (func))
(type (;7;) s8)
(type (;8;) (func (param "x" 7)))
(type (;9;)
(instance
(alias outer 1 5 (type (;0;)))
(export "baz1" (func (type 0)))
(alias outer 1 6 (type (;1;)))
(export "baz2" (func (type 1)))
(alias outer 1 8 (type (;2;)))
(export "baz3" (func (type 2)))
)
)
(type (;10;) (func (param "x" u8)))
(type (;11;) (func (param "x" float32)))
(type (;12;)
(instance
(alias outer 1 6 (type (;0;)))
(export "foo1" (func (type 0)))
(alias outer 1 10 (type (;1;)))
(export "foo2" (func (type 1)))
(alias outer 1 11 (type (;2;)))
(export "foo3" (func (type 2)))
)
)
(import "bar" (instance (;0;) (type 3)))
(import "baz" (instance (;1;) (type 9)))
(import "foo" (instance (;2;) (type 12)))
(core module (;0;)
(type (;0;) (func))
(type (;1;) (func (param i32)))
(type (;2;) (func (param f32)))
(type (;3;) (func (param i32 i32)))
(type (;4;) (func (param i32 i32 i32 i32) (result i32)))
(import "foo" "foo1" (func (;0;) (type 0)))
(import "foo" "foo2" (func (;1;) (type 1)))
(import "foo" "foo3" (func (;2;) (type 2)))
(import "bar" "bar1" (func (;3;) (type 3)))
(import "bar" "bar2" (func (;4;) (type 1)))
(import "baz" "baz1" (func (;5;) (type 3)))
(import "baz" "baz2" (func (;6;) (type 0)))
(import "baz" "baz3" (func (;7;) (type 1)))
(func (;8;) (type 4) (param i32 i32 i32 i32) (result i32)
unreachable
)
(memory (;0;) 1)
(export "memory" (memory 0))
(export "canonical_abi_realloc" (func 8))
)
(core module (;1;)
(type (;0;) (func (param i32 i32)))
(func (;0;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 0
call_indirect (type 0)
)
(func (;1;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 1
call_indirect (type 0)
)
(table (;0;) 2 2 funcref)
(export "0" (func 0))
(export "1" (func 1))
(export "$imports" (table 0))
)
(core module (;2;)
(type (;0;) (func (param i32 i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 0)))
(import "" "$imports" (table (;0;) 2 2 funcref))
(elem (;0;) (i32.const 0) func 0 1)
)
(core instance (;0;) (instantiate 1))
(core alias export 0 "0" (func (;0;)))
(alias export 0 "bar2" (func (;0;)))
(core func (;1;) (canon lower (func 0)))
(core alias export 0 "1" (func (;2;)))
(alias export 1 "baz2" (func (;1;)))
(alias export 1 "baz3" (func (;2;)))
(core func (;3;) (canon lower (func 1)))
(core func (;4;) (canon lower (func 2)))
(alias export 2 "foo1" (func (;3;)))
(alias export 2 "foo2" (func (;4;)))
(alias export 2 "foo3" (func (;5;)))
(core func (;5;) (canon lower (func 3)))
(core func (;6;) (canon lower (func 4)))
(core func (;7;) (canon lower (func 5)))
(core instance (;1;)
(export "bar1" (func 0))
(export "bar2" (func 1))
)
(core instance (;2;)
(export "baz1" (func 2))
(export "baz2" (func 3))
(export "baz3" (func 4))
)
(core instance (;3;)
(export "foo1" (func 5))
(export "foo2" (func 6))
(export "foo3" (func 7))
)
(core instance (;4;) (instantiate 0
(with "bar" (instance 1))
(with "baz" (instance 2))
(with "foo" (instance 3))
)
)
(core alias export 4 "memory" (memory (;0;)))
(core alias export 4 "canonical_abi_realloc" (func (;8;)))
(core alias export 0 "$imports" (table (;0;)))
(alias export 0 "bar1" (func (;6;)))
(alias export 1 "baz1" (func (;7;)))
(core func (;9;) (canon lower (func 6) (memory 0) (realloc 8) string-encoding=utf8))
(core func (;10;) (canon lower (func 7) (memory 0) (realloc 8) string-encoding=utf8))
(core instance (;5;)
(export "$imports" (table 0))
(export "0" (func 9))
(export "1" (func 10))
)
(core instance (;6;) (instantiate 2
(with "" (instance 5))
)
)
)

View File

@@ -0,0 +1,6 @@
record x {
a: u8
}
bar1: func(x: string)
bar2: func(x: x)

View File

@@ -0,0 +1,5 @@
type x = s8
baz1: func(x: list<string>)
baz2: func()
baz3: func(x: x)

View File

@@ -0,0 +1,3 @@
foo1: func()
foo2: func(x: u8)
foo3: func(x: float32)

View File

@@ -0,0 +1,12 @@
(module
(import "foo" "foo1" (func))
(import "foo" "foo2" (func (param i32)))
(import "foo" "foo3" (func (param f32)))
(import "bar" "bar1" (func (param i32 i32)))
(import "bar" "bar2" (func (param i32)))
(import "baz" "baz1" (func (param i32 i32)))
(import "baz" "baz2" (func))
(import "baz" "baz3" (func (param i32)))
(memory (export "memory") 1)
(func (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
)

View File

@@ -0,0 +1 @@
module is only allowed to import functions

View File

@@ -0,0 +1,3 @@
(module
(import "" "" (table 1 funcref))
)

View File

@@ -0,0 +1,161 @@
(component
(type (;0;) (func))
(type (;1;) (list string))
(type (;2;) (func (param "x" 1)))
(type (;3;) (record (field "s" string)))
(type (;4;) (func (param "x" 3)))
(type (;5;) (variant (case $c0 "s" string)))
(type (;6;) (func (param "x" 5)))
(type (;7;) (record (field "s" u32)))
(type (;8;) (func (param "x" 7)))
(type (;9;) (variant (case $c0 "s" u32)))
(type (;10;) (func (param "x" 9)))
(type (;11;) (list 3))
(type (;12;) (func (param "x" 11)))
(type (;13;) (list 5))
(type (;14;) (func (param "x" 13)))
(type (;15;) (list u32))
(type (;16;) (func (param "x" 15)))
(type (;17;) (func (param "x" u32)))
(type (;18;) (tuple u32 u32))
(type (;19;) (func (result 18)))
(type (;20;) (func (result string)))
(type (;21;) (func (result 15)))
(type (;22;) (func (result u32)))
(type (;23;) (func (result 5)))
(type (;24;) (list 9))
(type (;25;) (func (result 24)))
(export "r" (type 3))
(export "v" (type 5))
(export "r-no-string" (type 7))
(export "v-no-string" (type 9))
(core module (;0;)
(type (;0;) (func (param i32 i32 i32 i32) (result i32)))
(type (;1;) (func))
(type (;2;) (func (param i32 i32)))
(type (;3;) (func (param i32 i32 i32)))
(type (;4;) (func (param i32)))
(type (;5;) (func (result i32)))
(func (;0;) (type 0) (param i32 i32 i32 i32) (result i32)
unreachable
)
(func (;1;) (type 1)
unreachable
)
(func (;2;) (type 2) (param i32 i32)
unreachable
)
(func (;3;) (type 2) (param i32 i32)
unreachable
)
(func (;4;) (type 3) (param i32 i32 i32)
unreachable
)
(func (;5;) (type 4) (param i32)
unreachable
)
(func (;6;) (type 2) (param i32 i32)
unreachable
)
(func (;7;) (type 2) (param i32 i32)
unreachable
)
(func (;8;) (type 2) (param i32 i32)
unreachable
)
(func (;9;) (type 2) (param i32 i32)
unreachable
)
(func (;10;) (type 4) (param i32)
unreachable
)
(func (;11;) (type 5) (result i32)
unreachable
)
(func (;12;) (type 5) (result i32)
unreachable
)
(func (;13;) (type 5) (result i32)
unreachable
)
(func (;14;) (type 5) (result i32)
unreachable
)
(func (;15;) (type 5) (result i32)
unreachable
)
(func (;16;) (type 5) (result i32)
unreachable
)
(memory (;0;) 1)
(export "memory" (memory 0))
(export "canonical_abi_realloc" (func 0))
(export "a" (func 1))
(export "b" (func 2))
(export "c" (func 3))
(export "d" (func 4))
(export "e" (func 5))
(export "f" (func 6))
(export "g" (func 7))
(export "h" (func 8))
(export "i" (func 9))
(export "j" (func 10))
(export "k" (func 11))
(export "l" (func 12))
(export "m" (func 13))
(export "n" (func 14))
(export "o" (func 15))
(export "p" (func 16))
)
(core instance (;0;) (instantiate 0))
(core alias export 0 "memory" (memory (;0;)))
(core alias export 0 "canonical_abi_realloc" (func (;0;)))
(core alias export 0 "a" (func (;1;)))
(core alias export 0 "b" (func (;2;)))
(core alias export 0 "c" (func (;3;)))
(core alias export 0 "d" (func (;4;)))
(core alias export 0 "e" (func (;5;)))
(core alias export 0 "f" (func (;6;)))
(core alias export 0 "g" (func (;7;)))
(core alias export 0 "h" (func (;8;)))
(core alias export 0 "i" (func (;9;)))
(core alias export 0 "j" (func (;10;)))
(core alias export 0 "k" (func (;11;)))
(core alias export 0 "l" (func (;12;)))
(core alias export 0 "m" (func (;13;)))
(core alias export 0 "n" (func (;14;)))
(core alias export 0 "o" (func (;15;)))
(core alias export 0 "p" (func (;16;)))
(func (;0;) (type 0) (canon lift (core func 1)))
(func (;1;) (type 2) (canon lift (core func 2) (memory 0) (realloc 0) string-encoding=utf8))
(func (;2;) (type 4) (canon lift (core func 3) (memory 0) (realloc 0) string-encoding=utf8))
(func (;3;) (type 6) (canon lift (core func 4) (memory 0) (realloc 0) string-encoding=utf8))
(func (;4;) (type 8) (canon lift (core func 5)))
(func (;5;) (type 10) (canon lift (core func 6)))
(func (;6;) (type 12) (canon lift (core func 7) (memory 0) (realloc 0) string-encoding=utf8))
(func (;7;) (type 14) (canon lift (core func 8) (memory 0) (realloc 0) string-encoding=utf8))
(func (;8;) (type 16) (canon lift (core func 9) (memory 0) (realloc 0)))
(func (;9;) (type 17) (canon lift (core func 10)))
(func (;10;) (type 19) (canon lift (core func 11) (memory 0)))
(func (;11;) (type 20) (canon lift (core func 12) (memory 0) (realloc 0) string-encoding=utf8))
(func (;12;) (type 21) (canon lift (core func 13) (memory 0) (realloc 0)))
(func (;13;) (type 22) (canon lift (core func 14)))
(func (;14;) (type 23) (canon lift (core func 15) (memory 0) (realloc 0) string-encoding=utf8))
(func (;15;) (type 25) (canon lift (core func 16) (memory 0) (realloc 0)))
(export "a" (func 0))
(export "b" (func 1))
(export "c" (func 2))
(export "d" (func 3))
(export "e" (func 4))
(export "f" (func 5))
(export "g" (func 6))
(export "h" (func 7))
(export "i" (func 8))
(export "j" (func 9))
(export "k" (func 10))
(export "l" (func 11))
(export "m" (func 12))
(export "n" (func 13))
(export "o" (func 14))
(export "p" (func 15))
)

View File

@@ -0,0 +1,32 @@
record r {
s: string
}
record r-no-string {
s: u32
}
variant v {
s(string)
}
variant v-no-string {
s(u32)
}
a: func()
b: func(x: list<string>)
c: func(x: r)
d: func(x: v)
e: func(x: r-no-string)
f: func(x: v-no-string)
g: func(x: list<r>)
h: func(x: list<v>)
i: func(x: list<u32>)
j: func(x: u32)
k: func() -> tuple<u32, u32>
l: func() -> string
m: func() -> list<u32>
n: func() -> u32
o: func() -> v
p: func() -> list<v-no-string>

View File

@@ -0,0 +1,20 @@
(module
(memory (export "memory") 1)
(func (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
(func (export "a") unreachable)
(func (export "b") (param i32 i32) unreachable)
(func (export "c") (param i32 i32) unreachable)
(func (export "d") (param i32 i32 i32) unreachable)
(func (export "e") (param i32) unreachable)
(func (export "f") (param i32 i32) unreachable)
(func (export "g") (param i32 i32) unreachable)
(func (export "h") (param i32 i32) unreachable)
(func (export "i") (param i32 i32) unreachable)
(func (export "j") (param i32) unreachable)
(func (export "k") (result i32) unreachable)
(func (export "l") (result i32) unreachable)
(func (export "m") (result i32) unreachable)
(func (export "n") (result i32) unreachable)
(func (export "o") (result i32) unreachable)
(func (export "p") (result i32) unreachable)
)

View File

@@ -0,0 +1,280 @@
(component
(type (;0;) (func))
(type (;1;) (list string))
(type (;2;) (func (param "x" 1)))
(type (;3;) (record (field "s" string)))
(type (;4;) (func (param "x" 3)))
(type (;5;) (variant (case $c0 "s" string)))
(type (;6;) (func (param "x" 5)))
(type (;7;) (record (field "s" u32)))
(type (;8;) (func (param "x" 7)))
(type (;9;) (variant (case $c0 "s" u32)))
(type (;10;) (func (param "x" 9)))
(type (;11;) (list 3))
(type (;12;) (func (param "x" 11)))
(type (;13;) (list 5))
(type (;14;) (func (param "x" 13)))
(type (;15;) (list u32))
(type (;16;) (func (param "x" 15)))
(type (;17;) (func (param "x" u32)))
(type (;18;) (tuple u32 u32))
(type (;19;) (func (result 18)))
(type (;20;) (func (result string)))
(type (;21;) (func (result 15)))
(type (;22;) (func (result u32)))
(type (;23;) (func (result 5)))
(type (;24;) (list 9))
(type (;25;) (func (result 24)))
(type (;26;)
(instance
(alias outer 1 0 (type (;0;)))
(export "a" (func (type 0)))
(alias outer 1 2 (type (;1;)))
(export "b" (func (type 1)))
(alias outer 1 4 (type (;2;)))
(export "c" (func (type 2)))
(alias outer 1 6 (type (;3;)))
(export "d" (func (type 3)))
(alias outer 1 8 (type (;4;)))
(export "e" (func (type 4)))
(alias outer 1 10 (type (;5;)))
(export "f" (func (type 5)))
(alias outer 1 12 (type (;6;)))
(export "g" (func (type 6)))
(alias outer 1 14 (type (;7;)))
(export "h" (func (type 7)))
(alias outer 1 16 (type (;8;)))
(export "i" (func (type 8)))
(alias outer 1 17 (type (;9;)))
(export "j" (func (type 9)))
(alias outer 1 19 (type (;10;)))
(export "k" (func (type 10)))
(alias outer 1 20 (type (;11;)))
(export "l" (func (type 11)))
(alias outer 1 21 (type (;12;)))
(export "m" (func (type 12)))
(alias outer 1 22 (type (;13;)))
(export "n" (func (type 13)))
(alias outer 1 23 (type (;14;)))
(export "o" (func (type 14)))
(alias outer 1 25 (type (;15;)))
(export "p" (func (type 15)))
)
)
(import "foo" (instance (;0;) (type 26)))
(core module (;0;)
(type (;0;) (func))
(type (;1;) (func (param i32 i32)))
(type (;2;) (func (param i32 i32 i32)))
(type (;3;) (func (param i32)))
(type (;4;) (func (result i32)))
(type (;5;) (func (param i32 i32 i32 i32) (result i32)))
(import "foo" "a" (func (;0;) (type 0)))
(import "foo" "b" (func (;1;) (type 1)))
(import "foo" "c" (func (;2;) (type 1)))
(import "foo" "d" (func (;3;) (type 2)))
(import "foo" "e" (func (;4;) (type 3)))
(import "foo" "f" (func (;5;) (type 1)))
(import "foo" "g" (func (;6;) (type 1)))
(import "foo" "h" (func (;7;) (type 1)))
(import "foo" "i" (func (;8;) (type 1)))
(import "foo" "j" (func (;9;) (type 3)))
(import "foo" "k" (func (;10;) (type 3)))
(import "foo" "l" (func (;11;) (type 3)))
(import "foo" "m" (func (;12;) (type 3)))
(import "foo" "n" (func (;13;) (type 4)))
(import "foo" "o" (func (;14;) (type 3)))
(import "foo" "p" (func (;15;) (type 3)))
(func (;16;) (type 5) (param i32 i32 i32 i32) (result i32)
unreachable
)
(memory (;0;) 1)
(export "memory" (memory 0))
(export "canonical_abi_realloc" (func 16))
)
(core module (;1;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func (param i32 i32 i32)))
(type (;2;) (func (param i32)))
(func (;0;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 0
call_indirect (type 0)
)
(func (;1;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 1
call_indirect (type 0)
)
(func (;2;) (type 1) (param i32 i32 i32)
local.get 0
local.get 1
local.get 2
i32.const 2
call_indirect (type 1)
)
(func (;3;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 3
call_indirect (type 0)
)
(func (;4;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 4
call_indirect (type 0)
)
(func (;5;) (type 0) (param i32 i32)
local.get 0
local.get 1
i32.const 5
call_indirect (type 0)
)
(func (;6;) (type 2) (param i32)
local.get 0
i32.const 6
call_indirect (type 2)
)
(func (;7;) (type 2) (param i32)
local.get 0
i32.const 7
call_indirect (type 2)
)
(func (;8;) (type 2) (param i32)
local.get 0
i32.const 8
call_indirect (type 2)
)
(func (;9;) (type 2) (param i32)
local.get 0
i32.const 9
call_indirect (type 2)
)
(func (;10;) (type 2) (param i32)
local.get 0
i32.const 10
call_indirect (type 2)
)
(table (;0;) 11 11 funcref)
(export "0" (func 0))
(export "1" (func 1))
(export "2" (func 2))
(export "3" (func 3))
(export "4" (func 4))
(export "5" (func 5))
(export "6" (func 6))
(export "7" (func 7))
(export "8" (func 8))
(export "9" (func 9))
(export "10" (func 10))
(export "$imports" (table 0))
)
(core module (;2;)
(type (;0;) (func (param i32 i32)))
(type (;1;) (func (param i32 i32 i32)))
(type (;2;) (func (param i32)))
(import "" "0" (func (;0;) (type 0)))
(import "" "1" (func (;1;) (type 0)))
(import "" "2" (func (;2;) (type 1)))
(import "" "3" (func (;3;) (type 0)))
(import "" "4" (func (;4;) (type 0)))
(import "" "5" (func (;5;) (type 0)))
(import "" "6" (func (;6;) (type 2)))
(import "" "7" (func (;7;) (type 2)))
(import "" "8" (func (;8;) (type 2)))
(import "" "9" (func (;9;) (type 2)))
(import "" "10" (func (;10;) (type 2)))
(import "" "$imports" (table (;0;) 11 11 funcref))
(elem (;0;) (i32.const 0) func 0 1 2 3 4 5 6 7 8 9 10)
)
(core instance (;0;) (instantiate 1))
(core alias export 0 "0" (func (;0;)))
(core alias export 0 "1" (func (;1;)))
(core alias export 0 "2" (func (;2;)))
(core alias export 0 "3" (func (;3;)))
(core alias export 0 "4" (func (;4;)))
(core alias export 0 "5" (func (;5;)))
(core alias export 0 "6" (func (;6;)))
(core alias export 0 "7" (func (;7;)))
(core alias export 0 "8" (func (;8;)))
(core alias export 0 "9" (func (;9;)))
(core alias export 0 "10" (func (;10;)))
(alias export 0 "a" (func (;0;)))
(alias export 0 "e" (func (;1;)))
(alias export 0 "f" (func (;2;)))
(alias export 0 "j" (func (;3;)))
(alias export 0 "n" (func (;4;)))
(core func (;11;) (canon lower (func 0)))
(core func (;12;) (canon lower (func 1)))
(core func (;13;) (canon lower (func 2)))
(core func (;14;) (canon lower (func 3)))
(core func (;15;) (canon lower (func 4)))
(core instance (;1;)
(export "b" (func 0))
(export "c" (func 1))
(export "d" (func 2))
(export "g" (func 3))
(export "h" (func 4))
(export "i" (func 5))
(export "k" (func 6))
(export "l" (func 7))
(export "m" (func 8))
(export "o" (func 9))
(export "p" (func 10))
(export "a" (func 11))
(export "e" (func 12))
(export "f" (func 13))
(export "j" (func 14))
(export "n" (func 15))
)
(core instance (;2;) (instantiate 0
(with "foo" (instance 1))
)
)
(core alias export 2 "memory" (memory (;0;)))
(core alias export 2 "canonical_abi_realloc" (func (;16;)))
(core alias export 0 "$imports" (table (;0;)))
(alias export 0 "b" (func (;5;)))
(alias export 0 "c" (func (;6;)))
(alias export 0 "d" (func (;7;)))
(alias export 0 "g" (func (;8;)))
(alias export 0 "h" (func (;9;)))
(alias export 0 "i" (func (;10;)))
(alias export 0 "k" (func (;11;)))
(alias export 0 "l" (func (;12;)))
(alias export 0 "m" (func (;13;)))
(alias export 0 "o" (func (;14;)))
(alias export 0 "p" (func (;15;)))
(core func (;17;) (canon lower (func 5) (memory 0) (realloc 16) string-encoding=utf8))
(core func (;18;) (canon lower (func 6) (memory 0) (realloc 16) string-encoding=utf8))
(core func (;19;) (canon lower (func 7) (memory 0) (realloc 16) string-encoding=utf8))
(core func (;20;) (canon lower (func 8) (memory 0) (realloc 16) string-encoding=utf8))
(core func (;21;) (canon lower (func 9) (memory 0) (realloc 16) string-encoding=utf8))
(core func (;22;) (canon lower (func 10) (memory 0) (realloc 16)))
(core func (;23;) (canon lower (func 11) (memory 0)))
(core func (;24;) (canon lower (func 12) (memory 0) (realloc 16) string-encoding=utf8))
(core func (;25;) (canon lower (func 13) (memory 0) (realloc 16)))
(core func (;26;) (canon lower (func 14) (memory 0) (realloc 16) string-encoding=utf8))
(core func (;27;) (canon lower (func 15) (memory 0) (realloc 16)))
(core instance (;3;)
(export "$imports" (table 0))
(export "0" (func 17))
(export "1" (func 18))
(export "2" (func 19))
(export "3" (func 20))
(export "4" (func 21))
(export "5" (func 22))
(export "6" (func 23))
(export "7" (func 24))
(export "8" (func 25))
(export "9" (func 26))
(export "10" (func 27))
)
(core instance (;4;) (instantiate 2
(with "" (instance 3))
)
)
)

View File

@@ -0,0 +1,32 @@
record r {
s: string
}
record r-no-string {
s: u32
}
variant v {
s(string)
}
variant v-no-string {
s(u32)
}
a: func()
b: func(x: list<string>)
c: func(x: r)
d: func(x: v)
e: func(x: r-no-string)
f: func(x: v-no-string)
g: func(x: list<r>)
h: func(x: list<v>)
i: func(x: list<u32>)
j: func(x: u32)
k: func() -> tuple<u32, u32>
l: func() -> string
m: func() -> list<u32>
n: func() -> u32
o: func() -> v
p: func() -> list<v-no-string>

View File

@@ -0,0 +1,20 @@
(module
(import "foo" "a" (func))
(import "foo" "b" (func (param i32 i32)))
(import "foo" "c" (func (param i32 i32)))
(import "foo" "d" (func (param i32 i32 i32)))
(import "foo" "e" (func (param i32)))
(import "foo" "f" (func (param i32 i32)))
(import "foo" "g" (func (param i32 i32)))
(import "foo" "h" (func (param i32 i32)))
(import "foo" "i" (func (param i32 i32)))
(import "foo" "j" (func (param i32)))
(import "foo" "k" (func (param i32)))
(import "foo" "l" (func (param i32)))
(import "foo" "m" (func (param i32)))
(import "foo" "n" (func (result i32)))
(import "foo" "o" (func (param i32)))
(import "foo" "p" (func (param i32)))
(memory (export "memory") 1)
(func (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
)

View File

@@ -0,0 +1 @@
module does not export required function `a`

View File

@@ -0,0 +1 @@
module does not export required function `foo#a`

View File

@@ -0,0 +1 @@
import interface `foo` is missing function `bar` that is required by the module

View File

@@ -0,0 +1,3 @@
(module
(import "foo" "bar" (func))
)

View File

@@ -0,0 +1 @@
module requires an import interface named `foo`

View File

@@ -0,0 +1,3 @@
(module
(import "foo" "bar" (func))
)

View File

@@ -0,0 +1,52 @@
(component
(type (;0;) (func))
(type (;1;) (func (result string)))
(type (;2;) (func (param "x" string) (result string)))
(type (;3;) (list string))
(type (;4;) (func (param "x" 3)))
(export "x" (type 3))
(core module (;0;)
(type (;0;) (func (param i32 i32 i32 i32) (result i32)))
(type (;1;) (func))
(type (;2;) (func (result i32)))
(type (;3;) (func (param i32 i32) (result i32)))
(type (;4;) (func (param i32 i32)))
(func $canonical_abi_realloc (;0;) (type 0) (param i32 i32 i32 i32) (result i32)
unreachable
)
(func $a (;1;) (type 1)
unreachable
)
(func $b (;2;) (type 2) (result i32)
unreachable
)
(func $c (;3;) (type 3) (param i32 i32) (result i32)
unreachable
)
(func $d (;4;) (type 4) (param i32 i32)
unreachable
)
(memory $memory (;0;) 1)
(export "memory" (memory $memory))
(export "canonical_abi_realloc" (func $canonical_abi_realloc))
(export "a" (func $a))
(export "b" (func $b))
(export "c" (func $c))
(export "d" (func $d))
)
(core instance (;0;) (instantiate 0))
(core alias export 0 "memory" (memory (;0;)))
(core alias export 0 "canonical_abi_realloc" (func (;0;)))
(core alias export 0 "a" (func (;1;)))
(core alias export 0 "b" (func (;2;)))
(core alias export 0 "c" (func (;3;)))
(core alias export 0 "d" (func (;4;)))
(func (;0;) (type 0) (canon lift (core func 1)))
(func (;1;) (type 1) (canon lift (core func 2) (memory 0) (realloc 0) string-encoding=utf8))
(func (;2;) (type 2) (canon lift (core func 3) (memory 0) (realloc 0) string-encoding=utf8))
(func (;3;) (type 4) (canon lift (core func 4) (memory 0) (realloc 0) string-encoding=utf8))
(export "a" (func 0))
(export "b" (func 1))
(export "c" (func 2))
(export "d" (func 3))
)

View File

@@ -0,0 +1,6 @@
type x = list<string>
a: func()
b: func() -> string
c: func(x: string) -> string
d: func(x: x)

View File

@@ -0,0 +1,8 @@
(module
(memory $memory (export "memory") 1)
(func $canonical_abi_realloc (export "canonical_abi_realloc") (param i32 i32 i32 i32) (result i32) unreachable)
(func $a (export "a") unreachable)
(func $b (export "b") (result i32) unreachable)
(func $c (export "c") (param i32 i32) (result i32) unreachable)
(func $d (export "d") (param i32 i32) unreachable)
)

View File

@@ -0,0 +1,60 @@
use anyhow::{Context, Result};
use pretty_assertions::assert_eq;
use std::fs;
use wit_component::InterfaceEncoder;
use wit_parser::Interface;
/// Tests the encoding of individual interface files.
///
/// This test looks in the `interfaces/` directory for test cases.
///
/// Each test case is a directory containing a `<testcase>.wit` file
/// and an expected `<testcase>.wat` file.
///
/// The test encodes the wit file, prints the resulting component, and
/// compares the output to the wat file.
///
/// Run the test with the environment variable `BLESS` set to update
/// the wat baseline file.
#[test]
fn interface_encoding() -> Result<()> {
for entry in fs::read_dir("tests/interfaces")? {
let path = entry?.path();
if !path.is_dir() {
continue;
}
let test_case = path.file_stem().unwrap().to_str().unwrap();
let wit_path = path.join(test_case).with_extension("wit");
let interface = Interface::parse_file(&wit_path)?;
let encoder = InterfaceEncoder::new(&interface).validate(true);
let bytes = encoder.encode().with_context(|| {
format!(
"failed to encode a component from interface `{}` for test case `{}`",
wit_path.display(),
test_case,
)
})?;
let output = wasmprinter::print_bytes(&bytes)?;
let wat_path = wit_path.with_extension("wat");
if std::env::var_os("BLESS").is_some() {
fs::write(&wat_path, output)?;
} else {
assert_eq!(
fs::read_to_string(&wat_path)?.replace("\r\n", "\n"),
output,
"encoding of wit file `{}` did not match the expected wat file `{}` for test case `{}`",
wit_path.display(),
wat_path.display(),
test_case
);
}
}
Ok(())
}

View File

@@ -0,0 +1,30 @@
(component
(type (;0;) (flags "b0"))
(type (;1;) (func (param "x" 0) (result 0)))
(type (;2;) (flags "b0" "b1"))
(type (;3;) (func (param "x" 2) (result 2)))
(type (;4;) (flags "b0" "b1" "b2" "b3"))
(type (;5;) (func (param "x" 4) (result 4)))
(type (;6;) (flags "b0" "b1" "b2" "b3" "b4" "b5" "b6" "b7"))
(type (;7;) (func (param "x" 6) (result 6)))
(type (;8;) (flags "b0" "b1" "b2" "b3" "b4" "b5" "b6" "b7" "b8" "b9" "b10" "b11" "b12" "b13" "b14" "b15"))
(type (;9;) (func (param "x" 8) (result 8)))
(type (;10;) (flags "b0" "b1" "b2" "b3" "b4" "b5" "b6" "b7" "b8" "b9" "b10" "b11" "b12" "b13" "b14" "b15" "b16" "b17" "b18" "b19" "b20" "b21" "b22" "b23" "b24" "b25" "b26" "b27" "b28" "b29" "b30" "b31"))
(type (;11;) (func (param "x" 10) (result 10)))
(type (;12;) (flags "b0" "b1" "b2" "b3" "b4" "b5" "b6" "b7" "b8" "b9" "b10" "b11" "b12" "b13" "b14" "b15" "b16" "b17" "b18" "b19" "b20" "b21" "b22" "b23" "b24" "b25" "b26" "b27" "b28" "b29" "b30" "b31" "b32" "b33" "b34" "b35" "b36" "b37" "b38" "b39" "b40" "b41" "b42" "b43" "b44" "b45" "b46" "b47" "b48" "b49" "b50" "b51" "b52" "b53" "b54" "b55" "b56" "b57" "b58" "b59" "b60" "b61" "b62" "b63"))
(type (;13;) (func (param "x" 12) (result 12)))
(export "flag1" (type 0))
(export "roundtrip-flag1" (type 1))
(export "flag2" (type 2))
(export "roundtrip-flag2" (type 3))
(export "flag4" (type 4))
(export "roundtrip-flag4" (type 5))
(export "flag8" (type 6))
(export "roundtrip-flag8" (type 7))
(export "flag16" (type 8))
(export "roundtrip-flag16" (type 9))
(export "flag32" (type 10))
(export "roundtrip-flag32" (type 11))
(export "flag64" (type 12))
(export "roundtrip-flag64" (type 13))
)

View File

@@ -0,0 +1,162 @@
flags flag1 {
b0,
}
flags flag2 {
b0,
b1,
}
flags flag4 {
b0,
b1,
b2,
b3,
}
flags flag8 {
b0,
b1,
b2,
b3,
b4,
b5,
b6,
b7,
}
flags flag16 {
b0,
b1,
b2,
b3,
b4,
b5,
b6,
b7,
b8,
b9,
b10,
b11,
b12,
b13,
b14,
b15,
}
flags flag32 {
b0,
b1,
b2,
b3,
b4,
b5,
b6,
b7,
b8,
b9,
b10,
b11,
b12,
b13,
b14,
b15,
b16,
b17,
b18,
b19,
b20,
b21,
b22,
b23,
b24,
b25,
b26,
b27,
b28,
b29,
b30,
b31,
}
flags flag64 {
b0,
b1,
b2,
b3,
b4,
b5,
b6,
b7,
b8,
b9,
b10,
b11,
b12,
b13,
b14,
b15,
b16,
b17,
b18,
b19,
b20,
b21,
b22,
b23,
b24,
b25,
b26,
b27,
b28,
b29,
b30,
b31,
b32,
b33,
b34,
b35,
b36,
b37,
b38,
b39,
b40,
b41,
b42,
b43,
b44,
b45,
b46,
b47,
b48,
b49,
b50,
b51,
b52,
b53,
b54,
b55,
b56,
b57,
b58,
b59,
b60,
b61,
b62,
b63,
}
roundtrip-flag1: func(x: flag1) -> flag1
roundtrip-flag2: func(x: flag2) -> flag2
roundtrip-flag4: func(x: flag4) -> flag4
roundtrip-flag8: func(x: flag8) -> flag8
roundtrip-flag16: func(x: flag16) -> flag16
roundtrip-flag32: func(x: flag32) -> flag32
roundtrip-flag64: func(x: flag64) -> flag64

View File

@@ -0,0 +1,10 @@
(component
(type (;0;) (func (param "x" float32)))
(type (;1;) (func (param "x" float64)))
(type (;2;) (func (result float32)))
(type (;3;) (func (result float64)))
(export "float32-param" (type 0))
(export "float64-param" (type 1))
(export "float32-result" (type 2))
(export "float64-result" (type 3))
)

View File

@@ -0,0 +1,8 @@
float32-param: func(x: float32)
float64-param: func(x: float64)
float32-result: func() -> float32
float64-result: func() -> float64

View File

@@ -0,0 +1,40 @@
(component
(type (;0;) (func (param "x" u8)))
(type (;1;) (func (param "x" s8)))
(type (;2;) (func (param "x" u16)))
(type (;3;) (func (param "x" s16)))
(type (;4;) (func (param "x" u32)))
(type (;5;) (func (param "x" s32)))
(type (;6;) (func (param "x" u64)))
(type (;7;) (func (param "x" s64)))
(type (;8;) (func (param "p1" u8) (param "p2" s8) (param "p3" u16) (param "p4" s16) (param "p5" u32) (param "p6" s32) (param "p7" u64) (param "p8" s64)))
(type (;9;) (func (result u8)))
(type (;10;) (func (result s8)))
(type (;11;) (func (result u16)))
(type (;12;) (func (result s16)))
(type (;13;) (func (result u32)))
(type (;14;) (func (result s32)))
(type (;15;) (func (result u64)))
(type (;16;) (func (result s64)))
(type (;17;) (tuple s64 u8))
(type (;18;) (func (result 17)))
(export "a1" (type 0))
(export "a2" (type 1))
(export "a3" (type 2))
(export "a4" (type 3))
(export "a5" (type 4))
(export "a6" (type 5))
(export "a7" (type 6))
(export "a8" (type 7))
(export "a9" (type 8))
(export "r1" (type 9))
(export "r2" (type 10))
(export "r3" (type 11))
(export "r4" (type 12))
(export "r5" (type 13))
(export "r6" (type 14))
(export "r7" (type 15))
(export "r8" (type 16))
(export "pair-ret" (type 18))
(export "multi-ret" (type 18))
)

View File

@@ -0,0 +1,38 @@
a1: func(x: u8)
a2: func(x: s8)
a3: func(x: u16)
a4: func(x: s16)
a5: func(x: u32)
a6: func(x: s32)
a7: func(x: u64)
a8: func(x: s64)
a9: func(p1: u8, p2: s8, p3: u16, p4: s16, p5: u32, p6: s32, p7: u64, p8: s64)
r1: func() -> u8
r2: func() -> s8
r3: func() -> u16
r4: func() -> s16
r5: func() -> u32
r6: func() -> s32
r7: func() -> u64
r8: func() -> s64
pair-ret: func() -> tuple<s64, u8>
multi-ret: func() -> tuple<s64, u8>

View File

@@ -0,0 +1,92 @@
(component
(type (;0;) (list u8))
(type (;1;) (func (param "x" 0)))
(type (;2;) (list u16))
(type (;3;) (func (param "x" 2)))
(type (;4;) (list u32))
(type (;5;) (func (param "x" 4)))
(type (;6;) (list u64))
(type (;7;) (func (param "x" 6)))
(type (;8;) (list s8))
(type (;9;) (func (param "x" 8)))
(type (;10;) (list s16))
(type (;11;) (func (param "x" 10)))
(type (;12;) (list s32))
(type (;13;) (func (param "x" 12)))
(type (;14;) (list s64))
(type (;15;) (func (param "x" 14)))
(type (;16;) (list float32))
(type (;17;) (func (param "x" 16)))
(type (;18;) (list float64))
(type (;19;) (func (param "x" 18)))
(type (;20;) (func (result 0)))
(type (;21;) (func (result 2)))
(type (;22;) (func (result 4)))
(type (;23;) (func (result 6)))
(type (;24;) (func (result 8)))
(type (;25;) (func (result 10)))
(type (;26;) (func (result 12)))
(type (;27;) (func (result 14)))
(type (;28;) (func (result 16)))
(type (;29;) (func (result 18)))
(type (;30;) (tuple u8 s8))
(type (;31;) (list 30))
(type (;32;) (tuple s64 u32))
(type (;33;) (list 32))
(type (;34;) (func (param "x" 31) (result 33)))
(type (;35;) (list string))
(type (;36;) (func (param "a" 35)))
(type (;37;) (func (result 35)))
(type (;38;) (tuple u8 string))
(type (;39;) (list 38))
(type (;40;) (tuple string u8))
(type (;41;) (list 40))
(type (;42;) (func (param "x" 39) (result 41)))
(type (;43;) (func (param "x" 35) (result 35)))
(type (;44;) (record (field "a1" u32) (field "a2" u64) (field "a3" s32) (field "a4" s64) (field "b" string) (field "c" 0)))
(type (;45;) (record (field "x" string) (field "y" 44) (field "c1" u32) (field "c2" u64) (field "c3" s32) (field "c4" s64)))
(type (;46;) (list 45))
(type (;47;) (list 44))
(type (;48;) (func (param "x" 46) (result 47)))
(type (;49;) (variant (case $c0 "a" unit) (case $c1 "b" u32) (case $c2 "c" string)))
(type (;50;) (list 49))
(type (;51;) (variant (case $c0 "a" string) (case $c1 "b" unit) (case $c2 "c" u32) (case $c3 "d" 50)))
(type (;52;) (list 51))
(type (;53;) (func (param "x" 52) (result 50)))
(type (;54;) (tuple string u8 s8 u16 s16 u32 s32 u64 s64 float32 float64 char))
(type (;55;) (list 54))
(type (;56;) (func (param "a" 55) (result 55)))
(export "list-u8-param" (type 1))
(export "list-u16-param" (type 3))
(export "list-u32-param" (type 5))
(export "list-u64-param" (type 7))
(export "list-s8-param" (type 9))
(export "list-s16-param" (type 11))
(export "list-s32-param" (type 13))
(export "list-s64-param" (type 15))
(export "list-float32-param" (type 17))
(export "list-float64-param" (type 19))
(export "list-u8-ret" (type 20))
(export "list-u16-ret" (type 21))
(export "list-u32-ret" (type 22))
(export "list-u64-ret" (type 23))
(export "list-s8-ret" (type 24))
(export "list-s16-ret" (type 25))
(export "list-s32-ret" (type 26))
(export "list-s64-ret" (type 27))
(export "list-float32-ret" (type 28))
(export "list-float64-ret" (type 29))
(export "tuple-list" (type 34))
(export "string-list-arg" (type 36))
(export "string-list-ret" (type 37))
(export "tuple-string-list" (type 42))
(export "string-list" (type 43))
(export "other-record" (type 44))
(export "some-record" (type 45))
(export "record-list" (type 48))
(export "other-variant" (type 49))
(export "some-variant" (type 51))
(export "variant-list" (type 53))
(export "load-store-all-sizes" (type 55))
(export "load-store-everything" (type 56))
)

View File

@@ -0,0 +1,89 @@
record other-record {
a1: u32,
a2: u64,
a3: s32,
a4: s64,
b: string,
c: list<u8>,
}
record some-record {
x: string,
y: other-record,
c1: u32,
c2: u64,
c3: s32,
c4: s64,
}
variant other-variant {
a,
b(u32),
c(string),
}
variant some-variant {
a(string),
b,
c(u32),
d(list<other-variant>),
}
type load-store-all-sizes = list<tuple<string, u8, s8, u16, s16, u32, s32, u64, s64, float32, float64, char>>
list-u8-param: func(x: list<u8>)
list-u16-param: func(x: list<u16>)
list-u32-param: func(x: list<u32>)
list-u64-param: func(x: list<u64>)
list-s8-param: func(x: list<s8>)
list-s16-param: func(x: list<s16>)
list-s32-param: func(x: list<s32>)
list-s64-param: func(x: list<s64>)
list-float32-param: func(x: list<float32>)
list-float64-param: func(x: list<float64>)
list-u8-ret: func() -> list<u8>
list-u16-ret: func() -> list<u16>
list-u32-ret: func() -> list<u32>
list-u64-ret: func() -> list<u64>
list-s8-ret: func() -> list<s8>
list-s16-ret: func() -> list<s16>
list-s32-ret: func() -> list<s32>
list-s64-ret: func() -> list<s64>
list-float32-ret: func() -> list<float32>
list-float64-ret: func() -> list<float64>
tuple-list: func(x: list<tuple<u8, s8>>) -> list<tuple<s64, u32>>
string-list-arg: func(a: list<string>)
string-list-ret: func() -> list<string>
tuple-string-list: func(x: list<tuple<u8, string>>) -> list<tuple<string, u8>>
string-list: func(x: list<string>) -> list<string>
record-list: func(x: list<some-record>) -> list<other-record>
variant-list: func(x: list<some-variant>) -> list<other-variant>
load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes

View File

@@ -0,0 +1,37 @@
(component
(type (;0;) (tuple char u32))
(type (;1;) (func (param "x" 0)))
(type (;2;) (func (result 0)))
(type (;3;) (record))
(type (;4;) (func (param "x" 3)))
(type (;5;) (func (result 3)))
(type (;6;) (record (field "a" u32) (field "b" u32)))
(type (;7;) (func (param "x" 6)))
(type (;8;) (func (result 6)))
(type (;9;) (flags "a" "b" "c" "d" "e" "f" "g" "h" "i"))
(type (;10;) (func (param "x" 9)))
(type (;11;) (func (result 9)))
(type (;12;) (record (field "a" 6) (field "b" u32) (field "c" 3) (field "d" string) (field "e" 9)))
(type (;13;) (func (param "x" 12)))
(type (;14;) (func (result 12)))
(type (;15;) s32)
(type (;16;) (tuple 15))
(type (;17;) (func (param "e" 16) (result s32)))
(export "tuple-arg" (type 1))
(export "tuple-result" (type 2))
(export "empty" (type 3))
(export "empty-arg" (type 4))
(export "empty-result" (type 5))
(export "scalars" (type 6))
(export "scalar-arg" (type 7))
(export "scalar-result" (type 8))
(export "really-flags" (type 9))
(export "flags-arg" (type 10))
(export "flags-result" (type 11))
(export "aggregates" (type 12))
(export "aggregate-arg" (type 13))
(export "aggregate-result" (type 14))
(export "int-typedef" (type 15))
(export "tuple-typedef2" (type 16))
(export "typedef-inout" (type 17))
)

View File

@@ -0,0 +1,54 @@
record empty {
}
record scalars {
a: u32,
b: u32,
}
flags really-flags {
a,
b,
c,
d,
e,
f,
g,
h,
i,
}
record aggregates {
a: scalars,
b: u32,
c: empty,
d: string,
e: really-flags,
}
type int-typedef = s32
type tuple-typedef2 = tuple<int-typedef>
tuple-arg: func(x: tuple<char, u32>)
tuple-result: func() -> tuple<char, u32>
empty-arg: func(x: empty)
empty-result: func() -> empty
scalar-arg: func(x: scalars)
scalar-result: func() -> scalars
flags-arg: func(x: really-flags)
flags-result: func() -> really-flags
aggregate-arg: func(x: aggregates)
aggregate-result: func() -> aggregates
typedef-inout: func(e: tuple-typedef2) -> s32

View File

@@ -0,0 +1,92 @@
(component
(type (;0;) (enum "a"))
(type (;1;) (func (param "x" 0)))
(type (;2;) (func (result 0)))
(type (;3;) (union u32 float32))
(type (;4;) (func (param "x" 3)))
(type (;5;) (func (result 3)))
(type (;6;) (record))
(type (;7;) (variant (case $c0 "a" unit) (case $c1 "b" 3) (case $c2 "c" 0) (case $c3 "d" string) (case $c4 "e" 6) (case $c5 "f" unit) (case $c6 "g" u32)))
(type (;8;) (func (param "x" 7)))
(type (;9;) (func (result 7)))
(type (;10;) (func (param "x" bool)))
(type (;11;) (func (result bool)))
(type (;12;) (option bool))
(type (;13;) (tuple))
(type (;14;) (option 13))
(type (;15;) (option u32))
(type (;16;) (option 0))
(type (;17;) (option float32))
(type (;18;) (option 3))
(type (;19;) (option 12))
(type (;20;) (func (param "a" 12) (param "b" 14) (param "c" 15) (param "d" 16) (param "e" 17) (param "f" 18) (param "g" 19)))
(type (;21;) (tuple 12 14 15 16 17 18 19))
(type (;22;) (func (result 21)))
(type (;23;) (variant (case $c0 "a" s32) (case $c1 "b" float32)))
(type (;24;) (variant (case $c0 "a" float64) (case $c1 "b" float32)))
(type (;25;) (variant (case $c0 "a" float64) (case $c1 "b" u64)))
(type (;26;) (variant (case $c0 "a" u32) (case $c1 "b" s64)))
(type (;27;) (variant (case $c0 "a" float32) (case $c1 "b" s64)))
(type (;28;) (tuple float32 u32))
(type (;29;) (tuple u32 u32))
(type (;30;) (variant (case $c0 "a" 28) (case $c1 "b" 29)))
(type (;31;) (tuple 23 24 25 26 27 30))
(type (;32;) (func (param "a" 23) (param "b" 24) (param "c" 25) (param "d" 26) (param "e" 27) (param "f" 30) (result 31)))
(type (;33;) (expected unit unit))
(type (;34;) (expected unit 0))
(type (;35;) (expected 0 unit))
(type (;36;) (expected 13 13))
(type (;37;) (expected u32 7))
(type (;38;) (list u8))
(type (;39;) (expected string 38))
(type (;40;) (func (param "a" 33) (param "b" 34) (param "c" 35) (param "d" 36) (param "e" 37) (param "f" 39)))
(type (;41;) (tuple 33 34 35 36 37 39))
(type (;42;) (func (result 41)))
(type (;43;) (enum "bad1" "bad2"))
(type (;44;) (expected s32 43))
(type (;45;) (func (result 44)))
(type (;46;) (expected unit 43))
(type (;47;) (func (result 46)))
(type (;48;) (expected 43 43))
(type (;49;) (func (result 48)))
(type (;50;) (tuple s32 u32))
(type (;51;) (expected 50 43))
(type (;52;) (func (result 51)))
(type (;53;) (option s32))
(type (;54;) (func (result 53)))
(type (;55;) (option 43))
(type (;56;) (func (result 55)))
(type (;57;) (expected u32 s32))
(type (;58;) (func (result 57)))
(export "e1" (type 0))
(export "e1-arg" (type 1))
(export "e1-result" (type 2))
(export "u1" (type 3))
(export "u1-arg" (type 4))
(export "u1-result" (type 5))
(export "empty" (type 6))
(export "v1" (type 7))
(export "v1-arg" (type 8))
(export "v1-result" (type 9))
(export "bool-arg" (type 10))
(export "bool-result" (type 11))
(export "option-arg" (type 20))
(export "option-result" (type 22))
(export "casts1" (type 23))
(export "casts2" (type 24))
(export "casts3" (type 25))
(export "casts4" (type 26))
(export "casts5" (type 27))
(export "casts6" (type 30))
(export "casts" (type 32))
(export "expected-arg" (type 40))
(export "expected-result" (type 42))
(export "my-errno" (type 43))
(export "return-expected-sugar" (type 45))
(export "return-expected-sugar2" (type 47))
(export "return-expected-sugar3" (type 49))
(export "return-expected-sugar4" (type 52))
(export "return-option-sugar" (type 54))
(export "return-option-sugar2" (type 56))
(export "expected-simple" (type 58))
)

View File

@@ -0,0 +1,97 @@
enum e1 {
a,
}
union u1 {
u32,
float32,
}
record empty {
}
variant v1 {
a,
b(u1),
c(e1),
d(string),
e(empty),
f,
g(u32),
}
variant casts1 {
a(s32),
b(float32),
}
variant casts2 {
a(float64),
b(float32),
}
variant casts3 {
a(float64),
b(u64),
}
variant casts4 {
a(u32),
b(s64),
}
variant casts5 {
a(float32),
b(s64),
}
variant casts6 {
a(tuple<float32, u32>),
b(tuple<u32, u32>),
}
enum my-errno {
bad1,
bad2,
}
e1-arg: func(x: e1)
e1-result: func() -> e1
u1-arg: func(x: u1)
u1-result: func() -> u1
v1-arg: func(x: v1)
v1-result: func() -> v1
bool-arg: func(x: bool)
bool-result: func() -> bool
option-arg: func(a: option<bool>, b: option<tuple<>>, c: option<u32>, d: option<e1>, e: option<float32>, f: option<u1>, g: option<option<bool>>)
option-result: func() -> tuple<option<bool>, option<tuple<>>, option<u32>, option<e1>, option<float32>, option<u1>, option<option<bool>>>
casts: func(a: casts1, b: casts2, c: casts3, d: casts4, e: casts5, f: casts6) -> tuple<casts1, casts2, casts3, casts4, casts5, casts6>
expected-arg: func(a: expected<unit, unit>, b: expected<unit, e1>, c: expected<e1, unit>, d: expected<tuple<>, tuple<>>, e: expected<u32, v1>, f: expected<string, list<u8>>)
expected-result: func() -> tuple<expected<unit, unit>, expected<unit, e1>, expected<e1, unit>, expected<tuple<>, tuple<>>, expected<u32, v1>, expected<string, list<u8>>>
return-expected-sugar: func() -> expected<s32, my-errno>
return-expected-sugar2: func() -> expected<unit, my-errno>
return-expected-sugar3: func() -> expected<my-errno, my-errno>
return-expected-sugar4: func() -> expected<tuple<s32, u32>, my-errno>
return-option-sugar: func() -> option<s32>
return-option-sugar2: func() -> option<my-errno>
expected-simple: func() -> expected<u32, s32>

View File

@@ -0,0 +1,63 @@
use anyhow::{Context, Result};
use pretty_assertions::assert_eq;
use std::fs;
use wit_component::{decode_interface_component, InterfaceEncoder, InterfacePrinter};
use wit_parser::Interface;
/// Tests the the roundtrip encoding of individual interface files.
///
/// This test looks in the `interfaces/` directory for test cases.
///
/// Each test case is a directory containing a `<testcase>.wit` file.
///
/// The test encodes the wit file, decodes the resulting bytes, and
/// compares the generated interface definition to the original interface
/// definition.
///
/// Run the test with the environment variable `BLESS` set to update
/// the wit file based on the decoded output.
#[test]
fn roundtrip_interfaces() -> Result<()> {
for entry in fs::read_dir("tests/interfaces")? {
let path = entry?.path();
if !path.is_dir() {
continue;
}
let test_case = path.file_stem().unwrap().to_str().unwrap();
let wit_path = path.join(test_case).with_extension("wit");
let interface = Interface::parse_file(&wit_path).context("failed to parse `wit` file")?;
let encoder = InterfaceEncoder::new(&interface).validate(true);
let bytes = encoder.encode().with_context(|| {
format!(
"failed to encode a component from interface `{}` for test case `{}`",
path.display(),
test_case,
)
})?;
let interface = decode_interface_component(&bytes).context("failed to decode bytes")?;
let mut printer = InterfacePrinter::default();
let output = printer
.print(&interface)
.context("failed to print interface")?;
if std::env::var_os("BLESS").is_some() {
fs::write(&wit_path, output)?;
} else {
assert_eq!(
fs::read_to_string(&wit_path)?.replace("\r\n", "\n"),
output,
"encoding of wit file `{}` did not match the the decoded interface for test case `{}`",
wit_path.display(),
test_case,
);
}
}
Ok(())
}