Skip to content

Commit

Permalink
Explain what is unsupported in the interpreter (#292)
Browse files Browse the repository at this point in the history
This is a first step towards #42, but the most useful one for now.
  • Loading branch information
ia0 authored Nov 13, 2023
1 parent a39538a commit 19ac560
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 36 deletions.
8 changes: 7 additions & 1 deletion crates/interpreter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.2.0-git

### Major

- Change `Error::Unsupported` to take a reason with the `debug` feature

## 0.1.4

### Patch
Expand Down Expand Up @@ -32,4 +38,4 @@

## 0.1.0

<!-- Increment to skip CHANGELOG.md test: 2 -->
<!-- Increment to skip CHANGELOG.md test: 3 -->
2 changes: 1 addition & 1 deletion crates/interpreter/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/interpreter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wasefire-interpreter"
version = "0.1.4"
version = "0.2.0-git"
authors = ["Julien Cretin <[email protected]>"]
license = "Apache-2.0"
publish = true
Expand Down
18 changes: 15 additions & 3 deletions crates/interpreter/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,25 @@ pub enum Error {
NotFound,

/// An argument is not supported.
Unsupported,
Unsupported(Unsupported),

/// Execution trapped.
// TODO: In debug mode, trap could describe the reason (like resource exhaustion, etc).
Trap,
}

#[cfg(not(feature = "debug"))]
pub type Unsupported = ();

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[non_exhaustive]
#[cfg(feature = "debug")]
pub enum Unsupported {
Opcode(u8),
OpcodeFc(u32),
MaxLocals,
}

#[cfg(feature = "debug")]
pub fn print_backtrace() {
let backtrace = std::backtrace::Backtrace::capture();
Expand All @@ -49,10 +61,10 @@ pub fn not_found() -> Error {
Error::NotFound
}

pub fn unsupported() -> Error {
pub fn unsupported(reason: Unsupported) -> Error {
#[cfg(feature = "debug")]
print_backtrace();
Error::Unsupported
Error::Unsupported(reason)
}

pub fn trap() -> Error {
Expand Down
12 changes: 11 additions & 1 deletion crates/interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@

extern crate alloc;

macro_rules! if_debug {
($unit:expr) => {{
#[cfg(feature = "debug")]
let unit = $unit;
#[cfg(not(feature = "debug"))]
let unit = ();
unit
}};
}

macro_rules! support_if {
($feature:literal[$($var:ident)*], $then:expr, $else:expr) => {{
#[cfg(feature = $feature)]
Expand All @@ -140,7 +150,7 @@ mod syntax;
mod toctou;
mod valid;

pub use error::Error;
pub use error::{Error, Unsupported};
pub use exec::{Call, InstId, RunAnswer, RunResult, Store, StoreId, Val, MEMORY_ALIGN};
pub use module::Module;
pub use syntax::{GlobalType, ImportDesc, Limits, Mut, RefType, TableType, ValType};
Expand Down
34 changes: 20 additions & 14 deletions crates/interpreter/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use alloc::vec::Vec;
use core::marker::PhantomData;

#[cfg(feature = "debug")]
use crate::error::*;
use crate::syntax::*;
use crate::toctou::*;

Expand Down Expand Up @@ -304,7 +306,7 @@ impl<'m, M: Mode> Parser<'m, M> {
x @ 0x2a ..= 0x2b => support_if!(
"float-types"[x],
Instr::FLoad((x - 0x2a).into(), self.parse_memarg()?),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0x2c ..= 0x35 => Instr::ILoad_(
((x - 0x2c) / 2).into(),
Expand All @@ -315,7 +317,7 @@ impl<'m, M: Mode> Parser<'m, M> {
x @ 0x38 ..= 0x39 => support_if!(
"float-types"[x],
Instr::FStore((x - 0x38).into(), self.parse_memarg()?),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0x3a ..= 0x3e => Instr::IStore_((x - 0x3a).into(), self.parse_memarg()?),
0x3f => {
Expand All @@ -331,12 +333,12 @@ impl<'m, M: Mode> Parser<'m, M> {
0x43 => support_if!(
"float-types"[],
Instr::F32Const(u32::from_le_bytes(self.parse_bytes(4)?.try_into().unwrap())),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(0x43)))?
),
0x44 => support_if!(
"float-types"[],
Instr::F64Const(u64::from_le_bytes(self.parse_bytes(8)?.try_into().unwrap())),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(0x44)))?
),
x @ 0x45 ..= 0x5a => {
let n = Nx::from((x - 0x45) / 11);
Expand All @@ -348,7 +350,7 @@ impl<'m, M: Mode> Parser<'m, M> {
x @ 0x5b ..= 0x66 => support_if!(
"float-types"[x],
Instr::FRelOp(((x - 0x5b) / 6).into(), ((x - 0x5b) % 6).into()),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0x67 ..= 0x8a => {
let n = Nx::from((x - 0x67) / 18);
Expand All @@ -366,7 +368,7 @@ impl<'m, M: Mode> Parser<'m, M> {
y => Instr::FBinOp(n, (y - 7).into()),
}
},
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
0xa7 => Instr::CvtOp(CvtOp::Wrap),
x @ 0xa8 ..= 0xab => support_if!(
Expand All @@ -376,7 +378,7 @@ impl<'m, M: Mode> Parser<'m, M> {
((x - 0xa8) / 2).into(),
((x - 0xa8) % 2).into(),
)),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0xac ..= 0xad => Instr::CvtOp(CvtOp::Extend((x - 0xac).into())),
x @ 0xae ..= 0xb1 => support_if!(
Expand All @@ -386,7 +388,7 @@ impl<'m, M: Mode> Parser<'m, M> {
((x - 0xae) / 2).into(),
((x - 0xae) % 2).into(),
)),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0xb2 ..= 0xbb => support_if!(
"float-types"[x],
Expand All @@ -400,17 +402,17 @@ impl<'m, M: Mode> Parser<'m, M> {
y => Instr::CvtOp(CvtOp::Convert(n, (y / 2).into(), (y % 2).into())),
}
},
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0xbc ..= 0xbd => support_if!(
"float-types"[x],
Instr::CvtOp(CvtOp::IReinterpret((x - 0xbc).into())),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0xbe ..= 0xbf => support_if!(
"float-types"[x],
Instr::CvtOp(CvtOp::FReinterpret((x - 0xbe).into())),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::Opcode(x)))?
),
x @ 0xc0 ..= 0xc4 => Instr::IExtend((x - 0xc0).into()),
0xd0 => Instr::RefNull(self.parse_reftype()?),
Expand All @@ -424,7 +426,7 @@ impl<'m, M: Mode> Parser<'m, M> {
((x & 2 != 0) as u8).into(),
((x & 1) as u8).into(),
)),
M::unsupported()?
M::unsupported(if_debug!(Unsupported::OpcodeFc(x)))?
),
8 => {
let x = self.parse_dataidx()?;
Expand Down Expand Up @@ -453,7 +455,11 @@ impl<'m, M: Mode> Parser<'m, M> {
17 => Instr::TableFill(self.parse_tableidx()?),
_ => M::invalid()?,
},
0xfd => support_if!("vector-types"[], unimplemented!(), M::unsupported()?),
0xfd => support_if!(
"vector-types"[],
unimplemented!(),
M::unsupported(if_debug!(Unsupported::Opcode(0xfd)))?
),
_ => M::invalid()?,
})
}
Expand All @@ -462,7 +468,7 @@ impl<'m, M: Mode> Parser<'m, M> {
for _ in 0 .. self.parse_vec()? {
let len = self.parse_u32()? as usize;
if locals.len().checked_add(len).map_or(true, |x| x > MAX_LOCALS) {
return M::unsupported();
return M::unsupported(if_debug!(Unsupported::MaxLocals));
}
let val = self.parse_valtype()?;
locals.extend(core::iter::repeat(val).take(len));
Expand Down
8 changes: 4 additions & 4 deletions crates/interpreter/src/toctou.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub trait Mode {

fn invalid<T>() -> Result<T, Self::Error>;

fn unsupported<T>() -> Result<T, Self::Error>;
fn unsupported<T>(reason: Unsupported) -> Result<T, Self::Error>;

fn check(cond: impl FnOnce() -> bool) -> Result<(), Self::Error>;

Expand All @@ -41,8 +41,8 @@ impl Mode for Check {
Err(invalid())
}

fn unsupported<T>() -> Result<T, Self::Error> {
Err(unsupported())
fn unsupported<T>(reason: Unsupported) -> Result<T, Self::Error> {
Err(unsupported(reason))
}

fn check(cond: impl FnOnce() -> bool) -> Result<(), Self::Error> {
Expand Down Expand Up @@ -75,7 +75,7 @@ impl Mode for Use {
unreachable!()
}

fn unsupported<T>() -> Result<T, Self::Error> {
fn unsupported<T>(_: Unsupported) -> Result<T, Self::Error> {
Self::invalid()
}

Expand Down
12 changes: 6 additions & 6 deletions crates/interpreter/tests/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn test(name: &str) {
match directive {
WastDirective::Wat(QuoteWat::Wat(Wat::Module(mut m))) => {
env.instantiate(name, &m.encode().unwrap());
if !matches!(env.inst, Err(Error::Unsupported)) {
if !matches!(env.inst, Err(Error::Unsupported(_))) {
env.register_id(m.id, env.inst.unwrap());
}
}
Expand Down Expand Up @@ -120,7 +120,7 @@ impl<'m> Env<'m> {

fn set_inst(&mut self, inst: Result<InstId, Error>) {
match inst {
Ok(_) | Err(Error::Unsupported) => (),
Ok(_) | Err(Error::Unsupported(_)) => (),
Err(e) => panic!("{e:?}"),
}
self.inst = inst;
Expand Down Expand Up @@ -296,7 +296,7 @@ fn assert_invoke(env: &mut Env, invoke: WastInvoke) {
fn assert_malformed(mut wat: QuoteWat) {
if let Ok(wasm) = wat.encode() {
let module = Module::new(&wasm);
if !matches!(module, Err(Error::Unsupported)) {
if !matches!(module, Err(Error::Unsupported(_))) {
assert_eq!(module.err(), Some(Error::Invalid));
}
}
Expand All @@ -305,21 +305,21 @@ fn assert_malformed(mut wat: QuoteWat) {
fn assert_invalid(mut wat: QuoteWat) {
let wasm = wat.encode().unwrap();
let module = Module::new(&wasm);
if !matches!(module, Err(Error::Unsupported)) {
if !matches!(module, Err(Error::Unsupported(_))) {
assert_eq!(module.err(), Some(Error::Invalid));
}
}

fn assert_exhaustion(env: &mut Env, call: WastInvoke) {
let result = wast_invoke(env, call);
if !matches!(result, Err(Error::Unsupported)) {
if !matches!(result, Err(Error::Unsupported(_))) {
assert_eq!(result, Err(Error::Trap));
}
}

fn assert_unlinkable(env: &mut Env, mut wat: Wat) {
let inst = env.maybe_instantiate("", &wat.encode().unwrap());
if !matches!(inst, Err(Error::Unsupported)) {
if !matches!(inst, Err(Error::Unsupported(_))) {
assert_eq!(inst.err(), Some(Error::NotFound));
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/runner-host/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/runner-nordic/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/scheduler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,4 @@

## 0.1.0

<!-- Increment to skip CHANGELOG.md test: 18 -->
<!-- Increment to skip CHANGELOG.md test: 19 -->
2 changes: 1 addition & 1 deletion crates/scheduler/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/scheduler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ wasefire-logger = { version = "0.1.3", path = "../logger" }
wasefire-store = { version = "0.2.2", path = "../store" }

[dependencies.wasefire-interpreter]
version = "0.1.4"
version = "0.2.0-git"
path = "../interpreter"
features = ["toctou"]
optional = true
Expand Down

0 comments on commit 19ac560

Please sign in to comment.