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

@ -42,6 +42,12 @@ pub trait ToRefWasmBoundary: WasmDescribe {
fn to_abi_ref(&self, extra: &mut Stack) -> u32;
}
pub trait ToRefMutWasmBoundary: WasmDescribe {
type Abi: WasmAbi;
fn to_abi_ref_mut(&mut self, extra: &mut Stack) -> u32;
}
pub trait Stack {
fn push(&mut self, bits: u32);
fn pop(&mut self) -> u32;
@ -364,6 +370,61 @@ macro_rules! stack_closures {
}
}
}
impl<'a, $($var,)* R> ToRefMutWasmBoundary for FnMut($($var),*) -> R + 'a
where $($var: WasmAbi + WasmDescribe,)*
R: WasmAbi + WasmDescribe
{
type Abi = u32;
fn to_abi_ref_mut(&mut self, extra: &mut Stack) -> u32 {
#[allow(non_snake_case)]
unsafe extern fn invoke<$($var,)* R>(
a: usize,
b: usize,
$($var: $var),*
) -> R {
if a == 0 {
throw("closure invoked recursively or destroyed already");
}
let f: &mut FnMut($($var),*) -> R = mem::transmute((a, b));
f($($var),*)
}
unsafe {
let (a, b): (usize, usize) = mem::transmute(self);
extra.push(a as u32);
extra.push(b as u32);
invoke::<$($var,)* R> as u32
}
}
}
impl<'a, $($var,)*> ToRefMutWasmBoundary for FnMut($($var),*) + 'a
where $($var: WasmAbi + WasmDescribe,)*
{
type Abi = u32;
fn to_abi_ref_mut(&mut self, extra: &mut Stack) -> u32 {
#[allow(non_snake_case)]
unsafe extern fn invoke<$($var,)* >(
a: usize,
b: usize,
$($var: $var),*
) {
if a == 0 {
throw("closure invoked recursively or destroyed already");
}
let f: &mut FnMut($($var),*) = mem::transmute((a, b));
f($($var),*)
}
unsafe {
let (a, b): (usize, usize) = mem::transmute(self);
extra.push(a as u32);
extra.push(b as u32);
invoke::<$($var,)*> as u32
}
}
}
)*)
}