Merge remote-tracking branch 'upstream/master' into new

This commit is contained in:
konstin 2018-04-15 01:50:23 +02:00
commit 3999642f66
9 changed files with 442 additions and 293 deletions

View File

@ -407,7 +407,7 @@ you can do are:
```rust ```rust
#[wasm_bindgen] #[wasm_bindgen]
extern { extern {
fn foo(a: &Fn()); // must be `Fn`, not `FnMut` fn foo(a: &Fn()); // could also be `&mut FnMut()`
} }
``` ```
@ -452,7 +452,7 @@ returns, and the validity of the JS closure is tied to the lifetime of the
`Closure` in Rust. Once `Closure` is dropped it will deallocate its internal `Closure` in Rust. Once `Closure` is dropped it will deallocate its internal
memory and invalidate the corresponding JS function. memory and invalidate the corresponding JS function.
Unlike stack closures a `Closure` supports `FnMut`: Like stack closures a `Closure` also supports `FnMut`:
```rust ```rust
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;

View File

@ -58,8 +58,8 @@ pub struct ImportType {
pub struct Function { pub struct Function {
pub name: syn::Ident, pub name: syn::Ident,
pub arguments: Vec<Type>, pub arguments: Vec<syn::Type>,
pub ret: Option<Type>, pub ret: Option<syn::Type>,
pub opts: BindgenAttrs, pub opts: BindgenAttrs,
pub rust_attrs: Vec<syn::Attribute>, pub rust_attrs: Vec<syn::Attribute>,
pub rust_decl: Box<syn::FnDecl>, pub rust_decl: Box<syn::FnDecl>,
@ -80,12 +80,6 @@ pub struct Variant {
pub value: u32, pub value: u32,
} }
pub struct Type {
pub ty: syn::Type,
pub kind: TypeKind,
pub loc: TypeLocation,
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub enum TypeKind { pub enum TypeKind {
ByRef, ByRef,
@ -218,7 +212,6 @@ impl Program {
opts, opts,
method.vis.clone(), method.vis.clone(),
true, true,
false,
); );
self.exports.push(Export { self.exports.push(Export {
@ -315,7 +308,6 @@ impl Program {
opts, opts,
f.vis, f.vis,
false, false,
true,
).0; ).0;
if wasm.opts.catch() { if wasm.opts.catch() {
// TODO: this assumes a whole bunch: // TODO: this assumes a whole bunch:
@ -333,7 +325,15 @@ impl Program {
let class = wasm.arguments let class = wasm.arguments
.get(0) .get(0)
.expect("methods must have at least one argument"); .expect("methods must have at least one argument");
let class_name = match class.ty { let class = match *class {
syn::Type::Reference(syn::TypeReference {
mutability: None,
ref elem,
..
}) => &**elem,
_ => panic!("first argument of method must be a shared reference"),
};
let class_name = match *class {
syn::Type::Path(syn::TypePath { syn::Type::Path(syn::TypePath {
qself: None, qself: None,
ref path, ref path,
@ -345,11 +345,11 @@ impl Program {
ImportFunctionKind::Method { ImportFunctionKind::Method {
class: class_name.as_ref().to_string(), class: class_name.as_ref().to_string(),
ty: class.ty.clone(), ty: class.clone(),
} }
} else if wasm.opts.constructor() { } else if wasm.opts.constructor() {
let class = match wasm.ret { let class = match wasm.ret {
Some(Type { ref ty, kind: TypeKind::ByValue, .. }) => ty, Some(ref ty) => ty,
_ => panic!("constructor returns must be bare types"), _ => panic!("constructor returns must be bare types"),
}; };
let class_name = match *class { let class_name = match *class {
@ -443,7 +443,6 @@ impl Function {
opts, opts,
input.vis, input.vis,
false, false,
false,
).0 ).0
} }
@ -454,7 +453,6 @@ impl Function {
opts: BindgenAttrs, opts: BindgenAttrs,
vis: syn::Visibility, vis: syn::Visibility,
allow_self: bool, allow_self: bool,
import: bool,
) -> (Function, Option<bool>) { ) -> (Function, Option<bool>) {
if decl.variadic.is_some() { if decl.variadic.is_some() {
panic!("can't bindgen variadic functions") panic!("can't bindgen variadic functions")
@ -463,6 +461,8 @@ impl Function {
panic!("can't bindgen functions with lifetime or type parameters") panic!("can't bindgen functions with lifetime or type parameters")
} }
assert_no_lifetimes(&decl);
let mut mutable = None; let mut mutable = None;
let arguments = decl.inputs let arguments = decl.inputs
.iter() .iter()
@ -478,24 +478,12 @@ impl Function {
} }
_ => panic!("arguments cannot be `self` or ignored"), _ => panic!("arguments cannot be `self` or ignored"),
}) })
.map(|arg| { .map(|arg| arg.ty.clone())
Type::from(&arg.ty, if import {
TypeLocation::ImportArgument
} else {
TypeLocation::ExportArgument
})
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let ret = match decl.output { let ret = match decl.output {
syn::ReturnType::Default => None, syn::ReturnType::Default => None,
syn::ReturnType::Type(_, ref t) => { syn::ReturnType::Type(_, ref t) => Some((**t).clone()),
Some(Type::from(t, if import {
TypeLocation::ImportRet
} else {
TypeLocation::ExportRet
}))
}
}; };
( (
@ -674,22 +662,6 @@ impl Struct {
} }
} }
impl Type {
pub fn from(ty: &syn::Type, loc: TypeLocation) -> Type {
let (ty, kind) = match *ty {
syn::Type::Reference(ref r) => {
if r.mutability.is_some() {
((*r.elem).clone(), TypeKind::ByMutRef)
} else {
((*r.elem).clone(), TypeKind::ByRef)
}
}
_ => (ty.clone(), TypeKind::ByValue),
};
Type { loc, ty, kind }
}
}
#[derive(Default)] #[derive(Default)]
pub struct BindgenAttrs { pub struct BindgenAttrs {
attrs: Vec<BindgenAttr>, attrs: Vec<BindgenAttr>,
@ -869,16 +841,12 @@ impl syn::synom::Synom for BindgenAttr {
)); ));
} }
fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> { fn extract_first_ty_param(ty: Option<&syn::Type>) -> Option<Option<syn::Type>> {
let t = match ty { let t = match ty {
Some(t) => t, Some(t) => t,
None => return Some(None), None => return Some(None),
}; };
let ty = match *t { let path = match *t {
Type { ref ty, kind: TypeKind::ByValue, .. } => ty,
_ => return None,
};
let path = match *ty {
syn::Type::Path(syn::TypePath { syn::Type::Path(syn::TypePath {
qself: None, qself: None,
ref path, ref path,
@ -898,11 +866,7 @@ fn extract_first_ty_param(ty: Option<&Type>) -> Option<Option<Type>> {
syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Some(None), syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Some(None),
_ => {} _ => {}
} }
Some(Some(Type { Some(Some(ty.clone()))
ty: ty.clone(),
kind: TypeKind::ByValue,
loc: t.loc,
}))
} }
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> { fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> {
@ -913,3 +877,16 @@ fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<
} }
syn::parse_error() syn::parse_error()
} }
fn assert_no_lifetimes(decl: &syn::FnDecl) {
struct Walk;
impl<'ast> syn::visit::Visit<'ast> for Walk {
fn visit_lifetime(&mut self, _i: &'ast syn::Lifetime) {
panic!("it is currently not sound to use lifetimes in function \
signatures");
}
}
syn::visit::Visit::visit_fn_decl(&mut Walk, decl);
}

View File

@ -116,7 +116,7 @@ impl ToTokens for ast::Struct {
} }
} }
impl ::wasm_bindgen::convert::WasmBoundary for #name { impl ::wasm_bindgen::convert::IntoWasmAbi for #name {
type Abi = u32; type Abi = u32;
fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack)
@ -124,40 +124,44 @@ impl ToTokens for ast::Struct {
{ {
Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32 Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32
} }
}
unsafe fn from_abi(js: u32, _extra: &mut ::wasm_bindgen::convert::Stack) impl ::wasm_bindgen::convert::FromWasmAbi for #name {
type Abi = u32;
unsafe fn from_abi(js: u32, extra: &mut ::wasm_bindgen::convert::Stack)
-> Self -> Self
{ {
let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; let ptr = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>;
::wasm_bindgen::__rt::assert_not_null(js); ::wasm_bindgen::__rt::assert_not_null(ptr);
let js = Box::from_raw(js); let js = Box::from_raw(ptr);
js.borrow_mut(); // make sure no one's borrowing js.borrow_mut(); // make sure no one's borrowing
js.into_inner() js.into_inner()
} }
} }
impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { impl ::wasm_bindgen::convert::RefFromWasmAbi for #name {
type Abi = u32; type Abi = u32;
type RefAnchor = ::wasm_bindgen::__rt::Ref<'static, #name>; type Anchor = ::wasm_bindgen::__rt::Ref<'static, #name>;
unsafe fn from_abi_ref( unsafe fn ref_from_abi(
js: Self::Abi, js: Self::Abi,
_extra: &mut ::wasm_bindgen::convert::Stack, _extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self::RefAnchor { ) -> Self::Anchor {
let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>;
::wasm_bindgen::__rt::assert_not_null(js); ::wasm_bindgen::__rt::assert_not_null(js);
(*js).borrow() (*js).borrow()
} }
} }
impl ::wasm_bindgen::convert::FromRefMutWasmBoundary for #name { impl ::wasm_bindgen::convert::RefMutFromWasmAbi for #name {
type Abi = u32; type Abi = u32;
type RefAnchor = ::wasm_bindgen::__rt::RefMut<'static, #name>; type Anchor = ::wasm_bindgen::__rt::RefMut<'static, #name>;
unsafe fn from_abi_ref_mut( unsafe fn ref_mut_from_abi(
js: Self::Abi, js: Self::Abi,
_extra: &mut ::wasm_bindgen::convert::Stack, _extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self::RefAnchor { ) -> Self::Anchor {
let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>;
::wasm_bindgen::__rt::assert_not_null(js); ::wasm_bindgen::__rt::assert_not_null(js);
(*js).borrow_mut() (*js).borrow_mut()
@ -166,7 +170,7 @@ impl ToTokens for ast::Struct {
impl ::std::convert::From<#name> for ::wasm_bindgen::JsValue { impl ::std::convert::From<#name> for ::wasm_bindgen::JsValue {
fn from(value: #name) -> Self { fn from(value: #name) -> Self {
let ptr = ::wasm_bindgen::convert::WasmBoundary::into_abi( let ptr = ::wasm_bindgen::convert::IntoWasmAbi::into_abi(
value, value,
unsafe { &mut ::wasm_bindgen::convert::GlobalStack::new() }, unsafe { &mut ::wasm_bindgen::convert::GlobalStack::new() },
); );
@ -177,7 +181,7 @@ impl ToTokens for ast::Struct {
} }
unsafe { unsafe {
<::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary> <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi>
::from_abi( ::from_abi(
#new_fn(ptr), #new_fn(ptr),
&mut ::wasm_bindgen::convert::GlobalStack::new(), &mut ::wasm_bindgen::convert::GlobalStack::new(),
@ -188,7 +192,7 @@ impl ToTokens for ast::Struct {
#[no_mangle] #[no_mangle]
pub unsafe extern fn #free_fn(ptr: u32) { pub unsafe extern fn #free_fn(ptr: u32) {
<#name as ::wasm_bindgen::convert::WasmBoundary>::from_abi( <#name as ::wasm_bindgen::convert::FromWasmAbi>::from_abi(
ptr, ptr,
&mut ::wasm_bindgen::convert::GlobalStack::new(), &mut ::wasm_bindgen::convert::GlobalStack::new(),
); );
@ -220,41 +224,44 @@ impl ToTokens for ast::Export {
for (i, ty) in self.function.arguments.iter().enumerate() { for (i, ty) in self.function.arguments.iter().enumerate() {
let i = i + offset; let i = i + offset;
let ident = syn::Ident::from(format!("arg{}", i)); let ident = syn::Ident::from(format!("arg{}", i));
let t = &ty.ty; match *ty {
match ty.kind { syn::Type::Reference(syn::TypeReference {
ast::TypeKind::ByValue => { mutability: Some(_),
ref elem,
..
}) => {
args.push(quote! { args.push(quote! {
#ident: <#t as ::wasm_bindgen::convert::WasmBoundary>::Abi #ident: <#elem as ::wasm_bindgen::convert::RefMutFromWasmAbi>::Abi
}); });
arg_conversions.push(quote! { arg_conversions.push(quote! {
let #ident = unsafe { let mut #ident = unsafe {
<#t as ::wasm_bindgen::convert::WasmBoundary> <#elem as ::wasm_bindgen::convert::RefMutFromWasmAbi>
::from_abi(#ident, &mut __stack) ::ref_mut_from_abi(#ident, &mut __stack)
}; };
let #ident = &mut *#ident;
}); });
} }
ast::TypeKind::ByRef => { syn::Type::Reference(syn::TypeReference { ref elem, .. }) => {
args.push(quote! { args.push(quote! {
#ident: <#t as ::wasm_bindgen::convert::FromRefWasmBoundary>::Abi #ident: <#elem as ::wasm_bindgen::convert::RefFromWasmAbi>::Abi
}); });
arg_conversions.push(quote! { arg_conversions.push(quote! {
let #ident = unsafe { let #ident = unsafe {
<#t as ::wasm_bindgen::convert::FromRefWasmBoundary> <#elem as ::wasm_bindgen::convert::RefFromWasmAbi>
::from_abi_ref(#ident, &mut __stack) ::ref_from_abi(#ident, &mut __stack)
}; };
let #ident = &*#ident; let #ident = &*#ident;
}); });
} }
ast::TypeKind::ByMutRef => { _ => {
args.push(quote! { args.push(quote! {
#ident: <#t as ::wasm_bindgen::convert::FromRefMutWasmBoundary>::Abi #ident: <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi
}); });
arg_conversions.push(quote! { arg_conversions.push(quote! {
let mut #ident = unsafe { let #ident = unsafe {
<#t as ::wasm_bindgen::convert::FromRefMutWasmBoundary> <#ty as ::wasm_bindgen::convert::FromWasmAbi>
::from_abi_ref_mut(#ident, &mut __stack) ::from_abi(#ident, &mut __stack)
}; };
let #ident = &mut *#ident;
}); });
} }
} }
@ -263,28 +270,25 @@ impl ToTokens for ast::Export {
let ret_ty; let ret_ty;
let convert_ret; let convert_ret;
match self.function.ret { match self.function.ret {
Some(ast::Type { ref ty, kind: ast::TypeKind::ByValue, .. }) => { Some(syn::Type::Reference(_)) => panic!("can't return a borrowed ref"),
Some(ref ty) => {
ret_ty = quote! { ret_ty = quote! {
-> <#ty as ::wasm_bindgen::convert::WasmBoundary>::Abi -> <#ty as ::wasm_bindgen::convert::IntoWasmAbi>::Abi
}; };
convert_ret = quote! { convert_ret = quote! {
<#ty as ::wasm_bindgen::convert::WasmBoundary> <#ty as ::wasm_bindgen::convert::IntoWasmAbi>
::into_abi(#ret, &mut unsafe { ::into_abi(#ret, &mut unsafe {
::wasm_bindgen::convert::GlobalStack::new() ::wasm_bindgen::convert::GlobalStack::new()
}) })
}; };
} }
Some(ast::Type { kind: ast::TypeKind::ByMutRef, .. }) |
Some(ast::Type { kind: ast::TypeKind::ByRef, .. }) => {
panic!("can't return a borrowed ref");
}
None => { None => {
ret_ty = quote!{}; ret_ty = quote!{};
convert_ret = quote!{}; convert_ret = quote!{};
} }
} }
let describe_ret = match self.function.ret { let describe_ret = match self.function.ret {
Some(ast::Type { ref ty, .. }) => { Some(ref ty) => {
quote! { quote! {
inform(1); inform(1);
<#ty as WasmDescribe>::describe(); <#ty as WasmDescribe>::describe();
@ -380,47 +384,55 @@ impl ToTokens for ast::ImportType {
} }
} }
impl ::wasm_bindgen::convert::WasmBoundary for #name { impl ::wasm_bindgen::convert::IntoWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::Abi; ::wasm_bindgen::convert::IntoWasmAbi>::Abi;
fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi { fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
self.obj.into_abi(extra) self.obj.into_abi(extra)
} }
}
impl ::wasm_bindgen::convert::FromWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::FromWasmAbi>::Abi;
unsafe fn from_abi( unsafe fn from_abi(
js: Self::Abi, js: Self::Abi,
extra: &mut ::wasm_bindgen::convert::Stack, extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self { ) -> Self {
#name { obj: ::wasm_bindgen::JsValue::from_abi(js, extra) } #name {
obj: ::wasm_bindgen::JsValue::from_abi(js, extra),
}
} }
} }
impl ::wasm_bindgen::convert::ToRefWasmBoundary for #name { impl<'a> ::wasm_bindgen::convert::IntoWasmAbi for &'a #name {
type Abi = <::wasm_bindgen::JsValue as type Abi = <&'a ::wasm_bindgen::JsValue as
::wasm_bindgen::convert::ToRefWasmBoundary>::Abi; ::wasm_bindgen::convert::IntoWasmAbi>::Abi;
fn to_abi_ref(&self, extra: &mut ::wasm_bindgen::convert::Stack) -> u32 { fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
self.obj.to_abi_ref(extra) (&self.obj).into_abi(extra)
} }
} }
impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { impl ::wasm_bindgen::convert::RefFromWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::ToRefWasmBoundary>::Abi; ::wasm_bindgen::convert::RefFromWasmAbi>::Abi;
type RefAnchor = ::std::mem::ManuallyDrop<#name>; type Anchor = ::std::mem::ManuallyDrop<#name>;
unsafe fn from_abi_ref( unsafe fn ref_from_abi(
js: Self::Abi, js: Self::Abi,
extra: &mut ::wasm_bindgen::convert::Stack, extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self::RefAnchor { ) -> Self::Anchor {
let obj = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary> let tmp = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::RefFromWasmAbi>
::from_abi(js, extra); ::ref_from_abi(js, extra);
::std::mem::ManuallyDrop::new(#name { obj }) ::std::mem::ManuallyDrop::new(#name {
obj: ::std::mem::ManuallyDrop::into_inner(tmp),
})
} }
} }
impl From<::wasm_bindgen::JsValue> for #name { impl From<::wasm_bindgen::JsValue> for #name {
fn from(obj: ::wasm_bindgen::JsValue) -> #name { fn from(obj: ::wasm_bindgen::JsValue) -> #name {
#name { obj } #name { obj }
@ -478,58 +490,38 @@ impl ToTokens for ast::ImportFunction {
}); });
for (i, (ty, name)) in self.function.arguments.iter().zip(names).enumerate() { for (i, (ty, name)) in self.function.arguments.iter().zip(names).enumerate() {
let t = &ty.ty; abi_argument_names.push(name);
match ty.kind { abi_arguments.push(quote! {
ast::TypeKind::ByValue => { #name: <#ty as ::wasm_bindgen::convert::IntoWasmAbi>::Abi
abi_argument_names.push(name); });
abi_arguments.push(quote! { let var = if i == 0 && is_method {
#name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Abi quote! { self }
}); } else {
let var = if i == 0 && is_method { quote! { #name }
quote! { self } };
} else { arg_conversions.push(quote! {
quote! { #name } let #name = <#ty as ::wasm_bindgen::convert::IntoWasmAbi>
}; ::into_abi(#var, &mut __stack);
arg_conversions.push(quote! { });
let #name = <#t as ::wasm_bindgen::convert::WasmBoundary>
::into_abi(#var, &mut __stack);
});
}
ast::TypeKind::ByMutRef => panic!("urgh mut"),
ast::TypeKind::ByRef => {
abi_argument_names.push(name);
abi_arguments.push(quote! { #name: u32 });
let var = if i == 0 && is_method {
quote! { self }
} else {
quote! { #name }
};
arg_conversions.push(quote! {
let #name = <#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
::to_abi_ref(#var, &mut __stack);
});
}
}
} }
let abi_ret; let abi_ret;
let mut convert_ret; let mut convert_ret;
match self.function.ret { match self.function.ret {
Some(ast::Type { ref ty, kind: ast::TypeKind::ByValue, .. }) => { Some(syn::Type::Reference(_)) => {
panic!("cannot return references in imports yet");
}
Some(ref ty) => {
abi_ret = quote! { abi_ret = quote! {
<#ty as ::wasm_bindgen::convert::WasmBoundary>::Abi <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi
}; };
convert_ret = quote! { convert_ret = quote! {
<#ty as ::wasm_bindgen::convert::WasmBoundary> <#ty as ::wasm_bindgen::convert::FromWasmAbi>
::from_abi( ::from_abi(
#ret_ident, #ret_ident,
&mut ::wasm_bindgen::convert::GlobalStack::new(), &mut ::wasm_bindgen::convert::GlobalStack::new(),
) )
}; };
} }
Some(ast::Type { kind: ast::TypeKind::ByRef, .. }) |
Some(ast::Type { kind: ast::TypeKind::ByMutRef, .. }) => {
panic!("can't return a borrowed ref")
}
None => { None => {
abi_ret = quote! { () }; abi_ret = quote! { () };
convert_ret = quote! { () }; convert_ret = quote! { () };
@ -547,8 +539,8 @@ impl ToTokens for ast::ImportFunction {
if #exn_data[0] == 1 { if #exn_data[0] == 1 {
return Err( return Err(
< <
::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary ::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi
>::from_abi(#exn_data[1], &mut ::wasm_bindgen::convert::GlobalStack::new()), >::from_abi(#exn_data[1], &mut ::wasm_bindgen::convert::GlobalStack::new())
) )
} }
}; };
@ -656,12 +648,16 @@ impl ToTokens for ast::Enum {
} }
}); });
(quote! { (quote! {
impl ::wasm_bindgen::convert::WasmBoundary for #enum_name { impl ::wasm_bindgen::convert::IntoWasmAbi for #enum_name {
type Abi = u32; type Abi = u32;
fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) -> u32 { fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) -> u32 {
self as u32 self as u32
} }
}
impl ::wasm_bindgen::convert::FromWasmAbi for #enum_name {
type Abi = u32;
unsafe fn from_abi( unsafe fn from_abi(
js: u32, js: u32,
@ -695,13 +691,14 @@ impl ToTokens for ast::ImportStatic {
fn init() -> #ty { fn init() -> #ty {
#[wasm_import_module = "__wbindgen_placeholder__"] #[wasm_import_module = "__wbindgen_placeholder__"]
extern { extern {
fn #shim_name() -> <#ty as ::wasm_bindgen::convert::WasmBoundary>::Abi; fn #shim_name() -> <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi;
} }
unsafe { unsafe {
::wasm_bindgen::convert::WasmBoundary::from_abi( <#ty as ::wasm_bindgen::convert::FromWasmAbi>::from_abi(
#shim_name(), #shim_name(),
&mut ::wasm_bindgen::convert::GlobalStack::new(), &mut ::wasm_bindgen::convert::GlobalStack::new(),
) )
} }
} }
::wasm_bindgen::JsStatic { ::wasm_bindgen::JsStatic {
@ -712,19 +709,3 @@ impl ToTokens for ast::ImportStatic {
}).to_tokens(into); }).to_tokens(into);
} }
} }
impl ToTokens for ast::Type {
fn to_tokens(&self, into: &mut Tokens) {
match self.kind {
ast::TypeKind::ByValue => {}
ast::TypeKind::ByRef => {
syn::token::And::default().to_tokens(into);
}
ast::TypeKind::ByMutRef => {
syn::token::And::default().to_tokens(into);
syn::token::Mut::default().to_tokens(into);
}
}
self.ty.to_tokens(into);
}
}

View File

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

View File

@ -8,7 +8,7 @@ use shared;
use wasm_gc; use wasm_gc;
use super::Bindgen; use super::Bindgen;
use descriptor::{Descriptor, VectorKind}; use descriptor::{Descriptor, VectorKind, Function};
pub struct Context<'a> { pub struct Context<'a> {
pub globals: String, pub globals: String,
@ -1325,10 +1325,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
if let Some(ref class) = export.class { if let Some(ref class) = export.class {
return self.generate_export_for_class(class, export); return self.generate_export_for_class(class, export);
} }
let descriptor = self.cx.describe(&export.function.name);
let (js, ts) = self.generate_function("function", let (js, ts) = self.generate_function("function",
&export.function.name,
&export.function.name, &export.function.name,
false, false,
&export.function); descriptor.unwrap_function());
self.cx.export(&export.function.name, &js); self.cx.export(&export.function.name, &js);
self.cx.globals.push_str("\n"); self.cx.globals.push_str("\n");
self.cx.typescript.push_str("export "); self.cx.typescript.push_str("export ");
@ -1336,12 +1338,15 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.typescript.push_str("\n"); self.cx.typescript.push_str("\n");
} }
pub fn generate_export_for_class(&mut self, class_name: &str, export: &shared::Export) { pub fn generate_export_for_class(&mut self, class: &str, export: &shared::Export) {
let wasm_name = shared::struct_function_export_name(class, &export.function.name);
let descriptor = self.cx.describe(&wasm_name);
let (js, ts) = self.generate_function( let (js, ts) = self.generate_function(
"", "",
&shared::struct_function_export_name(class_name, &export.function.name), &export.function.name,
&wasm_name,
export.method, export.method,
&export.function, &descriptor.unwrap_function(),
); );
let class = self.cx.exported_classes.entry(class_name.to_string()) let class = self.cx.exported_classes.entry(class_name.to_string())
@ -1372,13 +1377,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
fn generate_function(&mut self, fn generate_function(&mut self,
prefix: &str, prefix: &str,
js_name: &str,
wasm_name: &str, wasm_name: &str,
is_method: bool, is_method: bool,
function: &shared::Function) -> (String, String) { function: &Function) -> (String, String) {
let descriptor = self.cx.describe(wasm_name);
let desc_function = descriptor.unwrap_function();
let mut dst = String::from("("); let mut dst = String::from("(");
let mut dst_ts = format!("{}(", function.name); let mut dst_ts = format!("{}(", js_name);
let mut passed_args = String::new(); let mut passed_args = String::new();
let mut arg_conversions = String::new(); let mut arg_conversions = String::new();
let mut destructors = String::new(); let mut destructors = String::new();
@ -1388,7 +1392,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
} }
let mut global_idx = 0; let mut global_idx = 0;
for (i, arg) in desc_function.arguments.iter().enumerate() { for (i, arg) in function.arguments.iter().enumerate() {
let name = format!("arg{}", i); let name = format!("arg{}", i);
if i > 0 { if i > 0 {
dst.push_str(", "); dst.push_str(", ");
@ -1487,7 +1491,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
} }
dst.push_str(")"); dst.push_str(")");
dst_ts.push_str(")"); dst_ts.push_str(")");
let convert_ret = self.cx.return_from_rust(&desc_function.ret, &mut dst_ts); let convert_ret = self.cx.return_from_rust(&function.ret, &mut dst_ts);
dst_ts.push_str(";"); dst_ts.push_str(";");
dst.push_str(" {\n "); dst.push_str(" {\n ");
dst.push_str(&arg_conversions); dst.push_str(&arg_conversions);
@ -1594,7 +1598,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
continue continue
} }
if let Some(f) = arg.stack_closure() { if let Some((f, mutable)) = arg.stack_closure() {
let args = (0..f.arguments.len()) let args = (0..f.arguments.len())
.map(|i| format!("arg{}", i)) .map(|i| format!("arg{}", i))
.collect::<Vec<_>>() .collect::<Vec<_>>()
@ -1602,14 +1606,25 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.expose_get_global_argument(); self.cx.expose_get_global_argument();
self.cx.function_table_needed = true; self.cx.function_table_needed = true;
let sep = if f.arguments.len() == 0 {""} else {","}; 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!(" extra.push_str(&format!("
let cb{0} = function({args}) {{ let cb{0} = function({args}) {{ {body} }};
return this.f(this.a, this.b {sep} {args});
}};
cb{0}.f = wasm.__wbg_function_table.get(arg{0}); cb{0}.f = wasm.__wbg_function_table.get(arg{0});
cb{0}.a = getGlobalArgument({next_global}); cb{0}.a = getGlobalArgument({next_global});
cb{0}.b = getGlobalArgument({next_global} + 1); 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; next_global += 2;
finally.push_str(&format!(" finally.push_str(&format!("
cb{0}.a = cb{0}.b = 0; cb{0}.a = cb{0}.b = 0;

View File

@ -99,7 +99,7 @@ impl<T> Closure<T>
let js = T::factory()(T::shim(), data[0], data[1]); let js = T::factory()(T::shim(), data[0], data[1]);
Closure { Closure {
_inner: t, _inner: t,
js: ManuallyDrop::new(JsValue::from_abi(js, &mut GlobalStack::new())), js: ManuallyDrop::new(JsValue { idx: js }),
} }
} }
} }
@ -117,7 +117,7 @@ impl<T> Closure<T>
/// cleanup as it can. /// cleanup as it can.
pub fn forget(self) { pub fn forget(self) {
unsafe { unsafe {
super::__wbindgen_cb_forget(self.js.to_abi_ref(&mut GlobalStack::new())); super::__wbindgen_cb_forget(self.js.idx);
mem::forget(self); mem::forget(self);
} }
} }
@ -133,13 +133,13 @@ impl<T> WasmDescribe for Closure<T>
} }
// `Closure` can only be passed by reference to imports. // `Closure` can only be passed by reference to imports.
impl<T> ToRefWasmBoundary for Closure<T> impl<'a, T> IntoWasmAbi for &'a Closure<T>
where T: WasmShim + ?Sized, where T: WasmShim + ?Sized,
{ {
type Abi = u32; type Abi = u32;
fn to_abi_ref(&self, extra: &mut Stack) -> u32 { fn into_abi(self, _extra: &mut Stack) -> u32 {
self.js.to_abi_ref(extra) self.js.idx
} }
} }
@ -148,8 +148,7 @@ impl<T> Drop for Closure<T>
{ {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
let idx = self.js.to_abi_ref(&mut GlobalStack::new()); super::__wbindgen_cb_drop(self.js.idx);
super::__wbindgen_cb_drop(idx);
} }
} }
} }

View File

@ -15,31 +15,27 @@ pub struct Descriptor {
pub __x: [u8; 4], pub __x: [u8; 4],
} }
pub trait WasmBoundary: WasmDescribe { pub trait IntoWasmAbi: WasmDescribe {
type Abi: WasmAbi; type Abi: WasmAbi;
fn into_abi(self, extra: &mut Stack) -> Self::Abi; fn into_abi(self, extra: &mut Stack) -> Self::Abi;
}
pub trait FromWasmAbi: WasmDescribe {
type Abi: WasmAbi;
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self; unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self;
} }
pub trait FromRefWasmBoundary: WasmDescribe { pub trait RefFromWasmAbi: WasmDescribe {
type Abi: WasmAbi; type Abi: WasmAbi;
type RefAnchor: Deref<Target = Self>; type Anchor: Deref<Target=Self>;
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
unsafe fn from_abi_ref(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor;
} }
pub trait FromRefMutWasmBoundary: WasmDescribe { pub trait RefMutFromWasmAbi: WasmDescribe {
type Abi: WasmAbi; type Abi: WasmAbi;
type RefAnchor: DerefMut<Target = Self>; type Anchor: DerefMut<Target=Self>;
unsafe fn ref_mut_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor;
unsafe fn from_abi_ref_mut(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor;
}
pub trait ToRefWasmBoundary: WasmDescribe {
type Abi: WasmAbi;
fn to_abi_ref(&self, extra: &mut Stack) -> u32;
} }
pub trait Stack { pub trait Stack {
@ -64,10 +60,13 @@ unsafe impl WasmAbi for f64 {}
macro_rules! simple { macro_rules! simple {
($($t:tt)*) => ($( ($($t:tt)*) => ($(
impl WasmBoundary for $t { impl IntoWasmAbi for $t {
type Abi = $t; type Abi = $t;
fn into_abi(self, _extra: &mut Stack) -> $t { self } fn into_abi(self, _extra: &mut Stack) -> $t { self }
}
impl FromWasmAbi for $t {
type Abi = $t;
unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js } unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js }
} }
)*) )*)
@ -77,10 +76,13 @@ simple!(u32 u64 i32 i64 f32 f64);
macro_rules! as_u32 { macro_rules! as_u32 {
($($t:tt)*) => ($( ($($t:tt)*) => ($(
impl WasmBoundary for $t { impl IntoWasmAbi for $t {
type Abi = u32; type Abi = u32;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
}
impl FromWasmAbi for $t {
type Abi = u32;
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t } unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t }
} }
)*) )*)
@ -88,30 +90,49 @@ macro_rules! as_u32 {
as_u32!(i8 u8 i16 u16 isize usize); as_u32!(i8 u8 i16 u16 isize usize);
impl WasmBoundary for bool { impl IntoWasmAbi for bool {
type Abi = u32; type Abi = u32;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
}
impl FromWasmAbi for bool {
type Abi = u32;
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool { js != 0 } unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool { js != 0 }
} }
impl<T> WasmBoundary for *const T { impl<T> IntoWasmAbi for *const T {
type Abi = u32; type Abi = u32;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *const T { js as *const T }
} }
impl<T> WasmBoundary for *mut T { impl<T> FromWasmAbi for *const T {
type Abi = u32;
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *const T {
js as *const T
}
}
impl<T> IntoWasmAbi for *mut T {
type Abi = u32; type Abi = u32;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *mut T { js as *mut T } }
impl<T> FromWasmAbi for *mut T {
type Abi = u32;
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *mut T {
js as *mut T
}
} }
macro_rules! vectors { macro_rules! vectors {
($($t:ident)*) => ($( ($($t:ident)*) => ($(
impl WasmBoundary for Box<[$t]> { impl IntoWasmAbi for Box<[$t]> {
type Abi = u32; type Abi = u32;
fn into_abi(self, extra: &mut Stack) -> u32 { fn into_abi(self, extra: &mut Stack) -> u32 {
@ -121,19 +142,22 @@ macro_rules! vectors {
extra.push(len as u32); extra.push(len as u32);
ptr.into_abi(extra) ptr.into_abi(extra)
} }
}
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Box<[$t]> { impl FromWasmAbi for Box<[$t]> {
type Abi = u32;
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self {
let ptr = <*mut $t>::from_abi(js, extra); let ptr = <*mut $t>::from_abi(js, extra);
let len = extra.pop() as usize; let len = extra.pop() as usize;
Vec::from_raw_parts(ptr, len, len).into_boxed_slice() Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
} }
} }
impl ToRefWasmBoundary for [$t] { impl<'a> IntoWasmAbi for &'a [$t] {
type Abi = u32; type Abi = u32;
fn to_abi_ref(&self, extra: &mut Stack) -> u32 { fn into_abi(self, extra: &mut Stack) -> u32 {
let ptr = self.as_ptr(); let ptr = self.as_ptr();
let len = self.len(); let len = self.len();
extra.push(len as u32); extra.push(len as u32);
@ -141,90 +165,73 @@ macro_rules! vectors {
} }
} }
impl FromRefWasmBoundary for [$t] { impl RefFromWasmAbi for [$t] {
type Abi = u32; type Abi = u32;
type RefAnchor = SliceAnchor<$t>; type Anchor = &'static [$t];
unsafe fn from_abi_ref(js: u32, extra: &mut Stack) -> SliceAnchor<$t> { unsafe fn ref_from_abi(js: u32, extra: &mut Stack) -> &'static [$t] {
SliceAnchor { slice::from_raw_parts(
ptr: <*mut $t>::from_abi(js, extra), <*const $t>::from_abi(js, extra),
len: extra.pop() as usize, extra.pop() as usize,
} )
} }
} }
)*) )*)
} }
pub struct SliceAnchor<T> {
ptr: *mut T,
len: usize,
}
impl<T> Deref for SliceAnchor<T> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.ptr, self.len) }
}
}
vectors! { vectors! {
u8 i8 u16 i16 u32 i32 f32 f64 u8 i8 u16 i16 u32 i32 f32 f64
} }
impl<T> WasmBoundary for Vec<T> where Box<[T]>: WasmBoundary { impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi {
type Abi = <Box<[T]> as WasmBoundary>::Abi; type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
fn into_abi(self, extra: &mut Stack) -> Self::Abi { fn into_abi(self, extra: &mut Stack) -> Self::Abi {
self.into_boxed_slice().into_abi(extra) self.into_boxed_slice().into_abi(extra)
} }
}
impl<T> FromWasmAbi for Vec<T> where Box<[T]>: FromWasmAbi {
type Abi = <Box<[T]> as FromWasmAbi>::Abi;
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self { unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
<Box<[T]>>::from_abi(js, extra).into() <Box<[T]>>::from_abi(js, extra).into()
} }
} }
impl WasmBoundary for String { impl IntoWasmAbi for String {
type Abi = u32; type Abi = u32;
fn into_abi(self, extra: &mut Stack) -> u32 { fn into_abi(self, extra: &mut Stack) -> u32 {
self.into_bytes().into_abi(extra) self.into_bytes().into_abi(extra)
} }
}
unsafe fn from_abi(js: u32, extra: &mut Stack) -> String { impl FromWasmAbi for String {
String::from_utf8_unchecked(Vec::from_abi(js, extra)) type Abi = u32;
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self {
String::from_utf8_unchecked(<Vec<u8>>::from_abi(js, extra))
} }
} }
impl ToRefWasmBoundary for str { impl<'a> IntoWasmAbi for &'a str {
type Abi = <[u8] as ToRefWasmBoundary>::Abi; type Abi = <&'a [u8] as IntoWasmAbi>::Abi;
fn to_abi_ref(&self, extra: &mut Stack) -> Self::Abi { fn into_abi(self, extra: &mut Stack) -> Self::Abi {
self.as_bytes().to_abi_ref(extra) self.as_bytes().into_abi(extra)
} }
} }
impl FromRefWasmBoundary for str { impl RefFromWasmAbi for str {
type Abi = <[u8] as ToRefWasmBoundary>::Abi; type Abi = <[u8] as RefFromWasmAbi>::Abi;
type RefAnchor = StrAnchor; type Anchor = &'static str;
unsafe fn from_abi_ref(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor { unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
StrAnchor { inner: <[u8]>::from_abi_ref(js, extra) } str::from_utf8_unchecked(<[u8]>::ref_from_abi(js, extra))
} }
} }
pub struct StrAnchor { impl IntoWasmAbi for JsValue {
inner: SliceAnchor<u8>,
}
impl Deref for StrAnchor {
type Target = str;
fn deref(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.inner) }
}
}
impl WasmBoundary for JsValue {
type Abi = u32; type Abi = u32;
fn into_abi(self, _extra: &mut Stack) -> u32 { fn into_abi(self, _extra: &mut Stack) -> u32 {
@ -232,29 +239,33 @@ impl WasmBoundary for JsValue {
mem::forget(self); mem::forget(self);
return ret return ret
} }
}
impl FromWasmAbi for JsValue {
type Abi = u32;
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue { unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue {
JsValue { idx: js } JsValue { idx: js }
} }
} }
impl ToRefWasmBoundary for JsValue { impl<'a> IntoWasmAbi for &'a JsValue {
type Abi = u32; type Abi = u32;
fn to_abi_ref(&self, _extra: &mut Stack) -> u32 { fn into_abi(self, _extra: &mut Stack) -> u32 {
self.idx self.idx
} }
} }
impl FromRefWasmBoundary for JsValue { impl RefFromWasmAbi for JsValue {
type Abi = u32; type Abi = u32;
type RefAnchor = ManuallyDrop<JsValue>; type Anchor = ManuallyDrop<JsValue>;
unsafe fn from_abi_ref(js: u32, _extra: &mut Stack) -> ManuallyDrop<JsValue> { unsafe fn ref_from_abi(js: u32, _extra: &mut Stack) -> Self::Anchor {
ManuallyDrop::new(JsValue { idx: js }) ManuallyDrop::new(JsValue { idx: js })
} }
} }
impl WasmBoundary for Box<[JsValue]> { impl IntoWasmAbi for Box<[JsValue]> {
type Abi = u32; type Abi = u32;
fn into_abi(self, extra: &mut Stack) -> u32 { fn into_abi(self, extra: &mut Stack) -> u32 {
@ -264,8 +275,12 @@ impl WasmBoundary for Box<[JsValue]> {
extra.push(len as u32); extra.push(len as u32);
ptr.into_abi(extra) ptr.into_abi(extra)
} }
}
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Box<[JsValue]> { impl FromWasmAbi for Box<[JsValue]> {
type Abi = u32;
unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self {
let ptr = <*mut JsValue>::from_abi(js, extra); let ptr = <*mut JsValue>::from_abi(js, extra);
let len = extra.pop() as usize; let len = extra.pop() as usize;
Vec::from_raw_parts(ptr, len, len).into_boxed_slice() Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
@ -310,13 +325,13 @@ pub unsafe extern fn __wbindgen_global_argument_ptr() -> *mut u32 {
macro_rules! stack_closures { macro_rules! stack_closures {
($( ($($var:ident)*) )*) => ($( ($( ($($var:ident)*) )*) => ($(
impl<'a, $($var,)* R> ToRefWasmBoundary for Fn($($var),*) -> R + 'a impl<'a, $($var,)* R> IntoWasmAbi for &'a (Fn($($var),*) -> R + 'a)
where $($var: WasmAbi + WasmDescribe,)* where $($var: WasmAbi + WasmDescribe,)*
R: WasmAbi + WasmDescribe R: WasmAbi + WasmDescribe
{ {
type Abi = u32; type Abi = u32;
fn to_abi_ref(&self, extra: &mut Stack) -> u32 { fn into_abi(self, extra: &mut Stack) -> u32 {
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern fn invoke<$($var,)* R>( unsafe extern fn invoke<$($var,)* R>(
a: usize, a: usize,
@ -338,12 +353,12 @@ macro_rules! stack_closures {
} }
} }
impl<'a, $($var,)*> ToRefWasmBoundary for Fn($($var),*) + 'a impl<'a, $($var,)*> IntoWasmAbi for &'a (Fn($($var),*) + 'a)
where $($var: WasmAbi + WasmDescribe,)* where $($var: WasmAbi + WasmDescribe,)*
{ {
type Abi = u32; type Abi = u32;
fn to_abi_ref(&self, extra: &mut Stack) -> u32 { fn into_abi(self, extra: &mut Stack) -> u32 {
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe extern fn invoke<$($var,)* >( unsafe extern fn invoke<$($var,)* >(
a: usize, a: usize,
@ -364,6 +379,61 @@ macro_rules! stack_closures {
} }
} }
} }
impl<'a, $($var,)* R> IntoWasmAbi for &'a mut (FnMut($($var),*) -> R + 'a)
where $($var: WasmAbi + WasmDescribe,)*
R: WasmAbi + WasmDescribe
{
type Abi = u32;
fn into_abi(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,)*> IntoWasmAbi for &'a mut (FnMut($($var),*) + 'a)
where $($var: WasmAbi + WasmDescribe,)*
{
type Abi = u32;
fn into_abi(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
}
}
}
)*) )*)
} }

View File

@ -13,7 +13,7 @@ use std::cell::UnsafeCell;
use std::ops::Deref; use std::ops::Deref;
use std::ptr; use std::ptr;
use convert::WasmBoundary; use convert::FromWasmAbi;
/// A module which is typically glob imported from: /// A module which is typically glob imported from:
/// ///
@ -295,7 +295,7 @@ pub struct JsStatic<T> {
unsafe impl<T: Sync> Sync for JsStatic<T> {} unsafe impl<T: Sync> Sync for JsStatic<T> {}
unsafe impl<T: Send> Send for JsStatic<T> {} unsafe impl<T: Send> Send for JsStatic<T> {}
impl<T: WasmBoundary> Deref for JsStatic<T> { impl<T: FromWasmAbi + 'static> Deref for JsStatic<T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {
unsafe { unsafe {

View File

@ -327,3 +327,109 @@ fn long_fnmut_recursive() {
"#) "#)
.test(); .test();
} }
#[test]
fn fnmut() {
project()
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use std::cell::Cell;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "./test")]
extern {
fn call(a: &mut FnMut());
fn thread(a: &mut FnMut(u32) -> u32) -> u32;
}
#[wasm_bindgen]
pub fn run() {
let mut a = false;
call(&mut || a = true);
assert!(a);
let mut x = false;
assert_eq!(thread(&mut |a| {
x = true;
a + 1
}), 3);
assert!(x);
}
"#)
.file("test.ts", r#"
import { run } from "./out";
export function call(a: any) {
a();
}
export function thread(a: any) {
return a(2);
}
export function test() {
run();
}
"#)
.test();
}
#[test]
fn fnmut_bad() {
project()
.file("src/lib.rs", r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use std::cell::Cell;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "./test")]
extern {
fn call(a: &mut FnMut());
#[wasm_bindgen(catch)]
fn again(a: bool) -> Result<(), JsValue>;
}
#[wasm_bindgen]
pub fn run() {
let mut x = true;
let mut hits = 0;
call(&mut || {
hits += 1;
if again(hits == 1).is_err() {
return
}
x = false;
});
assert!(hits == 1);
assert!(x);
assert!(again(true).is_err());
}
"#)
.file("test.ts", r#"
import { run } from "./out";
let F: any = null;
export function call(a: any) {
F = a;
a();
}
export function again(x: boolean) {
if (x) F();
}
export function test() {
run();
}
"#)
.test();
}