mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-06-14 08:41:09 +00:00
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:
committed by
GitHub
parent
77ad07e347
commit
a0b548b37d
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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");
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user