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]
name = "pwasm-utils"
version = "0.1.5"
version = "0.6.1"
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
license = "MIT/Apache-2.0"
readme = "README.md"
@@ -8,7 +8,7 @@ description = "Collection of command-line utilities and corresponding Rust api f
keywords = ["wasm", "webassembly", "pwasm"]
[dependencies]
parity-wasm = { version = "0.30", default-features = false }
parity-wasm = { version = "0.31", default-features = false }
log = { version = "0.4", 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)
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
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)
```
cargo install pwasm-utils
cargo install pwasm-utils-cli --bin wasm-prune
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)
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>
```
@@ -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.
```
cargo install pwasm-utils
cargo install pwasm-utils-cli --bin wasm-ext
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.
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]
name = "pwasm-utils-cli"
version = "0.1.5"
version = "0.6.0"
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Sergey Pepyakin <s.pepyakin@gmail.com>"]
license = "MIT/Apache-2.0"
readme = "README.md"
@@ -29,9 +29,17 @@ path = "build/main.rs"
name = "wasm-stack-height"
path = "stack_height/main.rs"
[[bin]]
name = "wasm-pack"
path = "pack/main.rs"
[[bin]]
name = "wasm-check"
path = "check/main.rs"
[dependencies]
parity-wasm = "0.30"
pwasm-utils = { path = ".." }
parity-wasm = "0.31"
pwasm-utils = { path = "..", version = "0.6" }
glob = "0.2"
clap = "2.24"
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;
use std::{fs, io};
use std::io::Write;
use std::path::PathBuf;
use clap::{App, Arg};
use parity_wasm::elements;
use utils::{CREATE_SYMBOL, CALL_SYMBOL, ununderscore_funcs, externalize_mem, shrink_unknown_stack};
use utils::{build, BuildError, SourceTarget, TargetRuntime};
#[derive(Debug)]
pub enum Error {
@@ -23,38 +21,18 @@ pub enum Error {
FailedToCopy(String),
Decoding(elements::Error, String),
Encoding(elements::Error),
Packing(utils::PackingError),
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)
}
Build(BuildError),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
use Error::*;
use self::Error::*;
match *self {
Io(ref io) => write!(f, "Generic i/o error: {}", io),
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),
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?"),
Packing(ref e) => write!(f, "Packing failed due to module structure error: {}. Sure used correct libraries for building contracts?", e),
Build(ref err) => write!(f, "Build error: {}", err)
}
}
}
@@ -70,8 +48,8 @@ pub fn process_output(input: &source::SourceInput) -> Result<(), Error> {
let wasm_name = input.bin_name().to_string().replace("-", "_");
cargo_path.push(
match input.target() {
source::SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET,
source::SourceTarget::Unknown => source::UNKNOWN_TRIPLET,
SourceTarget::Emscripten => source::EMSCRIPTEN_TRIPLET,
SourceTarget::Unknown => source::UNKNOWN_TRIPLET,
}
);
cargo_path.push("release");
@@ -87,14 +65,6 @@ pub fn process_output(input: &source::SourceInput) -> Result<(), Error> {
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> {
logger::init_log();
@@ -107,6 +77,12 @@ fn do_main() -> Result<(), Error> {
.index(2)
.required(true)
.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")
.help("Skip symbol optimization step producing final wasm")
.long("skip-optimization"))
@@ -137,6 +113,11 @@ fn do_main() -> Result<(), Error> {
.help("Shrinks the new stack size for wasm32-unknown-unknown")
.takes_value(true)
.long("shrink-stack"))
.arg(Arg::with_name("public_api")
.help("Preserves specific imports in the library")
.takes_value(true)
.long("public-api"))
.get_matches();
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 mut module = parity_wasm::deserialize_file(&path)
let module = parity_wasm::deserialize_file(&path)
.map_err(|e| Error::Decoding(e, path.to_string()))?;
if let source::SourceTarget::Emscripten = source_input.target() {
module = ununderscore_funcs(module);
}
if let source::SourceTarget::Unknown = source_input.target() {
// 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 {
let runtime_type_version = if let (Some(runtime_type), Some(runtime_version))
= (matches.value_of("runtime_type"), matches.value_of("runtime_version")) {
let mut ty: [u8; 4] = Default::default();
let runtime_bytes = runtime_type.as_bytes();
if runtime_bytes.len() != 4 {
panic!("--runtime-type should be equal to 4 bytes");
}
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");
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") {
utils::optimize(
&mut module,
vec![CALL_SYMBOL]
)?;
}
let target_runtime = match matches.value_of("target-runtime").expect("target-runtime has a default value; qed") {
"pwasm" => TargetRuntime::pwasm(),
"substrate" => TargetRuntime::substrate(),
_ => unreachable!("all possible values are enumerated in clap config; qed"),
};
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") {
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 module has an exported function with name=CREATE_SYMBOL
// build will pack the module (raw_module) into this funciton and export as CALL_SYMBOL.
// Otherwise it will just save an optimised raw_module
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)?;
if let Some(ctor_module) = ctor_module {
parity_wasm::serialize_to_file(
&path,
ctor_module,
).map_err(Error::Encoding)?;
} else {
let mut file = fs::File::create(&path)?;
file.write_all(&raw_module)?;
parity_wasm::serialize_to_file(&path, module).map_err(Error::Encoding)?;
}
Ok(())
+2 -7
View File
@@ -3,12 +3,7 @@
pub const UNKNOWN_TRIPLET: &str = "wasm32-unknown-unknown";
pub const EMSCRIPTEN_TRIPLET: &str = "wasm32-unknown-emscripten";
/// Target configiration of previous build step
#[derive(Debug, Clone, Copy)]
pub enum SourceTarget {
Emscripten,
Unknown,
}
use utils::SourceTarget;
/// Configuration of previous build step (cargo compilation)
#[derive(Debug)]
@@ -59,4 +54,4 @@ impl<'a> SourceInput<'a> {
pub fn target(&self) -> SourceTarget {
self.target
}
}
}
+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() {
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")
.index(1)
.required(true)
@@ -22,12 +24,12 @@ fn main() {
.short("e")
.takes_value(true)
.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();
let exports = matches
.value_of("exports")
.unwrap_or("_call")
.unwrap_or(target_runtime.call_symbol)
.split(',')
.collect();
@@ -39,7 +41,7 @@ fn main() {
// Invoke optimizer
// Contract is supposed to have only these functions as public api
// 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))
}
}
+15 -24
View File
@@ -8,29 +8,23 @@ use byteorder::{LittleEndian, ByteOrder};
type Insertion = (usize, u32, u32, String);
pub fn update_call_index(opcodes: &mut elements::Opcodes, original_imports: usize, inserts: &[Insertion]) {
use parity_wasm::elements::Opcode::*;
for opcode in opcodes.elements_mut().iter_mut() {
match opcode {
&mut Call(ref mut call_index) => {
if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
*call_index = (original_imports + pos) as u32;
} else if *call_index as usize > original_imports {
*call_index += inserts.len() as u32;
}
},
_ => { }
pub fn update_call_index(instructions: &mut elements::Instructions, original_imports: usize, inserts: &[Insertion]) {
use parity_wasm::elements::Instruction::*;
for instruction in instructions.elements_mut().iter_mut() {
if let &mut Call(ref mut call_index) = instruction {
if let Some(pos) = inserts.iter().position(|x| x.1 == *call_index) {
*call_index = (original_imports + pos) as u32;
} else if *call_index as usize > original_imports {
*call_index += inserts.len() as u32;
}
}
}
}
pub fn memory_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::MemorySection> {
for section in module.sections_mut() {
match section {
&mut elements::Section::Memory(ref mut sect) => {
return Some(sect);
},
_ => { }
for section in module.sections_mut() {
if let &mut elements::Section::Memory(ref mut sect) = section {
return Some(sect);
}
}
None
@@ -104,7 +98,7 @@ pub fn shrink_unknown_stack(
match section {
&mut elements::Section::Data(ref mut data_section) => {
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);
let current_val = LittleEndian::read_u32(data_segment.value());
let new_val = current_val - shrink_amount;
@@ -182,11 +176,8 @@ pub fn externalize(
},
&mut elements::Section::Export(ref mut export_section) => {
for ref mut export in export_section.entries_mut() {
match 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 let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
if *func_index >= import_funcs_total as u32 { *func_index += replaces.len() as u32; }
}
}
},
+43 -49
View File
@@ -3,14 +3,11 @@ use std::vec::Vec;
use parity_wasm::{elements, builder};
use rules;
pub fn update_call_index(opcodes: &mut elements::Opcodes, inserted_index: u32) {
use parity_wasm::elements::Opcode::*;
for opcode in opcodes.elements_mut().iter_mut() {
match opcode {
&mut Call(ref mut call_index) => {
if *call_index >= inserted_index { *call_index += 1}
},
_ => { },
pub fn update_call_index(instructions: &mut elements::Instructions, inserted_index: u32) {
use parity_wasm::elements::Instruction::*;
for instruction in instructions.elements_mut().iter_mut() {
if let &mut Call(ref mut call_index) = instruction {
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 {
use parity_wasm::elements::Opcode::*;
fn inject_grow_counter(instructions: &mut elements::Instructions, grow_counter_func: u32) -> usize {
use parity_wasm::elements::Instruction::*;
let mut counter = 0;
for opcode in opcodes.elements_mut() {
match *opcode {
GrowMemory(_) => {
*opcode = Call(grow_counter_func);
counter += 1;
},
_ => {}
for instruction in instructions.elements_mut() {
if let GrowMemory(_) = *instruction {
*instruction = Call(grow_counter_func);
counter += 1;
}
}
counter
}
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);
b.push_function(
builder::function()
.signature().params().i32().build().with_return_type(Some(elements::ValueType::I32)).build()
.body()
.with_opcodes(elements::Opcodes::new(vec![
.with_instructions(elements::Instructions::new(vec![
GetLocal(0),
GetLocal(0),
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(
opcodes: &mut elements::Opcodes,
instructions: &mut elements::Instructions,
rules: &rules::Set,
gas_func: u32,
) -> Result<(), ()> {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let mut counter = Counter::new();
// Begin an implicit function (i.e. `func...end`) block.
counter.begin(0);
for cursor in 0..opcodes.elements().len() {
let opcode = &opcodes.elements()[cursor];
match *opcode {
for cursor in 0..instructions.elements().len() {
let instruction = &instructions.elements()[cursor];
match *instruction {
Block(_) | If(_) | Loop(_) => {
// Increment previous block with the cost of the current opcode.
let opcode_cost = rules.process(opcode)?;
counter.increment(opcode_cost)?;
let instruction_cost = rules.process(instruction)?;
counter.increment(instruction_cost)?;
// Begin new block. The cost of the following opcodes until `End` or `Else` will
// 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.
let opcode_cost = rules.process(opcode)?;
counter.increment(opcode_cost)?;
let instruction_cost = rules.process(instruction)?;
counter.increment(instruction_cost)?;
}
}
}
@@ -181,8 +175,8 @@ pub fn inject_counter(
for block in counter.blocks {
let effective_pos = block.start_pos + cumulative_offset;
opcodes.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, I32Const(block.cost as i32));
instructions.elements_mut().insert(effective_pos+1, Call(gas_func));
// Take into account these two inserted instructions.
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) => {
for ref mut export in export_section.entries_mut() {
match export.internal_mut() {
&mut elements::Internal::Function(ref mut func_index) => {
if *func_index >= gas_func { *func_index += 1}
},
_ => {}
if let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
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]
fn simple_grow() {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let module = builder::module()
.global()
@@ -289,7 +283,7 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
GetGlobal(0),
GrowMemory(0),
@@ -335,7 +329,7 @@ mod tests {
#[test]
fn grow_no_gas_no_track() {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let module = builder::module()
.global()
@@ -344,7 +338,7 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
GetGlobal(0),
GrowMemory(0),
@@ -378,7 +372,7 @@ mod tests {
#[test]
fn simple() {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let module = builder::module()
.global()
@@ -387,7 +381,7 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
GetGlobal(0),
End
@@ -414,7 +408,7 @@ mod tests {
#[test]
fn nested() {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let module = builder::module()
.global()
@@ -423,7 +417,7 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
GetGlobal(0),
Block(elements::BlockType::NoResult),
@@ -464,7 +458,7 @@ mod tests {
#[test]
fn ifelse() {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let module = builder::module()
.global()
@@ -473,7 +467,7 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
GetGlobal(0),
If(elements::BlockType::NoResult),
@@ -522,7 +516,7 @@ mod tests {
#[test]
fn call_index() {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let module = builder::module()
.global()
@@ -535,7 +529,7 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
Call(0),
If(elements::BlockType::NoResult),
@@ -585,7 +579,7 @@ mod tests {
#[test]
fn forbidden() {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let module = builder::module()
.global()
@@ -594,7 +588,7 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
F32Const(555555),
End
+27 -5
View File
@@ -9,12 +9,9 @@ extern crate parity_wasm;
extern crate byteorder;
#[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;
mod build;
mod optimizer;
mod gas;
mod symbols;
@@ -24,18 +21,43 @@ mod runtime_type;
pub mod stack_height;
pub use build::{build, SourceTarget, Error as BuildError};
pub use optimizer::{optimize, Error as OptimizerError};
pub use gas::inject_gas_counter;
pub use ext::{externalize, externalize_mem, underscore_funcs, ununderscore_funcs, shrink_unknown_stack};
pub use pack::{pack_instance, Error as PackingError};
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"))]
mod std {
pub use core::*;
pub use alloc::{vec, string, boxed, borrow};
pub mod collections {
pub use alloc::{BTreeMap, BTreeSet};
pub use alloc::collections::{BTreeMap, BTreeSet};
}
}
+44 -68
View File
@@ -270,25 +270,22 @@ pub fn optimize(
}
pub fn update_call_index(opcodes: &mut elements::Opcodes, eliminated_indices: &[usize]) {
use parity_wasm::elements::Opcode::*;
for opcode in opcodes.elements_mut().iter_mut() {
match opcode {
&mut Call(ref mut call_index) => {
let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count();
trace!("rewired call {} -> call {}", *call_index, *call_index - totalle as u32);
*call_index -= totalle as u32;
},
_ => { },
pub fn update_call_index(instructions: &mut elements::Instructions, eliminated_indices: &[usize]) {
use parity_wasm::elements::Instruction::*;
for instruction in instructions.elements_mut().iter_mut() {
if let &mut Call(ref mut call_index) = instruction {
let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count();
trace!("rewired call {} -> call {}", *call_index, *call_index - totalle as u32);
*call_index -= totalle as u32;
}
}
}
/// Updates global references considering the _ordered_ list of eliminated indices
pub fn update_global_index(opcodes: &mut Vec<elements::Opcode>, eliminated_indices: &[usize]) {
use parity_wasm::elements::Opcode::*;
for opcode in opcodes.iter_mut() {
match opcode {
pub fn update_global_index(instructions: &mut Vec<elements::Instruction>, eliminated_indices: &[usize]) {
use parity_wasm::elements::Instruction::*;
for instruction in instructions.iter_mut() {
match instruction {
&mut GetGlobal(ref mut index) | &mut SetGlobal(ref mut index) => {
let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *index).count();
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
pub fn update_type_index(opcodes: &mut elements::Opcodes, eliminated_indices: &[usize]) {
use parity_wasm::elements::Opcode::*;
for opcode in opcodes.elements_mut().iter_mut() {
match opcode {
&mut CallIndirect(ref mut call_index, _) => {
let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count();
trace!("rewired call_indrect {} -> call_indirect {}", *call_index, *call_index - totalle as u32);
*call_index -= totalle as u32;
},
_ => { },
pub fn update_type_index(instructions: &mut elements::Instructions, eliminated_indices: &[usize]) {
use parity_wasm::elements::Instruction::*;
for instruction in instructions.elements_mut().iter_mut() {
if let &mut CallIndirect(ref mut call_index, _) = instruction {
let totalle = eliminated_indices.iter().take_while(|i| (**i as u32) < *call_index).count();
trace!("rewired call_indrect {} -> call_indirect {}", *call_index, *call_index - totalle as u32);
*call_index -= totalle as u32;
}
}
}
pub fn import_section<'a>(module: &'a mut elements::Module) -> Option<&'a mut elements::ImportSection> {
for section in module.sections_mut() {
match section {
&mut elements::Section::Import(ref mut sect) => {
return Some(sect);
},
_ => { }
if let &mut elements::Section::Import(ref mut sect) = section {
return Some(sect);
}
}
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> {
for section in module.sections_mut() {
match section {
&mut elements::Section::Global(ref mut sect) => {
return Some(sect);
},
_ => { }
if let &mut elements::Section::Global(ref mut sect) = section {
return Some(sect);
}
}
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> {
for section in module.sections_mut() {
match section {
&mut elements::Section::Function(ref mut sect) => {
return Some(sect);
},
_ => { }
if let &mut elements::Section::Function(ref mut sect) = section {
return Some(sect);
}
}
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> {
for section in module.sections_mut() {
match section {
&mut elements::Section::Code(ref mut sect) => {
return Some(sect);
},
_ => { }
if let &mut elements::Section::Code(ref mut sect) = section {
return Some(sect);
}
}
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> {
for section in module.sections_mut() {
match section {
&mut elements::Section::Export(ref mut sect) => {
return Some(sect);
},
_ => { }
if let &mut elements::Section::Export(ref mut sect) = section {
return Some(sect);
}
}
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> {
for section in module.sections_mut() {
match section {
&mut elements::Section::Type(ref mut sect) => {
return Some(sect);
},
_ => { }
if let &mut elements::Section::Type(ref mut sect) = section {
return Some(sect);
}
}
None
@@ -459,10 +435,10 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::GetGlobal(0),
elements::Opcode::End
elements::Instruction::GetGlobal(0),
elements::Instruction::End
]
))
.build()
@@ -501,10 +477,10 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::GetGlobal(1),
elements::Opcode::End
elements::Instruction::GetGlobal(1),
elements::Instruction::End
]
))
.build()
@@ -534,10 +510,10 @@ mod tests {
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::Call(1),
elements::Opcode::End
elements::Instruction::Call(1),
elements::Instruction::End
]
))
.build()
@@ -582,15 +558,15 @@ mod tests {
.signature().param().i32().param().i32().build()
.build()
.function()
.signature().param().i32().param().i32().build()
.signature().param().i32().param().i32().param().i32().build()
.build()
.function()
.signature().param().i32().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::CallIndirect(1, 0),
elements::Opcode::End
elements::Instruction::CallIndirect(1, 0),
elements::Instruction::End
]
))
.build()
@@ -610,7 +586,7 @@ mod tests {
let indirect_opcode = &module.code_section().expect("code section to be generated").bodies()[0].code().elements()[0];
match *indirect_opcode {
elements::Opcode::CallIndirect(0, 0) => {},
elements::Instruction::CallIndirect(0, 0) => {},
_ => {
panic!(
"Expected call_indirect to use index 0 after optimization, since previois 0th was eliminated, but got {:?}",
+75 -78
View File
@@ -3,11 +3,11 @@ use std::vec::Vec;
use std::borrow::ToOwned;
use parity_wasm::elements::{
self, Section, DataSection, Opcode, DataSegment, InitExpr, Internal, External,
self, Section, DataSection, Instruction, DataSegment, InitExpr, Internal, External,
ImportCountType,
};
use parity_wasm::builder;
use super::{CREATE_SYMBOL, CALL_SYMBOL, RET_SYMBOL};
use super::TargetRuntime;
use super::gas::update_call_index;
/// Pack error.
@@ -20,9 +20,9 @@ pub enum Error {
NoTypeSection,
NoExportSection,
NoCodeSection,
InvalidCreateSignature,
NoCreateSymbol,
InvalidCreateMember,
InvalidCreateSignature(&'static str),
NoCreateSymbol(&'static str),
InvalidCreateMember(&'static str),
NoImportSection,
}
@@ -33,37 +33,37 @@ impl fmt::Display for Error {
Error::NoTypeSection => write!(f, "No type section in the module"),
Error::NoExportSection => write!(f, "No export section in the module"),
Error::NoCodeSection => write!(f, "No code section inthe module"),
Error::InvalidCreateSignature => write!(f, "Exported symbol `deploy` has invalid signature, should be () -> ()"),
Error::InvalidCreateMember => write!(f, "Exported symbol `deploy` should be a function"),
Error::NoCreateSymbol => write!(f, "No exported `deploy` symbol"),
Error::InvalidCreateSignature(sym) => write!(f, "Exported symbol `{}` has invalid signature, should be () -> ()", sym),
Error::InvalidCreateMember(sym) => write!(f, "Exported symbol `{}` should be a function", sym),
Error::NoCreateSymbol(sym) => write!(f, "No exported `{}` symbol", sym),
Error::NoImportSection => write!(f, "No import section in the module"),
}
}
}
/// 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
/// `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
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
let mut create_func_id = {
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() {
&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
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)?
.entries().get(function_index - ctor_import_functions).ok_or(Error::MalformedModule)?
.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
if !func.params().is_empty() {
return Err(Error::InvalidCreateSignature);
return Err(Error::InvalidCreateSignature(target.create_symbol));
}
if func.return_type().is_some() {
return Err(Error::InvalidCreateSignature);
return Err(Error::InvalidCreateSignature(target.create_symbol));
}
function_internal_index
@@ -87,7 +87,7 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
let mut found = false;
for entry in ctor_module.import_section().ok_or(Error::NoImportSection)?.entries().iter() {
if let External::Function(_) = *entry.external() {
if entry.field() == RET_SYMBOL { found = true; break; }
if entry.field() == target.return_symbol { found = true; break; }
else { id += 1; }
}
}
@@ -102,7 +102,7 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
mbuilder.push_import(
builder::import()
.module("env")
.field("ret")
.field(&target.return_symbol)
.external().func(import_sig)
.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) => {
for ref mut export in export_section.entries_mut() {
match export.internal_mut() {
&mut elements::Internal::Function(ref mut func_index) => {
if *func_index >= ret_func { *func_index += 1}
},
_ => {}
if let &mut elements::Internal::Function(ref mut func_index) = export.internal_mut() {
if *func_index >= ret_func { *func_index += 1}
}
}
},
@@ -163,57 +160,51 @@ pub fn pack_instance(raw_module: Vec<u8>, mut ctor_module: elements::Module) ->
let mut code_data_address = 0i32;
for section in ctor_module.sections_mut() {
match section {
&mut Section::Data(ref mut data_section) => {
let (index, offset) = if let Some(ref entry) = data_section.entries().iter().last() {
if let Opcode::I32Const(offst) = entry.offset().code()[0] {
let len = entry.value().len() as i32;
let offst = offst as i32;
(entry.index(), offst + (len + 4) - len % 4)
} else {
(0, 0)
}
if let &mut Section::Data(ref mut data_section) = section {
let (index, offset) = if let Some(ref entry) = data_section.entries().iter().last() {
if let Instruction::I32Const(offst) = entry.offset().code()[0] {
let len = entry.value().len() as i32;
let offst = offst as i32;
(entry.index(), offst + (len + 4) - len % 4)
} else {
(0, 0)
};
let code_data = DataSegment::new(
index,
InitExpr::new(vec![Opcode::I32Const(offset), Opcode::End]),
raw_module.clone()
);
data_section.entries_mut().push(code_data);
code_data_address = offset;
},
_ => {;}
}
} else {
(0, 0)
};
let code_data = DataSegment::new(
index,
InitExpr::new(vec![Instruction::I32Const(offset), Instruction::End]),
raw_module.clone()
);
data_section.entries_mut().push(code_data);
code_data_address = offset;
}
}
let mut new_module = builder::from_module(ctor_module)
.function()
.signature().build()
.body().with_opcodes(elements::Opcodes::new(
.body().with_instructions(elements::Instructions::new(
vec![
Opcode::Call((create_func_id + ctor_import_functions) as u32),
Opcode::I32Const(code_data_address),
Opcode::I32Const(raw_module.len() as i32),
Opcode::Call(ret_function_id as u32),
Opcode::End,
Instruction::Call((create_func_id + ctor_import_functions) as u32),
Instruction::I32Const(code_data_address),
Instruction::I32Const(raw_module.len() as i32),
Instruction::Call(ret_function_id as u32),
Instruction::End,
])).build()
.build()
.build();
for section in new_module.sections_mut() {
match section {
&mut Section::Export(ref mut export_section) => {
for entry in export_section.entries_mut().iter_mut() {
if CREATE_SYMBOL == entry.field() {
// change _create export name into default _call
*entry.field_mut() = CALL_SYMBOL.to_owned();
*entry.internal_mut() = elements::Internal::Function(last_function_index as u32);
}
if let &mut Section::Export(ref mut export_section) = section {
for entry in export_section.entries_mut().iter_mut() {
if target.create_symbol == entry.field() {
// change `create_symbol` export name into default `call_symbol`.
*entry.field_mut() = target.call_symbol.to_owned();
*entry.internal_mut() = elements::Internal::Function(last_function_index as u32);
}
},
_ => { },
}
}
};
@@ -228,13 +219,13 @@ mod test {
use super::*;
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();
optimize(&mut module, vec![CALL_SYMBOL]).expect("Optimizer to finish without errors");
optimize(&mut ctor_module, vec![CREATE_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![target_runtime.create_symbol]).expect("Optimizer to finish without errors");
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_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]
fn no_data_section() {
let target_runtime = TargetRuntime::pwasm();
test_packer(builder::module()
.import()
.module("env")
@@ -258,9 +251,9 @@ mod test {
.function()
.signature().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::End
elements::Instruction::End
]
))
.build()
@@ -268,27 +261,30 @@ mod test {
.function()
.signature().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::End
elements::Instruction::End
]
))
.build()
.build()
.export()
.field(CALL_SYMBOL)
.field(target_runtime.call_symbol)
.internal().func(1)
.build()
.export()
.field(CREATE_SYMBOL)
.field(target_runtime.create_symbol)
.internal().func(2)
.build()
.build()
.build(),
&target_runtime,
);
}
#[test]
fn with_data_section() {
let target_runtime = TargetRuntime::pwasm();
test_packer(builder::module()
.import()
.module("env")
@@ -296,7 +292,7 @@ mod test {
.external().memory(1 as u32, Some(1 as u32))
.build()
.data()
.offset(elements::Opcode::I32Const(16)).value(vec![0u8])
.offset(elements::Instruction::I32Const(16)).value(vec![0u8])
.build()
.function()
.signature()
@@ -307,9 +303,9 @@ mod test {
.function()
.signature().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::End
elements::Instruction::End
]
))
.build()
@@ -317,22 +313,23 @@ mod test {
.function()
.signature().build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
vec![
elements::Opcode::End
elements::Instruction::End
]
))
.build()
.build()
.export()
.field(CALL_SYMBOL)
.field(target_runtime.call_symbol)
.internal().func(1)
.build()
.export()
.field(CREATE_SYMBOL)
.field(target_runtime.create_symbol)
.internal().func(2)
.build()
.build()
.build(),
&target_runtime,
);
}
}
+6 -6
View File
@@ -62,7 +62,7 @@ impl ::std::str::FromStr for InstructionType {
"reinterpret" => Ok(InstructionType::Reinterpretation),
"unreachable" => Ok(InstructionType::Unreachable),
"nop" => Ok(InstructionType::Nop),
"currrent_mem" => Ok(InstructionType::CurrentMemory),
"current_mem" => Ok(InstructionType::CurrentMemory),
"grow_mem" => Ok(InstructionType::GrowMemory),
_ => Err(UnknownInstruction),
}
@@ -70,10 +70,10 @@ impl ::std::str::FromStr for InstructionType {
}
impl InstructionType {
pub fn op(opcode: &elements::Opcode) -> Self {
use parity_wasm::elements::Opcode::*;
pub fn op(instruction: &elements::Instruction) -> Self {
use parity_wasm::elements::Instruction::*;
match *opcode {
match *instruction {
Unreachable => InstructionType::Unreachable,
Nop => InstructionType::Nop,
Block(_) => InstructionType::ControlFlow,
@@ -288,8 +288,8 @@ impl Set {
Set { regular: regular, entries: entries, grow: 0 }
}
pub fn process(&self, opcode: &elements::Opcode) -> Result<u32, ()> {
match self.entries.get(&InstructionType::op(opcode)).map(|x| *x) {
pub fn process(&self, instruction: &elements::Instruction) -> Result<u32, ()> {
match self.entries.get(&InstructionType::op(instruction)).map(|x| *x) {
None | Some(Metering::Regular) => Ok(self.regular),
Some(Metering::Forbidden) => Err(()),
Some(Metering::Fixed(val)) => Ok(val),
+8 -6
View File
@@ -1,8 +1,8 @@
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 };
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 globals_count: u32 = match module.global_section() {
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;
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_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)))
.build()
}
@@ -31,9 +31,11 @@ mod tests {
#[test]
fn it_injects() {
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();
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");
assert_eq!(3, global_section.entries().len());
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.
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
.function_section()
@@ -165,7 +165,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
.bodies()
.get(func_idx as usize)
.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 max_height: u32 = 0;
@@ -186,7 +186,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
});
loop {
if pc >= opcodes.elements().len() {
if pc >= instructions.elements().len() {
break;
}
@@ -197,7 +197,7 @@ pub(crate) fn compute(func_idx: u32, module: &elements::Module) -> Result<u32, E
max_height = stack.height();
}
let opcode = &opcodes.elements()[pc];
let opcode = &instructions.elements()[pc];
trace!(target: "max_height", "{:?}", opcode);
match *opcode {
+19 -24
View File
@@ -25,11 +25,12 @@
//! 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.
//! - Start function is executed externally (similar to exported functions).
//! - 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
//! 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
//!
@@ -57,7 +58,7 @@ use parity_wasm::builder;
/// Macro to generate preamble and postamble.
macro_rules! instrument_call {
($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)
GetGlobal($stack_height_global_idx),
@@ -160,20 +161,17 @@ fn generate_stack_height_global(ctx: &mut Context, module: &mut elements::Module
.value_type()
.i32()
.mutable()
.init_expr(elements::Opcode::I32Const(0))
.init_expr(elements::Instruction::I32Const(0))
.build();
// Try to find an existing global section.
for section in module.sections_mut() {
match *section {
elements::Section::Global(ref mut gs) => {
gs.entries_mut().push(global_entry);
if let elements::Section::Global(ref mut gs) = *section {
gs.entries_mut().push(global_entry);
let stack_height_global_idx = (gs.entries().len() as u32) - 1;
ctx.stack_height_global_idx = Some(stack_height_global_idx);
return;
}
_ => {}
let stack_height_global_idx = (gs.entries().len() as u32) - 1;
ctx.stack_height_global_idx = Some(stack_height_global_idx);
return;
}
}
@@ -233,14 +231,11 @@ 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> {
for section in module.sections_mut() {
match *section {
elements::Section::Code(ref mut code_section) => {
for func_body in code_section.bodies_mut() {
let mut opcodes = func_body.code_mut();
instrument_function(ctx, opcodes)?;
}
if let elements::Section::Code(ref mut code_section) = *section {
for func_body in code_section.bodies_mut() {
let mut opcodes = func_body.code_mut();
instrument_function(ctx, opcodes)?;
}
_ => {}
}
}
Ok(())
@@ -274,13 +269,13 @@ fn instrument_functions(ctx: &mut Context, module: &mut elements::Module) -> Res
/// ```
fn instrument_function(
ctx: &mut Context,
opcodes: &mut elements::Opcodes,
instructions: &mut elements::Instructions,
) -> Result<(), Error> {
use parity_wasm::elements::Opcode::*;
use parity_wasm::elements::Instruction::*;
let mut cursor = 0;
loop {
if cursor >= opcodes.elements().len() {
if cursor >= instructions.elements().len() {
break;
}
@@ -293,8 +288,8 @@ fn instrument_function(
}
let action: Action = {
let opcode = &opcodes.elements()[cursor];
match *opcode {
let instruction = &instructions.elements()[cursor];
match *instruction {
Call(ref callee_idx) => {
let callee_stack_cost = ctx
.stack_cost(*callee_idx)
@@ -336,7 +331,7 @@ fn instrument_function(
//
// To splice actually take a place, we need to consume iterator
// splice returns. So we just `count()` it.
let _ = opcodes
let _ = instructions
.elements_mut()
.splice(cursor..(cursor + 1), new_seq.iter().cloned())
.count();
+12 -8
View File
@@ -35,6 +35,8 @@ pub(crate) fn generate_thunks(
.elements_section()
.map(|es| es.entries())
.unwrap_or(&[]);
let start_func_idx = module
.start_section();
let exported_func_indicies = exports.iter().filter_map(|entry| match *entry.internal() {
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.
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(|| {
Error(format!("function with idx {} isn't found", func_idx))
})?;
@@ -93,17 +95,17 @@ pub(crate) fn generate_thunks(
// - argument pushing
// - instrumented call
// - 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() +
instrumented_call.len() +
1
);
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.push(elements::Opcode::End);
thunk_body.push(elements::Instruction::End);
// 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())
.build()
.body()
.with_opcodes(elements::Opcodes::new(
.with_instructions(elements::Instructions::new(
thunk_body
))
.build()
@@ -142,9 +144,8 @@ pub(crate) fn generate_thunks(
match *section {
elements::Section::Export(ref mut export_section) => {
for entry in export_section.entries_mut() {
match *entry.internal_mut() {
Internal::Function(ref mut function_idx) => fixup(function_idx),
_ => {}
if let Internal::Function(ref mut function_idx) = *entry.internal_mut() {
fixup(function_idx)
}
}
}
@@ -155,6 +156,9 @@ pub(crate) fn generate_thunks(
}
}
}
elements::Section::Start(ref mut start_idx) => {
fixup(start_idx)
}
_ => {}
}
}
+20 -29
View File
@@ -19,14 +19,11 @@ pub fn resolve_function(module: &elements::Module, index: u32) -> Symbol {
let mut functions = 0;
if let Some(import_section) = module.import_section() {
for (item_index, item) in import_section.entries().iter().enumerate() {
match item.external() {
&elements::External::Function(_) => {
if functions == index {
return Symbol::Import(item_index as usize);
}
functions += 1;
},
_ => {}
if let &elements::External::Function(_) = item.external() {
if functions == index {
return Symbol::Import(item_index as usize);
}
functions += 1;
}
}
}
@@ -38,14 +35,11 @@ pub fn resolve_global(module: &elements::Module, index: u32) -> Symbol {
let mut globals = 0;
if let Some(import_section) = module.import_section() {
for (item_index, item) in import_section.entries().iter().enumerate() {
match item.external() {
&elements::External::Global(_) => {
if globals == index {
return Symbol::Import(item_index as usize);
}
globals += 1;
},
_ => {}
if let &elements::External::Global(_) = item.external() {
if globals == index {
return Symbol::Import(item_index as usize);
}
globals += 1;
}
}
}
@@ -53,11 +47,11 @@ pub fn resolve_global(module: &elements::Module, index: u32) -> Symbol {
Symbol::Global(index as usize - globals as usize)
}
pub fn push_code_symbols(module: &elements::Module, opcodes: &[elements::Opcode], dest: &mut Vec<Symbol>) {
use parity_wasm::elements::Opcode::*;
pub fn push_code_symbols(module: &elements::Module, instructions: &[elements::Instruction], dest: &mut Vec<Symbol>) {
use parity_wasm::elements::Instruction::*;
for opcode in opcodes {
match opcode {
for instruction in instructions {
match instruction {
&Call(idx) => {
dest.push(resolve_function(module, idx));
},
@@ -109,15 +103,12 @@ pub fn expand_symbols(module: &elements::Module, set: &mut Set<Symbol>) {
},
Import(idx) => {
let entry = &module.import_section().expect("Import section to exist").entries()[idx];
match entry.external() {
&elements::External::Function(type_idx) => {
let type_symbol = Symbol::Type(type_idx as usize);
if !stop.contains(&type_symbol) {
fringe.push(type_symbol);
}
set.insert(type_symbol);
},
_ => {}
if let &elements::External::Function(type_idx) = entry.external() {
let type_symbol = Symbol::Type(type_idx as usize);
if !stop.contains(&type_symbol) {
fringe.push(type_symbol);
}
set.insert(type_symbol);
}
},
Function(idx) => {
+42 -15
View File
@@ -74,20 +74,47 @@ fn run_diff_test<F: FnOnce(&[u8]) -> Vec<u8>>(test_dir: &str, name: &str, test:
}
}
macro_rules! def_stack_height_test {
( $name:ident ) => {
#[test]
fn $name() {
run_diff_test("stack-height", concat!(stringify!($name), ".wat"), |input| {
let module = elements::deserialize_buffer(input).expect("Failed to deserialize");
let instrumented = utils::stack_height::inject_limiter(module, 1024).expect("Failed to instrument with stack counter");
elements::serialize(instrumented).expect("Failed to serialize")
});
}
};
mod stack_height {
use super::*;
macro_rules! def_stack_height_test {
( $name:ident ) => {
#[test]
fn $name() {
run_diff_test("stack-height", concat!(stringify!($name), ".wat"), |input| {
let module = elements::deserialize_buffer(input).expect("Failed to deserialize");
let instrumented = utils::stack_height::inject_limiter(module, 1024).expect("Failed to instrument with stack counter");
elements::serialize(instrumented).expect("Failed to serialize")
});
}
};
}
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);
def_stack_height_test!(table);
def_stack_height_test!(global);
def_stack_height_test!(imports);
mod gas {
use super::*;
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 (;1;) (func (param i32 i32) (result i32)))
(type (;2;) (func (param i32)))
(type (;3;) (func (param i32 i32) (result i32)))
(import "env" "foo" (func (;0;) (type 0)))
(func (;1;) (type 1) (param i32 i32) (result i32)
get_local 0
@@ -33,7 +32,7 @@
i32.sub
set_global 1
drop)
(func (;3;) (type 3) (param i32 i32) (result i32)
(func (;3;) (type 1) (param i32 i32) (result i32)
get_local 0
get_local 1
get_global 1
+1 -2
View File
@@ -1,7 +1,6 @@
(module
(type (;0;) (func))
(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" "boo" (func (;1;) (type 0)))
(func (;2;) (type 1) (param i32 i32) (result i32)
@@ -10,7 +9,7 @@
get_local 0
get_local 1
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 1
get_global 0
+1 -2
View File
@@ -1,10 +1,9 @@
(module
(type (;0;) (func))
(type (;1;) (func))
(func (;0;) (type 0)
i32.const 123
drop)
(func (;1;) (type 1)
(func (;1;) (type 0)
get_global 0
i32.const 1
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 (;1;) (func (param 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)))
(func (;1;) (type 1) (param i32)
get_local 0
@@ -29,7 +26,7 @@
get_local 0
get_local 1
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 1
get_global 0
@@ -47,7 +44,7 @@
i32.const 2
i32.sub
set_global 0)
(func (;4;) (type 4) (param i32)
(func (;4;) (type 1) (param i32)
get_local 0
get_global 0
i32.const 2
@@ -64,7 +61,7 @@
i32.const 2
i32.sub
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 1
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")
)
)