mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 06:08:29 +00:00
Improved type reflection
This commit is contained in:
@@ -9,18 +9,36 @@ use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasmer_types::{ExternType, FunctionType, MemoryType};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct VMMemory {
|
||||
pub(crate) memory: Memory,
|
||||
pub(crate) ty: MemoryType,
|
||||
}
|
||||
|
||||
impl VMMemory {
|
||||
pub(crate) fn new(memory: Memory, ty: MemoryType) -> Self {
|
||||
Self { memory, ty }
|
||||
}
|
||||
}
|
||||
|
||||
pub type VMMemory = Memory;
|
||||
#[derive(Clone)]
|
||||
pub struct VMFunction {
|
||||
pub(crate) function: Function,
|
||||
pub(crate) ty: FunctionType,
|
||||
pub(crate) environment: Option<Arc<RefCell<Box<dyn WasmerEnv>>>>,
|
||||
}
|
||||
|
||||
impl VMFunction {
|
||||
pub(crate) fn new(function: Function, environment: Option<Box<dyn WasmerEnv>>) -> Self {
|
||||
pub(crate) fn new(
|
||||
function: Function,
|
||||
ty: FunctionType,
|
||||
environment: Option<Box<dyn WasmerEnv>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
function,
|
||||
ty,
|
||||
environment: environment.map(|env| Arc::new(RefCell::new(env))),
|
||||
}
|
||||
}
|
||||
@@ -63,22 +81,38 @@ pub enum Export {
|
||||
impl Export {
|
||||
pub fn as_jsvalue(&self) -> &JsValue {
|
||||
match self {
|
||||
Export::Memory(js_wasm_memory) => js_wasm_memory.as_ref(),
|
||||
Export::Memory(js_wasm_memory) => js_wasm_memory.memory.as_ref(),
|
||||
Export::Function(js_func) => js_func.function.as_ref(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsValue> for Export {
|
||||
fn from(val: JsValue) -> Export {
|
||||
if val.is_instance_of::<Memory>() {
|
||||
return Export::Memory(val.unchecked_into::<Memory>());
|
||||
impl From<(JsValue, ExternType)> for Export {
|
||||
fn from((val, extern_type): (JsValue, ExternType)) -> Export {
|
||||
match extern_type {
|
||||
ExternType::Memory(memory_type) => {
|
||||
if val.is_instance_of::<Memory>() {
|
||||
return Export::Memory(VMMemory::new(
|
||||
val.unchecked_into::<Memory>(),
|
||||
memory_type,
|
||||
));
|
||||
} else {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
}
|
||||
}
|
||||
ExternType::Function(function_type) => {
|
||||
if val.is_instance_of::<Function>() {
|
||||
return Export::Function(VMFunction::new(
|
||||
val.unchecked_into::<Function>(),
|
||||
function_type,
|
||||
None,
|
||||
));
|
||||
} else {
|
||||
panic!("Extern type doesn't match js value type");
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
// Leave this last
|
||||
else if val.is_instance_of::<Function>() {
|
||||
return Export::Function(VMFunction::new(val.unchecked_into::<Function>(), None));
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
37
lib/js-api/src/externals/function.rs
vendored
37
lib/js-api/src/externals/function.rs
vendored
@@ -2,7 +2,7 @@ use crate::exports::{ExportError, Exportable};
|
||||
use crate::externals::Extern;
|
||||
use crate::store::Store;
|
||||
use crate::types::{AsJs /* ValFuncRef */, Val};
|
||||
use crate::FunctionType;
|
||||
use crate::{FunctionType, ValType};
|
||||
use core::any::Any;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::{JsCast, __rt::WasmRefCell};
|
||||
@@ -45,7 +45,6 @@ pub struct VMFunctionBody(u8);
|
||||
// #[derive(PartialEq)]
|
||||
pub struct Function {
|
||||
pub(crate) store: Store,
|
||||
ty: FunctionType,
|
||||
pub(crate) exported: VMFunction,
|
||||
}
|
||||
|
||||
@@ -175,8 +174,7 @@ impl Function {
|
||||
// let environment: Option<Arc>
|
||||
Self {
|
||||
store: store.clone(),
|
||||
ty: ty.into(),
|
||||
exported: VMFunction::new(func, None),
|
||||
exported: VMFunction::new(func, ty.into(), None),
|
||||
}
|
||||
// Function::new
|
||||
// let wrapped_func =
|
||||
@@ -314,8 +312,7 @@ impl Function {
|
||||
let ty = FunctionType::new(Args::wasm_types(), Rets::wasm_types());
|
||||
Self {
|
||||
store: store.clone(),
|
||||
ty,
|
||||
exported: VMFunction::new(binded_func, None),
|
||||
exported: VMFunction::new(binded_func, ty, None),
|
||||
}
|
||||
|
||||
// let vmctx = VMFunctionEnvironment {
|
||||
@@ -396,8 +393,7 @@ impl Function {
|
||||
// panic!("Function env {:?}", environment.type_id());
|
||||
Self {
|
||||
store: store.clone(),
|
||||
ty,
|
||||
exported: VMFunction::new(binded_func, Some(environment)),
|
||||
exported: VMFunction::new(binded_func, ty, Some(environment)),
|
||||
}
|
||||
|
||||
// let function = inner::Function::<Args, Rets>::new(func);
|
||||
@@ -443,8 +439,7 @@ impl Function {
|
||||
/// assert_eq!(f.ty().results(), vec![Type::I32]);
|
||||
/// ```
|
||||
pub fn ty(&self) -> &FunctionType {
|
||||
unimplemented!();
|
||||
// &self.exported.vm_function.signature
|
||||
&self.exported.ty
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Function` belongs.
|
||||
@@ -599,8 +594,25 @@ impl Function {
|
||||
}
|
||||
let result =
|
||||
js_sys::Reflect::apply(&self.exported.function, &wasm_bindgen::JsValue::NULL, &arr);
|
||||
// Ok(vec![Val::F64(result.unwrap().as_f64().unwrap())].into_boxed_slice())
|
||||
Ok(Box::new([]))
|
||||
|
||||
let result_types = self.exported.ty.results();
|
||||
match result_types.len() {
|
||||
0 => Ok(Box::new([])),
|
||||
1 => {
|
||||
let num_value = result.unwrap().as_f64().unwrap();
|
||||
let value = match result_types[0] {
|
||||
ValType::I32 => Val::I32(num_value as _),
|
||||
ValType::I64 => Val::I64(num_value as _),
|
||||
ValType::F32 => Val::F32(num_value as _),
|
||||
ValType::F64 => Val::F64(num_value),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
Ok(vec![value].into_boxed_slice())
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
//
|
||||
|
||||
// if let Some(trampoline) = self.exported.vm_function.call_trampoline {
|
||||
// let mut results = vec![Val::null(); self.result_arity()];
|
||||
// self.call_wasm(trampoline, params, &mut results)?;
|
||||
@@ -613,7 +625,6 @@ impl Function {
|
||||
pub(crate) fn from_vm_export(store: &Store, wasmer_export: VMFunction) -> Self {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
ty: FunctionType::new(vec![], vec![]),
|
||||
exported: wasmer_export,
|
||||
}
|
||||
}
|
||||
|
||||
20
lib/js-api/src/externals/memory.rs
vendored
20
lib/js-api/src/externals/memory.rs
vendored
@@ -49,7 +49,6 @@ extern "C" {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Memory {
|
||||
store: Store,
|
||||
ty: MemoryType,
|
||||
vm_memory: VMMemory,
|
||||
}
|
||||
|
||||
@@ -75,11 +74,11 @@ impl Memory {
|
||||
}
|
||||
js_sys::Reflect::set(&descriptor, &"shared".into(), &ty.shared.into());
|
||||
|
||||
let memory = VMMemory::new(&descriptor).unwrap();
|
||||
// unimplemented!();
|
||||
let js_memory = js_sys::WebAssembly::Memory::new(&descriptor).unwrap();
|
||||
|
||||
let memory = VMMemory::new(js_memory, ty);
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
ty,
|
||||
vm_memory: memory,
|
||||
})
|
||||
}
|
||||
@@ -98,9 +97,7 @@ impl Memory {
|
||||
/// assert_eq!(m.ty(), mt);
|
||||
/// ```
|
||||
pub fn ty(&self) -> MemoryType {
|
||||
self.ty.clone()
|
||||
// unimplemented!();
|
||||
// self.vm_memory.from.ty()
|
||||
self.vm_memory.ty.clone()
|
||||
}
|
||||
|
||||
/// Returns the [`Store`] where the `Memory` belongs.
|
||||
@@ -158,7 +155,7 @@ impl Memory {
|
||||
|
||||
/// Returns the size (in bytes) of the `Memory`.
|
||||
pub fn data_size(&self) -> u64 {
|
||||
let bytes = js_sys::Reflect::get(&self.vm_memory.buffer(), &"byteLength".into())
|
||||
let bytes = js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into())
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap() as u64;
|
||||
@@ -180,7 +177,7 @@ impl Memory {
|
||||
/// assert_eq!(m.size(), Pages(1));
|
||||
/// ```
|
||||
pub fn size(&self) -> Pages {
|
||||
let bytes = js_sys::Reflect::get(&self.vm_memory.buffer(), &"byteLength".into())
|
||||
let bytes = js_sys::Reflect::get(&self.vm_memory.memory.buffer(), &"byteLength".into())
|
||||
.unwrap()
|
||||
.as_f64()
|
||||
.unwrap() as u64;
|
||||
@@ -225,7 +222,7 @@ impl Memory {
|
||||
let pages = delta.into();
|
||||
// let new_pages = js_memory_grow(&self.vm_memory.unchecked_into::<JSMemory>(), pages.0).unwrap();
|
||||
// let new_pages = self.vm_memory.unchecked_ref::<JSMemory>().grow(pages.0);
|
||||
let new_pages = self.vm_memory.grow(pages.0);
|
||||
let new_pages = self.vm_memory.memory.grow(pages.0);
|
||||
Ok(Pages(new_pages))
|
||||
}
|
||||
|
||||
@@ -271,13 +268,12 @@ impl Memory {
|
||||
|
||||
/// example view
|
||||
pub fn uint8view(&self) -> js_sys::Uint8Array {
|
||||
js_sys::Uint8Array::new(&self.vm_memory.buffer())
|
||||
js_sys::Uint8Array::new(&self.vm_memory.memory.buffer())
|
||||
}
|
||||
|
||||
pub(crate) fn from_vm_export(store: &Store, vm_memory: VMMemory) -> Self {
|
||||
Self {
|
||||
store: store.clone(),
|
||||
ty: MemoryType::new(Pages(1), None, false),
|
||||
vm_memory,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::export::Export;
|
||||
use crate::exports::Exports;
|
||||
use crate::externals::Extern;
|
||||
use crate::module::Module;
|
||||
@@ -117,8 +118,10 @@ impl Instance {
|
||||
.exports()
|
||||
.map(|export_type| {
|
||||
let name = export_type.name();
|
||||
let export = js_sys::Reflect::get(&instance_exports, &name.into()).unwrap();
|
||||
let extern_ = Extern::from_vm_export(store, export.into());
|
||||
let extern_type = export_type.ty().clone();
|
||||
let js_export = js_sys::Reflect::get(&instance_exports, &name.into()).unwrap();
|
||||
let export: Export = (js_export, extern_type).into();
|
||||
let extern_ = Extern::from_vm_export(store, export);
|
||||
(name.to_string(), extern_)
|
||||
})
|
||||
.collect::<Exports>();
|
||||
|
||||
@@ -313,7 +313,7 @@ pub use crate::externals::{
|
||||
};
|
||||
pub use crate::import_object::{ImportObject, ImportObjectIterator, LikeNamespace};
|
||||
pub use crate::instance::{Instance, InstantiationError};
|
||||
pub use crate::module::Module;
|
||||
pub use crate::module::{Module, ModuleTypeHints};
|
||||
pub use wasm_bindgen::JsValue as RuntimeError;
|
||||
// pub use crate::native::NativeFunc;
|
||||
pub use crate::ptr::{Array, Item, WasmPtr};
|
||||
|
||||
@@ -30,6 +30,23 @@ pub enum IoCompileError {
|
||||
Compile(#[from] CompileError),
|
||||
}
|
||||
|
||||
/// WebAssembly in the browser doesn't yet output the descriptor/types
|
||||
/// corresponding to each extern (import and export).
|
||||
///
|
||||
/// This should be fixed once the JS-Types Wasm proposal is adopted
|
||||
/// by the browsers:
|
||||
/// https://github.com/WebAssembly/js-types/blob/master/proposals/js-types/Overview.md
|
||||
///
|
||||
/// Until that happens, we annotate the module with the expected
|
||||
/// types so we can built on top of them at runtime.
|
||||
#[derive(Clone)]
|
||||
pub struct ModuleTypeHints {
|
||||
/// The type hints for the imported types
|
||||
pub imports: Vec<ExternType>,
|
||||
/// The type hints for the exported types
|
||||
pub exports: Vec<ExternType>,
|
||||
}
|
||||
|
||||
/// A WebAssembly Module contains stateless WebAssembly
|
||||
/// code that has already been compiled and can be instantiated
|
||||
/// multiple times.
|
||||
@@ -43,6 +60,8 @@ pub struct Module {
|
||||
store: Store,
|
||||
module: WebAssembly::Module,
|
||||
name: Option<String>,
|
||||
// WebAssembly type hints
|
||||
type_hints: Option<ModuleTypeHints>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
@@ -148,6 +167,7 @@ impl Module {
|
||||
Ok(Self {
|
||||
store: store.clone(),
|
||||
module,
|
||||
type_hints: None,
|
||||
name: None,
|
||||
})
|
||||
}
|
||||
@@ -352,6 +372,37 @@ impl Module {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the type hints for this module.
|
||||
///
|
||||
/// Returns an error if the hints doesn't match the shape of
|
||||
/// import or export types of the module.
|
||||
pub fn set_type_hints(&mut self, type_hints: ModuleTypeHints) -> Result<(), String> {
|
||||
let exports = WebAssembly::Module::exports(&self.module);
|
||||
// Check exports
|
||||
if exports.length() as usize != type_hints.exports.len() {
|
||||
return Err("The exports length must match the type hints lenght".to_owned());
|
||||
}
|
||||
for (i, val) in exports.iter().enumerate() {
|
||||
let kind = Reflect::get(val.as_ref(), &"kind".into())
|
||||
.unwrap()
|
||||
.as_string()
|
||||
.unwrap();
|
||||
// It is safe to unwrap as we have already checked for the exports length
|
||||
let type_hint = type_hints.exports.get(i).unwrap();
|
||||
let expected_kind = match type_hint {
|
||||
ExternType::Function(_) => "function",
|
||||
ExternType::Global(_) => "global",
|
||||
ExternType::Memory(_) => "memory",
|
||||
ExternType::Table(_) => "table",
|
||||
};
|
||||
if expected_kind != kind.as_str() {
|
||||
return Err(format!("The provided type hint for the export {} is {} which doesn't match the expected kind: {}", i, kind.as_str(), expected_kind));
|
||||
}
|
||||
}
|
||||
self.type_hints = Some(type_hints);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// /// Get the custom sections of the module given a `name`.
|
||||
// pub fn custom_sections<'a>(&'a self, name: &'a str) -> impl Iterator<Item = Arc<[u8]>> + 'a {
|
||||
// self.custom_sections
|
||||
@@ -391,7 +442,8 @@ impl Module {
|
||||
let exports = WebAssembly::Module::exports(&self.module);
|
||||
let iter = exports
|
||||
.iter()
|
||||
.map(move |val| {
|
||||
.enumerate()
|
||||
.map(move |(i, val)| {
|
||||
let field = Reflect::get(val.as_ref(), &"name".into())
|
||||
.unwrap()
|
||||
.as_string()
|
||||
@@ -400,24 +452,33 @@ impl Module {
|
||||
.unwrap()
|
||||
.as_string()
|
||||
.unwrap();
|
||||
let extern_type = match kind.as_str() {
|
||||
"function" => {
|
||||
let func_type = FunctionType::new(vec![], vec![]);
|
||||
ExternType::Function(func_type)
|
||||
let type_hint = self
|
||||
.type_hints
|
||||
.as_ref()
|
||||
.map(|hints| hints.exports.get(i).unwrap().clone());
|
||||
let extern_type = if let Some(hint) = type_hint {
|
||||
hint
|
||||
} else {
|
||||
// The default types
|
||||
match kind.as_str() {
|
||||
"function" => {
|
||||
let func_type = FunctionType::new(vec![], vec![]);
|
||||
ExternType::Function(func_type)
|
||||
}
|
||||
"global" => {
|
||||
let global_type = GlobalType::new(Type::I32, Mutability::Const);
|
||||
ExternType::Global(global_type)
|
||||
}
|
||||
"memory" => {
|
||||
let memory_type = MemoryType::new(Pages(1), None, false);
|
||||
ExternType::Memory(memory_type)
|
||||
}
|
||||
"table" => {
|
||||
let table_type = TableType::new(Type::FuncRef, 1, None);
|
||||
ExternType::Table(table_type)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
"global" => {
|
||||
let global_type = GlobalType::new(Type::I32, Mutability::Const);
|
||||
ExternType::Global(global_type)
|
||||
}
|
||||
"memory" => {
|
||||
let memory_type = MemoryType::new(Pages(1), None, false);
|
||||
ExternType::Memory(memory_type)
|
||||
}
|
||||
"table" => {
|
||||
let table_type = TableType::new(Type::FuncRef, 1, None);
|
||||
ExternType::Table(table_type)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
ExportType::new(&field, extern_type)
|
||||
})
|
||||
@@ -454,3 +515,472 @@ impl fmt::Debug for Module {
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// use anyhow::{bail, Result};
|
||||
// use std::fmt::Write;
|
||||
// use wasmparser::*;
|
||||
|
||||
// pub fn wasm_types(bytes: &[u8]) -> Result<ModuleTypes> {
|
||||
// let mut d = ModuleTypes::new(bytes);
|
||||
// d.parse()?;
|
||||
// Ok(d.dst)
|
||||
// }
|
||||
|
||||
// struct ModuleTypes<'a> {
|
||||
// bytes: &'a [u8],
|
||||
// cur: usize,
|
||||
// }
|
||||
|
||||
// #[derive(Default)]
|
||||
// struct ModuleTypesIndices {
|
||||
// funcs: u32,
|
||||
// globals: u32,
|
||||
// tables: u32,
|
||||
// memories: u32,
|
||||
// }
|
||||
|
||||
// const NBYTES: usize = 4;
|
||||
|
||||
// impl<'a> ModuleTypes<'a> {
|
||||
// fn new(bytes: &'a [u8]) -> Dump<'a> {
|
||||
// Dump {
|
||||
// bytes,
|
||||
// cur: 0,
|
||||
// nesting: 0,
|
||||
// state: String::new(),
|
||||
// dst: String::new(),
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn run(&mut self) -> Result<()> {
|
||||
// self.print_module()?;
|
||||
// assert_eq!(self.cur, self.bytes.len());
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn print_module(&mut self) -> Result<()> {
|
||||
// let mut stack = Vec::new();
|
||||
// let mut i = ModuleTypesIndices::default();
|
||||
// self.nesting += 1;
|
||||
|
||||
// for item in Parser::new(0).parse_all(self.bytes) {
|
||||
// match item? {
|
||||
// Payload::Version { num, range } => {
|
||||
// write!(self.state, "version {}", num)?;
|
||||
// self.print(range.end)?;
|
||||
// }
|
||||
// Payload::TypeSection(s) => self.section(s, "type", |me, end, t| {
|
||||
// write!(me.state, "[type {}] {:?}", i.types, t)?;
|
||||
// i.types += 1;
|
||||
// me.print(end)
|
||||
// })?,
|
||||
// Payload::ImportSection(s) => self.section(s, "import", |me, end, imp| {
|
||||
// write!(me.state, "import ")?;
|
||||
// match imp.ty {
|
||||
// ImportSectionEntryType::Function(_) => {
|
||||
// write!(me.state, "[func {}]", i.funcs)?;
|
||||
// i.funcs += 1;
|
||||
// }
|
||||
// ImportSectionEntryType::Memory(_) => {
|
||||
// write!(me.state, "[memory {}]", i.memories)?;
|
||||
// i.memories += 1;
|
||||
// }
|
||||
// ImportSectionEntryType::Tag(_) => {
|
||||
// write!(me.state, "[tag {}]", i.tags)?;
|
||||
// i.tags += 1;
|
||||
// }
|
||||
// ImportSectionEntryType::Table(_) => {
|
||||
// write!(me.state, "[table {}]", i.tables)?;
|
||||
// i.tables += 1;
|
||||
// }
|
||||
// ImportSectionEntryType::Global(_) => {
|
||||
// write!(me.state, "[global {}]", i.globals)?;
|
||||
// i.globals += 1;
|
||||
// }
|
||||
// ImportSectionEntryType::Instance(_) => {
|
||||
// write!(me.state, "[instance {}]", i.instances)?;
|
||||
// i.instances += 1;
|
||||
// }
|
||||
// ImportSectionEntryType::Module(_) => {
|
||||
// write!(me.state, "[module {}]", i.modules)?;
|
||||
// i.modules += 1;
|
||||
// }
|
||||
// }
|
||||
// write!(me.state, " {:?}", imp)?;
|
||||
// me.print(end)
|
||||
// })?,
|
||||
// Payload::FunctionSection(s) => {
|
||||
// let mut cnt = 0;
|
||||
// self.section(s, "func", |me, end, f| {
|
||||
// write!(me.state, "[func {}] type {:?}", cnt + i.funcs, f)?;
|
||||
// cnt += 1;
|
||||
// me.print(end)
|
||||
// })?
|
||||
// }
|
||||
// Payload::TableSection(s) => self.section(s, "table", |me, end, t| {
|
||||
// write!(me.state, "[table {}] {:?}", i.tables, t)?;
|
||||
// i.tables += 1;
|
||||
// me.print(end)
|
||||
// })?,
|
||||
// Payload::MemorySection(s) => self.section(s, "memory", |me, end, m| {
|
||||
// write!(me.state, "[memory {}] {:?}", i.memories, m)?;
|
||||
// i.memories += 1;
|
||||
// me.print(end)
|
||||
// })?,
|
||||
// Payload::TagSection(s) => self.section(s, "tag", |me, end, m| {
|
||||
// write!(me.state, "[tag {}] {:?}", i.tags, m)?;
|
||||
// i.tags += 1;
|
||||
// me.print(end)
|
||||
// })?,
|
||||
// Payload::ExportSection(s) => self.section(s, "export", |me, end, e| {
|
||||
// write!(me.state, "export {:?}", e)?;
|
||||
// me.print(end)
|
||||
// })?,
|
||||
// Payload::GlobalSection(s) => self.section(s, "global", |me, _end, g| {
|
||||
// write!(me.state, "[global {}] {:?}", i.globals, g.ty)?;
|
||||
// i.globals += 1;
|
||||
// me.print(g.init_expr.get_binary_reader().original_position())?;
|
||||
// me.print_ops(g.init_expr.get_operators_reader())
|
||||
// })?,
|
||||
// Payload::AliasSection(s) => self.section(s, "alias", |me, end, a| {
|
||||
// write!(me.state, "[alias] {:?}", a)?;
|
||||
// match a {
|
||||
// Alias::InstanceExport { kind, .. } => match kind {
|
||||
// ExternalKind::Function => i.funcs += 1,
|
||||
// ExternalKind::Global => i.globals += 1,
|
||||
// ExternalKind::Module => i.modules += 1,
|
||||
// ExternalKind::Table => i.tables += 1,
|
||||
// ExternalKind::Instance => i.instances += 1,
|
||||
// ExternalKind::Memory => i.memories += 1,
|
||||
// ExternalKind::Tag => i.tags += 1,
|
||||
// ExternalKind::Type => i.types += 1,
|
||||
// },
|
||||
// Alias::OuterType { .. } => i.types += 1,
|
||||
// Alias::OuterModule { .. } => i.modules += 1,
|
||||
// }
|
||||
// me.print(end)
|
||||
// })?,
|
||||
// Payload::InstanceSection(s) => {
|
||||
// self.section(s, "instance", |me, _end, instance| {
|
||||
// write!(
|
||||
// me.state,
|
||||
// "[instance {}] instantiate module:{}",
|
||||
// i.instances,
|
||||
// instance.module()
|
||||
// )?;
|
||||
// me.print(instance.original_position())?;
|
||||
// i.instances += 1;
|
||||
// me.print_iter(instance.args()?, |me, end, arg| {
|
||||
// write!(me.state, "[instantiate arg] {:?}", arg)?;
|
||||
// me.print(end)
|
||||
// })
|
||||
// })?
|
||||
// }
|
||||
// Payload::StartSection { func, range } => {
|
||||
// write!(self.state, "start section")?;
|
||||
// self.print(range.start)?;
|
||||
// write!(self.state, "start function {}", func)?;
|
||||
// self.print(range.end)?;
|
||||
// }
|
||||
// Payload::DataCountSection { count, range } => {
|
||||
// write!(self.state, "data count section")?;
|
||||
// self.print(range.start)?;
|
||||
// write!(self.state, "data count {}", count)?;
|
||||
// self.print(range.end)?;
|
||||
// }
|
||||
// Payload::ElementSection(s) => self.section(s, "element", |me, _end, i| {
|
||||
// write!(me.state, "element {:?}", i.ty)?;
|
||||
// let mut items = i.items.get_items_reader()?;
|
||||
// match i.kind {
|
||||
// ElementKind::Passive => {
|
||||
// write!(me.state, " passive, {} items", items.get_count())?;
|
||||
// }
|
||||
// ElementKind::Active {
|
||||
// table_index,
|
||||
// init_expr,
|
||||
// } => {
|
||||
// write!(me.state, " table[{}]", table_index)?;
|
||||
// me.print(init_expr.get_binary_reader().original_position())?;
|
||||
// me.print_ops(init_expr.get_operators_reader())?;
|
||||
// write!(me.state, "{} items", items.get_count())?;
|
||||
// }
|
||||
// ElementKind::Declared => {
|
||||
// write!(me.state, " declared {} items", items.get_count())?;
|
||||
// }
|
||||
// }
|
||||
// me.print(items.original_position())?;
|
||||
// for _ in 0..items.get_count() {
|
||||
// let item = items.read()?;
|
||||
// write!(me.state, "item {:?}", item)?;
|
||||
// me.print(items.original_position())?;
|
||||
// }
|
||||
// Ok(())
|
||||
// })?,
|
||||
|
||||
// Payload::DataSection(s) => self.section(s, "data", |me, end, i| {
|
||||
// match i.kind {
|
||||
// DataKind::Passive => {
|
||||
// write!(me.state, "data passive")?;
|
||||
// me.print(end - i.data.len())?;
|
||||
// }
|
||||
// DataKind::Active {
|
||||
// memory_index,
|
||||
// init_expr,
|
||||
// } => {
|
||||
// write!(me.state, "data memory[{}]", memory_index)?;
|
||||
// me.print(init_expr.get_binary_reader().original_position())?;
|
||||
// me.print_ops(init_expr.get_operators_reader())?;
|
||||
// }
|
||||
// }
|
||||
// write!(me.dst, "0x{:04x} |", me.cur)?;
|
||||
// for _ in 0..NBYTES {
|
||||
// write!(me.dst, "---")?;
|
||||
// }
|
||||
// write!(me.dst, "-| ... {} bytes of data\n", i.data.len())?;
|
||||
// me.cur = end;
|
||||
// Ok(())
|
||||
// })?,
|
||||
|
||||
// Payload::CodeSectionStart { count, range, size } => {
|
||||
// write!(self.state, "code section")?;
|
||||
// self.print(range.start)?;
|
||||
// write!(self.state, "{} count", count)?;
|
||||
// self.print(range.end - size as usize)?;
|
||||
// }
|
||||
|
||||
// Payload::CodeSectionEntry(body) => {
|
||||
// write!(
|
||||
// self.dst,
|
||||
// "============== func {} ====================\n",
|
||||
// i.funcs
|
||||
// )?;
|
||||
// i.funcs += 1;
|
||||
// write!(self.state, "size of function")?;
|
||||
// self.print(body.get_binary_reader().original_position())?;
|
||||
// let mut locals = body.get_locals_reader()?;
|
||||
// write!(self.state, "{} local blocks", locals.get_count())?;
|
||||
// self.print(locals.original_position())?;
|
||||
// for _ in 0..locals.get_count() {
|
||||
// let (amt, ty) = locals.read()?;
|
||||
// write!(self.state, "{} locals of type {:?}", amt, ty)?;
|
||||
// self.print(locals.original_position())?;
|
||||
// }
|
||||
// self.print_ops(body.get_operators_reader()?)?;
|
||||
// }
|
||||
|
||||
// Payload::ModuleSectionStart { count, range, size } => {
|
||||
// write!(self.state, "module section")?;
|
||||
// self.print(range.start)?;
|
||||
// write!(self.state, "{} count", count)?;
|
||||
// self.print(range.end - size as usize)?;
|
||||
// }
|
||||
// Payload::ModuleSectionEntry { parser: _, range } => {
|
||||
// write!(self.state, "inline module size")?;
|
||||
// self.print(range.start)?;
|
||||
// self.nesting += 1;
|
||||
// stack.push(i);
|
||||
// i = Indices::default();
|
||||
// }
|
||||
|
||||
// Payload::CustomSection {
|
||||
// name,
|
||||
// data_offset,
|
||||
// data,
|
||||
// range,
|
||||
// } => {
|
||||
// write!(self.state, "custom section")?;
|
||||
// self.print(range.start)?;
|
||||
// write!(self.state, "name: {:?}", name)?;
|
||||
// self.print(data_offset)?;
|
||||
// if name == "name" {
|
||||
// let mut iter = NameSectionReader::new(data, data_offset)?;
|
||||
// while !iter.eof() {
|
||||
// self.print_custom_name_section(iter.read()?, iter.original_position())?;
|
||||
// }
|
||||
// } else {
|
||||
// write!(self.dst, "0x{:04x} |", self.cur)?;
|
||||
// for _ in 0..NBYTES {
|
||||
// write!(self.dst, "---")?;
|
||||
// }
|
||||
// write!(self.dst, "-| ... {} bytes of data\n", data.len())?;
|
||||
// self.cur += data.len();
|
||||
// }
|
||||
// }
|
||||
// Payload::UnknownSection {
|
||||
// id,
|
||||
// range,
|
||||
// contents,
|
||||
// } => {
|
||||
// write!(self.state, "unknown section: {}", id)?;
|
||||
// self.print(range.start)?;
|
||||
// write!(self.dst, "0x{:04x} |", self.cur)?;
|
||||
// for _ in 0..NBYTES {
|
||||
// write!(self.dst, "---")?;
|
||||
// }
|
||||
// write!(self.dst, "-| ... {} bytes of data\n", contents.len())?;
|
||||
// self.cur += contents.len();
|
||||
// }
|
||||
// Payload::End => {
|
||||
// self.nesting -= 1;
|
||||
// if self.nesting > 0 {
|
||||
// i = stack.pop().unwrap();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn print_name_map(&mut self, thing: &str, n: NameMap<'_>) -> Result<()> {
|
||||
// write!(self.state, "{} names", thing)?;
|
||||
// self.print(n.original_position())?;
|
||||
// let mut map = n.get_map()?;
|
||||
// write!(self.state, "{} count", map.get_count())?;
|
||||
// self.print(map.original_position())?;
|
||||
// for _ in 0..map.get_count() {
|
||||
// write!(self.state, "{:?}", map.read()?)?;
|
||||
// self.print(map.original_position())?;
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn print_indirect_name_map(
|
||||
// &mut self,
|
||||
// thing_a: &str,
|
||||
// thing_b: &str,
|
||||
// n: IndirectNameMap<'_>,
|
||||
// ) -> Result<()> {
|
||||
// write!(self.state, "{} names", thing_b)?;
|
||||
// self.print(n.original_position())?;
|
||||
// let mut outer_map = n.get_indirect_map()?;
|
||||
// write!(self.state, "{} count", outer_map.get_indirect_count())?;
|
||||
// self.print(outer_map.original_position())?;
|
||||
// for _ in 0..outer_map.get_indirect_count() {
|
||||
// let inner = outer_map.read()?;
|
||||
// write!(
|
||||
// self.state,
|
||||
// "{} {} {}s",
|
||||
// thing_a, inner.indirect_index, thing_b,
|
||||
// )?;
|
||||
// self.print(inner.original_position())?;
|
||||
// let mut map = inner.get_map()?;
|
||||
// write!(self.state, "{} count", map.get_count())?;
|
||||
// self.print(map.original_position())?;
|
||||
// for _ in 0..map.get_count() {
|
||||
// write!(self.state, "{:?}", map.read()?)?;
|
||||
// self.print(map.original_position())?;
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn print_custom_name_section(&mut self, name: Name<'_>, end: usize) -> Result<()> {
|
||||
// match name {
|
||||
// Name::Module(n) => {
|
||||
// write!(self.state, "module name")?;
|
||||
// self.print(n.original_position())?;
|
||||
// write!(self.state, "{:?}", n.get_name()?)?;
|
||||
// self.print(end)?;
|
||||
// }
|
||||
// Name::Function(n) => self.print_name_map("function", n)?,
|
||||
// Name::Local(n) => self.print_indirect_name_map("function", "local", n)?,
|
||||
// Name::Label(n) => self.print_indirect_name_map("function", "label", n)?,
|
||||
// Name::Type(n) => self.print_name_map("type", n)?,
|
||||
// Name::Table(n) => self.print_name_map("table", n)?,
|
||||
// Name::Memory(n) => self.print_name_map("memory", n)?,
|
||||
// Name::Global(n) => self.print_name_map("global", n)?,
|
||||
// Name::Element(n) => self.print_name_map("element", n)?,
|
||||
// Name::Data(n) => self.print_name_map("data", n)?,
|
||||
// Name::Unknown { ty, range, .. } => {
|
||||
// write!(self.state, "unknown names: {}", ty)?;
|
||||
// self.print(range.start)?;
|
||||
// self.print(end)?;
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn section<T>(
|
||||
// &mut self,
|
||||
// iter: T,
|
||||
// name: &str,
|
||||
// print: impl FnMut(&mut Self, usize, T::Item) -> Result<()>,
|
||||
// ) -> Result<()>
|
||||
// where
|
||||
// T: SectionReader + SectionWithLimitedItems,
|
||||
// {
|
||||
// write!(self.state, "{} section", name)?;
|
||||
// self.print(iter.range().start)?;
|
||||
// self.print_iter(iter, print)
|
||||
// }
|
||||
|
||||
// fn print_iter<T>(
|
||||
// &mut self,
|
||||
// mut iter: T,
|
||||
// mut print: impl FnMut(&mut Self, usize, T::Item) -> Result<()>,
|
||||
// ) -> Result<()>
|
||||
// where
|
||||
// T: SectionReader + SectionWithLimitedItems,
|
||||
// {
|
||||
// write!(self.state, "{} count", iter.get_count())?;
|
||||
// self.print(iter.original_position())?;
|
||||
// for _ in 0..iter.get_count() {
|
||||
// let item = iter.read()?;
|
||||
// print(self, iter.original_position(), item)?;
|
||||
// }
|
||||
// if !iter.eof() {
|
||||
// bail!("too many bytes in section");
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn print_ops(&mut self, mut i: OperatorsReader) -> Result<()> {
|
||||
// while !i.eof() {
|
||||
// match i.read() {
|
||||
// Ok(op) => write!(self.state, "{:?}", op)?,
|
||||
// Err(_) => write!(self.state, "??")?,
|
||||
// }
|
||||
// self.print(i.original_position())?;
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn print(&mut self, end: usize) -> Result<()> {
|
||||
// assert!(
|
||||
// self.cur < end,
|
||||
// "{:#x} >= {:#x}\ntrying to print: {}\n{}",
|
||||
// self.cur,
|
||||
// end,
|
||||
// self.state,
|
||||
// self.dst
|
||||
// );
|
||||
// let bytes = &self.bytes[self.cur..end];
|
||||
// for _ in 0..self.nesting - 1 {
|
||||
// write!(self.dst, " ")?;
|
||||
// }
|
||||
// write!(self.dst, "0x{:04x} |", self.cur)?;
|
||||
// for (i, chunk) in bytes.chunks(NBYTES).enumerate() {
|
||||
// if i > 0 {
|
||||
// for _ in 0..self.nesting - 1 {
|
||||
// write!(self.dst, " ")?;
|
||||
// }
|
||||
// self.dst.push_str(" |");
|
||||
// }
|
||||
// for j in 0..NBYTES {
|
||||
// match chunk.get(j) {
|
||||
// Some(b) => write!(self.dst, " {:02x}", b)?,
|
||||
// None => write!(self.dst, " ")?,
|
||||
// }
|
||||
// }
|
||||
// if i == 0 {
|
||||
// self.dst.push_str(" | ");
|
||||
// self.dst.push_str(&self.state);
|
||||
// self.state.truncate(0);
|
||||
// }
|
||||
// self.dst.push_str("\n");
|
||||
// }
|
||||
// self.cur = end;
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -67,7 +67,7 @@ fn test_exported_memory() {
|
||||
#[wasm_bindgen_test]
|
||||
fn test_exported_function() {
|
||||
let store = Store::default();
|
||||
let module = Module::new(
|
||||
let mut module = Module::new(
|
||||
&store,
|
||||
br#"
|
||||
(module
|
||||
@@ -78,6 +78,13 @@ fn test_exported_function() {
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
module.set_type_hints(ModuleTypeHints {
|
||||
imports: vec![],
|
||||
exports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
});
|
||||
|
||||
let import_object = imports! {};
|
||||
let instance = Instance::new(&module, &import_object).unwrap();
|
||||
@@ -87,8 +94,12 @@ fn test_exported_function() {
|
||||
// assert_eq!(memory.data_size(), 65536);
|
||||
|
||||
let get_magic = instance.exports.get_function("get_magic").unwrap();
|
||||
assert_eq!(
|
||||
get_magic.ty().clone(),
|
||||
FunctionType::new(vec![], vec![Type::I32])
|
||||
);
|
||||
|
||||
let expected = vec![Val::F64(42.0)].into_boxed_slice();
|
||||
let expected = vec![Val::I32(42)].into_boxed_slice();
|
||||
assert_eq!(get_magic.call(&[]), Ok(expected));
|
||||
}
|
||||
|
||||
@@ -122,7 +133,7 @@ fn test_exported_function() {
|
||||
#[wasm_bindgen_test]
|
||||
fn test_imported_function_dynamic() {
|
||||
let store = Store::default();
|
||||
let module = Module::new(
|
||||
let mut module = Module::new(
|
||||
&store,
|
||||
br#"
|
||||
(module
|
||||
@@ -134,6 +145,16 @@ fn test_imported_function_dynamic() {
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
module.set_type_hints(ModuleTypeHints {
|
||||
imports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![Type::I32],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
exports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![Type::I32],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
});
|
||||
|
||||
let imported_signature = FunctionType::new(vec![Type::I32], vec![Type::I32]);
|
||||
let imported = Function::new(&store, &imported_signature, |args| {
|
||||
@@ -156,14 +177,14 @@ fn test_imported_function_dynamic() {
|
||||
|
||||
let exported = instance.exports.get_function("exported").unwrap();
|
||||
|
||||
let expected = vec![Val::F64(5.0)].into_boxed_slice();
|
||||
let expected = vec![Val::I32(5)].into_boxed_slice();
|
||||
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_imported_function_native() {
|
||||
let store = Store::default();
|
||||
let module = Module::new(
|
||||
let mut module = Module::new(
|
||||
&store,
|
||||
br#"
|
||||
(module
|
||||
@@ -175,6 +196,16 @@ fn test_imported_function_native() {
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
module.set_type_hints(ModuleTypeHints {
|
||||
imports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![Type::I32],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
exports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![Type::I32],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
});
|
||||
|
||||
fn imported_fn(arg: u32) -> u32 {
|
||||
return arg + 1;
|
||||
@@ -195,14 +226,14 @@ fn test_imported_function_native() {
|
||||
|
||||
let exported = instance.exports.get_function("exported").unwrap();
|
||||
|
||||
let expected = vec![Val::F64(5.0)].into_boxed_slice();
|
||||
let expected = vec![Val::I32(5)].into_boxed_slice();
|
||||
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_imported_function_native_with_env() {
|
||||
let store = Store::default();
|
||||
let module = Module::new(
|
||||
let mut module = Module::new(
|
||||
&store,
|
||||
br#"
|
||||
(module
|
||||
@@ -214,6 +245,16 @@ fn test_imported_function_native_with_env() {
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
module.set_type_hints(ModuleTypeHints {
|
||||
imports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![Type::I32],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
exports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![Type::I32],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
});
|
||||
|
||||
#[derive(WasmerEnv, Clone)]
|
||||
struct Env {
|
||||
@@ -239,14 +280,14 @@ fn test_imported_function_native_with_env() {
|
||||
|
||||
let exported = instance.exports.get_function("exported").unwrap();
|
||||
|
||||
let expected = vec![Val::F64(12.0)].into_boxed_slice();
|
||||
let expected = vec![Val::I32(12)].into_boxed_slice();
|
||||
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_imported_function_native_with_wasmer_env() {
|
||||
let store = Store::default();
|
||||
let module = Module::new(
|
||||
let mut module = Module::new(
|
||||
&store,
|
||||
br#"
|
||||
(module
|
||||
@@ -259,6 +300,16 @@ fn test_imported_function_native_with_wasmer_env() {
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
module.set_type_hints(ModuleTypeHints {
|
||||
imports: vec![ExternType::Function(FunctionType::new(
|
||||
vec![Type::I32],
|
||||
vec![Type::I32],
|
||||
))],
|
||||
exports: vec![
|
||||
ExternType::Function(FunctionType::new(vec![Type::I32], vec![Type::I32])),
|
||||
ExternType::Memory(MemoryType::new(Pages(1), None, false)),
|
||||
],
|
||||
});
|
||||
|
||||
#[derive(WasmerEnv, Clone)]
|
||||
struct Env {
|
||||
@@ -301,11 +352,11 @@ fn test_imported_function_native_with_wasmer_env() {
|
||||
let exported = instance.exports.get_function("exported").unwrap();
|
||||
|
||||
/// It with the provided memory
|
||||
let expected = vec![Val::F64(24.0)].into_boxed_slice();
|
||||
let expected = vec![Val::I32(24)].into_boxed_slice();
|
||||
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
|
||||
|
||||
/// It works if we update the memory
|
||||
memory.uint8view().set_index(0, 3);
|
||||
let expected = vec![Val::F64(36.0)].into_boxed_slice();
|
||||
let expected = vec![Val::I32(36)].into_boxed_slice();
|
||||
assert_eq!(exported.call(&[Val::I32(4)]), Ok(expected));
|
||||
}
|
||||
|
||||
@@ -101,11 +101,20 @@ fn exports() {
|
||||
let store = Store::default();
|
||||
let wat = r#"(module
|
||||
(func (export "func") nop)
|
||||
(memory (export "memory") 1)
|
||||
(table (export "table") 1 funcref)
|
||||
(memory (export "memory") 2)
|
||||
(table (export "table") 2 funcref)
|
||||
(global (export "global") i32 (i32.const 0))
|
||||
)"#;
|
||||
let module = Module::new(&store, wat).unwrap();
|
||||
let mut module = Module::new(&store, wat).unwrap();
|
||||
module.set_type_hints(ModuleTypeHints {
|
||||
exports: vec![
|
||||
ExternType::Function(FunctionType::new(vec![], vec![])),
|
||||
ExternType::Memory(MemoryType::new(Pages(2), None, false)),
|
||||
ExternType::Table(TableType::new(Type::FuncRef, 2, None)),
|
||||
ExternType::Global(GlobalType::new(Type::I32, Mutability::Const)),
|
||||
],
|
||||
imports: vec![],
|
||||
});
|
||||
assert_eq!(
|
||||
module.exports().collect::<Vec<_>>(),
|
||||
vec![
|
||||
@@ -115,11 +124,11 @@ fn exports() {
|
||||
),
|
||||
ExportType::new(
|
||||
"memory",
|
||||
ExternType::Memory(MemoryType::new(Pages(1), None, false))
|
||||
ExternType::Memory(MemoryType::new(Pages(2), None, false))
|
||||
),
|
||||
ExportType::new(
|
||||
"table",
|
||||
ExternType::Table(TableType::new(Type::FuncRef, 1, None))
|
||||
ExternType::Table(TableType::new(Type::FuncRef, 2, None))
|
||||
),
|
||||
ExportType::new(
|
||||
"global",
|
||||
@@ -137,14 +146,14 @@ fn exports() {
|
||||
module.exports().memories().collect::<Vec<_>>(),
|
||||
vec![ExportType::new(
|
||||
"memory",
|
||||
MemoryType::new(Pages(1), None, false)
|
||||
MemoryType::new(Pages(2), None, false)
|
||||
),]
|
||||
);
|
||||
assert_eq!(
|
||||
module.exports().tables().collect::<Vec<_>>(),
|
||||
vec![ExportType::new(
|
||||
"table",
|
||||
TableType::new(Type::FuncRef, 1, None)
|
||||
TableType::new(Type::FuncRef, 2, None)
|
||||
),]
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
Reference in New Issue
Block a user