Files
parity-wasm/src/interpreter/env.rs

230 lines
9.6 KiB
Rust
Raw Normal View History

2017-05-08 11:35:56 +03:00
use std::sync::{Arc, Weak};
2017-05-15 13:58:55 +03:00
2017-05-18 15:08:55 +03:00
use builder::module;
2017-05-08 11:35:56 +03:00
use elements::{Module, FunctionType, ExportEntry, Internal, GlobalEntry, GlobalType,
ValueType, InitExpr, Opcode, Opcodes};
2017-05-04 11:25:25 +03:00
use interpreter::Error;
2017-05-18 15:08:55 +03:00
use interpreter::env_native::NATIVE_INDEX_FUNC_MIN;
use interpreter::module::{ModuleInstanceInterface, ModuleInstance, ExecutionParams,
ItemIndex, CallerContext};
2017-05-08 11:35:56 +03:00
use interpreter::memory::{MemoryInstance, LINEAR_MEMORY_PAGE_SIZE};
2017-05-04 11:25:25 +03:00
use interpreter::table::TableInstance;
2017-05-08 11:35:56 +03:00
use interpreter::value::{RuntimeValue, TransmuteInto};
use interpreter::variable::VariableInstance;
/// Memory address, at which stack begins.
const DEFAULT_STACK_BASE: u32 = 0;
/// 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 = false;
/// Default tableBase variable value.
const DEFAULT_TABLE_BASE: u32 = 0;
/// Defaul table size.
const DEFAULT_TABLE_SIZE: u32 = 16;
/// Index of default memory.
const INDEX_MEMORY: u32 = 0;
/// Index of default table.
const INDEX_TABLE: u32 = 0;
/// Index of STACK_BASE global variable.
const INDEX_GLOBAL_STACK_BASE: u32 = 0;
/// Index of STACK_TOP global variable.
const INDEX_GLOBAL_STACK_TOP: u32 = 1;
/// Index of STACK_MAX global variable.
const INDEX_GLOBAL_STACK_MAX: u32 = 2;
/// Index of DYNAMIC_BASE global variable.
const INDEX_GLOBAL_DYNAMIC_BASE: u32 = 3;
/// Index of DYNAMICTOP_PTR global variable.
const INDEX_GLOBAL_DYNAMICTOP_PTR: u32 = 4;
/// Index of TOTAL_MEMORY global variable.
const INDEX_GLOBAL_TOTAL_MEMORY: u32 = 5;
/// Index of ABORT global variable.
const INDEX_GLOBAL_ABORT: u32 = 6;
/// Index of EXITSTATUS global variable.
const INDEX_GLOBAL_EXIT_STATUS: u32 = 7;
/// Index of tableBase global variable.
const INDEX_GLOBAL_TABLE_BASE: u32 = 8;
/// Index of abort function.
const INDEX_FUNC_ABORT: u32 = 0;
/// Index of assert function.
const INDEX_FUNC_ASSERT: u32 = 1;
/// Index of enlargeMemory function.
const INDEX_FUNC_ENLARGE_MEMORY: u32 = 2;
/// Index of getTotalMemory function.
const INDEX_FUNC_GET_TOTAL_MEMORY: u32 = 3;
/// Min index of reserver function.
2017-05-18 15:08:55 +03:00
const INDEX_FUNC_MIN_NONUSED: u32 = 4;
2017-05-08 11:35:56 +03:00
/// Max index of reserved function.
2017-05-18 15:08:55 +03:00
const INDEX_FUNC_MAX: u32 = NATIVE_INDEX_FUNC_MIN - 1;
2017-05-15 13:58:55 +03:00
2017-05-08 11:35:56 +03:00
/// 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,
}
2017-05-04 19:09:43 +03:00
2017-05-04 11:25:25 +03:00
pub struct EnvModuleInstance {
2017-05-08 11:35:56 +03:00
_params: EnvParams,
instance: ModuleInstance,
2017-05-04 11:25:25 +03:00
}
impl EnvModuleInstance {
2017-05-18 15:08:55 +03:00
pub fn new(params: EnvParams, module: Module) -> Result<Self, Error> {
2017-05-08 11:35:56 +03:00
let instance = ModuleInstance::new(Weak::default(), module)?;
2017-05-04 11:25:25 +03:00
Ok(EnvModuleInstance {
2017-05-08 11:35:56 +03:00
_params: params,
instance: instance,
2017-05-04 11:25:25 +03:00
})
}
}
impl ModuleInstanceInterface for EnvModuleInstance {
2017-05-18 15:08:55 +03:00
fn execute_main(&self, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.instance.execute_main(params)
2017-05-04 11:25:25 +03:00
}
2017-05-18 15:08:55 +03:00
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.instance.execute_index(index, params)
2017-05-04 19:09:43 +03:00
}
2017-05-18 15:08:55 +03:00
fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
self.instance.execute_export(name, params)
2017-05-04 11:25:25 +03:00
}
2017-05-18 15:08:55 +03:00
fn export_entry(&self, name: &str) -> Result<Internal, Error> {
self.instance.export_entry(name)
2017-05-04 11:25:25 +03:00
}
2017-05-04 19:09:43 +03:00
fn table(&self, index: ItemIndex) -> Result<Arc<TableInstance>, Error> {
2017-05-08 11:35:56 +03:00
self.instance.table(index)
2017-05-04 11:25:25 +03:00
}
2017-05-04 12:01:21 +03:00
fn memory(&self, index: ItemIndex) -> Result<Arc<MemoryInstance>, Error> {
2017-05-08 11:35:56 +03:00
self.instance.memory(index)
2017-05-04 11:25:25 +03:00
}
2017-05-04 19:09:43 +03:00
fn global(&self, index: ItemIndex) -> Result<Arc<VariableInstance>, Error> {
2017-05-08 11:35:56 +03:00
self.instance.global(index)
2017-05-04 11:25:25 +03:00
}
2017-05-08 11:35:56 +03:00
fn call_function(&self, outer: CallerContext, index: ItemIndex) -> Result<Option<RuntimeValue>, Error> {
self.instance.call_function(outer, index)
2017-05-04 11:25:25 +03:00
}
2017-05-08 11:35:56 +03:00
fn call_function_indirect(&self, outer: CallerContext, table_index: ItemIndex, type_index: u32, func_index: u32) -> Result<Option<RuntimeValue>, Error> {
self.instance.call_function_indirect(outer, table_index, type_index, func_index)
2017-05-04 11:25:25 +03:00
}
2017-05-04 19:50:48 +03:00
fn call_internal_function(&self, outer: CallerContext, index: u32, _function_type: Option<&FunctionType>) -> Result<Option<RuntimeValue>, Error> {
2017-05-18 15:08:55 +03:00
// TODO: check function type
2017-05-08 11:35:56 +03:00
// to make interpreter independent of *SCRIPTEN runtime, just make abort/assert = interpreter Error
2017-05-04 19:30:42 +03:00
match index {
2017-05-08 11:35:56 +03:00
INDEX_FUNC_ABORT => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT))
.and_then(|g| g.set(RuntimeValue::I32(1)))
.and_then(|_| Err(Error::Trap("abort".into()))),
INDEX_FUNC_ASSERT => outer.value_stack.pop_as::<i32>()
.and_then(|condition| if condition == 0 {
self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_ABORT))
.and_then(|g| g.set(RuntimeValue::I32(1)))
.and_then(|_| Err(Error::Trap("assertion failed".into())))
} else {
Ok(None)
}),
INDEX_FUNC_ENLARGE_MEMORY => Ok(Some(RuntimeValue::I32(0))), // TODO: support memory enlarge
INDEX_FUNC_GET_TOTAL_MEMORY => self.global(ItemIndex::IndexSpace(INDEX_GLOBAL_TOTAL_MEMORY))
.map(|g| g.get())
.map(Some),
INDEX_FUNC_MIN_NONUSED ... INDEX_FUNC_MAX => Err(Error::Trap("unimplemented".into())),
2017-05-15 15:40:08 +03:00
_ => Err(Error::Trap(format!("trying to call function with index {} in env module", index))),
2017-05-04 19:30:42 +03:00
}
2017-05-04 11:25:25 +03:00
}
}
2017-05-30 17:15:36 +03:00
pub fn env_module(params: EnvParams) -> Result<EnvModuleInstance, 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);
2017-05-18 15:08:55 +03:00
let builder = module()
2017-05-04 19:09:43 +03:00
// memory regions
2017-05-08 11:35:56 +03:00
.memory()
2017-05-30 17:15:36 +03:00
.with_min(params.total_memory / LINEAR_MEMORY_PAGE_SIZE)
.with_max(params.max_memory().map(|m| m / LINEAR_MEMORY_PAGE_SIZE))
2017-05-08 11:35:56 +03:00
.build()
.with_export(ExportEntry::new("memory".into(), Internal::Memory(INDEX_MEMORY)))
2017-05-04 19:09:43 +03:00
// tables
2017-05-08 11:35:56 +03:00
.table()
.with_min(DEFAULT_TABLE_SIZE)
.build()
.with_export(ExportEntry::new("table".into(), Internal::Table(INDEX_TABLE)))
2017-05-04 19:09:43 +03:00
// globals
2017-05-08 11:35:56 +03:00
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE.transmute_into())])))
.with_export(ExportEntry::new("STACK_BASE".into(), Internal::Global(INDEX_GLOBAL_STACK_BASE)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(DEFAULT_STACK_BASE.transmute_into())])))
.with_export(ExportEntry::new("STACKTOP".into(), Internal::Global(INDEX_GLOBAL_STACK_TOP)))
2017-05-30 17:15:36 +03:00
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())])))
2017-05-08 11:35:56 +03:00
.with_export(ExportEntry::new("STACK_MAX".into(), Internal::Global(INDEX_GLOBAL_STACK_MAX)))
2017-05-30 17:15:36 +03:00
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())])))
2017-05-08 11:35:56 +03:00
.with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE)))
2017-05-30 17:15:36 +03:00
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack).transmute_into())])))
2017-05-08 11:35:56 +03:00
.with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR)))
2017-05-30 17:15:36 +03:00
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory.transmute_into())])))
2017-05-08 11:35:56 +03:00
.with_export(ExportEntry::new("TOTAL_MEMORY".into(), Internal::Global(INDEX_GLOBAL_TOTAL_MEMORY)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(0)])))
.with_export(ExportEntry::new("ABORT".into(), Internal::Global(INDEX_GLOBAL_ABORT)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const(0)])))
.with_export(ExportEntry::new("EXITSTATUS".into(), Internal::Global(INDEX_GLOBAL_EXIT_STATUS)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(DEFAULT_TABLE_BASE.transmute_into())]))) // TODO: what is this?
.with_export(ExportEntry::new("tableBase".into(), Internal::Global(INDEX_GLOBAL_TABLE_BASE)))
2017-05-04 19:09:43 +03:00
// functions
2017-05-08 11:35:56 +03:00
.function()
.signature().build()
.body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
.build()
.with_export(ExportEntry::new("abort".into(), Internal::Function(INDEX_FUNC_ABORT)))
.function()
.signature().param().i32().build()
.body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
.build()
.with_export(ExportEntry::new("assert".into(), Internal::Function(INDEX_FUNC_ASSERT)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
.build()
.with_export(ExportEntry::new("enlargeMemory".into(), Internal::Function(INDEX_FUNC_ENLARGE_MEMORY)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![Opcode::Unreachable, Opcode::End])).build()
.build()
2017-05-15 15:40:08 +03:00
.with_export(ExportEntry::new("getTotalMemory".into(), Internal::Function(INDEX_FUNC_GET_TOTAL_MEMORY)));
2017-05-15 13:58:55 +03:00
2017-05-30 17:15:36 +03:00
EnvModuleInstance::new(params, builder.build())
2017-05-08 11:35:56 +03:00
}
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,
}
}
}
impl EnvParams {
fn max_memory(&self) -> Option<u32> {
if self.allow_memory_growth { None } else { Some(self.total_memory) }
}
2017-05-04 11:25:25 +03:00
}