Merge pull request #1439 from Hywan/feat-interface-types-new-repo-rm

feat(interface-types) Move to its own repository
This commit is contained in:
Syrus Akbary 2020-05-12 20:44:17 -07:00 committed by GitHub
commit 0575a7ed3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 3 additions and 7985 deletions

View File

@ -2,6 +2,8 @@
## **[Unreleased]**
- [#1439](https://github.com/wasmerio/wasmer/pull/1439) Move `wasmer-interface-types` into its own repository
## 0.17.0 - 2020-05-11
- [#1401](https://github.com/wasmerio/wasmer/pull/1401) Make breaking change to `RuntimeError`: `RuntimeError` is now more explicit about its possible error values allowing for better insight into why a call into Wasm failed.

View File

@ -60,7 +60,6 @@ members = [
"lib/kernel-loader",
"lib/kernel-net",
"lib/wasi-experimental-io-devices",
"lib/interface-types",
"examples/parallel",
"examples/plugin-for-example",
"examples/parallel-guest",

View File

@ -214,7 +214,6 @@ test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-ems
capi-test: test-capi
test-rest:
cargo test --release -p wasmer-interface-types
cargo test --release -p wasmer-runtime
cargo test --release -p wasmer-runtime-core
cargo test --release -p wasmer-wasi-experimental-io-devices
@ -274,7 +273,7 @@ check-nightly: check-kernel-net
# TODO: We wanted `--workspace --exclude wasmer-runtime`, but can't due
# to https://github.com/rust-lang/cargo/issues/6745 .
NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader -p wasmer-interface-types
NOT_RUNTIME_CRATES = -p wasmer-clif-backend -p wasmer-singlepass-backend -p wasmer-middleware-common -p wasmer-runtime-core -p wasmer-emscripten -p wasmer-llvm-backend -p wasmer-wasi -p wasmer-kernel-loader
RUNTIME_CHECK = cargo check --manifest-path lib/runtime/Cargo.toml --no-default-features
check: check-bench
cargo check $(NOT_RUNTIME_CRATES)

View File

@ -1,20 +0,0 @@
[package]
name = "wasmer-interface-types"
version = "0.17.0"
description = "WebAssembly Interface Types library for Wasmer"
license = "MIT"
authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
nom = "5.1"
wast = "8.0"
# `serde` is useful only to simplify the users' life. It is not
# required by WIT itself, is is used to cross the boundary between the
# host and WIT more easily, but it is not used inside Wasm.
serde = { version = "1.0", features = ["derive"], optional = true }
[features]
default = ["serde"]

View File

@ -1,94 +0,0 @@
<p align="center">
<a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
<img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/logo.png" alt="Wasmer logo">
</a>
</p>
<p align="center">
<a href="https://dev.azure.com/wasmerio/wasmer/_build/latest?definitionId=3&branchName=master">
<img src="https://img.shields.io/azure-devops/build/wasmerio/wasmer/3.svg?style=flat-square" alt="Build Status">
</a>
<a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square" alt="License">
</a>
<a href="https://spectrum.chat/wasmer">
<img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community">
</a>
<a href="https://crates.io/crates/wasmer-interface-types">
<img src="https://img.shields.io/crates/d/wasmer-interface-types.svg?style=flat-square" alt="Number of downloads from crates.io">
</a>
<a href="https://docs.rs/wasmer-interface-types">
<img src="https://docs.rs/wasmer-interface-types/badge.svg" alt="Read our API documentation">
</a>
</p>
# Wasmer Interface Types
Wasmer is a standalone JIT WebAssembly runtime, aiming to be fully
compatible with WASI, Emscripten, Rust and Go. [Learn
more](https://github.com/wasmerio/wasmer).
This crate is an implementation of [the living WebAssembly Interface
Types standard](https://github.com/WebAssembly/interface-types).
## Encoders and decoders
The `wasmer-interface-types` crate comes with an encoder and a decoder
for the WAT format, and the binary format, for the WebAssembly
Interface Types. An encoder writes an AST into another format, like
WAT or binary. A decoder reads an AST from another format, like WAT or
binary.
## Instructions
Very basically, WebAssembly Interface Types defines a [set of
instructions](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/working-notes/Instructions.md),
used by adapters to transform the data between WebAssembly core and
the outside world ([learn
mode](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md)).
Here is the instructions that are implemented by this crate:
| Instruction | WAT encoder/decoder | Binary encoder/decoder | Interpreter | Comment |
|-|-|-|-|-|
| `arg.get` | ✅ | ✅ | ✅ | |
| `call-core` | ✅ | ✅ | ✅ | |
| `s8.from_i32` | ✅ | ✅ | ✅ | |
| `s8.from_i64` | ✅ | ✅ | ✅ | |
| `s16.from_i32` | ✅ | ✅ | ✅ | |
| `s16.from_i64` | ✅ | ✅ | ✅ | |
| `s32.from_i32` | ✅ | ✅ | ✅ | |
| `s32.from_i64` | ✅ | ✅ | ✅ | |
| `s64.from_i32` | ✅ | ✅ | ✅ | |
| `s64.from_i64` | ✅ | ✅ | ✅ | |
| `i32.from_s8` | ✅ | ✅ | ✅ | |
| `i32.from_s16` | ✅ | ✅ | ✅ | |
| `i32.from_s32` | ✅ | ✅ | ✅ | |
| `i32.from_s64` | ✅ | ✅ | ✅ | |
| `i64.from_s8` | ✅ | ✅ | ✅ | |
| `i64.from_s16` | ✅ | ✅ | ✅ | |
| `i64.from_s32` | ✅ | ✅ | ✅ | |
| `i64.from_s64` | ✅ | ✅ | ✅ | |
| `u8.from_i32` | ✅ | ✅ | ✅ | |
| `u8.from_i64` | ✅ | ✅ | ✅ | |
| `u16.from_i32` | ✅ | ✅ | ✅ | |
| `u16.from_i64` | ✅ | ✅ | ✅ | |
| `u32.from_i32` | ✅ | ✅ | ✅ | |
| `u32.from_i64` | ✅ | ✅ | ✅ | |
| `u64.from_i32` | ✅ | ✅ | ✅ | |
| `u64.from_i64` | ✅ | ✅ | ✅ | |
| `i32.from_u8` | ✅ | ✅ | ✅ | |
| `i32.from_u16` | ✅ | ✅ | ✅ | |
| `i32.from_u32` | ✅ | ✅ | ✅ | |
| `i32.from_u64` | ✅ | ✅ | ✅ | |
| `i64.from_u8` | ✅ | ✅ | ✅ | |
| `i64.from_u16` | ✅ | ✅ | ✅ | |
| `i64.from_u32` | ✅ | ✅ | ✅ | |
| `i64.from_u64` | ✅ | ✅ | ✅ | |
| `string.lift_memory` | ✅ | ✅ | ✅ | `#memidx` is not supported; `#encoding` is not supported but UTF-8 is assumed |
| `string.lower_memory` | ✅ | ✅ | ✅ | `#memidx` is not supported; `#encoding` is not supported but UTF-8 is assumed |
| `string.size` | ✅ | ✅ | ✅ | `#encoding` is not supported but UTF-8 is assumed |
| `record.lift` | ✅ | ✅ | ✅ | |
| `record.lower` | ✅ | ✅ | ✅ | |
| `call-adapter` | ❌ | ❌ | ❌ | |
| `defer-call-core` | ❌ | ❌ | ❌ | |

View File

@ -1,124 +0,0 @@
//! Represents the WIT language as a tree. This is the central
//! representation of the language.
use crate::{
interpreter::Instruction,
types::{InterfaceType, RecordType},
};
use std::str;
/// Represents the kind of type.
#[derive(PartialEq, Debug)]
pub enum TypeKind {
/// A function type.
Function,
/// A record type.
Record,
}
/// Represents a type.
#[derive(PartialEq, Debug)]
pub enum Type {
/// A function type, like:
///
/// ```wasm,ignore
/// (@interface type (func (param i32 i32) (result string)))
/// ```
Function {
/// Types for the parameters (`(param …)`).
inputs: Vec<InterfaceType>,
/// Types for the results (`(result …)`).
outputs: Vec<InterfaceType>,
},
/// A record type, like:
///
/// ```wasm,ignore
/// (@interface type (record string i32))
/// ```
Record(RecordType),
}
/// Represents an imported function.
#[derive(PartialEq, Debug)]
pub struct Import<'input> {
/// The function namespace.
pub namespace: &'input str,
/// The function name.
pub name: &'input str,
/// The type signature.
pub signature_type: u32,
}
/// Represents an exported function signature.
#[derive(PartialEq, Debug)]
pub struct Export<'input> {
/// The export name.
pub name: &'input str,
/// The WIT function type being exported.
pub function_type: u32,
}
/// Represents an adapter.
#[derive(PartialEq, Debug)]
pub struct Adapter {
/// The adapter function type.
pub function_type: u32,
/// The instructions.
pub instructions: Vec<Instruction>,
}
/// Represents an implementation.
#[derive(PartialEq, Debug)]
pub struct Implementation {
/// The core function type.
pub core_function_type: u32,
/// The adapter function type.
pub adapter_function_type: u32,
}
/// Represents the kind of interface.
#[derive(PartialEq, Debug)]
pub(crate) enum InterfaceKind {
/// A type.
Type,
/// An imported function.
Import,
/// An adapter.
Adapter,
/// An exported function.
Export,
/// An implementation.
Implementation,
}
/// Represents a set of interfaces, i.e. it entirely describes a WIT
/// definition.
#[derive(PartialEq, Default, Debug)]
pub struct Interfaces<'input> {
/// All the types.
pub types: Vec<Type>,
/// All the imported functions.
pub imports: Vec<Import<'input>>,
/// All the adapters.
pub adapters: Vec<Adapter>,
/// All the exported functions.
pub exports: Vec<Export<'input>>,
/// All the implementations.
pub implementations: Vec<Implementation>,
}

View File

@ -1,990 +0,0 @@
//! Parse the WIT binary representation into an [AST](crate::ast).
use crate::{ast::*, interpreter::Instruction, types::*, vec1::Vec1};
use nom::{
error::{make_error, ErrorKind, ParseError},
Err, IResult,
};
use std::{convert::TryFrom, str};
/// Parse a type kind.
impl TryFrom<u8> for TypeKind {
type Error = &'static str;
fn try_from(code: u8) -> Result<Self, Self::Error> {
Ok(match code {
0x00 => Self::Function,
0x01 => Self::Record,
_ => return Err("Unknown type kind code."),
})
}
}
/// Parse an interface kind.
impl TryFrom<u8> for InterfaceKind {
type Error = &'static str;
fn try_from(code: u8) -> Result<Self, Self::Error> {
Ok(match code {
0x00 => Self::Type,
0x01 => Self::Import,
0x02 => Self::Adapter,
0x03 => Self::Export,
0x04 => Self::Implementation,
_ => return Err("Unknown interface kind code."),
})
}
}
/// Parse a byte.
fn byte<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u8, E> {
if input.is_empty() {
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
}
Ok((&input[1..], input[0]))
}
/// Parse an unsigned Little Endian Based (LEB) with value no larger
/// than a 64-bits number. Read
/// [LEB128](https://en.wikipedia.org/wiki/LEB128) to learn more, or
/// the Variable Length Data Section from the [DWARF 4
/// standard](http://dwarfstd.org/doc/DWARF4.pdf).
fn uleb<'input, E: ParseError<&'input [u8]>>(input: &'input [u8]) -> IResult<&'input [u8], u64, E> {
if input.is_empty() {
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
}
let (output, bytes) = match input.iter().position(|&byte| byte & 0x80 == 0) {
Some(length) if length <= 8 => (&input[length + 1..], &input[..=length]),
Some(_) => return Err(Err::Error(make_error(input, ErrorKind::TooLarge))),
None => return Err(Err::Error(make_error(input, ErrorKind::Eof))),
};
Ok((
output,
bytes
.iter()
.rev()
.fold(0, |acc, byte| (acc << 7) | u64::from(byte & 0x7f)),
))
}
/// Parse an interface type.
fn ty<'input, E: ParseError<&'input [u8]>>(
mut input: &'input [u8],
) -> IResult<&'input [u8], InterfaceType, E> {
if input.is_empty() {
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
}
consume!((input, opcode) = byte(input)?);
let ty = match opcode {
0x00 => InterfaceType::S8,
0x01 => InterfaceType::S16,
0x02 => InterfaceType::S32,
0x03 => InterfaceType::S64,
0x04 => InterfaceType::U8,
0x05 => InterfaceType::U16,
0x06 => InterfaceType::U32,
0x07 => InterfaceType::U64,
0x08 => InterfaceType::F32,
0x09 => InterfaceType::F64,
0x0a => InterfaceType::String,
0x0b => InterfaceType::Anyref,
0x0c => InterfaceType::I32,
0x0d => InterfaceType::I64,
0x0e => {
consume!((input, record_type) = record_type(input)?);
InterfaceType::Record(record_type)
}
_ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
};
Ok((input, ty))
}
/// Parse a record type.
fn record_type<'input, E: ParseError<&'input [u8]>>(
input: &'input [u8],
) -> IResult<&'input [u8], RecordType, E> {
let (output, fields) = list(input, ty)?;
Ok((
output,
RecordType {
fields: Vec1::new(fields).expect("Record must have at least one field, zero given."),
},
))
}
/// Parse a UTF-8 string.
fn string<'input, E: ParseError<&'input [u8]>>(
input: &'input [u8],
) -> IResult<&'input [u8], &'input str, E> {
if input.is_empty() {
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
}
let length = input[0] as usize;
let input = &input[1..];
if input.len() < length {
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
}
Ok((
&input[length..],
str::from_utf8(&input[..length])
.map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?,
))
}
/// Parse a list, with an item parser.
#[allow(clippy::type_complexity)]
fn list<'input, I, E: ParseError<&'input [u8]>>(
input: &'input [u8],
item_parser: fn(&'input [u8]) -> IResult<&'input [u8], I, E>,
) -> IResult<&'input [u8], Vec<I>, E> {
if input.is_empty() {
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
}
let length = input[0] as usize;
let mut input = &input[1..];
if input.len() < length {
return Err(Err::Error(make_error(input, ErrorKind::Eof)));
}
let mut items = Vec::with_capacity(length as usize);
for _ in 0..length {
consume!((input, item) = item_parser(input)?);
items.push(item);
}
Ok((input, items))
}
/// Parse an instruction with its arguments.
fn instruction<'input, E: ParseError<&'input [u8]>>(
input: &'input [u8],
) -> IResult<&'input [u8], Instruction, E> {
let (mut input, opcode) = byte(input)?;
Ok(match opcode {
0x00 => {
consume!((input, argument_0) = uleb(input)?);
(
input,
Instruction::ArgumentGet {
index: argument_0 as u32,
},
)
}
0x01 => {
consume!((input, argument_0) = uleb(input)?);
(
input,
Instruction::CallCore {
function_index: argument_0 as u32,
},
)
}
0x02 => (input, Instruction::S8FromI32),
0x03 => (input, Instruction::S8FromI64),
0x04 => (input, Instruction::S16FromI32),
0x05 => (input, Instruction::S16FromI64),
0x06 => (input, Instruction::S32FromI32),
0x07 => (input, Instruction::S32FromI64),
0x08 => (input, Instruction::S64FromI32),
0x09 => (input, Instruction::S64FromI64),
0x0a => (input, Instruction::I32FromS8),
0x0b => (input, Instruction::I32FromS16),
0x0c => (input, Instruction::I32FromS32),
0x0d => (input, Instruction::I32FromS64),
0x0e => (input, Instruction::I64FromS8),
0x0f => (input, Instruction::I64FromS16),
0x10 => (input, Instruction::I64FromS32),
0x11 => (input, Instruction::I64FromS64),
0x12 => (input, Instruction::U8FromI32),
0x13 => (input, Instruction::U8FromI64),
0x14 => (input, Instruction::U16FromI32),
0x15 => (input, Instruction::U16FromI64),
0x16 => (input, Instruction::U32FromI32),
0x17 => (input, Instruction::U32FromI64),
0x18 => (input, Instruction::U64FromI32),
0x19 => (input, Instruction::U64FromI64),
0x1a => (input, Instruction::I32FromU8),
0x1b => (input, Instruction::I32FromU16),
0x1c => (input, Instruction::I32FromU32),
0x1d => (input, Instruction::I32FromU64),
0x1e => (input, Instruction::I64FromU8),
0x1f => (input, Instruction::I64FromU16),
0x20 => (input, Instruction::I64FromU32),
0x21 => (input, Instruction::I64FromU64),
0x22 => (input, Instruction::StringLiftMemory),
0x23 => (input, Instruction::StringLowerMemory),
0x24 => (input, Instruction::StringSize),
0x25 => {
consume!((input, argument_0) = uleb(input)?);
(
input,
Instruction::RecordLift {
type_index: argument_0 as u32,
},
)
}
0x26 => {
consume!((input, argument_0) = uleb(input)?);
(
input,
Instruction::RecordLower {
type_index: argument_0 as u32,
},
)
}
_ => return Err(Err::Error(make_error(input, ErrorKind::ParseTo))),
})
}
/// Parse a list of types.
fn types<'input, E: ParseError<&'input [u8]>>(
mut input: &'input [u8],
) -> IResult<&'input [u8], Vec<Type>, E> {
consume!((input, number_of_types) = uleb(input)?);
let mut types = Vec::with_capacity(number_of_types as usize);
for _ in 0..number_of_types {
consume!((input, type_kind) = byte(input)?);
let type_kind = TypeKind::try_from(type_kind)
.map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?;
match type_kind {
TypeKind::Function => {
consume!((input, inputs) = list(input, ty)?);
consume!((input, outputs) = list(input, ty)?);
types.push(Type::Function { inputs, outputs });
}
TypeKind::Record => {
consume!((input, record_type) = record_type(input)?);
types.push(Type::Record(record_type));
}
}
}
Ok((input, types))
}
/// Parse a list of imports.
fn imports<'input, E: ParseError<&'input [u8]>>(
mut input: &'input [u8],
) -> IResult<&'input [u8], Vec<Import>, E> {
consume!((input, number_of_imports) = uleb(input)?);
let mut imports = Vec::with_capacity(number_of_imports as usize);
for _ in 0..number_of_imports {
consume!((input, namespace) = string(input)?);
consume!((input, name) = string(input)?);
consume!((input, signature_type) = uleb(input)?);
imports.push(Import {
namespace,
name,
signature_type: signature_type as u32,
});
}
Ok((input, imports))
}
/// Parse a list of adapters.
fn adapters<'input, E: ParseError<&'input [u8]>>(
mut input: &'input [u8],
) -> IResult<&'input [u8], Vec<Adapter>, E> {
consume!((input, number_of_adapters) = uleb(input)?);
let mut adapters = Vec::with_capacity(number_of_adapters as usize);
for _ in 0..number_of_adapters {
consume!((input, function_type) = uleb(input)?);
consume!((input, instructions) = list(input, instruction)?);
adapters.push(Adapter {
function_type: function_type as u32,
instructions,
});
}
Ok((input, adapters))
}
/// Parse a list of exports.
fn exports<'input, E: ParseError<&'input [u8]>>(
mut input: &'input [u8],
) -> IResult<&'input [u8], Vec<Export>, E> {
consume!((input, number_of_exports) = uleb(input)?);
let mut exports = Vec::with_capacity(number_of_exports as usize);
for _ in 0..number_of_exports {
consume!((input, name) = string(input)?);
consume!((input, function_type) = uleb(input)?);
exports.push(Export {
name,
function_type: function_type as u32,
});
}
Ok((input, exports))
}
/// Parse a list of implementations.
fn implementations<'input, E: ParseError<&'input [u8]>>(
mut input: &'input [u8],
) -> IResult<&'input [u8], Vec<Implementation>, E> {
consume!((input, number_of_implementations) = uleb(input)?);
let mut implementations = Vec::with_capacity(number_of_implementations as usize);
for _ in 0..number_of_implementations {
consume!((input, core_function_type) = uleb(input)?);
consume!((input, adapter_function_type) = uleb(input)?);
implementations.push(Implementation {
core_function_type: core_function_type as u32,
adapter_function_type: adapter_function_type as u32,
});
}
Ok((input, implementations))
}
/// Parse complete interfaces.
fn interfaces<'input, E: ParseError<&'input [u8]>>(
bytes: &'input [u8],
) -> IResult<&'input [u8], Interfaces, E> {
let mut input = bytes;
let mut all_types = vec![];
let mut all_imports = vec![];
let mut all_adapters = vec![];
let mut all_exports = vec![];
let mut all_implementations = vec![];
while !input.is_empty() {
consume!((input, interface_kind) = byte(input)?);
let interface_kind = InterfaceKind::try_from(interface_kind)
.map_err(|_| Err::Error(make_error(input, ErrorKind::ParseTo)))?;
match interface_kind {
InterfaceKind::Type => {
consume!((input, mut new_types) = types(input)?);
all_types.append(&mut new_types);
}
InterfaceKind::Import => {
consume!((input, mut new_imports) = imports(input)?);
all_imports.append(&mut new_imports);
}
InterfaceKind::Adapter => {
consume!((input, mut new_adapters) = adapters(input)?);
all_adapters.append(&mut new_adapters);
}
InterfaceKind::Export => {
consume!((input, mut new_exports) = exports(input)?);
all_exports.append(&mut new_exports);
}
InterfaceKind::Implementation => {
consume!((input, mut new_implementations) = implementations(input)?);
all_implementations.append(&mut new_implementations)
}
}
}
Ok((
input,
Interfaces {
types: all_types,
imports: all_imports,
adapters: all_adapters,
exports: all_exports,
implementations: all_implementations,
},
))
}
/// Parse a sequence of bytes, expecting it to be a valid WIT binary
/// representation, into an [`Interfaces`](crate::ast::Interfaces)
/// structure.
///
/// # Example
///
/// ```rust
/// use wasmer_interface_types::{
/// ast::{Adapter, Export, Implementation, Import, Interfaces, Type},
/// decoders::binary::parse,
/// interpreter::Instruction,
/// types::InterfaceType,
/// };
///
/// let input = &[
/// 0x00, // type section
/// 0x01, // 1 type
/// 0x00, // function type
/// 0x01, // list of 1 item
/// 0x00, // S8
/// 0x01, // list of 1 item
/// 0x01, // S16
/// //
/// 0x01, // import section
/// 0x01, // 1 import
/// 0x02, // string of 2 bytes
/// 0x61, 0x62, // "a", "b"
/// 0x01, // string of 1 byte
/// 0x63, // "c"
/// 0x00, // signature type
/// //
/// 0x02, // adapter section
/// 0x01, // 1 adapter
/// 0x00, // function type
/// 0x01, // list of 1 item
/// 0x00, 0x01, // ArgumentGet { index: 1 }
/// //
/// 0x03, // export section
/// 0x01, // 1 export
/// 0x02, // string of 2 bytes
/// 0x61, 0x62, // "a", "b"
/// 0x01, // function type
/// //
/// 0x04, // implementation section
/// 0x01, // 1 implementation
/// 0x02, // core function type
/// 0x03, // adapter function type
/// ];
/// let output = Ok((
/// &[] as &[u8],
/// Interfaces {
/// types: vec![Type::Function {
/// inputs: vec![InterfaceType::S8],
/// outputs: vec![InterfaceType::S16],
/// }],
/// imports: vec![Import {
/// namespace: "ab",
/// name: "c",
/// signature_type: 0,
/// }],
/// adapters: vec![Adapter {
/// function_type: 0,
/// instructions: vec![Instruction::ArgumentGet { index: 1 }],
/// }],
/// exports: vec![Export {
/// name: "ab",
/// function_type: 1,
/// }],
/// implementations: vec![Implementation {
/// core_function_type: 2,
/// adapter_function_type: 3,
/// }],
/// },
/// ));
///
/// assert_eq!(parse::<()>(input), output);
/// ```
pub fn parse<'input, E: ParseError<&'input [u8]>>(
bytes: &'input [u8],
) -> IResult<&'input [u8], Interfaces, E> {
interfaces(bytes)
}
#[cfg(test)]
mod tests {
use super::*;
use nom::{error, Err};
#[test]
fn test_byte() {
let input = &[0x01, 0x02, 0x03];
let output = Ok((&[0x02, 0x03][..], 0x01u8));
assert_eq!(byte::<()>(input), output);
}
#[test]
fn test_uleb_1_byte() {
let input = &[0x01, 0x02, 0x03];
let output = Ok((&[0x02, 0x03][..], 0x01u64));
assert_eq!(uleb::<()>(input), output);
}
#[test]
fn test_uleb_3_bytes() {
let input = &[0xfc, 0xff, 0x01, 0x02];
let output = Ok((&[0x02][..], 0x7ffcu64));
assert_eq!(uleb::<()>(input), output);
}
// Examples from Figure 22 of [DWARF 4
// standard](http://dwarfstd.org/doc/DWARF4.pdf).
#[test]
fn test_uleb_from_dwarf_standard() {
macro_rules! assert_uleb {
($to_parse:expr => $expected_result:expr) => {
assert_eq!(uleb::<()>($to_parse), Ok((&[][..], $expected_result)));
};
}
assert_uleb!(&[2u8] => 2u64);
assert_uleb!(&[127u8] => 127u64);
assert_uleb!(&[0x80, 1u8] => 128u64);
assert_uleb!(&[1u8 | 0x80, 1] => 129u64);
assert_uleb!(&[2u8 | 0x80, 1] => 130u64);
assert_uleb!(&[57u8 | 0x80, 100] => 12857u64);
}
#[test]
fn test_uleb_eof() {
let input = &[0x80];
assert_eq!(
uleb::<(&[u8], error::ErrorKind)>(input),
Err(Err::Error((&input[..], error::ErrorKind::Eof))),
);
}
#[test]
fn test_uleb_overflow() {
let input = &[
0x01 | 0x80,
0x02 | 0x80,
0x03 | 0x80,
0x04 | 0x80,
0x05 | 0x80,
0x06 | 0x80,
0x07 | 0x80,
0x08 | 0x80,
0x09 | 0x80,
0x0a,
];
assert_eq!(
uleb::<(&[u8], error::ErrorKind)>(input),
Err(Err::Error((&input[..], error::ErrorKind::TooLarge))),
);
}
#[test]
fn test_ty() {
let input = &[
0x0f, // list of 15 items
0x00, // S8
0x01, // S16
0x02, // S32
0x03, // S64
0x04, // U8
0x05, // U16
0x06, // U32
0x07, // U64
0x08, // F32
0x09, // F64
0x0a, // String
0x0b, // Anyref
0x0c, // I32
0x0d, // I64
0x0e, 0x01, 0x02, // Record
0x01,
];
let output = Ok((
&[0x01][..],
vec![
InterfaceType::S8,
InterfaceType::S16,
InterfaceType::S32,
InterfaceType::S64,
InterfaceType::U8,
InterfaceType::U16,
InterfaceType::U32,
InterfaceType::U64,
InterfaceType::F32,
InterfaceType::F64,
InterfaceType::String,
InterfaceType::Anyref,
InterfaceType::I32,
InterfaceType::I64,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::S32],
}),
],
));
assert_eq!(list::<_, ()>(input, ty), output);
}
#[test]
fn test_record_type() {
let input = &[
0x03, // list of 3 items
0x01, // 1 field
0x0a, // String
0x02, // 2 fields
0x0a, // String
0x0c, // I32
0x03, // 3 fields
0x0a, // String
0x0e, // Record
0x02, // 2 fields
0x0c, // I32
0x0c, // I32
0x09, // F64
0x01,
];
let output = Ok((
&[0x01][..],
vec![
RecordType {
fields: vec1![InterfaceType::String],
},
RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32],
},
RecordType {
fields: vec1![
InterfaceType::String,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I32],
}),
InterfaceType::F64,
],
},
],
));
assert_eq!(list::<_, ()>(input, record_type), output);
}
#[test]
fn test_string() {
let input = &[
0x03, // string of 3 bytes
0x61, // "a"
0x62, // "b"
0x63, // "c"
0x64, 0x65,
];
let output = Ok((&[0x64, 0x65][..], "abc"));
assert_eq!(string::<()>(input), output);
}
#[test]
fn test_list() {
let input = &[
0x02, // list of 2 items
0x01, // string of 1 byte
0x61, // "a"
0x02, // string of 2 bytes
0x62, // "b"
0x63, // "c"
0x07,
];
let output = Ok((&[0x07][..], vec!["a", "bc"]));
assert_eq!(list::<_, ()>(input, string), output);
}
#[test]
fn test_instructions() {
let input = &[
0x27, // list of 39 items
0x00, 0x01, // ArgumentGet { index: 1 }
0x01, 0x01, // CallCore { function_index: 1 }
0x02, // S8FromI32
0x03, // S8FromI64
0x04, // S16FromI32
0x05, // S16FromI64
0x06, // S32FromI32
0x07, // S32FromI64
0x08, // S64FromI32
0x09, // S64FromI64
0x0a, // I32FromS8
0x0b, // I32FromS16
0x0c, // I32FromS32
0x0d, // I32FromS64
0x0e, // I64FromS8
0x0f, // I64FromS16
0x10, // I64FromS32
0x11, // I64FromS64
0x12, // U8FromI32
0x13, // U8FromI64
0x14, // U16FromI32
0x15, // U16FromI64
0x16, // U32FromI32
0x17, // U32FromI64
0x18, // U64FromI32
0x19, // U64FromI64
0x1a, // I32FromU8
0x1b, // I32FromU16
0x1c, // I32FromU32
0x1d, // I32FromU64
0x1e, // I64FromU8
0x1f, // I64FromU16
0x20, // I64FromU32
0x21, // I64FromU64
0x22, // StringLiftMemory
0x23, // StringLowerMemory
0x24, // StringSize
0x25, 0x01, // RecordLift { type_index: 1 },
0x26, 0x01, // RecordLower { type_index: 1 },
0x0a,
];
let output = Ok((
&[0x0a][..],
vec![
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 1 },
Instruction::S8FromI32,
Instruction::S8FromI64,
Instruction::S16FromI32,
Instruction::S16FromI64,
Instruction::S32FromI32,
Instruction::S32FromI64,
Instruction::S64FromI32,
Instruction::S64FromI64,
Instruction::I32FromS8,
Instruction::I32FromS16,
Instruction::I32FromS32,
Instruction::I32FromS64,
Instruction::I64FromS8,
Instruction::I64FromS16,
Instruction::I64FromS32,
Instruction::I64FromS64,
Instruction::U8FromI32,
Instruction::U8FromI64,
Instruction::U16FromI32,
Instruction::U16FromI64,
Instruction::U32FromI32,
Instruction::U32FromI64,
Instruction::U64FromI32,
Instruction::U64FromI64,
Instruction::I32FromU8,
Instruction::I32FromU16,
Instruction::I32FromU32,
Instruction::I32FromU64,
Instruction::I64FromU8,
Instruction::I64FromU16,
Instruction::I64FromU32,
Instruction::I64FromU64,
Instruction::StringLiftMemory,
Instruction::StringLowerMemory,
Instruction::StringSize,
Instruction::RecordLift { type_index: 1 },
Instruction::RecordLower { type_index: 1 },
],
));
assert_eq!(list::<_, ()>(input, instruction), output);
}
#[test]
fn test_exports() {
let input = &[
0x02, // 2 exports
0x02, // string of 2 bytes
0x61, 0x62, // "a", "b"
0x01, // function type
0x02, // string of 2 bytes
0x63, 0x64, // "c", "d"
0x02, // function type
];
let output = Ok((
&[] as &[u8],
vec![
Export {
name: "ab",
function_type: 1,
},
Export {
name: "cd",
function_type: 2,
},
],
));
assert_eq!(exports::<()>(input), output);
}
#[test]
fn test_types() {
let input = &[
0x02, // 2 type
0x00, // function type
0x02, // list of 2 items
0x02, // S32
0x02, // S32
0x01, // list of 2 items
0x02, // S32
0x01, // record type
0x02, // list of 2 items
0x02, // S32
0x02, // S32
];
let output = Ok((
&[] as &[u8],
vec![
Type::Function {
inputs: vec![InterfaceType::S32, InterfaceType::S32],
outputs: vec![InterfaceType::S32],
},
Type::Record(RecordType {
fields: vec1![InterfaceType::S32, InterfaceType::S32],
}),
],
));
assert_eq!(types::<()>(input), output);
}
#[test]
fn test_imports() {
let input = &[
0x02, // 2 imports
0x01, // string of 1 byte
0x61, // "a"
0x01, // string of 1 byte
0x62, // "b"
0x01, // signature type
0x01, // string of 1 byte
0x63, // "c"
0x01, // string of 1 byte
0x64, // "d"
0x02, // signature type
];
let output = Ok((
&[] as &[u8],
vec![
Import {
namespace: "a",
name: "b",
signature_type: 1,
},
Import {
namespace: "c",
name: "d",
signature_type: 2,
},
],
));
assert_eq!(imports::<()>(input), output);
}
#[test]
fn test_adapters() {
let input = &[
0x01, // 1 adapters
0x00, // function type
0x01, // list of 1 item
0x00, 0x01, // ArgumentGet { index: 1 }
];
let output = Ok((
&[] as &[u8],
vec![Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 1 }],
}],
));
assert_eq!(adapters::<()>(input), output);
}
#[test]
fn test_parse() {
let input = &[
0x00, // type section
0x01, // 1 type
0x00, // function type
0x01, // list of 1 item
0x00, // S8
0x01, // list of 1 item
0x01, // S16
//
0x01, // import section
0x01, // 1 import
0x02, // string of 2 bytes
0x61, 0x62, // "a", "b"
0x01, // string of 1 byte
0x63, // "c"
0x00, // signature type
//
0x02, // adapter section
0x01, // 1 adapter
0x00, // function type
0x01, // list of 1 item
0x00, 0x01, // ArgumentGet { index: 1 }
//
0x03, // export section
0x01, // 1 export
0x02, // string of 2 bytes
0x61, 0x62, // "a", "b"
0x01, // function type
//
0x04, // implementation section
0x01, // 1 implementation
0x02, // core function type
0x03, // adapter function type
];
let output = Ok((
&[] as &[u8],
Interfaces {
types: vec![Type::Function {
inputs: vec![InterfaceType::S8],
outputs: vec![InterfaceType::S16],
}],
imports: vec![Import {
namespace: "ab",
name: "c",
signature_type: 0,
}],
adapters: vec![Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 1 }],
}],
exports: vec![Export {
name: "ab",
function_type: 1,
}],
implementations: vec![Implementation {
core_function_type: 2,
adapter_function_type: 3,
}],
},
));
assert_eq!(interfaces::<()>(input), output);
}
}

