mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-04 18:10:18 +00:00
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:
commit
0575a7ed3f
@ -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.
|
||||
|
@ -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",
|
||||
|
3
Makefile
3
Makefile
@ -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)
|
||||
|
@ -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"]
|
@ -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` | ❌ | ❌ | ❌ | |
|
@ -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>,
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 }
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 }
|
||||
}
|
||||
}
|
@ -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"
|
||||
);
|
||||
}
|
@ -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: [],
|
||||
);
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)],
|
||||
);
|
||||
}
|
@ -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"#,
|
||||
);
|
||||
}
|
@ -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"#,
|
||||
);
|
||||
}
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
@ -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
|
||||
}
|
||||
}
|
@ -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;
|
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
@ -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);
|
||||
}
|
||||
}
|
@ -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>,
|
||||
}
|
@ -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
|
||||
]
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user