Add rustfmt.toml from substrate repo (#161)

* Add rustfmt.toml from substrate repo

* Apply rustfmt to code base

* Fix formatting

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