View File

@ -1,6 +0,0 @@
//! Reads the AST from a particular data representation; for instance,
//! [`decoders::binary`](binary) reads the [AST](crate::ast)
//! from a binary.
pub mod binary;
pub mod wat;

View File

@ -1,977 +0,0 @@
//! Parse the WIT textual representation into an [AST](crate::ast).
use crate::{ast::*, interpreter::Instruction, types::*, vec1::Vec1};
pub use wast::parser::ParseBuffer as Buffer;
use wast::parser::{self, Cursor, Parse, Parser, Peek, Result};
mod keyword {
pub use wast::{
custom_keyword,
kw::{anyref, export, f32, f64, func, i32, i64, import, param, result},
};
// New keywords.
custom_keyword!(implement);
custom_keyword!(r#type = "type");
custom_keyword!(record);
custom_keyword!(field);
// New types.
custom_keyword!(s8);
custom_keyword!(s16);
custom_keyword!(s32);
custom_keyword!(s64);
custom_keyword!(u8);
custom_keyword!(u16);
custom_keyword!(u32);
custom_keyword!(u64);
custom_keyword!(string);
// Instructions.
custom_keyword!(argument_get = "arg.get");
custom_keyword!(call_core = "call-core");
custom_keyword!(s8_from_i32 = "s8.from_i32");
custom_keyword!(s8_from_i64 = "s8.from_i64");
custom_keyword!(s16_from_i32 = "s16.from_i32");
custom_keyword!(s16_from_i64 = "s16.from_i64");
custom_keyword!(s32_from_i32 = "s32.from_i32");
custom_keyword!(s32_from_i64 = "s32.from_i64");
custom_keyword!(s64_from_i32 = "s64.from_i32");
custom_keyword!(s64_from_i64 = "s64.from_i64");
custom_keyword!(i32_from_s8 = "i32.from_s8");
custom_keyword!(i32_from_s16 = "i32.from_s16");
custom_keyword!(i32_from_s32 = "i32.from_s32");
custom_keyword!(i32_from_s64 = "i32.from_s64");
custom_keyword!(i64_from_s8 = "i64.from_s8");
custom_keyword!(i64_from_s16 = "i64.from_s16");
custom_keyword!(i64_from_s32 = "i64.from_s32");
custom_keyword!(i64_from_s64 = "i64.from_s64");
custom_keyword!(u8_from_i32 = "u8.from_i32");
custom_keyword!(u8_from_i64 = "u8.from_i64");
custom_keyword!(u16_from_i32 = "u16.from_i32");
custom_keyword!(u16_from_i64 = "u16.from_i64");
custom_keyword!(u32_from_i32 = "u32.from_i32");
custom_keyword!(u32_from_i64 = "u32.from_i64");
custom_keyword!(u64_from_i32 = "u64.from_i32");
custom_keyword!(u64_from_i64 = "u64.from_i64");
custom_keyword!(i32_from_u8 = "i32.from_u8");
custom_keyword!(i32_from_u16 = "i32.from_u16");
custom_keyword!(i32_from_u32 = "i32.from_u32");
custom_keyword!(i32_from_u64 = "i32.from_u64");
custom_keyword!(i64_from_u8 = "i64.from_u8");
custom_keyword!(i64_from_u16 = "i64.from_u16");
custom_keyword!(i64_from_u32 = "i64.from_u32");
custom_keyword!(i64_from_u64 = "i64.from_u64");
custom_keyword!(string_lift_memory = "string.lift_memory");
custom_keyword!(string_lower_memory = "string.lower_memory");
custom_keyword!(string_size = "string.size");
custom_keyword!(record_lift = "record.lift");
custom_keyword!(record_lower = "record.lower");
}
impl Parse<'_> for InterfaceType {
fn parse(parser: Parser<'_>) -> Result<Self> {
let mut lookahead = parser.lookahead1();
if lookahead.peek::<keyword::s8>() {
parser.parse::<keyword::s8>()?;
Ok(InterfaceType::S8)
} else if lookahead.peek::<keyword::s16>() {
parser.parse::<keyword::s16>()?;
Ok(InterfaceType::S16)
} else if lookahead.peek::<keyword::s32>() {
parser.parse::<keyword::s32>()?;
Ok(InterfaceType::S32)
} else if lookahead.peek::<keyword::s64>() {
parser.parse::<keyword::s64>()?;
Ok(InterfaceType::S64)
} else if lookahead.peek::<keyword::u8>() {
parser.parse::<keyword::u8>()?;
Ok(InterfaceType::U8)
} else if lookahead.peek::<keyword::u16>() {
parser.parse::<keyword::u16>()?;
Ok(InterfaceType::U16)
} else if lookahead.peek::<keyword::u32>() {
parser.parse::<keyword::u32>()?;
Ok(InterfaceType::U32)
} else if lookahead.peek::<keyword::u64>() {
parser.parse::<keyword::u64>()?;
Ok(InterfaceType::U64)
} else if lookahead.peek::<keyword::f32>() {
parser.parse::<keyword::f32>()?;
Ok(InterfaceType::F32)
} else if lookahead.peek::<keyword::f64>() {
parser.parse::<keyword::f64>()?;
Ok(InterfaceType::F64)
} else if lookahead.peek::<keyword::string>() {
parser.parse::<keyword::string>()?;
Ok(InterfaceType::String)
} else if lookahead.peek::<keyword::anyref>() {
parser.parse::<keyword::anyref>()?;
Ok(InterfaceType::Anyref)
} else if lookahead.peek::<keyword::i32>() {
parser.parse::<keyword::i32>()?;
Ok(InterfaceType::I32)
} else if lookahead.peek::<keyword::i64>() {
parser.parse::<keyword::i64>()?;
Ok(InterfaceType::I64)
} else if lookahead.peek::<keyword::record>() {
Ok(InterfaceType::Record(parser.parse()?))
} else {
Err(lookahead.error())
}
}
}
impl Parse<'_> for RecordType {
fn parse(parser: Parser<'_>) -> Result<Self> {
parser.parse::<keyword::record>()?;
let mut fields = vec![];
while !parser.is_empty() {
fields.push(parser.parens(|parser| {
parser.parse::<keyword::field>()?;
parser.parse()
})?);
}
Ok(RecordType {
fields: Vec1::new(fields).expect("Record must have at least one field, zero given."),
})
}
}
impl<'a> Parse<'a> for Instruction {
#[allow(clippy::cognitive_complexity)]
fn parse(parser: Parser<'a>) -> Result<Self> {
let mut lookahead = parser.lookahead1();
if lookahead.peek::<keyword::argument_get>() {
parser.parse::<keyword::argument_get>()?;
Ok(Instruction::ArgumentGet {
index: parser.parse()?,
})
} else if lookahead.peek::<keyword::call_core>() {
parser.parse::<keyword::call_core>()?;
Ok(Instruction::CallCore {
function_index: parser.parse::<u32>()?,
})
} else if lookahead.peek::<keyword::s8_from_i32>() {
parser.parse::<keyword::s8_from_i32>()?;
Ok(Instruction::S8FromI32)
} else if lookahead.peek::<keyword::s8_from_i64>() {
parser.parse::<keyword::s8_from_i64>()?;
Ok(Instruction::S8FromI64)
} else if lookahead.peek::<keyword::s16_from_i32>() {
parser.parse::<keyword::s16_from_i32>()?;
Ok(Instruction::S16FromI32)
} else if lookahead.peek::<keyword::s16_from_i64>() {
parser.parse::<keyword::s16_from_i64>()?;
Ok(Instruction::S16FromI64)
} else if lookahead.peek::<keyword::s32_from_i32>() {
parser.parse::<keyword::s32_from_i32>()?;
Ok(Instruction::S32FromI32)
} else if lookahead.peek::<keyword::s32_from_i64>() {
parser.parse::<keyword::s32_from_i64>()?;
Ok(Instruction::S32FromI64)
} else if lookahead.peek::<keyword::s64_from_i32>() {
parser.parse::<keyword::s64_from_i32>()?;
Ok(Instruction::S64FromI32)
} else if lookahead.peek::<keyword::s64_from_i64>() {
parser.parse::<keyword::s64_from_i64>()?;
Ok(Instruction::S64FromI64)
} else if lookahead.peek::<keyword::i32_from_s8>() {
parser.parse::<keyword::i32_from_s8>()?;
Ok(Instruction::I32FromS8)
} else if lookahead.peek::<keyword::i32_from_s16>() {
parser.parse::<keyword::i32_from_s16>()?;
Ok(Instruction::I32FromS16)
} else if lookahead.peek::<keyword::i32_from_s32>() {
parser.parse::<keyword::i32_from_s32>()?;
Ok(Instruction::I32FromS32)
} else if lookahead.peek::<keyword::i32_from_s64>() {
parser.parse::<keyword::i32_from_s64>()?;
Ok(Instruction::I32FromS64)
} else if lookahead.peek::<keyword::i64_from_s8>() {
parser.parse::<keyword::i64_from_s8>()?;
Ok(Instruction::I64FromS8)
} else if lookahead.peek::<keyword::i64_from_s16>() {
parser.parse::<keyword::i64_from_s16>()?;
Ok(Instruction::I64FromS16)
} else if lookahead.peek::<keyword::i64_from_s32>() {
parser.parse::<keyword::i64_from_s32>()?;
Ok(Instruction::I64FromS32)
} else if lookahead.peek::<keyword::i64_from_s64>() {
parser.parse::<keyword::i64_from_s64>()?;
Ok(Instruction::I64FromS64)
} else if lookahead.peek::<keyword::u8_from_i32>() {
parser.parse::<keyword::u8_from_i32>()?;
Ok(Instruction::U8FromI32)
} else if lookahead.peek::<keyword::u8_from_i64>() {
parser.parse::<keyword::u8_from_i64>()?;
Ok(Instruction::U8FromI64)
} else if lookahead.peek::<keyword::u16_from_i32>() {
parser.parse::<keyword::u16_from_i32>()?;
Ok(Instruction::U16FromI32)
} else if lookahead.peek::<keyword::u16_from_i64>() {
parser.parse::<keyword::u16_from_i64>()?;
Ok(Instruction::U16FromI64)
} else if lookahead.peek::<keyword::u32_from_i32>() {
parser.parse::<keyword::u32_from_i32>()?;
Ok(Instruction::U32FromI32)
} else if lookahead.peek::<keyword::u32_from_i64>() {
parser.parse::<keyword::u32_from_i64>()?;
Ok(Instruction::U32FromI64)
} else if lookahead.peek::<keyword::u64_from_i32>() {
parser.parse::<keyword::u64_from_i32>()?;
Ok(Instruction::U64FromI32)
} else if lookahead.peek::<keyword::u64_from_i64>() {
parser.parse::<keyword::u64_from_i64>()?;
Ok(Instruction::U64FromI64)
} else if lookahead.peek::<keyword::i32_from_u8>() {
parser.parse::<keyword::i32_from_u8>()?;
Ok(Instruction::I32FromU8)
} else if lookahead.peek::<keyword::i32_from_u16>() {
parser.parse::<keyword::i32_from_u16>()?;
Ok(Instruction::I32FromU16)
} else if lookahead.peek::<keyword::i32_from_u32>() {
parser.parse::<keyword::i32_from_u32>()?;
Ok(Instruction::I32FromU32)
} else if lookahead.peek::<keyword::i32_from_u64>() {
parser.parse::<keyword::i32_from_u64>()?;
Ok(Instruction::I32FromU64)
} else if lookahead.peek::<keyword::i64_from_u8>() {
parser.parse::<keyword::i64_from_u8>()?;
Ok(Instruction::I64FromU8)
} else if lookahead.peek::<keyword::i64_from_u16>() {
parser.parse::<keyword::i64_from_u16>()?;
Ok(Instruction::I64FromU16)
} else if lookahead.peek::<keyword::i64_from_u32>() {
parser.parse::<keyword::i64_from_u32>()?;
Ok(Instruction::I64FromU32)
} else if lookahead.peek::<keyword::i64_from_u64>() {
parser.parse::<keyword::i64_from_u64>()?;
Ok(Instruction::I64FromU64)
} else if lookahead.peek::<keyword::string_lift_memory>() {
parser.parse::<keyword::string_lift_memory>()?;
Ok(Instruction::StringLiftMemory)
} else if lookahead.peek::<keyword::string_lower_memory>() {
parser.parse::<keyword::string_lower_memory>()?;
Ok(Instruction::StringLowerMemory)
} else if lookahead.peek::<keyword::string_size>() {
parser.parse::<keyword::string_size>()?;
Ok(Instruction::StringSize)
} else if lookahead.peek::<keyword::record_lift>() {
parser.parse::<keyword::record_lift>()?;
Ok(Instruction::RecordLift {
type_index: parser.parse()?,
})
} else if lookahead.peek::<keyword::record_lower>() {
parser.parse::<keyword::record_lower>()?;
Ok(Instruction::RecordLower {
type_index: parser.parse()?,
})
} else {
Err(lookahead.error())
}
}
}
struct AtInterface;
impl Peek for AtInterface {
fn peek(cursor: Cursor<'_>) -> bool {
cursor.reserved().map(|(string, _)| string) == Some("@interface")
}
fn display() -> &'static str {
"`@interface`"
}
}
impl Parse<'_> for AtInterface {
fn parse(parser: Parser<'_>) -> Result<Self> {
parser.step(|cursor| {
if let Some(("@interface", rest)) = cursor.reserved() {
return Ok((AtInterface, rest));
}
Err(cursor.error("expected `@interface`"))
})
}
}
#[derive(PartialEq, Debug)]
enum FunctionType {
Input(Vec<InterfaceType>),
Output(Vec<InterfaceType>),
}
impl Parse<'_> for FunctionType {
fn parse(parser: Parser<'_>) -> Result<Self> {
parser.parens(|parser| {
let mut lookahead = parser.lookahead1();
if lookahead.peek::<keyword::param>() {
parser.parse::<keyword::param>()?;
let mut inputs = vec![];
while !parser.is_empty() {
inputs.push(parser.parse()?);
}
Ok(FunctionType::Input(inputs))
} else if lookahead.peek::<keyword::result>() {
parser.parse::<keyword::result>()?;
let mut outputs = vec![];
while !parser.is_empty() {
outputs.push(parser.parse()?);
}
Ok(FunctionType::Output(outputs))
} else {
Err(lookahead.error())
}
})
}
}
#[derive(PartialEq, Debug)]
enum Interface<'a> {
Type(Type),
Import(Import<'a>),
Adapter(Adapter),
Export(Export<'a>),
Implementation(Implementation),
}
impl<'a> Parse<'a> for Interface<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parens(|parser| {
let mut lookahead = parser.lookahead1();
if lookahead.peek::<AtInterface>() {
parser.parse::<AtInterface>()?;
let mut lookahead = parser.lookahead1();
if lookahead.peek::<keyword::r#type>() {
Ok(Interface::Type(parser.parse()?))
} else if lookahead.peek::<keyword::import>() {
Ok(Interface::Import(parser.parse()?))
} else if lookahead.peek::<keyword::func>() {
Ok(Interface::Adapter(parser.parse()?))
} else if lookahead.peek::<keyword::export>() {
Ok(Interface::Export(parser.parse()?))
} else if lookahead.peek::<keyword::implement>() {
Ok(Interface::Implementation(parser.parse()?))
} else {
Err(lookahead.error())
}
} else {
Err(lookahead.error())
}
})
}
}
impl<'a> Parse<'a> for Type {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<keyword::r#type>()?;
let ty = parser.parens(|parser| {
let mut lookahead = parser.lookahead1();
if lookahead.peek::<keyword::func>() {
parser.parse::<keyword::func>()?;
let mut input_types = vec![];
let mut output_types = vec![];
while !parser.is_empty() {
let function_type = parser.parse::<FunctionType>()?;
match function_type {
FunctionType::Input(mut inputs) => input_types.append(&mut inputs),
FunctionType::Output(mut outputs) => output_types.append(&mut outputs),
}
}
Ok(Type::Function {
inputs: input_types,
outputs: output_types,
})
} else if lookahead.peek::<keyword::record>() {
Ok(Type::Record(parser.parse()?))
} else {
Err(lookahead.error())
}
})?;
Ok(ty)
}
}
impl<'a> Parse<'a> for Import<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<keyword::import>()?;
let namespace = parser.parse()?;
let name = parser.parse()?;
let signature_type = parser.parens(|parser| {
parser.parse::<keyword::func>()?;
parser.parens(|parser| {
parser.parse::<keyword::r#type>()?;
parser.parse()
})
})?;
Ok(Import {
namespace,
name,
signature_type,
})
}
}
impl<'a> Parse<'a> for Export<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<keyword::export>()?;
let name = parser.parse()?;
let function_type = parser.parens(|parser| {
parser.parse::<keyword::func>()?;
parser.parse()
})?;
Ok(Export {
name,
function_type,
})
}
}
impl<'a> Parse<'a> for Implementation {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<keyword::implement>()?;
let core_function_type = parser.parens(|parser| {
parser.parse::<keyword::func>()?;
parser.parse()
})?;
let adapter_function_type = parser.parens(|parser| {
parser.parse::<keyword::func>()?;
parser.parse()
})?;
Ok(Implementation {
core_function_type,
adapter_function_type,
})
}
}
impl<'a> Parse<'a> for Adapter {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<keyword::func>()?;
let function_type = parser.parens(|parser| {
parser.parse::<keyword::r#type>()?;
parser.parse()
})?;
let mut instructions = vec![];
while !parser.is_empty() {
instructions.push(parser.parse()?);
}
Ok(Adapter {
function_type,
instructions,
})
}
}
impl<'a> Parse<'a> for Interfaces<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let mut interfaces: Interfaces = Default::default();
while !parser.is_empty() {
let interface = parser.parse::<Interface>()?;
match interface {
Interface::Type(ty) => interfaces.types.push(ty),
Interface::Import(import) => interfaces.imports.push(import),
Interface::Adapter(adapter) => interfaces.adapters.push(adapter),
Interface::Export(export) => interfaces.exports.push(export),
Interface::Implementation(implementation) => {
interfaces.implementations.push(implementation)
}
}
}
Ok(interfaces)
}
}
/// Parse a WIT definition in its textual format, and produces an
/// [AST](crate::ast) with the [`Interfaces`](crate::ast::Interfaces)
/// structure upon succesful.
///
/// # Examples
///
/// ```rust
/// use wasmer_interface_types::{
/// ast::{Adapter, Export, Implementation, Import, Interfaces, Type},
/// decoders::wat::{parse, Buffer},
/// interpreter::Instruction,
/// types::InterfaceType,
/// };
///
/// let input = Buffer::new(
/// r#"(@interface type (func (param i32) (result s8)))
///
/// (@interface import "ns" "foo" (func (type 0)))
///
/// (@interface func (type 0) arg.get 42)
///
/// (@interface export "bar" (func 0))
///
/// (@interface implement (func 0) (func 1))"#,
/// )
/// .unwrap();
/// let output = Interfaces {
/// types: vec![Type::Function {
/// inputs: vec![InterfaceType::I32],
/// outputs: vec![InterfaceType::S8],
/// }],
/// imports: vec![Import {
/// namespace: "ns",
/// name: "foo",
/// signature_type: 0,
/// }],
/// adapters: vec![Adapter {
/// function_type: 0,
/// instructions: vec![Instruction::ArgumentGet { index: 42 }],
/// }],
/// exports: vec![Export {
/// name: "bar",
/// function_type: 0,
/// }],
/// implementations: vec![Implementation {
/// core_function_type: 0,
/// adapter_function_type: 1,
/// }],
/// };
///
/// assert_eq!(parse(&input).unwrap(), output);
/// ```
pub fn parse<'input>(input: &'input Buffer) -> Result<Interfaces<'input>> {
parser::parse::<Interfaces>(&input)
}
#[cfg(test)]
mod tests {
use super::*;
use wast::parser;
fn buffer(input: &str) -> Buffer {
Buffer::new(input).expect("Failed to build the parser buffer.")
}
#[test]
fn test_interface_type() {
let inputs = vec![
"s8",
"s16",
"s32",
"s64",
"u8",
"u16",
"u32",
"u64",
"f32",
"f64",
"string",
"anyref",
"i32",
"i64",
"record (field string)",
];
let outputs = vec![
InterfaceType::S8,
InterfaceType::S16,
InterfaceType::S32,
InterfaceType::S64,
InterfaceType::U8,
InterfaceType::U16,
InterfaceType::U32,
InterfaceType::U64,
InterfaceType::F32,
InterfaceType::F64,
InterfaceType::String,
InterfaceType::Anyref,
InterfaceType::I32,
InterfaceType::I64,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::String],
}),
];
assert_eq!(inputs.len(), outputs.len());
for (input, output) in inputs.iter().zip(outputs.iter()) {
assert_eq!(
&parser::parse::<InterfaceType>(&buffer(input)).unwrap(),
output
);
}
}
#[test]
fn test_record_type() {
let inputs = vec![
"record (field string)",
"record (field string) (field i32)",
"record (field string) (field record (field i32) (field i32)) (field f64)",
];
let outputs = vec![
RecordType {
fields: vec1![InterfaceType::String],
},
RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32],
},
RecordType {
fields: vec1![
InterfaceType::String,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I32],
}),
InterfaceType::F64,
],
},
];
assert_eq!(inputs.len(), outputs.len());
for (input, output) in inputs.iter().zip(outputs.iter()) {
assert_eq!(
&parser::parse::<RecordType>(&buffer(input)).unwrap(),
output
);
}
}
#[test]
fn test_instructions() {
let inputs = vec![
"arg.get 7",
"call-core 7",
"s8.from_i32",
"s8.from_i64",
"s16.from_i32",
"s16.from_i64",
"s32.from_i32",
"s32.from_i64",
"s64.from_i32",
"s64.from_i64",
"i32.from_s8",
"i32.from_s16",
"i32.from_s32",
"i32.from_s64",
"i64.from_s8",
"i64.from_s16",
"i64.from_s32",
"i64.from_s64",
"u8.from_i32",
"u8.from_i64",
"u16.from_i32",
"u16.from_i64",
"u32.from_i32",
"u32.from_i64",
"u64.from_i32",
"u64.from_i64",
"i32.from_u8",
"i32.from_u16",
"i32.from_u32",
"i32.from_u64",
"i64.from_u8",
"i64.from_u16",
"i64.from_u32",
"i64.from_u64",
"string.lift_memory",
"string.lower_memory",
"string.size",
"record.lift 42",
"record.lower 42",
];
let outputs = vec![
Instruction::ArgumentGet { index: 7 },
Instruction::CallCore { function_index: 7 },
Instruction::S8FromI32,
Instruction::S8FromI64,
Instruction::S16FromI32,
Instruction::S16FromI64,
Instruction::S32FromI32,
Instruction::S32FromI64,
Instruction::S64FromI32,
Instruction::S64FromI64,
Instruction::I32FromS8,
Instruction::I32FromS16,
Instruction::I32FromS32,
Instruction::I32FromS64,
Instruction::I64FromS8,
Instruction::I64FromS16,
Instruction::I64FromS32,
Instruction::I64FromS64,
Instruction::U8FromI32,
Instruction::U8FromI64,
Instruction::U16FromI32,
Instruction::U16FromI64,
Instruction::U32FromI32,
Instruction::U32FromI64,
Instruction::U64FromI32,
Instruction::U64FromI64,
Instruction::I32FromU8,
Instruction::I32FromU16,
Instruction::I32FromU32,
Instruction::I32FromU64,
Instruction::I64FromU8,
Instruction::I64FromU16,
Instruction::I64FromU32,
Instruction::I64FromU64,
Instruction::StringLiftMemory,
Instruction::StringLowerMemory,
Instruction::StringSize,
Instruction::RecordLift { type_index: 42 },
Instruction::RecordLower { type_index: 42 },
];
assert_eq!(inputs.len(), outputs.len());
for (input, output) in inputs.iter().zip(outputs.iter()) {
assert_eq!(
&parser::parse::<Instruction>(&buffer(input)).unwrap(),
output
);
}
}
#[test]
fn test_param_empty() {
let input = buffer("(param)");
let output = FunctionType::Input(vec![]);
assert_eq!(parser::parse::<FunctionType>(&input).unwrap(), output);
}
#[test]
fn test_param() {
let input = buffer("(param i32 string)");
let output = FunctionType::Input(vec![InterfaceType::I32, InterfaceType::String]);
assert_eq!(parser::parse::<FunctionType>(&input).unwrap(), output);
}
#[test]
fn test_result_empty() {
let input = buffer("(result)");
let output = FunctionType::Output(vec![]);
assert_eq!(parser::parse::<FunctionType>(&input).unwrap(), output);
}
#[test]
fn test_result() {
let input = buffer("(result i32 string)");
let output = FunctionType::Output(vec![InterfaceType::I32, InterfaceType::String]);
assert_eq!(parser::parse::<FunctionType>(&input).unwrap(), output);
}
#[test]
fn test_type_function() {
let input = buffer(r#"(@interface type (func (param i32 i32) (result i32)))"#);
let output = Interface::Type(Type::Function {
inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::I32],
});
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
}
#[test]
fn test_type_record() {
let input = buffer(r#"(@interface type (record (field string) (field i32)))"#);
let output = Interface::Type(Type::Record(RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32],
}));
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
}
#[test]
fn test_export() {
let input = buffer(r#"(@interface export "foo" (func 0))"#);
let output = Interface::Export(Export {
name: "foo",
function_type: 0,
});
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
}
#[test]
fn test_export_escaped_name() {
let input = buffer(r#"(@interface export "fo\"o" (func 0))"#);
let output = Interface::Export(Export {
name: r#"fo"o"#,
function_type: 0,
});
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
}
#[test]
fn test_import() {
let input = buffer(r#"(@interface import "ns" "foo" (func (type 0)))"#);
let output = Interface::Import(Import {
namespace: "ns",
name: "foo",
signature_type: 0,
});
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
}
#[test]
fn test_adapter() {
let input = buffer(r#"(@interface func (type 0) arg.get 42)"#);
let output = Interface::Adapter(Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 42 }],
});
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
}
#[test]
fn test_implementation() {
let input = buffer(r#"(@interface implement (func 0) (func 1))"#);
let output = Interface::Implementation(Implementation {
core_function_type: 0,
adapter_function_type: 1,
});
assert_eq!(parser::parse::<Interface>(&input).unwrap(), output);
}
#[test]
fn test_interfaces() {
let input = buffer(
r#"(@interface type (func (param i32) (result s8)))
(@interface import "ns" "foo" (func (type 0)))
(@interface func (type 0) arg.get 42)
(@interface export "bar" (func 0))
(@interface implement (func 0) (func 1))"#,
);
let output = Interfaces {
types: vec![Type::Function {
inputs: vec![InterfaceType::I32],
outputs: vec![InterfaceType::S8],
}],
imports: vec![Import {
namespace: "ns",
name: "foo",
signature_type: 0,
}],
adapters: vec![Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 42 }],
}],
exports: vec![Export {
name: "bar",
function_type: 0,
}],
implementations: vec![Implementation {
core_function_type: 0,
adapter_function_type: 1,
}],
};
assert_eq!(parser::parse::<Interfaces>(&input).unwrap(), output);
}
}

