Some changes

This commit is contained in:
Sergey Pepyakin 2017-11-28 16:54:51 +03:00
parent e6bf41a10c
commit c3a3ad83fa
4 changed files with 275 additions and 20 deletions

1
pwasm-emscripten/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

View File

@ -0,0 +1,7 @@
[package]
name = "pwasm-emscripten"
version = "0.1.0"
authors = ["Sergey Pepyakin <s.pepyakin@gmail.com>"]
[dependencies]
parity-wasm = { path = ".." }

258
pwasm-emscripten/src/lib.rs Normal file
View File

@ -0,0 +1,258 @@
extern crate parity_wasm;
//This module provides some of the simplest exports
//from the Emscripten runtime, such as `STACKTOP` or `abort`.
use std::sync::{Arc, Weak};
use parity_wasm::builder::module;
use parity_wasm::elements::{ExportEntry, Internal, ValueType};
use parity_wasm::interpreter::Error;
use parity_wasm::interpreter::{native_module, UserDefinedElements, UserFunctionDescriptor, UserFunctionExecutor};
use parity_wasm::interpreter::{CallerContext, ModuleInstance, ModuleInstanceInterface};
use parity_wasm::interpreter::RuntimeValue;
use parity_wasm::interpreter::ProgramInstance;
use parity_wasm::interpreter::{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;
const LINEAR_MEMORY_PAGE_SIZE: u32 = 64 * 1024;
/// 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<u32>,
}
struct EmscriptenFunctionExecutor {
abort_global: Arc<VariableInstance>,
total_mem_global: Arc<VariableInstance>,
}
impl<'a> UserFunctionExecutor for EmscriptenFunctionExecutor {
fn execute(
&mut self,
name: &str,
context: CallerContext,
) -> Result<Option<RuntimeValue>, 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::<i32>()?;
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<Arc<ModuleInstanceInterface>, 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<u32> {
if self.allow_memory_growth { None } else { Some(self.total_memory) }
}
}
pub fn program_with_emscripten(params: EnvParams) -> ProgramInstance {
let program = ProgramInstance::new();
program.insert_loaded_module("env", env_module(params).unwrap()).unwrap();
program
}

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use parking_lot::RwLock;
use elements::Module;
use interpreter::Error;
use interpreter::emscripten::{EmscriptenParams, env_module};
use interpreter::emscripten::{self, env_module};
use interpreter::module::{ModuleInstance, ModuleInstanceInterface};
/// Program instance. Program is a set of instantiated modules.
@ -29,10 +29,12 @@ 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: EmscriptenParams) -> Result<Self, Error> {
Ok(ProgramInstance {
essence: Arc::new(ProgramInstanceEssence::with_env_params(params)?),
})
pub fn with_emscripten_env(params: emscripten::EnvParams) -> Result<Self, Error> {
let instance = ProgramInstance {
essence: Arc::new(ProgramInstanceEssence::new()),
};
// instance.essence.modules.write().insert("env".into(), env_module(params)?);
Ok(instance)
}
/// Instantiate module with validation.
@ -47,9 +49,9 @@ impl ProgramInstance {
}
/// Insert instantiated module.
pub fn insert_loaded_module(&self, name: &str, module_instance: Arc<ModuleInstance>) -> Result<Arc<ModuleInstance>, Error> {
pub fn insert_loaded_module(&self, name: &str, module_instance: Arc<ModuleInstanceInterface>) -> Result<Arc<ModuleInstanceInterface>, Error> {
// replace existing module with the same name with new one
self.essence.modules.write().insert(name.into(), module_instance.clone());
self.essence.modules.write().insert(name.into(), Arc::clone(&module_instance));
Ok(module_instance)
}
@ -67,19 +69,6 @@ impl ProgramInstanceEssence {
}
}
pub fn with_env_params(env_params: EmscriptenParams) -> Result<Self, Error> {
let env_mod = env_module(env_params)?;
Ok(ProgramInstanceEssence::with_env_module(env_mod))
}
pub fn with_env_module(env_module: Arc<ModuleInstanceInterface>) -> Self {
let mut modules = HashMap::new();
modules.insert("env".into(), env_module);
ProgramInstanceEssence {
modules: RwLock::new(modules),
}
}
/// Get module reference.
pub fn module(&self, name: &str) -> Option<Arc<ModuleInstanceInterface>> {
self.modules.read().get(name).cloned()