Add support for mutable stack closures

This commit adds support for passing `&mut FnMut(..)` to JS via imports. These
closures cannot be invoked recursively in JS (they invalidate themselves while
they're being invoked) and otherwise work the same as `&Fn(..)` closures.

Closes #123
This commit is contained in:
Alex Crichton
2018-04-13 14:13:14 -07:00
parent 3305621012
commit a8d6ca3d62
6 changed files with 198 additions and 12 deletions

View File

@ -212,13 +212,14 @@ impl Descriptor {
}
}
pub fn stack_closure(&self) -> Option<&Function> {
let inner = match *self {
Descriptor::Ref(ref d) => &**d,
pub fn stack_closure(&self) -> Option<(&Function, bool)> {
let (inner, mutable) = match *self {
Descriptor::Ref(ref d) => (&**d, false),
Descriptor::RefMut(ref d) => (&**d, true),
_ => return None,
};
match *inner {
Descriptor::Function(ref f) => Some(f),
Descriptor::Function(ref f) => Some((f, mutable)),
_ => None,
}
}

View File

@ -1570,7 +1570,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
continue
}
if let Some(f) = arg.stack_closure() {
if let Some((f, mutable)) = arg.stack_closure() {
let args = (0..f.arguments.len())
.map(|i| format!("arg{}", i))
.collect::<Vec<_>>()
@ -1578,14 +1578,25 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.expose_get_global_argument();
self.cx.function_table_needed = true;
let sep = if f.arguments.len() == 0 {""} else {","};
let body = if mutable {
format!("
let a = this.a;
this.a = 0;
try {{
return this.f(a, this.b {} {});
}} finally {{
this.a = a;
}}
", sep, args)
} else {
format!("return this.f(this.a, this.b {} {});", sep, args)
};
extra.push_str(&format!("
let cb{0} = function({args}) {{
return this.f(this.a, this.b {sep} {args});
}};
let cb{0} = function({args}) {{ {body} }};
cb{0}.f = wasm.__wbg_function_table.get(arg{0});
cb{0}.a = getGlobalArgument({next_global});
cb{0}.b = getGlobalArgument({next_global} + 1);
", i, next_global = next_global, args = args, sep = sep));
", i, next_global = next_global, body = body, args = args));
next_global += 2;
finally.push_str(&format!("
cb{0}.a = cb{0}.b = 0;