View File

@ -1,737 +0,0 @@
//! Writes the AST into bytes representing WIT with its binary format.
use crate::{ast::*, interpreter::Instruction, types::*};
use std::io::{self, Write};
/// A trait for converting a value to bytes.
pub trait ToBytes<W>
where
W: Write,
{
/// Converts the given value into `&[u8]` in the given `writer`.
fn to_bytes(&self, writer: &mut W) -> io::Result<()>;
}
/// Encode a `u8` into a byte (well, it's already a byte!).
impl<W> ToBytes<W> for u8
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
writer.write_all(&[*self])
}
}
/// Encode a `u64` into bytes with a LEB128 representation.
///
/// Decoder is `decoders::binary::uleb`.
impl<W> ToBytes<W> for u64
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
let mut value = *self;
// Code adapted from the Rust' `serialize` library.
loop {
if value < 0x80 {
writer.write_all(&[value as u8])?;
break;
}
writer.write_all(&[((value & 0x7f) | 0x80) as u8])?;
value >>= 7;
}
Ok(())
}
}
/// Encode a `str` into bytes.
///
/// Decoder is `decoders::binary::string`.
impl<W> ToBytes<W> for &str
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
// Size first.
writer.write_all(&[self.len() as u8])?;
// Then the string.
writer.write_all(self.as_bytes())?;
Ok(())
}
}
/// Encode a vector into bytes.
///
/// Decoder is `decoders::binary::list`.
impl<W, I> ToBytes<W> for Vec<I>
where
W: Write,
I: ToBytes<W>,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
// Size first.
(self.len() as u64).to_bytes(writer)?;
// Then the items.
for item in self {
item.to_bytes(writer)?;
}
Ok(())
}
}
/// Encode an `InterfaceType` into bytes.
impl<W> ToBytes<W> for InterfaceType
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
InterfaceType::S8 => 0x00_u8.to_bytes(writer),
InterfaceType::S16 => 0x01_u8.to_bytes(writer),
InterfaceType::S32 => 0x02_u8.to_bytes(writer),
InterfaceType::S64 => 0x03_u8.to_bytes(writer),
InterfaceType::U8 => 0x04_u8.to_bytes(writer),
InterfaceType::U16 => 0x05_u8.to_bytes(writer),
InterfaceType::U32 => 0x06_u8.to_bytes(writer),
InterfaceType::U64 => 0x07_u8.to_bytes(writer),
InterfaceType::F32 => 0x08_u8.to_bytes(writer),
InterfaceType::F64 => 0x09_u8.to_bytes(writer),
InterfaceType::String => 0x0a_u8.to_bytes(writer),
InterfaceType::Anyref => 0x0b_u8.to_bytes(writer),
InterfaceType::I32 => 0x0c_u8.to_bytes(writer),
InterfaceType::I64 => 0x0d_u8.to_bytes(writer),
InterfaceType::Record(record_type) => {
0x0e_u8.to_bytes(writer)?;
record_type.to_bytes(writer)
}
}
}
}
/// Encode a `RecordType` into bytes.
impl<W> ToBytes<W> for RecordType
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
self.fields.to_bytes(writer)
}
}
/// Encode a `TypeKind` into bytes.
impl<W> ToBytes<W> for TypeKind
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
TypeKind::Function => 0x00_u8.to_bytes(writer),
TypeKind::Record => 0x01_u8.to_bytes(writer),
}
}
}
/// Encode an `InterfaceKind` into bytes.
impl<W> ToBytes<W> for InterfaceKind
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
Self::Type => 0x00_u8.to_bytes(writer),
Self::Import => 0x01_u8.to_bytes(writer),
Self::Adapter => 0x02_u8.to_bytes(writer),
Self::Export => 0x03_u8.to_bytes(writer),
Self::Implementation => 0x04_u8.to_bytes(writer),
}
}
}
/// Encode a `Type` into bytes.
///
/// Decoder is in `decoders::binary::types`.
impl<W> ToBytes<W> for Type
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
Type::Function { inputs, outputs } => {
TypeKind::Function.to_bytes(writer)?;
inputs.to_bytes(writer)?;
outputs.to_bytes(writer)?;
}
Type::Record(record_type) => {
TypeKind::Record.to_bytes(writer)?;
record_type.to_bytes(writer)?;
}
}
Ok(())
}
}
/// Encode an `Import` into bytes.
///
/// Decoder is in `decoders::binary::imports`.
impl<W> ToBytes<W> for Import<'_>
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
self.namespace.to_bytes(writer)?;
self.name.to_bytes(writer)?;
(self.signature_type as u64).to_bytes(writer)?;
Ok(())
}
}
/// Encode an `Adapter` into bytes.
///
/// Decoder is in `decoders::binary::adapters`.
impl<W> ToBytes<W> for Adapter
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
(self.function_type as u64).to_bytes(writer)?;
self.instructions.to_bytes(writer)?;
Ok(())
}
}
/// Encode an `Export` into bytes.
///
/// Decoder is in `decoders::binary::exports`.
impl<W> ToBytes<W> for Export<'_>
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
self.name.to_bytes(writer)?;
(self.function_type as u64).to_bytes(writer)?;
Ok(())
}
}
/// Encode an `Implementation` into bytes.
///
/// Decoder is in `decoders::binary::implementations`.
impl<W> ToBytes<W> for Implementation
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
(self.core_function_type as u64).to_bytes(writer)?;
(self.adapter_function_type as u64).to_bytes(writer)?;
Ok(())
}
}
/// Encode an `Interfaces` into bytes.
///
/// Decoder is `decoders::binary::parse`.
impl<W> ToBytes<W> for Interfaces<'_>
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
if !self.types.is_empty() {
InterfaceKind::Type.to_bytes(writer)?;
self.types.to_bytes(writer)?;
}
if !self.imports.is_empty() {
InterfaceKind::Import.to_bytes(writer)?;
self.imports.to_bytes(writer)?;
}
if !self.adapters.is_empty() {
InterfaceKind::Adapter.to_bytes(writer)?;
self.adapters.to_bytes(writer)?;
}
if !self.exports.is_empty() {
InterfaceKind::Export.to_bytes(writer)?;
self.exports.to_bytes(writer)?;
}
if !self.implementations.is_empty() {
InterfaceKind::Implementation.to_bytes(writer)?;
self.implementations.to_bytes(writer)?;
}
Ok(())
}
}
/// Encode an `Instruction` into bytes.
///
/// Decoder is `decoders::binary::instruction`.
impl<W> ToBytes<W> for Instruction
where
W: Write,
{
fn to_bytes(&self, writer: &mut W) -> io::Result<()> {
match self {
Instruction::ArgumentGet { index } => {
0x00_u8.to_bytes(writer)?;
(*index as u64).to_bytes(writer)?;
}
Instruction::CallCore { function_index } => {
0x01_u8.to_bytes(writer)?;
(*function_index as u64).to_bytes(writer)?;
}
Instruction::S8FromI32 => 0x02_u8.to_bytes(writer)?,
Instruction::S8FromI64 => 0x03_u8.to_bytes(writer)?,
Instruction::S16FromI32 => 0x04_u8.to_bytes(writer)?,
Instruction::S16FromI64 => 0x05_u8.to_bytes(writer)?,
Instruction::S32FromI32 => 0x06_u8.to_bytes(writer)?,
Instruction::S32FromI64 => 0x07_u8.to_bytes(writer)?,
Instruction::S64FromI32 => 0x08_u8.to_bytes(writer)?,
Instruction::S64FromI64 => 0x09_u8.to_bytes(writer)?,
Instruction::I32FromS8 => 0x0a_u8.to_bytes(writer)?,
Instruction::I32FromS16 => 0x0b_u8.to_bytes(writer)?,
Instruction::I32FromS32 => 0x0c_u8.to_bytes(writer)?,
Instruction::I32FromS64 => 0x0d_u8.to_bytes(writer)?,
Instruction::I64FromS8 => 0x0e_u8.to_bytes(writer)?,
Instruction::I64FromS16 => 0x0f_u8.to_bytes(writer)?,
Instruction::I64FromS32 => 0x10_u8.to_bytes(writer)?,
Instruction::I64FromS64 => 0x11_u8.to_bytes(writer)?,
Instruction::U8FromI32 => 0x12_u8.to_bytes(writer)?,
Instruction::U8FromI64 => 0x13_u8.to_bytes(writer)?,
Instruction::U16FromI32 => 0x14_u8.to_bytes(writer)?,
Instruction::U16FromI64 => 0x15_u8.to_bytes(writer)?,
Instruction::U32FromI32 => 0x16_u8.to_bytes(writer)?,
Instruction::U32FromI64 => 0x17_u8.to_bytes(writer)?,
Instruction::U64FromI32 => 0x18_u8.to_bytes(writer)?,
Instruction::U64FromI64 => 0x19_u8.to_bytes(writer)?,
Instruction::I32FromU8 => 0x1a_u8.to_bytes(writer)?,
Instruction::I32FromU16 => 0x1b_u8.to_bytes(writer)?,
Instruction::I32FromU32 => 0x1c_u8.to_bytes(writer)?,
Instruction::I32FromU64 => 0x1d_u8.to_bytes(writer)?,
Instruction::I64FromU8 => 0x1e_u8.to_bytes(writer)?,
Instruction::I64FromU16 => 0x1f_u8.to_bytes(writer)?,
Instruction::I64FromU32 => 0x20_u8.to_bytes(writer)?,
Instruction::I64FromU64 => 0x21_u8.to_bytes(writer)?,
Instruction::StringLiftMemory => 0x22_u8.to_bytes(writer)?,
Instruction::StringLowerMemory => 0x23_u8.to_bytes(writer)?,
Instruction::StringSize => 0x24_u8.to_bytes(writer)?,
Instruction::RecordLift { type_index } => {
0x25_u8.to_bytes(writer)?;
(*type_index as u64).to_bytes(writer)?
}
Instruction::RecordLower { type_index } => {
0x26_u8.to_bytes(writer)?;
(*type_index as u64).to_bytes(writer)?
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_to_bytes {
($expr:expr, $expected_output:expr) => {{
let mut output = vec![];
$expr.to_bytes(&mut output).expect(concat!(
"Unable to encode the expression `",
stringify!($expr),
"` to bytes."
));
assert_eq!(output.as_slice(), &$expected_output[..]);
}};
}
#[test]
fn test_u8() {
assert_to_bytes!(0x01_u8, &[0x01]);
}
#[test]
fn test_uleb_1_byte() {
assert_to_bytes!(0x01_u64, &[0x01]);
}
#[test]
fn test_uleb_3_bytes() {
assert_to_bytes!(0x7ffc_u64, &[0xfc, 0xff, 0x01]);
}
// Examples from Figure 22 of [DWARF 4
// standard](http://dwarfstd.org/doc/DWARF4.pdf).
#[test]
fn test_uleb_from_dward_standard() {
assert_to_bytes!(2u64, &[2u8]);
assert_to_bytes!(127u64, &[127u8]);
assert_to_bytes!(128u64, &[0x80, 1u8]);
assert_to_bytes!(129u64, &[1u8 | 0x80, 1]);
assert_to_bytes!(130u64, &[2u8 | 0x80, 1]);
assert_to_bytes!(12857u64, &[57u8 | 0x80, 100]);
}
#[test]
fn test_empty_str() {
assert_to_bytes!("", &[0x00]);
}
#[test]
fn test_str() {
assert_to_bytes!("abc", &[0x03, 0x61, 0x62, 0x63]);
}
#[test]
fn test_empty_vec() {
assert_to_bytes!(Vec::<u8>::new(), &[0x00]);
}
#[test]
fn test_vec() {
assert_to_bytes!(
vec!["a", "b", "c"],
&[
0x03, // list of 3 items
0x01, // string of 1 byte
0x61, // "a"
0x01, // string of 1 byte
0x62, // "b"
0x01, // string of 1 byte
0x63, // "c"
]
);
}
#[test]
fn test_interface_type() {
assert_to_bytes!(InterfaceType::S8, &[0x00]);
assert_to_bytes!(InterfaceType::S16, &[0x01]);
assert_to_bytes!(InterfaceType::S32, &[0x02]);
assert_to_bytes!(InterfaceType::S64, &[0x03]);
assert_to_bytes!(InterfaceType::U8, &[0x04]);
assert_to_bytes!(InterfaceType::U16, &[0x05]);
assert_to_bytes!(InterfaceType::U32, &[0x06]);
assert_to_bytes!(InterfaceType::U64, &[0x07]);
assert_to_bytes!(InterfaceType::F32, &[0x08]);
assert_to_bytes!(InterfaceType::F64, &[0x09]);
assert_to_bytes!(InterfaceType::String, &[0x0a]);
assert_to_bytes!(InterfaceType::Anyref, &[0x0b]);
assert_to_bytes!(InterfaceType::I32, &[0x0c]);
assert_to_bytes!(InterfaceType::I64, &[0x0d]);
assert_to_bytes!(
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::String]
}),
&[0x0e, 0x01, 0x0a]
);
}
#[test]
fn test_record_type() {
assert_to_bytes!(
RecordType {
fields: vec1![InterfaceType::String]
},
&[
0x01, // 1 field
0x0a, // String
]
);
assert_to_bytes!(
RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32]
},
&[
0x02, // 2 fields
0x0a, // String
0x0c, // I32
]
);
assert_to_bytes!(
RecordType {
fields: vec1![
InterfaceType::String,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I32],
}),
InterfaceType::F64,
],
},
&[
0x03, // 3 fields
0x0a, // String
0x0e, // Record
0x02, // 2 fields
0x0c, // I32
0x0c, // I32
0x09, // F64
]
);
}
#[test]
fn test_interface_kind() {
assert_to_bytes!(InterfaceKind::Type, &[0x00]);
assert_to_bytes!(InterfaceKind::Import, &[0x01]);
assert_to_bytes!(InterfaceKind::Adapter, &[0x02]);
assert_to_bytes!(InterfaceKind::Export, &[0x03]);
assert_to_bytes!(InterfaceKind::Implementation, &[0x04]);
}
#[test]
fn test_export() {
assert_to_bytes!(
Export {
name: "abc",
function_type: 0,
},
&[
0x03, // string of length 3
0x61, // "a"
0x62, // "b"
0x63, // "c"
0x00, // function type
]
);
}
#[test]
fn test_type_function() {
assert_to_bytes!(
Type::Function {
inputs: vec![InterfaceType::I32, InterfaceType::I64],
outputs: vec![InterfaceType::S32],
},
&[
0x00, // function type
0x02, // list of 2 items
0x0c, // I32
0x0d, // I64
0x01, // list of 1 items
0x02, // I64
]
);
}
#[test]
fn test_type_record() {
assert_to_bytes!(
Type::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I64],
}),
&[
0x01, // record type
0x02, // list of 2 items
0x0c, // I32
0x0d, // I64
]
);
}
#[test]
fn test_import() {
assert_to_bytes!(
Import {
namespace: "a",
name: "b",
signature_type: 0,
},
&[
0x01, // string of length 1
0x61, // "a"
0x01, // string of length 1
0x62, // "b"
0x00, // signature typr
]
);
}
#[test]
fn test_adapter() {
assert_to_bytes!(
Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 1 }],
},
&[
0x00, // function type
0x01, // list of 1 item
0x00, 0x01, // ArgumentGet { index: 1 }
]
);
}
#[test]
fn test_interfaces() {
assert_to_bytes!(
Interfaces {
types: vec![Type::Function {
inputs: vec![InterfaceType::S8],
outputs: vec![InterfaceType::S16],
}],
imports: vec![Import {
namespace: "ab",
name: "c",
signature_type: 0,
}],
adapters: vec![Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 1 }],
}],
exports: vec![Export {
name: "ab",
function_type: 1,
}],
implementations: vec![Implementation {
core_function_type: 2,
adapter_function_type: 3,
}],
},
&[
0x00, // type section
0x01, // 1 type
0x00, // function type
0x01, // list of 1 item
0x00, // S8
0x01, // list of 1 item
0x01, // S16
//
0x01, // import section
0x01, // 1 import
0x02, // string of 2 bytes
0x61, 0x62, // "a", "b"
0x01, // string of 1 byte
0x63, // "c"
0x00, // signature type
//
0x02, // adapter section
0x01, // 1 adapter
0x00, // function type
0x01, // list of 1 item
0x00, 0x01, // ArgumentGet { index: 1 }
//
0x03, // export section
0x01, // 1 export
0x02, // string of 2 bytes
0x61, 0x62, // "a", "b"
0x01, // function type
//
0x04, // implementation section
0x01, // 1 implementation
0x02, // core function type
0x03, // adapter function type
]
);
}
#[test]
fn test_instructions() {
assert_to_bytes!(
vec![
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 1 },
Instruction::S8FromI32,
Instruction::S8FromI64,
Instruction::S16FromI32,
Instruction::S16FromI64,
Instruction::S32FromI32,
Instruction::S32FromI64,
Instruction::S64FromI32,
Instruction::S64FromI64,
Instruction::I32FromS8,
Instruction::I32FromS16,
Instruction::I32FromS32,
Instruction::I32FromS64,
Instruction::I64FromS8,
Instruction::I64FromS16,
Instruction::I64FromS32,
Instruction::I64FromS64,
Instruction::U8FromI32,
Instruction::U8FromI64,
Instruction::U16FromI32,
Instruction::U16FromI64,
Instruction::U32FromI32,
Instruction::U32FromI64,
Instruction::U64FromI32,
Instruction::U64FromI64,
Instruction::I32FromU8,
Instruction::I32FromU16,
Instruction::I32FromU32,
Instruction::I32FromU64,
Instruction::I64FromU8,
Instruction::I64FromU16,
Instruction::I64FromU32,
Instruction::I64FromU64,
Instruction::StringLiftMemory,
Instruction::StringLowerMemory,
Instruction::StringSize,
Instruction::RecordLift { type_index: 1 },
Instruction::RecordLower { type_index: 1 },
],
&[
0x27, // list of 39 items
0x00, 0x01, // ArgumentGet { index: 1 }
0x01, 0x01, // CallCore { function_index: 1 }
0x02, // S8FromI32
0x03, // S8FromI64
0x04, // S16FromI32
0x05, // S16FromI64
0x06, // S32FromI32
0x07, // S32FromI64
0x08, // S64FromI32
0x09, // S64FromI64
0x0a, // I32FromS8
0x0b, // I32FromS16
0x0c, // I32FromS32
0x0d, // I32FromS64
0x0e, // I64FromS8
0x0f, // I64FromS16
0x10, // I64FromS32
0x11, // I64FromS64
0x12, // U8FromI32
0x13, // U8FromI64
0x14, // U16FromI32
0x15, // U16FromI64
0x16, // U32FromI32
0x17, // U32FromI64
0x18, // U64FromI32
0x19, // U64FromI64
0x1a, // I32FromU8
0x1b, // I32FromU16
0x1c, // I32FromU32
0x1d, // I32FromU64
0x1e, // I64FromU8
0x1f, // I64FromU16
0x20, // I64FromU32
0x21, // I64FromU64
0x22, // StringLiftMemory
0x23, // StringLowerMemory
0x24, // StringSize
0x025, 0x01, // RecordLift { type_index: 1 }
0x026, 0x01, // RecordLower { type_index: 1 }
]
);
}
}

