Merge remote-tracking branch 'origin/master' into fix-membase

This commit is contained in:
NikVolf 2017-07-26 21:57:11 +03:00
commit 8bb17db6d9
13 changed files with 325 additions and 52 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "parity-wasm" name = "parity-wasm"
version = "0.12.1" version = "0.12.2"
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Svyatoslav Nikolsky <svyatonik@yandex.ru>"] authors = ["Nikolay Volf <nikvolf@gmail.com>", "Svyatoslav Nikolsky <svyatonik@yandex.ru>"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
readme = "README.md" readme = "README.md"
@ -8,7 +8,7 @@ repository = "https://github.com/nikvolf/parity-wasm"
homepage = "https://github.com/nikvolf/parity-wasm" homepage = "https://github.com/nikvolf/parity-wasm"
documentation = "https://nikvolf.github.io/parity-wasm/parity_wasm/" documentation = "https://nikvolf.github.io/parity-wasm/parity_wasm/"
description = "WebAssembly binary format serialization/deserialization/interpreter" description = "WebAssembly binary format serialization/deserialization/interpreter"
keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"] keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"]
exclude = [ "res/*", "spec/*" ] exclude = [ "res/*", "spec/*" ]
[dependencies] [dependencies]

54
benches/interpreter.rs Normal file
View File

@ -0,0 +1,54 @@
#![feature(test)]
extern crate test;
extern crate parity_wasm;
use test::Bencher;
use parity_wasm::builder::module;
use parity_wasm::elements::{ExportEntry, Internal, ImportEntry, External, Opcodes, Opcode};
use parity_wasm::interpreter::{ProgramInstance, ModuleInstanceInterface, RuntimeValue};
#[bench]
fn export_entry_performance(b: &mut Bencher) {
// create module with 1000 functions
const NUM_FUNCTIONS: u32 = 1000;
let mut callee_module = module();
for i in 0..NUM_FUNCTIONS {
callee_module = callee_module
.with_export(ExportEntry::new(format!("func{}", i), Internal::Function(i)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::I32Const(i as i32),
Opcode::End,
])).build()
.build();
}
let callee_module = callee_module.build();
// create module which call one of 1000 functions
let caller_module = module()
.with_import(ImportEntry::new("callee_module".into(), "func500".into(), External::Function(0)))
.function()
.signature().return_type().i32().build()
.body().with_opcodes(Opcodes::new(vec![
Opcode::Call(0),
Opcode::I32Const(1000),
Opcode::I32Add,
Opcode::End,
])).build()
.build()
.build();
// add both modules to program
let program = ProgramInstance::new().unwrap();
program.add_module("callee_module", callee_module, None).unwrap();
let caller_module = program.add_module("caller_module", caller_module, None).unwrap();
// run bench
b.iter(||
assert_eq!(caller_module.execute_index(1, vec![].into()).unwrap(), Some(RuntimeValue::I32(1500)))
);
// test export_entry_performance ... bench: 3,497 ns/iter (+/- 200)
}

View File

