diff --git a/.travis.yml b/.travis.yml index e86a341..5bc878e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ script: - cargo build --release --verbose - cargo test --release --verbose - cargo test --release --manifest-path=spec/Cargo.toml +- cargo test --manifest-path=pwasm-emscripten/Cargo.toml after_success: |- [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && diff --git a/examples/interpret.rs b/examples/interpret.rs index f71d5d2..b5cd137 100644 --- a/examples/interpret.rs +++ b/examples/interpret.rs @@ -4,7 +4,7 @@ extern crate parity_wasm; use std::env::args; -use parity_wasm::{interpreter, ModuleInstanceInterface}; +use parity_wasm::ModuleInstanceInterface; fn main() { let args: Vec<_> = args().collect(); diff --git a/pwasm-emscripten/src/lib.rs b/pwasm-emscripten/src/lib.rs index 3bb0b8b..2d542d2 100644 --- a/pwasm-emscripten/src/lib.rs +++ b/pwasm-emscripten/src/lib.rs @@ -251,8 +251,26 @@ impl EnvParams { } } -pub fn program_with_emscripten(params: EnvParams) -> ProgramInstance { +pub fn program_with_emscripten_env(params: EnvParams) -> Result { let program = ProgramInstance::new(); - program.insert_loaded_module("env", env_module(params).unwrap()).unwrap(); - program + program.insert_loaded_module("env", env_module(params)?)?; + Ok(program) +} + +#[cfg(test)] +mod tests { + use super::program_with_emscripten_env; + use parity_wasm::builder::module; + use parity_wasm::elements::{ImportEntry, External, GlobalType, ValueType}; + + #[test] + fn import_env_mutable_global() { + let program = program_with_emscripten_env(Default::default()).unwrap(); + + let module = module() + .with_import(ImportEntry::new("env".into(), "STACKTOP".into(), External::Global(GlobalType::new(ValueType::I32, false)))) + .build(); + + program.add_module("main", module, None).unwrap(); + } } diff --git a/src/interpreter/emscripten.rs b/src/interpreter/emscripten.rs deleted file mode 100644 index 96d26b9..0000000 --- a/src/interpreter/emscripten.rs +++ /dev/null @@ -1,248 +0,0 @@ -//! This module provides some of the simplest exports -//! from the Emscripten runtime, such as `STACKTOP` or `abort`. - -use std::sync::{Arc, Weak}; -use builder::module; -use elements::{ExportEntry, Internal, ValueType}; -use interpreter::Error; -use interpreter::native::{native_module, UserDefinedElements, UserFunctionDescriptor, UserFunctionExecutor}; -use interpreter::module::{CallerContext, ModuleInstance, ModuleInstanceInterface}; -use interpreter::memory::LINEAR_MEMORY_PAGE_SIZE; -use interpreter::value::RuntimeValue; -use interpreter::variable::{VariableInstance, VariableType}; - -/// Memory address, at which stack begins. -const DEFAULT_STACK_TOP: u32 = 256 * 1024; -/// Memory, allocated for stack. -const DEFAULT_TOTAL_STACK: u32 = 5 * 1024 * 1024; -/// Total memory, allocated by default. -const DEFAULT_TOTAL_MEMORY: u32 = 16 * 1024 * 1024; -/// Whether memory can be enlarged, or not. -const DEFAULT_ALLOW_MEMORY_GROWTH: bool = true; -/// Default tableBase variable value. -const DEFAULT_TABLE_BASE: u32 = 0; -/// Default tableBase variable value. -const DEFAULT_MEMORY_BASE: u32 = 0; - -/// Default table size. -const DEFAULT_TABLE_SIZE: u32 = 64; - -/// Index of default memory. -const INDEX_MEMORY: u32 = 0; - -/// Index of default table. -const INDEX_TABLE: u32 = 0; - -/// Emscripten environment parameters. -pub struct EnvParams { - /// Stack size in bytes. - pub total_stack: u32, - /// Total memory size in bytes. - pub total_memory: u32, - /// Allow memory growth. - pub allow_memory_growth: bool, - /// Table size. - pub table_size: u32, - /// Static reserve, if any - pub static_size: Option, -} - -struct EmscriptenFunctionExecutor { - abort_global: Arc, - total_mem_global: Arc, -} - -impl<'a> UserFunctionExecutor for EmscriptenFunctionExecutor { - fn execute( - &mut self, - name: &str, - context: CallerContext, - ) -> Result, Error> { - match name { - "_abort" | "abort" => { - self.abort_global.set(RuntimeValue::I32(1))?; - Err(Error::Trap("abort".into()).into()) - } - "assert" => { - let condition = context.value_stack.pop_as::()?; - if condition == 0 { - self.abort_global.set(RuntimeValue::I32(1))?; - Err(Error::Trap("assertion failed".into())) - } else { - Ok(None) - } - } - "enlargeMemory" => { - // TODO: support memory enlarge - Ok(Some(RuntimeValue::I32(0))) - } - "getTotalMemory" => { - let total_memory = self.total_mem_global.get(); - Ok(Some(total_memory)) - } - _ => Err(Error::Trap("not implemented".into()).into()), - } - } -} - -pub fn env_module(params: EnvParams) -> Result, Error> { - debug_assert!(params.total_stack < params.total_memory); - debug_assert!((params.total_stack % LINEAR_MEMORY_PAGE_SIZE) == 0); - debug_assert!((params.total_memory % LINEAR_MEMORY_PAGE_SIZE) == 0); - - let stack_top = params.static_size.unwrap_or(DEFAULT_STACK_TOP); - - // Build module with defined memory and table, - // instantiate it and wrap into an Arc. - let instance = { - let builder = module() - // memory regions - .memory() - .with_min(params.total_memory / LINEAR_MEMORY_PAGE_SIZE) - .with_max(params.max_memory().map(|m| m / LINEAR_MEMORY_PAGE_SIZE)) - .build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(INDEX_MEMORY))) - // tables - .table() - .with_min(params.table_size) - .build() - .with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE))); - let mut instance = ModuleInstance::new(Weak::default(), "env".into(), builder.build())?; - instance.instantiate(None)?; - Arc::new(instance) - }; - - let abort_global = Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(0) - ).unwrap() - ); - let total_mem_global = Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(params.total_memory as i32), - ).unwrap(), - ); - - let function_executor = EmscriptenFunctionExecutor { - abort_global: Arc::clone(&abort_global), - total_mem_global: Arc::clone(&total_mem_global), - }; - - const SIGNATURES: &'static [UserFunctionDescriptor] = &[ - UserFunctionDescriptor::Static("_abort", &[], None), - UserFunctionDescriptor::Static("abort", &[], None), - UserFunctionDescriptor::Static("assert", &[ValueType::I32], None), - UserFunctionDescriptor::Static("enlargeMemory", &[], Some(ValueType::I32)), - UserFunctionDescriptor::Static("getTotalMemory", &[], Some(ValueType::I32)), - ]; - - let elements = UserDefinedElements { - executor: Some(function_executor), - globals: vec![ - ( - "STACK_BASE".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(stack_top as i32), - ).unwrap(), - ), - ), - ( - "STACKTOP".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(stack_top as i32), - ).unwrap(), - ), - ), - ( - "STACK_MAX".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32((stack_top + params.total_stack) as i32), - ).unwrap(), - ), - ), - ( - "DYNAMIC_BASE".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32((stack_top + params.total_stack) as i32), - ).unwrap(), - ), - ), - ( - "DYNAMICTOP_PTR".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32((stack_top + params.total_stack) as i32), - ).unwrap(), - ), - ), - ( - "EXITSTATUS".into(), - Arc::new( - VariableInstance::new(false, VariableType::I32, RuntimeValue::I32(0)).unwrap(), - ), - ), - ( - "tableBase".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(DEFAULT_TABLE_BASE as i32), - ).unwrap(), - ), - ), - ( - "memoryBase".into(), - Arc::new( - VariableInstance::new( - false, - VariableType::I32, - RuntimeValue::I32(DEFAULT_MEMORY_BASE as i32), - ).unwrap(), - ), - ), - ("TOTAL_MEMORY".into(), total_mem_global), - ("ABORT".into(), abort_global), - ].into_iter() - .collect(), - functions: ::std::borrow::Cow::from(SIGNATURES), - }; - - Ok(native_module(instance, elements)?) -} - -impl Default for EnvParams { - fn default() -> Self { - EnvParams { - total_stack: DEFAULT_TOTAL_STACK, - total_memory: DEFAULT_TOTAL_MEMORY, - allow_memory_growth: DEFAULT_ALLOW_MEMORY_GROWTH, - table_size: DEFAULT_TABLE_SIZE, - static_size: None, - } - } -} - -impl EnvParams { - fn max_memory(&self) -> Option { - if self.allow_memory_growth { None } else { Some(self.total_memory) } - } -} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index c056d7e..5749939 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -116,7 +116,6 @@ impl From for Error where U: UserError + Sized { } } -mod emscripten; mod native; mod imports; mod memory; @@ -140,4 +139,3 @@ pub use self::program::ProgramInstance; pub use self::value::RuntimeValue; pub use self::variable::{VariableInstance, VariableType, ExternalVariableValue}; pub use self::native::{native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; -pub use self::emscripten::EmscriptenParams; diff --git a/src/interpreter/native.rs b/src/interpreter/native.rs index 6cc2323..29fa045 100644 --- a/src/interpreter/native.rs +++ b/src/interpreter/native.rs @@ -1,17 +1,16 @@ -use std::sync::{Arc, Weak}; +use std::sync::Arc; use std::collections::HashMap; use std::borrow::Cow; use parking_lot::RwLock; use elements::{Internal, ValueType}; use interpreter::Error; -use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ExecutionParams, ItemIndex, +use interpreter::module::{ModuleInstanceInterface, ExecutionParams, ItemIndex, CallerContext, ExportEntryType, InternalFunctionReference, InternalFunction, FunctionSignature}; use interpreter::memory::MemoryInstance; use interpreter::table::TableInstance; use interpreter::value::RuntimeValue; use interpreter::variable::{VariableInstance, VariableType}; -use builder::module; /// Min index of native function. pub const NATIVE_INDEX_FUNC_MIN: u32 = 10001; @@ -285,12 +284,6 @@ pub fn native_module<'a, E: UserFunctionExecutor + 'a>(base: Arc(user_elements: UserDefinedElements) -> Result, Error> { - let mut instance = ModuleInstance::new(Weak::default(), "env".into(), module().build())?; - instance.instantiate(None)?; - native_module(Arc::new(instance), user_elements) -} - impl<'a> PartialEq for UserFunctionDescriptor { fn eq(&self, other: &Self) -> bool { self.params() == other.params() diff --git a/src/interpreter/program.rs b/src/interpreter/program.rs index 7615fc8..9efedc7 100644 --- a/src/interpreter/program.rs +++ b/src/interpreter/program.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use parking_lot::RwLock; use elements::Module; use interpreter::Error; -use interpreter::emscripten::{self, env_module}; use interpreter::module::{ModuleInstance, ModuleInstanceInterface}; /// Program instance. Program is a set of instantiated modules. @@ -26,17 +25,6 @@ impl ProgramInstance { } } - /// Create new program instance with added Emscripten's `env` module. - /// - /// You can specify desired environment params. Or you can just pass `Default::default()`. - pub fn with_emscripten_env(params: emscripten::EnvParams) -> Result { - let instance = ProgramInstance { - essence: Arc::new(ProgramInstanceEssence::new()), - }; - // instance.essence.modules.write().insert("env".into(), env_module(params)?); - Ok(instance) - } - /// Instantiate module with validation. pub fn add_module<'a>(&self, name: &str, module: Module, externals: Option<&'a HashMap>>) -> Result, Error> { let mut module_instance = ModuleInstance::new(Arc::downgrade(&self.essence), name.into(), module)?; diff --git a/src/interpreter/tests/basics.rs b/src/interpreter/tests/basics.rs index d95bbf7..3efc20f 100644 --- a/src/interpreter/tests/basics.rs +++ b/src/interpreter/tests/basics.rs @@ -7,12 +7,13 @@ use builder::module; use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, GlobalType, InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType, TableType, MemoryType}; use interpreter::{Error, UserError, ProgramInstance}; -use interpreter::native::{simple_native_module, native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; +use interpreter::native::{native_module, UserDefinedElements, UserFunctionExecutor, UserFunctionDescriptor}; use interpreter::memory::MemoryInstance; use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType, FunctionSignature}; use interpreter::validator::{FunctionValidationContext, Validator}; use interpreter::value::{RuntimeValue, TryInto}; use interpreter::variable::{VariableInstance, ExternalVariableValue, VariableType}; +use super::utils::program_with_default_env; #[test] fn import_function() { @@ -195,14 +196,11 @@ impl<'a> UserFunctionExecutor for &'a mut FunctionExecutor { #[test] fn native_env_function() { - let program = ProgramInstance::new(); - let env_module = module() - .memory() - .with_min(1) - .build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) - .build(); - let env_instance = program.add_module("env", env_module, None).unwrap(); + // create new program + let program = program_with_default_env(); + // => env module is created + let env_instance = program.module("env").unwrap(); + // => linear memory is created let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); // create native env module executor @@ -259,7 +257,7 @@ fn native_env_function() { #[test] fn native_env_function_own_memory() { // create program + env module is auto instantiated + env module memory is instantiated (we do not need this) - let program = ProgramInstance::with_emscripten_env(Default::default()).unwrap(); + let program = program_with_default_env(); struct OwnMemoryReference { pub memory: RefCell>>, @@ -287,9 +285,10 @@ fn native_env_function_own_memory() { } } + let env_instance = program.module("env").unwrap(); let memory_ref = Arc::new(OwnMemoryReference { memory: RefCell::new(None) }); let mut executor = OwnMemoryExecutor { memory_ref: memory_ref.clone() }; - let native_env_instance = simple_native_module(UserDefinedElements { + let native_env_instance = native_module(env_instance, UserDefinedElements { executor: Some(&mut executor), globals: HashMap::new(), functions: ::std::borrow::Cow::from(SIGNATURES), @@ -336,8 +335,9 @@ fn native_env_global() { } let module_constructor = |elements: UserDefinedElements| { - let program = ProgramInstance::new(); - let native_env_instance = simple_native_module(elements).unwrap(); + let program = program_with_default_env(); + let env_instance = program.module("env").unwrap(); + let native_env_instance = native_module(env_instance, elements).unwrap(); let params = ExecutionParams::with_external("env".into(), native_env_instance); let module = module() @@ -384,14 +384,8 @@ fn native_env_global() { #[test] fn native_custom_error() { - let program = ProgramInstance::new(); - let env_module = module() - .memory() - .with_min(1) - .build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) - .build(); - let env_instance = program.add_module("env", env_module, None).unwrap(); + let program = program_with_default_env(); + let env_instance = program.module("env").unwrap(); let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); let mut executor = FunctionExecutor { memory: env_memory.clone(), values: Vec::new() }; @@ -442,29 +436,10 @@ fn native_custom_error() { assert_eq!(user_error2.downcast_ref::().unwrap(), &UserErrorWithCode { error_code: 777 }); } -// TODO: Move into pwasm-emscripten -#[ignore] -#[test] -fn import_env_mutable_global() { - let program = ProgramInstance::with_emscripten_env(Default::default()).unwrap(); - - let module = module() - .with_import(ImportEntry::new("env".into(), "STACKTOP".into(), External::Global(GlobalType::new(ValueType::I32, false)))) - .build(); - - program.add_module("main", module, None).unwrap(); -} - #[test] fn env_native_export_entry_type_check() { - let program = ProgramInstance::new(); - let env_module = module() - .memory() - .with_min(1) - .build() - .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) - .build(); - let env_instance = program.add_module("env", env_module, None).unwrap(); + let program = program_with_default_env(); + let env_instance = program.module("env").unwrap(); let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap(); let mut function_executor = FunctionExecutor { memory: env_memory, diff --git a/src/interpreter/tests/mod.rs b/src/interpreter/tests/mod.rs index 5f018af..8842ad9 100644 --- a/src/interpreter/tests/mod.rs +++ b/src/interpreter/tests/mod.rs @@ -1,3 +1,29 @@ mod basics; mod wabt; mod wasm; + +mod utils { + use elements::{Internal, ExportEntry, InitExpr, Opcode, ValueType, GlobalType, GlobalEntry}; + use interpreter::ProgramInstance; + use builder::module; + + pub fn program_with_default_env() -> ProgramInstance { + let program = ProgramInstance::new(); + let env_module = module() + .memory() + .with_min(256) // 256 pages. 256 * 64K = 16MB + .build() + .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) + .table() + .with_min(64) + .build() + .with_export(ExportEntry::new("table".into(), Internal::Table(0))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_export(ExportEntry::new("tableBase".into(), Internal::Global(0))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1))) + .build(); + program.add_module("env", env_module, None).unwrap(); + program + } +} diff --git a/src/interpreter/tests/wasm.rs b/src/interpreter/tests/wasm.rs index 2f6b27a..dc34724 100644 --- a/src/interpreter/tests/wasm.rs +++ b/src/interpreter/tests/wasm.rs @@ -1,8 +1,9 @@ use elements::deserialize_file; -use elements::Module; +use elements::{Module, Internal, ExportEntry, InitExpr, Opcode, ValueType, GlobalType, GlobalEntry}; use interpreter::{ExecutionParams, ProgramInstance}; use interpreter::value::RuntimeValue; use interpreter::module::{ModuleInstanceInterface, ItemIndex}; +use builder::module; #[test] fn interpreter_inc_i32() { @@ -11,7 +12,22 @@ fn interpreter_inc_i32() { // The WASM file containing the module and function const WASM_FILE: &str = &"res/cases/v1/inc_i32.wasm"; - let program = ProgramInstance::with_emscripten_env(Default::default()).expect("Failed to instanciate program"); + let program = ProgramInstance::new(); + let env_module = module() + .memory() + .with_min(256) + .build() + .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) + .table() + .with_min(64) + .build() + .with_export(ExportEntry::new("table".into(), Internal::Table(0))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_export(ExportEntry::new("tableBase".into(), Internal::Global(0))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1))) + .build(); + let _env_instance = program.add_module("env", env_module, None).unwrap(); let module: Module = deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer"); @@ -43,7 +59,22 @@ fn interpreter_accumulate_u8() { const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1]; // Declare the memory limits of the runtime-environment - let program = ProgramInstance::with_emscripten_env(Default::default()).expect("Failed to instanciate program"); + let program = ProgramInstance::new(); + let env_module = module() + .memory() + .with_min(256) + .build() + .with_export(ExportEntry::new("memory".into(), Internal::Memory(0))) + .table() + .with_min(64) + .build() + .with_export(ExportEntry::new("table".into(), Internal::Table(0))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_export(ExportEntry::new("tableBase".into(), Internal::Global(0))) + .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)]))) + .with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1))) + .build(); + let _env_instance = program.add_module("env", env_module, None).unwrap(); // Load the module-structure from wasm-file and add to program let module: Module =