View File

@ -1,6 +0,0 @@
//! Writes the AST into a particular format; for instance,
//! `encoders::wat` writes the AST into a string representing WIT with
//! its textual format.
pub mod binary;
pub mod wat;

View File

@ -1,641 +0,0 @@
//! Writes the AST into a string representing WIT with its textual format.
//!
//! # Example
//!
//! ```rust
//! use wasmer_interface_types::{
//! ast::{Adapter, Export, Implementation, Import, Interfaces, Type},
//! encoders::wat::*,
//! interpreter::Instruction,
//! types::InterfaceType,
//! };
//!
//! let input: String = (&Interfaces {
//! types: vec![Type::Function {
//! inputs: vec![InterfaceType::I32],
//! outputs: vec![InterfaceType::S8],
//! }],
//! imports: vec![Import {
//! namespace: "ns",
//! name: "foo",
//! signature_type: 0,
//! }],
//! adapters: vec![Adapter {
//! function_type: 0,
//! instructions: vec![Instruction::ArgumentGet { index: 42 }],
//! }],
//! exports: vec![Export {
//! name: "bar",
//! function_type: 0,
//! }],
//! implementations: vec![Implementation {
//! core_function_type: 0,
//! adapter_function_type: 1,
//! }],
//! })
//! .to_string();
//! let output = r#";; Types
//! (@interface type (func
//! (param i32)
//! (result s8)))
//!
//! ;; Imports
//! (@interface import "ns" "foo" (func (type 0)))
//!
//! ;; Adapters
//! (@interface func (type 0)
//! arg.get 42)
//!
//! ;; Exports
//! (@interface export "bar" (func 0))
//!
//! ;; Implementations
//! (@interface implement (func 0) (func 1))"#;
//!
//! assert_eq!(input, output);
//! ```
use crate::{ast::*, interpreter::Instruction, types::*};
use std::string::ToString;
/// Encode an `InterfaceType` into a string.
impl ToString for &InterfaceType {
fn to_string(&self) -> String {
match self {
InterfaceType::S8 => "s8".to_string(),
InterfaceType::S16 => "s16".to_string(),
InterfaceType::S32 => "s32".to_string(),
InterfaceType::S64 => "s64".to_string(),
InterfaceType::U8 => "u8".to_string(),
InterfaceType::U16 => "u16".to_string(),
InterfaceType::U32 => "u32".to_string(),
InterfaceType::U64 => "u64".to_string(),
InterfaceType::F32 => "f32".to_string(),
InterfaceType::F64 => "f64".to_string(),
InterfaceType::String => "string".to_string(),
InterfaceType::Anyref => "anyref".to_string(),
InterfaceType::I32 => "i32".to_string(),
InterfaceType::I64 => "i64".to_string(),
InterfaceType::Record(record_type) => record_type.to_string(),
}
}
}
impl ToString for &RecordType {
fn to_string(&self) -> String {
format!(
"record{fields}",
fields = self
.fields
.iter()
.fold(String::new(), |mut accumulator, interface_type| {
accumulator.push(' ');
accumulator.push_str(&format!("(field {})", &interface_type.to_string()));
accumulator
}),
)
}
}
/// Encode an `Instruction` into a string.
impl ToString for &Instruction {
fn to_string(&self) -> String {
match self {
Instruction::ArgumentGet { index } => format!("arg.get {}", index),
Instruction::CallCore { function_index } => format!("call-core {}", function_index),
Instruction::S8FromI32 => "s8.from_i32".into(),
Instruction::S8FromI64 => "s8.from_i64".into(),
Instruction::S16FromI32 => "s16.from_i32".into(),
Instruction::S16FromI64 => "s16.from_i64".into(),
Instruction::S32FromI32 => "s32.from_i32".into(),
Instruction::S32FromI64 => "s32.from_i64".into(),
Instruction::S64FromI32 => "s64.from_i32".into(),
Instruction::S64FromI64 => "s64.from_i64".into(),
Instruction::I32FromS8 => "i32.from_s8".into(),
Instruction::I32FromS16 => "i32.from_s16".into(),
Instruction::I32FromS32 => "i32.from_s32".into(),
Instruction::I32FromS64 => "i32.from_s64".into(),
Instruction::I64FromS8 => "i64.from_s8".into(),
Instruction::I64FromS16 => "i64.from_s16".into(),
Instruction::I64FromS32 => "i64.from_s32".into(),
Instruction::I64FromS64 => "i64.from_s64".into(),
Instruction::U8FromI32 => "u8.from_i32".into(),
Instruction::U8FromI64 => "u8.from_i64".into(),
Instruction::U16FromI32 => "u16.from_i32".into(),
Instruction::U16FromI64 => "u16.from_i64".into(),
Instruction::U32FromI32 => "u32.from_i32".into(),
Instruction::U32FromI64 => "u32.from_i64".into(),
Instruction::U64FromI32 => "u64.from_i32".into(),
Instruction::U64FromI64 => "u64.from_i64".into(),
Instruction::I32FromU8 => "i32.from_u8".into(),
Instruction::I32FromU16 => "i32.from_u16".into(),
Instruction::I32FromU32 => "i32.from_u32".into(),
Instruction::I32FromU64 => "i32.from_u64".into(),
Instruction::I64FromU8 => "i64.from_u8".into(),
Instruction::I64FromU16 => "i64.from_u16".into(),
Instruction::I64FromU32 => "i64.from_u32".into(),
Instruction::I64FromU64 => "i64.from_u64".into(),
Instruction::StringLiftMemory => "string.lift_memory".into(),
Instruction::StringLowerMemory => "string.lower_memory".into(),
Instruction::StringSize => "string.size".into(),
Instruction::RecordLift { type_index } => format!("record.lift {}", type_index),
Instruction::RecordLower { type_index } => format!("record.lower {}", type_index),
}
}
}
/// Encode a list of `InterfaceType` representing inputs into a
/// string.
fn input_types_to_param(input_types: &[InterfaceType]) -> String {
if input_types.is_empty() {
"".into()
} else {
format!(
"\n (param{})",
input_types
.iter()
.fold(String::new(), |mut accumulator, interface_type| {
accumulator.push(' ');
accumulator.push_str(&interface_type.to_string());
accumulator
})
)
}
}
/// Encode a list of `InterfaceType` representing outputs into a
/// string.
fn output_types_to_result(output_types: &[InterfaceType]) -> String {
if output_types.is_empty() {
"".into()
} else {
format!(
"\n (result{})",
output_types
.iter()
.fold(String::new(), |mut accumulator, interface_type| {
accumulator.push(' ');
accumulator.push_str(&interface_type.to_string());
accumulator
})
)
}
}
/// Encode a `Type` into a string.
impl<'input> ToString for &Type {
fn to_string(&self) -> String {
match self {
Type::Function { inputs, outputs } => format!(
r#"(@interface type (func{inputs}{outputs}))"#,
inputs = input_types_to_param(&inputs),
outputs = output_types_to_result(&outputs),
),
Type::Record(record_type) => format!(
r#"(@interface type ({record_type}))"#,
record_type = record_type.to_string(),
),
}
}
}
/// Encode an `Import` into a string.
impl<'input> ToString for &Import<'input> {
fn to_string(&self) -> String {
format!(
r#"(@interface import "{namespace}" "{name}" (func (type {type})))"#,
namespace = self.namespace,
name = self.name,
type = self.signature_type,
)
}
}
/// Encode an `Adapter` into a string.
impl ToString for &Adapter {
fn to_string(&self) -> String {
format!(
r#"(@interface func (type {function_type}){instructions})"#,
function_type = self.function_type,
instructions =
self.instructions
.iter()
.fold(String::new(), |mut accumulator, instruction| {
accumulator.push_str("\n ");
accumulator.push_str(&instruction.to_string());
accumulator
}),
)
}
}
/// Encode an `Export` into a string.
impl<'input> ToString for &Export<'input> {
fn to_string(&self) -> String {
format!(
r#"(@interface export "{name}" (func {type}))"#,
name = self.name,
type = self.function_type,
)
}
}
/// Encode an `Implementation` into a string.
impl<'input> ToString for &Implementation {
fn to_string(&self) -> String {
format!(
r#"(@interface implement (func {core_function_type}) (func {adapter_function_type}))"#,
core_function_type = self.core_function_type,
adapter_function_type = self.adapter_function_type,
)
}
}
/// Encode an `Interfaces` into a string.
impl<'input> ToString for &Interfaces<'input> {
fn to_string(&self) -> String {
let mut output = String::new();
let types = self
.types
.iter()
.fold(String::new(), |mut accumulator, ty| {
accumulator.push('\n');
accumulator.push_str(&ty.to_string());
accumulator
});
let imports = self
.imports
.iter()
.fold(String::new(), |mut accumulator, import| {
accumulator.push('\n');
accumulator.push_str(&import.to_string());
accumulator
});
let adapters = self
.adapters
.iter()
.fold(String::new(), |mut accumulator, adapter| {
accumulator.push('\n');
accumulator.push_str(&adapter.to_string());
accumulator
});
let exports = self
.exports
.iter()
.fold(String::new(), |mut accumulator, export| {
accumulator.push('\n');
accumulator.push_str(&export.to_string());
accumulator
});
let implementations =
self.implementations
.iter()
.fold(String::new(), |mut accumulator, implementation| {
accumulator.push('\n');
accumulator.push_str(&implementation.to_string());
accumulator
});
let separator = |output: &mut String| {
if !output.is_empty() {
output.push_str("\n\n");
}
};
if !types.is_empty() {
output.push_str(";; Types");
output.push_str(&types);
}
separator(&mut output);
if !imports.is_empty() {
output.push_str(";; Imports");
output.push_str(&imports);
}
separator(&mut output);
if !adapters.is_empty() {
output.push_str(";; Adapters");
output.push_str(&adapters);
}
separator(&mut output);
if !exports.is_empty() {
output.push_str(";; Exports");
output.push_str(&exports);
}
separator(&mut output);
if !implementations.is_empty() {
output.push_str(";; Implementations");
output.push_str(&implementations);
}
output
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interface_types() {
let inputs: Vec<String> = vec![
(&InterfaceType::S8).to_string(),
(&InterfaceType::S16).to_string(),
(&InterfaceType::S32).to_string(),
(&InterfaceType::S64).to_string(),
(&InterfaceType::U8).to_string(),
(&InterfaceType::U16).to_string(),
(&InterfaceType::U32).to_string(),
(&InterfaceType::U64).to_string(),
(&InterfaceType::F32).to_string(),
(&InterfaceType::F64).to_string(),
(&InterfaceType::String).to_string(),
(&InterfaceType::Anyref).to_string(),
(&InterfaceType::I32).to_string(),
(&InterfaceType::I64).to_string(),
(&InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::String],
}))
.to_string(),
];
let outputs = vec![
"s8",
"s16",
"s32",
"s64",
"u8",
"u16",
"u32",
"u64",
"f32",
"f64",
"string",
"anyref",
"i32",
"i64",
"record (field string)",
];
assert_eq!(inputs, outputs);
}
#[test]
fn test_record_type() {
let inputs = vec![
(&RecordType {
fields: vec1![InterfaceType::String],
})
.to_string(),
(&RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32],
})
.to_string(),
(&RecordType {
fields: vec1![
InterfaceType::String,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I32],
}),
InterfaceType::F64,
],
})
.to_string(),
];
let outputs = vec![
"record (field string)",
"record (field string) (field i32)",
"record (field string) (field record (field i32) (field i32)) (field f64)",
];
assert_eq!(inputs, outputs);
}
#[test]
fn test_instructions() {
let inputs: Vec<String> = vec![
(&Instruction::ArgumentGet { index: 7 }).to_string(),
(&Instruction::CallCore { function_index: 7 }).to_string(),
(&Instruction::S8FromI32).to_string(),
(&Instruction::S8FromI64).to_string(),
(&Instruction::S16FromI32).to_string(),
(&Instruction::S16FromI64).to_string(),
(&Instruction::S32FromI32).to_string(),
(&Instruction::S32FromI64).to_string(),
(&Instruction::S64FromI32).to_string(),
(&Instruction::S64FromI64).to_string(),
(&Instruction::I32FromS8).to_string(),
(&Instruction::I32FromS16).to_string(),
(&Instruction::I32FromS32).to_string(),
(&Instruction::I32FromS64).to_string(),
(&Instruction::I64FromS8).to_string(),
(&Instruction::I64FromS16).to_string(),
(&Instruction::I64FromS32).to_string(),
(&Instruction::I64FromS64).to_string(),
(&Instruction::U8FromI32).to_string(),
(&Instruction::U8FromI64).to_string(),
(&Instruction::U16FromI32).to_string(),
(&Instruction::U16FromI64).to_string(),
(&Instruction::U32FromI32).to_string(),
(&Instruction::U32FromI64).to_string(),
(&Instruction::U64FromI32).to_string(),
(&Instruction::U64FromI64).to_string(),
(&Instruction::I32FromU8).to_string(),
(&Instruction::I32FromU16).to_string(),
(&Instruction::I32FromU32).to_string(),
(&Instruction::I32FromU64).to_string(),
(&Instruction::I64FromU8).to_string(),
(&Instruction::I64FromU16).to_string(),
(&Instruction::I64FromU32).to_string(),
(&Instruction::I64FromU64).to_string(),
(&Instruction::StringLiftMemory).to_string(),
(&Instruction::StringLowerMemory).to_string(),
(&Instruction::StringSize).to_string(),
(&Instruction::RecordLift { type_index: 42 }).to_string(),
(&Instruction::RecordLower { type_index: 42 }).to_string(),
];
let outputs = vec![
"arg.get 7",
"call-core 7",
"s8.from_i32",
"s8.from_i64",
"s16.from_i32",
"s16.from_i64",
"s32.from_i32",
"s32.from_i64",
"s64.from_i32",
"s64.from_i64",
"i32.from_s8",
"i32.from_s16",
"i32.from_s32",
"i32.from_s64",
"i64.from_s8",
"i64.from_s16",
"i64.from_s32",
"i64.from_s64",
"u8.from_i32",
"u8.from_i64",
"u16.from_i32",
"u16.from_i64",
"u32.from_i32",
"u32.from_i64",
"u64.from_i32",
"u64.from_i64",
"i32.from_u8",
"i32.from_u16",
"i32.from_u32",
"i32.from_u64",
"i64.from_u8",
"i64.from_u16",
"i64.from_u32",
"i64.from_u64",
"string.lift_memory",
"string.lower_memory",
"string.size",
"record.lift 42",
"record.lower 42",
];
assert_eq!(inputs, outputs);
}
#[test]
fn test_types() {
let inputs: Vec<String> = vec![
(&Type::Function {
inputs: vec![InterfaceType::I32, InterfaceType::F32],
outputs: vec![InterfaceType::I32],
})
.to_string(),
(&Type::Function {
inputs: vec![InterfaceType::I32],
outputs: vec![],
})
.to_string(),
(&Type::Function {
inputs: vec![],
outputs: vec![InterfaceType::I32],
})
.to_string(),
(&Type::Function {
inputs: vec![],
outputs: vec![],
})
.to_string(),
(&Type::Record(RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32],
}))
.to_string(),
];
let outputs = vec![
r#"(@interface type (func
(param i32 f32)
(result i32)))"#,
r#"(@interface type (func
(param i32)))"#,
r#"(@interface type (func
(result i32)))"#,
r#"(@interface type (func))"#,
r#"(@interface type (record (field string) (field i32)))"#,
];
assert_eq!(inputs, outputs);
}
#[test]
fn test_exports() {
let input = (&Export {
name: "foo",
function_type: 0,
})
.to_string();
let output = r#"(@interface export "foo" (func 0))"#;
assert_eq!(input, output);
}
#[test]
fn test_imports() {
let input = (&Import {
namespace: "ns",
name: "foo",
signature_type: 0,
})
.to_string();
let output = r#"(@interface import "ns" "foo" (func (type 0)))"#;
assert_eq!(input, output);
}
#[test]
fn test_adapter() {
let input = (&Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 42 }],
})
.to_string();
let output = r#"(@interface func (type 0)
arg.get 42)"#;
assert_eq!(input, output);
}
#[test]
fn test_interfaces() {
let input: String = (&Interfaces {
types: vec![Type::Function {
inputs: vec![InterfaceType::I32],
outputs: vec![InterfaceType::S8],
}],
imports: vec![Import {
namespace: "ns",
name: "foo",
signature_type: 0,
}],
adapters: vec![Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 42 }],
}],
exports: vec![Export {
name: "bar",
function_type: 0,
}],
implementations: vec![Implementation {
core_function_type: 0,
adapter_function_type: 1,
}],
})
.to_string();
let output = r#";; Types
(@interface type (func
(param i32)
(result s8)))
;; Imports
(@interface import "ns" "foo" (func (type 0)))
;; Adapters
(@interface func (type 0)
arg.get 42)
;; Exports
(@interface export "bar" (func 0))
;; Implementations
(@interface implement (func 0) (func 1))"#;
assert_eq!(input, output);
}
}

View File

