mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-04-22 19:37:55 +00:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b58e01ec67 | |||
| 8db40174ae | |||
| 471a9b3fcc | |||
| 3db0d60e70 | |||
| fe25beca2b | |||
| 24b97b517a | |||
| 836ec93008 | |||
| 5238b41af2 | |||
| 19ce379f64 | |||
| a9f5058b4f | |||
| 4b8b07a0b5 | |||
| 67d67f3fba | |||
| 3a7f8836dd | |||
| abb5ae6f22 | |||
| 9d0ad5b309 | |||
| 6c510a7f35 | |||
| e491789127 | |||
| 3e7946ab1c | |||
| 41839664bb | |||
| 735110e8d5 | |||
| 0fe96ee497 | |||
| 0837464ec4 | |||
| 7366384861 | |||
| db80363d56 | |||
| bbb6c6078a | |||
| f7e71718a4 | |||
| af2d61b9f8 | |||
| d6f82000ee | |||
| f4b75bd840 | |||
| 187844f79d | |||
| a4ff19d358 | |||
| e31f1040e1 | |||
| e6e340fa0a | |||
| de23bfac0a | |||
| 261c823b63 | |||
| ce865c1e8a | |||
| 2d60c0bb0e | |||
| 5609a08e99 | |||
| edabee0649 | |||
| 367514ae07 | |||
| 816f14eac7 | |||
| f9540c5423 | |||
| 04ac17c3d5 | |||
| f7e6631c83 | |||
| 20ff66c649 | |||
| 5ef171209b | |||
| 947f0b8bbb | |||
| 7f8811cb2c | |||
| 4112b4b961 |
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
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 }
|
||||
|
||||
|
||||
@@ -2,31 +2,31 @@
|
||||
|
||||
[](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
@@ -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"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# pwasm-utils-cli
|
||||
|
||||
Collection of WASM utilities used in Parity and WASM contract devepment
|
||||
|
||||
## Install
|
||||
|
||||
Easiest way to use is to install via `cargo install`:
|
||||
|
||||
```
|
||||
cargo install pwasm-utils-cli
|
||||
```
|
||||
+55
-82
@@ -9,13 +9,11 @@ extern crate pwasm_utils_cli as logger;
|
||||
mod source;
|
||||
|
||||
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
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate pwasm_utils as utils;
|
||||
extern crate pwasm_utils_cli as logger;
|
||||
extern crate clap;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use parity_wasm::elements;
|
||||
|
||||
fn fail(msg: &str) -> ! {
|
||||
eprintln!("{}", msg);
|
||||
std::process::exit(1)
|
||||
}
|
||||
|
||||
const ALLOWED_IMPORTS: &'static [&'static str] = &[
|
||||
"ret",
|
||||
"storage_read",
|
||||
"storage_write",
|
||||
"balance",
|
||||
"sender",
|
||||
"origin",
|
||||
"fetch_input",
|
||||
"input_length",
|
||||
"ccall",
|
||||
"dcall",
|
||||
"scall",
|
||||
"create",
|
||||
"balance",
|
||||
"blockhash",
|
||||
"blocknumber",
|
||||
"coinbase",
|
||||
"timestamp",
|
||||
"difficulty",
|
||||
"gaslimit",
|
||||
"address",
|
||||
"value",
|
||||
"suicide",
|
||||
"panic",
|
||||
"elog",
|
||||
"abort"
|
||||
];
|
||||
|
||||
fn main() {
|
||||
logger::init_log();
|
||||
|
||||
let matches = App::new("wasm-check")
|
||||
.arg(Arg::with_name("input")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("Input WASM file"))
|
||||
.get_matches();
|
||||
|
||||
let input = matches.value_of("input").expect("is required; qed");
|
||||
|
||||
let module = parity_wasm::deserialize_file(&input).expect("Input module deserialization failed");
|
||||
|
||||
for section in module.sections() {
|
||||
match *section {
|
||||
elements::Section::Import(ref import_section) => {
|
||||
let mut has_imported_memory_properly_named = false;
|
||||
for entry in import_section.entries() {
|
||||
if entry.module() != "env" {
|
||||
fail("All imports should be from env");
|
||||
}
|
||||
match *entry.external() {
|
||||
elements::External::Function(_) => {
|
||||
if !ALLOWED_IMPORTS.contains(&entry.field()) {
|
||||
fail(&format!("'{}' is not supported by the runtime", entry.field()));
|
||||
}
|
||||
},
|
||||
elements::External::Memory(m) => {
|
||||
if entry.field() == "memory" {
|
||||
has_imported_memory_properly_named = true;
|
||||
}
|
||||
|
||||
let max = if let Some(max) = m.limits().maximum() {
|
||||
max
|
||||
} else {
|
||||
fail("There is a limit on memory in Parity runtime, and this program does not limit memory");
|
||||
};
|
||||
|
||||
if max > 16 {
|
||||
fail(&format!(
|
||||
"Parity runtime has 1Mb limit (16 pages) on max contract memory, this program speicifies {}",
|
||||
max
|
||||
));
|
||||
}
|
||||
},
|
||||
elements::External::Global(_) => {
|
||||
fail("Parity runtime does not provide any globals")
|
||||
},
|
||||
_ => { continue; }
|
||||
}
|
||||
}
|
||||
|
||||
if !has_imported_memory_properly_named {
|
||||
fail("No imported memory from env::memory in the contract");
|
||||
}
|
||||
}
|
||||
_ => { continue; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate pwasm_utils as utils;
|
||||
extern crate pwasm_utils_cli as logger;
|
||||
extern crate clap;
|
||||
|
||||
use clap::{App, Arg};
|
||||
|
||||
fn main() {
|
||||
logger::init_log();
|
||||
|
||||
let target_runtime = utils::TargetRuntime::pwasm();
|
||||
|
||||
let matches = App::new("wasm-pack")
|
||||
.arg(Arg::with_name("input")
|
||||
.index(1)
|
||||
.required(true)
|
||||
.help("Input WASM file"))
|
||||
.arg(Arg::with_name("output")
|
||||
.index(2)
|
||||
.required(true)
|
||||
.help("Output WASM file"))
|
||||
.get_matches();
|
||||
|
||||
let input = matches.value_of("input").expect("is required; qed");
|
||||
let output = matches.value_of("output").expect("is required; qed");
|
||||
|
||||
let module = parity_wasm::deserialize_file(&input).expect("Input module deserialization failed");
|
||||
let ctor_module = module.clone();
|
||||
let raw_module = parity_wasm::serialize(module).expect("Serialization failed");
|
||||
|
||||
// Invoke packer
|
||||
let mut result_module = utils::pack_instance(raw_module, ctor_module, &utils::TargetRuntime::pwasm()).expect("Packing failed");
|
||||
// Optimize constructor, since it does not need everything
|
||||
utils::optimize(&mut result_module, vec![target_runtime.call_symbol]).expect("Optimization failed");
|
||||
|
||||
parity_wasm::serialize_to_file(&output, result_module).expect("Serialization failed");
|
||||
}
|
||||
+7
-5
@@ -8,7 +8,9 @@ use clap::{App, Arg};
|
||||
fn main() {
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(type (;1;) (func (param i32)))
|
||||
(import "env" "gas" (func (;0;) (type 1)))
|
||||
(func (;1;) (type 0)
|
||||
i32.const 3
|
||||
call 0
|
||||
i32.const 1
|
||||
if ;; label = @1
|
||||
i32.const 2
|
||||
call 0
|
||||
loop ;; label = @2
|
||||
i32.const 3
|
||||
call 0
|
||||
i32.const 123
|
||||
drop
|
||||
end
|
||||
end)
|
||||
(func (;2;) (type 0)
|
||||
i32.const 2
|
||||
call 0
|
||||
block ;; label = @1
|
||||
i32.const 1
|
||||
call 0
|
||||
end)
|
||||
(export "simple" (func 1)))
|
||||
@@ -0,0 +1,20 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32 i32)))
|
||||
(type (;1;) (func))
|
||||
(type (;2;) (func (param i32)))
|
||||
(import "env" "ext_return" (func (;0;) (type 0)))
|
||||
(import "env" "memory" (memory (;0;) 1 1))
|
||||
(import "env" "gas" (func (;1;) (type 2)))
|
||||
(func (;2;) (type 1)
|
||||
i32.const 5
|
||||
call 1
|
||||
i32.const 8
|
||||
i32.const 4
|
||||
call 0
|
||||
unreachable)
|
||||
(func (;3;) (type 1)
|
||||
i32.const 1
|
||||
call 1)
|
||||
(export "call" (func 3))
|
||||
(start 2)
|
||||
(data (i32.const 8) "\01\02\03\04"))
|
||||
@@ -2,7 +2,6 @@
|
||||
(type (;0;) (func))
|
||||
(type (;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,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,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
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
(module
|
||||
(type (;0;) (func (param i32 i32)))
|
||||
(type (;1;) (func))
|
||||
(import "env" "ext_return" (func (;0;) (type 0)))
|
||||
(import "env" "memory" (memory (;0;) 1 1))
|
||||
(func (;1;) (type 1)
|
||||
(local i32))
|
||||
(func (;2;) (type 1))
|
||||
(func (;3;) (type 1)
|
||||
get_global 0
|
||||
i32.const 1
|
||||
i32.add
|
||||
set_global 0
|
||||
get_global 0
|
||||
i32.const 1024
|
||||
i32.gt_u
|
||||
if ;; label = @1
|
||||
unreachable
|
||||
end
|
||||
call 1
|
||||
get_global 0
|
||||
i32.const 1
|
||||
i32.sub
|
||||
set_global 0)
|
||||
(func (;4;) (type 1)
|
||||
get_global 0
|
||||
i32.const 1
|
||||
i32.add
|
||||
set_global 0
|
||||
get_global 0
|
||||
i32.const 1024
|
||||
i32.gt_u
|
||||
if ;; label = @1
|
||||
unreachable
|
||||
end
|
||||
call 1
|
||||
get_global 0
|
||||
i32.const 1
|
||||
i32.sub
|
||||
set_global 0)
|
||||
(global (;0;) (mut i32) (i32.const 0))
|
||||
(export "exported_start" (func 4))
|
||||
(export "call" (func 2))
|
||||
(start 4))
|
||||
@@ -2,9 +2,6 @@
|
||||
(type (;0;) (func))
|
||||
(type (;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
|
||||
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
(module
|
||||
(func (export "simple")
|
||||
(if (i32.const 1)
|
||||
(loop
|
||||
i32.const 123
|
||||
drop
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(func
|
||||
block
|
||||
end
|
||||
)
|
||||
)
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
(module
|
||||
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(start $start)
|
||||
(func $start
|
||||
(call $ext_return
|
||||
(i32.const 8)
|
||||
(i32.const 4)
|
||||
)
|
||||
(unreachable)
|
||||
)
|
||||
|
||||
(func (export "call")
|
||||
)
|
||||
|
||||
(data (i32.const 8) "\01\02\03\04")
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
(module
|
||||
(import "env" "ext_return" (func $ext_return (param i32 i32)))
|
||||
(import "env" "memory" (memory 1 1))
|
||||
|
||||
(start $start)
|
||||
(func $start (export "exported_start")
|
||||
(local i32)
|
||||
)
|
||||
(func (export "call")
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user