use alloc::{
borrow::ToOwned,
rc::Rc,
string::{String, ToString},
vec::Vec,
};
use core::cell::RefCell;
use core::fmt;
use Trap;
use alloc::collections::BTreeMap;
use core::cell::Ref;
use func::{FuncBody, FuncInstance, FuncRef};
use global::{GlobalInstance, GlobalRef};
use host::Externals;
use imports::ImportResolver;
use memory::MemoryRef;
use memory_units::Pages;
use parity_wasm::elements::{External, InitExpr, Instruction, Internal, ResizableLimits, Type};
use runner::StackRecycler;
use table::TableRef;
use types::{GlobalDescriptor, MemoryDescriptor, TableDescriptor};
use validation::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use {Error, MemoryInstance, Module, RuntimeValue, Signature, TableInstance};
#[derive(Clone, Debug)]
pub struct ModuleRef(pub(crate) Rc<ModuleInstance>);
impl ::core::ops::Deref for ModuleRef {
type Target = ModuleInstance;
fn deref(&self) -> &ModuleInstance {
&self.0
}
}
pub enum ExternVal {
Func(FuncRef),
Table(TableRef),
Memory(MemoryRef),
Global(GlobalRef),
}
impl Clone for ExternVal {
fn clone(&self) -> Self {
match *self {
ExternVal::Func(ref func) => ExternVal::Func(func.clone()),
ExternVal::Table(ref table) => ExternVal::Table(table.clone()),
ExternVal::Memory(ref memory) => ExternVal::Memory(memory.clone()),
ExternVal::Global(ref global) => ExternVal::Global(global.clone()),
}
}
}
impl fmt::Debug for ExternVal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ExternVal {{ {} }}",
match *self {
ExternVal::Func(_) => "Func",
ExternVal::Table(_) => "Table",
ExternVal::Memory(_) => "Memory",
ExternVal::Global(_) => "Global",
}
)
}
}
impl ExternVal {
pub fn as_func(&self) -> Option<&FuncRef> {
match *self {
ExternVal::Func(ref func) => Some(func),
_ => None,
}
}
pub fn as_table(&self) -> Option<&TableRef> {
match *self {
ExternVal::Table(ref table) => Some(table),
_ => None,
}
}
pub fn as_memory(&self) -> Option<&MemoryRef> {
match *self {
ExternVal::Memory(ref memory) => Some(memory),
_ => None,
}
}
pub fn as_global(&self) -> Option<&GlobalRef> {
match *self {
ExternVal::Global(ref global) => Some(global),
_ => None,
}
}
}
#[derive(Debug)]
pub struct ModuleInstance {
signatures: RefCell<Vec<Rc<Signature>>>,
tables: RefCell<Vec<TableRef>>,
funcs: RefCell<Vec<FuncRef>>,
memories: RefCell<Vec<MemoryRef>>,
globals: RefCell<Vec<GlobalRef>>,
exports: RefCell<BTreeMap<String, ExternVal>>,
}
impl ModuleInstance {
fn default() -> Self {
ModuleInstance {
funcs: RefCell::new(Vec::new()),
signatures: RefCell::new(Vec::new()),
tables: RefCell::new(Vec::new()),
memories: RefCell::new(Vec::new()),
globals: RefCell::new(Vec::new()),
exports: RefCell::new(BTreeMap::new()),
}
}
pub(crate) fn memory_by_index(&self, idx: u32) -> Option<MemoryRef> {
self.memories.borrow_mut().get(idx as usize).cloned()
}
pub(crate) fn table_by_index(&self, idx: u32) -> Option<TableRef> {
self.tables.borrow_mut().get(idx as usize).cloned()
}
pub(crate) fn global_by_index(&self, idx: u32) -> Option<GlobalRef> {
self.globals.borrow_mut().get(idx as usize).cloned()
}
pub(crate) fn func_by_index(&self, idx: u32) -> Option<FuncRef> {
self.funcs.borrow().get(idx as usize).cloned()
}
pub(crate) fn signature_by_index(&self, idx: u32) -> Option<Rc<Signature>> {
self.signatures.borrow().get(idx as usize).cloned()
}
fn push_func(&self, func: FuncRef) {
self.funcs.borrow_mut().push(func);
}
fn push_signature(&self, signature: Rc<Signature>) {
self.signatures.borrow_mut().push(signature)
}
fn push_memory(&self, memory: MemoryRef) {
self.memories.borrow_mut().push(memory)
}
fn push_table(&self, table: TableRef) {
self.tables.borrow_mut().push(table)
}
fn push_global(&self, global: GlobalRef) {
self.globals.borrow_mut().push(global)
}
pub fn globals<'a>(&self) -> Ref<Vec<GlobalRef>> {
self.globals.borrow()
}
fn insert_export<N: Into<String>>(&self, name: N, extern_val: ExternVal) {
self.exports.borrow_mut().insert(name.into(), extern_val);
}
fn alloc_module<'i, I: Iterator<Item = &'i ExternVal>>(
loaded_module: &Module,
extern_vals: I,
) -> Result<ModuleRef, Error> {
let module = loaded_module.module();
let instance = ModuleRef(Rc::new(ModuleInstance::default()));
for &Type::Function(ref ty) in module.type_section().map(|ts| ts.types()).unwrap_or(&[]) {
let signature = Rc::new(Signature::from_elements(ty));
instance.push_signature(signature);
}
{
let mut imports = module
.import_section()
.map(|is| is.entries())
.unwrap_or(&[])
.into_iter();
let mut extern_vals = extern_vals;
loop {
let (import, extern_val) = match (imports.next(), extern_vals.next()) {
(Some(import), Some(extern_val)) => (import, extern_val),
(None, None) => break,
(Some(_), None) | (None, Some(_)) => {
return Err(Error::Instantiation(
"extern_vals length is not equal to import section entries".to_owned(),
));
}
};
match (import.external(), extern_val) {
(&External::Function(fn_type_idx), &ExternVal::Func(ref func)) => {
let expected_fn_type = instance
.signature_by_index(fn_type_idx)
.expect("Due to validation function type should exists");
let actual_fn_type = func.signature();
if &*expected_fn_type != actual_fn_type {
return Err(Error::Instantiation(format!(
"Expected function with type {:?}, but actual type is {:?} for entry {}",
expected_fn_type,
actual_fn_type,
import.field(),
)));
}
instance.push_func(func.clone())
}
(&External::Table(ref tt), &ExternVal::Table(ref table)) => {
match_limits(table.limits(), tt.limits())?;
instance.push_table(table.clone());
}
(&External::Memory(ref mt), &ExternVal::Memory(ref memory)) => {
match_limits(memory.limits(), mt.limits())?;
instance.push_memory(memory.clone());
}
(&External::Global(ref gl), &ExternVal::Global(ref global)) => {
if gl.content_type() != global.elements_value_type() {
return Err(Error::Instantiation(format!(
"Expect global with {:?} type, but provided global with {:?} type",
gl.content_type(),
global.value_type(),
)));
}
instance.push_global(global.clone());
}
(expected_import, actual_extern_val) => {
return Err(Error::Instantiation(format!(
"Expected {:?} type, but provided {:?} extern_val",
expected_import, actual_extern_val
)));
}
}
}
}
let code = loaded_module.code();
{
let funcs = module
.function_section()
.map(|fs| fs.entries())
.unwrap_or(&[]);
let bodies = module.code_section().map(|cs| cs.bodies()).unwrap_or(&[]);
debug_assert!(
funcs.len() == bodies.len(),
"Due to validation func and body counts must match"
);
for (index, (ty, body)) in
Iterator::zip(funcs.into_iter(), bodies.into_iter()).enumerate()
{
let signature = instance
.signature_by_index(ty.type_ref())
.expect("Due to validation type should exists");
let code = code.get(index).expect(
"At func validation time labels are collected; Collected labels are added by index; qed",
).clone();
let func_body = FuncBody {
locals: body.locals().to_vec(),
code: code,
};
let func_instance =
FuncInstance::alloc_internal(Rc::downgrade(&instance.0), signature, func_body);
instance.push_func(func_instance);
}
}
for table_type in module.table_section().map(|ts| ts.entries()).unwrap_or(&[]) {
let table =
TableInstance::alloc(table_type.limits().initial(), table_type.limits().maximum())?;
instance.push_table(table);
}
for memory_type in module
.memory_section()
.map(|ms| ms.entries())
.unwrap_or(&[])
{
let initial: Pages = Pages(memory_type.limits().initial() as usize);
let maximum: Option<Pages> = memory_type.limits().maximum().map(|m| Pages(m as usize));
let memory = MemoryInstance::alloc(initial, maximum)
.expect("Due to validation `initial` and `maximum` should be valid");
instance.push_memory(memory);
}
for global_entry in module
.global_section()
.map(|gs| gs.entries())
.unwrap_or(&[])
{
let init_val = eval_init_expr(global_entry.init_expr(), &*instance);
let global = GlobalInstance::alloc(init_val, global_entry.global_type().is_mutable());
instance.push_global(global);
}
for export in module
.export_section()
.map(|es| es.entries())
.unwrap_or(&[])
{
let field = export.field();
let extern_val: ExternVal = match *export.internal() {
Internal::Function(idx) => {
let func = instance
.func_by_index(idx)
.expect("Due to validation func should exists");
ExternVal::Func(func)
}
Internal::Global(idx) => {
let global = instance
.global_by_index(idx)
.expect("Due to validation global should exists");
ExternVal::Global(global)
}
Internal::Memory(idx) => {
let memory = instance
.memory_by_index(idx)
.expect("Due to validation memory should exists");
ExternVal::Memory(memory)
}
Internal::Table(idx) => {
let table = instance
.table_by_index(idx)
.expect("Due to validation table should exists");
ExternVal::Table(table)
}
};
instance.insert_export(field, extern_val);
}
Ok(instance)
}
pub fn with_externvals<'a, 'i, I: Iterator<Item = &'i ExternVal>>(
loaded_module: &'a Module,
extern_vals: I,
) -> Result<NotStartedModuleRef<'a>, Error> {
let module = loaded_module.module();
let module_ref = ModuleInstance::alloc_module(loaded_module, extern_vals)?;
for element_segment in module
.elements_section()
.map(|es| es.entries())
.unwrap_or(&[])
{
let offset = element_segment
.offset()
.as_ref()
.expect("passive segments are rejected due to validation");
let offset_val = match eval_init_expr(offset, &module_ref) {
RuntimeValue::I32(v) => v as u32,
_ => panic!("Due to validation elem segment offset should evaluate to i32"),
};
let table_inst = module_ref
.table_by_index(DEFAULT_TABLE_INDEX)
.expect("Due to validation default table should exists");
if offset_val as u64 + element_segment.members().len() as u64
> table_inst.current_size() as u64
{
return Err(Error::Instantiation(
"elements segment does not fit".to_string(),
));
}
for (j, func_idx) in element_segment.members().into_iter().enumerate() {
let func = module_ref
.func_by_index(*func_idx)
.expect("Due to validation funcs from element segments should exists");
table_inst.set(offset_val + j as u32, Some(func))?;
}
}
for data_segment in module.data_section().map(|ds| ds.entries()).unwrap_or(&[]) {
let offset = data_segment
.offset()
.as_ref()
.expect("passive segments are rejected due to validation");
let offset_val = match eval_init_expr(offset, &module_ref) {
RuntimeValue::I32(v) => v as u32,
_ => panic!("Due to validation data segment offset should evaluate to i32"),
};
let memory_inst = module_ref
.memory_by_index(DEFAULT_MEMORY_INDEX)
.expect("Due to validation default memory should exists");
memory_inst.set(offset_val, data_segment.value())?;
}
Ok(NotStartedModuleRef {
loaded_module,
instance: module_ref,
})
}
pub fn new<'m, I: ImportResolver>(
loaded_module: &'m Module,
imports: &I,
) -> Result<NotStartedModuleRef<'m>, Error> {
let module = loaded_module.module();
let mut extern_vals = Vec::new();
for import_entry in module.import_section().map(|s| s.entries()).unwrap_or(&[]) {
let module_name = import_entry.module();
let field_name = import_entry.field();
let extern_val = match *import_entry.external() {
External::Function(fn_ty_idx) => {
let types = module.type_section().map(|s| s.types()).unwrap_or(&[]);
let &Type::Function(ref func_type) = types
.get(fn_ty_idx as usize)
.expect("Due to validation functions should have valid types");
let signature = Signature::from_elements(func_type);
let func = imports.resolve_func(module_name, field_name, &signature)?;
ExternVal::Func(func)
}
External::Table(ref table_type) => {
let table_descriptor = TableDescriptor::from_elements(table_type);
let table =
imports.resolve_table(module_name, field_name, &table_descriptor)?;
ExternVal::Table(table)
}
External::Memory(ref memory_type) => {
let memory_descriptor = MemoryDescriptor::from_elements(memory_type);
let memory =
imports.resolve_memory(module_name, field_name, &memory_descriptor)?;
ExternVal::Memory(memory)
}
External::Global(ref global_type) => {
let global_descriptor = GlobalDescriptor::from_elements(global_type);
let global =
imports.resolve_global(module_name, field_name, &global_descriptor)?;
ExternVal::Global(global)
}
};
extern_vals.push(extern_val);
}
Self::with_externvals(loaded_module, extern_vals.iter())
}
pub fn invoke_export<E: Externals>(
&self,
func_name: &str,
args: &[RuntimeValue],
externals: &mut E,
) -> Result<Option<RuntimeValue>, Error> {
let func_instance = self.func_by_name(func_name)?;
FuncInstance::invoke(&func_instance, args, externals).map_err(|t| Error::Trap(t))
}
pub fn invoke_export_with_stack<E: Externals>(
&self,
func_name: &str,
args: &[RuntimeValue],
externals: &mut E,
stack_recycler: &mut StackRecycler,
) -> Result<Option<RuntimeValue>, Error> {
let func_instance = self.func_by_name(func_name)?;
FuncInstance::invoke_with_stack(&func_instance, args, externals, stack_recycler)
.map_err(|t| Error::Trap(t))
}
fn func_by_name(&self, func_name: &str) -> Result<FuncRef, Error> {
let extern_val = self
.export_by_name(func_name)
.ok_or_else(|| Error::Function(format!("Module doesn't have export {}", func_name)))?;
match extern_val {
ExternVal::Func(func_instance) => Ok(func_instance),
unexpected => Err(Error::Function(format!(
"Export {} is not a function, but {:?}",
func_name, unexpected
))),
}
}
pub fn export_by_name(&self, name: &str) -> Option<ExternVal> {
self.exports.borrow().get(name).cloned()
}
}
pub struct NotStartedModuleRef<'a> {
loaded_module: &'a Module,
instance: ModuleRef,
}
impl<'a> NotStartedModuleRef<'a> {
pub fn not_started_instance(&self) -> &ModuleRef {
&self.instance
}
pub fn run_start<E: Externals>(self, state: &mut E) -> Result<ModuleRef, Trap> {
if let Some(start_fn_idx) = self.loaded_module.module().start_section() {
let start_func = self
.instance
.func_by_index(start_fn_idx)
.expect("Due to validation start function should exists");
FuncInstance::invoke(&start_func, &[], state)?;
}
Ok(self.instance)
}
pub fn assert_no_start(self) -> ModuleRef {
if self.loaded_module.module().start_section().is_some() {
panic!("assert_no_start called on module with `start` function");
}
self.instance
}
pub fn has_start(&self) -> bool {
self.loaded_module.module().start_section().is_some()
}
}
fn eval_init_expr(init_expr: &InitExpr, module: &ModuleInstance) -> RuntimeValue {
let code = init_expr.code();
debug_assert!(
code.len() == 2,
"Due to validation `code`.len() should be 2"
);
match code[0] {
Instruction::I32Const(v) => v.into(),
Instruction::I64Const(v) => v.into(),
Instruction::F32Const(v) => RuntimeValue::decode_f32(v),
Instruction::F64Const(v) => RuntimeValue::decode_f64(v),
Instruction::GetGlobal(idx) => {
let global = module
.global_by_index(idx)
.expect("Due to validation global should exists in module");
global.get()
}
_ => panic!("Due to validation init should be a const expr"),
}
}
fn match_limits(l1: &ResizableLimits, l2: &ResizableLimits) -> Result<(), Error> {
if l1.initial() < l2.initial() {
return Err(Error::Instantiation(format!(
"trying to import with limits l1.initial={} and l2.initial={}",
l1.initial(),
l2.initial()
)));
}
match (l1.maximum(), l2.maximum()) {
(_, None) => (),
(Some(m1), Some(m2)) if m1 <= m2 => (),
_ => {
return Err(Error::Instantiation(format!(
"trying to import with limits l1.max={:?} and l2.max={:?}",
l1.maximum(),
l2.maximum()
)));
}
}
Ok(())
}
pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> {
if let Some(maximum) = limits.maximum() {
if maximum < limits.initial() {
return Err(Error::Instantiation(format!(
"maximum limit {} is less than minimum {}",
maximum,
limits.initial()
)));
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{ExternVal, ModuleInstance};
use func::FuncInstance;
use imports::ImportsBuilder;
use tests::parse_wat;
use types::{Signature, ValueType};
#[should_panic]
#[test]
fn assert_no_start_panics_on_module_with_start() {
let module_with_start = parse_wat(
r#"
(module
(func $f)
(start $f))
"#,
);
let module = ModuleInstance::new(&module_with_start, &ImportsBuilder::default()).unwrap();
assert!(!module.has_start());
module.assert_no_start();
}
#[test]
fn imports_provided_by_externvals() {
let module_with_single_import = parse_wat(
r#"
(module
(import "foo" "bar" (func))
)
"#,
);
assert!(ModuleInstance::with_externvals(
&module_with_single_import,
[ExternVal::Func(FuncInstance::alloc_host(
Signature::new(&[][..], None),
0
),)]
.iter(),
)
.is_ok());
assert!(ModuleInstance::with_externvals(
&module_with_single_import,
[
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 0)),
ExternVal::Func(FuncInstance::alloc_host(Signature::new(&[][..], None), 1)),
]
.iter(),
)
.is_err());
assert!(ModuleInstance::with_externvals(&module_with_single_import, [].iter(),).is_err());
assert!(ModuleInstance::with_externvals(
&module_with_single_import,
[ExternVal::Func(FuncInstance::alloc_host(
Signature::new(&[][..], Some(ValueType::I32)),
0
),)]
.iter(),
)
.is_err());
}
}