mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-01 00:51:42 +00:00
Merge remote-tracking branch 'origin/master' into fix-membase
This commit is contained in:
commit
8bb17db6d9
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "parity-wasm"
|
||||
version = "0.12.1"
|
||||
version = "0.12.2"
|
||||
authors = ["Nikolay Volf <nikvolf@gmail.com>", "Svyatoslav Nikolsky <svyatonik@yandex.ru>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
readme = "README.md"
|
||||
@ -8,7 +8,7 @@ repository = "https://github.com/nikvolf/parity-wasm"
|
||||
homepage = "https://github.com/nikvolf/parity-wasm"
|
||||
documentation = "https://nikvolf.github.io/parity-wasm/parity_wasm/"
|
||||
description = "WebAssembly binary format serialization/deserialization/interpreter"
|
||||
keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"]
|
||||
keywords = ["wasm", "webassembly", "bytecode", "serde", "interpreter"]
|
||||
exclude = [ "res/*", "spec/*" ]
|
||||
|
||||
[dependencies]
|
||||
|
54
benches/interpreter.rs
Normal file
54
benches/interpreter.rs
Normal 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)
|
||||
}
|
@ -15,20 +15,25 @@ fn main() {
|
||||
println!("Module sections: {}", module.sections().len());
|
||||
|
||||
for section in module.sections() {
|
||||
match section {
|
||||
&Section::Import(ref import_section) => {
|
||||
match *section {
|
||||
Section::Import(ref import_section) => {
|
||||
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());
|
||||
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());
|
||||
},
|
||||
&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());
|
||||
},
|
||||
&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];
|
||||
println!(" Data size: {}", data.value().len());
|
||||
},
|
||||
|
68
examples/invoke.rs
Normal file
68
examples/invoke.rs
Normal 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(""));
|
||||
}
|
@ -346,7 +346,7 @@ mod tests {
|
||||
.build()
|
||||
.bind();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
assert_eq!(result.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -301,6 +301,20 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
import::ImportBuilder::with_callback(self)
|
||||
}
|
||||
@ -318,11 +332,43 @@ impl<F> ModuleBuilder<F> where F: Invoke<elements::Module> {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
export::ExportBuilder::with_callback(self)
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
global::GlobalBuilder::with_callback(self)
|
||||
}
|
||||
@ -442,6 +488,22 @@ impl<F> Invoke<elements::DataSegment> for ModuleBuilder<F>
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
ModuleBuilder::new()
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ impl<I: Serialize<Error=::elements::Error>, T: IntoIterator<Item=I>> Serialize f
|
||||
mod tests {
|
||||
|
||||
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>) {
|
||||
let mut buf = Vec::new();
|
||||
@ -594,6 +594,40 @@ mod tests {
|
||||
varint32_de_test(dt.clone(), val);
|
||||
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]
|
||||
fn varuint32_0() {
|
||||
@ -625,6 +659,36 @@ mod tests {
|
||||
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]
|
||||
fn counted_list() {
|
||||
let payload = vec![
|
||||
|
@ -85,7 +85,8 @@ pub struct EnvModuleInstance {
|
||||
|
||||
impl EnvModuleInstance {
|
||||
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 {
|
||||
_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_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_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_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)))
|
||||
|
@ -8,7 +8,6 @@ use interpreter::program::ProgramInstanceEssence;
|
||||
use interpreter::table::TableInstance;
|
||||
use interpreter::variable::{VariableInstance, VariableType};
|
||||
|
||||
// TODO: cache Internal-s to fasten access
|
||||
/// Module imports.
|
||||
pub struct ModuleImports {
|
||||
/// Program instance.
|
||||
|
@ -49,12 +49,9 @@ impl MemoryInstance {
|
||||
.ok_or(Error::Memory(format!("initial memory size must be at most {} pages", LINEAR_MEMORY_MAX_PAGES)))?;
|
||||
|
||||
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,
|
||||
};
|
||||
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))
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ mod variable;
|
||||
mod tests;
|
||||
|
||||
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::program::ProgramInstance;
|
||||
pub use self::value::RuntimeValue;
|
||||
|
@ -98,6 +98,8 @@ pub struct ModuleInstance {
|
||||
functions_labels: HashMap<u32, HashMap<usize, usize>>,
|
||||
/// Module imports.
|
||||
imports: ModuleImports,
|
||||
/// Module exports.
|
||||
exports: HashMap<String, Vec<Internal>>,
|
||||
/// Tables.
|
||||
tables: Vec<Arc<TableInstance>>,
|
||||
/// Linear memory regions.
|
||||
@ -211,6 +213,7 @@ impl ModuleInstance {
|
||||
name: name,
|
||||
module: module,
|
||||
imports: imports,
|
||||
exports: HashMap::new(),
|
||||
functions_labels: HashMap::new(),
|
||||
memory: memory,
|
||||
tables: tables,
|
||||
@ -232,24 +235,30 @@ impl ModuleInstance {
|
||||
}
|
||||
|
||||
// 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() {
|
||||
for export in export_section.entries() {
|
||||
match export.internal() {
|
||||
&Internal::Function(function_index) =>
|
||||
self.require_function(ItemIndex::IndexSpace(function_index)).map(|_| ())?,
|
||||
&Internal::Global(global_index) =>
|
||||
self.global(ItemIndex::IndexSpace(global_index), None)
|
||||
.and_then(|g| if g.is_mutable() {
|
||||
Err(Error::Validation(format!("trying to export mutable global {}", export.field())))
|
||||
} else {
|
||||
Ok(())
|
||||
})?,
|
||||
&Internal::Memory(memory_index) =>
|
||||
self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?,
|
||||
&Internal::Table(table_index) =>
|
||||
self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?,
|
||||
}
|
||||
if let Some(export_section) = self.module.export_section() {
|
||||
for export in export_section.entries() {
|
||||
match export.internal() {
|
||||
&Internal::Function(function_index) => {
|
||||
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)
|
||||
.and_then(|g| if g.is_mutable() && is_user_module {
|
||||
Err(Error::Validation(format!("trying to export mutable global {}", export.field())))
|
||||
} else {
|
||||
Ok(())
|
||||
})?;
|
||||
self.exports.entry(export.field().into()).or_insert_with(Default::default).push(Internal::Global(global_index));
|
||||
},
|
||||
&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> {
|
||||
let index = self.module.export_section()
|
||||
.ok_or(Error::Function("missing export section".into()))
|
||||
.and_then(|s| s.entries().iter()
|
||||
.find(|e| e.field() == name && match e.internal() {
|
||||
&Internal::Function(_) => true,
|
||||
let index = self.exports.get(name)
|
||||
.ok_or(Error::Function(format!("missing exports with name {}", name)))
|
||||
.and_then(|l| l.iter()
|
||||
.find(|i| match i {
|
||||
&&Internal::Function(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
.ok_or(Error::Function(format!("missing export section exported function with name {}", name)))
|
||||
.map(|e| match e.internal() {
|
||||
.ok_or(Error::Function(format!("missing exported function with name {}", name)))
|
||||
.map(|i| match i {
|
||||
&Internal::Function(index) => index,
|
||||
_ => 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> {
|
||||
self.module.export_section()
|
||||
.ok_or(Error::Program(format!("trying to import {} from module without export section", name)))
|
||||
.and_then(|s| s.entries().iter()
|
||||
.find(|e| e.field() == name && match required_type {
|
||||
self.exports.get(name)
|
||||
.ok_or(Error::Function(format!("missing exports with name {}", name)))
|
||||
.and_then(|l| l.iter()
|
||||
.find(|i| match required_type {
|
||||
&ExportEntryType::Any => true,
|
||||
&ExportEntryType::Global(global_type) => match e.internal() {
|
||||
&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false),
|
||||
&ExportEntryType::Global(global_type) => match i {
|
||||
&&Internal::Global(global_index) => self.global(ItemIndex::IndexSpace(global_index), Some(global_type)).map(|_| true).unwrap_or(false),
|
||||
_ => false,
|
||||
},
|
||||
&ExportEntryType::Function(ref required_type) => match e.internal() {
|
||||
&Internal::Function(function_index) =>
|
||||
&ExportEntryType::Function(ref required_type) => match i {
|
||||
&&Internal::Function(function_index) =>
|
||||
self.function_type(ItemIndex::IndexSpace(function_index))
|
||||
.map(|ft| ft == *required_type)
|
||||
.unwrap_or(false),
|
||||
_ => false,
|
||||
},
|
||||
})
|
||||
.map(|e| *e.internal())
|
||||
.map(|i| *i)
|
||||
.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> {
|
||||
/// Get return type of this function.
|
||||
pub fn return_type(&self) -> Option<ValueType> {
|
||||
match self {
|
||||
&FunctionSignature::Module(ft) => ft.return_type(),
|
||||
@ -660,6 +670,7 @@ impl<'a> FunctionSignature<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get parameters of this function.
|
||||
pub fn params(&self) -> &[ValueType] {
|
||||
match self {
|
||||
&FunctionSignature::Module(ft) => ft.params(),
|
||||
|
@ -97,6 +97,7 @@ fn global_get_set() {
|
||||
let program = ProgramInstance::new().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(58));
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn env_native_export_entry_type_check() {
|
||||
let program = ProgramInstance::new().unwrap();
|
||||
|
Loading…
x
Reference in New Issue
Block a user