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.
This commit is contained in:
Alex Crichton 2018-08-20 23:33:29 -07:00
parent 8ce7465bba
commit 335c0b1ab6
3 changed files with 53 additions and 15 deletions

View File

@ -43,6 +43,7 @@ pub struct Context<'a> {
pub exported_classes: HashMap<String, ExportedClass>,
pub function_table_needed: bool,
pub interpreter: &'a mut Interpreter,
pub memory_init: Option<ResizableLimits>,
}
#[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> {

View File

@ -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 {

View File

@ -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;
}