@ -1,269 +0,0 @@
//! The error module contains all the data structures that represent
//! an error.
use crate::{ast::TypeKind, interpreter::Instruction, types::InterfaceType};
use std::{
error::Error,
fmt::{self, Display, Formatter},
num::TryFromIntError,
result::Result,
string::{self, ToString},
};
/// A type alias for instruction's results.
pub type InstructionResult<T> = Result<T, InstructionError>;
/// A type alias for the interpreter result.
pub type InterpreterResult<T> = Result<T, InstructionError>;
/// Structure to represent errors when casting from an `InterfaceType`
/// to a native value.
#[derive(Debug)]
pub struct WasmValueNativeCastError {
/// The initial type.
pub from: InterfaceType,
/// The targeted type.
///
/// `InterfaceType` is used to represent the native type by
/// associativity.
pub to: InterfaceType,
}
impl Error for WasmValueNativeCastError {}
impl Display for WasmValueNativeCastError {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "{:?}", self)
}
}
/// Structure to represent the errors for instructions.
#[derive(Debug)]
pub struct InstructionError {
/// The instruction that raises the error.
pub instruction: Instruction,
/// The error kind.
pub error_kind: InstructionErrorKind,
}
impl InstructionError {
pub(crate) fn new(instruction: Instruction, error_kind: InstructionErrorKind) -> Self {
Self {
instruction,
error_kind,
}
}
}
impl Error for InstructionError {}
impl Display for InstructionError {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(
formatter,
"`{}` {}",
(&self.instruction).to_string(),
self.error_kind
)
}
}
/// The kind of instruction errors.
#[derive(Debug)]
pub enum InstructionErrorKind {
/// The instruction needs to read an invocation input at index `index`, but it's missing.
InvocationInputIsMissing {
/// The invocation input index.
index: u32,
},
/// Failed to cast from a WIT value to a native value.
ToNative(WasmValueNativeCastError),
/// Failed to cast from `from` to `to`.
LoweringLifting {
/// The initial type.
from: InterfaceType,
/// The targeted type.
to: InterfaceType,
},
/// Read a value from the stack, but it doesn't have the expected
/// type.
InvalidValueOnTheStack {
/// The expected type.
expected_type: InterfaceType,
/// The received type.
received_type: InterfaceType,
},
/// Need to read some values from the stack, but it doesn't
/// contain enough data.
StackIsTooSmall {
/// The number of values that were needed.
needed: usize,
},
/// The local or import function doesn't exist.
LocalOrImportIsMissing {
/// The local or import function index.
function_index: u32,
},
/// Values given to a local or import function doesn't match the
/// function signature.
LocalOrImportSignatureMismatch {
/// The local or import function index.
function_index: u32,
/// The expected signature.
expected: (Vec<InterfaceType>, Vec<InterfaceType>),
/// The received signature.
received: (Vec<InterfaceType>, Vec<InterfaceType>),
},
/// Failed to call a local or import function.
LocalOrImportCall {
/// The local or import function index that has been called.
function_index: u32,
},
/// The memory doesn't exist.
MemoryIsMissing {
/// The memory indeX.
memory_index: u32,
},
/// Tried to read out of bounds of the memory.
MemoryOutOfBoundsAccess {
/// The access index.
index: usize,
/// The memory length.
length: usize,
},
/// The string contains invalid UTF-8 encoding.
String(string::FromUtf8Error),
/// Out of range integral type conversion attempted.
NegativeValue {
/// The variable name that triggered the error.
subject: &'static str,
},
/// The type doesn't exist.
TypeIsMissing {
/// The type index.
type_index: u32,
},
/// Read a type that has an unexpected type.
InvalidTypeKind {
/// The expected kind.
expected_kind: TypeKind,
/// The received kind.
received_kind: TypeKind,
},
}
impl Error for InstructionErrorKind {}
impl Display for InstructionErrorKind {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match self {
Self::InvocationInputIsMissing { index } => write!(
formatter,
"cannot access invocation inputs #{} because it doesn't exist",
index
),
Self::ToNative(WasmValueNativeCastError { from, .. }) => write!(
formatter,
"failed to cast the WIT value `{:?}` to its native type",
from,
),
Self::LoweringLifting { from, to } => {
write!(formatter, "failed to cast `{:?}` to `{:?}`", from, to)
}
Self::InvalidValueOnTheStack {
expected_type,
received_type,
} => write!(
formatter,
"read a value of type `{:?}` from the stack, but the type `{:?}` was expected",
received_type, expected_type,
),
Self::StackIsTooSmall { needed } => write!(
formatter,
"needed to read `{}` value(s) from the stack, but it doesn't contain enough data",
needed
),
Self::LocalOrImportIsMissing { function_index } => write!(
formatter,
"the local or import function `{}` doesn't exist",
function_index
),
Self::LocalOrImportSignatureMismatch { function_index, expected, received } => write!(
formatter,
"the local or import function `{}` has the signature `{:?} -> {:?}` but it received values of kind `{:?} -> {:?}`",
function_index, expected.0, expected.1, received.0, received.1,
),
Self::LocalOrImportCall { function_index } => write!(
formatter,
"failed while calling the local or import function `{}`",
function_index
),
Self::MemoryIsMissing { memory_index } => write!(
formatter,
"memory `{}` does not exist",
memory_index,
),
Self::MemoryOutOfBoundsAccess { index, length } => write!(
formatter,
"read out of the memory bounds (index {} > memory length {})",
index, length,
),
Self::String(error) => write!(formatter, "{}", error),
Self::NegativeValue { subject } => write!(
formatter,
"attempted to convert `{}` but it appears to be a negative value",
subject
),
Self::TypeIsMissing { type_index } => write!(
formatter,
"the type `{}` doesn't exist",
type_index
),
Self::InvalidTypeKind { expected_kind, received_kind } => write!(
formatter,
"read a type of kind `{:?}`, but the kind `{:?}` was expected",
received_kind, expected_kind
),
}
}
}
impl From<(TryFromIntError, &'static str)> for InstructionErrorKind {
fn from((_, subject): (TryFromIntError, &'static str)) -> Self {
InstructionErrorKind::NegativeValue { subject }
}
}

View File

@ -1,59 +0,0 @@
use crate::{
errors::{InstructionError, InstructionErrorKind},
interpreter::Instruction,
};
executable_instruction!(
argument_get(index: u32, instruction: Instruction) -> _ {
move |runtime| -> _ {
let invocation_inputs = runtime.invocation_inputs;
if (index as usize) >= invocation_inputs.len() {
return Err(InstructionError::new(
instruction,
InstructionErrorKind::InvocationInputIsMissing { index },
));
}
runtime.stack.push(invocation_inputs[index as usize].clone());
Ok(())
}
}
);
#[cfg(test)]
mod tests {
test_executable_instruction!(
test_argument_get =
instructions: [Instruction::ArgumentGet { index: 0 }],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_argument_get__twice =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
],
invocation_inputs: [
InterfaceValue::I32(7),
InterfaceValue::I32(42),
],
instance: Instance::new(),
stack: [
InterfaceValue::I32(7),
InterfaceValue::I32(42),
],
);
test_executable_instruction!(
test_argument_get__invalid_index =
instructions: [Instruction::ArgumentGet { index: 1 }],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
error: "`arg.get 1` cannot access invocation inputs #1 because it doesn't exist"
);
}

View File

@ -1,188 +0,0 @@
use crate::{
errors::{InstructionError, InstructionErrorKind},
interpreter::wasm::structures::{FunctionIndex, TypedIndex},
interpreter::Instruction,
types::InterfaceType,
};
executable_instruction!(
call_core(function_index: u32, instruction: Instruction) -> _ {
move |runtime| -> _ {
let instance = &mut runtime.wasm_instance;
let index = FunctionIndex::new(function_index as usize);
let local_or_import = instance.local_or_import(index).ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::LocalOrImportIsMissing {
function_index: function_index,
},
)
})?;
let inputs_cardinality = local_or_import.inputs_cardinality();
let inputs = runtime.stack.pop(inputs_cardinality).ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall {
needed: inputs_cardinality,
},
)
})?;
let input_types = inputs
.iter()
.map(Into::into)
.collect::<Vec<InterfaceType>>();
if input_types != local_or_import.inputs() {
return Err(InstructionError::new(
instruction,
InstructionErrorKind::LocalOrImportSignatureMismatch {
function_index: function_index,
expected: (local_or_import.inputs().to_vec(), vec![]),
received: (input_types, vec![]),
},
));
}
let outputs = local_or_import.call(&inputs).map_err(|_| {
InstructionError::new(
instruction,
InstructionErrorKind::LocalOrImportCall {
function_index: function_index,
},
)
})?;
for output in outputs.into_iter() {
runtime.stack.push(output)
}
Ok(())
}
}
);
#[cfg(test)]
mod tests {
test_executable_instruction!(
test_call_core =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 },
],
invocation_inputs: [
InterfaceValue::I32(3),
InterfaceValue::I32(4),
],
instance: Instance::new(),
stack: [InterfaceValue::I32(12)],
);
test_executable_instruction!(
test_call_core__invalid_local_import_index =
instructions: [
Instruction::CallCore { function_index: 42 },
],
invocation_inputs: [
InterfaceValue::I32(3),
InterfaceValue::I32(4),
],
instance: Default::default(),
error: r#"`call-core 42` the local or import function `42` doesn't exist"#,
);
test_executable_instruction!(
test_call_core__stack_is_too_small =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::CallCore { function_index: 42 },
// ^^ `42` expects 2 values on the stack, only one is present
],
invocation_inputs: [
InterfaceValue::I32(3),
InterfaceValue::I32(4),
],
instance: Instance::new(),
error: r#"`call-core 42` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#,
);
test_executable_instruction!(
test_call_core__invalid_types_in_the_stack =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 },
],
invocation_inputs: [
InterfaceValue::I32(3),
InterfaceValue::I64(4),
// ^^^ mismatch with `42` signature
],
instance: Instance::new(),
error: r#"`call-core 42` the local or import function `42` has the signature `[I32, I32] -> []` but it received values of kind `[I32, I64] -> []`"#,
);
test_executable_instruction!(
test_call_core__failure_when_calling =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 },
],
invocation_inputs: [
InterfaceValue::I32(3),
InterfaceValue::I32(4),
],
instance: Instance {
locals_or_imports: {
let mut hashmap = HashMap::new();
hashmap.insert(
42,
LocalImport {
inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |_| Err(()),
// ^^^^^^^ function fails
},
);
hashmap
},
..Default::default()
},
error: r#"`call-core 42` failed while calling the local or import function `42`"#,
);
test_executable_instruction!(
test_call_core__void =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::CallCore { function_index: 42 },
],
invocation_inputs: [
InterfaceValue::I32(3),
InterfaceValue::I32(4),
],
instance: Instance {
locals_or_imports: {
let mut hashmap = HashMap::new();
hashmap.insert(
42,
LocalImport {
inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |_| Ok(vec![]),
// ^^^^^^^^^^ void
},
);
hashmap
},
..Default::default()
},
stack: [],
);
}

View File

@ -1,352 +0,0 @@
mod argument_get;
mod call_core;
mod numbers;
mod records;
mod strings;
use crate::{
errors::{InstructionError, InstructionErrorKind, InstructionResult, WasmValueNativeCastError},
values::{InterfaceValue, NativeType},
};
pub(crate) use argument_get::argument_get;
pub(crate) use call_core::call_core;
pub(crate) use numbers::*;
pub(crate) use records::*;
use std::convert::TryFrom;
pub(crate) use strings::*;
/// Represents all the possible WIT instructions.
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Instruction {
/// The `arg.get` instruction.
ArgumentGet {
/// The argument index.
index: u32,
},
/// The `call-core` instruction.
CallCore {
/// The function index.
function_index: u32,
},
/// The `s8.from_i32` instruction.
S8FromI32,
/// The `s8.from_i64` instruction.
S8FromI64,
/// The `s16.from_i32` instruction.
S16FromI32,
/// The `s16.from_i64` instruction.
S16FromI64,
/// The `s32.from_i32` instruction.
S32FromI32,
/// The `s32.from_i64` instruction.
S32FromI64,
/// The `s64.from_i32` instruction.
S64FromI32,
/// The `s64.from_i64` instruction.
S64FromI64,
/// The `i32.from_s8` instruction.
I32FromS8,
/// The `i32.from_s16` instruction.
I32FromS16,
/// The `i32.from_s32` instruction.
I32FromS32,
/// The `i32.from_s64` instruction.
I32FromS64,
/// The `i64.from_s8` instruction.
I64FromS8,
/// The `i64.from_s16` instruction.
I64FromS16,
/// The `i64.from_s32` instruction.
I64FromS32,
/// The `i64.from_s64` instruction.
I64FromS64,
/// The `u8.from_i32` instruction.
U8FromI32,
/// The `u8.from_i64` instruction.
U8FromI64,
/// The `u16.from_i32` instruction.
U16FromI32,
/// The `u16.from_i64` instruction.
U16FromI64,
/// The `u32.from_i32` instruction.
U32FromI32,
/// The `u32.from_i64` instruction.
U32FromI64,
/// The `u64.from_i32` instruction.
U64FromI32,
/// The `u64.from_i64` instruction.
U64FromI64,
/// The `i32.from_u8` instruction.
I32FromU8,
/// The `i32.from_u16` instruction.
I32FromU16,
/// The `i32.from_u32` instruction.
I32FromU32,
/// The `i32.from_u64` instruction.
I32FromU64,
/// The `i64.from_u8` instruction.
I64FromU8,
/// The `i64.from_u16` instruction.
I64FromU16,
/// The `i64.from_u32` instruction.
I64FromU32,
/// The `i64.from_u64` instruction.
I64FromU64,
/// The `string.lift_memory` instruction.
StringLiftMemory,
/// The `string.lower_memory` instruction.
StringLowerMemory,
/// The `string.size` instruction.
StringSize,
/// The `record.lift` instruction.
RecordLift {
/// The type index of the record.
type_index: u32,
},
/// The `record.lower` instruction.
RecordLower {
/// The type index of the record.
type_index: u32,
},
}
/// Just a short helper to map the error of a cast from an
/// `InterfaceValue` to a native value.
pub(crate) fn to_native<'a, T>(
wit_value: &'a InterfaceValue,
instruction: Instruction,
) -> InstructionResult<T>
where
T: NativeType + TryFrom<&'a InterfaceValue, Error = WasmValueNativeCastError>,
{
T::try_from(wit_value)
.map_err(|error| InstructionError::new(instruction, InstructionErrorKind::ToNative(error)))
}
#[cfg(test)]
pub(crate) mod tests {
use crate::{ast::*, interpreter::wasm, types::*, values::*};
use std::{cell::Cell, collections::HashMap, convert::TryInto, ops::Deref, rc::Rc};
pub(crate) struct Export {
pub(crate) inputs: Vec<InterfaceType>,
pub(crate) outputs: Vec<InterfaceType>,
pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
}
impl wasm::structures::Export for Export {
fn inputs_cardinality(&self) -> usize {
self.inputs.len() as usize
}
fn outputs_cardinality(&self) -> usize {
self.outputs.len()
}
fn inputs(&self) -> &[InterfaceType] {
&self.inputs
}
fn outputs(&self) -> &[InterfaceType] {
&self.outputs
}
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
(self.function)(arguments)
}
}
pub(crate) struct LocalImport {
pub(crate) inputs: Vec<InterfaceType>,
pub(crate) outputs: Vec<InterfaceType>,
pub(crate) function: fn(arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>,
}
impl wasm::structures::LocalImport for LocalImport {
fn inputs_cardinality(&self) -> usize {
self.inputs.len()
}
fn outputs_cardinality(&self) -> usize {
self.outputs.len()
}
fn inputs(&self) -> &[InterfaceType] {
&self.inputs
}
fn outputs(&self) -> &[InterfaceType] {
&self.outputs
}
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
(self.function)(arguments)
}
}
#[derive(Default, Clone)]
pub(crate) struct MemoryView(Rc<Vec<Cell<u8>>>);
impl wasm::structures::MemoryView for MemoryView {}
impl Deref for MemoryView {
type Target = [Cell<u8>];
fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
#[derive(Default)]
pub(crate) struct Memory {
pub(crate) view: MemoryView,
}
impl Memory {
pub(crate) fn new(data: Vec<Cell<u8>>) -> Self {
Self {
view: MemoryView(Rc::new(data)),
}
}
}
impl wasm::structures::Memory<MemoryView> for Memory {
fn view(&self) -> MemoryView {
self.view.clone()
}
}
#[derive(Default)]
pub(crate) struct Instance {
pub(crate) exports: HashMap<String, Export>,
pub(crate) locals_or_imports: HashMap<usize, LocalImport>,
pub(crate) memory: Memory,
pub(crate) wit_types: Vec<Type>,
}
impl Instance {
pub(crate) fn new() -> Self {
Self {
exports: {
let mut hashmap = HashMap::new();
hashmap.insert(
"sum".into(),
Export {
inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |arguments: &[InterfaceValue]| {
let a: i32 = (&arguments[0]).try_into().unwrap();
let b: i32 = (&arguments[1]).try_into().unwrap();
Ok(vec![InterfaceValue::I32(a + b)])
},
},
);
hashmap
},
locals_or_imports: {
let mut hashmap = HashMap::new();
// sum
hashmap.insert(
42,
LocalImport {
inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |arguments: &[InterfaceValue]| {
let a: i32 = (&arguments[0]).try_into().unwrap();
let b: i32 = (&arguments[1]).try_into().unwrap();
Ok(vec![InterfaceValue::I32(a * b)])
},
},
);
// string allocator
hashmap.insert(
43,
LocalImport {
inputs: vec![InterfaceType::I32],
outputs: vec![InterfaceType::I32],
function: |arguments: &[InterfaceValue]| {
let _size: i32 = (&arguments[0]).try_into().unwrap();
Ok(vec![InterfaceValue::I32(0)])
},
},
);
hashmap
},
memory: Memory::new(vec![Cell::new(0); 128]),
wit_types: vec![Type::Record(RecordType {
fields: vec1![
InterfaceType::I32,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::String, InterfaceType::F32],
}),
InterfaceType::I64,
],
})],
}
}
}
impl wasm::structures::Instance<Export, LocalImport, Memory, MemoryView> for Instance {
fn export(&self, export_name: &str) -> Option<&Export> {
self.exports.get(export_name)
}
fn local_or_import<I: wasm::structures::TypedIndex + wasm::structures::LocalImportIndex>(
&mut self,
index: I,
) -> Option<&LocalImport> {
self.locals_or_imports.get(&index.index())
}
fn memory(&self, _index: usize) -> Option<&Memory> {
Some(&self.memory)
}
fn wit_type(&self, index: u32) -> Option<&Type> {
self.wit_types.get(index as usize)
}
}
}

View File

