Skip to content
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ validate = [
'wasmparser/validate',
'wasmparser/features',
]
print = []
print = ['dep:bitflags', 'wasmparser/features']
parse = []
smith = ['wasm-smith', 'arbitrary', 'dep:serde', 'dep:serde_derive', 'dep:serde_json']
shrink = ['wasm-shrink', 'is_executable']
Expand Down
4 changes: 2 additions & 2 deletions crates/wasm-encoder/src/reencode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ pub mod utils {
let data_range = start as usize..end;
data.get(data_range).ok_or(Error::InvalidCodeSectionSize)
};
for section in parser.parse_all(data) {
for section in parser.clone().parse_all(data) {
match section? {
Comment on lines -696 to 697

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why does this need to .clone() now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah that's because parse_all consumes self, but the parser is needed below. The metadata of parser though is pretty cheap in general, just a few integers

wasmparser::Payload::Version {
encoding: wasmparser::Encoding::Module,
Expand Down Expand Up @@ -849,7 +849,7 @@ pub mod utils {
// that users overriding `parse_code_section` always get
// that function called.
let section = get_original_section(unchecked_range.clone())?;
let reader = wasmparser::BinaryReader::new(section, unchecked_range.start);
let reader = parser.binary_reader(section, unchecked_range.start);
let section = wasmparser::CodeSectionReader::new(reader)?;
reencoder.parse_code_section(&mut codes, section)?;
module.section(&codes);
Expand Down
29 changes: 24 additions & 5 deletions crates/wasmparser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,29 @@ impl Parser {
self.features = features;
}

/// Creates a new [`BinaryReader`] for `data` which uses the same set of
/// wasm features as this parser.
///
/// This is useful when parsing a range of the input, such as a section's
/// contents, outside of this parser while still respecting the features
/// configured with [`Parser::set_features`].
///
/// For more information see [`BinaryReader::new`].
///
/// # Panics
///
/// Panics in the same situations as [`BinaryReader::new`].
pub fn binary_reader<'a>(&self, data: &'a [u8], original_offset: u64) -> BinaryReader<'a> {
#[cfg(feature = "features")]
{
BinaryReader::new_features(data, original_offset, self.features)
}
#[cfg(not(feature = "features"))]
{
BinaryReader::new(data, original_offset)
}
}

/// Returns the original offset that this parser is currently at.
pub fn offset(&self) -> u64 {
self.offset
Expand Down Expand Up @@ -640,11 +663,7 @@ impl Parser {
} else {
(data, eof)
};
let mut reader = BinaryReader::new(data, self.offset);
#[cfg(feature = "features")]
{
reader.set_features(self.features);
}
let mut reader = self.binary_reader(data, self.offset);
match self.parse_reader(&mut reader, eof) {
Ok(payload) => {
// Be sure to update our offset with how far we got in the reader
Expand Down
3 changes: 2 additions & 1 deletion crates/wasmprinter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ wat = { path = "../wat" }
[features]
default = ['component-model', 'validate']
component-model = ['wasmparser/component-model']
validate = ['wasmparser/validate', 'wasmparser/features']
validate = ['wasmparser/validate', 'features']
features = ['wasmparser/features']
17 changes: 16 additions & 1 deletion crates/wasmprinter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub struct Config {
fold_instructions: bool,
indent_text: String,
print_operand_stack: bool,
#[cfg(feature = "features")]
features: wasmparser::WasmFeatures,
}

impl Default for Config {
Expand All @@ -76,6 +78,8 @@ impl Default for Config {
fold_instructions: false,
indent_text: " ".to_string(),
print_operand_stack: false,
#[cfg(feature = "features")]
features: wasmparser::WasmFeatures::all(),
}
}
}
Expand Down Expand Up @@ -285,6 +289,15 @@ impl Config {
self
}

/// Select the set of WebAssembly features used when decoding the input.
///
/// The default setting is [`WasmFeatures::all`](wasmparser::WasmFeatures::all).
#[cfg(feature = "features")]
pub fn features(&mut self, features: wasmparser::WasmFeatures) -> &mut Self {
self.features = features;
self
}

/// Print a WebAssembly binary.
///
/// This function takes an entire `wasm` binary blob and prints it to the
Expand Down Expand Up @@ -441,11 +454,13 @@ impl Printer<'_, '_> {
let mut expected = None;
let mut states: Vec<State> = Vec::new();
let mut parser = Parser::new(0);
#[cfg(feature = "features")]
parser.set_features(self.config.features);
#[cfg(feature = "component-model")]
let mut parsers = Vec::new();

let mut validator = if self.config.print_operand_stack {
operand_stack::Validator::new()
operand_stack::Validator::new(self.config)
} else {
None
};
Expand Down
6 changes: 3 additions & 3 deletions crates/wasmprinter/src/operand_stack.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{Config, PrintTermcolor, Printer};
use wasmparser::{
BinaryReader, Frame, OperatorsReader, Payload, Result, ValType, ValidPayload,
ValidatorResources, WasmFeatures,
ValidatorResources,
};

pub struct Validator {
Expand All @@ -10,9 +10,9 @@ pub struct Validator {
}

impl Validator {
pub fn new() -> Option<Validator> {
pub fn new(config: &Config) -> Option<Validator> {
Some(Validator {
validator: wasmparser::Validator::new_with_features(WasmFeatures::all()),
validator: wasmparser::Validator::new_with_features(config.features),
last_function: None,
})
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmprinter/src/operand_stack_disabled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use wasmparser::{BinaryReader, OperatorsReader, Payload, Result};
pub enum Validator {}

impl Validator {
pub fn new() -> Option<Validator> {
pub fn new(_config: &crate::Config) -> Option<Validator> {
None
}

Expand Down
6 changes: 6 additions & 0 deletions src/bin/wasm-tools/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,15 @@ Exit status:
0 on success,
nonzero if the input file fails to parse.
")]
// `-f` is already taken by `--fold-instructions`, so `--features` is long-only.
#[clap(mut_arg("features", |a| a.short(None)))]
pub struct Opts {
#[clap(flatten)]
generate_dwarf: wasm_tools::GenerateDwarfArg,

#[clap(flatten)]
features: wasm_tools::CliFeatures,

#[clap(flatten)]
io: wasm_tools::InputOutput,

Expand Down Expand Up @@ -102,6 +107,7 @@ impl Opts {
let wasm = self.io.get_input_wasm(Some(&self.generate_dwarf))?;

let mut config = wasmprinter::Config::new();
config.features(self.features.features_from(wasmparser::WasmFeatures::all()));
config.print_offsets(self.print_offsets);
config.print_skeleton(self.skeleton);
config.name_unnamed(self.name_unnamed);
Expand Down
144 changes: 2 additions & 142 deletions src/bin/wasm-tools/validate.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use addr2line::LookupResult;
use anyhow::{Result, bail};
use bitflags::Flags;
use anyhow::Result;
use std::fmt::Write;
use std::time::Instant;
use wasm_tools::CliFeatures;
use wasm_tools::addr2line::Addr2lineModules;
use wasmparser::WasmFeatures;

/// Validate a WebAssembly binary
///
Expand Down Expand Up @@ -56,38 +55,6 @@ pub struct Opts {
validate_custom: bool,
}

// Helper structure extracted used to parse the feature flags for `validate`.
#[derive(clap::Parser)]
pub struct CliFeatures {
/// Comma-separated list of WebAssembly features to enable during
/// validation.
///
/// If a "-" character is present in front of a feature it will disable that
/// feature. For example "-simd" will disable the simd proposal.
///
/// The placeholder "all" can be used to enable all wasm features and the
/// term "-all" can be used to disable all features.
///
/// The default set of features enabled are all WebAssembly proposals that
/// are at phase 4 or after. This means that the default set of features
/// accepted are relatively bleeding edge. Versions of the WebAssembly
/// specification can also be selected. The "wasm1" or "mvp" feature can
/// select the original WebAssembly specification and "wasm2" can be used to
/// select the 2.0 version.
///
/// Available feature options can be found in the wasmparser crate:
/// <https://github.com/bytecodealliance/wasm-tools/blob/main/crates/wasmparser/src/features.rs>
#[clap(long, short = 'f', value_parser = parse_features)]
features: Vec<Vec<FeatureAction>>,
}

#[derive(Clone)]
enum FeatureAction {
Reset(WasmFeatures),
Enable(WasmFeatures),
Disable(WasmFeatures),
}

impl Opts {
pub fn general_opts(&self) -> &wasm_tools::GeneralOpts {
&self.general
Expand Down Expand Up @@ -169,110 +136,3 @@ impl Opts {
}
}
}

impl CliFeatures {
pub fn features(&self) -> WasmFeatures {
let mut ret = WasmFeatures::default();

for action in self.features.iter().flat_map(|v| v) {
match action {
FeatureAction::Enable(features) => {
ret |= *features;
}
FeatureAction::Disable(features) => {
ret &= !*features;
}
FeatureAction::Reset(features) => {
ret = *features;
}
}
}

ret
}
}

fn parse_features(arg: &str) -> Result<Vec<FeatureAction>> {
let mut ret = Vec::new();

const GROUPS: &[(&str, WasmFeatures)] = &[
("mvp", WasmFeatures::MVP),
("wasm1", WasmFeatures::WASM1),
("wasm2", WasmFeatures::WASM2),
("wasm3", WasmFeatures::WASM3),
("lime1", WasmFeatures::LIME1),
];

enum Action {
ChangeAll,
Group(WasmFeatures),
Modify(WasmFeatures),
}

fn actions() -> impl Iterator<Item = (&'static str, Action)> {
WasmFeatures::FLAGS
.iter()
.map(|f| (f.name(), Action::Modify(*f.value())))
.chain(
GROUPS
.iter()
.map(|(name, features)| (*name, Action::Group(*features))),
)
.chain([("all", Action::ChangeAll)])
}

fn flag_name(name: &str) -> String {
name.to_lowercase().replace('_', "-")
}

'outer: for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) {
let (enable, part) = if let Some(part) = part.strip_prefix("-") {
(false, part)
} else {
(true, part)
};
for (name, action) in actions() {
if part != flag_name(name) {
continue;
}
match action {
Action::ChangeAll => {
ret.push(if enable {
FeatureAction::Enable(WasmFeatures::all())
} else {
FeatureAction::Disable(WasmFeatures::all())
});
}
Action::Modify(feature) => {
ret.push(if enable {
FeatureAction::Enable(feature)
} else {
FeatureAction::Disable(feature)
});
}
Action::Group(features) => {
if !enable {
bail!("cannot disable `{part}`, it can only be enabled");
}
ret.push(FeatureAction::Reset(features));
}
}
continue 'outer;
}

let mut error = format!("unknown feature `{part}`\n");
error.push_str("Valid features: ");
let mut first = true;
for (name, _) in actions() {
if first {
first = false;
} else {
error.push_str(", ");
}
error.push_str(&flag_name(name));
}
bail!("{error}")
}

Ok(ret)
}
Loading
Loading