diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a0abbba..d5aaf6e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -10,6 +10,25 @@ env: CARGO_TERM_COLOR: always jobs: + rustfmt: + runs-on: "ubuntu-latest" + steps: + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + components: rustfmt + + - uses: actions/checkout@v2 + + - name: Cargo fmt + uses: actions-rs/cargo@v1 + with: + toolchain: nightly + command: fmt + args: --all -- --check + test: strategy: matrix: @@ -23,31 +42,30 @@ jobs: profile: minimal target: wasm32-unknown-unknown toolchain: ${{ matrix.toolchain }} - components: clippy, rustfmt - uses: actions/checkout@v2 - - name: Build + - name: Cargo build uses: actions-rs/cargo@v1 with: toolchain: ${{ matrix.toolchain }} command: build - - name: Build no_std + - name: Cargo build (no_std) uses: actions-rs/cargo@v1 with: toolchain: ${{ matrix.toolchain }} command: build args: --no-default-features - - name: Build wasm + - name: Cargo build (wasm) uses: actions-rs/cargo@v1 with: toolchain: ${{ matrix.toolchain }} command: build args: --no-default-features --target wasm32-unknown-unknown - - name: Test + - name: Cargo test uses: actions-rs/cargo@v1 with: toolchain: ${{ matrix.toolchain }} diff --git a/cli/build/main.rs b/cli/build/main.rs index 35fdbaf..827487d 100644 --- a/cli/build/main.rs +++ b/cli/build/main.rs @@ -1,13 +1,12 @@ //! Experimental build tool for cargo -use pwasm_utils::{build, BuildError, SourceTarget, TargetRuntime, logger}; +use pwasm_utils::{build, logger, BuildError, SourceTarget, TargetRuntime}; mod source; -use std::{fs, io}; -use std::path::PathBuf; +use std::{fs, io, path::PathBuf}; -use clap::{App, Arg, crate_version}; +use clap::{crate_version, App, Arg}; use parity_wasm::elements; #[derive(Debug)] @@ -25,9 +24,17 @@ impl std::fmt::Display for Error { match self { Io(io) => write!(f, "Generic i/o error: {}", io), FailedToCopy(msg) => write!(f, "{}. Have you tried to run \"cargo build\"?", msg), - Decoding(err, file) => write!(f, "Decoding error ({}). Must be a valid wasm file {}. Pointed wrong file?", err, file), - Encoding(err) => write!(f, "Encoding error ({}). Almost impossible to happen, no free disk space?", err), - Build(err) => write!(f, "Build error: {}", err) + Decoding(err, file) => write!( + f, + "Decoding error ({}). Must be a valid wasm file {}. Pointed wrong file?", + err, file + ), + Encoding(err) => write!( + f, + "Encoding error ({}). Almost impossible to happen, no free disk space?", + err + ), + Build(err) => write!(f, "Build error: {}", err), } } } @@ -41,21 +48,23 @@ pub fn wasm_path(input: &source::SourceInput) -> String { pub fn process_output(input: &source::SourceInput) -> Result<(), Error> { let mut cargo_path = PathBuf::from(input.target_dir()); let wasm_name = input.bin_name().to_string().replace("-", "_"); - cargo_path.push( - match input.target() { - SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET, - SourceTarget::Unknown => source::UNKNOWN_TRIPLET, - } - ); + cargo_path.push(match input.target() { + SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET, + SourceTarget::Unknown => source::UNKNOWN_TRIPLET, + }); cargo_path.push("release"); cargo_path.push(format!("{}.wasm", wasm_name)); let mut target_path = PathBuf::from(input.target_dir()); target_path.push(format!("{}.wasm", input.final_name())); - fs::copy(cargo_path.as_path(), target_path.as_path()) - .map_err(|io| Error::FailedToCopy( - format!("Failed to copy '{}' to '{}': {}", cargo_path.display(), target_path.display(), io) - ))?; + fs::copy(cargo_path.as_path(), target_path.as_path()).map_err(|io| { + Error::FailedToCopy(format!( + "Failed to copy '{}' to '{}': {}", + cargo_path.display(), + target_path.display(), + io + )) + })?; Ok(()) } @@ -121,13 +130,18 @@ fn do_main() -> Result<(), Error> { let mut source_input = source::SourceInput::new(target_dir, wasm_binary); - let source_target_val = matches.value_of("source_target").unwrap_or_else(|| source::EMSCRIPTEN_TRIPLET); + let source_target_val = + matches.value_of("source_target").unwrap_or_else(|| source::EMSCRIPTEN_TRIPLET); if source_target_val == source::UNKNOWN_TRIPLET { source_input = source_input.unknown() } else if source_target_val == source::EMSCRIPTEN_TRIPLET { source_input = source_input.emscripten() } else { - eprintln!("--target can be: '{}' or '{}'", source::EMSCRIPTEN_TRIPLET, source::UNKNOWN_TRIPLET); + eprintln!( + "--target can be: '{}' or '{}'", + source::EMSCRIPTEN_TRIPLET, + source::UNKNOWN_TRIPLET + ); ::std::process::exit(1); } @@ -139,29 +153,34 @@ fn do_main() -> Result<(), Error> { let path = wasm_path(&source_input); - let module = parity_wasm::deserialize_file(&path) - .map_err(|e| Error::Decoding(e, path.to_string()))?; + let module = + parity_wasm::deserialize_file(&path).map_err(|e| Error::Decoding(e, path.to_string()))?; - let runtime_type_version = if let (Some(runtime_type), Some(runtime_version)) - = (matches.value_of("runtime_type"), matches.value_of("runtime_version")) { + let runtime_type_version = if let (Some(runtime_type), Some(runtime_version)) = + (matches.value_of("runtime_type"), matches.value_of("runtime_version")) + { let mut ty: [u8; 4] = Default::default(); let runtime_bytes = runtime_type.as_bytes(); if runtime_bytes.len() != 4 { panic!("--runtime-type should be equal to 4 bytes"); } ty.copy_from_slice(runtime_bytes); - let version: u32 = runtime_version.parse() - .expect("--runtime-version should be a positive integer"); + let version: u32 = + runtime_version.parse().expect("--runtime-version should be a positive integer"); Some((ty, version)) } else { None }; - let public_api_entries: Vec<_> = matches.value_of("public_api") + let public_api_entries: Vec<_> = matches + .value_of("public_api") .map(|val| val.split(',').collect()) .unwrap_or_default(); - let target_runtime = match matches.value_of("target-runtime").expect("target-runtime has a default value; qed") { + let target_runtime = match matches + .value_of("target-runtime") + .expect("target-runtime has a default value; qed") + { "pwasm" => TargetRuntime::pwasm(), "substrate" => TargetRuntime::substrate(), _ => unreachable!("all possible values are enumerated in clap config; qed"), @@ -173,21 +192,22 @@ fn do_main() -> Result<(), Error> { runtime_type_version, &public_api_entries, matches.is_present("enforce_stack_adjustment"), - matches.value_of("shrink_stack").unwrap_or_else(|| "49152").parse() + matches + .value_of("shrink_stack") + .unwrap_or_else(|| "49152") + .parse() .expect("New stack size is not valid u32"), matches.is_present("skip_optimization"), &target_runtime, - ).map_err(Error::Build)?; + ) + .map_err(Error::Build)?; if let Some(save_raw_path) = matches.value_of("save_raw") { parity_wasm::serialize_to_file(save_raw_path, module.clone()).map_err(Error::Encoding)?; } if let Some(ctor_module) = ctor_module { - parity_wasm::serialize_to_file( - &path, - ctor_module, - ).map_err(Error::Encoding)?; + parity_wasm::serialize_to_file(&path, ctor_module).map_err(Error::Encoding)?; } else { parity_wasm::serialize_to_file(&path, module).map_err(Error::Encoding)?; } @@ -204,11 +224,10 @@ fn main() { #[cfg(test)] mod tests { - use tempdir::TempDir; use std::fs; + use tempdir::TempDir; - use super::process_output; - use super::source::SourceInput; + use super::{process_output, source::SourceInput}; #[test] fn processes_cargo_output() { @@ -230,8 +249,8 @@ mod tests { process_output(&input).expect("process output failed"); - assert!( - fs::metadata(tmp_dir.path().join("example-wasm.wasm")).expect("metadata failed").is_file() - ) + assert!(fs::metadata(tmp_dir.path().join("example-wasm.wasm")) + .expect("metadata failed") + .is_file()) } } diff --git a/cli/build/source.rs b/cli/build/source.rs index 0f7bbc3..3c39923 100644 --- a/cli/build/source.rs +++ b/cli/build/source.rs @@ -16,12 +16,7 @@ pub struct SourceInput<'a> { impl<'a> SourceInput<'a> { pub fn new<'b>(target_dir: &'b str, bin_name: &'b str) -> SourceInput<'b> { - SourceInput { - target_dir, - bin_name, - final_name: bin_name, - target: SourceTarget::Emscripten, - } + SourceInput { target_dir, bin_name, final_name: bin_name, target: SourceTarget::Emscripten } } pub fn unknown(mut self) -> Self { diff --git a/cli/check/main.rs b/cli/check/main.rs index be6d5e5..b439d15 100644 --- a/cli/check/main.rs +++ b/cli/check/main.rs @@ -1,6 +1,6 @@ -use pwasm_utils::logger; use clap::{App, Arg}; use parity_wasm::elements; +use pwasm_utils::logger; fn fail(msg: &str) -> ! { eprintln!("{}", msg); @@ -32,22 +32,20 @@ const ALLOWED_IMPORTS: &[&str] = &[ "suicide", "panic", "elog", - "abort" + "abort", ]; fn main() { logger::init(); let matches = App::new("wasm-check") - .arg(Arg::with_name("input") - .index(1) - .required(true) - .help("Input WASM file")) - .get_matches(); + .arg(Arg::with_name("input").index(1).required(true).help("Input WASM file")) + .get_matches(); let input = matches.value_of("input").expect("is required; qed"); - let module = parity_wasm::deserialize_file(&input).expect("Input module deserialization failed"); + let module = + parity_wasm::deserialize_file(&input).expect("Input module deserialization failed"); for section in module.sections() { match section { @@ -60,7 +58,10 @@ fn main() { match entry.external() { elements::External::Function(_) => { if !ALLOWED_IMPORTS.contains(&entry.field()) { - fail(&format!("'{}' is not supported by the runtime", entry.field())); + fail(&format!( + "'{}' is not supported by the runtime", + entry.field() + )); } }, elements::External::Memory(m) => { @@ -81,18 +82,17 @@ fn main() { )); } }, - elements::External::Global(_) => { - fail("Parity runtime does not provide any globals") - }, - _ => { continue; } + elements::External::Global(_) => + fail("Parity runtime does not provide any globals"), + _ => continue, } } if !has_imported_memory_properly_named { fail("No imported memory from env::memory in the contract"); } - } - _ => { continue; } + }, + _ => continue, } } } diff --git a/cli/ext/main.rs b/cli/ext/main.rs index 72d51d6..03c157b 100644 --- a/cli/ext/main.rs +++ b/cli/ext/main.rs @@ -4,7 +4,7 @@ fn main() { let args = std::env::args().collect::>(); if args.len() != 3 { println!("Usage: {} input_file.wasm output_file.wasm", args[0]); - return; + return } let module = pwasm_utils::externalize( diff --git a/cli/gas/main.rs b/cli/gas/main.rs index 785c598..0eafffa 100644 --- a/cli/gas/main.rs +++ b/cli/gas/main.rs @@ -7,15 +7,15 @@ fn main() { let args = env::args().collect::>(); if args.len() != 3 { println!("Usage: {} input_file.wasm output_file.wasm", args[0]); - return; + return } // Loading module - let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); + let module = + parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - let result = utils::inject_gas_counter( - module, &utils::rules::Set::default(), "env" - ).expect("Failed to inject gas. Some forbidden opcodes?"); + let result = utils::inject_gas_counter(module, &utils::rules::Set::default(), "env") + .expect("Failed to inject gas. Some forbidden opcodes?"); parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed") } diff --git a/cli/pack/main.rs b/cli/pack/main.rs index ab2baab..edbeb77 100644 --- a/cli/pack/main.rs +++ b/cli/pack/main.rs @@ -1,5 +1,5 @@ -use pwasm_utils::{self as utils, logger}; use clap::{App, Arg}; +use pwasm_utils::{self as utils, logger}; fn main() { logger::init(); @@ -7,27 +7,25 @@ fn main() { let target_runtime = utils::TargetRuntime::pwasm(); let matches = App::new("wasm-pack") - .arg(Arg::with_name("input") - .index(1) - .required(true) - .help("Input WASM file")) - .arg(Arg::with_name("output") - .index(2) - .required(true) - .help("Output WASM file")) + .arg(Arg::with_name("input").index(1).required(true).help("Input WASM file")) + .arg(Arg::with_name("output").index(2).required(true).help("Output WASM file")) .get_matches(); let input = matches.value_of("input").expect("is required; qed"); let output = matches.value_of("output").expect("is required; qed"); - let module = parity_wasm::deserialize_file(&input).expect("Input module deserialization failed"); + let module = + parity_wasm::deserialize_file(&input).expect("Input module deserialization failed"); let ctor_module = module.clone(); let raw_module = parity_wasm::serialize(module).expect("Serialization failed"); // Invoke packer - let mut result_module = utils::pack_instance(raw_module, ctor_module, &utils::TargetRuntime::pwasm()).expect("Packing failed"); + let mut result_module = + utils::pack_instance(raw_module, ctor_module, &utils::TargetRuntime::pwasm()) + .expect("Packing failed"); // Optimize constructor, since it does not need everything - utils::optimize(&mut result_module, vec![target_runtime.symbols().call]).expect("Optimization failed"); + utils::optimize(&mut result_module, vec![target_runtime.symbols().call]) + .expect("Optimization failed"); parity_wasm::serialize_to_file(&output, result_module).expect("Serialization failed"); } diff --git a/cli/prune/main.rs b/cli/prune/main.rs index b6c99c7..c9c0177 100644 --- a/cli/prune/main.rs +++ b/cli/prune/main.rs @@ -1,5 +1,5 @@ -use pwasm_utils::{self as utils, logger}; use clap::{App, Arg}; +use pwasm_utils::{self as utils, logger}; fn main() { logger::init(); @@ -7,27 +7,26 @@ fn main() { let target_runtime = utils::TargetRuntime::pwasm(); let matches = App::new("wasm-prune") - .arg(Arg::with_name("input") - .index(1) - .required(true) - .help("Input WASM file")) - .arg(Arg::with_name("output") - .index(2) - .required(true) - .help("Output WASM file")) - .arg(Arg::with_name("exports") - .long("exports") - .short("e") - .takes_value(true) - .value_name("functions") - .help(&format!("Comma-separated list of exported functions to keep. Default: '{}'", target_runtime.symbols().call))) + .arg(Arg::with_name("input").index(1).required(true).help("Input WASM file")) + .arg(Arg::with_name("output").index(2).required(true).help("Output WASM file")) + .arg( + Arg::with_name("exports") + .long("exports") + .short("e") + .takes_value(true) + .value_name("functions") + .help(&format!( + "Comma-separated list of exported functions to keep. Default: '{}'", + target_runtime.symbols().call + )), + ) .get_matches(); let exports = matches - .value_of("exports") - .unwrap_or(target_runtime.symbols().call) - .split(',') - .collect(); + .value_of("exports") + .unwrap_or(target_runtime.symbols().call) + .split(',') + .collect(); let input = matches.value_of("input").expect("is required; qed"); let output = matches.value_of("output").expect("is required; qed"); diff --git a/cli/stack_height/main.rs b/cli/stack_height/main.rs index e20f002..e7c6a87 100644 --- a/cli/stack_height/main.rs +++ b/cli/stack_height/main.rs @@ -7,18 +7,18 @@ fn main() { let args = env::args().collect::>(); if args.len() != 3 { println!("Usage: {} input_file.wasm output_file.wasm", args[0]); - return; + return } let input_file = &args[1]; let output_file = &args[2]; // Loading module - let module = parity_wasm::deserialize_file(&input_file).expect("Module deserialization to succeed"); + let module = + parity_wasm::deserialize_file(&input_file).expect("Module deserialization to succeed"); - let result = stack_height::inject_limiter( - module, 1024 - ).expect("Failed to inject stack height counter"); + let result = + stack_height::inject_limiter(module, 1024).expect("Failed to inject stack height counter"); parity_wasm::serialize_to_file(&output_file, result).expect("Module serialization to succeed") } diff --git a/examples/opt_imports.rs b/examples/opt_imports.rs index 096f7bd..07ad201 100644 --- a/examples/opt_imports.rs +++ b/examples/opt_imports.rs @@ -4,13 +4,14 @@ fn main() { let args = env::args().collect::>(); if args.len() != 3 { println!("Usage: {} input_file.wasm output_file.wasm", args[0]); - return; + return } // Loading module let mut module = pwasm_utils::Module::from_elements( - &parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed") - ).expect("Failed to parse parity-wasm format"); + &parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"), + ) + .expect("Failed to parse parity-wasm format"); let mut delete_types = Vec::new(); for type_ in module.types.iter() { @@ -20,7 +21,9 @@ fn main() { } module.types.delete(&delete_types[..]); - parity_wasm::serialize_to_file(&args[2], - module.generate().expect("Failed to generate valid format") - ).expect("Module serialization to succeed") + parity_wasm::serialize_to_file( + &args[2], + module.generate().expect("Failed to generate valid format"), + ) + .expect("Module serialization to succeed") } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..15e9bdc --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,20 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Misc +chain_width = 80 +spaces_around_ranges = false +binop_separator = "Back" +reorder_impl_items = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true diff --git a/src/build.rs b/src/build.rs index 8f742fc..6cdd975 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,14 +1,6 @@ use super::{ - optimize, - pack_instance, - ununderscore_funcs, - externalize_mem, - shrink_unknown_stack, - inject_runtime_type, - PackingError, - OptimizerError, - TargetRuntime, - std::fmt, + externalize_mem, inject_runtime_type, optimize, pack_instance, shrink_unknown_stack, std::fmt, + ununderscore_funcs, OptimizerError, PackingError, TargetRuntime, }; use parity_wasm::elements; @@ -67,7 +59,6 @@ pub fn build( skip_optimization: bool, target_runtime: &TargetRuntime, ) -> Result<(elements::Module, Option), Error> { - if let SourceTarget::Emscripten = source_target { module = ununderscore_funcs(module); } @@ -75,11 +66,14 @@ pub fn build( if let SourceTarget::Unknown = source_target { // 49152 is 48kb! if enforce_stack_adjustment { - assert!(stack_size <= 1024*1024); - let (new_module, new_stack_top) = shrink_unknown_stack(module, 1024 * 1024 - stack_size); + assert!(stack_size <= 1024 * 1024); + let (new_module, new_stack_top) = + shrink_unknown_stack(module, 1024 * 1024 - stack_size); module = new_module; let mut stack_top_page = new_stack_top / 65536; - if new_stack_top % 65536 > 0 { stack_top_page += 1 }; + if new_stack_top % 65536 > 0 { + stack_top_page += 1 + }; module = externalize_mem(module, Some(stack_top_page), 16); } else { module = externalize_mem(module, None, 16); @@ -106,7 +100,8 @@ pub fn build( if !skip_optimization { let preserved_exports = match target_runtime { TargetRuntime::PWasm(_) => vec![target_runtime.symbols().create], - TargetRuntime::Substrate(_) => vec![target_runtime.symbols().call, target_runtime.symbols().create], + TargetRuntime::Substrate(_) => + vec![target_runtime.symbols().call, target_runtime.symbols().create], }; optimize(&mut ctor_module, preserved_exports)?; } diff --git a/src/export_globals.rs b/src/export_globals.rs index 0214b51..177e546 100644 --- a/src/export_globals.rs +++ b/src/export_globals.rs @@ -1,25 +1,36 @@ use parity_wasm::elements; -use crate::optimizer::{global_section, export_section}; +use crate::optimizer::{export_section, global_section}; /// Export all declared mutable globals. /// /// This will export all internal mutable globals under the name of /// concat(`prefix`, i) where i is the index inside the range of /// [0..]. -pub fn export_mutable_globals( - module: &mut elements::Module, - prefix: impl Into, -) { - - let exports = global_section(module).map( - |section| section.entries().iter().enumerate().filter_map( - |(index, global)| if global.global_type().is_mutable() { Some(index) } else { None } - ).collect::>() - ).unwrap_or_default(); +pub fn export_mutable_globals(module: &mut elements::Module, prefix: impl Into) { + let exports = global_section(module) + .map(|section| { + section + .entries() + .iter() + .enumerate() + .filter_map( + |(index, global)| { + if global.global_type().is_mutable() { + Some(index) + } else { + None + } + }, + ) + .collect::>() + }) + .unwrap_or_default(); if module.export_section().is_none() { - module.sections_mut().push(elements::Section::Export(elements::ExportSection::default())); + module + .sections_mut() + .push(elements::Section::Export(elements::ExportSection::default())); } let prefix: String = prefix.into(); @@ -27,7 +38,7 @@ pub fn export_mutable_globals( let new_entry = elements::ExportEntry::new( format!("{}_{}", prefix, symbol_index), elements::Internal::Global( - (module.import_count(elements::ImportCountType::Global) + export) as _ + (module.import_count(elements::ImportCountType::Global) + export) as _, ), ); export_section(module) @@ -48,8 +59,7 @@ mod tests { .validate(true) .convert(source) .expect("failed to parse module"); - elements::deserialize_buffer(module_bytes.as_ref()) - .expect("failed to parse module") + elements::deserialize_buffer(module_bytes.as_ref()).expect("failed to parse module") } macro_rules! test_export_global { @@ -69,7 +79,7 @@ mod tests { assert_eq!(actual_bytes, expected_bytes); } - } + }; } test_export_global! { diff --git a/src/ext.rs b/src/ext.rs index 3b129e7..94a5530 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -1,15 +1,17 @@ -use crate::std::string::String; -use crate::std::vec::Vec; -use crate::std::borrow::ToOwned; +use crate::std::{borrow::ToOwned, string::String, vec::Vec}; -use parity_wasm::{elements, builder}; -use byteorder::{LittleEndian, ByteOrder}; +use byteorder::{ByteOrder, LittleEndian}; +use parity_wasm::{builder, elements}; -use crate::optimizer::{import_section, export_section}; +use crate::optimizer::{export_section, import_section}; type Insertion = (usize, u32, u32, String); -pub fn update_call_index(instructions: &mut elements::Instructions, original_imports: usize, inserts: &[Insertion]) { +pub fn update_call_index( + instructions: &mut elements::Instructions, + original_imports: usize, + inserts: &[Insertion], +) { use parity_wasm::elements::Instruction::*; for instruction in instructions.elements_mut().iter_mut() { if let Call(call_index) = instruction { @@ -24,14 +26,18 @@ pub fn update_call_index(instructions: &mut elements::Instructions, original_imp pub fn memory_section(module: &mut elements::Module) -> Option<&mut elements::MemorySection> { for section in module.sections_mut() { - if let elements::Section::Memory(sect) = section { - return Some(sect); + if let elements::Section::Memory(sect) = section { + return Some(sect) } } None } -pub fn externalize_mem(mut module: elements::Module, adjust_pages: Option, max_pages: u32) -> elements::Module { +pub fn externalize_mem( + mut module: elements::Module, + adjust_pages: Option, + max_pages: u32, +) -> elements::Module { let mut entry = memory_section(&mut module) .expect("Memory section to exist") .entries_mut() @@ -48,19 +54,18 @@ pub fn externalize_mem(mut module: elements::Module, adjust_pages: Option, } let mut builder = builder::from_module(module); - builder.push_import( - elements::ImportEntry::new( - "env".to_owned(), - "memory".to_owned(), - elements::External::Memory(entry), - ) - ); + builder.push_import(elements::ImportEntry::new( + "env".to_owned(), + "memory".to_owned(), + elements::External::Memory(entry), + )); builder.build() } fn foreach_public_func_name(mut module: elements::Module, f: F) -> elements::Module -where F: Fn(&mut String) +where + F: Fn(&mut String), { if let Some(section) = import_section(&mut module) { for entry in section.entries_mut() { @@ -86,7 +91,9 @@ pub fn underscore_funcs(module: elements::Module) -> elements::Module { } pub fn ununderscore_funcs(module: elements::Module) -> elements::Module { - foreach_public_func_name(module, |n| { n.remove(0); }) + foreach_public_func_name(module, |n| { + n.remove(0); + }) } pub fn shrink_unknown_stack( @@ -113,19 +120,17 @@ pub fn shrink_unknown_stack( } } }, - _ => continue + _ => continue, } } (module, new_stack_top) } -pub fn externalize( - module: elements::Module, - replaced_funcs: Vec<&str>, -) -> elements::Module { - // Save import functions number for later +pub fn externalize(module: elements::Module, replaced_funcs: Vec<&str>) -> elements::Module { + // Save import functions number for later let import_funcs_total = module - .import_section().expect("Import section to exist") + .import_section() + .expect("Import section to exist") .entries() .iter() .filter(|e| matches!(e.external(), &elements::External::Function(_))) @@ -137,16 +142,19 @@ pub fn externalize( .into_iter() .filter_map(|f| { let export = module - .export_section().expect("Export section to exist") - .entries().iter().enumerate() + .export_section() + .expect("Export section to exist") + .entries() + .iter() + .enumerate() .find(|&(_, entry)| entry.field() == f) .expect("All functions of interest to exist"); if let elements::Internal::Function(func_idx) = *export.1.internal() { - let type_ref = module - .function_section().expect("Functions section to exist") - .entries()[func_idx as usize - import_funcs_total] - .type_ref(); + let type_ref = + module.function_section().expect("Functions section to exist").entries() + [func_idx as usize - import_funcs_total] + .type_ref(); Some((export.0, func_idx, type_ref, export.1.field().to_owned())) } else { @@ -161,11 +169,7 @@ pub fn externalize( let mut mbuilder = builder::from_module(module); for (_, _, type_ref, field) in replaces.iter() { mbuilder.push_import( - builder::import() - .module("env") - .field(field) - .external().func(*type_ref) - .build() + builder::import().module("env").field(field).external().func(*type_ref).build(), ); } @@ -175,15 +179,16 @@ pub fn externalize( // Third, rewire all calls to imported functions and update all other calls indices for section in module.sections_mut() { match section { - elements::Section::Code(code_section) => { + elements::Section::Code(code_section) => for func_body in code_section.bodies_mut() { update_call_index(func_body.code_mut(), import_funcs_total, &replaces); - } - }, + }, elements::Section::Export(export_section) => { for export in export_section.entries_mut() { if let elements::Internal::Function(func_index) = export.internal_mut() { - if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; } + if *func_index >= import_funcs_total as u32 { + *func_index += replaces.len() as u32; + } } } }, @@ -191,14 +196,15 @@ pub fn externalize( for segment in elements_section.entries_mut() { // update all indirect call addresses initial values for func_index in segment.members_mut() { - if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; } + if *func_index >= import_funcs_total as u32 { + *func_index += replaces.len() as u32; + } } } }, - _ => { } + _ => {}, } } module - } diff --git a/src/gas/mod.rs b/src/gas/mod.rs index b31eb99..6466e9e 100644 --- a/src/gas/mod.rs +++ b/src/gas/mod.rs @@ -7,18 +7,18 @@ #[cfg(test)] mod validation; -use crate::std::cmp::min; -use crate::std::mem; -use crate::std::vec::Vec; +use crate::std::{cmp::min, mem, vec::Vec}; -use parity_wasm::{elements, elements::ValueType, builder}; use crate::rules::Rules; +use parity_wasm::{builder, elements, elements::ValueType}; pub fn update_call_index(instructions: &mut elements::Instructions, inserted_index: u32) { use parity_wasm::elements::Instruction::*; for instruction in instructions.elements_mut().iter_mut() { if let Call(call_index) = instruction { - if *call_index >= inserted_index { *call_index += 1} + if *call_index >= inserted_index { + *call_index += 1 + } } } } @@ -88,10 +88,7 @@ struct Counter { impl Counter { fn new() -> Counter { - Counter { - stack: Vec::new(), - finalized_blocks: Vec::new(), - } + Counter { stack: Vec::new(), finalized_blocks: Vec::new() } } /// Open a new control block. The cursor is the position of the first instruction in the block. @@ -99,10 +96,7 @@ impl Counter { let index = self.stack.len(); self.stack.push(ControlBlock { lowest_forward_br_target: index, - active_metered_block: MeteredBlock { - start_pos: cursor, - cost: 0, - }, + active_metered_block: MeteredBlock { start_pos: cursor, cost: 0 }, is_loop, }) } @@ -127,7 +121,7 @@ impl Counter { let control_block = self.stack.last_mut().ok_or_else(|| ())?; control_block.lowest_forward_br_target = min( control_block.lowest_forward_br_target, - closing_control_block.lowest_forward_br_target + closing_control_block.lowest_forward_br_target, ); } @@ -149,10 +143,7 @@ impl Counter { let control_block = self.stack.last_mut().ok_or_else(|| ())?; mem::replace( &mut control_block.active_metered_block, - MeteredBlock { - start_pos: cursor + 1, - cost: 0, - } + MeteredBlock { start_pos: cursor + 1, cost: 0 }, ) }; @@ -163,7 +154,9 @@ impl Counter { // cost into the other active metered block to avoid injecting unnecessary instructions. let last_index = self.stack.len() - 1; if last_index > 0 { - let prev_control_block = self.stack.get_mut(last_index - 1) + let prev_control_block = self + .stack + .get_mut(last_index - 1) .expect("last_index is greater than 0; last_index is stack size - 1; qed"); let prev_metered_block = &mut prev_control_block.active_metered_block; if closing_metered_block.start_pos == prev_metered_block.start_pos { @@ -192,7 +185,7 @@ impl Counter { target_block.is_loop }; if target_is_loop { - continue; + continue } let control_block = self.stack.last_mut().ok_or_else(|| ())?; @@ -237,10 +230,10 @@ fn inject_grow_counter(instructions: &mut elements::Instructions, grow_counter_f fn add_grow_counter( module: elements::Module, rules: &R, - gas_func: u32 + gas_func: u32, ) -> elements::Module { - use parity_wasm::elements::Instruction::*; use crate::rules::MemoryGrowCost; + use parity_wasm::elements::Instruction::*; let cost = match rules.memory_grow_cost() { None => return module, @@ -250,20 +243,23 @@ fn add_grow_counter( let mut b = builder::from_module(module); b.push_function( builder::function() - .signature().with_param(ValueType::I32).with_result(ValueType::I32).build() - .body() - .with_instructions(elements::Instructions::new(vec![ - GetLocal(0), - GetLocal(0), - I32Const(cost as i32), - I32Mul, - // todo: there should be strong guarantee that it does not return anything on stack? - Call(gas_func), - GrowMemory(0), - End, - ])) - .build() + .signature() + .with_param(ValueType::I32) + .with_result(ValueType::I32) .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + GetLocal(0), + GetLocal(0), + I32Const(cost as i32), + I32Mul, + // todo: there should be strong guarantee that it does not return anything on stack? + Call(gas_func), + GrowMemory(0), + End, + ])) + .build() + .build(), ); b.build() @@ -293,21 +289,21 @@ pub(crate) fn determine_metered_blocks( // unnecessary metering instructions. let top_block_start_pos = counter.active_metered_block()?.start_pos; counter.begin_control_block(top_block_start_pos, false); - } + }, If(_) => { counter.increment(instruction_cost)?; counter.begin_control_block(cursor + 1, false); - } + }, Loop(_) => { counter.increment(instruction_cost)?; counter.begin_control_block(cursor + 1, true); - } + }, End => { counter.finalize_control_block(cursor)?; }, Else => { counter.finalize_metered_block(cursor)?; - } + }, Br(label) | BrIf(label) => { counter.increment(instruction_cost)?; @@ -315,7 +311,7 @@ pub(crate) fn determine_metered_blocks( let active_index = counter.active_control_block_index().ok_or_else(|| ())?; let target_index = active_index.checked_sub(*label as usize).ok_or_else(|| ())?; counter.branch(cursor, &[target_index])?; - } + }, BrTable(br_table_data) => { counter.increment(instruction_cost)?; @@ -327,15 +323,15 @@ pub(crate) fn determine_metered_blocks( .collect::>>() .ok_or_else(|| ())?; counter.branch(cursor, &target_indices)?; - } + }, Return => { counter.increment(instruction_cost)?; counter.branch(cursor, &[0])?; - } + }, _ => { // An ordinal non control flow instruction increments the cost of the current block. counter.increment(instruction_cost)?; - } + }, } } @@ -357,17 +353,14 @@ fn insert_metering_calls( instructions: &mut elements::Instructions, blocks: Vec, gas_func: u32, -) - -> Result<(), ()> -{ +) -> Result<(), ()> { use parity_wasm::elements::Instruction::*; // To do this in linear time, construct a new vector of instructions, copying over old // instructions one by one and injecting new ones as required. let new_instrs_len = instructions.elements().len() + 2 * blocks.len(); - let original_instrs = mem::replace( - instructions.elements_mut(), Vec::with_capacity(new_instrs_len) - ); + let original_instrs = + mem::replace(instructions.elements_mut(), Vec::with_capacity(new_instrs_len)); let new_instrs = instructions.elements_mut(); let mut block_iter = blocks.into_iter().peekable(); @@ -378,8 +371,12 @@ fn insert_metering_calls( new_instrs.push(I32Const(block.cost as i32)); new_instrs.push(Call(gas_func)); true - } else { false } - } else { false }; + } else { + false + } + } else { + false + }; if used_block { block_iter.next(); @@ -390,7 +387,7 @@ fn insert_metering_calls( } if block_iter.next().is_some() { - return Err(()); + return Err(()) } Ok(()) @@ -434,24 +431,20 @@ pub fn inject_gas_counter( module: elements::Module, rules: &R, gas_module_name: &str, -) - -> Result -{ +) -> Result { // Injecting gas counting external let mut mbuilder = builder::from_module(module); - let import_sig = mbuilder.push_signature( - builder::signature() - .with_param(ValueType::I32) - .build_sig() - ); + let import_sig = + mbuilder.push_signature(builder::signature().with_param(ValueType::I32).build_sig()); mbuilder.push_import( builder::import() .module(gas_module_name) .field("gas") - .external().func(import_sig) - .build() - ); + .external() + .func(import_sig) + .build(), + ); // back to plain module let mut module = mbuilder.build(); @@ -467,24 +460,25 @@ pub fn inject_gas_counter( // Updating calling addresses (all calls to function index >= `gas_func` should be incremented) for section in module.sections_mut() { match section { - elements::Section::Code(code_section) => { + elements::Section::Code(code_section) => for func_body in code_section.bodies_mut() { update_call_index(func_body.code_mut(), gas_func); if inject_counter(func_body.code_mut(), rules, gas_func).is_err() { error = true; - break; + break } - if rules.memory_grow_cost().is_some() - && inject_grow_counter(func_body.code_mut(), total_func) > 0 + if rules.memory_grow_cost().is_some() && + inject_grow_counter(func_body.code_mut(), total_func) > 0 { need_grow_counter = true; } - } - }, + }, elements::Section::Export(export_section) => { for export in export_section.entries_mut() { if let elements::Internal::Function(func_index) = export.internal_mut() { - if *func_index >= gas_func { *func_index += 1} + if *func_index >= gas_func { + *func_index += 1 + } } } }, @@ -494,33 +488,43 @@ pub fn inject_gas_counter( for segment in elements_section.entries_mut() { // update all indirect call addresses initial values for func_index in segment.members_mut() { - if *func_index >= gas_func { *func_index += 1} + if *func_index >= gas_func { + *func_index += 1 + } } } }, - elements::Section::Start(start_idx) => { - if *start_idx >= gas_func { *start_idx += 1} - }, - _ => { } + elements::Section::Start(start_idx) => + if *start_idx >= gas_func { + *start_idx += 1 + }, + _ => {}, } } - if error { return Err(module); } + if error { + return Err(module) + } - if need_grow_counter { Ok(add_grow_counter(module, rules, gas_func)) } else { Ok(module) } + if need_grow_counter { + Ok(add_grow_counter(module, rules, gas_func)) + } else { + Ok(module) + } } #[cfg(test)] mod tests { - use parity_wasm::{serialize, builder, elements}; - use parity_wasm::elements::Instruction::*; use super::*; use crate::rules; + use parity_wasm::{builder, elements, elements::Instruction::*, serialize}; - pub fn get_function_body(module: &elements::Module, index: usize) - -> Option<&[elements::Instruction]> - { - module.code_section() + pub fn get_function_body( + module: &elements::Module, + index: usize, + ) -> Option<&[elements::Instruction]> { + module + .code_section() .and_then(|code_section| code_section.bodies().get(index)) .map(|func_body| func_body.code().elements()) } @@ -529,49 +533,32 @@ mod tests { fn simple_grow() { let module = builder::module() .global() - .value_type().i32() - .build() + .value_type() + .i32() + .build() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - GetGlobal(0), - GrowMemory(0), - End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![GetGlobal(0), GrowMemory(0), End])) + .build() + .build() .build(); - let injected_module = inject_gas_counter( - module, - &rules::Set::default().with_grow_cost(10000), - "env", - ).unwrap(); + let injected_module = + inject_gas_counter(module, &rules::Set::default().with_grow_cost(10000), "env") + .unwrap(); assert_eq!( get_function_body(&injected_module, 0).unwrap(), - &vec![ - I32Const(2), - Call(0), - GetGlobal(0), - Call(2), - End - ][..] + &vec![I32Const(2), Call(0), GetGlobal(0), Call(2), End][..] ); assert_eq!( get_function_body(&injected_module, 1).unwrap(), - &vec![ - GetLocal(0), - GetLocal(0), - I32Const(10000), - I32Mul, - Call(0), - GrowMemory(0), - End, - ][..] + &vec![GetLocal(0), GetLocal(0), I32Const(10000), I32Mul, Call(0), GrowMemory(0), End,] + [..] ); let binary = serialize(injected_module).expect("serialization failed"); @@ -582,33 +569,25 @@ mod tests { fn grow_no_gas_no_track() { let module = builder::module() .global() - .value_type().i32() - .build() + .value_type() + .i32() + .build() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - GetGlobal(0), - GrowMemory(0), - End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![GetGlobal(0), GrowMemory(0), End])) + .build() + .build() .build(); let injected_module = inject_gas_counter(module, &rules::Set::default(), "env").unwrap(); assert_eq!( get_function_body(&injected_module, 0).unwrap(), - &vec![ - I32Const(2), - Call(0), - GetGlobal(0), - GrowMemory(0), - End - ][..] + &vec![I32Const(2), Call(0), GetGlobal(0), GrowMemory(0), End][..] ); assert_eq!(injected_module.functions_space(), 2); @@ -621,32 +600,38 @@ mod tests { fn call_index() { let module = builder::module() .global() - .value_type().i32() - .build() + .value_type() + .i32() + .build() .function() - .signature().param().i32().build() - .body().build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .build() + .build() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - Call(0), - If(elements::BlockType::NoResult), - Call(0), - Call(0), - Call(0), - Else, - Call(0), - Call(0), - End, - Call(0), - End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + Call(0), + If(elements::BlockType::NoResult), + Call(0), + Call(0), + Call(0), + Else, + Call(0), + Call(0), + End, + Call(0), + End, + ])) + .build() + .build() .build(); let injected_module = inject_gas_counter(module, &rules::Set::default(), "env").unwrap(); @@ -658,16 +643,16 @@ mod tests { Call(0), Call(1), If(elements::BlockType::NoResult), - I32Const(3), - Call(0), - Call(1), - Call(1), - Call(1), + I32Const(3), + Call(0), + Call(1), + Call(1), + Call(1), Else, - I32Const(2), - Call(0), - Call(1), - Call(1), + I32Const(2), + Call(0), + Call(1), + Call(1), End, Call(1), End @@ -679,24 +664,22 @@ mod tests { fn forbidden() { let module = builder::module() .global() - .value_type().i32() - .build() + .value_type() + .i32() + .build() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - F32Const(555555), - End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![F32Const(555555), End])) + .build() + .build() .build(); let rules = rules::Set::default().with_forbidden_floats(); - if inject_gas_counter(module, &rules, "env").is_ok() { panic!("Should be error because of the forbidden operation") } @@ -707,8 +690,7 @@ mod tests { .validate(false) .convert(source) .expect("failed to parse module"); - elements::deserialize_buffer(module_bytes.as_ref()) - .expect("failed to parse module") + elements::deserialize_buffer(module_bytes.as_ref()).expect("failed to parse module") } macro_rules! test_gas_counter_injection { @@ -718,8 +700,9 @@ mod tests { let input_module = parse_wat($input); let expected_module = parse_wat($expected); - let injected_module = inject_gas_counter(input_module, &rules::Set::default(), "env") - .expect("inject_gas_counter call failed"); + let injected_module = + inject_gas_counter(input_module, &rules::Set::default(), "env") + .expect("inject_gas_counter call failed"); let actual_func_body = get_function_body(&injected_module, 0) .expect("injected module must have a function body"); @@ -728,7 +711,7 @@ mod tests { assert_eq!(actual_func_body, expected_func_body); } - } + }; } test_gas_counter_injection! { diff --git a/src/gas/validation.rs b/src/gas/validation.rs index 5f68dcc..3f7d2f5 100644 --- a/src/gas/validation.rs +++ b/src/gas/validation.rs @@ -9,15 +9,16 @@ //! the worst case. use super::MeteredBlock; -use crate::rules::Set as RuleSet; -use crate::rules::Rules; -use crate::std::vec::Vec; +use crate::{ + rules::{Rules, Set as RuleSet}, + std::vec::Vec, +}; use parity_wasm::elements::{FuncBody, Instruction}; -#[cfg(features = "std")] -use crate::std::collections::HashMap as Map; #[cfg(not(features = "std"))] use crate::std::collections::BTreeMap as Map; +#[cfg(features = "std")] +use crate::std::collections::HashMap as Map; /// An ID for a node in a ControlFlowGraph. type NodeId = usize; @@ -71,9 +72,7 @@ pub struct ControlFlowGraph { impl ControlFlowGraph { fn new() -> Self { - ControlFlowGraph { - nodes: Vec::new(), - } + ControlFlowGraph { nodes: Vec::new() } } fn get_node(&self, node_id: NodeId) -> &ControlFlowNode { @@ -145,7 +144,7 @@ impl ControlFrame { fn build_control_flow_graph( body: &FuncBody, rules: &RuleSet, - blocks: &[MeteredBlock] + blocks: &[MeteredBlock], ) -> Result { let mut graph = ControlFlowGraph::new(); @@ -159,16 +158,17 @@ fn build_control_flow_graph( let mut metered_blocks_iter = blocks.iter().peekable(); for (cursor, instruction) in body.code().elements().iter().enumerate() { - let active_node_id = stack.last() + let active_node_id = stack + .last() .expect("module is valid by pre-condition; control stack must not be empty; qed") .active_node; // Increment the charged cost if there are metering instructions to be inserted here. - let apply_block = metered_blocks_iter.peek() - .map_or(false, |block| block.start_pos == cursor); + let apply_block = + metered_blocks_iter.peek().map_or(false, |block| block.start_pos == cursor); if apply_block { - let next_metered_block = metered_blocks_iter.next() - .expect("peek returned an item; qed"); + let next_metered_block = + metered_blocks_iter.next().expect("peek returned an item; qed"); graph.increment_charged_cost(active_node_id, next_metered_block.cost); } @@ -179,7 +179,7 @@ fn build_control_flow_graph( let exit_node_id = graph.add_node(); stack.push(ControlFrame::new(active_node_id, exit_node_id, false)); - } + }, Instruction::If(_) => { graph.increment_actual_cost(active_node_id, instruction_cost); @@ -189,7 +189,7 @@ fn build_control_flow_graph( stack.push(ControlFrame::new(then_node_id, exit_node_id, false)); graph.new_forward_edge(active_node_id, then_node_id); graph.set_first_instr_pos(then_node_id, cursor + 1); - } + }, Instruction::Loop(_) => { graph.increment_actual_cost(active_node_id, instruction_cost); @@ -199,7 +199,7 @@ fn build_control_flow_graph( stack.push(ControlFrame::new(loop_node_id, exit_node_id, true)); graph.new_forward_edge(active_node_id, loop_node_id); graph.set_first_instr_pos(loop_node_id, cursor + 1); - } + }, Instruction::Else => { let active_frame_idx = stack.len() - 1; let prev_frame_idx = stack.len() - 2; @@ -210,7 +210,7 @@ fn build_control_flow_graph( let prev_node_id = stack[prev_frame_idx].active_node; graph.new_forward_edge(prev_node_id, else_node_id); graph.set_first_instr_pos(else_node_id, cursor + 1); - } + }, Instruction::End => { let closing_frame = stack.pop() .expect("module is valid by pre-condition; ends correspond to control stack frames; qed"); @@ -221,7 +221,7 @@ fn build_control_flow_graph( if let Some(active_frame) = stack.last_mut() { active_frame.active_node = closing_frame.exit_node; } - } + }, Instruction::Br(label) => { graph.increment_actual_cost(active_node_id, instruction_cost); @@ -233,7 +233,7 @@ fn build_control_flow_graph( let new_node_id = graph.add_node(); stack[active_frame_idx].active_node = new_node_id; graph.set_first_instr_pos(new_node_id, cursor + 1); - } + }, Instruction::BrIf(label) => { graph.increment_actual_cost(active_node_id, instruction_cost); @@ -245,7 +245,7 @@ fn build_control_flow_graph( stack[active_frame_idx].active_node = new_node_id; graph.new_forward_edge(active_node_id, new_node_id); graph.set_first_instr_pos(new_node_id, cursor + 1); - } + }, Instruction::BrTable(br_table_data) => { graph.increment_actual_cost(active_node_id, instruction_cost); @@ -258,7 +258,7 @@ fn build_control_flow_graph( let new_node_id = graph.add_node(); stack[active_frame_idx].active_node = new_node_id; graph.set_first_instr_pos(new_node_id, cursor + 1); - } + }, Instruction::Return => { graph.increment_actual_cost(active_node_id, instruction_cost); @@ -268,7 +268,7 @@ fn build_control_flow_graph( let new_node_id = graph.add_node(); stack[active_frame_idx].active_node = new_node_id; graph.set_first_instr_pos(new_node_id, cursor + 1); - } + }, _ => graph.increment_actual_cost(active_node_id, instruction_cost), } } @@ -304,20 +304,21 @@ fn validate_graph_gas_costs(graph: &ControlFlowGraph) -> bool { } if node.forward_edges.is_empty() && total_actual != total_charged { - return false; + return false } for loop_node_id in node.loopback_edges.iter() { - let (loop_actual, loop_charged) = loop_costs.get_mut(loop_node_id) + let (loop_actual, loop_charged) = loop_costs + .get_mut(loop_node_id) .expect("cannot arrive at loopback edge without visiting loop entry node"); if loop_actual != loop_charged { - return false; + return false } } for next_node_id in node.forward_edges.iter() { if !visit(graph, *next_node_id, total_actual, total_charged, loop_costs) { - return false; + return false } } @@ -339,18 +340,17 @@ fn validate_graph_gas_costs(graph: &ControlFlowGraph) -> bool { fn validate_metering_injections( body: &FuncBody, rules: &RuleSet, - blocks: &[MeteredBlock] + blocks: &[MeteredBlock], ) -> Result { let graph = build_control_flow_graph(body, rules, blocks)?; Ok(validate_graph_gas_costs(&graph)) } mod tests { - use super::*; - use super::super::determine_metered_blocks; + use super::{super::determine_metered_blocks, *}; - use parity_wasm::elements; use binaryen::tools::translate_to_fuzz_mvp; + use parity_wasm::elements; use rand::{thread_rng, RngCore}; #[test] @@ -367,7 +367,8 @@ mod tests { let rules = RuleSet::default(); let metered_blocks = determine_metered_blocks(func_body.code(), &rules).unwrap(); - let success = validate_metering_injections(func_body, &rules, &metered_blocks).unwrap(); + let success = + validate_metering_injections(func_body, &rules, &metered_blocks).unwrap(); assert!(success); } } diff --git a/src/graph.rs b/src/graph.rs index b675169..bb0ac2f 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -2,21 +2,16 @@ #![warn(missing_docs)] +use super::ref_list::{EntryRef, RefList}; +use crate::std::{borrow::ToOwned, collections::BTreeMap, string::String, vec::Vec}; use parity_wasm::elements; -use super::ref_list::{RefList, EntryRef}; -use crate::std::{ - vec::Vec, - borrow::ToOwned, - string::String, - collections::BTreeMap, -}; /// Imported or declared variant of the same thing. /// /// In WebAssembly, function/global/memory/table instances can either be /// imported or declared internally, forming united index space. #[derive(Debug)] -pub enum ImportedOrDeclared { +pub enum ImportedOrDeclared { /// Variant for imported instances. Imported(String, String), /// Variant for instances declared internally in the module. @@ -206,38 +201,43 @@ pub struct Module { } impl Module { - fn map_instructions(&self, instructions: &[elements::Instruction]) -> Vec { use parity_wasm::elements::Instruction::*; - instructions.iter().map(|instruction| match instruction { - Call(func_idx) => Instruction::Call(self.funcs.clone_ref(*func_idx as usize)), - CallIndirect(type_idx, arg2) => - Instruction::CallIndirect( - self.types.clone_ref(*type_idx as usize), - *arg2, - ), - SetGlobal(global_idx) => - Instruction::SetGlobal(self.globals.clone_ref(*global_idx as usize)), - GetGlobal(global_idx) => - Instruction::GetGlobal(self.globals.clone_ref(*global_idx as usize)), - other_instruction => Instruction::Plain(other_instruction.clone()), - }).collect() + instructions + .iter() + .map(|instruction| match instruction { + Call(func_idx) => Instruction::Call(self.funcs.clone_ref(*func_idx as usize)), + CallIndirect(type_idx, arg2) => + Instruction::CallIndirect(self.types.clone_ref(*type_idx as usize), *arg2), + SetGlobal(global_idx) => + Instruction::SetGlobal(self.globals.clone_ref(*global_idx as usize)), + GetGlobal(global_idx) => + Instruction::GetGlobal(self.globals.clone_ref(*global_idx as usize)), + other_instruction => Instruction::Plain(other_instruction.clone()), + }) + .collect() } fn generate_instructions(&self, instructions: &[Instruction]) -> Vec { use parity_wasm::elements::Instruction::*; - instructions.iter().map(|instruction| match instruction { - Instruction::Call(func_ref) => Call(func_ref.order().expect("detached instruction!") as u32), - Instruction::CallIndirect(type_ref, arg2) => CallIndirect(type_ref.order().expect("detached instruction!") as u32, *arg2), - Instruction::SetGlobal(global_ref) => SetGlobal(global_ref.order().expect("detached instruction!") as u32), - Instruction::GetGlobal(global_ref) => GetGlobal(global_ref.order().expect("detached instruction!") as u32), - Instruction::Plain(plain) => plain.clone(), - }).collect() + instructions + .iter() + .map(|instruction| match instruction { + Instruction::Call(func_ref) => + Call(func_ref.order().expect("detached instruction!") as u32), + Instruction::CallIndirect(type_ref, arg2) => + CallIndirect(type_ref.order().expect("detached instruction!") as u32, *arg2), + Instruction::SetGlobal(global_ref) => + SetGlobal(global_ref.order().expect("detached instruction!") as u32), + Instruction::GetGlobal(global_ref) => + GetGlobal(global_ref.order().expect("detached instruction!") as u32), + Instruction::Plain(plain) => plain.clone(), + }) + .collect() } /// Initialize module from parity-wasm `Module`. pub fn from_elements(module: &elements::Module) -> Result { - let mut res = Module::default(); let mut imported_functions = 0; @@ -246,21 +246,23 @@ impl Module { elements::Section::Type(type_section) => { res.types = RefList::from_slice(type_section.types()); }, - elements::Section::Import(import_section) => { + elements::Section::Import(import_section) => for entry in import_section.entries() { match *entry.external() { elements::External::Function(f) => { res.funcs.push(Func { - type_ref: res.types.get(f as usize).ok_or(Error::InconsistentSource)?.clone(), + type_ref: res + .types + .get(f as usize) + .ok_or(Error::InconsistentSource)? + .clone(), origin: entry.into(), }); imported_functions += 1; }, elements::External::Memory(m) => { - res.memory.push(Memory { - limits: *m.limits(), - origin: entry.into(), - }); + res.memory + .push(Memory { limits: *m.limits(), origin: entry.into() }); }, elements::External::Global(g) => { res.globals.push(Global { @@ -270,44 +272,42 @@ impl Module { }); }, elements::External::Table(t) => { - res.tables.push(Table { - limits: *t.limits(), - origin: entry.into(), - }); + res.tables + .push(Table { limits: *t.limits(), origin: entry.into() }); }, }; - } - }, + }, elements::Section::Function(function_section) => { for f in function_section.entries() { res.funcs.push(Func { - type_ref: res.types.get(f.type_ref() as usize) - .ok_or(Error::InconsistentSource)?.clone(), + type_ref: res + .types + .get(f.type_ref() as usize) + .ok_or(Error::InconsistentSource)? + .clone(), origin: ImportedOrDeclared::Declared(FuncBody { locals: Vec::new(), // code will be populated later code: Vec::new(), }), }); - }; + } }, - elements::Section::Table(table_section) => { + elements::Section::Table(table_section) => for t in table_section.entries() { res.tables.push(Table { limits: *t.limits(), origin: ImportedOrDeclared::Declared(()), }); - } - }, - elements::Section::Memory(table_section) => { + }, + elements::Section::Memory(table_section) => for t in table_section.entries() { res.memory.push(Memory { limits: *t.limits(), origin: ImportedOrDeclared::Declared(()), }); - } - }, - elements::Section::Global(global_section) => { + }, + elements::Section::Global(global_section) => for g in global_section.entries() { let init_code = res.map_instructions(g.init_expr().code()); res.globals.push(Global { @@ -315,34 +315,27 @@ impl Module { is_mut: g.global_type().is_mutable(), origin: ImportedOrDeclared::Declared(init_code), }); - } - }, - elements::Section::Export(export_section) => { + }, + elements::Section::Export(export_section) => for e in export_section.entries() { let local = match e.internal() { - elements::Internal::Function(func_idx) => { - ExportLocal::Func(res.funcs.clone_ref(*func_idx as usize)) - }, - elements::Internal::Global(global_idx) => { - ExportLocal::Global(res.globals.clone_ref(*global_idx as usize)) - }, - elements::Internal::Memory(mem_idx) => { - ExportLocal::Memory(res.memory.clone_ref(*mem_idx as usize)) - }, - elements::Internal::Table(table_idx) => { - ExportLocal::Table(res.tables.clone_ref(*table_idx as usize)) - }, + elements::Internal::Function(func_idx) => + ExportLocal::Func(res.funcs.clone_ref(*func_idx as usize)), + elements::Internal::Global(global_idx) => + ExportLocal::Global(res.globals.clone_ref(*global_idx as usize)), + elements::Internal::Memory(mem_idx) => + ExportLocal::Memory(res.memory.clone_ref(*mem_idx as usize)), + elements::Internal::Table(table_idx) => + ExportLocal::Table(res.tables.clone_ref(*table_idx as usize)), }; res.exports.push(Export { local, name: e.field().to_owned() }) - } - }, + }, elements::Section::Start(start_func) => { res.start = Some(res.funcs.clone_ref(*start_func as usize)); }, elements::Section::Element(element_section) => { for element_segment in element_section.entries() { - // let location = if element_segment.passive() { // SegmentLocation::Passive // } else if element_segment.index() == 0 { @@ -360,14 +353,12 @@ impl Module { let location = SegmentLocation::Default(res.map_instructions(init_expr)); let funcs_map = element_segment - .members().iter() + .members() + .iter() .map(|idx| res.funcs.clone_ref(*idx as usize)) .collect::>>(); - res.elements.push(ElementSegment { - value: funcs_map, - location, - }); + res.elements.push(ElementSegment { value: funcs_map, location }); } }, elements::Section::Code(code_section) => { @@ -379,7 +370,7 @@ impl Module { body.code = code; body.locals = func_body.locals().to_vec(); }, - _ => { return Err(Error::InconsistentSource); } + _ => return Err(Error::InconsistentSource), } } }, @@ -394,15 +385,13 @@ impl Module { .code(); let location = SegmentLocation::Default(res.map_instructions(init_expr)); - res.data.push(DataSegment { - value: data_segment.value().to_vec(), - location, - }); + res.data + .push(DataSegment { value: data_segment.value().to_vec(), location }); } }, _ => { res.other.insert(idx, section.clone()); - } + }, } } @@ -441,77 +430,55 @@ impl Module { let imports = import_section.entries_mut(); for func in self.funcs.iter() { match &func.read().origin { - Imported(module, field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Function( - func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32 - ), - ) - ) - }, + Imported(module, field) => imports.push(elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Function( + func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32, + ), + )), _ => continue, } } for global in self.globals.iter() { match &global.read().origin { - Imported(module, field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Global( - elements::GlobalType::new( - global.read().content, - global.read().is_mut, - ) - ), - ) - ) - }, + Imported(module, field) => imports.push(elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Global(elements::GlobalType::new( + global.read().content, + global.read().is_mut, + )), + )), _ => continue, } } for memory in self.memory.iter() { match &memory.read().origin { - Imported(module, field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Memory( - elements::MemoryType::new( - memory.read().limits.initial(), - memory.read().limits.maximum(), - ) - ), - ) - ) - }, + Imported(module, field) => imports.push(elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Memory(elements::MemoryType::new( + memory.read().limits.initial(), + memory.read().limits.maximum(), + )), + )), _ => continue, } } for table in self.tables.iter() { match &table.read().origin { - Imported(module, field) => { - imports.push( - elements::ImportEntry::new( - module.to_owned(), - field.to_owned(), - elements::External::Table( - elements::TableType::new( - table.read().limits.initial(), - table.read().limits.maximum(), - ) - ), - ) - ) - }, + Imported(module, field) => imports.push(elements::ImportEntry::new( + module.to_owned(), + field.to_owned(), + elements::External::Table(elements::TableType::new( + table.read().limits.initial(), + table.read().limits.maximum(), + )), + )), _ => continue, } } @@ -534,7 +501,7 @@ impl Module { match func.read().origin { Declared(_) => { funcs.push(elements::Func::new( - func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32 + func.read().type_ref.order().ok_or(Error::DetachedEntry)? as u32, )); }, _ => continue, @@ -605,7 +572,10 @@ impl Module { match &global.read().origin { Declared(init_code) => { globals.push(elements::GlobalEntry::new( - elements::GlobalType::new(global.read().content, global.read().is_mut), + elements::GlobalType::new( + global.read().content, + global.read().is_mut, + ), elements::InitExpr::new(self.generate_instructions(&init_code[..])), )); }, @@ -627,18 +597,18 @@ impl Module { for export in self.exports.iter() { let internal = match &export.local { - ExportLocal::Func(func_ref) => { - elements::Internal::Function(func_ref.order().ok_or(Error::DetachedEntry)? as u32) - }, - ExportLocal::Global(global_ref) => { - elements::Internal::Global(global_ref.order().ok_or(Error::DetachedEntry)? as u32) - }, - ExportLocal::Table(table_ref) => { - elements::Internal::Table(table_ref.order().ok_or(Error::DetachedEntry)? as u32) - }, - ExportLocal::Memory(memory_ref) => { - elements::Internal::Memory(memory_ref.order().ok_or(Error::DetachedEntry)? as u32) - }, + ExportLocal::Func(func_ref) => elements::Internal::Function( + func_ref.order().ok_or(Error::DetachedEntry)? as u32, + ), + ExportLocal::Global(global_ref) => elements::Internal::Global( + global_ref.order().ok_or(Error::DetachedEntry)? as u32, + ), + ExportLocal::Table(table_ref) => elements::Internal::Table( + table_ref.order().ok_or(Error::DetachedEntry)? as u32, + ), + ExportLocal::Memory(memory_ref) => elements::Internal::Memory( + memory_ref.order().ok_or(Error::DetachedEntry)? as u32, + ), }; exports.push(elements::ExportEntry::new(export.name.to_owned(), internal)); @@ -671,13 +641,13 @@ impl Module { elements_map.push(f.order().ok_or(Error::DetachedEntry)? as u32); } - element_segments.push( - elements::ElementSegment::new( - 0, - Some(elements::InitExpr::new(self.generate_instructions(&offset_expr[..]))), - elements_map, - ) - ); + element_segments.push(elements::ElementSegment::new( + 0, + Some(elements::InitExpr::new( + self.generate_instructions(&offset_expr[..]), + )), + elements_map, + )); }, _ => unreachable!("Other segment location types are never added"), } @@ -701,7 +671,9 @@ impl Module { Declared(body) => { funcs.push(elements::FuncBody::new( body.locals.clone(), - elements::Instructions::new(self.generate_instructions(&body.code[..])), + elements::Instructions::new( + self.generate_instructions(&body.code[..]), + ), )); }, _ => continue, @@ -714,7 +686,6 @@ impl Module { custom_round(&self.other, &mut idx, &mut sections); } - if !self.data.is_empty() { // DATA SECTION (11) let mut data_section = elements::DataSection::default(); @@ -724,13 +695,13 @@ impl Module { for data_entry in self.data.iter() { match &data_entry.location { SegmentLocation::Default(offset_expr) => { - data_segments.push( - elements::DataSegment::new( - 0, - Some(elements::InitExpr::new(self.generate_instructions(&offset_expr[..]))), - data_entry.value.clone(), - ) - ); + data_segments.push(elements::DataSegment::new( + 0, + Some(elements::InitExpr::new( + self.generate_instructions(&offset_expr[..]), + )), + data_entry.value.clone(), + )); }, _ => unreachable!("Other segment location types are never added"), } @@ -771,8 +742,8 @@ pub fn generate(f: &Module) -> Result, Error> { #[cfg(test)] mod tests { - use parity_wasm::elements; use indoc::indoc; + use parity_wasm::elements; fn load_sample(wat: &'static str) -> super::Module { super::parse(&wabt::wat2wasm(wat).expect("faled to parse wat!")[..]) @@ -789,7 +760,8 @@ mod tests { #[test] fn smoky() { - let sample = load_sample(indoc!(r#" + let sample = load_sample(indoc!( + r#" (module (type (func)) (func (type 0)) @@ -809,7 +781,8 @@ mod tests { #[test] fn table() { - let mut sample = load_sample(indoc!(r#" + let mut sample = load_sample(indoc!( + r#" (module (import "env" "foo" (func $foo)) (func (param i32) @@ -859,7 +832,8 @@ mod tests { #[test] fn new_import() { - let mut sample = load_sample(indoc!(r#" + let mut sample = load_sample(indoc!( + r#" (module (type (;0;) (func)) (type (;1;) (func (param i32 i32) (result i32))) @@ -881,9 +855,9 @@ mod tests { let type_ref_0 = sample.types.clone_ref(0); let declared_func_2 = sample.funcs.clone_ref(2); - let mut tx = sample.funcs.begin_insert_not_until( - |f| matches!(f.origin, super::ImportedOrDeclared::Imported(_, _)) - ); + let mut tx = sample.funcs.begin_insert_not_until(|f| { + matches!(f.origin, super::ImportedOrDeclared::Imported(_, _)) + }); let new_import_func = tx.push(super::Func { type_ref: type_ref_0, @@ -914,7 +888,8 @@ mod tests { #[test] fn simple_opt() { - let mut sample = load_sample(indoc!(r#" + let mut sample = load_sample(indoc!( + r#" (module (type (;0;) (func)) (type (;1;) (func (param i32 i32) (result i32))) @@ -966,7 +941,10 @@ mod tests { super::ImportedOrDeclared::Declared(body) => { match &body.code[0] { super::Instruction::Call(called_func) => called_func.order(), - wrong_instruction => panic!("instruction #2 should be a call but got {:?}!", wrong_instruction), + wrong_instruction => panic!( + "instruction #2 should be a call but got {:?}!", + wrong_instruction + ), } }, _ => panic!("func #0 should be declared!"), diff --git a/src/lib.rs b/src/lib.rs index 0a55ec0..3347016 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,34 +7,34 @@ extern crate alloc; pub mod rules; mod build; -mod ext; -mod gas; -mod optimizer; -mod pack; -mod runtime_type; -mod graph; -mod ref_list; -mod symbols; #[cfg(feature = "std")] mod export_globals; +mod ext; +mod gas; +mod graph; #[cfg(feature = "cli")] pub mod logger; +mod optimizer; +mod pack; +mod ref_list; +mod runtime_type; +mod symbols; pub mod stack_height; pub use build::{build, Error as BuildError, SourceTarget}; +#[cfg(feature = "std")] +pub use export_globals::export_mutable_globals; pub use ext::{ externalize, externalize_mem, shrink_unknown_stack, underscore_funcs, ununderscore_funcs, }; pub use gas::inject_gas_counter; +pub use graph::{generate as graph_generate, parse as graph_parse, Module}; pub use optimizer::{optimize, Error as OptimizerError}; pub use pack::{pack_instance, Error as PackingError}; -pub use runtime_type::inject_runtime_type; -pub use graph::{Module, parse as graph_parse, generate as graph_generate}; -pub use ref_list::{RefList, Entry, EntryRef, DeleteTransaction}; -#[cfg(feature = "std")] -pub use export_globals::export_mutable_globals; pub use parity_wasm; +pub use ref_list::{DeleteTransaction, Entry, EntryRef, RefList}; +pub use runtime_type::inject_runtime_type; pub struct TargetSymbols { pub create: &'static str, @@ -48,7 +48,6 @@ pub enum TargetRuntime { } impl TargetRuntime { - pub fn substrate() -> TargetRuntime { TargetRuntime::Substrate(TargetSymbols { create: "deploy", @@ -58,11 +57,7 @@ impl TargetRuntime { } pub fn pwasm() -> TargetRuntime { - TargetRuntime::PWasm(TargetSymbols { - create: "deploy", - call: "call", - ret: "ret", - }) + TargetRuntime::PWasm(TargetSymbols { create: "deploy", call: "call", ret: "ret" }) } pub fn symbols(&self) -> &TargetSymbols { @@ -71,7 +66,6 @@ impl TargetRuntime { TargetRuntime::PWasm(s) => s, } } - } #[cfg(not(feature = "std"))] @@ -88,7 +82,6 @@ mod std { } } - #[cfg(feature = "std")] mod std { pub use std::*; diff --git a/src/logger.rs b/src/logger.rs index 0854be8..e1de812 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,6 +1,6 @@ -use log::{LevelFilter, trace}; use env_logger::Builder; use lazy_static::lazy_static; +use log::{trace, LevelFilter}; lazy_static! { static ref LOG_DUMMY: bool = { diff --git a/src/optimizer.rs b/src/optimizer.rs index 16854aa..074fcda 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1,13 +1,12 @@ -#[cfg(features = "std")] -use crate::std::collections::{HashSet as Set}; #[cfg(not(features = "std"))] -use crate::std::collections::{BTreeSet as Set}; -use crate::std::vec::Vec; -use crate::std::mem; +use crate::std::collections::BTreeSet as Set; +#[cfg(features = "std")] +use crate::std::collections::HashSet as Set; +use crate::std::{mem, vec::Vec}; +use crate::symbols::{expand_symbols, push_code_symbols, resolve_function, Symbol}; use log::trace; use parity_wasm::elements; -use crate::symbols::{Symbol, expand_symbols, push_code_symbols, resolve_function}; #[derive(Debug)] pub enum Error { @@ -26,14 +25,18 @@ pub fn optimize( // try to parse name section let module_temp = mem::take(module); - let module_temp = module_temp - .parse_names() - .unwrap_or_else(|(_err, module)| module); + let module_temp = module_temp.parse_names().unwrap_or_else(|(_err, module)| module); *module = module_temp; // Algo starts from the top, listing all items that should stay let mut stay = Set::new(); - for (index, entry) in module.export_section().ok_or(Error::NoExportSection)?.entries().iter().enumerate() { + for (index, entry) in module + .export_section() + .ok_or(Error::NoExportSection)? + .entries() + .iter() + .enumerate() + { if used_exports.iter().any(|e| *e == entry.field()) { stay.insert(Symbol::Export(index)); } @@ -66,14 +69,16 @@ pub fn optimize( .as_ref() .expect("parity-wasm is compiled without bulk-memory operations") .code(), - &mut init_symbols + &mut init_symbols, ); for func_index in segment.members() { stay.insert(resolve_function(&module, *func_index)); } } } - for symbol in init_symbols.drain(..) { stay.insert(symbol); } + for symbol in init_symbols.drain(..) { + stay.insert(symbol); + } // Call function which will traverse the list recursively, filling stay with all symbols // that are already used by those which already there @@ -94,7 +99,9 @@ pub fn optimize( { loop { - if type_section(module).map(|section| section.types_mut().len()).unwrap_or(0) == index { break; } + if type_section(module).map(|section| section.types_mut().len()).unwrap_or(0) == index { + break + } if stay.contains(&Symbol::Type(old_index)) { index += 1; @@ -125,7 +132,12 @@ pub fn optimize( } else { remove = true; eliminated_funcs.push(top_funcs); - trace!("Eliminated import({}) func({}, {})", old_index, top_funcs, imports.entries()[index].field()); + trace!( + "Eliminated import({}) func({}, {})", + old_index, + top_funcs, + imports.entries()[index].field() + ); } top_funcs += 1; }, @@ -135,13 +147,18 @@ pub fn optimize( } else { remove = true; eliminated_globals.push(top_globals); - trace!("Eliminated import({}) global({}, {})", old_index, top_globals, imports.entries()[index].field()); + trace!( + "Eliminated import({}) global({}, {})", + old_index, + top_globals, + imports.entries()[index].field() + ); } top_globals += 1; }, _ => { index += 1; - } + }, } if remove { imports.entries_mut().remove(index); @@ -149,7 +166,9 @@ pub fn optimize( old_index += 1; - if index == imports.entries().len() { break; } + if index == imports.entries().len() { + break + } } } @@ -159,7 +178,9 @@ pub fn optimize( old_index = 0; loop { - if globals.entries_mut().len() == index { break; } + if globals.entries_mut().len() == index { + break + } if stay.contains(&Symbol::Global(old_index)) { index += 1; } else { @@ -177,11 +198,18 @@ pub fn optimize( old_index = 0; loop { - if function_section(module).expect("Functons section to exist").entries_mut().len() == index { break; } + if function_section(module).expect("Functons section to exist").entries_mut().len() == + index + { + break + } if stay.contains(&Symbol::Function(old_index)) { index += 1; } else { - function_section(module).expect("Functons section to exist").entries_mut().remove(index); + function_section(module) + .expect("Functons section to exist") + .entries_mut() + .remove(index); code_section(module).expect("Code section to exist").bodies_mut().remove(index); eliminated_funcs.push(top_funcs + old_index); @@ -199,18 +227,27 @@ pub fn optimize( old_index = 0; loop { - if exports.entries_mut().len() == index { break; } + if exports.entries_mut().len() == index { + break + } if stay.contains(&Symbol::Export(old_index)) { index += 1; } else { - trace!("Eliminated export({}, {})", old_index, exports.entries_mut()[index].field()); + trace!( + "Eliminated export({}, {})", + old_index, + exports.entries_mut()[index].field() + ); exports.entries_mut().remove(index); } old_index += 1; } } - if !eliminated_globals.is_empty() || !eliminated_funcs.is_empty() || !eliminated_types.is_empty() { + if !eliminated_globals.is_empty() || + !eliminated_funcs.is_empty() || + !eliminated_types.is_empty() + { // Finaly, rewire all calls, globals references and types to the new indices // (only if there is anything to do) // When sorting primitives sorting unstable is faster without any difference in result. @@ -221,57 +258,78 @@ pub fn optimize( for section in module.sections_mut() { match section { elements::Section::Start(func_index) if !eliminated_funcs.is_empty() => { - let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); + let totalle = + eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); *func_index -= totalle as u32; }, - elements::Section::Function(function_section) if !eliminated_types.is_empty() => { + elements::Section::Function(function_section) if !eliminated_types.is_empty() => for func_signature in function_section.entries_mut() { - let totalle = eliminated_types.iter().take_while(|i| (**i as u32) < func_signature.type_ref()).count(); + let totalle = eliminated_types + .iter() + .take_while(|i| (**i as u32) < func_signature.type_ref()) + .count(); *func_signature.type_ref_mut() -= totalle as u32; - } - }, + }, elements::Section::Import(import_section) if !eliminated_types.is_empty() => { for import_entry in import_section.entries_mut() { - if let elements::External::Function(type_ref) = import_entry.external_mut() { - let totalle = eliminated_types.iter().take_while(|i| (**i as u32) < *type_ref).count(); + if let elements::External::Function(type_ref) = import_entry.external_mut() + { + let totalle = eliminated_types + .iter() + .take_while(|i| (**i as u32) < *type_ref) + .count(); *type_ref -= totalle as u32; } } }, - elements::Section::Code(code_section) if !eliminated_globals.is_empty() || !eliminated_funcs.is_empty() => { + elements::Section::Code(code_section) + if !eliminated_globals.is_empty() || !eliminated_funcs.is_empty() => + { for func_body in code_section.bodies_mut() { if !eliminated_funcs.is_empty() { update_call_index(func_body.code_mut(), &eliminated_funcs); } if !eliminated_globals.is_empty() { - update_global_index(func_body.code_mut().elements_mut(), &eliminated_globals) + update_global_index( + func_body.code_mut().elements_mut(), + &eliminated_globals, + ) } if !eliminated_types.is_empty() { update_type_index(func_body.code_mut(), &eliminated_types) } } - }, + } elements::Section::Export(export_section) => { for export in export_section.entries_mut() { match export.internal_mut() { elements::Internal::Function(func_index) => { - let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); + let totalle = eliminated_funcs + .iter() + .take_while(|i| (**i as u32) < *func_index) + .count(); *func_index -= totalle as u32; }, elements::Internal::Global(global_index) => { - let totalle = eliminated_globals.iter().take_while(|i| (**i as u32) < *global_index).count(); + let totalle = eliminated_globals + .iter() + .take_while(|i| (**i as u32) < *global_index) + .count(); *global_index -= totalle as u32; }, - _ => {} + _ => {}, } } }, elements::Section::Global(global_section) => { for global_entry in global_section.entries_mut() { - update_global_index(global_entry.init_expr_mut().code_mut(), &eliminated_globals) + update_global_index( + global_entry.init_expr_mut().code_mut(), + &eliminated_globals, + ) } }, - elements::Section::Data(data_section) => { + elements::Section::Data(data_section) => for segment in data_section.entries_mut() { update_global_index( segment @@ -281,21 +339,23 @@ pub fn optimize( .code_mut(), &eliminated_globals, ) - } - }, + }, elements::Section::Element(elements_section) => { for segment in elements_section.entries_mut() { update_global_index( segment - .offset_mut() - .as_mut() - .expect("parity-wasm is compiled without bulk-memory operations") - .code_mut(), - &eliminated_globals + .offset_mut() + .as_mut() + .expect("parity-wasm is compiled without bulk-memory operations") + .code_mut(), + &eliminated_globals, ); // update all indirect call addresses initial values for func_index in segment.members_mut() { - let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < *func_index).count(); + let totalle = eliminated_funcs + .iter() + .take_while(|i| (**i as u32) < *func_index) + .count(); *func_index -= totalle as u32; } } @@ -306,10 +366,16 @@ pub fn optimize( for index in &eliminated_funcs { func_name_map.remove(*index as u32); } - let updated_map = func_name_map.into_iter().map(|(index, value)| { - let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < index).count() as u32; - (index - totalle, value) - }).collect(); + let updated_map = func_name_map + .into_iter() + .map(|(index, value)| { + let totalle = eliminated_funcs + .iter() + .take_while(|i| (**i as u32) < index) + .count() as u32; + (index - totalle, value) + }) + .collect(); *func_name.names_mut() = updated_map; } @@ -318,31 +384,42 @@ pub fn optimize( for index in &eliminated_funcs { local_names_map.remove(*index as u32); } - let updated_map = local_names_map.into_iter().map(|(index, value)| { - let totalle = eliminated_funcs.iter().take_while(|i| (**i as u32) < index).count() as u32; - (index - totalle, value) - }).collect(); + let updated_map = local_names_map + .into_iter() + .map(|(index, value)| { + let totalle = eliminated_funcs + .iter() + .take_while(|i| (**i as u32) < index) + .count() as u32; + (index - totalle, value) + }) + .collect(); *local_name.local_names_mut() = updated_map; } - } - _ => { } + }, + _ => {}, } } } // Also drop all custom sections - module.sections_mut() - .retain(|section| if let elements::Section::Custom(_) = section { false } else { true }); + module.sections_mut().retain(|section| { + if let elements::Section::Custom(_) = section { + false + } else { + true + } + }); Ok(()) } - pub fn update_call_index(instructions: &mut elements::Instructions, eliminated_indices: &[usize]) { use parity_wasm::elements::Instruction::*; for instruction in instructions.elements_mut().iter_mut() { if let Call(call_index) = instruction { - let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count(); + let totalle = + eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count(); trace!("rewired call {} -> call {}", *call_index, *call_index - totalle as u32); *call_index -= totalle as u32; } @@ -350,16 +427,20 @@ pub fn update_call_index(instructions: &mut elements::Instructions, eliminated_i } /// Updates global references considering the _ordered_ list of eliminated indices -pub fn update_global_index(instructions: &mut Vec, eliminated_indices: &[usize]) { +pub fn update_global_index( + instructions: &mut Vec, + eliminated_indices: &[usize], +) { use parity_wasm::elements::Instruction::*; for instruction in instructions.iter_mut() { match instruction { GetGlobal(index) | SetGlobal(index) => { - let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *index).count(); + let totalle = + eliminated_indices.iter().take_while(|i| (**i as u32) < *index).count(); trace!("rewired global {} -> global {}", *index, *index - totalle as u32); *index -= totalle as u32; }, - _ => { }, + _ => {}, } } } @@ -369,62 +450,67 @@ pub fn update_type_index(instructions: &mut elements::Instructions, eliminated_i use parity_wasm::elements::Instruction::*; for instruction in instructions.elements_mut().iter_mut() { if let CallIndirect(call_index, _) = instruction { - let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count(); - trace!("rewired call_indrect {} -> call_indirect {}", *call_index, *call_index - totalle as u32); + let totalle = + eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count(); + trace!( + "rewired call_indrect {} -> call_indirect {}", + *call_index, + *call_index - totalle as u32 + ); *call_index -= totalle as u32; } } } pub fn import_section(module: &mut elements::Module) -> Option<&mut elements::ImportSection> { - for section in module.sections_mut() { + for section in module.sections_mut() { if let elements::Section::Import(sect) = section { - return Some(sect); + return Some(sect) } } None } pub fn global_section(module: &mut elements::Module) -> Option<&mut elements::GlobalSection> { - for section in module.sections_mut() { + for section in module.sections_mut() { if let elements::Section::Global(sect) = section { - return Some(sect); + return Some(sect) } } None } pub fn function_section(module: &mut elements::Module) -> Option<&mut elements::FunctionSection> { - for section in module.sections_mut() { + for section in module.sections_mut() { if let elements::Section::Function(sect) = section { - return Some(sect); + return Some(sect) } } None } pub fn code_section(module: &mut elements::Module) -> Option<&mut elements::CodeSection> { - for section in module.sections_mut() { + for section in module.sections_mut() { if let elements::Section::Code(sect) = section { - return Some(sect); + return Some(sect) } } None } pub fn export_section(module: &mut elements::Module) -> Option<&mut elements::ExportSection> { - for section in module.sections_mut() { + for section in module.sections_mut() { if let elements::Section::Export(sect) = section { - return Some(sect); + return Some(sect) } } None } pub fn type_section(module: &mut elements::Module) -> Option<&mut elements::TypeSection> { - for section in module.sections_mut() { + for section in module.sections_mut() { if let elements::Section::Type(sect) = section { - return Some(sect); + return Some(sect) } } None @@ -433,8 +519,8 @@ pub fn type_section(module: &mut elements::Module) -> Option<&mut elements::Type #[cfg(test)] mod tests { - use parity_wasm::{builder, elements}; use super::*; + use parity_wasm::{builder, elements}; /// @spec 0 /// Optimizer presumes that export section exists and contains @@ -458,22 +544,34 @@ mod tests { fn minimal() { let mut module = builder::module() .function() - .signature().param().i32().build() - .build() + .signature() + .param() + .i32() + .build() + .build() .function() - .signature() - .param().i32() - .param().i32() - .build() - .build() + .signature() + .param() + .i32() + .param() + .i32() + .build() + .build() .export() - .field("_call") - .internal().func(0).build() + .field("_call") + .internal() + .func(0) + .build() .export() - .field("_random") - .internal().func(1).build() + .field("_random") + .internal() + .func(1) + .build() .build(); - assert_eq!(module.export_section().expect("export section to be generated").entries().len(), 2); + assert_eq!( + module.export_section().expect("export section to be generated").entries().len(), + 2 + ); optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); @@ -485,7 +583,11 @@ mod tests { assert_eq!( 1, - module.function_section().expect("functions section to be generated").entries().len(), + module + .function_section() + .expect("functions section to be generated") + .entries() + .len(), "There should 2 (two) functions in the optimized module" ); } @@ -498,22 +600,26 @@ mod tests { fn globals() { let mut module = builder::module() .global() - .value_type().i32() - .build() + .value_type() + .i32() + .build() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::GetGlobal(0), - elements::Instruction::End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + elements::Instruction::GetGlobal(0), + elements::Instruction::End, + ])) + .build() + .build() .export() - .field("_call") - .internal().func(0).build() + .field("_call") + .internal() + .func(0) + .build() .build(); optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); @@ -534,28 +640,34 @@ mod tests { fn globals_2() { let mut module = builder::module() .global() - .value_type().i32() - .build() + .value_type() + .i32() + .build() .global() - .value_type().i64() - .build() + .value_type() + .i64() + .build() .global() - .value_type().f32() - .build() + .value_type() + .f32() + .build() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::GetGlobal(1), - elements::Instruction::End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + elements::Instruction::GetGlobal(1), + elements::Instruction::End, + ])) + .build() + .build() .export() - .field("_call") - .internal().func(0).build() + .field("_call") + .internal() + .func(0) + .build() .build(); optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); @@ -576,30 +688,40 @@ mod tests { fn call_ref() { let mut module = builder::module() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::Call(1), - elements::Instruction::End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + elements::Instruction::Call(1), + elements::Instruction::End, + ])) + .build() + .build() .function() - .signature() - .param().i32() - .param().i32() - .build() - .build() + .signature() + .param() + .i32() + .param() + .i32() + .build() + .build() .export() - .field("_call") - .internal().func(0).build() + .field("_call") + .internal() + .func(0) + .build() .export() - .field("_random") - .internal().func(1).build() + .field("_random") + .internal() + .func(1) + .build() .build(); - assert_eq!(module.export_section().expect("export section to be generated").entries().len(), 2); + assert_eq!( + module.export_section().expect("export section to be generated").entries().len(), + 2 + ); optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); @@ -611,7 +733,11 @@ mod tests { assert_eq!( 2, - module.function_section().expect("functions section to be generated").entries().len(), + module + .function_section() + .expect("functions section to be generated") + .entries() + .len(), "There should 2 (two) functions in the optimized module" ); } @@ -623,25 +749,40 @@ mod tests { fn call_indirect() { let mut module = builder::module() .function() - .signature().param().i32().param().i32().build() - .build() + .signature() + .param() + .i32() + .param() + .i32() + .build() + .build() .function() - .signature().param().i32().param().i32().param().i32().build() - .build() + .signature() + .param() + .i32() + .param() + .i32() + .param() + .i32() + .build() + .build() .function() - .signature().param().i32().build() - .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::CallIndirect(1, 0), - elements::Instruction::End - ] - )) - .build() - .build() + .signature() + .param() + .i32() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + elements::Instruction::CallIndirect(1, 0), + elements::Instruction::End, + ])) + .build() + .build() .export() - .field("_call") - .internal().func(2).build() + .field("_call") + .internal() + .func(2) + .build() .build(); optimize(&mut module, vec!["_call"]).expect("optimizer to succeed"); @@ -652,7 +793,10 @@ mod tests { "There should 2 (two) types left in the module, 1 for indirect call and one for _call" ); - let indirect_opcode = &module.code_section().expect("code section to be generated").bodies()[0].code().elements()[0]; + let indirect_opcode = + &module.code_section().expect("code section to be generated").bodies()[0] + .code() + .elements()[0]; match *indirect_opcode { elements::Instruction::CallIndirect(0, 0) => {}, _ => { @@ -660,8 +804,7 @@ mod tests { "Expected call_indirect to use index 0 after optimization, since previois 0th was eliminated, but got {:?}", indirect_opcode ); - } + }, } } - } diff --git a/src/pack.rs b/src/pack.rs index 5472961..7ca546e 100644 --- a/src/pack.rs +++ b/src/pack.rs @@ -1,14 +1,13 @@ -use crate::std::fmt; -use crate::std::vec::Vec; -use crate::std::borrow::ToOwned; +use crate::std::{borrow::ToOwned, fmt, vec::Vec}; -use parity_wasm::elements::{ - self, Section, DataSection, Instruction, DataSegment, InitExpr, Internal, External, - ImportCountType, +use super::{gas::update_call_index, TargetRuntime}; +use parity_wasm::{ + builder, + elements::{ + self, DataSection, DataSegment, External, ImportCountType, InitExpr, Instruction, Internal, + Section, + }, }; -use parity_wasm::builder; -use super::TargetRuntime; -use super::gas::update_call_index; /// Pack error. /// @@ -33,8 +32,10 @@ impl fmt::Display for Error { Error::NoTypeSection => write!(f, "No type section in the module"), Error::NoExportSection => write!(f, "No export section in the module"), Error::NoCodeSection => write!(f, "No code section inthe module"), - Error::InvalidCreateSignature(sym) => write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", sym), - Error::InvalidCreateMember(sym) => write!(f, "Exported symbol `{}` should be a function", sym), + Error::InvalidCreateSignature(sym) => + write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", sym), + Error::InvalidCreateMember(sym) => + write!(f, "Exported symbol `{}` should be a function", sym), Error::NoCreateSymbol(sym) => write!(f, "No exported `{}` symbol", sym), Error::NoImportSection => write!(f, "No import section in the module"), } @@ -44,39 +45,55 @@ impl fmt::Display for Error { /// If a pwasm module has an exported function matching "create" symbol we want to pack it into "constructor". /// `raw_module` is the actual contract code /// `ctor_module` is the constructor which should return `raw_module` -pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, target: &TargetRuntime) -> Result { - +pub fn pack_instance( + raw_module: Vec, + mut ctor_module: elements::Module, + target: &TargetRuntime, +) -> Result { // Total number of constructor module import functions let ctor_import_functions = ctor_module.import_section().map(|x| x.functions()).unwrap_or(0); // We need to find an internal ID of function which is exported as `symbols().create` // in order to find it in the Code section of the module let mut create_func_id = { - let found_entry = ctor_module.export_section().ok_or(Error::NoExportSection)?.entries().iter() - .find(|entry| target.symbols().create == entry.field()).ok_or_else(|| Error::NoCreateSymbol(target.symbols().create))?; + let found_entry = ctor_module + .export_section() + .ok_or(Error::NoExportSection)? + .entries() + .iter() + .find(|entry| target.symbols().create == entry.field()) + .ok_or_else(|| Error::NoCreateSymbol(target.symbols().create))?; let function_index: usize = match found_entry.internal() { Internal::Function(index) => *index as usize, - _ => { return Err(Error::InvalidCreateMember(target.symbols().create)) }, + _ => return Err(Error::InvalidCreateMember(target.symbols().create)), }; // Calculates a function index within module's function section let function_internal_index = function_index - ctor_import_functions; // Constructor should be of signature `func()` (void), fail otherwise - let type_id = ctor_module.function_section().ok_or(Error::NoCodeSection)? - .entries().get(function_index - ctor_import_functions).ok_or(Error::MalformedModule)? + let type_id = ctor_module + .function_section() + .ok_or(Error::NoCodeSection)? + .entries() + .get(function_index - ctor_import_functions) + .ok_or(Error::MalformedModule)? .type_ref(); - let elements::Type::Function(func) = ctor_module.type_section().ok_or(Error::NoTypeSection)? - .types().get(type_id as usize).ok_or(Error::MalformedModule)?; + let elements::Type::Function(func) = ctor_module + .type_section() + .ok_or(Error::NoTypeSection)? + .types() + .get(type_id as usize) + .ok_or(Error::MalformedModule)?; // Deploy should have no arguments and also should return nothing if !func.params().is_empty() { - return Err(Error::InvalidCreateSignature(target.symbols().create)); + return Err(Error::InvalidCreateSignature(target.symbols().create)) } if !func.results().is_empty() { - return Err(Error::InvalidCreateSignature(target.symbols().create)); + return Err(Error::InvalidCreateSignature(target.symbols().create)) } function_internal_index @@ -87,25 +104,27 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, tar let mut found = false; for entry in ctor_module.import_section().ok_or(Error::NoImportSection)?.entries().iter() { if let External::Function(_) = *entry.external() { - if entry.field() == target.symbols().ret { found = true; break; } - else { id += 1; } + if entry.field() == target.symbols().ret { + found = true; + break + } else { + id += 1; + } } } if !found { let mut mbuilder = builder::from_module(ctor_module); - let import_sig = mbuilder.push_signature( - builder::signature() - .param().i32().param().i32() - .build_sig() - ); + let import_sig = mbuilder + .push_signature(builder::signature().param().i32().param().i32().build_sig()); mbuilder.push_import( builder::import() .module("env") .field(&target.symbols().ret) - .external().func(import_sig) - .build() - ); + .external() + .func(import_sig) + .build(), + ); ctor_module = mbuilder.build(); @@ -120,8 +139,11 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, tar }, elements::Section::Export(export_section) => { for export in export_section.entries_mut() { - if let elements::Internal::Function(func_index) = export.internal_mut() { - if *func_index >= ret_func { *func_index += 1} + if let elements::Internal::Function(func_index) = export.internal_mut() + { + if *func_index >= ret_func { + *func_index += 1 + } } } }, @@ -129,18 +151,21 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, tar for segment in elements_section.entries_mut() { // update all indirect call addresses initial values for func_index in segment.members_mut() { - if *func_index >= ret_func { *func_index += 1} + if *func_index >= ret_func { + *func_index += 1 + } } } }, - _ => { } + _ => {}, } } create_func_id += 1; ret_func - } - else { id } + } else { + id + } }; // If new function is put in ctor module, it will have this callable index @@ -151,9 +176,12 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, tar .sections() .iter() .find(|section| matches!(**section, Section::Data(_))) - .is_none() { + .is_none() + { // DataSection has to be the last non-custom section according the to the spec - ctor_module.sections_mut().push(Section::Data(DataSection::with_entries(vec![]))); + ctor_module + .sections_mut() + .push(Section::Data(DataSection::with_entries(vec![]))); } // Code data address is an address where we put the contract's code (raw_module) @@ -180,7 +208,7 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, tar let code_data = DataSegment::new( index, Some(InitExpr::new(vec![Instruction::I32Const(offset), Instruction::End])), - raw_module.clone() + raw_module.clone(), ); data_section.entries_mut().push(code_data); code_data_address = offset; @@ -189,16 +217,18 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, tar let mut new_module = builder::from_module(ctor_module) .function() - .signature().build() - .body().with_instructions(elements::Instructions::new( - vec![ - Instruction::Call((create_func_id + ctor_import_functions) as u32), - Instruction::I32Const(code_data_address), - Instruction::I32Const(raw_module.len() as i32), - Instruction::Call(ret_function_id as u32), - Instruction::End, - ])).build() - .build() + .signature() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![ + Instruction::Call((create_func_id + ctor_import_functions) as u32), + Instruction::I32Const(code_data_address), + Instruction::I32Const(raw_module.len() as i32), + Instruction::Call(ret_function_id as u32), + Instruction::End, + ])) + .build() + .build() .build(); for section in new_module.sections_mut() { @@ -207,79 +237,92 @@ pub fn pack_instance(raw_module: Vec, mut ctor_module: elements::Module, tar if target.symbols().create == entry.field() { // change `create` symbol export name into default `call` symbol name. *entry.field_mut() = target.symbols().call.to_owned(); - *entry.internal_mut() = elements::Internal::Function(last_function_index as u32); + *entry.internal_mut() = + elements::Internal::Function(last_function_index as u32); } } } - }; + } Ok(new_module) } #[cfg(test)] mod test { + use super::{super::optimize, *}; use parity_wasm::builder; - use super::*; - use super::super::optimize; fn test_packer(mut module: elements::Module, target_runtime: &TargetRuntime) { let mut ctor_module = module.clone(); - optimize(&mut module, vec![target_runtime.symbols().call]).expect("Optimizer to finish without errors"); - optimize(&mut ctor_module, vec![target_runtime.symbols().create]).expect("Optimizer to finish without errors"); + optimize(&mut module, vec![target_runtime.symbols().call]) + .expect("Optimizer to finish without errors"); + optimize(&mut ctor_module, vec![target_runtime.symbols().create]) + .expect("Optimizer to finish without errors"); let raw_module = parity_wasm::serialize(module).unwrap(); - let ctor_module = pack_instance(raw_module.clone(), ctor_module, target_runtime).expect("Packing failed"); + let ctor_module = + pack_instance(raw_module.clone(), ctor_module, target_runtime).expect("Packing failed"); - let data_section = ctor_module.data_section().expect("Packed module has to have a data section"); - let data_segment = data_section.entries().iter().last().expect("Packed module has to have a data section with at least one entry"); - assert!(data_segment.value() == AsRef::<[u8]>::as_ref(&raw_module), "Last data segment should be equal to the raw module"); + let data_section = + ctor_module.data_section().expect("Packed module has to have a data section"); + let data_segment = data_section + .entries() + .iter() + .last() + .expect("Packed module has to have a data section with at least one entry"); + assert!( + data_segment.value() == AsRef::<[u8]>::as_ref(&raw_module), + "Last data segment should be equal to the raw module" + ); } #[test] fn no_data_section() { let target_runtime = TargetRuntime::pwasm(); - test_packer(builder::module() - .import() + test_packer( + builder::module() + .import() .module("env") .field("memory") - .external().memory(1 as u32, Some(1 as u32)) + .external() + .memory(1 as u32, Some(1 as u32)) .build() - .function() + .function() .signature() - .params().i32().i32().build() - .build() - .body().build() - .build() - .function() - .signature().build() + .params() + .i32() + .i32() + .build() + .build() .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::End - ] - )) - .build() - .build() - .function() - .signature().build() + .build() + .build() + .function() + .signature() + .build() .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::End - ] - )) - .build() - .build() - .export() + .with_instructions(elements::Instructions::new(vec![elements::Instruction::End])) + .build() + .build() + .function() + .signature() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![elements::Instruction::End])) + .build() + .build() + .export() .field(target_runtime.symbols().call) - .internal().func(1) - .build() - .export() + .internal() + .func(1) + .build() + .export() .field(target_runtime.symbols().create) - .internal().func(2) - .build() - .build(), + .internal() + .func(2) + .build() + .build(), &target_runtime, ); } @@ -288,50 +331,53 @@ mod test { fn with_data_section() { let target_runtime = TargetRuntime::pwasm(); - test_packer(builder::module() - .import() + test_packer( + builder::module() + .import() .module("env") .field("memory") - .external().memory(1 as u32, Some(1 as u32)) + .external() + .memory(1 as u32, Some(1 as u32)) .build() - .data() - .offset(elements::Instruction::I32Const(16)).value(vec![0u8]) + .data() + .offset(elements::Instruction::I32Const(16)) + .value(vec![0u8]) .build() - .function() + .function() .signature() - .params().i32().i32().build() - .build() - .body().build() - .build() - .function() - .signature().build() + .params() + .i32() + .i32() + .build() + .build() .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::End - ] - )) - .build() - .build() - .function() - .signature().build() + .build() + .build() + .function() + .signature() + .build() .body() - .with_instructions(elements::Instructions::new( - vec![ - elements::Instruction::End - ] - )) - .build() - .build() - .export() + .with_instructions(elements::Instructions::new(vec![elements::Instruction::End])) + .build() + .build() + .function() + .signature() + .build() + .body() + .with_instructions(elements::Instructions::new(vec![elements::Instruction::End])) + .build() + .build() + .export() .field(target_runtime.symbols().call) - .internal().func(1) - .build() - .export() + .internal() + .func(1) + .build() + .export() .field(target_runtime.symbols().create) - .internal().func(2) - .build() - .build(), + .internal() + .func(2) + .build() + .build(), &target_runtime, ); } diff --git a/src/ref_list.rs b/src/ref_list.rs index 1925d8a..d1792e8 100644 --- a/src/ref_list.rs +++ b/src/ref_list.rs @@ -1,9 +1,6 @@ #![warn(missing_docs)] -use crate::std::rc::Rc; -use crate::std::cell::RefCell; -use crate::std::vec::Vec; -use crate::std::slice; +use crate::std::{cell::RefCell, rc::Rc, slice, vec::Vec}; #[derive(Debug)] enum EntryOrigin { @@ -27,18 +24,12 @@ pub struct Entry { impl Entry { /// New entity. pub fn new(val: T, index: usize) -> Entry { - Entry { - val, - index: EntryOrigin::Index(index), - } + Entry { val, index: EntryOrigin::Index(index) } } /// New detached entry. pub fn new_detached(val: T) -> Entry { - Entry { - val, - index: EntryOrigin::Detached, - } + Entry { val, index: EntryOrigin::Detached } } /// Index of the element within the reference list. @@ -117,9 +108,10 @@ impl Default for RefList { } impl RefList { - /// New empty list. - pub fn new() -> Self { Self::default() } + pub fn new() -> Self { + Self::default() + } /// Push new element in the list. /// @@ -138,10 +130,7 @@ impl RefList { /// When transaction is finailized, all entries are deleted and /// internal indices of other entries are updated. pub fn begin_delete(&mut self) -> DeleteTransaction { - DeleteTransaction { - list: self, - deleted: Vec::new(), - } + DeleteTransaction { list: self, deleted: Vec::new() } } /// Start inserting. @@ -151,11 +140,7 @@ impl RefList { /// When transaction is finailized, all entries are inserted and /// internal indices of other entries might be updated. pub fn begin_insert(&mut self, at: usize) -> InsertTransaction { - InsertTransaction { - at, - list: self, - items: Vec::new(), - } + InsertTransaction { at, list: self, items: Vec::new() } } /// Start inserting after the condition first matches (or at the end). @@ -165,11 +150,14 @@ impl RefList { /// When transaction is finailized, all entries are inserted and /// internal indices of other entries might be updated. pub fn begin_insert_after(&mut self, mut f: F) -> InsertTransaction - where F : FnMut(&T) -> bool + where + F: FnMut(&T) -> bool, { let pos = self - .items.iter() - .position(|rf| f(&**rf.read())).map(|x| x + 1) + .items + .iter() + .position(|rf| f(&**rf.read())) + .map(|x| x + 1) .unwrap_or(self.items.len()); self.begin_insert(pos) @@ -182,7 +170,8 @@ impl RefList { /// When transaction is finailized, all entries are inserted and /// internal indices of other entries might be updated. pub fn begin_insert_not_until(&mut self, mut f: F) -> InsertTransaction - where F : FnMut(&T) -> bool + where + F: FnMut(&T) -> bool, { let pos = self.items.iter().take_while(|rf| f(&**rf.read())).count(); self.begin_insert(pos) @@ -198,12 +187,17 @@ impl RefList { fn done_delete(&mut self, indices: &[usize]) { for entry in self.items.iter_mut() { let mut entry = entry.write(); - let total_less = indices.iter() - .take_while(|x| **x < entry.order().expect("Items in the list always have order; qed")) + let total_less = indices + .iter() + .take_while(|x| { + **x < entry.order().expect("Items in the list always have order; qed") + }) .count(); match &mut entry.index { EntryOrigin::Detached => unreachable!("Items in the list always have order!"), - EntryOrigin::Index(idx) => { *idx -= total_less; }, + EntryOrigin::Index(idx) => { + *idx -= total_less; + }, }; } @@ -221,7 +215,7 @@ impl RefList { offset += 1; } - for idx in (index+offset)..self.items.len() { + for idx in (index + offset)..self.items.len() { self.get_ref(idx).write().index = EntryOrigin::Index(idx); } } @@ -240,7 +234,8 @@ impl RefList { /// /// Slice members are cloned. pub fn from_slice(list: &[T]) -> Self - where T: Clone + where + T: Clone, { let mut res = Self::new(); @@ -329,7 +324,6 @@ impl<'a, T> InsertTransaction<'a, T> { } } - #[cfg(test)] mod tests { diff --git a/src/rules.rs b/src/rules.rs index b277fbe..b4b428e 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -1,10 +1,9 @@ -#[cfg(features = "std")] -use crate::std::collections::HashMap as Map; #[cfg(not(features = "std"))] use crate::std::collections::BTreeMap as Map; +#[cfg(features = "std")] +use crate::std::collections::HashMap as Map; -use crate::std::num::NonZeroU32; -use crate::std::str::FromStr; +use crate::std::{num::NonZeroU32, str::FromStr}; use parity_wasm::elements::Instruction; pub struct UnknownInstruction; @@ -304,11 +303,7 @@ pub struct Set { impl Default for Set { fn default() -> Self { - Set { - regular: 1, - entries: Map::new(), - grow: 0, - } + Set { regular: 1, entries: Map::new(), grow: 0 } } } diff --git a/src/runtime_type.rs b/src/runtime_type.rs index 4b70554..b274230 100644 --- a/src/runtime_type.rs +++ b/src/runtime_type.rs @@ -1,12 +1,15 @@ -use parity_wasm::{elements, builder}; -use self::elements::{ Module, GlobalEntry, External, ExportEntry, GlobalType, ValueType, InitExpr, Instruction, Internal }; -use byteorder::{ LittleEndian, ByteOrder }; +use self::elements::{ + ExportEntry, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, Module, + ValueType, +}; +use byteorder::{ByteOrder, LittleEndian}; +use parity_wasm::{builder, elements}; pub fn inject_runtime_type(module: Module, runtime_type: [u8; 4], runtime_version: u32) -> Module { let runtime_type: u32 = LittleEndian::read_u32(&runtime_type); let globals_count: u32 = match module.global_section() { Some(section) => section.entries().len() as u32, - None => 0 + None => 0, }; let imported_globals_count: u32 = match module.import_section() { Some(section) => section @@ -14,16 +17,25 @@ pub fn inject_runtime_type(module: Module, runtime_type: [u8; 4], runtime_versio .iter() .filter(|e| matches!(*e.external(), External::Global(_))) .count() as u32, - None => 0 + None => 0, }; let total_globals_count: u32 = globals_count + imported_globals_count; builder::from_module(module) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(runtime_type as i32), Instruction::End]))) + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, false), + InitExpr::new(vec![Instruction::I32Const(runtime_type as i32), Instruction::End]), + )) .with_export(ExportEntry::new("RUNTIME_TYPE".into(), Internal::Global(total_globals_count))) - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(runtime_version as i32), Instruction::End]))) - .with_export(ExportEntry::new("RUNTIME_VERSION".into(), Internal::Global(total_globals_count + 1))) - .build() + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, false), + InitExpr::new(vec![Instruction::I32Const(runtime_version as i32), Instruction::End]), + )) + .with_export(ExportEntry::new( + "RUNTIME_VERSION".into(), + Internal::Global(total_globals_count + 1), + )) + .build() } #[cfg(test)] @@ -32,8 +44,11 @@ mod tests { #[test] fn it_injects() { let mut module = builder::module() - .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(42 as i32)]))) - .build(); + .with_global(GlobalEntry::new( + GlobalType::new(ValueType::I32, false), + InitExpr::new(vec![Instruction::I32Const(42 as i32)]), + )) + .build(); let mut runtime_type: [u8; 4] = Default::default(); runtime_type.copy_from_slice(b"emcc"); module = inject_runtime_type(module, runtime_type, 1); diff --git a/src/stack_height/max_height.rs b/src/stack_height/max_height.rs index 2713fcf..22ed253 100644 --- a/src/stack_height/max_height.rs +++ b/src/stack_height/max_height.rs @@ -1,8 +1,8 @@ use crate::std::vec::Vec; +use super::{resolve_func_type, Error}; use log::trace; use parity_wasm::elements::{self, BlockType, Type}; -use super::{resolve_func_type, Error}; /// Control stack frame. #[derive(Debug)] @@ -35,10 +35,7 @@ struct Stack { impl Stack { fn new() -> Stack { - Stack { - height: 0, - control_stack: Vec::new(), - } + Stack { height: 0, control_stack: Vec::new() } } /// Returns current height of the value stack. @@ -64,7 +61,8 @@ impl Stack { /// This effectively makes stack polymorphic. fn mark_unreachable(&mut self) -> Result<(), Error> { trace!(target: "max_height", "unreachable"); - let top_frame = self.control_stack + let top_frame = self + .control_stack .last_mut() .ok_or_else(|| Error("stack must be non-empty".into()))?; top_frame.is_polymorphic = true; @@ -82,7 +80,8 @@ impl Stack { /// Returns `Err` if the control stack is empty. fn pop_frame(&mut self) -> Result { trace!(target: "max_height", "pop_frame: {:?}", self.control_stack.last()); - Ok(self.control_stack + Ok(self + .control_stack .pop() .ok_or_else(|| Error("stack must be non-empty".into()))?) } @@ -98,7 +97,8 @@ impl Stack { /// Returns `Err` if the height overflow usize value. fn push_values(&mut self, value_count: u32) -> Result<(), Error> { trace!(target: "max_height", "push: {}", value_count); - self.height = self.height + self.height = self + .height .checked_add(value_count) .ok_or_else(|| Error("stack overflow".into()))?; Ok(()) @@ -111,7 +111,7 @@ impl Stack { fn pop_values(&mut self, value_count: u32) -> Result<(), Error> { trace!(target: "max_height", "pop: {}", value_count); if value_count == 0 { - return Ok(()); + return Ok(()) } { let top_frame = self.frame(0)?; @@ -127,7 +127,8 @@ impl Stack { } } - self.height = self.height + self.height = self + .height .checked_sub(value_count) .ok_or_else(|| Error("stack underflow".into()))?; @@ -139,16 +140,10 @@ impl Stack { pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result { use parity_wasm::elements::Instruction::*; - let func_section = module - .function_section() - .ok_or_else(|| Error("No function section".into()))?; - let code_section = module - .code_section() - .ok_or_else(|| Error("No code section".into()))?; - let type_section = module - .type_section() - .ok_or_else(|| Error("No type section".into()))?; - + let func_section = + module.function_section().ok_or_else(|| Error("No function section".into()))?; + let code_section = module.code_section().ok_or_else(|| Error("No code section".into()))?; + let type_section = module.type_section().ok_or_else(|| Error("No type section".into()))?; trace!(target: "max_height", "func_idx: {}", func_idx); @@ -184,7 +179,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result= instructions.elements().len() { - break; + break } // If current value stack is higher than maximal height observed so far, @@ -198,7 +193,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result {} + Nop => {}, Block(ty) | Loop(ty) | If(ty) => { let end_arity = if *ty == BlockType::NoResult { 0 } else { 1 }; let branch_arity = if let Loop(_) = *opcode { 0 } else { end_arity }; @@ -212,19 +207,19 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result { // The frame at the top should be pushed by `If`. So we leave // it as is. - } + }, End => { let frame = stack.pop_frame()?; stack.trunc(frame.start_height); stack.push_values(frame.end_arity)?; - } + }, Unreachable => { stack.mark_unreachable()?; - } + }, Br(target) => { // Pop values for the destination block result. let target_arity = stack.frame(*target)?.branch_arity; @@ -233,7 +228,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result { // Pop values for the destination block result. let target_arity = stack.frame(*target)?.branch_arity; @@ -244,7 +239,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result { let arity_of_default = stack.frame(br_table_data.default)?.branch_arity; @@ -252,9 +247,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result Result { // Pop return values of the function. Mark successive instructions as unreachable // since this instruction doesn't let control flow to go further. stack.pop_values(func_arity)?; stack.mark_unreachable()?; - } + }, Call(idx) => { let ty = resolve_func_type(*idx, module)?; @@ -281,7 +274,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result { let Type::Function(ty) = type_section .types() @@ -297,10 +290,10 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result { stack.pop_values(1)?; - } + }, Select => { // Pop two values and one condition. stack.pop_values(2)?; @@ -308,118 +301,118 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result { stack.push_values(1)?; - } + }, SetLocal(_) => { stack.pop_values(1)?; - } + }, TeeLocal(_) => { // This instruction pops and pushes the value, so // effectively it doesn't modify the stack height. stack.pop_values(1)?; stack.push_values(1)?; - } + }, GetGlobal(_) => { stack.push_values(1)?; - } + }, SetGlobal(_) => { stack.pop_values(1)?; - } - I32Load(_, _) - | I64Load(_, _) - | F32Load(_, _) - | F64Load(_, _) - | I32Load8S(_, _) - | I32Load8U(_, _) - | I32Load16S(_, _) - | I32Load16U(_, _) - | I64Load8S(_, _) - | I64Load8U(_, _) - | I64Load16S(_, _) - | I64Load16U(_, _) - | I64Load32S(_, _) - | I64Load32U(_, _) => { + }, + I32Load(_, _) | + I64Load(_, _) | + F32Load(_, _) | + F64Load(_, _) | + I32Load8S(_, _) | + I32Load8U(_, _) | + I32Load16S(_, _) | + I32Load16U(_, _) | + I64Load8S(_, _) | + I64Load8U(_, _) | + I64Load16S(_, _) | + I64Load16U(_, _) | + I64Load32S(_, _) | + I64Load32U(_, _) => { // These instructions pop the address and pushes the result, // which effictively don't modify the stack height. stack.pop_values(1)?; stack.push_values(1)?; - } + }, - I32Store(_, _) - | I64Store(_, _) - | F32Store(_, _) - | F64Store(_, _) - | I32Store8(_, _) - | I32Store16(_, _) - | I64Store8(_, _) - | I64Store16(_, _) - | I64Store32(_, _) => { + I32Store(_, _) | + I64Store(_, _) | + F32Store(_, _) | + F64Store(_, _) | + I32Store8(_, _) | + I32Store16(_, _) | + I64Store8(_, _) | + I64Store16(_, _) | + I64Store32(_, _) => { // These instructions pop the address and the value. stack.pop_values(2)?; - } + }, CurrentMemory(_) => { // Pushes current memory size stack.push_values(1)?; - } + }, GrowMemory(_) => { // Grow memory takes the value of pages to grow and pushes stack.pop_values(1)?; stack.push_values(1)?; - } + }, I32Const(_) | I64Const(_) | F32Const(_) | F64Const(_) => { // These instructions just push the single literal value onto the stack. stack.push_values(1)?; - } + }, I32Eqz | I64Eqz => { // These instructions pop the value and compare it against zero, and pushes // the result of the comparison. stack.pop_values(1)?; stack.push_values(1)?; - } + }, - I32Eq | I32Ne | I32LtS | I32LtU | I32GtS | I32GtU | I32LeS | I32LeU | I32GeS - | I32GeU | I64Eq | I64Ne | I64LtS | I64LtU | I64GtS | I64GtU | I64LeS | I64LeU - | I64GeS | I64GeU | F32Eq | F32Ne | F32Lt | F32Gt | F32Le | F32Ge | F64Eq | F64Ne - | F64Lt | F64Gt | F64Le | F64Ge => { + I32Eq | I32Ne | I32LtS | I32LtU | I32GtS | I32GtU | I32LeS | I32LeU | I32GeS | + I32GeU | I64Eq | I64Ne | I64LtS | I64LtU | I64GtS | I64GtU | I64LeS | I64LeU | + I64GeS | I64GeU | F32Eq | F32Ne | F32Lt | F32Gt | F32Le | F32Ge | F64Eq | F64Ne | + F64Lt | F64Gt | F64Le | F64Ge => { // Comparison operations take two operands and produce one result. stack.pop_values(2)?; stack.push_values(1)?; - } + }, - I32Clz | I32Ctz | I32Popcnt | I64Clz | I64Ctz | I64Popcnt | F32Abs | F32Neg - | F32Ceil | F32Floor | F32Trunc | F32Nearest | F32Sqrt | F64Abs | F64Neg | F64Ceil - | F64Floor | F64Trunc | F64Nearest | F64Sqrt => { + I32Clz | I32Ctz | I32Popcnt | I64Clz | I64Ctz | I64Popcnt | F32Abs | F32Neg | + F32Ceil | F32Floor | F32Trunc | F32Nearest | F32Sqrt | F64Abs | F64Neg | F64Ceil | + F64Floor | F64Trunc | F64Nearest | F64Sqrt => { // Unary operators take one operand and produce one result. stack.pop_values(1)?; stack.push_values(1)?; - } + }, - I32Add | I32Sub | I32Mul | I32DivS | I32DivU | I32RemS | I32RemU | I32And | I32Or - | I32Xor | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr | I64Add | I64Sub - | I64Mul | I64DivS | I64DivU | I64RemS | I64RemU | I64And | I64Or | I64Xor | I64Shl - | I64ShrS | I64ShrU | I64Rotl | I64Rotr | F32Add | F32Sub | F32Mul | F32Div - | F32Min | F32Max | F32Copysign | F64Add | F64Sub | F64Mul | F64Div | F64Min - | F64Max | F64Copysign => { + I32Add | I32Sub | I32Mul | I32DivS | I32DivU | I32RemS | I32RemU | I32And | I32Or | + I32Xor | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr | I64Add | I64Sub | + I64Mul | I64DivS | I64DivU | I64RemS | I64RemU | I64And | I64Or | I64Xor | I64Shl | + I64ShrS | I64ShrU | I64Rotl | I64Rotr | F32Add | F32Sub | F32Mul | F32Div | + F32Min | F32Max | F32Copysign | F64Add | F64Sub | F64Mul | F64Div | F64Min | + F64Max | F64Copysign => { // Binary operators take two operands and produce one result. stack.pop_values(2)?; stack.push_values(1)?; - } + }, - I32WrapI64 | I32TruncSF32 | I32TruncUF32 | I32TruncSF64 | I32TruncUF64 - | I64ExtendSI32 | I64ExtendUI32 | I64TruncSF32 | I64TruncUF32 | I64TruncSF64 - | I64TruncUF64 | F32ConvertSI32 | F32ConvertUI32 | F32ConvertSI64 | F32ConvertUI64 - | F32DemoteF64 | F64ConvertSI32 | F64ConvertUI32 | F64ConvertSI64 | F64ConvertUI64 - | F64PromoteF32 | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 - | F64ReinterpretI64 => { + I32WrapI64 | I32TruncSF32 | I32TruncUF32 | I32TruncSF64 | I32TruncUF64 | + I64ExtendSI32 | I64ExtendUI32 | I64TruncSF32 | I64TruncUF32 | I64TruncSF64 | + I64TruncUF64 | F32ConvertSI32 | F32ConvertUI32 | F32ConvertSI64 | F32ConvertUI64 | + F32DemoteF64 | F64ConvertSI32 | F64ConvertUI32 | F64ConvertSI64 | F64ConvertUI64 | + F64PromoteF32 | I32ReinterpretF32 | I64ReinterpretF64 | F32ReinterpretI32 | + F64ReinterpretI64 => { // Conversion operators take one value and produce one result. stack.pop_values(1)?; stack.push_values(1)?; - } + }, } pc += 1; } @@ -429,8 +422,8 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result elements::Module { elements::deserialize_buffer(&wabt::wat2wasm(source).expect("Failed to wat2wasm")) @@ -515,12 +508,14 @@ mod tests { ) ) "#; - let module = elements::deserialize_buffer(&wabt::Wat2Wasm::new() - .validate(false) - .convert(SOURCE) - .expect("Failed to wat2wasm") - .as_ref()) - .expect("Failed to deserialize the module"); + let module = elements::deserialize_buffer( + &wabt::Wat2Wasm::new() + .validate(false) + .convert(SOURCE) + .expect("Failed to wat2wasm") + .as_ref(), + ) + .expect("Failed to deserialize the module"); let height = compute(0, &module).unwrap(); assert_eq!(height, 2); @@ -550,7 +545,7 @@ mod tests { assert_eq!(height, 1); } -#[test] + #[test] fn breaks() { let module = parse_wat( r#" @@ -572,7 +567,7 @@ mod tests { assert_eq!(height, 1); } -#[test] + #[test] fn if_else_works() { let module = parse_wat( r#" diff --git a/src/stack_height/mod.rs b/src/stack_height/mod.rs index 64bdb01..7eae64d 100644 --- a/src/stack_height/mod.rs +++ b/src/stack_height/mod.rs @@ -49,11 +49,12 @@ //! between the frames. //! - upon entry into the function entire stack frame is allocated. -use crate::std::string::String; -use crate::std::vec::Vec; +use crate::std::{string::String, vec::Vec}; -use parity_wasm::elements::{self, Type}; -use parity_wasm::builder; +use parity_wasm::{ + builder, + elements::{self, Type}, +}; /// Macro to generate preamble and postamble. macro_rules! instrument_call { @@ -151,14 +152,14 @@ fn generate_stack_height_global(module: &mut elements::Module) -> u32 { for section in module.sections_mut() { if let elements::Section::Global(gs) = section { gs.entries_mut().push(global_entry); - return (gs.entries().len() as u32) - 1; + return (gs.entries().len() as u32) - 1 } } // Existing section not found, create one! - module.sections_mut().push(elements::Section::Global( - elements::GlobalSection::with_entries(vec![global_entry]), - )); + module + .sections_mut() + .push(elements::Section::Global(elements::GlobalSection::with_entries(vec![global_entry]))); 0 } @@ -188,13 +189,13 @@ fn compute_stack_cost(func_idx: u32, module: &elements::Module) -> Result Result= instructions.elements().len() { - break; + break } enum Action { - InstrumentCall { - callee_idx: u32, - callee_stack_cost: u32, - }, + InstrumentCall { callee_idx: u32, callee_stack_cost: u32 }, Nop, } @@ -279,21 +274,14 @@ fn instrument_function( let instruction = &instructions.elements()[cursor]; match instruction { Call(callee_idx) => { - let callee_stack_cost = ctx - .stack_cost(*callee_idx) - .ok_or_else(|| - Error( - format!("Call to function that out-of-bounds: {}", callee_idx) - ) - )?; + let callee_stack_cost = ctx.stack_cost(*callee_idx).ok_or_else(|| { + Error(format!("Call to function that out-of-bounds: {}", callee_idx)) + })?; // Instrument only calls to a functions which stack_cost is // non-zero. if callee_stack_cost > 0 { - Action::InstrumentCall { - callee_idx: *callee_idx, - callee_stack_cost, - } + Action::InstrumentCall { callee_idx: *callee_idx, callee_stack_cost } } else { Action::Nop } @@ -326,11 +314,11 @@ fn instrument_function( // Advance cursor to be after the inserted sequence. cursor += new_seq.len(); - } + }, // Do nothing for other instructions. _ => { cursor += 1; - } + }, } } @@ -342,10 +330,7 @@ fn resolve_func_type( module: &elements::Module, ) -> Result<&elements::FunctionType, Error> { let types = module.type_section().map(|ts| ts.types()).unwrap_or(&[]); - let functions = module - .function_section() - .map(|fs| fs.entries()) - .unwrap_or(&[]); + let functions = module.function_section().map(|fs| fs.entries()).unwrap_or(&[]); let func_imports = module.import_count(elements::ImportCountType::Function); let sig_idx = if func_idx < func_imports as u32 { @@ -371,18 +356,15 @@ fn resolve_func_type( .type_ref() }; let Type::Function(ty) = types.get(sig_idx as usize).ok_or_else(|| { - Error(format!( - "Signature {} (specified by func {}) isn't defined", - sig_idx, func_idx - )) + Error(format!("Signature {} (specified by func {}) isn't defined", sig_idx, func_idx)) })?; Ok(ty) } #[cfg(test)] mod tests { - use parity_wasm::elements; use super::*; + use parity_wasm::elements; fn parse_wat(source: &str) -> elements::Module { elements::deserialize_buffer(&wabt::wat2wasm(source).expect("Failed to wat2wasm")) @@ -411,8 +393,7 @@ mod tests { "#, ); - let module = inject_limiter(module, 1024) - .expect("Failed to inject stack counter"); + let module = inject_limiter(module, 1024).expect("Failed to inject stack counter"); validate_module(module); } } diff --git a/src/stack_height/thunk.rs b/src/stack_height/thunk.rs index 7270d7b..d9e16f0 100644 --- a/src/stack_height/thunk.rs +++ b/src/stack_height/thunk.rs @@ -1,11 +1,13 @@ -#[cfg(features = "std")] -use crate::std::collections::{HashMap as Map}; #[cfg(not(features = "std"))] -use crate::std::collections::{BTreeMap as Map}; +use crate::std::collections::BTreeMap as Map; +#[cfg(features = "std")] +use crate::std::collections::HashMap as Map; use crate::std::vec::Vec; -use parity_wasm::elements::{self, FunctionType, Internal}; -use parity_wasm::builder; +use parity_wasm::{ + builder, + elements::{self, FunctionType, Internal}, +}; use super::{resolve_func_type, Context, Error}; @@ -23,41 +25,38 @@ pub(crate) fn generate_thunks( // First, we need to collect all function indices that should be replaced by thunks let mut replacement_map: Map = { - let exports = module - .export_section() - .map(|es| es.entries()) - .unwrap_or(&[]); - let elem_segments = module - .elements_section() - .map(|es| es.entries()) - .unwrap_or(&[]); - let start_func_idx = module - .start_section(); + let exports = module.export_section().map(|es| es.entries()).unwrap_or(&[]); + let elem_segments = module.elements_section().map(|es| es.entries()).unwrap_or(&[]); + let start_func_idx = module.start_section(); let exported_func_indices = exports.iter().filter_map(|entry| match entry.internal() { Internal::Function(function_idx) => Some(*function_idx), _ => None, }); - let table_func_indices = elem_segments - .iter() - .flat_map(|segment| segment.members()) - .cloned(); + let table_func_indices = + elem_segments.iter().flat_map(|segment| segment.members()).cloned(); // Replacement map is at least export section size. let mut replacement_map: Map = Map::new(); - for func_idx in exported_func_indices.chain(table_func_indices).chain(start_func_idx.into_iter()) { - let callee_stack_cost = ctx.stack_cost(func_idx).ok_or_else(|| { - Error(format!("function with idx {} isn't found", func_idx)) - })?; + for func_idx in exported_func_indices + .chain(table_func_indices) + .chain(start_func_idx.into_iter()) + { + let callee_stack_cost = ctx + .stack_cost(func_idx) + .ok_or_else(|| Error(format!("function with idx {} isn't found", func_idx)))?; // Don't generate a thunk if stack_cost of a callee is zero. if callee_stack_cost != 0 { - replacement_map.insert(func_idx, Thunk { - signature: resolve_func_type(func_idx, &module)?.clone(), - idx: None, - callee_stack_cost, - }); + replacement_map.insert( + func_idx, + Thunk { + signature: resolve_func_type(func_idx, &module)?.clone(), + idx: None, + callee_stack_cost, + }, + ); } } @@ -81,11 +80,8 @@ pub(crate) fn generate_thunks( // - argument pushing // - instrumented call // - end - let mut thunk_body: Vec = Vec::with_capacity( - thunk.signature.params().len() + - instrumented_call.len() + - 1 - ); + let mut thunk_body: Vec = + Vec::with_capacity(thunk.signature.params().len() + instrumented_call.len() + 1); for (arg_idx, _) in thunk.signature.params().iter().enumerate() { thunk_body.push(elements::Instruction::GetLocal(arg_idx as u32)); @@ -95,18 +91,17 @@ pub(crate) fn generate_thunks( // TODO: Don't generate a signature, but find an existing one. - mbuilder = mbuilder.function() - // Signature of the thunk should match the original function signature. - .signature() - .with_params(thunk.signature.params().to_vec()) - .with_results(thunk.signature.results().to_vec()) - .build() - .body() - .with_instructions(elements::Instructions::new( - thunk_body - )) - .build() - .build(); + mbuilder = mbuilder + .function() + // Signature of the thunk should match the original function signature. + .signature() + .with_params(thunk.signature.params().to_vec()) + .with_results(thunk.signature.results().to_vec()) + .build() + .body() + .with_instructions(elements::Instructions::new(thunk_body)) + .build() + .build(); thunk.idx = Some(next_func_idx); next_func_idx += 1; @@ -120,32 +115,27 @@ pub(crate) fn generate_thunks( // Check whether this function is in replacement_map, since // we can skip thunk generation (e.g. if stack_cost of function is 0). if let Some(thunk) = replacement_map.get(function_idx) { - *function_idx = thunk - .idx - .expect("At this point an index must be assigned to each thunk"); + *function_idx = + thunk.idx.expect("At this point an index must be assigned to each thunk"); } }; for section in module.sections_mut() { match section { - elements::Section::Export(export_section) => { + elements::Section::Export(export_section) => for entry in export_section.entries_mut() { if let Internal::Function(function_idx) = entry.internal_mut() { fixup(function_idx) } - } - } - elements::Section::Element(elem_section) => { + }, + elements::Section::Element(elem_section) => for segment in elem_section.entries_mut() { for function_idx in segment.members_mut() { fixup(function_idx) } - } - } - elements::Section::Start(start_idx) => { - fixup(start_idx) - } - _ => {} + }, + elements::Section::Start(start_idx) => fixup(start_idx), + _ => {}, } } diff --git a/src/symbols.rs b/src/symbols.rs index 8504954..d88144f 100644 --- a/src/symbols.rs +++ b/src/symbols.rs @@ -1,7 +1,7 @@ -#[cfg(features = "std")] -use crate::std::collections::{HashSet as Set}; #[cfg(not(features = "std"))] -use crate::std::collections::{BTreeSet as Set}; +use crate::std::collections::BTreeSet as Set; +#[cfg(features = "std")] +use crate::std::collections::HashSet as Set; use crate::std::vec::Vec; use log::trace; @@ -22,7 +22,7 @@ pub fn resolve_function(module: &elements::Module, index: u32) -> Symbol { for (item_index, item) in import_section.entries().iter().enumerate() { if let elements::External::Function(_) = item.external() { if functions == index { - return Symbol::Import(item_index as usize); + return Symbol::Import(item_index as usize) } functions += 1; } @@ -38,7 +38,7 @@ pub fn resolve_global(module: &elements::Module, index: u32) -> Symbol { for (item_index, item) in import_section.entries().iter().enumerate() { if let elements::External::Global(_) = item.external() { if globals == index { - return Symbol::Import(item_index as usize); + return Symbol::Import(item_index as usize) } globals += 1; } @@ -48,7 +48,11 @@ pub fn resolve_global(module: &elements::Module, index: u32) -> Symbol { Symbol::Global(index as usize - globals as usize) } -pub fn push_code_symbols(module: &elements::Module, instructions: &[elements::Instruction], dest: &mut Vec) { +pub fn push_code_symbols( + module: &elements::Module, + instructions: &[elements::Instruction], + dest: &mut Vec, +) { use parity_wasm::elements::Instruction::*; for instruction in instructions { @@ -59,10 +63,8 @@ pub fn push_code_symbols(module: &elements::Module, instructions: &[elements::In &CallIndirect(idx, _) => { dest.push(Symbol::Type(idx as usize)); }, - &GetGlobal(idx) | &SetGlobal(idx) => { - dest.push(resolve_global(module, idx)) - }, - _ => { }, + &GetGlobal(idx) | &SetGlobal(idx) => dest.push(resolve_global(module, idx)), + _ => {}, } } } @@ -75,15 +77,16 @@ pub fn expand_symbols(module: &elements::Module, set: &mut Set) { let mut fringe = set.iter().cloned().collect::>(); loop { let next = match fringe.pop() { - Some(s) if stop.contains(&s) => { continue; } + Some(s) if stop.contains(&s) => continue, Some(s) => s, - _ => { break; } + _ => break, }; trace!("Processing symbol {:?}", next); match next { Export(idx) => { - let entry = &module.export_section().expect("Export section to exist").entries()[idx]; + let entry = + &module.export_section().expect("Export section to exist").entries()[idx]; match entry.internal() { elements::Internal::Function(func_idx) => { let symbol = resolve_function(module, *func_idx); @@ -99,11 +102,12 @@ pub fn expand_symbols(module: &elements::Module, set: &mut Set) { } set.insert(symbol); }, - _ => {} + _ => {}, } }, Import(idx) => { - let entry = &module.import_section().expect("Import section to exist").entries()[idx]; + let entry = + &module.import_section().expect("Import section to exist").entries()[idx]; if let elements::External::Function(type_idx) = entry.external() { let type_symbol = Symbol::Type(*type_idx as usize); if !stop.contains(&type_symbol) { @@ -123,7 +127,8 @@ pub fn expand_symbols(module: &elements::Module, set: &mut Set) { set.insert(symbol); } - let signature = &module.function_section().expect("Functions section to exist").entries()[idx]; + let signature = + &module.function_section().expect("Functions section to exist").entries()[idx]; let type_symbol = Symbol::Type(signature.type_ref() as usize); if !stop.contains(&type_symbol) { fringe.push(type_symbol); @@ -131,7 +136,8 @@ pub fn expand_symbols(module: &elements::Module, set: &mut Set) { set.insert(type_symbol); }, Global(idx) => { - let entry = &module.global_section().expect("Global section to exist").entries()[idx]; + let entry = + &module.global_section().expect("Global section to exist").entries()[idx]; let mut code_symbols = Vec::new(); push_code_symbols(module, entry.init_expr().code(), &mut code_symbols); for symbol in code_symbols.drain(..) { @@ -140,8 +146,8 @@ pub fn expand_symbols(module: &elements::Module, set: &mut Set) { } set.insert(symbol); } - } - _ => {} + }, + _ => {}, } stop.insert(next); diff --git a/tests/diff.rs b/tests/diff.rs index a9b59d1..9733d19 100644 --- a/tests/diff.rs +++ b/tests/diff.rs @@ -1,8 +1,10 @@ -use std::fs; -use std::io::{self, Read, Write}; -use std::path::{Path, PathBuf}; use parity_wasm::elements; use pwasm_utils as utils; +use std::{ + fs, + io::{self, Read, Write}, + path::{Path, PathBuf}, +}; fn slurp>(path: P) -> io::Result> { let mut f = fs::File::open(path)?; @@ -18,27 +20,19 @@ fn dump>(path: P, buf: &[u8]) -> io::Result<()> { } fn validate_wasm(binary: &[u8]) -> Result<(), wabt::Error> { - wabt::Module::read_binary( - &binary, - &Default::default() - )?.validate()?; + wabt::Module::read_binary(&binary, &Default::default())?.validate()?; Ok(()) } fn run_diff_test Vec>(test_dir: &str, name: &str, test: F) { // FIXME: not going to work on windows? - let mut fixture_path = PathBuf::from(concat!( - env!("CARGO_MANIFEST_DIR"), - "/tests/fixtures/", - )); + let mut fixture_path = PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/fixtures/",)); fixture_path.push(test_dir); fixture_path.push(name); // FIXME: not going to work on windows? - let mut expected_path = PathBuf::from(concat!( - env!("CARGO_MANIFEST_DIR"), - "/tests/expectations/" - )); + let mut expected_path = + PathBuf::from(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/expectations/")); expected_path.push(test_dir); expected_path.push(name); @@ -82,8 +76,10 @@ mod stack_height { #[test] fn $name() { run_diff_test("stack-height", concat!(stringify!($name), ".wat"), |input| { - let module = elements::deserialize_buffer(input).expect("Failed to deserialize"); - let instrumented = utils::stack_height::inject_limiter(module, 1024).expect("Failed to instrument with stack counter"); + let module = + elements::deserialize_buffer(input).expect("Failed to deserialize"); + let instrumented = utils::stack_height::inject_limiter(module, 1024) + .expect("Failed to instrument with stack counter"); elements::serialize(instrumented).expect("Failed to serialize") }); } @@ -108,7 +104,8 @@ mod gas { run_diff_test("gas", concat!(stringify!($name), ".wat"), |input| { let rules = utils::rules::Set::default(); - let module = elements::deserialize_buffer(input).expect("Failed to deserialize"); + let module = + elements::deserialize_buffer(input).expect("Failed to deserialize"); let instrumented = utils::inject_gas_counter(module, &rules, "env") .expect("Failed to instrument with gas metering"); elements::serialize(instrumented).expect("Failed to serialize") @@ -117,7 +114,6 @@ mod gas { }; } - def_gas_test!(ifs); def_gas_test!(simple); def_gas_test!(start);