@ -1,370 +0,0 @@
use crate::{
errors::{InstructionError, InstructionErrorKind},
interpreter::Instruction,
types::InterfaceType,
values::InterfaceValue,
};
use std::convert::TryInto;
macro_rules! lowering_lifting {
($instruction_function_name:ident, $instruction_name:expr, $to_variant:ident, $from_variant:ident) => {
executable_instruction!(
$instruction_function_name(instruction: Instruction) -> _ {
move |runtime| -> _ {
match runtime.stack.pop1() {
Some(InterfaceValue::$from_variant(value)) => {
runtime
.stack
.push(InterfaceValue::$to_variant(value.try_into().map_err(
|_| {
InstructionError::new(
instruction,
InstructionErrorKind::LoweringLifting {
from: InterfaceType::$from_variant,
to: InterfaceType::$to_variant
},
)
},
)?))
}
Some(wrong_value) => {
return Err(InstructionError::new(
instruction,
InstructionErrorKind::InvalidValueOnTheStack {
expected_type: InterfaceType::$from_variant,
received_type: (&wrong_value).into(),
}
))
},
None => {
return Err(InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall { needed: 1 },
))
}
}
Ok(())
}
}
);
};
}
lowering_lifting!(s8_from_i32, "s8.from_i32", S8, I32);
lowering_lifting!(s8_from_i64, "s8.from_i64", S8, I64);
lowering_lifting!(s16_from_i32, "s16.from_i32", S16, I32);
lowering_lifting!(s16_from_i64, "s16.from_i64", S16, I64);
lowering_lifting!(s32_from_i32, "s32.from_i32", S32, I32);
lowering_lifting!(s32_from_i64, "s32.from_i64", S32, I64);
lowering_lifting!(s64_from_i32, "s64.from_i32", S64, I32);
lowering_lifting!(s64_from_i64, "s64.from_i64", S64, I64);
lowering_lifting!(i32_from_s8, "i32.from_s8", I32, S8);
lowering_lifting!(i32_from_s16, "i32.from_s16", I32, S16);
lowering_lifting!(i32_from_s32, "i32.from_s32", I32, S32);
lowering_lifting!(i32_from_s64, "i32.from_s64", I32, S64);
lowering_lifting!(i64_from_s8, "i64.from_s8", I64, S8);
lowering_lifting!(i64_from_s16, "i64.from_s16", I64, S16);
lowering_lifting!(i64_from_s32, "i64.from_s32", I64, S32);
lowering_lifting!(i64_from_s64, "i64.from_s64", I64, S64);
lowering_lifting!(u8_from_i32, "u8.from_i32", U8, I32);
lowering_lifting!(u8_from_i64, "u8.from_i64", U8, I64);
lowering_lifting!(u16_from_i32, "u16.from_i32", U16, I32);
lowering_lifting!(u16_from_i64, "u16.from_i64", U16, I64);
lowering_lifting!(u32_from_i32, "u32.from_i32", U32, I32);
lowering_lifting!(u32_from_i64, "u32.from_i64", U32, I64);
lowering_lifting!(u64_from_i32, "u64.from_i32", U64, I32);
lowering_lifting!(u64_from_i64, "u64.from_i64", U64, I64);
lowering_lifting!(i32_from_u8, "i32.from_u8", I32, U8);
lowering_lifting!(i32_from_u16, "i32.from_u16", I32, U16);
lowering_lifting!(i32_from_u32, "i32.from_u32", I32, U32);
lowering_lifting!(i32_from_u64, "i32.from_u64", I32, U64);
lowering_lifting!(i64_from_u8, "i64.from_u8", I64, U8);
lowering_lifting!(i64_from_u16, "i64.from_u16", I64, U16);
lowering_lifting!(i64_from_u32, "i64.from_u32", I64, U32);
lowering_lifting!(i64_from_u64, "i64.from_u64", I64, U64);
#[cfg(test)]
mod tests {
test_executable_instruction!(
test_convert_fails =
instructions: [Instruction::ArgumentGet { index: 0}, Instruction::S8FromI32],
invocation_inputs: [InterfaceValue::I32(128)],
instance: Instance::new(),
error: "`s8.from_i32` failed to cast `I32` to `S8`"
);
test_executable_instruction!(
test_type_mismatch =
instructions: [Instruction::ArgumentGet { index: 0}, Instruction::S8FromI32],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
error: "`s8.from_i32` read a value of type `I64` from the stack, but the type `I32` was expected"
);
test_executable_instruction!(
test_no_value_on_the_stack =
instructions: [Instruction::S8FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
error: "`s8.from_i32` needed to read `1` value(s) from the stack, but it doesn't contain enough data"
);
test_executable_instruction!(
test_s8_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::S8(42)],
);
test_executable_instruction!(
test_s8_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S8FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::S8(42)],
);
test_executable_instruction!(
test_s16_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::S16(42)],
);
test_executable_instruction!(
test_s16_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S16FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::S16(42)],
);
test_executable_instruction!(
test_s32_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::S32(42)],
);
test_executable_instruction!(
test_s32_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S32FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::S32(42)],
);
test_executable_instruction!(
test_s64_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::S64(42)],
);
test_executable_instruction!(
test_s64_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::S64FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::S64(42)],
);
test_executable_instruction!(
test_i32_from_s8 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromS8],
invocation_inputs: [InterfaceValue::S8(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i32_from_s16 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromS16],
invocation_inputs: [InterfaceValue::S16(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i32_from_s32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromS32],
invocation_inputs: [InterfaceValue::S32(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i32_from_s64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromS64],
invocation_inputs: [InterfaceValue::S64(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i64_from_s8 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromS8],
invocation_inputs: [InterfaceValue::S8(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
test_executable_instruction!(
test_i64_from_s16 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromS16],
invocation_inputs: [InterfaceValue::S16(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
test_executable_instruction!(
test_i64_from_s32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromS32],
invocation_inputs: [InterfaceValue::S32(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
test_executable_instruction!(
test_i64_from_s64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromS64],
invocation_inputs: [InterfaceValue::S64(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
test_executable_instruction!(
test_u8_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::U8(42)],
);
test_executable_instruction!(
test_u8_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U8FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::U8(42)],
);
test_executable_instruction!(
test_u16_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::U16(42)],
);
test_executable_instruction!(
test_u16_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U16FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::U16(42)],
);
test_executable_instruction!(
test_u32_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::U32(42)],
);
test_executable_instruction!(
test_u32_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U32FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::U32(42)],
);
test_executable_instruction!(
test_u64_from_i32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64FromI32],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
stack: [InterfaceValue::U64(42)],
);
test_executable_instruction!(
test_u64_from_i64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::U64FromI64],
invocation_inputs: [InterfaceValue::I64(42)],
instance: Instance::new(),
stack: [InterfaceValue::U64(42)],
);
test_executable_instruction!(
test_i32_from_u8 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromU8],
invocation_inputs: [InterfaceValue::U8(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i32_from_u16 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromU16],
invocation_inputs: [InterfaceValue::U16(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i32_from_u32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromU32],
invocation_inputs: [InterfaceValue::U32(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i32_from_u64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I32FromU64],
invocation_inputs: [InterfaceValue::U64(42)],
instance: Instance::new(),
stack: [InterfaceValue::I32(42)],
);
test_executable_instruction!(
test_i64_from_u8 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromU8],
invocation_inputs: [InterfaceValue::U8(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
test_executable_instruction!(
test_i64_from_u16 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromU16],
invocation_inputs: [InterfaceValue::U16(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
test_executable_instruction!(
test_i64_from_u32 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromU32],
invocation_inputs: [InterfaceValue::U32(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
test_executable_instruction!(
test_i64_from_u64 =
instructions: [Instruction::ArgumentGet { index: 0 }, Instruction::I64FromU64],
invocation_inputs: [InterfaceValue::U64(42)],
instance: Instance::new(),
stack: [InterfaceValue::I64(42)],
);
}

View File

@ -1,384 +0,0 @@
use crate::{
ast::{Type, TypeKind},
errors::{InstructionError, InstructionErrorKind},
interpreter::{
stack::{Stack, Stackable},
Instruction,
},
types::{InterfaceType, RecordType},
values::{FlattenInterfaceValueIterator, InterfaceValue},
vec1::Vec1,
};
use std::collections::VecDeque;
/// Build an `InterfaceValue::Record` based on values on the stack.
///
/// To fill a record, every field `field_1` to `field_n` must get its
/// value from the stack with `value_1` to `value_n`. It is not
/// possible to use `Stack::pop` because the one-pass algorithm does
/// not know exactly the number of values to read from the stack
/// ahead-of-time, so `Stack::pop1` is used. It implies that values
/// are read one after the other from the stack, in a natural reverse
/// order, from `value_n` to `value_1`. Thus, the `values` vector must
/// be filled from the end to the beginning. It is not safely possible
/// to fill the `values` vector with empty values though (so that it
/// is possible to access to last positions). So a `VecDeque` type is
/// used: it is a double-ended queue.
fn record_lift_(
stack: &mut Stack<InterfaceValue>,
record_type: &RecordType,
) -> Result<InterfaceValue, InstructionErrorKind> {
let length = record_type.fields.len();
let mut values = VecDeque::with_capacity(length);
// Iterate over fields in reverse order to match the stack `pop`
// order.
for field in record_type.fields.iter().rev() {
match field {
// The record type tells a record is expected.
InterfaceType::Record(record_type) => {
// Build it recursively.
values.push_front(record_lift_(stack, &record_type)?)
}
// Any other type.
ty => {
let value = stack.pop1().unwrap();
let value_type = (&value).into();
if ty != &value_type {
return Err(InstructionErrorKind::InvalidValueOnTheStack {
expected_type: ty.clone(),
received_type: value_type,
});
}
values.push_front(value)
}
}
}
Ok(InterfaceValue::Record(
Vec1::new(values.into_iter().collect())
.expect("Record must have at least one field, zero given"), // normally unreachable because of the type-checking
))
}
executable_instruction!(
record_lift(type_index: u32, instruction: Instruction) -> _ {
move |runtime| -> _ {
let instance = &runtime.wasm_instance;
let record_type = match instance.wit_type(type_index).ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::TypeIsMissing { type_index },
)
})? {
Type::Record(record_type) => record_type,
Type::Function { .. } => return Err(InstructionError::new(
instruction,
InstructionErrorKind::InvalidTypeKind {
expected_kind: TypeKind::Record,
received_kind: TypeKind::Function
}
)),
};
let record = record_lift_(&mut runtime.stack, &record_type)
.map_err(|k| InstructionError::new(instruction, k))?;
runtime.stack.push(record);
Ok(())
}
}
);
executable_instruction!(
record_lower(type_index: u32, instruction: Instruction) -> _ {
move |runtime| -> _ {
let instance = &runtime.wasm_instance;
let record_type = match instance.wit_type(type_index).ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::TypeIsMissing { type_index },
)
})? {
Type::Record(record_type) => record_type,
Type::Function { .. } => return Err(InstructionError::new(
instruction,
InstructionErrorKind::InvalidTypeKind {
expected_kind: TypeKind::Record,
received_kind: TypeKind::Function
}
)),
};
match runtime.stack.pop1() {
Some(InterfaceValue::Record(record_values)) if record_type == &(&*record_values).into() => {
let values = FlattenInterfaceValueIterator::new(&record_values);
for value in values {
runtime.stack.push(value.clone());
}
Ok(())
},
Some(value) => Err(InstructionError::new(
instruction,
InstructionErrorKind::InvalidValueOnTheStack {
expected_type: InterfaceType::Record(record_type.clone()),
received_type: (&value).into(),
}
)),
None => Err(InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall { needed: 1 },
)),
}
}
}
);
#[cfg(test)]
mod tests {
use super::*;
test_executable_instruction!(
test_record_lift =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 2 },
Instruction::ArgumentGet { index: 3 },
Instruction::RecordLift { type_index: 0 },
],
invocation_inputs: [
InterfaceValue::I32(1),
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F32(2.),
InterfaceValue::I64(3),
],
instance: Instance::new(),
stack: [InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::Record(vec1![
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F32(2.),
]),
InterfaceValue::I64(3),
])],
);
#[cfg(feature = "serde")]
#[test]
#[allow(non_snake_case, unused)]
fn test_record_lift__to_rust_struct() {
use crate::{
interpreter::{
instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
stack::Stackable,
Instruction, Interpreter,
},
types::InterfaceType,
values::{from_interface_values, InterfaceValue},
};
use serde::Deserialize;
use std::{cell::Cell, collections::HashMap, convert::TryInto};
let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> = (&vec![
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 2 },
Instruction::ArgumentGet { index: 3 },
Instruction::RecordLift { type_index: 0 },
])
.try_into()
.unwrap();
let invocation_inputs = vec![
InterfaceValue::I32(1),
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F32(2.),
InterfaceValue::I64(3),
];
let mut instance = Instance::new();
let run = interpreter.run(&invocation_inputs, &mut instance);
assert!(run.is_ok());
let stack = run.unwrap();
#[derive(Deserialize, Debug, PartialEq)]
struct S {
a: String,
b: f32,
}
#[derive(Deserialize, Debug, PartialEq)]
struct T {
x: i32,
s: S,
y: i64,
}
let record: T = from_interface_values(stack.as_slice()).unwrap();
assert_eq!(
record,
T {
x: 1,
s: S {
a: "Hello".to_string(),
b: 2.,
},
y: 3,
}
);
}
test_executable_instruction!(
test_record_lift__one_dimension =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::RecordLift { type_index: 1 },
],
invocation_inputs: [
InterfaceValue::I32(1),
InterfaceValue::I32(2),
],
instance: {
let mut instance = Instance::new();
instance.wit_types.push(
Type::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::I32],
})
);
instance
},
stack: [InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::I32(2),
])],
);
test_executable_instruction!(
test_record_lift__type_is_missing =
instructions: [
Instruction::RecordLift { type_index: 0 },
],
invocation_inputs: [],
instance: Default::default(),
error: r#"`record.lift 0` the type `0` doesn't exist"#,
);
test_executable_instruction!(
test_record_lift__invalid_value_on_the_stack =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::ArgumentGet { index: 2 },
Instruction::ArgumentGet { index: 3 },
Instruction::RecordLift { type_index: 0 },
],
invocation_inputs: [
InterfaceValue::I32(1),
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F64(2.),
// ^^^ F32 is expected
InterfaceValue::I64(3),
],
instance: Instance::new(),
error: r#"`record.lift 0` read a value of type `F64` from the stack, but the type `F32` was expected"#,
);
test_executable_instruction!(
test_record_lower =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::RecordLower { type_index: 0 },
],
invocation_inputs: [
InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::Record(vec1![
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F32(2.),
]),
InterfaceValue::I64(3),
])
],
instance: Instance::new(),
stack: [
InterfaceValue::I32(1),
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F32(2.),
InterfaceValue::I64(3),
],
);
test_executable_instruction!(
test_record__roundtrip =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::RecordLower { type_index: 0 },
Instruction::RecordLift { type_index: 0 },
],
invocation_inputs: [
InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::Record(vec1![
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F32(2.),
]),
InterfaceValue::I64(3),
])
],
instance: Instance::new(),
stack: [
InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::Record(vec1![
InterfaceValue::String("Hello".to_string()),
InterfaceValue::F32(2.),
]),
InterfaceValue::I64(3),
])
],
);
test_executable_instruction!(
test_record_lower__invalid_value_on_the_stack =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::RecordLower { type_index: 0 },
],
invocation_inputs: [
InterfaceValue::I32(1),
],
instance: Instance::new(),
error: r#"`record.lower 0` read a value of type `I32` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#,
);
test_executable_instruction!(
test_record_lower__invalid_value_on_the_stack__different_record_type =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::RecordLower { type_index: 0 },
],
invocation_inputs: [
InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::Record(vec1![
InterfaceValue::String("Hello".to_string()),
]),
InterfaceValue::I64(3),
])
],
instance: Instance::new(),
error: r#"`record.lower 0` read a value of type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String] }), I64] })` from the stack, but the type `Record(RecordType { fields: [I32, Record(RecordType { fields: [String, F32] }), I64] })` was expected"#,
);
}

View File

@ -1,354 +0,0 @@
use super::to_native;
use crate::{
errors::{InstructionError, InstructionErrorKind},
interpreter::Instruction,
types::InterfaceType,
values::InterfaceValue,
};
use std::{cell::Cell, convert::TryInto};
executable_instruction!(
string_lift_memory(instruction: Instruction) -> _ {
move |runtime| -> _ {
let inputs = runtime.stack.pop(2).ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall { needed: 2 },
)
})?;
let memory_index: u32 = 0;
let memory = runtime
.wasm_instance
.memory(memory_index as usize)
.ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::MemoryIsMissing { memory_index },
)
})?;
let pointer: usize = to_native::<i32>(&inputs[0], instruction)?
.try_into()
.map_err(|e| (e, "pointer").into())
.map_err(|k| InstructionError::new(instruction, k))?;
let length: usize = to_native::<i32>(&inputs[1], instruction)?
.try_into()
.map_err(|e| (e, "length").into())
.map_err(|k| InstructionError::new(instruction, k))?;
let memory_view = memory.view();
if length == 0 {
runtime.stack.push(InterfaceValue::String("".into()));
return Ok(())
}
if memory_view.len() <= pointer + length - 1 {
return Err(InstructionError::new(
instruction,
InstructionErrorKind::MemoryOutOfBoundsAccess {
index: pointer + length,
length: memory_view.len(),
},
));
}
let data: Vec<u8> = (&memory_view[pointer..=pointer + length - 1])
.iter()
.map(Cell::get)
.collect();
let string = String::from_utf8(data)
.map_err(|error| InstructionError::new(instruction, InstructionErrorKind::String(error)))?;
runtime.stack.push(InterfaceValue::String(string));
Ok(())
}
}
);
executable_instruction!(
string_lower_memory(instruction: Instruction) -> _ {
move |runtime| -> _ {
let inputs = runtime.stack.pop(2).ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall { needed: 2 },
)
})?;
let string_pointer: usize = to_native::<i32>(&inputs[0], instruction)?
.try_into()
.map_err(|e| (e, "pointer").into())
.map_err(|k| InstructionError::new(instruction, k))?;
let string: String = to_native(&inputs[1], instruction)?;
let string_bytes = string.as_bytes();
let string_length: i32 = string_bytes.len().try_into().map_err(|_| {
InstructionError::new(
instruction,
InstructionErrorKind::NegativeValue { subject: "string_length" },
)
})?;
let instance = &mut runtime.wasm_instance;
let memory_index: u32 = 0;
let memory_view = instance
.memory(memory_index as usize)
.ok_or_else(|| {
InstructionError::new(
instruction,
InstructionErrorKind::MemoryIsMissing { memory_index },
)
})?
.view();
for (nth, byte) in string_bytes.iter().enumerate() {
memory_view[string_pointer as usize + nth].set(*byte);
}
runtime.stack.push(InterfaceValue::I32(string_pointer as i32));
runtime.stack.push(InterfaceValue::I32(string_length));
Ok(())
}
}
);
executable_instruction!(
string_size(instruction: Instruction) -> _ {
move |runtime| -> _ {
match runtime.stack.pop1() {
Some(InterfaceValue::String(string)) => {
let length = string.len() as i32;
runtime.stack.push(InterfaceValue::I32(length));
Ok(())
},
Some(value) => Err(InstructionError::new(
instruction,
InstructionErrorKind::InvalidValueOnTheStack {
expected_type: InterfaceType::String,
received_type: (&value).into(),
},
)),
None => Err(InstructionError::new(
instruction,
InstructionErrorKind::StackIsTooSmall { needed: 1 },
)),
}
}
}
);
#[cfg(test)]
mod tests {
test_executable_instruction!(
test_string_lift_memory =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::StringLiftMemory,
],
invocation_inputs: [
InterfaceValue::I32(0),
// ^^^^^^ pointer
InterfaceValue::I32(13),
// ^^^^^^^ length
],
instance: Instance {
memory: Memory::new("Hello, World!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
..Default::default()
},
stack: [InterfaceValue::String("Hello, World!".into())],
);
test_executable_instruction!(
test_string_lift_memory__empty_string =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::StringLiftMemory,
],
invocation_inputs: [
InterfaceValue::I32(0),
InterfaceValue::I32(0),
],
instance: Instance {
memory: Memory::new(vec![]),
..Default::default()
},
stack: [InterfaceValue::String("".into())],
);
test_executable_instruction!(
test_string_lift_memory__negative_pointer =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::StringLiftMemory,
],
invocation_inputs: [
InterfaceValue::I32(-42),
InterfaceValue::I32(13),
],
instance: Instance {
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
..Default::default()
},
error: r#"`string.lift_memory` attempted to convert `pointer` but it appears to be a negative value"#,
);
test_executable_instruction!(
test_string_lift_memory__negative_length =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::StringLiftMemory,
],
invocation_inputs: [
InterfaceValue::I32(0),
InterfaceValue::I32(-1),
],
instance: Instance {
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
..Default::default()
},
error: r#"`string.lift_memory` attempted to convert `length` but it appears to be a negative value"#,
);
test_executable_instruction!(
test_string_lift_memory__read_out_of_memory =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::StringLiftMemory,
],
invocation_inputs: [
InterfaceValue::I32(0),
// ^^^^^^ pointer
InterfaceValue::I32(13),
// ^^^^^^^ length is too long
],
instance: Instance {
memory: Memory::new("Hello!".as_bytes().iter().map(|u| Cell::new(*u)).collect()),
..Default::default()
},
error: r#"`string.lift_memory` read out of the memory bounds (index 13 > memory length 6)"#,
);
test_executable_instruction!(
test_string_lift_memory__invalid_encoding =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::ArgumentGet { index: 1 },
Instruction::StringLiftMemory,
],
invocation_inputs: [
InterfaceValue::I32(0),
// ^^^^^^ pointer
InterfaceValue::I32(4),
// ^^^^^^ length is too long
],
instance: Instance {
memory: Memory::new(vec![0, 159, 146, 150].iter().map(|b| Cell::new(*b)).collect::<Vec<Cell<u8>>>()),
..Default::default()
},
error: r#"`string.lift_memory` invalid utf-8 sequence of 1 bytes from index 1"#,
);
test_executable_instruction!(
test_string_lift_memory__stack_is_too_small =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::StringLiftMemory,
// ^^^^^^^^^^^^^^^^ `string.lift_memory` expects 2 values on the stack, only one is present.
],
invocation_inputs: [
InterfaceValue::I32(0),
InterfaceValue::I32(13),
],
instance: Instance::new(),
error: r#"`string.lift_memory` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#,
);
test_executable_instruction!(
test_string_lower_memory =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::StringSize,
Instruction::CallCore { function_index: 43 },
Instruction::ArgumentGet { index: 0 },
Instruction::StringLowerMemory,
],
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
instance: Instance::new(),
stack: [
InterfaceValue::I32(0),
// ^^^^^^ pointer
InterfaceValue::I32(13),
// ^^^^^^^ length
]
);
test_executable_instruction!(
test_string__roundtrip =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::StringSize,
Instruction::CallCore { function_index: 43 },
Instruction::ArgumentGet { index: 0 },
Instruction::StringLowerMemory,
Instruction::StringLiftMemory,
],
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
instance: Instance::new(),
stack: [InterfaceValue::String("Hello, World!".into())],
);
test_executable_instruction!(
test_string_lower_memory__stack_is_too_small =
instructions: [
Instruction::StringLowerMemory,
],
invocation_inputs: [],
instance: Instance::new(),
error: r#"`string.lower_memory` needed to read `2` value(s) from the stack, but it doesn't contain enough data"#,
);
test_executable_instruction!(
test_string_size =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::StringSize,
],
invocation_inputs: [InterfaceValue::String("Hello, World!".into())],
instance: Instance::new(),
stack: [InterfaceValue::I32(13)],
);
test_executable_instruction!(
test_string_size__stack_is_too_small =
instructions: [
Instruction::StringSize,
],
invocation_inputs: [],
instance: Instance::new(),
error: r#"`string.size` needed to read `1` value(s) from the stack, but it doesn't contain enough data"#,
);
test_executable_instruction!(
test_string_size__invalid_value_on_the_stack =
instructions: [
Instruction::ArgumentGet { index: 0 },
Instruction::StringSize,
],
invocation_inputs: [InterfaceValue::I32(42)],
instance: Instance::new(),
error: r#"`string.size` read a value of type `I32` from the stack, but the type `String` was expected"#,
);
}

View File

@ -1,254 +0,0 @@
//! A stack-based interpreter to execute instructions of WIT adapters.
mod instructions;
pub mod stack;
pub mod wasm;
use crate::{
errors::{InstructionResult, InterpreterResult},
values::InterfaceValue,
};
pub use instructions::Instruction;
use stack::Stack;
use std::{convert::TryFrom, marker::PhantomData};
/// Represents the `Runtime`, which is used by an adapter to execute
/// its instructions.
pub(crate) struct Runtime<'invocation, 'instance, Instance, Export, LocalImport, Memory, MemoryView>
where
Export: wasm::structures::Export + 'instance,
LocalImport: wasm::structures::LocalImport + 'instance,
Memory: wasm::structures::Memory<MemoryView> + 'instance,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView> + 'instance,
{
/// The invocation inputs are all the arguments received by an
/// adapter.
invocation_inputs: &'invocation [InterfaceValue],
/// Each runtime (so adapter) has its own stack instance.
stack: Stack<InterfaceValue>,
/// The WebAssembly module instance. It is used by adapter's
/// instructions.
wasm_instance: &'instance mut Instance,
/// Phantom data.
_phantom: PhantomData<(Export, LocalImport, Memory, MemoryView)>,
}
/// Type alias for an executable instruction. It's an implementation
/// details, but an instruction is a boxed closure instance.
pub(crate) type ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView> = Box<
dyn Fn(
&mut Runtime<Instance, Export, LocalImport, Memory, MemoryView>,
) -> InstructionResult<()>,
>;
/// An interpreter is the central piece of this crate. It is a set of
/// executable instructions. Each instruction takes the runtime as
/// argument. The runtime holds the invocation inputs, [the
/// stack](stack), and [the WebAssembly instance](wasm).
///
/// When the interpreter executes the instructions, each of them can
/// query the WebAssembly instance, operates on the stack, or reads
/// the invocation inputs. At the end of the execution, the stack
/// supposedly contains a result. Since an interpreter is used by a
/// WIT adapter to execute its instructions, the result on the stack
/// is the result of the adapter.
///
/// # Example
///
/// ```rust,ignore
/// use std::{cell::Cell, collections::HashMap, convert::TryInto};
/// use wasmer_interface_types::{
/// interpreter::{
/// instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
/// // ^^^^^^^^^^^^ This is private and for testing purposes only.
/// // It is basically a fake WebAssembly runtime.
/// stack::Stackable,
/// Instruction, Interpreter,
/// },
/// types::InterfaceType,
/// values::InterfaceValue,
/// };
///
/// // 1. Creates an interpreter from a set of instructions. They will
/// // be transformed into executable instructions.
/// let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> = (&vec![
/// Instruction::ArgumentGet { index: 1 },
/// Instruction::ArgumentGet { index: 0 },
/// Instruction::CallCore { function_index: 42 },
/// ])
/// .try_into()
/// .unwrap();
///
/// // 2. Defines the arguments of the adapter.
/// let invocation_inputs = vec![InterfaceValue::I32(3), InterfaceValue::I32(4)];
///
/// // 3. Creates a WebAssembly instance.
/// let mut instance = Instance {
/// // 3.1. Defines one function: `fn sum(a: i32, b: i32) -> i32 { a + b }`.
/// locals_or_imports: {
/// let mut hashmap = HashMap::new();
/// hashmap.insert(
/// 42,
/// LocalImport {
/// // Defines the argument types of the function.
/// inputs: vec![InterfaceType::I32, InterfaceType::I32],
///
/// // Defines the result types.
/// outputs: vec![InterfaceType::I32],
///
/// // Defines the function implementation.
/// function: |arguments: &[InterfaceValue]| {
/// let a: i32 = (&arguments[0]).try_into().unwrap();
/// let b: i32 = (&arguments[1]).try_into().unwrap();
///
/// Ok(vec![InterfaceValue::I32(a + b)])
/// },
/// },
/// );
/// },
/// ..Default::default()
/// };
///
/// // 4. Executes the instructions.
/// let run = interpreter.run(&invocation_inputs, &mut instance);
///
/// assert!(run.is_ok());
///
/// let stack = run.unwrap();
///
/// // 5. Read the stack to get the result.
/// assert_eq!(stack.as_slice(), &[InterfaceValue::I32(7)]);
/// ```
pub struct Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
where
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport,
Memory: wasm::structures::Memory<MemoryView>,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
executable_instructions:
Vec<ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>>,
}
impl<Instance, Export, LocalImport, Memory, MemoryView>
Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
where
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport,
Memory: wasm::structures::Memory<MemoryView>,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
fn iter(
&self,
) -> impl Iterator<
Item = &ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>,
> + '_ {
self.executable_instructions.iter()
}
/// Runs the interpreter, such as:
/// 1. Create a fresh stack,
/// 2. Create a fresh stack,
/// 3. Execute the instructions one after the other, and
/// returns the stack.
pub fn run(
&self,
invocation_inputs: &[InterfaceValue],
wasm_instance: &mut Instance,
) -> InterpreterResult<Stack<InterfaceValue>> {
let mut runtime = Runtime {
invocation_inputs,
stack: Stack::new(),
wasm_instance,
_phantom: PhantomData,
};
for executable_instruction in self.iter() {
executable_instruction(&mut runtime)?;
}
Ok(runtime.stack)
}
}
/// Transforms a `Vec<Instruction>` into an `Interpreter`.
impl<Instance, Export, LocalImport, Memory, MemoryView> TryFrom<&Vec<Instruction>>
for Interpreter<Instance, Export, LocalImport, Memory, MemoryView>
where
Export: wasm::structures::Export,
LocalImport: wasm::structures::LocalImport,
Memory: wasm::structures::Memory<MemoryView>,
MemoryView: wasm::structures::MemoryView,
Instance: wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
type Error = ();
fn try_from(instructions: &Vec<Instruction>) -> Result<Self, Self::Error> {
let executable_instructions = instructions
.iter()
.map(|instruction| match instruction {
Instruction::ArgumentGet { index } => {
instructions::argument_get(*index, *instruction)
}
Instruction::CallCore { function_index } => {
instructions::call_core(*function_index, *instruction)
}
Instruction::S8FromI32 => instructions::s8_from_i32(*instruction),
Instruction::S8FromI64 => instructions::s8_from_i64(*instruction),
Instruction::S16FromI32 => instructions::s16_from_i32(*instruction),
Instruction::S16FromI64 => instructions::s16_from_i64(*instruction),
Instruction::S32FromI32 => instructions::s32_from_i32(*instruction),
Instruction::S32FromI64 => instructions::s32_from_i64(*instruction),
Instruction::S64FromI32 => instructions::s64_from_i32(*instruction),
Instruction::S64FromI64 => instructions::s64_from_i64(*instruction),
Instruction::I32FromS8 => instructions::i32_from_s8(*instruction),
Instruction::I32FromS16 => instructions::i32_from_s16(*instruction),
Instruction::I32FromS32 => instructions::i32_from_s32(*instruction),
Instruction::I32FromS64 => instructions::i32_from_s64(*instruction),
Instruction::I64FromS8 => instructions::i64_from_s8(*instruction),
Instruction::I64FromS16 => instructions::i64_from_s16(*instruction),
Instruction::I64FromS32 => instructions::i64_from_s32(*instruction),
Instruction::I64FromS64 => instructions::i64_from_s64(*instruction),
Instruction::U8FromI32 => instructions::u8_from_i32(*instruction),
Instruction::U8FromI64 => instructions::u8_from_i64(*instruction),
Instruction::U16FromI32 => instructions::u16_from_i32(*instruction),
Instruction::U16FromI64 => instructions::u16_from_i64(*instruction),
Instruction::U32FromI32 => instructions::u32_from_i32(*instruction),
Instruction::U32FromI64 => instructions::u32_from_i64(*instruction),
Instruction::U64FromI32 => instructions::u64_from_i32(*instruction),
Instruction::U64FromI64 => instructions::u64_from_i64(*instruction),
Instruction::I32FromU8 => instructions::i32_from_u8(*instruction),
Instruction::I32FromU16 => instructions::i32_from_u16(*instruction),
Instruction::I32FromU32 => instructions::i32_from_u32(*instruction),
Instruction::I32FromU64 => instructions::i32_from_u64(*instruction),
Instruction::I64FromU8 => instructions::i64_from_u8(*instruction),
Instruction::I64FromU16 => instructions::i64_from_u16(*instruction),
Instruction::I64FromU32 => instructions::i64_from_u32(*instruction),
Instruction::I64FromU64 => instructions::i64_from_u64(*instruction),
Instruction::StringLiftMemory => instructions::string_lift_memory(*instruction),
Instruction::StringLowerMemory => instructions::string_lower_memory(*instruction),
Instruction::StringSize => instructions::string_size(*instruction),
Instruction::RecordLift { type_index } => {
instructions::record_lift(*type_index, *instruction)
}
Instruction::RecordLower { type_index } => {
instructions::record_lower(*type_index, *instruction)
}
})
.collect();
Ok(Interpreter {
executable_instructions,
})
}
}

