diff --git a/Cargo.lock b/Cargo.lock
index 6038064ab0b44..01b170f8cf6ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6176,6 +6176,7 @@ dependencies = [
"derive_more",
"log",
"parity-scale-codec",
+ "parity-wasm 0.41.0",
"sp-allocator",
"sp-core",
"sp-runtime-interface",
@@ -6190,7 +6191,6 @@ version = "0.8.0-alpha.5"
dependencies = [
"log",
"parity-scale-codec",
- "parity-wasm 0.41.0",
"sc-executor-common",
"sp-allocator",
"sp-core",
@@ -6204,6 +6204,8 @@ name = "sc-executor-wasmtime"
version = "0.8.0-alpha.5"
dependencies = [
"assert_matches",
+ "cranelift-codegen",
+ "cranelift-wasm",
"log",
"parity-scale-codec",
"parity-wasm 0.41.0",
@@ -6214,6 +6216,8 @@ dependencies = [
"sp-runtime-interface",
"sp-wasm-interface",
"substrate-wasmtime",
+ "substrate-wasmtime-runtime",
+ "wasmtime-environ",
]
[[package]]
diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml
index 1f52b959d00b1..f9ce7d4e399c5 100644
--- a/client/executor/common/Cargo.toml
+++ b/client/executor/common/Cargo.toml
@@ -12,6 +12,7 @@ documentation = "https://docs.rs/sc-executor-common/"
[dependencies]
log = "0.4.8"
derive_more = "0.99.2"
+parity-wasm = "0.41.0"
codec = { package = "parity-scale-codec", version = "1.3.0" }
wasmi = "0.6.2"
sp-core = { version = "2.0.0-alpha.5", path = "../../../primitives/core" }
diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs
index cc515dcf9dab9..7f3864e6152fb 100644
--- a/client/executor/common/src/lib.rs
+++ b/client/executor/common/src/lib.rs
@@ -18,6 +18,7 @@
#![warn(missing_docs)]
-pub mod sandbox;
pub mod error;
+pub mod sandbox;
+pub mod util;
pub mod wasm_runtime;
diff --git a/client/executor/common/src/util.rs b/client/executor/common/src/util.rs
new file mode 100644
index 0000000000000..149db13bc0768
--- /dev/null
+++ b/client/executor/common/src/util.rs
@@ -0,0 +1,138 @@
+// Copyright 2020 Parity Technologies (UK) Ltd.
+// This file is part of Substrate.
+
+// Substrate is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Substrate is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Substrate. If not, see .
+
+//! A set of utilities for resetting a wasm instance to its initial state.
+
+use crate::error::{self, Error};
+use std::mem;
+use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule};
+
+/// A bunch of information collected from a WebAssembly module.
+pub struct WasmModuleInfo {
+ raw_module: RawModule,
+}
+
+impl WasmModuleInfo {
+ /// Create `WasmModuleInfo` from the given wasm code.
+ ///
+ /// Returns `None` if the wasm code cannot be deserialized.
+ pub fn new(wasm_code: &[u8]) -> Option {
+ let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?;
+ Some(Self { raw_module })
+ }
+
+ /// Extract the data segments from the given wasm code.
+ ///
+ /// Returns `Err` if the given wasm code cannot be deserialized.
+ fn data_segments(&self) -> Vec {
+ self.raw_module
+ .data_section()
+ .map(|ds| ds.entries())
+ .unwrap_or(&[])
+ .to_vec()
+ }
+
+ /// The number of globals defined in locally in this module.
+ pub fn declared_globals_count(&self) -> u32 {
+ self.raw_module
+ .global_section()
+ .map(|gs| gs.entries().len() as u32)
+ .unwrap_or(0)
+ }
+
+ /// The number of imports of globals.
+ pub fn imported_globals_count(&self) -> u32 {
+ self.raw_module
+ .import_section()
+ .map(|is| is.globals() as u32)
+ .unwrap_or(0)
+ }
+}
+
+/// This is a snapshot of data segments specialzied for a particular instantiation.
+///
+/// Note that this assumes that no mutable globals are used.
+#[derive(Clone)]
+pub struct DataSegmentsSnapshot {
+ /// The list of data segments represented by (offset, contents).
+ data_segments: Vec<(u32, Vec)>,
+}
+
+impl DataSegmentsSnapshot {
+ /// Create a snapshot from the data segments from the module.
+ pub fn take(module: &WasmModuleInfo) -> error::Result {
+ let data_segments = module
+ .data_segments()
+ .into_iter()
+ .map(|mut segment| {
+ // Just replace contents of the segment since the segments will be discarded later
+ // anyway.
+ let contents = mem::replace(segment.value_mut(), vec![]);
+
+ let init_expr = match segment.offset() {
+ Some(offset) => offset.code(),
+ // Return if the segment is passive
+ None => return Err(Error::from("Shared memory is not supported".to_string())),
+ };
+
+ // [op, End]
+ if init_expr.len() != 2 {
+ return Err(Error::from(
+ "initializer expression can have only up to 2 expressions in wasm 1.0"
+ .to_string(),
+ ));
+ }
+ let offset = match &init_expr[0] {
+ Instruction::I32Const(v) => *v as u32,
+ Instruction::GetGlobal(_) => {
+ // In a valid wasm file, initializer expressions can only refer imported
+ // globals.
+ //
+ // At the moment of writing the Substrate Runtime Interface does not provide
+ // any globals. There is nothing that prevents us from supporting this
+ // if/when we gain those.
+ return Err(Error::from(
+ "Imported globals are not supported yet".to_string(),
+ ));
+ }
+ insn => {
+ return Err(Error::from(format!(
+ "{:?} is not supported as initializer expression in wasm 1.0",
+ insn
+ )))
+ }
+ };
+
+ Ok((offset, contents))
+ })
+ .collect::>>()?;
+
+ Ok(Self { data_segments })
+ }
+
+ /// Apply the given snapshot to a linear memory.
+ ///
+ /// Linear memory interface is represented by a closure `memory_set`.
+ pub fn apply(
+ &self,
+ mut memory_set: impl FnMut(u32, &[u8]) -> Result<(), E>,
+ ) -> Result<(), E> {
+ for (offset, contents) in &self.data_segments {
+ memory_set(*offset, contents)?;
+ }
+ Ok(())
+ }
+}
diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml
index ea8637b9e2830..fe5bd70d00a75 100644
--- a/client/executor/wasmi/Cargo.toml
+++ b/client/executor/wasmi/Cargo.toml
@@ -12,7 +12,6 @@ documentation = "https://docs.rs/sc-executor-wasmi"
[dependencies]
log = "0.4.8"
wasmi = "0.6.2"
-parity-wasm = "0.41.0"
codec = { package = "parity-scale-codec", version = "1.3.0" }
sc-executor-common = { version = "0.8.0-alpha.5", path = "../common" }
sp-wasm-interface = { version = "2.0.0-alpha.5", path = "../../../primitives/wasm-interface" }
diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs
index 6348c2413357f..e4b4aca40967d 100644
--- a/client/executor/wasmi/src/lib.rs
+++ b/client/executor/wasmi/src/lib.rs
@@ -16,21 +16,25 @@
//! This crate provides an implementation of `WasmModule` that is baked by wasmi.
-use sc_executor_common::{error::{Error, WasmError}, sandbox};
-use std::{str, mem, cell::RefCell, sync::Arc};
+use std::{str, cell::RefCell, sync::Arc};
use wasmi::{
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef,
- memory_units::Pages, RuntimeValue::{I32, I64, self},
+ memory_units::Pages,
+ RuntimeValue::{I32, I64, self},
};
use codec::{Encode, Decode};
use sp_core::sandbox as sandbox_primitives;
use log::{error, trace, debug};
-use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule};
use sp_wasm_interface::{
FunctionContext, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, Function,
};
use sp_runtime_interface::unpack_ptr_and_len;
use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance};
+use sc_executor_common::{
+ error::{Error, WasmError},
+ sandbox,
+};
+use sc_executor_common::util::{DataSegmentsSnapshot, WasmModuleInfo};
struct FunctionExecutor<'a> {
sandbox_store: sandbox::Store,
@@ -530,52 +534,14 @@ fn instantiate_module(
///
/// It is used for restoring the state of the module after execution.
#[derive(Clone)]
-struct StateSnapshot {
- /// The offset and the content of the memory segments that should be used to restore the snapshot
- data_segments: Vec<(u32, Vec)>,
+struct GlobalValsSnapshot {
/// The list of all global mutable variables of the module in their sequential order.
global_mut_values: Vec,
}
-impl StateSnapshot {
+impl GlobalValsSnapshot {
// Returns `None` if instance is not valid.
- fn take(
- module_instance: &ModuleRef,
- data_segments: Vec,
- ) -> Option {
- let prepared_segments = data_segments
- .into_iter()
- .map(|mut segment| {
- // Just replace contents of the segment since the segments will be discarded later
- // anyway.
- let contents = mem::replace(segment.value_mut(), vec![]);
-
- let init_expr = match segment.offset() {
- Some(offset) => offset.code(),
- // Return if the segment is passive
- None => return None
- };
-
- // [op, End]
- if init_expr.len() != 2 {
- return None;
- }
- let offset = match init_expr[0] {
- Instruction::I32Const(v) => v as u32,
- Instruction::GetGlobal(idx) => {
- let global_val = module_instance.globals().get(idx as usize)?.get();
- match global_val {
- RuntimeValue::I32(v) => v as u32,
- _ => return None,
- }
- }
- _ => return None,
- };
-
- Some((offset, contents))
- })
- .collect::