@ -15,20 +15,25 @@ fn main() {
println!("Module sections: {}", module.sections().len()); println!("Module sections: {}", module.sections().len());
for section in module.sections() { for section in module.sections() {
match section { match *section {
&Section::Import(ref import_section) => { Section::Import(ref import_section) => {
println!(" Imports: {}", import_section.entries().len()); println!(" Imports: {}", import_section.entries().len());
import_section.entries().iter().map(|e| println!(" {}.{}", e.module(), e.field())).count();
}, },
&Section::Export(ref exports_section) => { Section::Export(ref exports_section) => {
println!(" Exports: {}", exports_section.entries().len()); println!(" Exports: {}", exports_section.entries().len());
exports_section.entries().iter().map(|e| println!(" {}", e.field())).count();
}, },
&Section::Function(ref function_section) => { Section::Function(ref function_section) => {
println!(" Functions: {}", function_section.entries().len()); println!(" Functions: {}", function_section.entries().len());
}, },
&Section::Global(ref globals_section) => { Section::Type(ref type_section) => {
println!(" Types: {}", type_section.types().len());
},
Section::Global(ref globals_section) => {
println!(" Globals: {}", globals_section.entries().len()); println!(" Globals: {}", globals_section.entries().len());
}, },
&Section::Data(ref data_section) if data_section.entries().len() > 0 => { Section::Data(ref data_section) if data_section.entries().len() > 0 => {
let data = &data_section.entries()[0]; let data = &data_section.entries()[0];
println!(" Data size: {}", data.value().len()); println!(" Data size: {}", data.value().len());
}, },

68
examples/invoke.rs Normal file
View File

@ -0,0 +1,68 @@
extern crate parity_wasm;
use std::env::args;
use parity_wasm::{interpreter, ModuleInstanceInterface, RuntimeValue};
use parity_wasm::elements::{Internal, External, Type, FunctionType, ValueType};
fn main() {
let args: Vec<_> = args().collect();
if args.len() < 3 {
println!("Usage: {} <wasm file> <exported func> [<arg>...]", args[0]);
return;
}
let func_name = &args[2];
let (_, program_args) = args.split_at(3);
let program = parity_wasm::ProgramInstance::with_env_params(
interpreter::EnvParams {
total_stack: 128*1024,
total_memory: 2*1024*1024,
allow_memory_growth: false,
}
).expect("Program instance to load");
let module = parity_wasm::deserialize_file(&args[1]).expect("File to be deserialized");
let execution_params = {
let export_section = module.export_section().expect("No export section found");
let function_section = module.function_section().expect("No function section found");
let type_section = module.type_section().expect("No type section found");
let found_entry = export_section.entries().iter()
.find(|entry| func_name == entry.field()).expect(&format!("No export with name {} found", func_name));
// Function index with imported functions
let function_index: usize = match found_entry.internal() {
&Internal::Function(index) => index as usize,
_ => panic!("Founded export is not a function"),
};
let import_section_len: usize = match module.import_section() {
Some(import) =>
import.entries().iter().filter(|entry| match entry.external() {
&External::Function(_) => true,
_ => false,
}).count(),
None => 0,
};
let function_index_in_section = function_index - import_section_len;
let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize;
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
&Type::Function(ref func_type) => func_type,
};
let args: Vec<RuntimeValue> = function_type.params().iter().enumerate().map(|(i, value)| match value {
&ValueType::I32 => RuntimeValue::I32(program_args[i].parse::<i32>().expect(&format!("Can't parse arg #{} as i32", program_args[i]))),
&ValueType::I64 => RuntimeValue::I64(program_args[i].parse::<i64>().expect(&format!("Can't parse arg #{} as i64", program_args[i]))),
&ValueType::F32 => RuntimeValue::F32(program_args[i].parse::<f32>().expect(&format!("Can't parse arg #{} as f32", program_args[i]))),
&ValueType::F64 => RuntimeValue::F64(program_args[i].parse::<f64>().expect(&format!("Can't parse arg #{} as f64", program_args[i]))),
}).collect();
interpreter::ExecutionParams::from(args)
};
let module = program.add_module("main", module, None).expect("Failed to initialize module");
println!("Result: {:?}", module.execute_export(func_name, execution_params).expect(""));
}

View File

@ -346,7 +346,7 @@ mod tests {
.build() .build()
.bind(); .bind();
assert_eq!(result.len(), 1); assert_eq!(result.len(), 1);
} }
#[test] #[test]

View File

@ -301,6 +301,20 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
} }
/// Import entry builder /// Import entry builder
/// # Examples
/// ```
/// use parity_wasm::builder::module;
///
/// let module = module()
/// .import()
/// .module("env")
/// .field("memory")
/// .external().memory(256, Some(256))
/// .build()
/// .build();
///
/// assert_eq!(module.import_section().expect("import section to exist").entries().len(), 1);
/// ```
pub fn import(self) -> import::ImportBuilder<Self> { pub fn import(self) -> import::ImportBuilder<Self> {
import::ImportBuilder::with_callback(self) import::ImportBuilder::with_callback(self)
} }
@ -318,11 +332,43 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
} }
/// Export entry builder /// Export entry builder
/// # Examples
/// ```
/// use parity_wasm::builder::module;
/// use parity_wasm::elements::Opcode::*;
///
/// let module = module()
/// .global()
/// .value_type().i32()
/// .init_expr(I32Const(0))
/// .build()
/// .export()
/// .field("_zero")
/// .internal().global(0)
/// .build()
/// .build();
///
/// assert_eq!(module.export_section().expect("export section to exist").entries().len(), 1);
/// ```
pub fn export(self) -> export::ExportBuilder<Self> { pub fn export(self) -> export::ExportBuilder<Self> {
export::ExportBuilder::with_callback(self) export::ExportBuilder::with_callback(self)
} }
/// Glboal entry builder /// Glboal entry builder
/// # Examples
/// ```
/// use parity_wasm::builder::module;
/// use parity_wasm::elements::Opcode::*;
///
/// let module = module()
/// .global()
/// .value_type().i32()
/// .init_expr(I32Const(0))
/// .build()
/// .build();
///
/// assert_eq!(module.global_section().expect("global section to exist").entries().len(), 1);
/// ```
pub fn global(self) -> global::GlobalBuilder<Self> { pub fn global(self) -> global::GlobalBuilder<Self> {
global::GlobalBuilder::with_callback(self) global::GlobalBuilder::with_callback(self)
} }
@ -442,6 +488,22 @@ impl<F> Invoke<elements::DataSegment> for ModuleBuilder<F>
} }
/// Start new module builder /// Start new module builder
/// # Examples
///
/// ```
/// use parity_wasm::builder;
///
/// let module = builder::module()
/// .function()
/// .signature().param().i32().build()
/// .body().build()
/// .build()
/// .build();
///
/// assert_eq!(module.type_section().expect("type section to exist").types().len(), 1);
/// assert_eq!(module.function_section().expect("function section to exist").entries().len(), 1);
/// assert_eq!(module.code_section().expect("code section to exist").bodies().len(), 1);
/// ```
pub fn module() -> ModuleBuilder { pub fn module() -> ModuleBuilder {
ModuleBuilder::new() ModuleBuilder::new()
} }

