49 Commits

Author SHA1 Message Date
Sergey Pepyakin b58e01ec67 Bump version to 0.6.1 2019-01-04 14:38:08 +01:00
Sergey Pepyakin 8db40174ae Add gas tests. 2018-12-24 20:43:23 +01:00
Sergey Pepyakin 471a9b3fcc Account start function in gas func patching. 2018-12-24 19:28:53 +01:00
Sergey Pepyakin 3db0d60e70 Teach stack limiter to handle start fn 2018-12-24 19:20:33 +01:00
NikVolf fe25beca2b bump all to 0.6 2018-09-30 19:02:14 +01:00
Nikolay Volf 24b97b517a Merge pull request #102 from paritytech/ser-introduce-substrate-contracts
Introduce substrate contracts support.
2018-09-30 19:00:44 +01:00
Sergey Pepyakin 836ec93008 Introduce substrate contracts support. 2018-09-30 18:24:36 +01:00
NikVolf 5238b41af2 bump cli to 0.4.1 2018-08-06 16:51:35 +03:00
Nikolay Volf 19ce379f64 Merge pull request #101 from paritytech/fix-output
Fix not saving raw module when no constructor specified
2018-08-06 16:50:57 +03:00
NikVolf a9f5058b4f fix not saving raw module when no constructor specified 2018-08-06 16:09:11 +03:00
NikVolf 4b8b07a0b5 bump versions 2018-08-06 15:43:46 +03:00
Nikolay Volf 67d67f3fba Merge pull request #100 from paritytech/no-constructor
The constructor arg removed from
2018-08-06 15:40:04 +03:00
fro 3a7f8836dd the constructor arg removed from 2018-08-06 15:19:01 +03:00
NikVolf abb5ae6f22 bump cli to 0.3.0 2018-08-03 14:11:29 +03:00
NikVolf 9d0ad5b309 bump to 0.4.0 2018-08-03 14:10:51 +03:00
NikVolf 6c510a7f35 bump to 0.3.2 2018-08-01 17:29:42 +03:00
Alexey e491789127 Move build logic to lib (#97)
* refactored out build to lib

* save_raw returns

* fix indentations and other small fixes

* fix build API

* rename Target to SourceTarget

* fix formatting

* make join runtime_type into runtime_type and runtime_version
2018-08-01 17:26:22 +03:00
Alexey 3e7946ab1c fix call_indirect test (#98)
Merged
2018-08-01 13:48:38 +03:00
Nikolay Volf 41839664bb wasm-check utility (#94)
* wasm-check utility

* more runtime externs

* couple more imports
2018-07-12 18:49:09 +03:00
NikVolf 735110e8d5 bump to 0.3.1 2018-07-09 18:31:58 +03:00
Nikolay Volf 0fe96ee497 Merge pull request #93 from paritytech/fix-nightly
Fix nightly compilation
2018-07-09 18:31:20 +03:00
NikVolf 0837464ec4 change imports 2018-07-09 17:44:04 +03:00
NikVolf 7366384861 bump to lib to 0.3 and cli to 0.2 2018-07-04 12:17:42 +03:00
Sergey Pepyakin db80363d56 Merge pull request #91 from paritytech/bump-version-0.2.2
Bump version up to 0.2.2
2018-07-02 16:39:06 +03:00
Sergey Pepyakin bbb6c6078a Bump version up to 0.2.2. 2018-07-02 15:21:37 +03:00
Nikolay Volf f7e71718a4 Merge pull request #90 from paritytech/parity-wasm-bump
Update parity-wasm dependency to 0.31
2018-06-29 17:23:11 +03:00
Wei Tang af2d61b9f8 Fix tests and cli 2018-06-29 19:06:33 +08:00
Wei Tang d6f82000ee Update parity-wasm dependency to 0.31 2018-06-29 19:01:06 +08:00
Sergey Pepyakin f4b75bd840 Merge pull request #88 from sphinxc0re/patch-1
Fixed typo
2018-06-11 16:55:26 +03:00
Julian Laubstein 187844f79d Fixed typo 2018-06-06 16:27:20 +02:00
NikVolf a4ff19d358 bump to 0.2.1 2018-05-31 15:40:23 +02:00
Nikolay Volf e31f1040e1 Merge pull request #87 from HCastano/use-if-let-statements
Update matches with single arm to be if-let statements
2018-05-31 16:37:47 +03:00
Hernando Castano e6e340fa0a Update matches with single arm to be if-let statements 2018-05-29 22:46:11 -04:00
Nikolay Volf de23bfac0a Merge pull request #84 from paritytech/public-api
Add public api switch to wasm-build
2018-05-21 22:57:27 +03:00
Nikolay Volf 261c823b63 Merge pull request #82 from paritytech/pack-cli
Standalone wasm-pack binary
2018-05-20 22:51:40 +04:00
NikVolf ce865c1e8a add public api switch 2018-05-18 17:51:37 +04:00
Nikolay Volf 2d60c0bb0e Merge pull request #81 from paritytech/small-fixes
Small fixes in comments
2018-05-18 17:10:55 +04:00
NikVolf 5609a08e99 change to expect 2018-05-18 16:47:06 +04:00
NikVolf edabee0649 more fixes to fixes 2018-05-18 16:45:04 +04:00
NikVolf 367514ae07 actual implementation of packer 2018-05-18 16:37:49 +04:00
NikVolf 816f14eac7 also call/deploy update 2018-05-18 16:29:35 +04:00
NikVolf f9540c5423 some fixes 2018-05-18 16:22:41 +04:00
NikVolf 04ac17c3d5 additional binary 2018-05-18 16:21:25 +04:00
NikVolf f7e6631c83 add contribution section 2018-05-16 20:52:54 +04:00
Alexey 20ff66c649 Merge pull request #79 from elopio/patch-1
Fix typo
2018-05-16 19:04:26 +03:00
Leo Arias 5ef171209b Fix typo 2018-05-16 10:01:51 -06:00
Nikolay Volf 947f0b8bbb Update README.md 2018-05-16 17:39:34 +04:00
NikVolf 7f8811cb2c readme for cli 2018-05-15 18:18:49 +04:00
NikVolf 4112b4b961 bump/set versions 2018-05-15 18:14:23 +04:00
32 changed files with 812 additions and 433 deletions
+2 -2
View File
@@ -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 }
+12 -6
View File
@@ -2,31 +2,31 @@
[![Build Status](https://travis-ci.org/paritytech/wasm-utils.svg?branch=master)](https://travis-ci.org/paritytech/wasm-utils) [![Build Status](https://travis-ci.org/paritytech/wasm-utils.svg?branch=master)](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
View File
@@ -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"
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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)]
+102
View File
@@ -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; }
}
}
}
+37
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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");
+4 -4
View File
@@ -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
View File
@@ -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();
+12 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+26
View File
@@ -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)))
+20
View File
@@ -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"))
+1 -2
View File
@@ -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 -2
View File
@@ -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 -2
View File
@@ -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
+44
View File
@@ -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))
+3 -6
View File
@@ -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
+15
View File
@@ -0,0 +1,15 @@
(module
(func (export "simple")
(if (i32.const 1)
(loop
i32.const 123
drop
)
)
)
(func
block
end
)
)
+18
View File
@@ -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
View File
@@ -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")
)
)