Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explain what is unsupported in the interpreter #292

Merged
merged 4 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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