View File

@ -559,7 +559,7 @@ impl<I: Serialize<Error=::elements::Error>, T: IntoIterator<Item=I>> Serialize f
mod tests { mod tests {
use super::super::{deserialize_buffer, Serialize}; use super::super::{deserialize_buffer, Serialize};
use super::{CountedList, VarInt7, VarUint32, VarInt32}; use super::{CountedList, VarInt7, VarUint32, VarInt32, VarInt64, VarUint64};
fn varuint32_ser_test(val: u32, expected: Vec<u8>) { fn varuint32_ser_test(val: u32, expected: Vec<u8>) {
let mut buf = Vec::new(); let mut buf = Vec::new();
@ -594,6 +594,40 @@ mod tests {
varint32_de_test(dt.clone(), val); varint32_de_test(dt.clone(), val);
varint32_ser_test(val, dt); varint32_ser_test(val, dt);
} }
fn varuint64_ser_test(val: u64, expected: Vec<u8>) {
let mut buf = Vec::new();
let v1: VarUint64 = val.into();
v1.serialize(&mut buf).expect("to be serialized ok");
assert_eq!(expected, buf);
}
fn varuint64_de_test(dt: Vec<u8>, expected: u64) {
let val: VarUint64 = super::super::deserialize_buffer(dt).expect("buf to be serialized");
assert_eq!(expected, val.into());
}
fn varuint64_serde_test(dt: Vec<u8>, val: u64) {
varuint64_de_test(dt.clone(), val);
varuint64_ser_test(val, dt);
}
fn varint64_ser_test(val: i64, expected: Vec<u8>) {
let mut buf = Vec::new();
let v1: VarInt64 = val.into();
v1.serialize(&mut buf).expect("to be serialized ok");
assert_eq!(expected, buf);
}
fn varint64_de_test(dt: Vec<u8>, expected: i64) {
let val: VarInt64 = super::super::deserialize_buffer(dt).expect("buf to be serialized");
assert_eq!(expected, val.into());
}
fn varint64_serde_test(dt: Vec<u8>, val: i64) {
varint64_de_test(dt.clone(), val);
varint64_ser_test(val, dt);
}
#[test] #[test]
fn varuint32_0() { fn varuint32_0() {
@ -625,6 +659,36 @@ mod tests {
varint32_serde_test(vec![0x80, 0x40], -8192); varint32_serde_test(vec![0x80, 0x40], -8192);
} }
#[test]
fn varuint64_0() {
varuint64_serde_test(vec![0u8; 1], 0);
}
#[test]
fn varuint64_1() {
varuint64_serde_test(vec![1u8; 1], 1);
}
#[test]
fn varuint64_135() {
varuint64_serde_test(vec![135u8, 0x01], 135);
}
#[test]
fn varuint64_8192() {
varuint64_serde_test(vec![0x80, 0x40], 8192);
}
#[test]
fn varint64_8192() {
varint64_serde_test(vec![0x80, 0xc0, 0x00], 8192);
}
#[test]
fn varint64_neg_8192() {
varint64_serde_test(vec![0x80, 0x40], -8192);
}
#[test] #[test]
fn counted_list() { fn counted_list() {
let payload = vec![ let payload = vec![

View File

@ -85,7 +85,8 @@ pub struct EnvModuleInstance {
impl EnvModuleInstance { impl EnvModuleInstance {
pub fn new(params: EnvParams, module: Module) -> Result<Self, Error> { pub fn new(params: EnvParams, module: Module) -> Result<Self, Error> {
let instance = ModuleInstance::new(Weak::default(), "env".into(), module)?; let mut instance = ModuleInstance::new(Weak::default(), "env".into(), module)?;
instance.instantiate(false, None)?;
Ok(EnvModuleInstance { Ok(EnvModuleInstance {
_params: params, _params: params,
@ -190,7 +191,7 @@ pub fn env_module(params: EnvParams) -> Result<EnvModuleInstance, Error> {
.with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE))) .with_export(ExportEntry::new("DYNAMIC_BASE".into(), Internal::Global(INDEX_GLOBAL_DYNAMIC_BASE)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)]))) .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, true), InitExpr::new(vec![Opcode::I32Const((DEFAULT_STACK_BASE + params.total_stack) as i32)])))
.with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR))) .with_export(ExportEntry::new("DYNAMICTOP_PTR".into(), Internal::Global(INDEX_GLOBAL_DYNAMICTOP_PTR)))
.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)]))) .with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, params.allow_memory_growth), InitExpr::new(vec![Opcode::I32Const(params.total_memory as i32)])))
.with_export(ExportEntry::new("TOTAL_MEMORY".into(), Internal::Global(INDEX_GLOBAL_TOTAL_MEMORY))) .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_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_export(ExportEntry::new("ABORT".into(), Internal::Global(INDEX_GLOBAL_ABORT)))

