mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-06-17 13:31:04 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b58e01ec67 | |||
| 8db40174ae | |||
| 471a9b3fcc | |||
| 3db0d60e70 | |||
| fe25beca2b | |||
| 24b97b517a | |||
| 836ec93008 | |||
| 5238b41af2 | |||
| 19ce379f64 | |||
| a9f5058b4f | |||
| 4b8b07a0b5 | |||
| 67d67f3fba | |||
| 3a7f8836dd | |||
| abb5ae6f22 | |||
| 9d0ad5b309 | |||
| 6c510a7f35 | |||
| e491789127 | |||
| 3e7946ab1c | |||
| 41839664bb | |||
| 735110e8d5 | |||
| 0fe96ee497 | |||
| 0837464ec4 | |||
| 7366384861 | |||
| db80363d56 | |||
| bbb6c6078a | |||
| f7e71718a4 | |||
| af2d61b9f8 | |||
| d6f82000ee | |||
| f4b75bd840 | |||
| 187844f79d | |||
| a4ff19d358 | |||
| e31f1040e1 | |||
| e6e340fa0a | |||
| de23bfac0a | |||
| 261c823b63 | |||
| ce865c1e8a | |||
| 2d60c0bb0e | |||
| 5609a08e99 | |||
| edabee0649 | |||
| 367514ae07 | |||
| 816f14eac7 | |||
| f9540c5423 | |||
| 04ac17c3d5 | |||
| f7e6631c83 | |||
| 20ff66c649 | |||
| 5ef171209b | |||
| 947f0b8bbb | |||
| 7f8811cb2c | |||
| 4112b4b961 |
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pwasm-utils"
|
name = "pwasm-utils"
|
||||||
version = "0.1.5"
|
version = "0.6.1"
|
||||||
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -8,7 +8,7 @@ description = "Collection of command-line utilities and corresponding Rust api f
|
|||||||
keywords = ["wasm", "webassembly", "pwasm"]
|
keywords = ["wasm", "webassembly", "pwasm"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-wasm = { version = "0.30", default-features = false }
|
parity-wasm = { version = "0.31", default-features = false }
|
||||||
log = { version = "0.4", default-features = false }
|
log = { version = "0.4", default-features = false }
|
||||||
byteorder = { version = "1", default-features = false }
|
byteorder = { version = "1", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@@ -2,31 +2,31 @@
|
|||||||
|
|
||||||
[](https://travis-ci.org/paritytech/wasm-utils)
|
[](https://travis-ci.org/paritytech/wasm-utils)
|
||||||
|
|
||||||
Collection of WASM utilities used in Parity and WASM contract devepment
|
Collection of WASM utilities used in Parity and WASM contract development
|
||||||
|
|
||||||
## Build tools for cargo
|
## Build tools for cargo
|
||||||
|
|
||||||
Easiest way to use is to install via `cargo install`:
|
Easiest way to use is to install via `cargo install`:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo install pwasm-utils --bin wasm-build
|
cargo install pwasm-utils-cli --bin wasm-build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Symbols pruning (wasm-prune)
|
## Symbols pruning (wasm-prune)
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo install pwasm-utils
|
cargo install pwasm-utils-cli --bin wasm-prune
|
||||||
wasm-prune <input_wasm_binary.wasm> <output_wasm_binary.wasm>
|
wasm-prune <input_wasm_binary.wasm> <output_wasm_binary.wasm>
|
||||||
```
|
```
|
||||||
|
|
||||||
This will optimize WASM symbols tree to leave only those elements that are used by contract `_call` function entry.
|
This will optimize WASM symbols tree to leave only those elements that are used by contract `call` function entry.
|
||||||
|
|
||||||
## Gas counter (wasm-gas)
|
## Gas counter (wasm-gas)
|
||||||
|
|
||||||
For development puposes, raw WASM contract can be injected with gas counters (the same way as it done by Parity runtime when running contracts)
|
For development puposes, raw WASM contract can be injected with gas counters (the same way as it done by Parity runtime when running contracts)
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo install pwasm-utils
|
cargo install pwasm-utils-cli --bin wasm-gas
|
||||||
wasm-gas <input_wasm_binary.wasm> <output_wasm_binary.wasm>
|
wasm-gas <input_wasm_binary.wasm> <output_wasm_binary.wasm>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ Parity WASM runtime provides some library functions that can be commonly found i
|
|||||||
And then substitutes them with invocations of the imported ones. Should be run before `wasm-opt` for better results.
|
And then substitutes them with invocations of the imported ones. Should be run before `wasm-opt` for better results.
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo install pwasm-utils
|
cargo install pwasm-utils-cli --bin wasm-ext
|
||||||
wasm-ext <input_wasm_binary.wasm> <output_wasm_binary.wasm>
|
wasm-ext <input_wasm_binary.wasm> <output_wasm_binary.wasm>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -56,3 +56,9 @@ All executables use corresponding api methods of the root crate and can be combi
|
|||||||
license and the Apache License (Version 2.0), at your choice.
|
license and the Apache License (Version 2.0), at your choice.
|
||||||
|
|
||||||
See LICENSE-APACHE, and LICENSE-MIT for details.
|
See LICENSE-APACHE, and LICENSE-MIT for details.
|
||||||
|
|
||||||
|
## Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||||
|
for inclusion in `wasm-utils` by you, as defined in the Apache-2.0 license, shall be
|
||||||
|
dual licensed as above, without any additional terms or conditions.
|
||||||
+11
-3
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pwasm-utils-cli"
|
name = "pwasm-utils-cli"
|
||||||
version = "0.1.5"
|
version = "0.6.0"
|
||||||
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
|
||||||
license = "MIT/Apache-2.0"
|
license = "MIT/Apache-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -29,9 +29,17 @@ path = "build/main.rs"
|
|||||||
name = "wasm-stack-height"
|
name = "wasm-stack-height"
|
||||||
path = "stack_height/main.rs"
|
path = "stack_height/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "wasm-pack"
|
||||||
|
path = "pack/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "wasm-check"
|
||||||
|
path = "check/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-wasm = "0.30"
|
parity-wasm = "0.31"
|
||||||
pwasm-utils = { path = ".." }
|
pwasm-utils = { path = "..", version = "0.6" }
|
||||||
glob = "0.2"
|
glob = "0.2"
|
||||||
clap = "2.24"
|
clap = "2.24"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# pwasm-utils-cli
|
||||||
|
|
||||||
|
Collection of WASM utilities used in Parity and WASM contract devepment
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Easiest way to use is to install via `cargo install`:
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo install pwasm-utils-cli
|
||||||
|
```
|
||||||
+55
-82
@@ -9,13 +9,11 @@ extern crate pwasm_utils_cli as logger;
|
|||||||
mod source;
|
mod source;
|
||||||
|
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use parity_wasm::elements;
|
use parity_wasm::elements;
|
||||||
|
use utils::{build, BuildError, SourceTarget, TargetRuntime};
|
||||||
use utils::{CREATE_SYMBOL, CALL_SYMBOL, ununderscore_funcs, externalize_mem, shrink_unknown_stack};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -23,38 +21,18 @@ pub enum Error {
|
|||||||
FailedToCopy(String),
|
FailedToCopy(String),
|
||||||
Decoding(elements::Error, String),
|
Decoding(elements::Error, String),
|
||||||
Encoding(elements::Error),
|
Encoding(elements::Error),
|
||||||
Packing(utils::PackingError),
|
Build(BuildError),
|
||||||
Optimizer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
|
||||||
fn from(err: io::Error) -> Self {
|
|
||||||
Error::Io(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<utils::OptimizerError> for Error {
|
|
||||||
fn from(_err: utils::OptimizerError) -> Self {
|
|
||||||
Error::Optimizer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<utils::PackingError> for Error {
|
|
||||||
fn from(err: utils::PackingError) -> Self {
|
|
||||||
Error::Packing(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||||
use Error::*;
|
use self::Error::*;
|
||||||
match *self {
|
match *self {
|
||||||
Io(ref io) => write!(f, "Generic i/o error: {}", io),
|
Io(ref io) => write!(f, "Generic i/o error: {}", io),
|
||||||
FailedToCopy(ref msg) => write!(f, "{}. Have you tried to run \"cargo build\"?", msg),
|
FailedToCopy(ref msg) => write!(f, "{}. Have you tried to run \"cargo build\"?", msg),
|
||||||
Decoding(ref err, ref file) => write!(f, "Decoding error ({}). Must be a valid wasm file {}. Pointed wrong file?", err, file),
|
Decoding(ref err, ref file) => write!(f, "Decoding error ({}). Must be a valid wasm file {}. Pointed wrong file?", err, file),
|
||||||
Encoding(ref err) => write!(f, "Encoding error ({}). Almost impossible to happen, no free disk space?", err),
|
Encoding(ref err) => write!(f, "Encoding error ({}). Almost impossible to happen, no free disk space?", err),
|
||||||
Optimizer => write!(f, "Optimization error due to missing export section. Pointed wrong file?"),
|
Build(ref err) => write!(f, "Build error: {}", err)
|
||||||
Packing(ref e) => write!(f, "Packing failed due to module structure error: {}. Sure used correct libraries for building contracts?", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,8 +48,8 @@ pub fn process_output(input: &source::SourceInput) -> Result<(), Error> {
|
|||||||
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() {
|
||||||
source::SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET,
|
SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET,
|
||||||
source::SourceTarget::Unknown => source::UNKNOWN_TRIPLET,
|
SourceTarget::Unknown => source::UNKNOWN_TRIPLET,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
cargo_path.push("release");
|
cargo_path.push("release");
|
||||||
@@ -87,14 +65,6 @@ pub fn process_output(input: &source::SourceInput) -> Result<(), Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_ctor(module: &elements::Module) -> bool {
|
|
||||||
if let Some(ref section) = module.export_section() {
|
|
||||||
section.entries().iter().any(|e| CREATE_SYMBOL == e.field())
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_main() -> Result<(), Error> {
|
fn do_main() -> Result<(), Error> {
|
||||||
logger::init_log();
|
logger::init_log();
|
||||||
|
|
||||||
@@ -107,6 +77,12 @@ fn do_main() -> Result<(), Error> {
|
|||||||
.index(2)
|
.index(2)
|
||||||
.required(true)
|
.required(true)
|
||||||
.help("Wasm binary name"))
|
.help("Wasm binary name"))
|
||||||
|
.arg(Arg::with_name("target-runtime")
|
||||||
|
.help("What runtime we are compiling to")
|
||||||
|
.long("target-runtime")
|
||||||
|
.takes_value(true)
|
||||||
|
.default_value("pwasm")
|
||||||
|
.possible_values(&["substrate", "pwasm"]))
|
||||||
.arg(Arg::with_name("skip_optimization")
|
.arg(Arg::with_name("skip_optimization")
|
||||||
.help("Skip symbol optimization step producing final wasm")
|
.help("Skip symbol optimization step producing final wasm")
|
||||||
.long("skip-optimization"))
|
.long("skip-optimization"))
|
||||||
@@ -137,6 +113,11 @@ fn do_main() -> Result<(), Error> {
|
|||||||
.help("Shrinks the new stack size for wasm32-unknown-unknown")
|
.help("Shrinks the new stack size for wasm32-unknown-unknown")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.long("shrink-stack"))
|
.long("shrink-stack"))
|
||||||
|
.arg(Arg::with_name("public_api")
|
||||||
|
.help("Preserves specific imports in the library")
|
||||||
|
.takes_value(true)
|
||||||
|
.long("public-api"))
|
||||||
|
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let target_dir = matches.value_of("target").expect("is required; qed");
|
let target_dir = matches.value_of("target").expect("is required; qed");
|
||||||
@@ -161,65 +142,57 @@ fn do_main() -> Result<(), Error> {
|
|||||||
|
|
||||||
let path = wasm_path(&source_input);
|
let path = wasm_path(&source_input);
|
||||||
|
|
||||||
let mut module = parity_wasm::deserialize_file(&path)
|
let module = parity_wasm::deserialize_file(&path)
|
||||||
.map_err(|e| Error::Decoding(e, path.to_string()))?;
|
.map_err(|e| Error::Decoding(e, path.to_string()))?;
|
||||||
|
|
||||||
if let source::SourceTarget::Emscripten = source_input.target() {
|
let runtime_type_version = if let (Some(runtime_type), Some(runtime_version))
|
||||||
module = ununderscore_funcs(module);
|
= (matches.value_of("runtime_type"), matches.value_of("runtime_version")) {
|
||||||
}
|
let mut ty: [u8; 4] = Default::default();
|
||||||
|
let runtime_bytes = runtime_type.as_bytes();
|
||||||
if let source::SourceTarget::Unknown = source_input.target() {
|
if runtime_bytes.len() != 4 {
|
||||||
// 49152 is 48kb!
|
|
||||||
if matches.is_present("enforce_stack_adjustment") {
|
|
||||||
let stack_size: u32 = matches.value_of("shrink_stack").unwrap_or_else(|| "49152").parse().expect("New stack size is not valid u32");
|
|
||||||
assert!(stack_size <= 1024*1024);
|
|
||||||
let (new_module, new_stack_top) = shrink_unknown_stack(module, 1024 * 1024 - stack_size);
|
|
||||||
module = new_module;
|
|
||||||
let mut stack_top_page = new_stack_top / 65536;
|
|
||||||
if new_stack_top % 65536 > 0 { stack_top_page += 1 };
|
|
||||||
module = externalize_mem(module, Some(stack_top_page), 16);
|
|
||||||
} else {
|
|
||||||
module = externalize_mem(module, None, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(runtime_type) = matches.value_of("runtime_type") {
|
|
||||||
let runtime_type: &[u8] = runtime_type.as_bytes();
|
|
||||||
if runtime_type.len() != 4 {
|
|
||||||
panic!("--runtime-type should be equal to 4 bytes");
|
panic!("--runtime-type should be equal to 4 bytes");
|
||||||
}
|
}
|
||||||
let runtime_version: u32 = matches.value_of("runtime_version").unwrap_or("1").parse()
|
ty.copy_from_slice(runtime_bytes);
|
||||||
|
let version: u32 = runtime_version.parse()
|
||||||
.expect("--runtime-version should be a positive integer");
|
.expect("--runtime-version should be a positive integer");
|
||||||
module = utils::inject_runtime_type(module, &runtime_type, runtime_version);
|
Some((ty, version))
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut ctor_module = module.clone();
|
let public_api_entries = matches.value_of("public_api")
|
||||||
|
.map(|val| val.split(",").collect())
|
||||||
|
.unwrap_or(Vec::new());
|
||||||
|
|
||||||
if !matches.is_present("skip_optimization") {
|
let target_runtime = match matches.value_of("target-runtime").expect("target-runtime has a default value; qed") {
|
||||||
utils::optimize(
|
"pwasm" => TargetRuntime::pwasm(),
|
||||||
&mut module,
|
"substrate" => TargetRuntime::substrate(),
|
||||||
vec![CALL_SYMBOL]
|
_ => unreachable!("all possible values are enumerated in clap config; qed"),
|
||||||
)?;
|
};
|
||||||
}
|
|
||||||
|
let (module, ctor_module) = build(
|
||||||
|
module,
|
||||||
|
source_input.target(),
|
||||||
|
runtime_type_version,
|
||||||
|
&public_api_entries,
|
||||||
|
matches.is_present("enforce_stack_adjustment"),
|
||||||
|
matches.value_of("shrink_stack").unwrap_or_else(|| "49152").parse()
|
||||||
|
.expect("New stack size is not valid u32"),
|
||||||
|
matches.is_present("skip_optimization"),
|
||||||
|
&target_runtime,
|
||||||
|
).map_err(Error::Build)?;
|
||||||
|
|
||||||
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)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let raw_module = parity_wasm::serialize(module).map_err(Error::Encoding)?;
|
if let Some(ctor_module) = ctor_module {
|
||||||
|
parity_wasm::serialize_to_file(
|
||||||
// If module has an exported function with name=CREATE_SYMBOL
|
&path,
|
||||||
// build will pack the module (raw_module) into this funciton and export as CALL_SYMBOL.
|
ctor_module,
|
||||||
// Otherwise it will just save an optimised raw_module
|
).map_err(Error::Encoding)?;
|
||||||
if has_ctor(&ctor_module) {
|
|
||||||
if !matches.is_present("skip_optimization") {
|
|
||||||
utils::optimize(&mut ctor_module, vec![CREATE_SYMBOL])?;
|
|
||||||
}
|
|
||||||
let ctor_module = utils::pack_instance(raw_module, ctor_module)?;
|
|
||||||
parity_wasm::serialize_to_file(&path, ctor_module).map_err(Error::Encoding)?;
|
|
||||||
} else {
|
} else {
|
||||||
let mut file = fs::File::create(&path)?;
|
parity_wasm::serialize_to_file(&path, module).map_err(Error::Encoding)?;
|
||||||
file.write_all(&raw_module)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+1
-6
@@ -3,12 +3,7 @@
|
|||||||
pub const UNKNOWN_TRIPLET: &str = "wasm32-unknown-unknown";
|
pub const UNKNOWN_TRIPLET: &str = "wasm32-unknown-unknown";
|
||||||
pub const EMSCRIPTEN_TRIPLET: &str = "wasm32-unknown-emscripten";
|
pub const EMSCRIPTEN_TRIPLET: &str = "wasm32-unknown-emscripten";
|
||||||
|
|
||||||
/// Target configiration of previous build step
|
use utils::SourceTarget;
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum SourceTarget {
|
|
||||||
Emscripten,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration of previous build step (cargo compilation)
|
/// Configuration of previous build step (cargo compilation)
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
extern crate parity_wasm;
|
||||||
|
extern crate pwasm_utils as utils;
|
||||||
|
extern crate pwasm_utils_cli as logger;
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
use parity_wasm::elements;
|
||||||
|
|
||||||
|
fn fail(msg: &str) -> ! {
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ALLOWED_IMPORTS: &'static [&'static str] = &[
|
||||||
|
"ret",
|
||||||
|
"storage_read",
|
||||||
|
"storage_write",
|
||||||
|
"balance",
|
||||||
|
"sender",
|
||||||
|
"origin",
|
||||||
|
"fetch_input",
|
||||||
|
"input_length",
|
||||||
|
"ccall",
|
||||||
|
"dcall",
|
||||||
|
"scall",
|
||||||
|
"create",
|
||||||
|
"balance",
|
||||||
|
"blockhash",
|
||||||
|
"blocknumber",
|
||||||
|
"coinbase",
|
||||||
|
"timestamp",
|
||||||
|
"difficulty",
|
||||||
|
"gaslimit",
|
||||||
|
"address",
|
||||||
|
"value",
|
||||||
|
"suicide",
|
||||||
|
"panic",
|
||||||
|
"elog",
|
||||||
|
"abort"
|
||||||
|
];
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
logger::init_log();
|
||||||
|
|
||||||
|
let matches = App::new("wasm-check")
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
.help("Input WASM file"))
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let input = matches.value_of("input").expect("is required; qed");
|
||||||
|
|
||||||
|
let module = parity_wasm::deserialize_file(&input).expect("Input module deserialization failed");
|
||||||
|
|
||||||
|
for section in module.sections() {
|
||||||
|
match *section {
|
||||||
|
elements::Section::Import(ref import_section) => {
|
||||||
|
let mut has_imported_memory_properly_named = false;
|
||||||
|
for entry in import_section.entries() {
|
||||||
|
if entry.module() != "env" {
|
||||||
|
fail("All imports should be from env");
|
||||||
|
}
|
||||||
|
match *entry.external() {
|
||||||
|
elements::External::Function(_) => {
|
||||||
|
if !ALLOWED_IMPORTS.contains(&entry.field()) {
|
||||||
|
fail(&format!("'{}' is not supported by the runtime", entry.field()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
elements::External::Memory(m) => {
|
||||||
|
if entry.field() == "memory" {
|
||||||
|
has_imported_memory_properly_named = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max = if let Some(max) = m.limits().maximum() {
|
||||||
|
max
|
||||||
|
} else {
|
||||||
|
fail("There is a limit on memory in Parity runtime, and this program does not limit memory");
|
||||||
|
};
|
||||||
|
|
||||||
|
if max > 16 {
|
||||||
|
fail(&format!(
|
||||||
|
"Parity runtime has 1Mb limit (16 pages) on max contract memory, this program speicifies {}",
|
||||||
|
max
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
elements::External::Global(_) => {
|
||||||
|
fail("Parity runtime does not provide any globals")
|
||||||
|
},
|
||||||
|
_ => { continue; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_imported_memory_properly_named {
|
||||||
|
fail("No imported memory from env::memory in the contract");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { continue; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
extern crate parity_wasm;
|
||||||
|
extern crate pwasm_utils as utils;
|
||||||
|
extern crate pwasm_utils_cli as logger;
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use clap::{App, Arg};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
logger::init_log();
|
||||||
|
|
||||||
|
let target_runtime = utils::TargetRuntime::pwasm();
|
||||||
|
|
||||||
|
let matches = App::new("wasm-pack")
|
||||||
|
.arg(Arg::with_name("input")
|
||||||
|
.index(1)
|
||||||
|
.required(true)
|
||||||
|
.help("Input WASM file"))
|
||||||
|
.arg(Arg::with_name("output")
|
||||||
|
.index(2)
|
||||||
|
.required(true)
|
||||||
|
.help("Output WASM file"))
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let input = matches.value_of("input").expect("is required; qed");
|
||||||
|
let output = matches.value_of("output").expect("is required; qed");
|
||||||
|
|
||||||
|
let module = parity_wasm::deserialize_file(&input).expect("Input module deserialization failed");
|
||||||
|
let ctor_module = module.clone();
|
||||||
|
let raw_module = parity_wasm::serialize(module).expect("Serialization failed");
|
||||||
|
|
||||||
|
// Invoke packer
|
||||||
|
let mut result_module = utils::pack_instance(raw_module, ctor_module, &utils::TargetRuntime::pwasm()).expect("Packing failed");
|
||||||
|
// Optimize constructor, since it does not need everything
|
||||||
|
utils::optimize(&mut result_module, vec![target_runtime.call_symbol]).expect("Optimization failed");
|
||||||
|
|
||||||
|
parity_wasm::serialize_to_file(&output, result_module).expect("Serialization failed");
|
||||||
|
}
|
||||||
+7
-5
@@ -8,7 +8,9 @@ use clap::{App, Arg};
|
|||||||
fn main() {
|
fn main() {
|
||||||
logger::init_log();
|
logger::init_log();
|
||||||
|
|
||||||
let matches = App::new("wasm-opt")
|
let target_runtime = utils::TargetRuntime::pwasm();
|
||||||
|
|
||||||
|
let matches = App::new("wasm-prune")
|
||||||
.arg(Arg::with_name("input")
|
.arg(Arg::with_name("input")
|
||||||
.index(1)
|
.index(1)
|
||||||
.required(true)
|
.required(true)
|
||||||
@@ -22,12 +24,12 @@ fn main() {
|
|||||||
.short("e")
|
.short("e")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.value_name("functions")
|
.value_name("functions")
|
||||||
.help("Comma-separated list of exported functions to keep. Default: _call"))
|
.help(&format!("Comma-separated list of exported functions to keep. Default: '{}'", target_runtime.call_symbol)))
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let exports = matches
|
let exports = matches
|
||||||
.value_of("exports")
|
.value_of("exports")
|
||||||
.unwrap_or("_call")
|
.unwrap_or(target_runtime.call_symbol)
|
||||||
.split(',')
|
.split(',')
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ fn main() {
|
|||||||
// Invoke optimizer
|
// Invoke optimizer
|
||||||
// Contract is supposed to have only these functions as public api
|
// Contract is supposed to have only these functions as public api
|
||||||
// All other symbols not usable by this list is optimized away
|
// All other symbols not usable by this list is optimized away
|
||||||
utils::optimize(&mut module, exports).expect("Optimizer to finish without errors");
|
utils::optimize(&mut module, exports).expect("Optimizer failed");
|
||||||
|
|
||||||
parity_wasm::serialize_to_file(&output, module).unwrap();
|
parity_wasm::serialize_to_file(&output, module).expect("Serialization failed");
|
||||||
}
|
}
|
||||||
|
|||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
use std;
|
||||||
|
use super::{
|
||||||
|
optimize,
|
||||||
|
pack_instance,
|
||||||
|
ununderscore_funcs,
|
||||||
|
externalize_mem,
|
||||||
|
shrink_unknown_stack,
|
||||||
|
inject_runtime_type,
|
||||||
|
PackingError,
|
||||||
|
OptimizerError,
|
||||||
|
TargetRuntime,
|
||||||
|
};
|
||||||
|
use parity_wasm;
|
||||||
|
use parity_wasm::elements;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Encoding(elements::Error),
|
||||||
|
Packing(PackingError),
|
||||||
|
Optimizer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OptimizerError> for Error {
|
||||||
|
fn from(_err: OptimizerError) -> Self {
|
||||||
|
Error::Optimizer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PackingError> for Error {
|
||||||
|
fn from(err: PackingError) -> Self {
|
||||||
|
Error::Packing(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum SourceTarget {
|
||||||
|
Emscripten,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||||
|
use self::Error::*;
|
||||||
|
match *self {
|
||||||
|
Encoding(ref err) => write!(f, "Encoding error ({})", err),
|
||||||
|
Optimizer => write!(f, "Optimization error due to missing export section. Pointed wrong file?"),
|
||||||
|
Packing(ref e) => write!(f, "Packing failed due to module structure error: {}. Sure used correct libraries for building contracts?", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_ctor(module: &elements::Module, target_runtime: &TargetRuntime) -> bool {
|
||||||
|
if let Some(ref section) = module.export_section() {
|
||||||
|
section.entries().iter().any(|e| target_runtime.create_symbol == e.field())
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(
|
||||||
|
mut module: elements::Module,
|
||||||
|
source_target: SourceTarget,
|
||||||
|
runtime_type_version: Option<([u8; 4], u32)>,
|
||||||
|
public_api_entries: &[&str],
|
||||||
|
enforce_stack_adjustment: bool,
|
||||||
|
stack_size: u32,
|
||||||
|
skip_optimization: bool,
|
||||||
|
target_runtime: &TargetRuntime,
|
||||||
|
) -> Result<(elements::Module, Option<elements::Module>), Error> {
|
||||||
|
|
||||||
|
if let SourceTarget::Emscripten = source_target {
|
||||||
|
module = ununderscore_funcs(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let SourceTarget::Unknown = source_target {
|
||||||
|
// 49152 is 48kb!
|
||||||
|
if enforce_stack_adjustment {
|
||||||
|
assert!(stack_size <= 1024*1024);
|
||||||
|
let (new_module, new_stack_top) = shrink_unknown_stack(module, 1024 * 1024 - stack_size);
|
||||||
|
module = new_module;
|
||||||
|
let mut stack_top_page = new_stack_top / 65536;
|
||||||
|
if new_stack_top % 65536 > 0 { stack_top_page += 1 };
|
||||||
|
module = externalize_mem(module, Some(stack_top_page), 16);
|
||||||
|
} else {
|
||||||
|
module = externalize_mem(module, None, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(runtime_type_version) = runtime_type_version {
|
||||||
|
let (runtime_type, runtime_version) = runtime_type_version;
|
||||||
|
module = inject_runtime_type(module, runtime_type, runtime_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ctor_module = module.clone();
|
||||||
|
|
||||||
|
let mut public_api_entries = public_api_entries.to_vec();
|
||||||
|
public_api_entries.push(target_runtime.call_symbol);
|
||||||
|
if !skip_optimization {
|
||||||
|
optimize(
|
||||||
|
&mut module,
|
||||||
|
public_api_entries,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_ctor(&ctor_module, target_runtime) {
|
||||||
|
if !skip_optimization {
|
||||||
|
optimize(&mut ctor_module, vec![target_runtime.create_symbol])?;
|
||||||
|
}
|
||||||
|
let ctor_module = pack_instance(
|
||||||
|
parity_wasm::serialize(module.clone()).map_err(Error::Encoding)?,
|
||||||
|
ctor_module.clone(),
|
||||||
|
target_runtime,
|
||||||
|
)?;
|
||||||
|
Ok((module, Some(ctor_module)))
|
||||||
|
} else {
|
||||||
|
Ok((module, None))
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-16
@@ -8,29 +8,23 @@ use byteorder::{LittleEndian, ByteOrder};
|
|||||||
|
|
||||||
type Insertion = (usize, u32, u32, String);
|
type Insertion = (usize, u32, u32, String);
|
||||||
|
|
||||||
pub fn update_call_index(opcodes: &mut elements::Opcodes, original_imports: usize, inserts: &[Insertion]) {
|
pub fn update_call_index(instructions: &mut elements::Instructions, original_imports: usize, inserts: &[Insertion]) {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
for opcode in opcodes.elements_mut().iter_mut() {
|
for instruction in instructions.elements_mut().iter_mut() {
|
||||||
match opcode {
|
if let &mut Call(ref mut call_index) = instruction {
|
||||||
&mut Call(ref mut call_index) => {
|
|
||||||
if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
|
if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
|
||||||
*call_index = (original_imports + pos) as u32;
|
*call_index = (original_imports + pos) as u32;
|
||||||
} else if *call_index as usize > original_imports {
|
} else if *call_index as usize > original_imports {
|
||||||
*call_index += inserts.len() as u32;
|
*call_index += inserts.len() as u32;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn memory_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::MemorySection> {
|
pub fn memory_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::MemorySection> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match section {
|
if let &mut elements::Section::Memory(ref mut sect) = section {
|
||||||
&mut elements::Section::Memory(ref mut sect) => {
|
|
||||||
return Some(sect);
|
return Some(sect);
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -104,7 +98,7 @@ pub fn shrink_unknown_stack(
|
|||||||
match section {
|
match section {
|
||||||
&mut elements::Section::Data(ref mut data_section) => {
|
&mut elements::Section::Data(ref mut data_section) => {
|
||||||
for ref mut data_segment in data_section.entries_mut() {
|
for ref mut data_segment in data_section.entries_mut() {
|
||||||
if data_segment.offset().code() == &[elements::Opcode::I32Const(4), elements::Opcode::End] {
|
if data_segment.offset().code() == &[elements::Instruction::I32Const(4), elements::Instruction::End] {
|
||||||
assert_eq!(data_segment.value().len(), 4);
|
assert_eq!(data_segment.value().len(), 4);
|
||||||
let current_val = LittleEndian::read_u32(data_segment.value());
|
let current_val = LittleEndian::read_u32(data_segment.value());
|
||||||
let new_val = current_val - shrink_amount;
|
let new_val = current_val - shrink_amount;
|
||||||
@@ -182,11 +176,8 @@ pub fn externalize(
|
|||||||
},
|
},
|
||||||
&mut elements::Section::Export(ref mut export_section) => {
|
&mut elements::Section::Export(ref mut export_section) => {
|
||||||
for ref mut export in export_section.entries_mut() {
|
for ref mut export in export_section.entries_mut() {
|
||||||
match export.internal_mut() {
|
if let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
|
||||||
&mut elements::Internal::Function(ref mut func_index) => {
|
|
||||||
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; }
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+40
-46
@@ -3,14 +3,11 @@ use std::vec::Vec;
|
|||||||
use parity_wasm::{elements, builder};
|
use parity_wasm::{elements, builder};
|
||||||
use rules;
|
use rules;
|
||||||
|
|
||||||
pub fn update_call_index(opcodes: &mut elements::Opcodes, inserted_index: u32) {
|
pub fn update_call_index(instructions: &mut elements::Instructions, inserted_index: u32) {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
for opcode in opcodes.elements_mut().iter_mut() {
|
for instruction in instructions.elements_mut().iter_mut() {
|
||||||
match opcode {
|
if let &mut Call(ref mut call_index) = instruction {
|
||||||
&mut Call(ref mut call_index) => {
|
|
||||||
if *call_index >= inserted_index { *call_index += 1}
|
if *call_index >= inserted_index { *call_index += 1}
|
||||||
},
|
|
||||||
_ => { },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,30 +83,27 @@ impl Counter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inject_grow_counter(opcodes: &mut elements::Opcodes, grow_counter_func: u32) -> usize {
|
fn inject_grow_counter(instructions: &mut elements::Instructions, grow_counter_func: u32) -> usize {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
for opcode in opcodes.elements_mut() {
|
for instruction in instructions.elements_mut() {
|
||||||
match *opcode {
|
if let GrowMemory(_) = *instruction {
|
||||||
GrowMemory(_) => {
|
*instruction = Call(grow_counter_func);
|
||||||
*opcode = Call(grow_counter_func);
|
|
||||||
counter += 1;
|
counter += 1;
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
counter
|
counter
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32) -> elements::Module {
|
fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32) -> elements::Module {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
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().params().i32().build().with_return_type(Some(elements::ValueType::I32)).build()
|
.signature().params().i32().build().with_return_type(Some(elements::ValueType::I32)).build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(vec![
|
.with_instructions(elements::Instructions::new(vec![
|
||||||
GetLocal(0),
|
GetLocal(0),
|
||||||
GetLocal(0),
|
GetLocal(0),
|
||||||
I32Const(rules.grow_cost() as i32),
|
I32Const(rules.grow_cost() as i32),
|
||||||
@@ -127,24 +121,24 @@ fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn inject_counter(
|
pub fn inject_counter(
|
||||||
opcodes: &mut elements::Opcodes,
|
instructions: &mut elements::Instructions,
|
||||||
rules: &rules::Set,
|
rules: &rules::Set,
|
||||||
gas_func: u32,
|
gas_func: u32,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let mut counter = Counter::new();
|
let mut counter = Counter::new();
|
||||||
|
|
||||||
// Begin an implicit function (i.e. `func...end`) block.
|
// Begin an implicit function (i.e. `func...end`) block.
|
||||||
counter.begin(0);
|
counter.begin(0);
|
||||||
|
|
||||||
for cursor in 0..opcodes.elements().len() {
|
for cursor in 0..instructions.elements().len() {
|
||||||
let opcode = &opcodes.elements()[cursor];
|
let instruction = &instructions.elements()[cursor];
|
||||||
match *opcode {
|
match *instruction {
|
||||||
Block(_) | If(_) | Loop(_) => {
|
Block(_) | If(_) | Loop(_) => {
|
||||||
// Increment previous block with the cost of the current opcode.
|
// Increment previous block with the cost of the current opcode.
|
||||||
let opcode_cost = rules.process(opcode)?;
|
let instruction_cost = rules.process(instruction)?;
|
||||||
counter.increment(opcode_cost)?;
|
counter.increment(instruction_cost)?;
|
||||||
|
|
||||||
// Begin new block. The cost of the following opcodes until `End` or `Else` will
|
// Begin new block. The cost of the following opcodes until `End` or `Else` will
|
||||||
// be included into this block.
|
// be included into this block.
|
||||||
@@ -170,8 +164,8 @@ pub fn inject_counter(
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// An ordinal non control flow instruction. Just increment the cost of the current block.
|
// An ordinal non control flow instruction. Just increment the cost of the current block.
|
||||||
let opcode_cost = rules.process(opcode)?;
|
let instruction_cost = rules.process(instruction)?;
|
||||||
counter.increment(opcode_cost)?;
|
counter.increment(instruction_cost)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,8 +175,8 @@ pub fn inject_counter(
|
|||||||
for block in counter.blocks {
|
for block in counter.blocks {
|
||||||
let effective_pos = block.start_pos + cumulative_offset;
|
let effective_pos = block.start_pos + cumulative_offset;
|
||||||
|
|
||||||
opcodes.elements_mut().insert(effective_pos, I32Const(block.cost as i32));
|
instructions.elements_mut().insert(effective_pos, I32Const(block.cost as i32));
|
||||||
opcodes.elements_mut().insert(effective_pos+1, Call(gas_func));
|
instructions.elements_mut().insert(effective_pos+1, Call(gas_func));
|
||||||
|
|
||||||
// Take into account these two inserted instructions.
|
// Take into account these two inserted instructions.
|
||||||
cumulative_offset += 2;
|
cumulative_offset += 2;
|
||||||
@@ -244,11 +238,8 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set)
|
|||||||
},
|
},
|
||||||
&mut elements::Section::Export(ref mut export_section) => {
|
&mut elements::Section::Export(ref mut export_section) => {
|
||||||
for ref mut export in export_section.entries_mut() {
|
for ref mut export in export_section.entries_mut() {
|
||||||
match export.internal_mut() {
|
if let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
|
||||||
&mut elements::Internal::Function(ref mut func_index) => {
|
|
||||||
if *func_index >= gas_func { *func_index += 1}
|
if *func_index >= gas_func { *func_index += 1}
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -260,6 +251,9 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
&mut elements::Section::Start(ref mut start_idx) => {
|
||||||
|
if *start_idx >= gas_func { *start_idx += 1}
|
||||||
|
},
|
||||||
_ => { }
|
_ => { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,7 +274,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_grow() {
|
fn simple_grow() {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let module = builder::module()
|
let module = builder::module()
|
||||||
.global()
|
.global()
|
||||||
@@ -289,7 +283,7 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
GetGlobal(0),
|
GetGlobal(0),
|
||||||
GrowMemory(0),
|
GrowMemory(0),
|
||||||
@@ -335,7 +329,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn grow_no_gas_no_track() {
|
fn grow_no_gas_no_track() {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let module = builder::module()
|
let module = builder::module()
|
||||||
.global()
|
.global()
|
||||||
@@ -344,7 +338,7 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
GetGlobal(0),
|
GetGlobal(0),
|
||||||
GrowMemory(0),
|
GrowMemory(0),
|
||||||
@@ -378,7 +372,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple() {
|
fn simple() {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let module = builder::module()
|
let module = builder::module()
|
||||||
.global()
|
.global()
|
||||||
@@ -387,7 +381,7 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
GetGlobal(0),
|
GetGlobal(0),
|
||||||
End
|
End
|
||||||
@@ -414,7 +408,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested() {
|
fn nested() {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let module = builder::module()
|
let module = builder::module()
|
||||||
.global()
|
.global()
|
||||||
@@ -423,7 +417,7 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
GetGlobal(0),
|
GetGlobal(0),
|
||||||
Block(elements::BlockType::NoResult),
|
Block(elements::BlockType::NoResult),
|
||||||
@@ -464,7 +458,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ifelse() {
|
fn ifelse() {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let module = builder::module()
|
let module = builder::module()
|
||||||
.global()
|
.global()
|
||||||
@@ -473,7 +467,7 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
GetGlobal(0),
|
GetGlobal(0),
|
||||||
If(elements::BlockType::NoResult),
|
If(elements::BlockType::NoResult),
|
||||||
@@ -522,7 +516,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn call_index() {
|
fn call_index() {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let module = builder::module()
|
let module = builder::module()
|
||||||
.global()
|
.global()
|
||||||
@@ -535,7 +529,7 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
Call(0),
|
Call(0),
|
||||||
If(elements::BlockType::NoResult),
|
If(elements::BlockType::NoResult),
|
||||||
@@ -585,7 +579,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forbidden() {
|
fn forbidden() {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let module = builder::module()
|
let module = builder::module()
|
||||||
.global()
|
.global()
|
||||||
@@ -594,7 +588,7 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
F32Const(555555),
|
F32Const(555555),
|
||||||
End
|
End
|
||||||
|
|||||||
+27
-5
@@ -9,12 +9,9 @@ extern crate parity_wasm;
|
|||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
|
|
||||||
pub static CREATE_SYMBOL: &'static str = "deploy";
|
|
||||||
pub static CALL_SYMBOL: &'static str = "call";
|
|
||||||
pub static RET_SYMBOL: &'static str = "ret";
|
|
||||||
|
|
||||||
pub mod rules;
|
pub mod rules;
|
||||||
|
|
||||||
|
mod build;
|
||||||
mod optimizer;
|
mod optimizer;
|
||||||
mod gas;
|
mod gas;
|
||||||
mod symbols;
|
mod symbols;
|
||||||
@@ -24,18 +21,43 @@ mod runtime_type;
|
|||||||
|
|
||||||
pub mod stack_height;
|
pub mod stack_height;
|
||||||
|
|
||||||
|
pub use build::{build, SourceTarget, Error as BuildError};
|
||||||
pub use optimizer::{optimize, Error as OptimizerError};
|
pub use optimizer::{optimize, Error as OptimizerError};
|
||||||
pub use gas::inject_gas_counter;
|
pub use gas::inject_gas_counter;
|
||||||
pub use ext::{externalize, externalize_mem, underscore_funcs, ununderscore_funcs, shrink_unknown_stack};
|
pub use ext::{externalize, externalize_mem, underscore_funcs, ununderscore_funcs, shrink_unknown_stack};
|
||||||
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 runtime_type::inject_runtime_type;
|
||||||
|
|
||||||
|
pub struct TargetRuntime {
|
||||||
|
pub create_symbol: &'static str,
|
||||||
|
pub call_symbol: &'static str,
|
||||||
|
pub return_symbol: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetRuntime {
|
||||||
|
pub fn substrate() -> TargetRuntime {
|
||||||
|
TargetRuntime {
|
||||||
|
create_symbol: "deploy",
|
||||||
|
call_symbol: "call",
|
||||||
|
return_symbol: "ext_return",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pwasm() -> TargetRuntime {
|
||||||
|
TargetRuntime {
|
||||||
|
create_symbol: "deploy",
|
||||||
|
call_symbol: "call",
|
||||||
|
return_symbol: "ret",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
mod std {
|
mod std {
|
||||||
pub use core::*;
|
pub use core::*;
|
||||||
pub use alloc::{vec, string, boxed, borrow};
|
pub use alloc::{vec, string, boxed, borrow};
|
||||||
|
|
||||||
pub mod collections {
|
pub mod collections {
|
||||||
pub use alloc::{BTreeMap, BTreeSet};
|
pub use alloc::collections::{BTreeMap, BTreeSet};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-56
@@ -270,25 +270,22 @@ pub fn optimize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn update_call_index(opcodes: &mut elements::Opcodes, eliminated_indices: &[usize]) {
|
pub fn update_call_index(instructions: &mut elements::Instructions, eliminated_indices: &[usize]) {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
for opcode in opcodes.elements_mut().iter_mut() {
|
for instruction in instructions.elements_mut().iter_mut() {
|
||||||
match opcode {
|
if let &mut Call(ref mut call_index) = instruction {
|
||||||
&mut Call(ref mut call_index) => {
|
|
||||||
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;
|
||||||
},
|
|
||||||
_ => { },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(opcodes: &mut Vec<elements::Opcode>, eliminated_indices: &[usize]) {
|
pub fn update_global_index(instructions: &mut Vec<elements::Instruction>, eliminated_indices: &[usize]) {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
for opcode in opcodes.iter_mut() {
|
for instruction in instructions.iter_mut() {
|
||||||
match opcode {
|
match instruction {
|
||||||
&mut GetGlobal(ref mut index) | &mut SetGlobal(ref mut index) => {
|
&mut GetGlobal(ref mut index) | &mut SetGlobal(ref mut 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);
|
||||||
@@ -300,27 +297,21 @@ pub fn update_global_index(opcodes: &mut Vec<elements::Opcode>, eliminated_indic
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates global references considering the _ordered_ list of eliminated indices
|
/// Updates global references considering the _ordered_ list of eliminated indices
|
||||||
pub fn update_type_index(opcodes: &mut elements::Opcodes, eliminated_indices: &[usize]) {
|
pub fn update_type_index(instructions: &mut elements::Instructions, eliminated_indices: &[usize]) {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
for opcode in opcodes.elements_mut().iter_mut() {
|
for instruction in instructions.elements_mut().iter_mut() {
|
||||||
match opcode {
|
if let &mut CallIndirect(ref mut call_index, _) = instruction {
|
||||||
&mut CallIndirect(ref mut call_index, _) => {
|
|
||||||
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_indrect {} -> call_indirect {}", *call_index, *call_index - totalle as u32);
|
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<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::ImportSection> {
|
pub fn import_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::ImportSection> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match section {
|
if let &mut elements::Section::Import(ref mut sect) = section {
|
||||||
&mut elements::Section::Import(ref mut sect) => {
|
|
||||||
return Some(sect);
|
return Some(sect);
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -328,11 +319,8 @@ pub fn import_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut el
|
|||||||
|
|
||||||
pub fn global_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::GlobalSection> {
|
pub fn global_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::GlobalSection> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match section {
|
if let &mut elements::Section::Global(ref mut sect) = section {
|
||||||
&mut elements::Section::Global(ref mut sect) => {
|
|
||||||
return Some(sect);
|
return Some(sect);
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -340,11 +328,8 @@ pub fn global_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut el
|
|||||||
|
|
||||||
pub fn function_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::FunctionSection> {
|
pub fn function_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::FunctionSection> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match section {
|
if let &mut elements::Section::Function(ref mut sect) = section {
|
||||||
&mut elements::Section::Function(ref mut sect) => {
|
|
||||||
return Some(sect);
|
return Some(sect);
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -352,11 +337,8 @@ pub fn function_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut
|
|||||||
|
|
||||||
pub fn code_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::CodeSection> {
|
pub fn code_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::CodeSection> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match section {
|
if let &mut elements::Section::Code(ref mut sect) = section {
|
||||||
&mut elements::Section::Code(ref mut sect) => {
|
|
||||||
return Some(sect);
|
return Some(sect);
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -364,11 +346,8 @@ pub fn code_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elem
|
|||||||
|
|
||||||
pub fn export_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::ExportSection> {
|
pub fn export_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::ExportSection> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match section {
|
if let &mut elements::Section::Export(ref mut sect) = section {
|
||||||
&mut elements::Section::Export(ref mut sect) => {
|
|
||||||
return Some(sect);
|
return Some(sect);
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -376,11 +355,8 @@ pub fn export_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut el
|
|||||||
|
|
||||||
pub fn type_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::TypeSection> {
|
pub fn type_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::TypeSection> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match section {
|
if let &mut elements::Section::Type(ref mut sect) = section {
|
||||||
&mut elements::Section::Type(ref mut sect) => {
|
|
||||||
return Some(sect);
|
return Some(sect);
|
||||||
},
|
|
||||||
_ => { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@@ -459,10 +435,10 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::GetGlobal(0),
|
elements::Instruction::GetGlobal(0),
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
@@ -501,10 +477,10 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::GetGlobal(1),
|
elements::Instruction::GetGlobal(1),
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
@@ -534,10 +510,10 @@ mod tests {
|
|||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::Call(1),
|
elements::Instruction::Call(1),
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
@@ -582,15 +558,15 @@ mod tests {
|
|||||||
.signature().param().i32().param().i32().build()
|
.signature().param().i32().param().i32().build()
|
||||||
.build()
|
.build()
|
||||||
.function()
|
.function()
|
||||||
.signature().param().i32().param().i32().build()
|
.signature().param().i32().param().i32().param().i32().build()
|
||||||
.build()
|
.build()
|
||||||
.function()
|
.function()
|
||||||
.signature().param().i32().build()
|
.signature().param().i32().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::CallIndirect(1, 0),
|
elements::Instruction::CallIndirect(1, 0),
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
@@ -610,7 +586,7 @@ mod tests {
|
|||||||
|
|
||||||
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::Opcode::CallIndirect(0, 0) => {},
|
elements::Instruction::CallIndirect(0, 0) => {},
|
||||||
_ => {
|
_ => {
|
||||||
panic!(
|
panic!(
|
||||||
"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 {:?}",
|
||||||
|
|||||||
+57
-60
@@ -3,11 +3,11 @@ use std::vec::Vec;
|
|||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
|
||||||
use parity_wasm::elements::{
|
use parity_wasm::elements::{
|
||||||
self, Section, DataSection, Opcode, DataSegment, InitExpr, Internal, External,
|
self, Section, DataSection, Instruction, DataSegment, InitExpr, Internal, External,
|
||||||
ImportCountType,
|
ImportCountType,
|
||||||
};
|
};
|
||||||
use parity_wasm::builder;
|
use parity_wasm::builder;
|
||||||
use super::{CREATE_SYMBOL, CALL_SYMBOL, RET_SYMBOL};
|
use super::TargetRuntime;
|
||||||
use super::gas::update_call_index;
|
use super::gas::update_call_index;
|
||||||
|
|
||||||
/// Pack error.
|
/// Pack error.
|
||||||
@@ -20,9 +20,9 @@ pub enum Error {
|
|||||||
NoTypeSection,
|
NoTypeSection,
|
||||||
NoExportSection,
|
NoExportSection,
|
||||||
NoCodeSection,
|
NoCodeSection,
|
||||||
InvalidCreateSignature,
|
InvalidCreateSignature(&'static str),
|
||||||
NoCreateSymbol,
|
NoCreateSymbol(&'static str),
|
||||||
InvalidCreateMember,
|
InvalidCreateMember(&'static str),
|
||||||
NoImportSection,
|
NoImportSection,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,37 +33,37 @@ 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 => write!(f, "Exported symbol `deploy` has invalid signature, should be () -> ()"),
|
Error::InvalidCreateSignature(sym) => write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", sym),
|
||||||
Error::InvalidCreateMember => write!(f, "Exported symbol `deploy` should be a function"),
|
Error::InvalidCreateMember(sym) => write!(f, "Exported symbol `{}` should be a function", sym),
|
||||||
Error::NoCreateSymbol => write!(f, "No exported `deploy` symbol"),
|
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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If module has an exported "_create" function we want to pack it into "constructor".
|
/// If module has an exported "CREATE_SYMBOL" function 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) -> 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 witch is exported as "_create"
|
// We need to find an internal ID of function witch is exported as "CREATE_SYMBOL"
|
||||||
// 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.export_section().ok_or(Error::NoExportSection)?.entries().iter()
|
||||||
.find(|entry| CREATE_SYMBOL == entry.field()).ok_or(Error::NoCreateSymbol)?;
|
.find(|entry| target.create_symbol == entry.field()).ok_or_else(|| Error::NoCreateSymbol(target.create_symbol))?;
|
||||||
|
|
||||||
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) },
|
_ => { return Err(Error::InvalidCreateMember(target.create_symbol)) },
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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(i32)` (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.function_section().ok_or(Error::NoCodeSection)?
|
||||||
.entries().get(function_index - ctor_import_functions).ok_or(Error::MalformedModule)?
|
.entries().get(function_index - ctor_import_functions).ok_or(Error::MalformedModule)?
|
||||||
.type_ref();
|
.type_ref();
|
||||||
@@ -73,10 +73,10 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
|
|||||||
|
|
||||||
// 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);
|
return Err(Error::InvalidCreateSignature(target.create_symbol));
|
||||||
}
|
}
|
||||||
if func.return_type().is_some() {
|
if func.return_type().is_some() {
|
||||||
return Err(Error::InvalidCreateSignature);
|
return Err(Error::InvalidCreateSignature(target.create_symbol));
|
||||||
}
|
}
|
||||||
|
|
||||||
function_internal_index
|
function_internal_index
|
||||||
@@ -87,7 +87,7 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
|
|||||||
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() == RET_SYMBOL { found = true; break; }
|
if entry.field() == target.return_symbol { found = true; break; }
|
||||||
else { id += 1; }
|
else { id += 1; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
|
|||||||
mbuilder.push_import(
|
mbuilder.push_import(
|
||||||
builder::import()
|
builder::import()
|
||||||
.module("env")
|
.module("env")
|
||||||
.field("ret")
|
.field(&target.return_symbol)
|
||||||
.external().func(import_sig)
|
.external().func(import_sig)
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
@@ -120,11 +120,8 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
|
|||||||
},
|
},
|
||||||
elements::Section::Export(ref mut export_section) => {
|
elements::Section::Export(ref mut export_section) => {
|
||||||
for ref mut export in export_section.entries_mut() {
|
for ref mut export in export_section.entries_mut() {
|
||||||
match export.internal_mut() {
|
if let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
|
||||||
&mut elements::Internal::Function(ref mut func_index) => {
|
|
||||||
if *func_index >= ret_func { *func_index += 1}
|
if *func_index >= ret_func { *func_index += 1}
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -163,10 +160,9 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
|
|||||||
let mut code_data_address = 0i32;
|
let mut code_data_address = 0i32;
|
||||||
|
|
||||||
for section in ctor_module.sections_mut() {
|
for section in ctor_module.sections_mut() {
|
||||||
match section {
|
if let &mut Section::Data(ref mut data_section) = section {
|
||||||
&mut Section::Data(ref mut data_section) => {
|
|
||||||
let (index, offset) = if let Some(ref entry) = data_section.entries().iter().last() {
|
let (index, offset) = if let Some(ref entry) = data_section.entries().iter().last() {
|
||||||
if let Opcode::I32Const(offst) = entry.offset().code()[0] {
|
if let Instruction::I32Const(offst) = entry.offset().code()[0] {
|
||||||
let len = entry.value().len() as i32;
|
let len = entry.value().len() as i32;
|
||||||
let offst = offst as i32;
|
let offst = offst as i32;
|
||||||
(entry.index(), offst + (len + 4) - len % 4)
|
(entry.index(), offst + (len + 4) - len % 4)
|
||||||
@@ -178,42 +174,37 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
|
|||||||
};
|
};
|
||||||
let code_data = DataSegment::new(
|
let code_data = DataSegment::new(
|
||||||
index,
|
index,
|
||||||
InitExpr::new(vec![Opcode::I32Const(offset), Opcode::End]),
|
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;
|
||||||
},
|
|
||||||
_ => {;}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_module = builder::from_module(ctor_module)
|
let mut new_module = builder::from_module(ctor_module)
|
||||||
.function()
|
.function()
|
||||||
.signature().build()
|
.signature().build()
|
||||||
.body().with_opcodes(elements::Opcodes::new(
|
.body().with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
Opcode::Call((create_func_id + ctor_import_functions) as u32),
|
Instruction::Call((create_func_id + ctor_import_functions) as u32),
|
||||||
Opcode::I32Const(code_data_address),
|
Instruction::I32Const(code_data_address),
|
||||||
Opcode::I32Const(raw_module.len() as i32),
|
Instruction::I32Const(raw_module.len() as i32),
|
||||||
Opcode::Call(ret_function_id as u32),
|
Instruction::Call(ret_function_id as u32),
|
||||||
Opcode::End,
|
Instruction::End,
|
||||||
])).build()
|
])).build()
|
||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
for section in new_module.sections_mut() {
|
for section in new_module.sections_mut() {
|
||||||
match section {
|
if let &mut Section::Export(ref mut export_section) = section {
|
||||||
&mut Section::Export(ref mut export_section) => {
|
|
||||||
for entry in export_section.entries_mut().iter_mut() {
|
for entry in export_section.entries_mut().iter_mut() {
|
||||||
if CREATE_SYMBOL == entry.field() {
|
if target.create_symbol == entry.field() {
|
||||||
// change _create export name into default _call
|
// change `create_symbol` export name into default `call_symbol`.
|
||||||
*entry.field_mut() = CALL_SYMBOL.to_owned();
|
*entry.field_mut() = target.call_symbol.to_owned();
|
||||||
*entry.internal_mut() = elements::Internal::Function(last_function_index as u32);
|
*entry.internal_mut() = elements::Internal::Function(last_function_index as u32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
_ => { },
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -228,13 +219,13 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use super::super::optimize;
|
use super::super::optimize;
|
||||||
|
|
||||||
fn test_packer(mut module: elements::Module) {
|
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![CALL_SYMBOL]).expect("Optimizer to finish without errors");
|
optimize(&mut module, vec![target_runtime.call_symbol]).expect("Optimizer to finish without errors");
|
||||||
optimize(&mut ctor_module, vec![CREATE_SYMBOL]).expect("Optimizer to finish without errors");
|
optimize(&mut ctor_module, vec![target_runtime.create_symbol]).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).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 = ctor_module.data_section().expect("Packed module has to have a data section");
|
||||||
let data_segment = data_section.entries().iter().last().expect("Packed module has to have a data section with at least one entry");
|
let data_segment = data_section.entries().iter().last().expect("Packed module has to have a data section with at least one entry");
|
||||||
@@ -243,6 +234,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_data_section() {
|
fn no_data_section() {
|
||||||
|
let target_runtime = TargetRuntime::pwasm();
|
||||||
|
|
||||||
test_packer(builder::module()
|
test_packer(builder::module()
|
||||||
.import()
|
.import()
|
||||||
.module("env")
|
.module("env")
|
||||||
@@ -258,9 +251,9 @@ mod test {
|
|||||||
.function()
|
.function()
|
||||||
.signature().build()
|
.signature().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
@@ -268,27 +261,30 @@ mod test {
|
|||||||
.function()
|
.function()
|
||||||
.signature().build()
|
.signature().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
.build()
|
.build()
|
||||||
.export()
|
.export()
|
||||||
.field(CALL_SYMBOL)
|
.field(target_runtime.call_symbol)
|
||||||
.internal().func(1)
|
.internal().func(1)
|
||||||
.build()
|
.build()
|
||||||
.export()
|
.export()
|
||||||
.field(CREATE_SYMBOL)
|
.field(target_runtime.create_symbol)
|
||||||
.internal().func(2)
|
.internal().func(2)
|
||||||
.build()
|
.build()
|
||||||
.build()
|
.build(),
|
||||||
|
&target_runtime,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn with_data_section() {
|
fn with_data_section() {
|
||||||
|
let target_runtime = TargetRuntime::pwasm();
|
||||||
|
|
||||||
test_packer(builder::module()
|
test_packer(builder::module()
|
||||||
.import()
|
.import()
|
||||||
.module("env")
|
.module("env")
|
||||||
@@ -296,7 +292,7 @@ mod test {
|
|||||||
.external().memory(1 as u32, Some(1 as u32))
|
.external().memory(1 as u32, Some(1 as u32))
|
||||||
.build()
|
.build()
|
||||||
.data()
|
.data()
|
||||||
.offset(elements::Opcode::I32Const(16)).value(vec![0u8])
|
.offset(elements::Instruction::I32Const(16)).value(vec![0u8])
|
||||||
.build()
|
.build()
|
||||||
.function()
|
.function()
|
||||||
.signature()
|
.signature()
|
||||||
@@ -307,9 +303,9 @@ mod test {
|
|||||||
.function()
|
.function()
|
||||||
.signature().build()
|
.signature().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
@@ -317,22 +313,23 @@ mod test {
|
|||||||
.function()
|
.function()
|
||||||
.signature().build()
|
.signature().build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
vec![
|
vec![
|
||||||
elements::Opcode::End
|
elements::Instruction::End
|
||||||
]
|
]
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
.build()
|
.build()
|
||||||
.export()
|
.export()
|
||||||
.field(CALL_SYMBOL)
|
.field(target_runtime.call_symbol)
|
||||||
.internal().func(1)
|
.internal().func(1)
|
||||||
.build()
|
.build()
|
||||||
.export()
|
.export()
|
||||||
.field(CREATE_SYMBOL)
|
.field(target_runtime.create_symbol)
|
||||||
.internal().func(2)
|
.internal().func(2)
|
||||||
.build()
|
.build()
|
||||||
.build()
|
.build(),
|
||||||
|
&target_runtime,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -62,7 +62,7 @@ impl ::std::str::FromStr for InstructionType {
|
|||||||
"reinterpret" => Ok(InstructionType::Reinterpretation),
|
"reinterpret" => Ok(InstructionType::Reinterpretation),
|
||||||
"unreachable" => Ok(InstructionType::Unreachable),
|
"unreachable" => Ok(InstructionType::Unreachable),
|
||||||
"nop" => Ok(InstructionType::Nop),
|
"nop" => Ok(InstructionType::Nop),
|
||||||
"currrent_mem" => Ok(InstructionType::CurrentMemory),
|
"current_mem" => Ok(InstructionType::CurrentMemory),
|
||||||
"grow_mem" => Ok(InstructionType::GrowMemory),
|
"grow_mem" => Ok(InstructionType::GrowMemory),
|
||||||
_ => Err(UnknownInstruction),
|
_ => Err(UnknownInstruction),
|
||||||
}
|
}
|
||||||
@@ -70,10 +70,10 @@ impl ::std::str::FromStr for InstructionType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InstructionType {
|
impl InstructionType {
|
||||||
pub fn op(opcode: &elements::Opcode) -> Self {
|
pub fn op(instruction: &elements::Instruction) -> Self {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
match *opcode {
|
match *instruction {
|
||||||
Unreachable => InstructionType::Unreachable,
|
Unreachable => InstructionType::Unreachable,
|
||||||
Nop => InstructionType::Nop,
|
Nop => InstructionType::Nop,
|
||||||
Block(_) => InstructionType::ControlFlow,
|
Block(_) => InstructionType::ControlFlow,
|
||||||
@@ -288,8 +288,8 @@ impl Set {
|
|||||||
Set { regular: regular, entries: entries, grow: 0 }
|
Set { regular: regular, entries: entries, grow: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&self, opcode: &elements::Opcode) -> Result<u32, ()> {
|
pub fn process(&self, instruction: &elements::Instruction) -> Result<u32, ()> {
|
||||||
match self.entries.get(&InstructionType::op(opcode)).map(|x| *x) {
|
match self.entries.get(&InstructionType::op(instruction)).map(|x| *x) {
|
||||||
None | Some(Metering::Regular) => Ok(self.regular),
|
None | Some(Metering::Regular) => Ok(self.regular),
|
||||||
Some(Metering::Forbidden) => Err(()),
|
Some(Metering::Forbidden) => Err(()),
|
||||||
Some(Metering::Fixed(val)) => Ok(val),
|
Some(Metering::Fixed(val)) => Ok(val),
|
||||||
|
|||||||
+8
-6
@@ -1,8 +1,8 @@
|
|||||||
use parity_wasm::{elements, builder};
|
use parity_wasm::{elements, builder};
|
||||||
use self::elements::{ Module, GlobalEntry, External, ExportEntry, GlobalType, ValueType, InitExpr, Opcode, Internal };
|
use self::elements::{ Module, GlobalEntry, External, ExportEntry, GlobalType, ValueType, InitExpr, Instruction, Internal };
|
||||||
use byteorder::{ LittleEndian, ByteOrder };
|
use byteorder::{ LittleEndian, ByteOrder };
|
||||||
|
|
||||||
pub fn inject_runtime_type(module: Module, runtime_type: &[u8], 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(ref section) => section.entries().len() as u32,
|
Some(ref section) => section.entries().len() as u32,
|
||||||
@@ -18,9 +18,9 @@ pub fn inject_runtime_type(module: Module, runtime_type: &[u8], runtime_version:
|
|||||||
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![Opcode::I32Const(runtime_type as i32), Opcode::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![Opcode::I32Const(runtime_version as i32), Opcode::End])))
|
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(runtime_version as i32), Instruction::End])))
|
||||||
.with_export(ExportEntry::new("RUNTIME_VERSION".into(), Internal::Global(total_globals_count + 1)))
|
.with_export(ExportEntry::new("RUNTIME_VERSION".into(), Internal::Global(total_globals_count + 1)))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -31,9 +31,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![Opcode::I32Const(42 as i32)])))
|
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Instruction::I32Const(42 as i32)])))
|
||||||
.build();
|
.build();
|
||||||
module = inject_runtime_type(module, b"emcc", 1);
|
let mut runtime_type: [u8; 4] = Default::default();
|
||||||
|
runtime_type.copy_from_slice(b"emcc");
|
||||||
|
module = inject_runtime_type(module, runtime_type, 1);
|
||||||
let global_section = module.global_section().expect("Global section expected");
|
let global_section = module.global_section().expect("Global section expected");
|
||||||
assert_eq!(3, global_section.entries().len());
|
assert_eq!(3, global_section.entries().len());
|
||||||
let export_section = module.export_section().expect("Export section expected");
|
let export_section = module.export_section().expect("Export section expected");
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ impl Stack {
|
|||||||
|
|
||||||
/// This function expects the function to be validated.
|
/// This function expects the function to be validated.
|
||||||
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::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let func_section = module
|
let func_section = module
|
||||||
.function_section()
|
.function_section()
|
||||||
@@ -165,7 +165,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
|||||||
.bodies()
|
.bodies()
|
||||||
.get(func_idx as usize)
|
.get(func_idx as usize)
|
||||||
.ok_or_else(|| Error("Function body for the index isn't found".into()))?;
|
.ok_or_else(|| Error("Function body for the index isn't found".into()))?;
|
||||||
let opcodes = body.code();
|
let instructions = body.code();
|
||||||
|
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
let mut max_height: u32 = 0;
|
let mut max_height: u32 = 0;
|
||||||
@@ -186,7 +186,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
|||||||
});
|
});
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if pc >= opcodes.elements().len() {
|
if pc >= instructions.elements().len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
|||||||
max_height = stack.height();
|
max_height = stack.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
let opcode = &opcodes.elements()[pc];
|
let opcode = &instructions.elements()[pc];
|
||||||
trace!(target: "max_height", "{:?}", opcode);
|
trace!(target: "max_height", "{:?}", opcode);
|
||||||
|
|
||||||
match *opcode {
|
match *opcode {
|
||||||
|
|||||||
+12
-17
@@ -25,11 +25,12 @@
|
|||||||
//! Because stack height is increased prior the call few problems arises:
|
//! Because stack height is increased prior the call few problems arises:
|
||||||
//!
|
//!
|
||||||
//! - Stack height isn't increased upon an entry to the first function, i.e. exported function.
|
//! - Stack height isn't increased upon an entry to the first function, i.e. exported function.
|
||||||
|
//! - Start function is executed externally (similar to exported functions).
|
||||||
//! - It is statically unknown what function will be invoked in an indirect call.
|
//! - It is statically unknown what function will be invoked in an indirect call.
|
||||||
//!
|
//!
|
||||||
//! The solution for this problems is to generate a intermediate functions, called 'thunks', which
|
//! The solution for this problems is to generate a intermediate functions, called 'thunks', which
|
||||||
//! will increase before and decrease the stack height after the call to original function, and
|
//! will increase before and decrease the stack height after the call to original function, and
|
||||||
//! then make exported function and table entries to point to a corresponding thunks.
|
//! then make exported function and table entries, start section to point to a corresponding thunks.
|
||||||
//!
|
//!
|
||||||
//! # Stack cost
|
//! # Stack cost
|
||||||
//!
|
//!
|
||||||
@@ -57,7 +58,7 @@ use parity_wasm::builder;
|
|||||||
/// Macro to generate preamble and postamble.
|
/// Macro to generate preamble and postamble.
|
||||||
macro_rules! instrument_call {
|
macro_rules! instrument_call {
|
||||||
($callee_idx: expr, $callee_stack_cost: expr, $stack_height_global_idx: expr, $stack_limit: expr) => {{
|
($callee_idx: expr, $callee_stack_cost: expr, $stack_height_global_idx: expr, $stack_limit: expr) => {{
|
||||||
use $crate::parity_wasm::elements::Opcode::*;
|
use $crate::parity_wasm::elements::Instruction::*;
|
||||||
[
|
[
|
||||||
// stack_height += stack_cost(F)
|
// stack_height += stack_cost(F)
|
||||||
GetGlobal($stack_height_global_idx),
|
GetGlobal($stack_height_global_idx),
|
||||||
@@ -160,21 +161,18 @@ fn generate_stack_height_global(ctx: &mut Context, module: &mut elements::Module
|
|||||||
.value_type()
|
.value_type()
|
||||||
.i32()
|
.i32()
|
||||||
.mutable()
|
.mutable()
|
||||||
.init_expr(elements::Opcode::I32Const(0))
|
.init_expr(elements::Instruction::I32Const(0))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Try to find an existing global section.
|
// Try to find an existing global section.
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match *section {
|
if let elements::Section::Global(ref mut gs) = *section {
|
||||||
elements::Section::Global(ref mut gs) => {
|
|
||||||
gs.entries_mut().push(global_entry);
|
gs.entries_mut().push(global_entry);
|
||||||
|
|
||||||
let stack_height_global_idx = (gs.entries().len() as u32) - 1;
|
let stack_height_global_idx = (gs.entries().len() as u32) - 1;
|
||||||
ctx.stack_height_global_idx = Some(stack_height_global_idx);
|
ctx.stack_height_global_idx = Some(stack_height_global_idx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Existing section not found, create one!
|
// Existing section not found, create one!
|
||||||
@@ -233,15 +231,12 @@ fn compute_stack_cost(func_idx: u32, module: &elements::Module) -> Result<u32, E
|
|||||||
|
|
||||||
fn instrument_functions(ctx: &mut Context, module: &mut elements::Module) -> Result<(), Error> {
|
fn instrument_functions(ctx: &mut Context, module: &mut elements::Module) -> Result<(), Error> {
|
||||||
for section in module.sections_mut() {
|
for section in module.sections_mut() {
|
||||||
match *section {
|
if let elements::Section::Code(ref mut code_section) = *section {
|
||||||
elements::Section::Code(ref mut code_section) => {
|
|
||||||
for func_body in code_section.bodies_mut() {
|
for func_body in code_section.bodies_mut() {
|
||||||
let mut opcodes = func_body.code_mut();
|
let mut opcodes = func_body.code_mut();
|
||||||
instrument_function(ctx, opcodes)?;
|
instrument_function(ctx, opcodes)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -274,13 +269,13 @@ fn instrument_functions(ctx: &mut Context, module: &mut elements::Module) -> Res
|
|||||||
/// ```
|
/// ```
|
||||||
fn instrument_function(
|
fn instrument_function(
|
||||||
ctx: &mut Context,
|
ctx: &mut Context,
|
||||||
opcodes: &mut elements::Opcodes,
|
instructions: &mut elements::Instructions,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
loop {
|
loop {
|
||||||
if cursor >= opcodes.elements().len() {
|
if cursor >= instructions.elements().len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,8 +288,8 @@ fn instrument_function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let action: Action = {
|
let action: Action = {
|
||||||
let opcode = &opcodes.elements()[cursor];
|
let instruction = &instructions.elements()[cursor];
|
||||||
match *opcode {
|
match *instruction {
|
||||||
Call(ref callee_idx) => {
|
Call(ref callee_idx) => {
|
||||||
let callee_stack_cost = ctx
|
let callee_stack_cost = ctx
|
||||||
.stack_cost(*callee_idx)
|
.stack_cost(*callee_idx)
|
||||||
@@ -336,7 +331,7 @@ fn instrument_function(
|
|||||||
//
|
//
|
||||||
// To splice actually take a place, we need to consume iterator
|
// To splice actually take a place, we need to consume iterator
|
||||||
// splice returns. So we just `count()` it.
|
// splice returns. So we just `count()` it.
|
||||||
let _ = opcodes
|
let _ = instructions
|
||||||
.elements_mut()
|
.elements_mut()
|
||||||
.splice(cursor..(cursor + 1), new_seq.iter().cloned())
|
.splice(cursor..(cursor + 1), new_seq.iter().cloned())
|
||||||
.count();
|
.count();
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ pub(crate) fn generate_thunks(
|
|||||||
.elements_section()
|
.elements_section()
|
||||||
.map(|es| es.entries())
|
.map(|es| es.entries())
|
||||||
.unwrap_or(&[]);
|
.unwrap_or(&[]);
|
||||||
|
let start_func_idx = module
|
||||||
|
.start_section();
|
||||||
|
|
||||||
let exported_func_indicies = exports.iter().filter_map(|entry| match *entry.internal() {
|
let exported_func_indicies = exports.iter().filter_map(|entry| match *entry.internal() {
|
||||||
Internal::Function(ref function_idx) => Some(*function_idx),
|
Internal::Function(ref function_idx) => Some(*function_idx),
|
||||||
@@ -48,7 +50,7 @@ pub(crate) fn generate_thunks(
|
|||||||
// 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_indicies.chain(table_func_indicies) {
|
for func_idx in exported_func_indicies.chain(table_func_indicies).chain(start_func_idx.into_iter()) {
|
||||||
let callee_stack_cost = ctx.stack_cost(func_idx).ok_or_else(|| {
|
let callee_stack_cost = ctx.stack_cost(func_idx).ok_or_else(|| {
|
||||||
Error(format!("function with idx {} isn't found", func_idx))
|
Error(format!("function with idx {} isn't found", func_idx))
|
||||||
})?;
|
})?;
|
||||||
@@ -93,17 +95,17 @@ pub(crate) fn generate_thunks(
|
|||||||
// - argument pushing
|
// - argument pushing
|
||||||
// - instrumented call
|
// - instrumented call
|
||||||
// - end
|
// - end
|
||||||
let mut thunk_body: Vec<elements::Opcode> = Vec::with_capacity(
|
let mut thunk_body: Vec<elements::Instruction> = Vec::with_capacity(
|
||||||
thunk.signature.params().len() +
|
thunk.signature.params().len() +
|
||||||
instrumented_call.len() +
|
instrumented_call.len() +
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
for (arg_idx, _) in thunk.signature.params().iter().enumerate() {
|
for (arg_idx, _) in thunk.signature.params().iter().enumerate() {
|
||||||
thunk_body.push(elements::Opcode::GetLocal(arg_idx as u32));
|
thunk_body.push(elements::Instruction::GetLocal(arg_idx as u32));
|
||||||
}
|
}
|
||||||
thunk_body.extend(instrumented_call.iter().cloned());
|
thunk_body.extend(instrumented_call.iter().cloned());
|
||||||
thunk_body.push(elements::Opcode::End);
|
thunk_body.push(elements::Instruction::End);
|
||||||
|
|
||||||
// TODO: Don't generate a signature, but find an existing one.
|
// TODO: Don't generate a signature, but find an existing one.
|
||||||
|
|
||||||
@@ -114,7 +116,7 @@ pub(crate) fn generate_thunks(
|
|||||||
.with_return_type(thunk.signature.return_type().clone())
|
.with_return_type(thunk.signature.return_type().clone())
|
||||||
.build()
|
.build()
|
||||||
.body()
|
.body()
|
||||||
.with_opcodes(elements::Opcodes::new(
|
.with_instructions(elements::Instructions::new(
|
||||||
thunk_body
|
thunk_body
|
||||||
))
|
))
|
||||||
.build()
|
.build()
|
||||||
@@ -142,9 +144,8 @@ pub(crate) fn generate_thunks(
|
|||||||
match *section {
|
match *section {
|
||||||
elements::Section::Export(ref mut export_section) => {
|
elements::Section::Export(ref mut export_section) => {
|
||||||
for entry in export_section.entries_mut() {
|
for entry in export_section.entries_mut() {
|
||||||
match *entry.internal_mut() {
|
if let Internal::Function(ref mut function_idx) = *entry.internal_mut() {
|
||||||
Internal::Function(ref mut function_idx) => fixup(function_idx),
|
fixup(function_idx)
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +156,9 @@ pub(crate) fn generate_thunks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
elements::Section::Start(ref mut start_idx) => {
|
||||||
|
fixup(start_idx)
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-16
@@ -19,14 +19,11 @@ pub fn resolve_function(module: &elements::Module, index: u32) -> Symbol {
|
|||||||
let mut functions = 0;
|
let mut functions = 0;
|
||||||
if let Some(import_section) = module.import_section() {
|
if let Some(import_section) = module.import_section() {
|
||||||
for (item_index, item) in import_section.entries().iter().enumerate() {
|
for (item_index, item) in import_section.entries().iter().enumerate() {
|
||||||
match item.external() {
|
if let &elements::External::Function(_) = item.external() {
|
||||||
&elements::External::Function(_) => {
|
|
||||||
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,14 +35,11 @@ pub fn resolve_global(module: &elements::Module, index: u32) -> Symbol {
|
|||||||
let mut globals = 0;
|
let mut globals = 0;
|
||||||
if let Some(import_section) = module.import_section() {
|
if let Some(import_section) = module.import_section() {
|
||||||
for (item_index, item) in import_section.entries().iter().enumerate() {
|
for (item_index, item) in import_section.entries().iter().enumerate() {
|
||||||
match item.external() {
|
if let &elements::External::Global(_) = item.external() {
|
||||||
&elements::External::Global(_) => {
|
|
||||||
if globals == index {
|
if globals == index {
|
||||||
return Symbol::Import(item_index as usize);
|
return Symbol::Import(item_index as usize);
|
||||||
}
|
}
|
||||||
globals += 1;
|
globals += 1;
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,11 +47,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, opcodes: &[elements::Opcode], dest: &mut Vec<Symbol>) {
|
pub fn push_code_symbols(module: &elements::Module, instructions: &[elements::Instruction], dest: &mut Vec<Symbol>) {
|
||||||
use parity_wasm::elements::Opcode::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
for opcode in opcodes {
|
for instruction in instructions {
|
||||||
match opcode {
|
match instruction {
|
||||||
&Call(idx) => {
|
&Call(idx) => {
|
||||||
dest.push(resolve_function(module, idx));
|
dest.push(resolve_function(module, idx));
|
||||||
},
|
},
|
||||||
@@ -109,15 +103,12 @@ pub fn expand_symbols(module: &elements::Module, set: &mut Set<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];
|
||||||
match entry.external() {
|
if let &elements::External::Function(type_idx) = entry.external() {
|
||||||
&elements::External::Function(type_idx) => {
|
|
||||||
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) {
|
||||||
fringe.push(type_symbol);
|
fringe.push(type_symbol);
|
||||||
}
|
}
|
||||||
set.insert(type_symbol);
|
set.insert(type_symbol);
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Function(idx) => {
|
Function(idx) => {
|
||||||
|
|||||||
+32
-5
@@ -74,7 +74,10 @@ fn run_diff_test<F: FnOnce(&[u8]) -> Vec<u8>>(test_dir: &str, name: &str, test:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! def_stack_height_test {
|
mod stack_height {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! def_stack_height_test {
|
||||||
( $name:ident ) => {
|
( $name:ident ) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $name() {
|
fn $name() {
|
||||||
@@ -85,9 +88,33 @@ macro_rules! def_stack_height_test {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
def_stack_height_test!(simple);
|
||||||
|
def_stack_height_test!(start);
|
||||||
|
def_stack_height_test!(table);
|
||||||
|
def_stack_height_test!(global);
|
||||||
|
def_stack_height_test!(imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
def_stack_height_test!(simple);
|
mod gas {
|
||||||
def_stack_height_test!(table);
|
use super::*;
|
||||||
def_stack_height_test!(global);
|
|
||||||
def_stack_height_test!(imports);
|
macro_rules! def_gas_test {
|
||||||
|
( $name:ident ) => {
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
run_diff_test("gas", concat!(stringify!($name), ".wat"), |input| {
|
||||||
|
let rules = utils::rules::Set::default();
|
||||||
|
|
||||||
|
let module = elements::deserialize_buffer(input).expect("Failed to deserialize");
|
||||||
|
let instrumented = utils::inject_gas_counter(module, &rules).expect("Failed to instrument with gas metering");
|
||||||
|
elements::serialize(instrumented).expect("Failed to serialize")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
def_gas_test!(simple);
|
||||||
|
def_gas_test!(start);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
(module
|
||||||
|
(type (;0;) (func))
|
||||||
|
(type (;1;) (func (param i32)))
|
||||||
|
(import "env" "gas" (func (;0;) (type 1)))
|
||||||
|
(func (;1;) (type 0)
|
||||||
|
i32.const 3
|
||||||
|
call 0
|
||||||
|
i32.const 1
|
||||||
|
if ;; label = @1
|
||||||
|
i32.const 2
|
||||||
|
call 0
|
||||||
|
loop ;; label = @2
|
||||||
|
i32.const 3
|
||||||
|
call 0
|
||||||
|
i32.const 123
|
||||||
|
drop
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
(func (;2;) (type 0)
|
||||||
|
i32.const 2
|
||||||
|
call 0
|
||||||
|
block ;; label = @1
|
||||||
|
i32.const 1
|
||||||
|
call 0
|
||||||
|
end)
|
||||||
|
(export "simple" (func 1)))
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
(module
|
||||||
|
(type (;0;) (func (param i32 i32)))
|
||||||
|
(type (;1;) (func))
|
||||||
|
(type (;2;) (func (param i32)))
|
||||||
|
(import "env" "ext_return" (func (;0;) (type 0)))
|
||||||
|
(import "env" "memory" (memory (;0;) 1 1))
|
||||||
|
(import "env" "gas" (func (;1;) (type 2)))
|
||||||
|
(func (;2;) (type 1)
|
||||||
|
i32.const 5
|
||||||
|
call 1
|
||||||
|
i32.const 8
|
||||||
|
i32.const 4
|
||||||
|
call 0
|
||||||
|
unreachable)
|
||||||
|
(func (;3;) (type 1)
|
||||||
|
i32.const 1
|
||||||
|
call 1)
|
||||||
|
(export "call" (func 3))
|
||||||
|
(start 2)
|
||||||
|
(data (i32.const 8) "\01\02\03\04"))
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
(type (;0;) (func))
|
(type (;0;) (func))
|
||||||
(type (;1;) (func (param i32 i32) (result i32)))
|
(type (;1;) (func (param i32 i32) (result i32)))
|
||||||
(type (;2;) (func (param i32)))
|
(type (;2;) (func (param i32)))
|
||||||
(type (;3;) (func (param i32 i32) (result i32)))
|
|
||||||
(import "env" "foo" (func (;0;) (type 0)))
|
(import "env" "foo" (func (;0;) (type 0)))
|
||||||
(func (;1;) (type 1) (param i32 i32) (result i32)
|
(func (;1;) (type 1) (param i32 i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
@@ -33,7 +32,7 @@
|
|||||||
i32.sub
|
i32.sub
|
||||||
set_global 1
|
set_global 1
|
||||||
drop)
|
drop)
|
||||||
(func (;3;) (type 3) (param i32 i32) (result i32)
|
(func (;3;) (type 1) (param i32 i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
get_local 1
|
get_local 1
|
||||||
get_global 1
|
get_global 1
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
(module
|
(module
|
||||||
(type (;0;) (func))
|
(type (;0;) (func))
|
||||||
(type (;1;) (func (param i32 i32) (result i32)))
|
(type (;1;) (func (param i32 i32) (result i32)))
|
||||||
(type (;2;) (func (param i32 i32) (result i32)))
|
|
||||||
(import "env" "foo" (func (;0;) (type 0)))
|
(import "env" "foo" (func (;0;) (type 0)))
|
||||||
(import "env" "boo" (func (;1;) (type 0)))
|
(import "env" "boo" (func (;1;) (type 0)))
|
||||||
(func (;2;) (type 1) (param i32 i32) (result i32)
|
(func (;2;) (type 1) (param i32 i32) (result i32)
|
||||||
@@ -10,7 +9,7 @@
|
|||||||
get_local 0
|
get_local 0
|
||||||
get_local 1
|
get_local 1
|
||||||
i32.add)
|
i32.add)
|
||||||
(func (;3;) (type 2) (param i32 i32) (result i32)
|
(func (;3;) (type 1) (param i32 i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
get_local 1
|
get_local 1
|
||||||
get_global 0
|
get_global 0
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
(module
|
(module
|
||||||
(type (;0;) (func))
|
(type (;0;) (func))
|
||||||
(type (;1;) (func))
|
|
||||||
(func (;0;) (type 0)
|
(func (;0;) (type 0)
|
||||||
i32.const 123
|
i32.const 123
|
||||||
drop)
|
drop)
|
||||||
(func (;1;) (type 1)
|
(func (;1;) (type 0)
|
||||||
get_global 0
|
get_global 0
|
||||||
i32.const 1
|
i32.const 1
|
||||||
i32.add
|
i32.add
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
(module
|
||||||
|
(type (;0;) (func (param i32 i32)))
|
||||||
|
(type (;1;) (func))
|
||||||
|
(import "env" "ext_return" (func (;0;) (type 0)))
|
||||||
|
(import "env" "memory" (memory (;0;) 1 1))
|
||||||
|
(func (;1;) (type 1)
|
||||||
|
(local i32))
|
||||||
|
(func (;2;) (type 1))
|
||||||
|
(func (;3;) (type 1)
|
||||||
|
get_global 0
|
||||||
|
i32.const 1
|
||||||
|
i32.add
|
||||||
|
set_global 0
|
||||||
|
get_global 0
|
||||||
|
i32.const 1024
|
||||||
|
i32.gt_u
|
||||||
|
if ;; label = @1
|
||||||
|
unreachable
|
||||||
|
end
|
||||||
|
call 1
|
||||||
|
get_global 0
|
||||||
|
i32.const 1
|
||||||
|
i32.sub
|
||||||
|
set_global 0)
|
||||||
|
(func (;4;) (type 1)
|
||||||
|
get_global 0
|
||||||
|
i32.const 1
|
||||||
|
i32.add
|
||||||
|
set_global 0
|
||||||
|
get_global 0
|
||||||
|
i32.const 1024
|
||||||
|
i32.gt_u
|
||||||
|
if ;; label = @1
|
||||||
|
unreachable
|
||||||
|
end
|
||||||
|
call 1
|
||||||
|
get_global 0
|
||||||
|
i32.const 1
|
||||||
|
i32.sub
|
||||||
|
set_global 0)
|
||||||
|
(global (;0;) (mut i32) (i32.const 0))
|
||||||
|
(export "exported_start" (func 4))
|
||||||
|
(export "call" (func 2))
|
||||||
|
(start 4))
|
||||||
@@ -2,9 +2,6 @@
|
|||||||
(type (;0;) (func))
|
(type (;0;) (func))
|
||||||
(type (;1;) (func (param i32)))
|
(type (;1;) (func (param i32)))
|
||||||
(type (;2;) (func (param i32 i32) (result i32)))
|
(type (;2;) (func (param i32 i32) (result i32)))
|
||||||
(type (;3;) (func (param i32 i32) (result i32)))
|
|
||||||
(type (;4;) (func (param i32)))
|
|
||||||
(type (;5;) (func (param i32 i32) (result i32)))
|
|
||||||
(import "env" "foo" (func (;0;) (type 0)))
|
(import "env" "foo" (func (;0;) (type 0)))
|
||||||
(func (;1;) (type 1) (param i32)
|
(func (;1;) (type 1) (param i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
@@ -29,7 +26,7 @@
|
|||||||
get_local 0
|
get_local 0
|
||||||
get_local 1
|
get_local 1
|
||||||
i32.add)
|
i32.add)
|
||||||
(func (;3;) (type 3) (param i32 i32) (result i32)
|
(func (;3;) (type 2) (param i32 i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
get_local 1
|
get_local 1
|
||||||
get_global 0
|
get_global 0
|
||||||
@@ -47,7 +44,7 @@
|
|||||||
i32.const 2
|
i32.const 2
|
||||||
i32.sub
|
i32.sub
|
||||||
set_global 0)
|
set_global 0)
|
||||||
(func (;4;) (type 4) (param i32)
|
(func (;4;) (type 1) (param i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
get_global 0
|
get_global 0
|
||||||
i32.const 2
|
i32.const 2
|
||||||
@@ -64,7 +61,7 @@
|
|||||||
i32.const 2
|
i32.const 2
|
||||||
i32.sub
|
i32.sub
|
||||||
set_global 0)
|
set_global 0)
|
||||||
(func (;5;) (type 5) (param i32 i32) (result i32)
|
(func (;5;) (type 2) (param i32 i32) (result i32)
|
||||||
get_local 0
|
get_local 0
|
||||||
get_local 1
|
get_local 1
|
||||||
get_global 0
|
get_global 0
|
||||||
|
|||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
(module
|
||||||
|
(func (export "simple")
|
||||||
|
(if (i32.const 1)
|
||||||
|
(loop
|
||||||
|
i32.const 123
|
||||||
|
drop
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func
|
||||||
|
block
|
||||||
|
end
|
||||||
|
)
|
||||||
|
)
|
||||||
Vendored
+18
@@ -0,0 +1,18 @@
|
|||||||
|
(module
|
||||||
|
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||||
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
|
(start $start)
|
||||||
|
(func $start
|
||||||
|
(call $ext_return
|
||||||
|
(i32.const 8)
|
||||||
|
(i32.const 4)
|
||||||
|
)
|
||||||
|
(unreachable)
|
||||||
|
)
|
||||||
|
|
||||||
|
(func (export "call")
|
||||||
|
)
|
||||||
|
|
||||||
|
(data (i32.const 8) "\01\02\03\04")
|
||||||
|
)
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
(module
|
||||||
|
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||||
|
(import "env" "memory" (memory 1 1))
|
||||||
|
|
||||||
|
(start $start)
|
||||||
|
(func $start (export "exported_start")
|
||||||
|
(local i32)
|
||||||
|
)
|
||||||
|
(func (export "call")
|
||||||
|
)
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user