macro_rules! for_each_visit_operator {
($m:ident) => { ... };
}
Expand description
Used to implement the VisitOperator
trait.
A helper macro to conveniently iterate over all opcodes recognized by this
crate. This can be used to work with either the Operator
enumeration or
the VisitOperator
trait if your use case uniformly handles all operators
the same way.
It is also possible to specialize handling of operators depending on the Wasm proposal from which they are originating.
This is an “iterator macro” where this macro is invoked with the name of another macro, and then that macro is invoked with the list of all operators.
The list of specializable Wasm proposals is as follows:
@mvp
: Denoting a Wasm operator from the initial Wasm MVP version.@exceptions
: Wasmexception-handling
proposal@tail_call
: Wasmtail-calls
proposal@reference_types
: Wasmreference-types
proposal@sign_extension
: Wasmsign-extension-ops
proposal@saturating_float_to_int
: Wasmnon_trapping_float-to-int-conversions
proposal@bulk_memory
:Wasmbulk-memory
proposal@threads
: Wasmthreads
proposal@gc
: Wasmgc
proposal@stack_switching
: Wasmstack-switching
proposal@wide_arithmetic
: Wasmwide-arithmetic
proposal
Note that this macro does not iterate over the SIMD-related proposals. Those
are provided in VisitSimdOperator
and for_each_visit_simd_operator
.
This macro only handles non-SIMD related operators and so users wanting to
handle the SIMD-class of operators need to use that trait/macro as well.
macro_rules! define_visit_operator {
// The outer layer of repetition represents how all operators are
// provided to the macro at the same time.
//
// The `$proposal` identifier indicates the Wasm proposals from which
// the Wasm operator is originating.
// For example to specialize the macro match arm for Wasm `gc` proposal
// operators you could write `@gc` instead of `@$proposal:ident` to
// only catch those operators.
//
// The `$op` name is bound to the `Operator` variant name. The
// payload of the operator is optionally specified (the `$(...)?`
// clause) since not all instructions have payloads. Within the payload
// each argument is named and has its type specified.
//
// The `$visit` name is bound to the corresponding name in the
// `VisitOperator` trait that this corresponds to.
//
// The `$ann` annotations give information about the operator's type (e.g. binary i32 or arity 2 -> 1).
($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
$(
fn $visit(&mut self $($(,$arg: $argty)*)?) {
// do nothing for this example
}
)*
}
}
pub struct VisitAndDoNothing;
impl<'a> wasmparser::VisitOperator<'a> for VisitAndDoNothing {
type Output = ();
wasmparser::for_each_visit_operator!(define_visit_operator);
}
If you only wanted to visit the initial base set of wasm instructions, for example, you could do:
macro_rules! visit_only_mvp {
// delegate the macro invocation to sub-invocations of this macro to
// deal with each instruction on a case-by-case basis.
($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {
$(
visit_only_mvp!(visit_one @$proposal $op $({ $($arg: $argty),* })? => $visit);
)*
};
// MVP instructions are defined manually, so do nothing.
(visit_one @mvp $($rest:tt)*) => {};
// Non-MVP instructions all return `false` here. The exact type depends
// on `type Output` in the trait implementation below. You could change
// it to `Result<()>` for example and return an error here too.
(visit_one @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident) => {
fn $visit(&mut self $($(,$arg: $argty)*)?) -> bool {
false
}
}
}
pub struct VisitOnlyMvp;
impl<'a> wasmparser::VisitOperator<'a> for VisitOnlyMvp {
type Output = bool;
wasmparser::for_each_visit_operator!(visit_only_mvp);
// manually define `visit_*` for all MVP operators here
}