View File

@ -1,150 +0,0 @@
//! A very light and generic stack implementation, exposing only the
//! operations required by the interpreter.
/// The `Stackable` trait represents a small basic set of operations
/// required by the interpreter.
pub trait Stackable {
/// The kind of item the stack holds.
type Item;
/// Checks whether the stack is empty.
fn is_empty(&self) -> bool;
/// Extracts a slice containing the entire stack.
fn as_slice(&self) -> &[Self::Item];
/// Appends one item to the end of the stack.
fn push(&mut self, item: Self::Item);
/// Removes the last item of the stack and returns it, `None` if
/// the stack is empty.
fn pop1(&mut self) -> Option<Self::Item>;
/// Removes `n` elements from the end of the stack, `None` if the
/// stack doesn't contain enough elements.
/// Returned items are in reverse order: the last element comes
/// last in the list.
fn pop(&mut self, n: usize) -> Option<Vec<Self::Item>>;
/// Peek the last item of the stack and returns a reference to it,
/// `None` if the stack is empty.
fn peek1(&self) -> Option<&Self::Item>;
}
/// A stack implementation of the `Stackable` trait, based on a vector.
#[derive(Debug, Default)]
pub struct Stack<T>
where
T: Default + Clone,
{
/// Inner structure holding the items.
inner: Vec<T>,
}
impl<T> Stack<T>
where
T: Default + Clone,
{
/// Creates a new empty stack.
pub fn new() -> Self {
Self {
..Default::default()
}
}
}
impl<T> Stackable for Stack<T>
where
T: Default + Clone,
{
type Item = T;
fn is_empty(&self) -> bool {
self.inner.is_empty()
}
fn as_slice(&self) -> &[Self::Item] {
self.inner.as_slice()
}
fn push(&mut self, item: Self::Item) {
self.inner.push(item);
}
fn pop1(&mut self) -> Option<Self::Item> {
self.inner.pop()
}
fn pop(&mut self, n: usize) -> Option<Vec<Self::Item>> {
if self.inner.len() < n {
None
} else {
let items = self
.inner
.drain(self.inner.len() - n..)
.collect::<Vec<Self::Item>>();
assert!(items.len() == n);
Some(items)
}
}
fn peek1(&self) -> Option<&Self::Item> {
if self.inner.is_empty() {
None
} else {
Some(&self.inner[self.inner.len() - 1])
}
}
}
#[cfg(test)]
mod tests {
use super::{Stack, Stackable};
#[test]
fn test_is_empty() {
let mut stack = Stack::new();
assert_eq!(stack.is_empty(), true);
stack.push(1);
assert_eq!(stack.is_empty(), false);
}
#[test]
fn test_push_pop1() {
let mut stack = Stack::new();
stack.push(1);
assert_eq!(stack.pop1(), Some(1));
assert_eq!(stack.is_empty(), true);
}
#[test]
fn test_pop() {
let mut stack = Stack::new();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
stack.push(6);
assert_eq!(stack.pop(1), Some(vec![6]));
assert_eq!(stack.pop(2), Some(vec![4, 5]));
assert_eq!(stack.pop(4), None); // not enough items
assert_eq!(stack.pop(3), Some(vec![1, 2, 3]));
assert_eq!(stack.pop1(), None);
assert_eq!(stack.is_empty(), true);
}
#[test]
fn test_peek1() {
let mut stack = Stack::new();
stack.push(1);
stack.push(2);
assert_eq!(stack.peek1(), Some(&2));
}
}

View File

@ -1,5 +0,0 @@
//! An hypothetic WebAssembly runtime, represented as a set of enums,
//! types, and traits —basically this is the part a runtime should
//! take a look to use the `wasmer-interface-types` crate—.
pub mod structures;

View File

@ -1,164 +0,0 @@
#![allow(missing_docs)]
use crate::{ast, types::InterfaceType, values::InterfaceValue};
use std::{cell::Cell, ops::Deref};
pub trait TypedIndex: Copy + Clone {
fn new(index: usize) -> Self;
fn index(&self) -> usize;
}
macro_rules! typed_index {
($type:ident) => {
#[derive(Copy, Clone)]
pub struct $type(usize);
impl TypedIndex for $type {
fn new(index: usize) -> Self {
Self(index)
}
fn index(&self) -> usize {
self.0
}
}
};
}
typed_index!(FunctionIndex);
typed_index!(LocalFunctionIndex);
typed_index!(ImportFunctionIndex);
pub trait LocalImportIndex {
type Local: TypedIndex;
type Import: TypedIndex;
}
impl LocalImportIndex for FunctionIndex {
type Local = LocalFunctionIndex;
type Import = ImportFunctionIndex;
}
pub trait Export {
fn inputs_cardinality(&self) -> usize;
fn outputs_cardinality(&self) -> usize;
fn inputs(&self) -> &[InterfaceType];
fn outputs(&self) -> &[InterfaceType];
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>;
}
pub trait LocalImport {
fn inputs_cardinality(&self) -> usize;
fn outputs_cardinality(&self) -> usize;
fn inputs(&self) -> &[InterfaceType];
fn outputs(&self) -> &[InterfaceType];
fn call(&self, arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()>;
}
pub trait MemoryView: Deref<Target = [Cell<u8>]> {}
pub trait Memory<View>
where
View: MemoryView,
{
fn view(&self) -> View;
}
pub trait Instance<E, LI, M, MV>
where
E: Export,
LI: LocalImport,
M: Memory<MV>,
MV: MemoryView,
{
fn export(&self, export_name: &str) -> Option<&E>;
fn local_or_import<I: TypedIndex + LocalImportIndex>(&mut self, index: I) -> Option<&LI>;
fn memory(&self, index: usize) -> Option<&M>;
fn wit_type(&self, index: u32) -> Option<&ast::Type>;
}
impl Export for () {
fn inputs_cardinality(&self) -> usize {
0
}
fn outputs_cardinality(&self) -> usize {
0
}
fn inputs(&self) -> &[InterfaceType] {
&[]
}
fn outputs(&self) -> &[InterfaceType] {
&[]
}
fn call(&self, _arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
Err(())
}
}
impl LocalImport for () {
fn inputs_cardinality(&self) -> usize {
0
}
fn outputs_cardinality(&self) -> usize {
0
}
fn inputs(&self) -> &[InterfaceType] {
&[]
}
fn outputs(&self) -> &[InterfaceType] {
&[]
}
fn call(&self, _arguments: &[InterfaceValue]) -> Result<Vec<InterfaceValue>, ()> {
Err(())
}
}
pub(crate) struct EmptyMemoryView;
impl MemoryView for EmptyMemoryView {}
impl Deref for EmptyMemoryView {
type Target = [Cell<u8>];
fn deref(&self) -> &Self::Target {
&[]
}
}
impl Memory<EmptyMemoryView> for () {
fn view(&self) -> EmptyMemoryView {
EmptyMemoryView
}
}
impl<E, LI, M, MV> Instance<E, LI, M, MV> for ()
where
E: Export,
LI: LocalImport,
M: Memory<MV>,
MV: MemoryView,
{
fn export(&self, _export_name: &str) -> Option<&E> {
None
}
fn memory(&self, _: usize) -> Option<&M> {
None
}
fn local_or_import<I: TypedIndex + LocalImportIndex>(&mut self, _index: I) -> Option<&LI> {
None
}
fn wit_type(&self, _index: u32) -> Option<&ast::Type> {
None
}
}

View File

@ -1,67 +0,0 @@
//! This crate contains an implementation of [WebAssembly Interface
//! Types][wit] (abbreviated WIT). It is composed of 5 parts:
//!
//! 1. [Types] and [Values]: To represent the WIT types and values
//! representations,
//! 2. [AST]: To represent the WIT language as a tree
//! (which is not really abstract). This is the central
//! representation of the language.
//! 3. [Decoders](decoders): To read the [AST] from a particular data
//! representation; for instance, [`decoders::binary::parse`] reads
//! the [AST] from a binary.
//! 4. [Encoders](encoders): To write the [AST](ast) into a particular
//! format; for instance, [`encoders::wat`] writes the [AST] into a
//! string representing WIT with its textual format.
//! 5. [Interpreter](interpreter): WIT defines a concept called
//! Adapters. An adapter contains a set of [instructions]. So, in
//! more details, this module contains:
//! * [A very light and generic stack
//! implementation](interpreter::stack), exposing only the
//! operations required by the interpreter,
//! * [A stack-based interpreter](interpreter::Interpreter),
//! defined by:
//! * A compiler that transforms a set of instructions into a
//! set of executable instructions,
//! * A stack,
//! * A runtime that holds the “invocation inputs” (arguments
//! of the interpreter), the stack, and the WebAssembly
//! instance (which holds the exports, the imports, the
//! memories, the tables etc.),
//! * [An hypothetic WebAssembly runtime](interpreter::wasm),
//! represented as a set of enums, types, and traits —basically
//! this is the part a runtime should take a look to use the
//! `wasmer-interface-types` crate—.
//!
//! [wit]: https://github.com/WebAssembly/interface-types
//! [Types]: types
//! [Values]: values
//! [AST]: ast
//! [instructions]: interpreter::Instruction
#![deny(
dead_code,
intra_doc_link_resolution_failure,
missing_docs,
nonstandard_style,
unreachable_patterns,
unused_imports,
unused_mut,
unused_unsafe,
unused_variables
)]
#![forbid(unsafe_code)]
#![doc(html_favicon_url = "https://wasmer.io/static/icons/favicon.ico")]
#![doc(html_logo_url = "https://github.com/wasmerio.png")]
pub mod ast;
pub mod types;
#[macro_use]
mod macros;
pub mod decoders;
pub mod encoders;
pub mod errors;
pub mod interpreter;
#[cfg(feature = "serde")]
mod serde;
pub mod values;
pub mod vec1;

View File

@ -1,168 +0,0 @@
//! Collection of helpful macros.
/// This macro creates a `Vec1` by checking at compile-time that its
/// invariant holds.
#[macro_export]
macro_rules! vec1 {
($item:expr; 0) => {
compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
};
() => {
compile_error!("Cannot create an empty `Vec1`, it violates its invariant.")
};
($item:expr; $length:expr) => {
{
crate::vec1::Vec1::new(vec![$item; $length]).unwrap()
}
};
($($item:expr),+ $(,)?) => {
{
crate::vec1::Vec1::new(vec![$($item),*]).unwrap()
}
};
}
/// This macro runs a parser, extracts the next input and the parser
/// output, and positions the next input on `$input`.
macro_rules! consume {
(($input:ident, $parser_output:ident) = $parser_expression:expr) => {
let (next_input, $parser_output) = $parser_expression;
$input = next_input;
};
(($input:ident, mut $parser_output:ident) = $parser_expression:expr) => {
let (next_input, mut $parser_output) = $parser_expression;
$input = next_input;
};
}
/// This macro creates an executable instruction for the interpreter.
///
/// # Example
///
/// The following example creates a `foo` executable instruction,
/// which takes 2 arguments (`x` and `y`), and does something
/// mysterious by using the `interpreter::Runtime` API.
///
/// ```rust,ignore
/// executable_instruction!(
/// foo(x: u64, y: u64, instruction_name: String) -> _ {
/// // ^ output type is purposely blank
/// // ^^^^^^^^^^^^^^^^ the instruction name, for debugging purposes
/// // ^ the `y` argument
/// // ^ the `x` argument
///
/// // an executable instruction is a closure that takes a `Runtime` instance
/// move |runtime| -> _ {
/// // Do something.
///
/// Ok(())
/// }
/// );
/// ```
///
/// Check the existing executable instruction to get more examples.
macro_rules! executable_instruction {
($name:ident ( $($argument_name:ident: $argument_type:ty),* ) -> _ $implementation:block ) => {
pub(crate) fn $name<Instance, Export, LocalImport, Memory, MemoryView>(
$($argument_name: $argument_type),*
) -> crate::interpreter::ExecutableInstruction<Instance, Export, LocalImport, Memory, MemoryView>
where
Export: crate::interpreter::wasm::structures::Export,
LocalImport: crate::interpreter::wasm::structures::LocalImport,
Memory: crate::interpreter::wasm::structures::Memory<MemoryView>,
MemoryView: crate::interpreter::wasm::structures::MemoryView,
Instance: crate::interpreter::wasm::structures::Instance<Export, LocalImport, Memory, MemoryView>,
{
#[allow(unused_imports)]
use crate::interpreter::{stack::Stackable};
Box::new($implementation)
}
};
}
#[cfg(test)]
macro_rules! test_executable_instruction {
(
$test_name:ident =
instructions: [ $($instructions:expr),* $(,)* ],
invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ],
instance: $instance:expr,
stack: [ $($stack:expr),* $(,)* ]
$(,)*
) => {
#[test]
#[allow(non_snake_case, unused)]
fn $test_name() {
use crate::{
interpreter::{
instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
stack::Stackable,
Instruction, Interpreter,
},
types::InterfaceType,
values::InterfaceValue,
};
use std::{cell::Cell, collections::HashMap, convert::TryInto};
let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> =
(&vec![$($instructions),*]).try_into().unwrap();
let invocation_inputs = vec![$($invocation_inputs),*];
let mut instance = $instance;
let run = interpreter.run(&invocation_inputs, &mut instance);
let err = match &run {
Ok(_) => "".to_string(),
Err(e) => e.to_string(),
};
assert!(run.is_ok(), err);
let stack = run.unwrap();
assert_eq!(stack.as_slice(), &[$($stack),*]);
}
};
(
$test_name:ident =
instructions: [ $($instructions:expr),* $(,)* ],
invocation_inputs: [ $($invocation_inputs:expr),* $(,)* ],
instance: $instance:expr,
error: $error:expr
$(,)*
) => {
#[test]
#[allow(non_snake_case, unused)]
fn $test_name() {
use crate::{
interpreter::{
instructions::tests::{Export, Instance, LocalImport, Memory, MemoryView},
stack::Stackable,
Instruction, Interpreter,
},
types::InterfaceType,
values::InterfaceValue,
};
use std::{cell::Cell, collections::HashMap, convert::TryInto};
let interpreter: Interpreter<Instance, Export, LocalImport, Memory, MemoryView> =
(&vec![$($instructions),*]).try_into().unwrap();
let invocation_inputs = vec![$($invocation_inputs),*];
let mut instance = $instance;
let run = interpreter.run(&invocation_inputs, &mut instance);
assert!(run.is_err());
let error = run.unwrap_err().to_string();
assert_eq!(error, String::from($error));
}
};
}

View File

@ -1,590 +0,0 @@
//! Provides a deserializer from WIT values to Rust value.
use crate::{
types::InterfaceType,
values::{FlattenInterfaceValueIterator, InterfaceValue},
};
use serde::{de, Deserialize};
use std::{
fmt::{self, Display},
iter::Peekable,
};
/// Deserialize a set of `InterfaceValue`s to a type `T` that
/// implements the `Deserialize` trait.
///
/// This is not a requirement to use WIT, but Serde provides an even
/// nicer API to the user to rebuild its complex types from WIT
/// values.
///
/// # Example
///
/// ```rust
/// use wasmer_interface_types::{
/// values::{InterfaceValue, from_interface_values},
/// vec1::Vec1,
/// };
/// use serde::Deserialize;
///
/// #[derive(Deserialize, Debug, PartialEq)]
/// struct S(i32, i64);
///
/// #[derive(Deserialize, Debug, PartialEq)]
/// struct T<'a> {
/// x: &'a str,
/// s: S,
/// y: f32,
/// };
///
/// let values = vec![InterfaceValue::Record(Vec1::new(vec![
/// InterfaceValue::String("abc".to_string()),
/// InterfaceValue::Record(Vec1::new(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]).unwrap()),
/// InterfaceValue::F32(3.),
/// ]).unwrap())];
/// let t = from_interface_values::<T>(&values).unwrap();
///
/// assert_eq!(
/// t,
/// T {
/// x: "abc",
/// s: S(1, 2),
/// y: 3.,
/// }
/// );
/// ```
pub fn from_interface_values<'a, T>(values: &'a [InterfaceValue]) -> Result<T, DeserializeError>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::new(values);
let result = T::deserialize(&mut deserializer)?;
match deserializer.iterator.peek() {
None => Ok(result),
_ => Err(DeserializeError::InputNotEmpty),
}
}
/// The deserializer. The iterator iterates over `InterfaceValue`s,
/// all flatten, see `FlattenInterfaceValueIterator`.
struct Deserializer<'de> {
iterator: Peekable<FlattenInterfaceValueIterator<'de>>,
}
impl<'de> Deserializer<'de> {
pub fn new(input: &'de [InterfaceValue]) -> Deserializer<'de> {
Deserializer {
iterator: FlattenInterfaceValueIterator::new(input).peekable(),
}
}
}
macro_rules! next {
($method_name:ident, $variant:ident, $type:ty) => {
fn $method_name(&mut self) -> Result<$type, DeserializeError> {
match self.iterator.peek() {
Some(InterfaceValue::$variant(value)) => {
self.iterator.next();
Ok(*value)
}
Some(wrong_value) => Err(DeserializeError::TypeMismatch {
expected_type: InterfaceType::$variant,
received_type: (*wrong_value).into(),
}),
None => Err(DeserializeError::InputEmpty),
}
}
}
}
impl<'de> Deserializer<'de> {
next!(next_s8, S8, i8);
next!(next_s16, S16, i16);
next!(next_s32, S32, i32);
next!(next_s64, S64, i64);
next!(next_u8, U8, u8);
next!(next_u16, U16, u16);
next!(next_u32, U32, u32);
next!(next_u64, U64, u64);
next!(next_f32, F32, f32);
next!(next_f64, F64, f64);
fn next_string(&mut self) -> Result<&'de str, DeserializeError> {
match self.iterator.peek() {
Some(InterfaceValue::String(v)) => {
self.iterator.next();
Ok(v)
}
Some(wrong_value) => Err(DeserializeError::TypeMismatch {
expected_type: InterfaceType::String,
received_type: (*wrong_value).into(),
}),
None => Err(DeserializeError::InputEmpty),
}
}
next!(next_i32, I32, i32);
next!(next_i64, I64, i64);
}
/// Represents an error while deserializing.
#[derive(Clone, Debug, PartialEq)]
pub enum DeserializeError {
/// The input isn't empty, i.e. some values aren't deserialized.
InputNotEmpty,
/// The input is too short!
InputEmpty,
/// The current value hasn't the expected type.
TypeMismatch {
/// The expected type.
expected_type: InterfaceType,
/// The received type.
received_type: InterfaceType,
},
/// Arbitrary message.
Message(String),
}
impl de::Error for DeserializeError {
fn custom<T: Display>(msg: T) -> Self {
Self::Message(msg.to_string())
}
}
impl Display for DeserializeError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InputNotEmpty => write!(formatter, "Unexpected input remaining"),
Self::Message(ref msg) => write!(formatter, "{}", msg),
Self::InputEmpty => write!(formatter, "Unexpected end of input"),
Self::TypeMismatch {
ref expected_type,
ref received_type,
} => write!(
formatter,
"Type mismatch detected, expected `{:?}` but received `{:?}`",
expected_type, received_type
),
}
}
}
impl std::error::Error for DeserializeError {}
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = DeserializeError;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self.iterator.peek() {
Some(InterfaceValue::S8(_)) => self.deserialize_i8(visitor),
Some(InterfaceValue::S16(_)) => self.deserialize_i16(visitor),
Some(InterfaceValue::S32(_)) => self.deserialize_i32(visitor),
Some(InterfaceValue::S64(_)) => self.deserialize_i64(visitor),
Some(InterfaceValue::U8(_)) => self.deserialize_u8(visitor),
Some(InterfaceValue::U16(_)) => self.deserialize_u16(visitor),
Some(InterfaceValue::U32(_)) => self.deserialize_u32(visitor),
Some(InterfaceValue::U64(_)) => self.deserialize_u64(visitor),
Some(InterfaceValue::F32(_)) => self.deserialize_f32(visitor),
Some(InterfaceValue::F64(_)) => self.deserialize_f64(visitor),
Some(InterfaceValue::String(_)) => self.deserialize_string(visitor),
Some(InterfaceValue::I32(_)) => self.deserialize_i32(visitor),
Some(InterfaceValue::I64(_)) => self.deserialize_i64(visitor),
Some(InterfaceValue::Record(_)) => unreachable!("Records should have been flattened."), // already flattened
None => Err(DeserializeError::InputEmpty),
}
}
fn deserialize_bool<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
unimplemented!("`bool` is not supported by WIT for the moment.")
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_i8(self.next_s8()?)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_i16(self.next_s16()?)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// Both `InterfaceValue::S32` and `InterfaceValue::I32`
// represent `i32`.
visitor.visit_i32(self.next_s32().or_else(|_| self.next_i32())?)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// Both `InterfaceValue::S64` and `InterfaceValue::I64`
// represent `i64`.
visitor.visit_i64(self.next_s64().or_else(|_| self.next_i64())?)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_u8(self.next_u8()?)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_u16(self.next_u16()?)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_u32(self.next_u32()?)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_u64(self.next_u64()?)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_f32(self.next_f32()?)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_f64(self.next_f64()?)
}
fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`char` is not supported by WIT for the moment.")
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_borrowed_str(self.next_string()?)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_str(visitor)
}
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`bytes` is not supported by WIT for the moment.")
}
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`bytes` buffer is not supported by WIT for the moment.")
}
fn deserialize_option<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`option` is not supported by WIT for the moment.")
}
fn deserialize_unit<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`unit` is not supported by WIT for the moment.")
}
fn deserialize_unit_struct<V>(
self,
_name: &'static str,
_visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`unit_struct` is not supported by WIT for the moment.")
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_seq<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`seq` is not supported by WIT for the moment.")
}
fn deserialize_tuple<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`tuple` is not supported by WIT for the moment.")
}
fn deserialize_tuple_struct<V>(
mut self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(Sequence::new(&mut self))
}
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`map` is not supported by WIT for the moment.")
}
fn deserialize_struct<V>(
mut self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(Sequence::new(&mut self))
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
_visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`enum` is not supported by WIT for the moment.")
}
fn deserialize_identifier<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`identifier` is not supported by WIT for the moment.");
}
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
todo!("`ignored_any` is not implemented for the moment.")
}
}
struct Sequence<'a, 'de>
where
'de: 'a,
{
de: &'a mut Deserializer<'de>,
}
impl<'a, 'de> Sequence<'a, 'de> {
fn new(de: &'a mut Deserializer<'de>) -> Self {
Sequence { de }
}
}
impl<'de, 'a> de::SeqAccess<'de> for Sequence<'a, 'de> {
type Error = DeserializeError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
if self.de.iterator.peek().is_none() {
return Ok(None);
}
seed.deserialize(&mut *self.de).map(Some)
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! deserialize_value {
($test_name:ident, $variant:ident, $ty:ident, $value:expr) => {
#[test]
#[allow(non_snake_case)]
fn $test_name() {
let input = vec![InterfaceValue::$variant($value)];
let output: $ty = $value;
assert_eq!(from_interface_values::<$ty>(&input).unwrap(), output);
}
};
}
deserialize_value!(test_deserialize_value__s8, S8, i8, 42);
deserialize_value!(test_deserialize_value__s16, S16, i16, 42);
deserialize_value!(test_deserialize_value__s32, S32, i32, 42);
deserialize_value!(test_deserialize_value__s64, S64, i64, 42);
deserialize_value!(test_deserialize_value__u8, U8, u8, 42);
deserialize_value!(test_deserialize_value__u16, U16, u16, 42);
deserialize_value!(test_deserialize_value__u32, U32, u32, 42);
deserialize_value!(test_deserialize_value__u64, U64, u64, 42);
deserialize_value!(test_deserialize_value__f32, F32, f32, 42.);
deserialize_value!(test_deserialize_value__f64, F32, f32, 42.);
deserialize_value!(
test_deserialize_value__string,
String,
String,
"foo".to_string()
);
#[test]
#[allow(non_snake_case)]
fn test_deserialize_value__str() {
let foo = "foo".to_string();
let values = vec![InterfaceValue::String(foo)];
let input: &str = from_interface_values(&values).unwrap();
let output: &str = "foo";
assert_eq!(input, output);
}
deserialize_value!(test_deserialize_value__i32, I32, i32, 42);
deserialize_value!(test_deserialize_value__i64, I64, i64, 42);
#[test]
#[allow(non_snake_case)]
fn test_deserialize_value__newtype_struct() {
#[derive(Deserialize, Debug, PartialEq)]
struct S(i8);
let input = vec![InterfaceValue::Record(vec1![InterfaceValue::S8(42)])];
let output = S(42);
assert_eq!(from_interface_values::<S>(&input).unwrap(), output);
}
#[test]
#[allow(non_snake_case)]
fn test_deserialize_value__tuple_struct() {
#[derive(Deserialize, Debug, PartialEq)]
struct S(i8, f32);
let input = vec![InterfaceValue::Record(vec1![
InterfaceValue::S8(7),
InterfaceValue::F32(42.),
])];
let output = S(7, 42.);
assert_eq!(from_interface_values::<S>(&input).unwrap(), output);
}
#[test]
#[allow(non_snake_case)]
fn test_deserialize_value__struct() {
#[derive(Deserialize, Debug, PartialEq)]
struct S {
x: i8,
y: f32,
}
let input = vec![InterfaceValue::Record(vec1![
InterfaceValue::S8(7),
InterfaceValue::F32(42.),
])];
let output = S { x: 7, y: 42. };
assert_eq!(from_interface_values::<S>(&input).unwrap(), output);
}
#[test]
#[allow(non_snake_case)]
fn test_deserialize_value__struct_nested() {
#[derive(Deserialize, Debug, PartialEq)]
struct Point {
x: i32,
y: i32,
z: i32,
}
#[derive(Deserialize, Debug, PartialEq)]
struct Line {
p1: Point,
p2: Point,
}
let input = vec![InterfaceValue::Record(vec1![
InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::I32(2),
InterfaceValue::I32(3),
]),
InterfaceValue::Record(vec1![
InterfaceValue::I32(4),
InterfaceValue::I32(5),
InterfaceValue::I32(6),
]),
])];
let output = Line {
p1: Point { x: 1, y: 2, z: 3 },
p2: Point { x: 4, y: 5, z: 6 },
};
assert_eq!(from_interface_values::<Line>(&input).unwrap(), output);
}
}

