From 335c0b1ab6d27b9b578de8eb5fb9696025220c4a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 20 Aug 2018 23:33:29 -0700 Subject: [PATCH] Add support for modules importing memory The default of Rust wasm binaries is to export the memory that they contain, but LLD also supports an `--import-memory` option where memory is imported into a module instead. It's looking like importing memory is along the lines of how shared memory wasm modules will work (they'll all import the same memory). This commit adds support to wasm-bindgen to support modules which import memory. Memory accessors are tweaked to no longer always assume that the wasm module exports its memory. Additionally JS bindings will create a `memory` option automatically because LLD always imports memory from an `env` module which won't actually exist. --- crates/cli-support/src/js/mod.rs | 54 ++++++++++++++++++++++++++-- crates/cli-support/src/lib.rs | 1 + crates/cli-support/src/wasm2es6js.rs | 13 ------- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index a1986c35..efedf96e 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -43,6 +43,7 @@ pub struct Context<'a> { pub exported_classes: HashMap, pub function_table_needed: bool, pub interpreter: &'a mut Interpreter, + pub memory_init: Option, } #[derive(Default)] @@ -377,6 +378,7 @@ impl<'a> Context<'a> { )) })?; + self.create_memory_export(); self.unexport_unused_internal_exports(); self.gc()?; @@ -685,6 +687,20 @@ impl<'a> Context<'a> { } } + fn create_memory_export(&mut self) { + let limits = match self.memory_init.clone() { + Some(limits) => limits, + None => return, + }; + let mut initializer = String::from("new WebAssembly.Memory({"); + initializer.push_str(&format!("initial:{}", limits.initial())); + if let Some(max) = limits.maximum() { + initializer.push_str(&format!(",maximum:{}", max)); + } + initializer.push_str("})"); + self.export("memory", &initializer, None); + } + fn rewrite_imports(&mut self, module_name: &str) { for (name, contents) in self._rewrite_imports(module_name) { self.export(&name, &contents, None); @@ -715,6 +731,15 @@ impl<'a> Context<'a> { continue; } + // If memory is imported we'll have exported it from the shim module + // so let's import it from there. + if import.field() == "memory" { + import.module_mut().truncate(0); + import.module_mut().push_str("./"); + import.module_mut().push_str(module_name); + continue + } + let renamed_import = format!("__wbindgen_{}", import.field()); let mut bind_math = |expr: &str| { math_imports.push((renamed_import.clone(), format!("function{}", expr))); @@ -1333,18 +1358,20 @@ impl<'a> Context<'a> { if !self.exposed_globals.insert(name) { return; } + let mem = self.memory(); self.global(&format!( " let cache{name} = null; function {name}() {{ - if (cache{name} === null || cache{name}.buffer !== wasm.memory.buffer) {{ - cache{name} = new {js}(wasm.memory.buffer); + if (cache{name} === null || cache{name}.buffer !== {mem}.buffer) {{ + cache{name} = new {js}({mem}.buffer); }} return cache{name}; }} ", name = name, js = js, + mem = mem, )); } @@ -1690,6 +1717,29 @@ impl<'a> Context<'a> { fn use_node_require(&self) -> bool { self.config.nodejs && !self.config.nodejs_experimental_modules } + + fn memory(&mut self) -> &'static str { + if self.module.memory_section().is_some() { + return "wasm.memory"; + } + + let (entry, mem) = self.module.import_section() + .expect("must import memory") + .entries() + .iter() + .filter_map(|i| { + match i.external() { + External::Memory(m) => Some((i, m)), + _ => None, + } + }) + .next() + .expect("must import memory"); + assert_eq!(entry.module(), "env"); + assert_eq!(entry.field(), "memory"); + self.memory_init = Some(mem.limits().clone()); + "memory" + } } impl<'a, 'b> SubContext<'a, 'b> { diff --git a/crates/cli-support/src/lib.rs b/crates/cli-support/src/lib.rs index fdb09dc6..37280354 100644 --- a/crates/cli-support/src/lib.rs +++ b/crates/cli-support/src/lib.rs @@ -200,6 +200,7 @@ impl Bindgen { module: &mut module, function_table_needed: false, interpreter: &mut instance, + memory_init: None, }; for program in programs.iter() { js::SubContext { diff --git a/crates/cli-support/src/wasm2es6js.rs b/crates/cli-support/src/wasm2es6js.rs index 9d7cda53..517ee635 100644 --- a/crates/cli-support/src/wasm2es6js.rs +++ b/crates/cli-support/src/wasm2es6js.rs @@ -150,19 +150,6 @@ impl Output { if let Some(i) = self.module.import_section() { let mut set = HashSet::new(); for entry in i.entries() { - match *entry.external() { - External::Function(_) => {} - External::Table(_) => { - bail!("wasm imports a table which isn't supported yet"); - } - External::Memory(_) => { - bail!("wasm imports memory which isn't supported yet"); - } - External::Global(_) => { - bail!("wasm imports globals which aren't supported yet"); - } - } - if !set.insert(entry.module()) { continue; }