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

Add empty ValidMode and Prepare #725

Merged
merged 4 commits into from
Jan 22, 2025
Merged
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
51 changes: 32 additions & 19 deletions crates/interpreter/src/valid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use alloc::collections::BTreeSet;
use alloc::vec;
use alloc::vec::Vec;
use core::cmp::Ordering;
use core::marker::PhantomData;
use core::ops::Range;

use crate::error::*;
Expand All @@ -27,24 +28,32 @@ use crate::*;

/// Checks whether a WASM module in binary format is valid.
pub fn validate(binary: &[u8]) -> Result<Vec<MetadataEntry>, Error> {
Context::default().check_module(&mut Parser::new(binary))
Context::<Prepare>::default().check_module(&mut Parser::new(binary))
ia0 marked this conversation as resolved.
Show resolved Hide resolved
}

pub trait ValidMode: Default {}

#[derive(Default)]
pub struct Prepare;
impl ValidMode for Prepare {}

type Parser<'m> = parser::Parser<'m, Check>;
type CheckResult = MResult<(), Check>;

#[derive(Default)]
zhouwfang marked this conversation as resolved.
Show resolved Hide resolved
struct Context<'m> {
struct Context<'m, M: ValidMode> {
types: Vec<FuncType<'m>>,
funcs: Vec<TypeIdx>,
tables: Vec<TableType>,
mems: Vec<MemType>,
globals: Vec<GlobalType>,
elems: Vec<RefType>,
datas: Option<usize>,
#[allow(dead_code)]
mode: PhantomData<M>,
}

impl<'m> Context<'m> {
impl<'m, M: ValidMode> Context<'m, M> {
fn check_module(&mut self, parser: &mut Parser<'m>) -> MResult<Vec<MetadataEntry>, Check> {
check(parser.parse_bytes(8)? == b"\0asm\x01\0\0\0")?;
let module_start = parser.save().as_ptr() as usize;
Expand Down Expand Up @@ -263,16 +272,18 @@ impl<'m> Context<'m> {
}
}

struct ParseElem<'a, 'm> {
context: &'a mut Context<'m>,
struct ParseElem<'a, 'm, M: ValidMode> {
context: &'a mut Context<'m, M>,
refs: &'a mut [bool],
num_global_imports: usize,
table_type: OpdType,
elem_type: RefType,
}

impl<'a, 'm> ParseElem<'a, 'm> {
fn new(context: &'a mut Context<'m>, refs: &'a mut [bool], num_global_imports: usize) -> Self {
impl<'a, 'm, M: ValidMode> ParseElem<'a, 'm, M> {
fn new(
context: &'a mut Context<'m, M>, refs: &'a mut [bool], num_global_imports: usize,
) -> Self {
Self {
context,
num_global_imports,
Expand All @@ -283,7 +294,7 @@ impl<'a, 'm> ParseElem<'a, 'm> {
}
}

impl<'m> parser::ParseElem<'m, Check> for ParseElem<'_, 'm> {
impl<'m, M: ValidMode> parser::ParseElem<'m, Check> for ParseElem<'_, 'm, M> {
fn mode(&mut self, mode: parser::ElemMode<'_, 'm, Check>) -> MResult<(), Check> {
if let parser::ElemMode::Active { table, offset } = mode {
Expr::check_const(
Expand Down Expand Up @@ -322,19 +333,21 @@ impl<'m> parser::ParseElem<'m, Check> for ParseElem<'_, 'm> {
}
}

struct ParseData<'a, 'm> {
context: &'a mut Context<'m>,
struct ParseData<'a, 'm, M: ValidMode> {
context: &'a mut Context<'m, M>,
refs: &'a mut [bool],
num_global_imports: usize,
}

impl<'a, 'm> ParseData<'a, 'm> {
fn new(context: &'a mut Context<'m>, refs: &'a mut [bool], num_global_imports: usize) -> Self {
impl<'a, 'm, M: ValidMode> ParseData<'a, 'm, M> {
Copy link
Member Author

@zhouwfang zhouwfang Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust design question: Is there a good reason why the <'a, 'm, M> after ParseData couldn't be omitted? It seems redundant to write them since they could be inferred from the impl<'a, 'm, M: ValidMode>.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because those are different things. ParseData can take arbitrary parameters. If there would be a syntactic sugar it would be the opposite. You have to specify the ParseData parameters, and any free variable would be universally quantified on the impl. But I'm not sure this would help readability.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you give an example where the struct has more parameters than impl? Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an example from the standard library.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! "universally quantified on the impl" is a very good description.

fn new(
context: &'a mut Context<'m, M>, refs: &'a mut [bool], num_global_imports: usize,
) -> Self {
Self { context, refs, num_global_imports }
}
}

impl<'m> parser::ParseData<'m, Check> for ParseData<'_, 'm> {
impl<'m, M: ValidMode> parser::ParseData<'m, Check> for ParseData<'_, 'm, M> {
fn mode(&mut self, mode: parser::DataMode<'_, 'm, Check>) -> MResult<(), Check> {
if let parser::DataMode::Active { memory, offset } = mode {
self.context.mem(memory)?;
Expand Down Expand Up @@ -406,8 +419,8 @@ impl OpdType {
}
}

struct Expr<'a, 'm> {
context: &'a Context<'m>,
struct Expr<'a, 'm, M: ValidMode> {
context: &'a Context<'m, M>,
parser: &'a mut Parser<'m>,
globals_len: usize,
/// Whether the expression is const and the function references.
Expand Down Expand Up @@ -515,9 +528,9 @@ enum LabelKind<'m> {
If(SideTableBranch<'m>),
}

impl<'a, 'm> Expr<'a, 'm> {
impl<'a, 'm, M: ValidMode> Expr<'a, 'm, M> {
fn new(
context: &'a Context<'m>, parser: &'a mut Parser<'m>,
context: &'a Context<'m, M>, parser: &'a mut Parser<'m>,
is_const: Result<&'a mut [bool], &'a [bool]>,
) -> Self {
Self {
Expand All @@ -533,7 +546,7 @@ impl<'a, 'm> Expr<'a, 'm> {
}

fn check_const(
context: &'a Context<'m>, parser: &'a mut Parser<'m>, refs: &'a mut [bool],
context: &'a Context<'m, M>, parser: &'a mut Parser<'m>, refs: &'a mut [bool],
num_global_imports: usize, expected: ResultType<'m>,
) -> CheckResult {
let mut expr = Expr::new(context, parser, Ok(refs));
Expand All @@ -543,7 +556,7 @@ impl<'a, 'm> Expr<'a, 'm> {
}

fn check_body(
context: &'a Context<'m>, parser: &'a mut Parser<'m>, refs: &'a [bool],
context: &'a Context<'m, M>, parser: &'a mut Parser<'m>, refs: &'a [bool],
locals: Vec<ValType>, results: ResultType<'m>,
) -> MResult<Vec<BranchTableEntry>, Check> {
let mut expr = Expr::new(context, parser, Err(refs));
Expand Down
Loading