View File

@ -8,7 +8,6 @@ use interpreter::program::ProgramInstanceEssence;
use interpreter::table::TableInstance; use interpreter::table::TableInstance;
use interpreter::variable::{VariableInstance, VariableType}; use interpreter::variable::{VariableInstance, VariableType};
// TODO: cache Internal-s to fasten access
/// Module imports. /// Module imports.
pub struct ModuleImports { pub struct ModuleImports {
/// Program instance. /// Program instance.

View File

@ -49,12 +49,9 @@ impl MemoryInstance {
.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?; .ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;
let memory = MemoryInstance { let memory = MemoryInstance {
buffer: RwLock::new(Vec::with_capacity(initial_size as usize)), buffer: RwLock::new(vec![0; initial_size as usize]),
maximum_size: maximum_size, maximum_size: maximum_size,
}; };
if memory.grow(memory_type.limits().initial())? == u32::MAX {
return Err(Error::Memory(format!("error initializing {}-bytes linear memory region", initial_size)));
}
Ok(Arc::new(memory)) Ok(Arc::new(memory))
} }

View File

@ -74,7 +74,7 @@ mod variable;
mod tests; mod tests;
pub use self::memory::MemoryInstance; pub use self::memory::MemoryInstance;
pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, ExportEntryType, CallerContext, ExecutionParams}; pub use self::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex, ExportEntryType, CallerContext, ExecutionParams, FunctionSignature};
pub use self::table::TableInstance; pub use self::table::TableInstance;
pub use self::program::ProgramInstance; pub use self::program::ProgramInstance;
pub use self::value::RuntimeValue; pub use self::value::RuntimeValue;

