From 401a99a9deddcd4cf2336f3c129576fdb4929ba9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Apr 2018 22:58:35 -0700 Subject: [PATCH] Overhaul the conversion traits This commit overhauls the conversion traits used for types crossing the Rust/JS boundary. Previously there were a few ad-hoc traits but now there's only two. One is for converting JS values to Rust values, `FromWasmAbi`, and the other is for converting Rust values into JS values, `IntoWasmAbi`. These two traits are now uniformly used for all operations such as creating references and such. The `IntoWasmAbi` trait is pretty straightforward but `FromWasmAbi` is much trickier. We need to be able to create references which necessitates a good deal of funkiness with the API, but it in general should work out alright. The goal of this commit is to reduce the number of traits in play so it's more straightforward which traits are used for closures. --- crates/backend/src/ast.rs | 89 ++++------ crates/backend/src/codegen.rs | 306 ++++++++++++++++------------------ crates/cli-support/src/js.rs | 24 +-- src/closure.rs | 13 +- src/convert.rs | 250 +++++++++++++++------------ src/lib.rs | 4 +- 6 files changed, 346 insertions(+), 340 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index b29f140d871..397af170b60 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -57,8 +57,8 @@ pub struct ImportType { pub struct Function { pub name: syn::Ident, - pub arguments: Vec, - pub ret: Option, + pub arguments: Vec, + pub ret: Option, pub opts: BindgenAttrs, pub rust_attrs: Vec, pub rust_decl: Box, @@ -79,12 +79,6 @@ pub struct Variant { pub value: u32, } -pub struct Type { - pub ty: syn::Type, - pub kind: TypeKind, - pub loc: TypeLocation, -} - #[derive(Copy, Clone)] pub enum TypeKind { ByRef, @@ -210,7 +204,6 @@ impl Program { opts, method.vis, true, - false, ); self.exports.push(Export { class: Some(class), @@ -305,7 +298,6 @@ impl Program { opts, f.vis, false, - true, ).0; if wasm.opts.catch() { // TODO: this assumes a whole bunch: @@ -323,7 +315,15 @@ impl Program { let class = wasm.arguments .get(0) .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 { qself: None, ref path, @@ -335,11 +335,11 @@ impl Program { ImportFunctionKind::Method { class: class_name.as_ref().to_string(), - ty: class.ty.clone(), + ty: class.clone(), } } else if wasm.opts.constructor() { let class = match wasm.ret { - Some(Type { ref ty, kind: TypeKind::ByValue, .. }) => ty, + Some(ref ty) => ty, _ => panic!("constructor returns must be bare types"), }; let class_name = match *class { @@ -433,7 +433,6 @@ impl Function { opts, input.vis, false, - false, ).0 } @@ -444,7 +443,6 @@ impl Function { opts: BindgenAttrs, vis: syn::Visibility, allow_self: bool, - import: bool, ) -> (Function, Option) { if decl.variadic.is_some() { panic!("can't bindgen variadic functions") @@ -453,6 +451,8 @@ impl Function { panic!("can't bindgen functions with lifetime or type parameters") } + assert_no_lifetimes(&decl); + let mut mutable = None; let arguments = decl.inputs .iter() @@ -468,24 +468,12 @@ impl Function { } _ => panic!("arguments cannot be `self` or ignored"), }) - .map(|arg| { - Type::from(&arg.ty, if import { - TypeLocation::ImportArgument - } else { - TypeLocation::ExportArgument - }) - }) + .map(|arg| arg.ty.clone()) .collect::>(); let ret = match decl.output { syn::ReturnType::Default => None, - syn::ReturnType::Type(_, ref t) => { - Some(Type::from(t, if import { - TypeLocation::ImportRet - } else { - TypeLocation::ExportRet - })) - } + syn::ReturnType::Type(_, ref t) => Some((**t).clone()), }; ( @@ -663,22 +651,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)] pub struct BindgenAttrs { attrs: Vec, @@ -857,16 +829,12 @@ impl syn::synom::Synom for BindgenAttr { )); } -fn extract_first_ty_param(ty: Option<&Type>) -> Option> { +fn extract_first_ty_param(ty: Option<&syn::Type>) -> Option> { let t = match ty { Some(t) => t, None => return Some(None), }; - let ty = match *t { - Type { ref ty, kind: TypeKind::ByValue, .. } => ty, - _ => return None, - }; - let path = match *ty { + let path = match *t { syn::Type::Path(syn::TypePath { qself: None, ref path, @@ -886,11 +854,7 @@ fn extract_first_ty_param(ty: Option<&Type>) -> Option> { syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Some(None), _ => {} } - Some(Some(Type { - ty: ty.clone(), - kind: TypeKind::ByValue, - loc: t.loc, - })) + Some(Some(ty.clone())) } fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> { @@ -901,3 +865,16 @@ fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult< } 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); +} diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index cfce2a6f22f..e484ea878a6 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -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; fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) @@ -124,49 +124,66 @@ impl ToTokens for ast::Struct { { Box::into_raw(Box::new(::wasm_bindgen::__rt::WasmRefCell::new(self))) as u32 } + } + + impl ::wasm_bindgen::convert::FromWasmAbi for #name { + type Abi = u32; + type Temp = *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; - unsafe fn from_abi(js: u32, _extra: &mut ::wasm_bindgen::convert::Stack) - -> Self + unsafe fn from_abi(js: u32, extra: &mut ::wasm_bindgen::convert::Stack) + -> Self::Temp { - let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; - ::wasm_bindgen::__rt::assert_not_null(js); - let js = Box::from_raw(js); + + ::from_abi(js, extra) + } + + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + ::wasm_bindgen::__rt::assert_not_null(*temp); + let js = Box::from_raw(*temp); js.borrow_mut(); // make sure no one's borrowing js.into_inner() } } - impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { + impl<'a> ::wasm_bindgen::convert::FromWasmAbi for &'a #name { type Abi = u32; - type RefAnchor = ::wasm_bindgen::__rt::Ref<'static, #name>; + type Temp = ::wasm_bindgen::__rt::Ref<'static, #name>; - unsafe fn from_abi_ref( + unsafe fn from_abi( js: Self::Abi, _extra: &mut ::wasm_bindgen::convert::Stack, - ) -> Self::RefAnchor { + ) -> Self::Temp { let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; ::wasm_bindgen::__rt::assert_not_null(js); (*js).borrow() } + + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + &*(&**temp as *const #name) + } } - impl ::wasm_bindgen::convert::FromRefMutWasmBoundary for #name { + impl<'a> ::wasm_bindgen::convert::FromWasmAbi for &'a mut #name { type Abi = u32; - type RefAnchor = ::wasm_bindgen::__rt::RefMut<'static, #name>; + type Temp = ::wasm_bindgen::__rt::RefMut<'static, #name>; - unsafe fn from_abi_ref_mut( + unsafe fn from_abi( js: Self::Abi, _extra: &mut ::wasm_bindgen::convert::Stack, - ) -> Self::RefAnchor { + ) -> Self::Temp { let js = js as *mut ::wasm_bindgen::__rt::WasmRefCell<#name>; ::wasm_bindgen::__rt::assert_not_null(js); (*js).borrow_mut() } + + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + &mut *(&mut **temp as *mut #name) + } } impl ::std::convert::From<#name> for ::wasm_bindgen::JsValue { fn from(value: #name) -> Self { - let ptr = ::wasm_bindgen::convert::WasmBoundary::into_abi( + let ptr = ::wasm_bindgen::convert::IntoWasmAbi::into_abi( value, unsafe { &mut ::wasm_bindgen::convert::GlobalStack::new() }, ); @@ -177,21 +194,25 @@ impl ToTokens for ast::Struct { } unsafe { - <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary> - ::from_abi( - #new_fn(ptr), - &mut ::wasm_bindgen::convert::GlobalStack::new(), - ) + let mut t = + <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi> + ::from_abi( + #new_fn(ptr), + &mut ::wasm_bindgen::convert::GlobalStack::new(), + ); + <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi> + ::from_temp(&mut t) } } } #[no_mangle] pub unsafe extern fn #free_fn(ptr: u32) { - <#name as ::wasm_bindgen::convert::WasmBoundary>::from_abi( + let mut t = <#name as ::wasm_bindgen::convert::FromWasmAbi>::from_abi( ptr, &mut ::wasm_bindgen::convert::GlobalStack::new(), ); + drop(<#name as ::wasm_bindgen::convert::FromWasmAbi>::from_temp(&mut t)); } }).to_tokens(tokens); } @@ -220,71 +241,43 @@ impl ToTokens for ast::Export { for (i, ty) in self.function.arguments.iter().enumerate() { let i = i + offset; let ident = syn::Ident::from(format!("arg{}", i)); - let t = &ty.ty; - match ty.kind { - ast::TypeKind::ByValue => { - args.push(quote! { - #ident: <#t as ::wasm_bindgen::convert::WasmBoundary>::Abi - }); - arg_conversions.push(quote! { - let #ident = unsafe { - <#t as ::wasm_bindgen::convert::WasmBoundary> - ::from_abi(#ident, &mut __stack) - }; - }); - } - ast::TypeKind::ByRef => { - args.push(quote! { - #ident: <#t as ::wasm_bindgen::convert::FromRefWasmBoundary>::Abi - }); - arg_conversions.push(quote! { - let #ident = unsafe { - <#t as ::wasm_bindgen::convert::FromRefWasmBoundary> - ::from_abi_ref(#ident, &mut __stack) - }; - let #ident = &*#ident; - }); - } - ast::TypeKind::ByMutRef => { - args.push(quote! { - #ident: <#t as ::wasm_bindgen::convert::FromRefMutWasmBoundary>::Abi - }); - arg_conversions.push(quote! { - let mut #ident = unsafe { - <#t as ::wasm_bindgen::convert::FromRefMutWasmBoundary> - ::from_abi_ref_mut(#ident, &mut __stack) - }; - let #ident = &mut *#ident; - }); - } - } + args.push(quote! { + #ident: <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi + }); + arg_conversions.push(quote! { + let mut #ident = unsafe { + <#ty as ::wasm_bindgen::convert::FromWasmAbi> + ::from_abi(#ident, &mut __stack) + }; + let #ident = unsafe { + <#ty as ::wasm_bindgen::convert::FromWasmAbi> + ::from_temp(&mut #ident) + }; + }); converted_arguments.push(quote! { #ident }); } let ret_ty; let convert_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! { - -> <#ty as ::wasm_bindgen::convert::WasmBoundary>::Abi + -> <#ty as ::wasm_bindgen::convert::IntoWasmAbi>::Abi }; convert_ret = quote! { - <#ty as ::wasm_bindgen::convert::WasmBoundary> + <#ty as ::wasm_bindgen::convert::IntoWasmAbi> ::into_abi(#ret, &mut unsafe { ::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 => { ret_ty = quote!{}; convert_ret = quote!{}; } } let describe_ret = match self.function.ret { - Some(ast::Type { ref ty, .. }) => { + Some(ref ty) => { quote! { inform(1); <#ty as WasmDescribe>::describe(); @@ -380,46 +373,62 @@ 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 - ::wasm_bindgen::convert::WasmBoundary>::Abi; + ::wasm_bindgen::convert::IntoWasmAbi>::Abi; fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi { self.obj.into_abi(extra) } + } + + impl ::wasm_bindgen::convert::FromWasmAbi for #name { + type Abi = <::wasm_bindgen::JsValue as + ::wasm_bindgen::convert::FromWasmAbi>::Abi; + type Temp = <::wasm_bindgen::JsValue as + ::wasm_bindgen::convert::FromWasmAbi>::Temp; unsafe fn from_abi( js: Self::Abi, extra: &mut ::wasm_bindgen::convert::Stack, - ) -> Self { - #name { obj: ::wasm_bindgen::JsValue::from_abi(js, extra) } + ) -> Self::Temp { + ::wasm_bindgen::JsValue::from_abi(js, extra) + } + + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + #name { obj: ::wasm_bindgen::JsValue::from_temp(temp) } } } - impl ::wasm_bindgen::convert::ToRefWasmBoundary for #name { - type Abi = <::wasm_bindgen::JsValue as - ::wasm_bindgen::convert::ToRefWasmBoundary>::Abi; + impl<'a> ::wasm_bindgen::convert::IntoWasmAbi for &'a #name { + type Abi = <&'a ::wasm_bindgen::JsValue as + ::wasm_bindgen::convert::IntoWasmAbi>::Abi; - fn to_abi_ref(&self, extra: &mut ::wasm_bindgen::convert::Stack) -> u32 { - self.obj.to_abi_ref(extra) + fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi { + (&self.obj).into_abi(extra) } } - impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name { - type Abi = <::wasm_bindgen::JsValue as - ::wasm_bindgen::convert::ToRefWasmBoundary>::Abi; - type RefAnchor = ::std::mem::ManuallyDrop<#name>; + impl<'a> ::wasm_bindgen::convert::FromWasmAbi for &'a #name { + type Abi = <&'a ::wasm_bindgen::JsValue as + ::wasm_bindgen::convert::FromWasmAbi>::Abi; + type Temp = ::std::mem::ManuallyDrop<#name>; - unsafe fn from_abi_ref( + unsafe fn from_abi( js: Self::Abi, extra: &mut ::wasm_bindgen::convert::Stack, - ) -> Self::RefAnchor { - let obj = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary> + ) -> Self::Temp { + let tmp = <&::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi> ::from_abi(js, extra); - ::std::mem::ManuallyDrop::new(#name { obj }) + ::std::mem::ManuallyDrop::new(#name { + obj: ::std::mem::ManuallyDrop::into_inner(tmp), + }) } - } + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + &*(&**temp as *const #name) + } + } impl From<::wasm_bindgen::JsValue> for #name { fn from(obj: ::wasm_bindgen::JsValue) -> #name { @@ -478,64 +487,39 @@ impl ToTokens for ast::ImportFunction { }); for (i, (ty, name)) in self.function.arguments.iter().zip(names).enumerate() { - let t = &ty.ty; - match ty.kind { - ast::TypeKind::ByValue => { - abi_argument_names.push(name); - abi_arguments.push(quote! { - #name: <#t as ::wasm_bindgen::convert::WasmBoundary>::Abi - }); - let var = if i == 0 && is_method { - quote! { self } - } else { - quote! { #name } - }; - arg_conversions.push(quote! { - let #name = <#t as ::wasm_bindgen::convert::WasmBoundary> - ::into_abi(#var, &mut __stack); - }); - } - ast::TypeKind::ByMutRef => { - abi_argument_names.push(name); - abi_arguments.push(quote! { #name: u32 }); - arg_conversions.push(quote! { - let #name = <#t as ::wasm_bindgen::convert::ToRefMutWasmBoundary> - ::to_abi_ref_mut(#name, &mut __stack); - }); - } - 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); - }); - } - } + abi_argument_names.push(name); + abi_arguments.push(quote! { + #name: <#ty as ::wasm_bindgen::convert::IntoWasmAbi>::Abi + }); + let var = if i == 0 && is_method { + quote! { self } + } else { + quote! { #name } + }; + arg_conversions.push(quote! { + let #name = <#ty as ::wasm_bindgen::convert::IntoWasmAbi> + ::into_abi(#var, &mut __stack); + }); } let abi_ret; let mut convert_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! { - <#ty as ::wasm_bindgen::convert::WasmBoundary>::Abi + <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi }; - convert_ret = quote! { - <#ty as ::wasm_bindgen::convert::WasmBoundary> + convert_ret = quote! {{ + let mut tmp = <#ty as ::wasm_bindgen::convert::FromWasmAbi> ::from_abi( #ret_ident, &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") + ); + <#ty as ::wasm_bindgen::convert::FromWasmAbi> + ::from_temp(&mut tmp) + }}; } None => { abi_ret = quote! { () }; @@ -552,11 +536,14 @@ impl ToTokens for ast::ImportFunction { convert_ret = quote! { Ok(#convert_ret) }; exceptional_ret = quote! { if #exn_data[0] == 1 { - return Err( + return Err({ + let mut t = < + ::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi + >::from_abi(#exn_data[1], &mut ::wasm_bindgen::convert::GlobalStack::new()); < - ::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary - >::from_abi(#exn_data[1], &mut ::wasm_bindgen::convert::GlobalStack::new()), - ) + ::wasm_bindgen::JsValue as ::wasm_bindgen::convert::FromWasmAbi + >::from_temp(&mut t) + }) } }; quote! { @@ -663,17 +650,27 @@ impl ToTokens for ast::Enum { } }); (quote! { - impl ::wasm_bindgen::convert::WasmBoundary for #enum_name { + impl ::wasm_bindgen::convert::IntoWasmAbi for #enum_name { type Abi = u32; fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) -> u32 { self as u32 } + } + + impl ::wasm_bindgen::convert::FromWasmAbi for #enum_name { + type Abi = u32; + type Temp = u32; unsafe fn from_abi( js: u32, _extra: &mut ::wasm_bindgen::convert::Stack, - ) -> Self { + ) -> Self::Temp { + js + } + + unsafe fn from_temp(temp: &mut u32) -> Self { + let js = *temp; #(#cast_clauses else)* { wasm_bindgen::throw("invalid enum value passed") } @@ -702,13 +699,16 @@ impl ToTokens for ast::ImportStatic { fn init() -> #ty { #[wasm_import_module = "__wbindgen_placeholder__"] extern { - fn #shim_name() -> <#ty as ::wasm_bindgen::convert::WasmBoundary>::Abi; + fn #shim_name() -> <#ty as ::wasm_bindgen::convert::FromWasmAbi>::Abi; } unsafe { - ::wasm_bindgen::convert::WasmBoundary::from_abi( - #shim_name(), - &mut ::wasm_bindgen::convert::GlobalStack::new(), - ) + let mut t = + <#ty as ::wasm_bindgen::convert::FromWasmAbi>::from_abi( + #shim_name(), + &mut ::wasm_bindgen::convert::GlobalStack::new(), + ); + <#ty as ::wasm_bindgen::convert::FromWasmAbi>::from_temp(&mut t) + } } ::wasm_bindgen::JsStatic { @@ -719,19 +719,3 @@ impl ToTokens for ast::ImportStatic { }).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); - } -} diff --git a/crates/cli-support/src/js.rs b/crates/cli-support/src/js.rs index 8352295ad3f..181988fe6af 100644 --- a/crates/cli-support/src/js.rs +++ b/crates/cli-support/src/js.rs @@ -8,7 +8,7 @@ use shared; use wasm_gc; use super::Bindgen; -use descriptor::{Descriptor, VectorKind}; +use descriptor::{Descriptor, VectorKind, Function}; pub struct Context<'a> { pub globals: String, @@ -1311,10 +1311,12 @@ impl<'a, 'b> SubContext<'a, 'b> { if let Some(ref class) = export.class { return self.generate_export_for_class(class, export); } + let descriptor = self.cx.describe(&export.function.name); let (js, ts) = self.generate_function("function", + &export.function.name, &export.function.name, false, - &export.function); + descriptor.unwrap_function()); self.cx.export(&export.function.name, &js); self.cx.globals.push_str("\n"); self.cx.typescript.push_str("export "); @@ -1323,11 +1325,14 @@ impl<'a, 'b> SubContext<'a, 'b> { } 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( "", - &shared::struct_function_export_name(class, &export.function.name), + &export.function.name, + &wasm_name, export.method, - &export.function, + &descriptor.unwrap_function(), ); let class = self.cx.exported_classes.entry(class.to_string()) .or_insert(ExportedClass::default()); @@ -1344,13 +1349,12 @@ impl<'a, 'b> SubContext<'a, 'b> { fn generate_function(&mut self, prefix: &str, + js_name: &str, wasm_name: &str, is_method: bool, - function: &shared::Function) -> (String, String) { - let descriptor = self.cx.describe(wasm_name); - let desc_function = descriptor.unwrap_function(); + function: &Function) -> (String, String) { 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 arg_conversions = String::new(); let mut destructors = String::new(); @@ -1360,7 +1364,7 @@ impl<'a, 'b> SubContext<'a, 'b> { } 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); if i > 0 { dst.push_str(", "); @@ -1459,7 +1463,7 @@ impl<'a, 'b> SubContext<'a, 'b> { } dst.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.push_str(" {\n "); dst.push_str(&arg_conversions); diff --git a/src/closure.rs b/src/closure.rs index 56c1d995410..b5a99c9ff73 100644 --- a/src/closure.rs +++ b/src/closure.rs @@ -99,7 +99,7 @@ impl Closure let js = T::factory()(T::shim(), data[0], data[1]); Closure { _inner: t, - js: ManuallyDrop::new(JsValue::from_abi(js, &mut GlobalStack::new())), + js: ManuallyDrop::new(JsValue { idx: js }), } } } @@ -117,7 +117,7 @@ impl Closure /// cleanup as it can. pub fn forget(self) { unsafe { - super::__wbindgen_cb_forget(self.js.to_abi_ref(&mut GlobalStack::new())); + super::__wbindgen_cb_forget(self.js.idx); mem::forget(self); } } @@ -133,13 +133,13 @@ impl WasmDescribe for Closure } // `Closure` can only be passed by reference to imports. -impl ToRefWasmBoundary for Closure +impl<'a, T> IntoWasmAbi for &'a Closure where T: WasmShim + ?Sized, { type Abi = u32; - fn to_abi_ref(&self, extra: &mut Stack) -> u32 { - self.js.to_abi_ref(extra) + fn into_abi(self, _extra: &mut Stack) -> u32 { + self.js.idx } } @@ -148,8 +148,7 @@ impl Drop for Closure { fn drop(&mut self) { unsafe { - let idx = self.js.to_abi_ref(&mut GlobalStack::new()); - super::__wbindgen_cb_drop(idx); + super::__wbindgen_cb_drop(self.js.idx); } } } diff --git a/src/convert.rs b/src/convert.rs index 0f88f71f7f4..c5234c2c50a 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -2,7 +2,6 @@ //! at your own risk. use std::mem::{self, ManuallyDrop}; -use std::ops::{Deref, DerefMut}; use std::slice; use std::str; @@ -15,37 +14,22 @@ pub struct Descriptor { pub __x: [u8; 4], } -pub trait WasmBoundary: WasmDescribe { +pub trait IntoWasmAbi: WasmDescribe { type Abi: WasmAbi; - fn into_abi(self, extra: &mut Stack) -> Self::Abi; - unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self; -} - -pub trait FromRefWasmBoundary: WasmDescribe { - type Abi: WasmAbi; - type RefAnchor: Deref; - - unsafe fn from_abi_ref(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor; } -pub trait FromRefMutWasmBoundary: WasmDescribe { +pub trait FromWasmAbi: WasmDescribe { type Abi: WasmAbi; - type RefAnchor: DerefMut; + type Temp; - unsafe fn from_abi_ref_mut(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor; -} - -pub trait ToRefWasmBoundary: WasmDescribe { - type Abi: WasmAbi; + // SUPER UNSAFE + // + // Many types implement `FromWasmAbi` which have a lifetime parameter and + // the lifetime isn't connected to the lifetime of `js` itself. + unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Temp; + unsafe fn from_temp(temp: &mut Self::Temp) -> Self; - 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 { @@ -70,11 +54,16 @@ unsafe impl WasmAbi for f64 {} macro_rules! simple { ($($t:tt)*) => ($( - impl WasmBoundary for $t { + impl IntoWasmAbi for $t { type Abi = $t; - fn into_abi(self, _extra: &mut Stack) -> $t { self } + } + + impl FromWasmAbi for $t { + type Abi = $t; + type Temp = $t; unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js } + unsafe fn from_temp(temp: &mut $t) -> $t { *temp } } )*) } @@ -83,41 +72,71 @@ simple!(u32 u64 i32 i64 f32 f64); macro_rules! as_u32 { ($($t:tt)*) => ($( - impl WasmBoundary for $t { + impl IntoWasmAbi for $t { type Abi = u32; - fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } + } + + impl FromWasmAbi for $t { + type Abi = u32; + type Temp = $t; unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t } + unsafe fn from_temp(temp: &mut $t) -> $t { *temp } } )*) } as_u32!(i8 u8 i16 u16 isize usize); -impl WasmBoundary for bool { +impl IntoWasmAbi for bool { type Abi = u32; fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 } +} + +impl FromWasmAbi for bool { + type Abi = u32; + type Temp = bool; + unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool { js != 0 } + unsafe fn from_temp(temp: &mut bool) -> bool { *temp } } -impl WasmBoundary for *const T { +impl IntoWasmAbi for *const T { type Abi = 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 WasmBoundary for *mut T { +impl FromWasmAbi for *const T { + type Abi = u32; + type Temp = *const T; + + unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *const T { + js as *const T + } + unsafe fn from_temp(temp: &mut *const T) -> *const T { *temp } +} + +impl IntoWasmAbi for *mut T { type Abi = 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 FromWasmAbi for *mut T { + type Abi = u32; + type Temp = *mut T; + + unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *mut T { + js as *mut T + } + unsafe fn from_temp(temp: &mut *mut T) -> *mut T { *temp } } macro_rules! vectors { ($($t:ident)*) => ($( - impl WasmBoundary for Box<[$t]> { + impl IntoWasmAbi for Box<[$t]> { type Abi = u32; fn into_abi(self, extra: &mut Stack) -> u32 { @@ -127,19 +146,27 @@ macro_rules! vectors { extra.push(len as u32); ptr.into_abi(extra) } + } + + impl FromWasmAbi for Box<[$t]> { + type Abi = u32; + type Temp = (*mut $t, usize); - unsafe fn from_abi(js: u32, extra: &mut Stack) -> Box<[$t]> { + unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self::Temp { let ptr = <*mut $t>::from_abi(js, extra); let len = extra.pop() as usize; - Vec::from_raw_parts(ptr, len, len).into_boxed_slice() + (ptr, len) } + unsafe fn from_temp(temp: &mut Self::Temp) -> Box<[$t]> { + Vec::from_raw_parts(temp.0, temp.1, temp.1).into_boxed_slice() + } } - impl ToRefWasmBoundary for [$t] { + impl<'a> IntoWasmAbi for &'a [$t] { 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 len = self.len(); extra.push(len as u32); @@ -147,90 +174,86 @@ macro_rules! vectors { } } - impl FromRefWasmBoundary for [$t] { + impl<'a> FromWasmAbi for &'a [$t] { type Abi = u32; - type RefAnchor = SliceAnchor<$t>; + type Temp = &'a [$t]; - unsafe fn from_abi_ref(js: u32, extra: &mut Stack) -> SliceAnchor<$t> { - SliceAnchor { - ptr: <*mut $t>::from_abi(js, extra), - len: extra.pop() as usize, - } + unsafe fn from_abi(js: u32, extra: &mut Stack) -> &'a [$t] { + slice::from_raw_parts( + <*const $t>::from_abi(js, extra), + extra.pop() as usize, + ) } + unsafe fn from_temp(temp: &mut &'a [$t]) -> &'a [$t] { *temp } } )*) } -pub struct SliceAnchor { - ptr: *mut T, - len: usize, -} - -impl Deref for SliceAnchor { - type Target = [T]; - fn deref(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.ptr, self.len) } - } -} - vectors! { u8 i8 u16 i16 u32 i32 f32 f64 } -impl WasmBoundary for Vec where Box<[T]>: WasmBoundary { - type Abi = as WasmBoundary>::Abi; - +impl IntoWasmAbi for Vec where Box<[T]>: IntoWasmAbi { + type Abi = as IntoWasmAbi>::Abi; fn into_abi(self, extra: &mut Stack) -> Self::Abi { self.into_boxed_slice().into_abi(extra) } +} + +impl FromWasmAbi for Vec where Box<[T]>: FromWasmAbi { + type Abi = as FromWasmAbi>::Abi; + type Temp = as FromWasmAbi>::Temp; + unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Temp { + >::from_abi(js, extra) + } - unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self { - >::from_abi(js, extra).into() + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + >::from_temp(temp).into() } } -impl WasmBoundary for String { +impl IntoWasmAbi for String { type Abi = u32; fn into_abi(self, extra: &mut Stack) -> u32 { self.into_bytes().into_abi(extra) } - - unsafe fn from_abi(js: u32, extra: &mut Stack) -> String { - String::from_utf8_unchecked(Vec::from_abi(js, extra)) - } } -impl ToRefWasmBoundary for str { - type Abi = <[u8] as ToRefWasmBoundary>::Abi; +impl FromWasmAbi for String { + type Abi = u32; + type Temp = as FromWasmAbi>::Temp; - fn to_abi_ref(&self, extra: &mut Stack) -> Self::Abi { - self.as_bytes().to_abi_ref(extra) + unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self::Temp { + >::from_abi(js, extra) } -} -impl FromRefWasmBoundary for str { - type Abi = <[u8] as ToRefWasmBoundary>::Abi; - type RefAnchor = StrAnchor; - - unsafe fn from_abi_ref(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor { - StrAnchor { inner: <[u8]>::from_abi_ref(js, extra) } + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + String::from_utf8_unchecked(Vec::from_temp(temp)) } } -pub struct StrAnchor { - inner: SliceAnchor, +impl<'a> IntoWasmAbi for &'a str { + type Abi = <&'a [u8] as IntoWasmAbi>::Abi; + + fn into_abi(self, extra: &mut Stack) -> Self::Abi { + self.as_bytes().into_abi(extra) + } } -impl Deref for StrAnchor { - type Target = str; +impl<'a> FromWasmAbi for &'a str { + type Abi = <&'a [u8] as FromWasmAbi>::Abi; + type Temp = <&'a [u8] as FromWasmAbi>::Temp; - fn deref(&self) -> &str { - unsafe { str::from_utf8_unchecked(&self.inner) } + unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Temp { + <&'a [u8]>::from_abi(js, extra) + } + unsafe fn from_temp(temp: &mut Self::Temp) -> Self { + str::from_utf8_unchecked(<&'a [u8]>::from_temp(temp)) } } -impl WasmBoundary for JsValue { +impl IntoWasmAbi for JsValue { type Abi = u32; fn into_abi(self, _extra: &mut Stack) -> u32 { @@ -238,29 +261,39 @@ impl WasmBoundary for JsValue { mem::forget(self); return ret } +} - unsafe fn from_abi(js: u32, _extra: &mut Stack) -> JsValue { - JsValue { idx: js } +impl FromWasmAbi for JsValue { + type Abi = u32; + type Temp = u32; + unsafe fn from_abi(js: u32, _extra: &mut Stack) -> u32 { js } + + unsafe fn from_temp(temp: &mut u32) -> JsValue { + JsValue { idx: *temp } } } -impl ToRefWasmBoundary for JsValue { +impl<'a> IntoWasmAbi for &'a JsValue { type Abi = u32; - fn to_abi_ref(&self, _extra: &mut Stack) -> u32 { + fn into_abi(self, _extra: &mut Stack) -> u32 { self.idx } } -impl FromRefWasmBoundary for JsValue { +impl<'a> FromWasmAbi for &'a JsValue { type Abi = u32; - type RefAnchor = ManuallyDrop; + type Temp = ManuallyDrop; - unsafe fn from_abi_ref(js: u32, _extra: &mut Stack) -> ManuallyDrop { + unsafe fn from_abi(js: u32, _extra: &mut Stack) -> Self::Temp { ManuallyDrop::new(JsValue { idx: js }) } + + unsafe fn from_temp(temp: &mut ManuallyDrop) -> Self { + &*(&**temp as *const JsValue) + } } -impl WasmBoundary for Box<[JsValue]> { +impl IntoWasmAbi for Box<[JsValue]> { type Abi = u32; fn into_abi(self, extra: &mut Stack) -> u32 { @@ -270,11 +303,20 @@ impl WasmBoundary for Box<[JsValue]> { extra.push(len as u32); ptr.into_abi(extra) } +} + +impl FromWasmAbi for Box<[JsValue]> { + type Abi = u32; + type Temp = (*mut JsValue, usize); - unsafe fn from_abi(js: u32, extra: &mut Stack) -> Box<[JsValue]> { + unsafe fn from_abi(js: u32, extra: &mut Stack) -> Self::Temp { let ptr = <*mut JsValue>::from_abi(js, extra); let len = extra.pop() as usize; - Vec::from_raw_parts(ptr, len, len).into_boxed_slice() + (ptr ,len) + } + + unsafe fn from_temp(temp: &mut Self::Temp) -> Box<[JsValue]> { + Vec::from_raw_parts(temp.0, temp.1, temp.1).into_boxed_slice() } } @@ -316,13 +358,13 @@ pub unsafe extern fn __wbindgen_global_argument_ptr() -> *mut u32 { macro_rules! stack_closures { ($( ($($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,)* R: WasmAbi + WasmDescribe { type Abi = u32; - fn to_abi_ref(&self, extra: &mut Stack) -> u32 { + fn into_abi(self, extra: &mut Stack) -> u32 { #[allow(non_snake_case)] unsafe extern fn invoke<$($var,)* R>( a: usize, @@ -344,12 +386,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,)* { type Abi = u32; - fn to_abi_ref(&self, extra: &mut Stack) -> u32 { + fn into_abi(self, extra: &mut Stack) -> u32 { #[allow(non_snake_case)] unsafe extern fn invoke<$($var,)* >( a: usize, @@ -371,13 +413,13 @@ macro_rules! stack_closures { } } - impl<'a, $($var,)* R> ToRefMutWasmBoundary for FnMut($($var),*) -> R + 'a + impl<'a, $($var,)* R> IntoWasmAbi for &'a mut (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 { + fn into_abi(self, extra: &mut Stack) -> u32 { #[allow(non_snake_case)] unsafe extern fn invoke<$($var,)* R>( a: usize, @@ -399,12 +441,12 @@ macro_rules! stack_closures { } } - impl<'a, $($var,)*> ToRefMutWasmBoundary for FnMut($($var),*) + 'a + impl<'a, $($var,)*> IntoWasmAbi for &'a mut (FnMut($($var),*) + 'a) where $($var: WasmAbi + WasmDescribe,)* { type Abi = u32; - fn to_abi_ref_mut(&mut self, extra: &mut Stack) -> u32 { + fn into_abi(self, extra: &mut Stack) -> u32 { #[allow(non_snake_case)] unsafe extern fn invoke<$($var,)* >( a: usize, diff --git a/src/lib.rs b/src/lib.rs index a74363d9336..c63a990167a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ use std::cell::UnsafeCell; use std::ops::Deref; use std::ptr; -use convert::WasmBoundary; +use convert::FromWasmAbi; /// A module which is typically glob imported from: /// @@ -295,7 +295,7 @@ pub struct JsStatic { unsafe impl Sync for JsStatic {} unsafe impl Send for JsStatic {} -impl Deref for JsStatic { +impl Deref for JsStatic { type Target = T; fn deref(&self) -> &T { unsafe {