View File

@ -1,6 +0,0 @@
//! Serde is not necessary to use WIT. It only provides a nicer API
//! for the end-user to send or receive its complex types to/from WIT
//! values, like `record` for instance.
pub(crate) mod de;
pub(crate) mod ser;

View File

@ -1,581 +0,0 @@
//! Provides a serializer from Rust value to WIT values.
use crate::{values::InterfaceValue, vec1::Vec1};
use serde::{ser, Serialize};
use std::fmt::{self, Display};
/// Serialize a type `T` that implements the `Serialize` trait to an
/// `InterfaceValue`.
///
/// This is not a requirement to use WIT, but Serde provides an even
/// nicer API to the user to send its complex types to WIT.
///
/// # Example
///
/// ```rust
/// use wasmer_interface_types::{
/// values::{InterfaceValue, to_interface_value},
/// vec1::Vec1,
/// };
/// use serde::Serialize;
///
/// #[derive(Serialize)]
/// struct S(i32, i64);
///
/// #[derive(Serialize)]
/// struct T {
/// x: String,
/// s: S,
/// y: f32,
/// };
///
/// let input = T {
/// x: "abc".to_string(),
/// s: S(1, 2),
/// y: 3.,
/// };
///
/// assert_eq!(
/// to_interface_value(&input).unwrap(),
/// InterfaceValue::Record(Vec1::new(vec![
/// InterfaceValue::String("abc".to_string()),
/// InterfaceValue::Record(Vec1::new(vec![InterfaceValue::I32(1), InterfaceValue::I64(2)]).unwrap()),
/// InterfaceValue::F32(3.),
/// ]).unwrap()),
/// );
/// ```
pub fn to_interface_value<T>(value: &T) -> Result<InterfaceValue, SerializeError>
where
T: Serialize,
{
let mut serializer = Serializer::new();
value.serialize(&mut serializer)?;
if serializer.values.len() != 1 {
Err(SerializeError::TransformationNotFinished)
} else {
let mut first_values = serializer.values.pop().unwrap(); // this `unwrap` is safe because we are sure the length is 1.
if first_values.len() != 1 {
Err(SerializeError::TransformationNotFinished)
} else {
let first_value = first_values.pop().unwrap(); // this `unwrap` is safe because we are sure the length is 1.
Ok(first_value)
}
}
}
/// The serializer.
struct Serializer {
values: Vec<Vec<InterfaceValue>>,
}
impl Serializer {
fn new() -> Self {
Self {
values: vec![vec![]],
}
}
fn last(&mut self) -> &mut Vec<InterfaceValue> {
self.values.last_mut().unwrap()
}
fn push_with_capacity(&mut self, capacity: usize) {
self.values.push(Vec::with_capacity(capacity));
}
fn pop(&mut self) -> Result<Vec<InterfaceValue>, SerializeError> {
// The first `vec` contains the final result. It is forbidden
// to `pop` it as is.
if self.values.len() < 2 {
Err(SerializeError::InternalValuesCorrupted)
} else {
Ok(self.values.pop().unwrap()) // this `unwrap` is safe before `self.values` contains at least 2 items
}
}
}
/// Represents an error while serializing.
#[derive(Clone, Debug, PartialEq)]
pub enum SerializeError {
/// The serialization still has pending values internally.
TransformationNotFinished,
/// The internal values have been corrupted during the
/// serialization.
InternalValuesCorrupted,
/// A record must contain at least one field.
RecordNeedsAtLeastOneField,
/// Arbitrary message.
Message(String),
}
impl ser::Error for SerializeError {
fn custom<T: Display>(msg: T) -> Self {
Self::Message(msg.to_string())
}
}
impl Display for SerializeError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TransformationNotFinished => write!(
formatter,
"serialization still has pending values internally, something incorrect happened"
),
Self::InternalValuesCorrupted => write!(
formatter,
"the internal values have been corrutped during the serialization"
),
Self::RecordNeedsAtLeastOneField => write!(
formatter,
"a record must contain at least one field, zero given"
),
Self::Message(ref msg) => write!(formatter, "{}", msg),
}
}
}
impl std::error::Error for SerializeError {}
impl<'a> ser::Serializer for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;
fn serialize_bool(self, _value: bool) -> Result<Self::Ok, Self::Error> {
unimplemented!("`bool` is not supported by WIT for the moment.")
}
fn serialize_i8(self, value: i8) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_i16(self, value: i16) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_i32(self, value: i32) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_i64(self, value: i64) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_u8(self, value: u8) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_u16(self, value: u16) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_u32(self, value: u32) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_u64(self, value: u64) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_f32(self, value: f32) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_f64(self, value: f64) -> Result<Self::Ok, Self::Error> {
self.last().push(value.into());
Ok(())
}
fn serialize_char(self, _value: char) -> Result<Self::Ok, Self::Error> {
todo!("`char` is not supported by WIT for the moment.")
}
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
self.last().push(value.to_owned().into());
Ok(())
}
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
todo!("`bytes` is not supported by WIT for the moment.")
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
fn serialize_some<T>(self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
todo!("`some` is not supported by WIT for the moment.")
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
todo!("`unit` is not supported by WIT for the moment.")
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
todo!("`unit_struct` is not supported by WIT for the moment.")
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
todo!("`unit_variant` is not supported by WIT for the moment.")
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
todo!("`newtype_variant` is not supported by WIT for the moment.")
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
todo!("`seq` is not supported by WIT for the moment.")
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
todo!("`tuple` is not supported by WIT for the moment.")
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.push_with_capacity(len);
Ok(self)
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
todo!("`tuple_variant` is not supported by WIT for the moment.")
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
todo!("`map` is not supported by WIT for the moment.")
}
fn serialize_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
self.push_with_capacity(len);
Ok(self)
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
todo!("`struct_variant` is not supported by WIT for the moment.")
}
}
impl<'a> ser::SerializeSeq for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
fn serialize_element<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
unimplemented!()
}
fn end(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
}
impl<'a> ser::SerializeTuple for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
fn serialize_element<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
unimplemented!()
}
fn end(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
}
impl<'a> ser::SerializeTupleStruct for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
fn serialize_field<T>(&mut self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let record = InterfaceValue::Record(
Vec1::new(self.pop()?).map_err(|_| Self::Error::RecordNeedsAtLeastOneField)?,
);
self.last().push(record);
Ok(())
}
}
impl<'a> ser::SerializeTupleVariant for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
fn serialize_field<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
unimplemented!()
}
fn end(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
}
impl<'a> ser::SerializeMap for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
fn serialize_key<T>(&mut self, _key: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
unimplemented!()
}
fn serialize_value<T>(&mut self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
unimplemented!()
}
fn end(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
}
impl<'a> ser::SerializeStruct for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let record = InterfaceValue::Record(
Vec1::new(self.pop()?).map_err(|_| Self::Error::RecordNeedsAtLeastOneField)?,
);
self.last().push(record);
Ok(())
}
}
impl<'a> ser::SerializeStructVariant for &'a mut Serializer {
type Ok = ();
type Error = SerializeError;
fn serialize_field<T>(
&mut self,
_key: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
unimplemented!()
}
fn end(self) -> Result<Self::Ok, Self::Error> {
unimplemented!()
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! serialize_value {
($test_name:ident, $ty:ident, $variant:ident, $value:expr) => {
#[test]
#[allow(non_snake_case)]
fn $test_name() {
let input: $ty = $value;
let output = InterfaceValue::$variant($value);
assert_eq!(to_interface_value(&input).unwrap(), output);
}
};
}
serialize_value!(test_serialize_value__s8, i8, S8, 42);
serialize_value!(test_serialize_value__s16, i16, S16, 42);
serialize_value!(test_serialize_value__i32, i32, I32, 42);
serialize_value!(test_serialize_value__i64, i64, I64, 42);
serialize_value!(test_serialize_value__u8, u8, U8, 42);
serialize_value!(test_serialize_value__u16, u16, U16, 42);
serialize_value!(test_serialize_value__u32, u32, U32, 42);
serialize_value!(test_serialize_value__u64, u64, U64, 42);
serialize_value!(test_serialize_value__f32, f32, F32, 42.);
serialize_value!(test_serialize_value__f64, f32, F32, 42.);
serialize_value!(
test_serialize_value__string,
String,
String,
"foo".to_string()
);
#[test]
#[allow(non_snake_case)]
fn test_serialize_value__newtype_struct() {
#[derive(Serialize)]
struct S(i8);
let input = S(42);
let output = InterfaceValue::S8(42);
assert_eq!(to_interface_value(&input).unwrap(), output);
}
#[test]
#[allow(non_snake_case)]
fn test_serialize_value__tuple_struct() {
#[derive(Serialize)]
struct S(i8, f32);
let input = S(7, 42.);
let output = InterfaceValue::Record(vec1![InterfaceValue::S8(7), InterfaceValue::F32(42.)]);
assert_eq!(to_interface_value(&input).unwrap(), output);
}
#[test]
#[allow(non_snake_case)]
fn test_serialize_value__struct() {
#[derive(Serialize)]
struct S {
x: i8,
y: f32,
}
let input = S { x: 7, y: 42. };
let output = InterfaceValue::Record(vec1![InterfaceValue::S8(7), InterfaceValue::F32(42.)]);
assert_eq!(to_interface_value(&input).unwrap(), output);
}
#[test]
#[allow(non_snake_case)]
fn test_serialize_value__struct_nested() {
#[derive(Serialize)]
struct Point {
x: i32,
y: i32,
z: i32,
}
#[derive(Serialize)]
struct Line {
p1: Point,
p2: Point,
}
let input = Line {
p1: Point { x: 1, y: 2, z: 3 },
p2: Point { x: 4, y: 5, z: 6 },
};
let output = InterfaceValue::Record(vec1![
InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::I32(2),
InterfaceValue::I32(3),
]),
InterfaceValue::Record(vec1![
InterfaceValue::I32(4),
InterfaceValue::I32(5),
InterfaceValue::I32(6),
]),
]);
assert_eq!(to_interface_value(&input).unwrap(), output);
}
}

View File

@ -1,61 +0,0 @@
//! This module defines the WIT types.
use crate::vec1::Vec1;
/// Represents the types supported by WIT.
#[derive(PartialEq, Debug, Clone)]
pub enum InterfaceType {
/// A 8-bits signed integer.
S8,
/// A 16-bits signed integer.
S16,
/// A 32-bits signed integer.
S32,
/// A 64-bits signed integer.
S64,
/// A 8-bits unsigned integer.
U8,
/// A 16-bits unsigned integer.
U16,
/// A 32-bits unsigned integer.
U32,
/// A 64-bits unsigned integer.
U64,
/// A 32-bits float.
F32,
/// A 64-bits float.
F64,
/// A string.
String,
/// An `any` reference.
Anyref,
/// A 32-bits integer (as defined in WebAssembly core).
I32,
/// A 64-bits integer (as defiend in WebAssembly core).
I64,
/// A record.
Record(RecordType),
}
/// Represents a record type.
#[derive(PartialEq, Debug, Clone)]
pub struct RecordType {
/// Types representing the fields.
/// A record must have at least one field, hence the
/// [`Vec1`][crate::vec1::Vec1].
pub fields: Vec1<InterfaceType>,
}

View File

@ -1,250 +0,0 @@
//! Defines WIT values and associated operations.
use crate::{
errors::WasmValueNativeCastError,
types::{InterfaceType, RecordType},
vec1::Vec1,
};
use std::{convert::TryFrom, slice::Iter};
#[cfg(feature = "serde")]
pub use crate::serde::{de::from_interface_values, ser::to_interface_value};
/// A WIT value.
#[derive(Debug, Clone, PartialEq)]
pub enum InterfaceValue {
/// A 8-bits signed integer.
S8(i8),
/// A 16-bits signed integer.
S16(i16),
/// A 32-bits signed integer.
S32(i32),
/// A 64-bits signed integer.
S64(i64),
/// A 8-bits unsigned integer.
U8(u8),
/// A 16-bits unsigned integer.
U16(u16),
/// A 32-bits unsigned integer.
U32(u32),
/// A 64-bits unsigned integer.
U64(u64),
/// A 32-bits float.
F32(f32),
/// A 64-bits float.
F64(f64),
/// A string.
String(String),
//Anyref(?),
/// A 32-bits integer (as defined in WebAssembly core).
I32(i32),
/// A 64-bits integer (as defiend in WebAssembly core).
I64(i64),
/// A record.
Record(Vec1<InterfaceValue>),
}
impl From<&InterfaceValue> for InterfaceType {
fn from(value: &InterfaceValue) -> Self {
match value {
InterfaceValue::S8(_) => Self::S8,
InterfaceValue::S16(_) => Self::S16,
InterfaceValue::S32(_) => Self::S32,
InterfaceValue::S64(_) => Self::S64,
InterfaceValue::U8(_) => Self::U8,
InterfaceValue::U16(_) => Self::U16,
InterfaceValue::U32(_) => Self::U32,
InterfaceValue::U64(_) => Self::U64,
InterfaceValue::F32(_) => Self::F32,
InterfaceValue::F64(_) => Self::F64,
InterfaceValue::String(_) => Self::String,
//InterfaceValue::Anyref(_) => Self::Anyref,
InterfaceValue::I32(_) => Self::I32,
InterfaceValue::I64(_) => Self::I64,
InterfaceValue::Record(values) => Self::Record((&**values).into()),
}
}
}
impl Default for InterfaceValue {
fn default() -> Self {
Self::I32(0)
}
}
impl From<&Vec<InterfaceValue>> for RecordType {
fn from(values: &Vec<InterfaceValue>) -> Self {
RecordType {
fields: Vec1::new(values.iter().map(Into::into).collect())
.expect("Record must have at least one field, zero given."),
}
}
}
/// Represents a native type supported by WIT.
pub trait NativeType {
/// The associated interface type that maps to the native type.
const INTERFACE_TYPE: InterfaceType;
}
macro_rules! native {
($native_type:ty, $variant:ident) => {
impl NativeType for $native_type {
const INTERFACE_TYPE: InterfaceType = InterfaceType::$variant;
}
impl From<$native_type> for InterfaceValue {
fn from(n: $native_type) -> Self {
Self::$variant(n)
}
}
impl TryFrom<&InterfaceValue> for $native_type {
type Error = WasmValueNativeCastError;
fn try_from(w: &InterfaceValue) -> Result<Self, Self::Error> {
match w {
InterfaceValue::$variant(n) => Ok(n.clone()),
_ => Err(WasmValueNativeCastError {
from: w.into(),
to: <$native_type>::INTERFACE_TYPE,
}),
}
}
}
};
}
native!(i8, S8);
native!(i16, S16);
native!(i32, I32);
native!(i64, I64);
native!(u8, U8);
native!(u16, U16);
native!(u32, U32);
native!(u64, U64);
native!(f32, F32);
native!(f64, F64);
native!(String, String);
/// Iterates over a vector of `InterfaceValues` but flatten all the
/// values. So `I32(1), Record([I32(2), I32(3)]), I32(4)` will be
/// iterated like `I32(1), I32(2), I32(3), I32(4)`.
pub(crate) struct FlattenInterfaceValueIterator<'a> {
iterators: Vec<Iter<'a, InterfaceValue>>,
}
impl<'a> FlattenInterfaceValueIterator<'a> {
pub(crate) fn new(values: &'a [InterfaceValue]) -> Self {
Self {
iterators: vec![values.iter()],
}
}
}
impl<'a> Iterator for FlattenInterfaceValueIterator<'a> {
type Item = &'a InterfaceValue;
fn next(&mut self) -> Option<Self::Item> {
match self.iterators.last_mut()?.next() {
// End of the current iterator, go back to the previous
// one.
None => {
self.iterators.pop();
self.next()
}
// Recursively iterate over the record.
Some(InterfaceValue::Record(values)) => {
self.iterators.push(values.iter());
self.next()
}
// A regular item.
e @ Some(_) => e,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! value_to_type {
($test_name:ident, $ty:ident, $value:expr) => {
#[test]
#[allow(non_snake_case)]
fn $test_name() {
assert_eq!(
InterfaceType::from(&InterfaceValue::$ty($value)),
InterfaceType::$ty
);
}
};
}
value_to_type!(interface_type_from_interface_value__s8, S8, 42);
value_to_type!(interface_type_from_interface_value__s16, S16, 42);
value_to_type!(interface_type_from_interface_value__s32, S32, 42);
value_to_type!(interface_type_from_interface_value__s64, S64, 42);
value_to_type!(interface_type_from_interface_value__u8, U8, 42);
value_to_type!(interface_type_from_interface_value__u16, U16, 42);
value_to_type!(interface_type_from_interface_value__u32, U32, 42);
value_to_type!(interface_type_from_interface_value__u64, U64, 42);
value_to_type!(interface_type_from_interface_value__f32, F32, 42.);
value_to_type!(interface_type_from_interface_value__f64, F64, 42.);
value_to_type!(
interface_type_from_interface_value__string,
String,
"foo".to_string()
);
value_to_type!(interface_type_from_interface_value__i32, I32, 42);
value_to_type!(interface_type_from_interface_value__i64, I64, 42);
#[test]
#[allow(non_snake_case)]
fn interface_type_from_interface_value__record() {
assert_eq!(
InterfaceType::from(&InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::S8(2)
])),
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::I32, InterfaceType::S8]
})
);
assert_eq!(
InterfaceType::from(&InterfaceValue::Record(vec1![
InterfaceValue::I32(1),
InterfaceValue::Record(vec1![
InterfaceValue::String("a".to_string()),
InterfaceValue::F64(42.)
]),
InterfaceValue::S8(2)
])),
InterfaceType::Record(RecordType {
fields: vec1![
InterfaceType::I32,
InterfaceType::Record(RecordType {
fields: vec1![InterfaceType::String, InterfaceType::F64]
}),
InterfaceType::S8
]
})
);
}
}

View File

@ -1,62 +0,0 @@
//! `Vec1<T>` represents a non-empty `Vec<T>`.
use std::{
error,
fmt::{self, Debug},
ops,
};
/// `Vec1<T>` represents a non-empty `Vec<T>`. It derefs to `Vec<T>`
/// directly.
#[derive(Clone, PartialEq)]
pub struct Vec1<T>(Vec<T>)
where
T: Debug;
/// Represents the only error that can be emitted by `Vec1`, i.e. when
/// the number of items is zero.
#[derive(Debug)]
pub struct EmptyVec;
impl error::Error for EmptyVec {}
impl fmt::Display for EmptyVec {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "Vec1 must as least contain one item, zero given")
}
}
impl<T> Vec1<T>
where
T: Debug,
{
/// Creates a new non-empty vector, based on an inner `Vec<T>`. If
/// the inner vector is empty, a `EmptyVec` error is returned.
pub fn new(items: Vec<T>) -> Result<Self, EmptyVec> {
if items.len() == 0 {
Err(EmptyVec)
} else {
Ok(Self(items))
}
}
}
impl<T> fmt::Debug for Vec1<T>
where
T: Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{:?}", self.0)
}
}
impl<T> ops::Deref for Vec1<T>
where
T: Debug,
{
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View File

@ -1,53 +0,0 @@
use wasmer_interface_types::{
ast::*, decoders::binary::parse, encoders::binary::ToBytes, interpreter::Instruction, types::*,
vec1,
};
/// Tests an AST to binary, then binary to AST roundtrip.
#[test]
fn test_binary_encoding_decoding_roundtrip() {
let original_ast = Interfaces {
types: vec![
Type::Function {
inputs: vec![],
outputs: vec![],
},
Type::Function {
inputs: vec![InterfaceType::I32, InterfaceType::I32],
outputs: vec![InterfaceType::S32],
},
Type::Record(RecordType {
fields: vec1![InterfaceType::String, InterfaceType::I32],
}),
],
imports: vec![Import {
namespace: "a",
name: "b",
signature_type: 0,
}],
adapters: vec![Adapter {
function_type: 0,
instructions: vec![Instruction::ArgumentGet { index: 1 }],
}],
exports: vec![Export {
name: "ab",
function_type: 1,
}],
implementations: vec![Implementation {
core_function_type: 0,
adapter_function_type: 0,
}],
};
let mut binary = vec![];
original_ast
.to_bytes(&mut binary)
.expect("Failed to encode the AST.");
let (remainder, ast) = parse::<()>(binary.as_slice()).expect("Failed to decode the AST.");
assert!(remainder.is_empty());
assert_eq!(original_ast, ast);
}