View File

@ -98,6 +98,8 @@ pub struct ModuleInstance {
functions_labels: HashMap<u32, HashMap<usize, usize>>, functions_labels: HashMap<u32, HashMap<usize, usize>>,
/// Module imports. /// Module imports.
imports: ModuleImports, imports: ModuleImports,
/// Module exports.
exports: HashMap<String, Vec<Internal>>,
/// Tables. /// Tables.
tables: Vec<Arc<TableInstance>>, tables: Vec<Arc<TableInstance>>,
/// Linear memory regions. /// Linear memory regions.
@ -211,6 +213,7 @@ impl ModuleInstance {
name: name, name: name,
module: module, module: module,
imports: imports, imports: imports,
exports: HashMap::new(),
functions_labels: HashMap::new(), functions_labels: HashMap::new(),
memory: memory, memory: memory,
tables: tables, tables: tables,
@ -232,24 +235,30 @@ impl ModuleInstance {
} }
// validate export section // validate export section
if is_user_module { // TODO: env module exports STACKTOP global, which is mutable => check is failed if let Some(export_section) = self.module.export_section() {
if let Some(export_section) = self.module.export_section() { for export in export_section.entries() {
for export in export_section.entries() { match export.internal() {
match export.internal() { &Internal::Function(function_index) => {
&Internal::Function(function_index) => self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?;
self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?, self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Function(function_index));
&Internal::Global(global_index) => },
self.global(ItemIndex::IndexSpace(global_index), None) &Internal::Global(global_index) => {
.and_then(|g| if g.is_mutable() { self.global(ItemIndex::IndexSpace(global_index), None)
Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) .and_then(|g| if g.is_mutable() && is_user_module {
} else { Err(Error::Validation(format!("trying to export mutable global {}", export.field())))
Ok(()) } else {
})?, Ok(())
&Internal::Memory(memory_index) => })?;
self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?, self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index));
&Internal::Table(table_index) => },
self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?, &Internal::Memory(memory_index) => {
} self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?;
self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Memory(memory_index));
},
&Internal::Table(table_index) => {
self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?;
self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Table(table_index));
},
} }
} }
} }
@ -422,15 +431,15 @@ impl ModuleInstanceInterface for ModuleInstance {
} }
fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> { fn execute_export(&self, name: &str, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error> {
let index = self.module.export_section() let index = self.exports.get(name)
.ok_or(Error::Function("missing export section".into())) .ok_or(Error::Function(format!("missing exports with name {}", name)))
.and_then(|s| s.entries().iter() .and_then(|l| l.iter()
.find(|e| e.field() == name && match e.internal() { .find(|i| match i {
&Internal::Function(_) => true, &&Internal::Function(_) => true,
_ => false, _ => false,
}) })
.ok_or(Error::Function(format!("missing export section exported function with name {}", name))) .ok_or(Error::Function(format!("missing exported function with name {}", name)))
.map(|e| match e.internal() { .map(|i| match i {
&Internal::Function(index) => index, &Internal::Function(index) => index,
_ => unreachable!(), // checked couple of lines above _ => unreachable!(), // checked couple of lines above
}) })
@ -439,24 +448,24 @@ impl ModuleInstanceInterface for ModuleInstance {
} }
fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> { fn export_entry<'a>(&self, name: &str, required_type: &ExportEntryType) -> Result<Internal, Error> {
self.module.export_section() self.exports.get(name)
.ok_or(Error::Program(format!("trying to import {} from module without export section", name))) .ok_or(Error::Function(format!("missing exports with name {}", name)))
.and_then(|s| s.entries().iter() .and_then(|l| l.iter()
.find(|e| e.field() == name && match required_type { .find(|i| match required_type {
&ExportEntryType::Any => true, &ExportEntryType::Any => true,
&ExportEntryType::Global(global_type) => match e.internal() { &ExportEntryType::Global(global_type) => match i {
&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false), &&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false),
_ => false, _ => false,
}, },
&ExportEntryType::Function(ref required_type) => match e.internal() { &ExportEntryType::Function(ref required_type) => match i {
&Internal::Function(function_index) => &&Internal::Function(function_index) =>
self.function_type(ItemIndex::IndexSpace(function_index)) self.function_type(ItemIndex::IndexSpace(function_index))
.map(|ft| ft == *required_type) .map(|ft| ft == *required_type)
.unwrap_or(false), .unwrap_or(false),
_ => false, _ => false,
}, },
}) })
.map(|e| *e.internal()) .map(|i| *i)
.ok_or(Error::Program(format!("unresolved import {}", name)))) .ok_or(Error::Program(format!("unresolved import {}", name))))
} }
@ -653,6 +662,7 @@ fn get_initializer(expr: &InitExpr, module: &Module, imports: &ModuleImports, ex
} }
impl<'a> FunctionSignature<'a> { impl<'a> FunctionSignature<'a> {
/// Get return type of this function.
pub fn return_type(&self) -> Option<ValueType> { pub fn return_type(&self) -> Option<ValueType> {
match self { match self {
&FunctionSignature::Module(ft) => ft.return_type(), &FunctionSignature::Module(ft) => ft.return_type(),
@ -660,6 +670,7 @@ impl<'a> FunctionSignature<'a> {
} }
} }
/// Get parameters of this function.
pub fn params(&self) -> &[ValueType] { pub fn params(&self) -> &[ValueType] {
match self { match self {
&FunctionSignature::Module(ft) => ft.params(), &FunctionSignature::Module(ft) => ft.params(),

View File

@ -97,6 +97,7 @@ fn global_get_set() {
let program = ProgramInstance::new().unwrap(); let program = ProgramInstance::new().unwrap();
let module = program.add_module("main", module, None).unwrap(); let module = program.add_module("main", module, None).unwrap();
assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(50)); assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(50));
assert_eq!(module.execute_index(0, vec![].into()).unwrap().unwrap(), RuntimeValue::I32(58));
} }
const SIGNATURE_I32_I32: &'static [ValueType] = &[ValueType::I32, ValueType::I32]; const SIGNATURE_I32_I32: &'static [ValueType] = &[ValueType::I32, ValueType::I32];
@ -209,6 +210,17 @@ fn single_program_different_modules() {
assert_eq!(executor.values, vec![7, 57, 42]); assert_eq!(executor.values, vec![7, 57, 42]);
} }
#[test]
fn import_env_mutable_global() {
let program = ProgramInstance::new().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] #[test]
fn env_native_export_entry_type_check() { fn env_native_export_entry_type_check() {
let program = ProgramInstance::new().unwrap(); let program = ProgramInstance::new().unwrap();