Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RFC #2 - casting hierarchy between JS values #640

Merged
merged 5 commits into from
Aug 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ pub struct ImportType {
pub name: Ident,
pub attrs: Vec<syn::Attribute>,
pub doc_comment: Option<String>,
pub instanceof_shim: String,
pub extends: Vec<Ident>,
}

#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
Expand Down Expand Up @@ -408,7 +410,10 @@ impl ImportStatic {

impl ImportType {
fn shared(&self) -> shared::ImportType {
shared::ImportType {}
shared::ImportType {
name: self.name.to_string(),
instanceof_shim: self.instanceof_shim.clone(),
}
}
}

Expand Down
189 changes: 132 additions & 57 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,93 +519,168 @@ impl ToTokens for ast::ImportType {
None => "",
Some(comment) => comment,
};
let const_name = format!("__wbg_generated_const_{}", name);
let const_name = Ident::new(&const_name, Span::call_site());
let instanceof_shim = Ident::new(&self.instanceof_shim, Span::call_site());
(quote! {
#[allow(bad_style)]
#(#attrs)*
#[doc = #doc_comment]
#[repr(transparent)]
#vis struct #name {
obj: ::wasm_bindgen::JsValue,
}

impl ::wasm_bindgen::describe::WasmDescribe for #name {
fn describe() {
::wasm_bindgen::JsValue::describe();
#[allow(bad_style)]
const #const_name: () = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol this is a nice/nasty trick

use wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi, Stack};
use wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
use wasm_bindgen::convert::{RefFromWasmAbi, GlobalStack};
use wasm_bindgen::describe::WasmDescribe;
use wasm_bindgen::{JsValue, JsCast};
use wasm_bindgen::__rt::core::mem::ManuallyDrop;

impl WasmDescribe for #name {
fn describe() {
JsValue::describe();
}
}
}

impl ::wasm_bindgen::convert::IntoWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::IntoWasmAbi>::Abi;
impl IntoWasmAbi for #name {
type Abi = <JsValue as IntoWasmAbi>::Abi;

fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
self.obj.into_abi(extra)
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
self.obj.into_abi(extra)
}
}
}

impl ::wasm_bindgen::convert::OptionIntoWasmAbi for #name {
fn none() -> Self::Abi { 0 }
}
impl OptionIntoWasmAbi for #name {
fn none() -> Self::Abi { 0 }
}

impl<'a> ::wasm_bindgen::convert::OptionIntoWasmAbi for &'a #name {
fn none() -> Self::Abi { 0 }
}
impl<'a> OptionIntoWasmAbi for &'a #name {
fn none() -> Self::Abi { 0 }
}

impl ::wasm_bindgen::convert::FromWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::FromWasmAbi>::Abi;
impl FromWasmAbi for #name {
type Abi = <JsValue as FromWasmAbi>::Abi;

unsafe fn from_abi(
js: Self::Abi,
extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self {
#name {
obj: ::wasm_bindgen::JsValue::from_abi(js, extra),
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self {
#name {
obj: JsValue::from_abi(js, extra),
}
}
}
}

impl ::wasm_bindgen::convert::OptionFromWasmAbi for #name {
fn is_none(abi: &Self::Abi) -> bool { *abi == 0 }
}
impl OptionFromWasmAbi for #name {
fn is_none(abi: &Self::Abi) -> bool { *abi == 0 }
}

impl<'a> ::wasm_bindgen::convert::IntoWasmAbi for &'a #name {
type Abi = <&'a ::wasm_bindgen::JsValue as
::wasm_bindgen::convert::IntoWasmAbi>::Abi;
impl<'a> IntoWasmAbi for &'a #name {
type Abi = <&'a JsValue as IntoWasmAbi>::Abi;

fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
(&self.obj).into_abi(extra)
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
(&self.obj).into_abi(extra)
}
}
}

impl ::wasm_bindgen::convert::RefFromWasmAbi for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::RefFromWasmAbi>::Abi;
type Anchor = ::wasm_bindgen::__rt::core::mem::ManuallyDrop<#name>;
impl RefFromWasmAbi for #name {
type Abi = <JsValue as RefFromWasmAbi>::Abi;
type Anchor = ManuallyDrop<#name>;

unsafe fn ref_from_abi(
js: Self::Abi,
extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self::Anchor {
let tmp = <::wasm_bindgen::JsValue as ::wasm_bindgen::convert::RefFromWasmAbi>
::ref_from_abi(js, extra);
::wasm_bindgen::__rt::core::mem::ManuallyDrop::new(#name {
obj: ::wasm_bindgen::__rt::core::mem::ManuallyDrop::into_inner(tmp),
})
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
let tmp = <JsValue as RefFromWasmAbi>::ref_from_abi(js, extra);
ManuallyDrop::new(#name {
obj: ManuallyDrop::into_inner(tmp),
})
}
}
}

impl From<::wasm_bindgen::JsValue> for #name {
fn from(obj: ::wasm_bindgen::JsValue) -> #name {
#name { obj }
// TODO: remove this on the next major version
impl From<JsValue> for #name {
fn from(obj: JsValue) -> #name {
#name { obj }
}
}
}

impl From<#name> for ::wasm_bindgen::JsValue {
fn from(obj: #name) -> ::wasm_bindgen::JsValue {
obj.obj
impl AsRef<JsValue> for #name {
fn as_ref(&self) -> &JsValue { &self.obj }
}
}

impl AsMut<JsValue> for #name {
fn as_mut(&mut self) -> &mut JsValue { &mut self.obj }
}

impl From<#name> for JsValue {
fn from(obj: #name) -> JsValue {
obj.obj
}
}

impl JsCast for #name {
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
fn instanceof(val: &JsValue) -> bool {
#[link(wasm_import_module = "__wbindgen_placeholder__")]
extern {
fn #instanceof_shim(val: u32) -> u32;
}
unsafe {
let idx = val.into_abi(&mut GlobalStack::new());
#instanceof_shim(idx) != 0
}
}

#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
fn instanceof(val: &JsValue) -> bool {
drop(val);
panic!("cannot check instanceof on non-wasm targets");
}

fn unchecked_from_js(val: JsValue) -> Self {
#name { obj: val }
}

fn unchecked_from_js_ref(val: &JsValue) -> &Self {
// Should be safe because `#name` is a transparent
// wrapper around `val`
unsafe { &*(val as *const JsValue as *const #name) }
}

fn unchecked_from_js_mut(val: &mut JsValue) -> &mut Self {
// Should be safe because `#name` is a transparent
// wrapper around `val`
unsafe { &mut *(val as *mut JsValue as *mut #name) }
}
}

()
};
}).to_tokens(tokens);

for superclass in self.extends.iter() {
(quote! {
impl From<#name> for #superclass {
fn from(obj: #name) -> #superclass {
use wasm_bindgen::JsCast;
#superclass::unchecked_from_js(obj.into())
}
}

impl AsRef<#superclass> for #name {
fn as_ref(&self) -> &#superclass {
use wasm_bindgen::JsCast;
#superclass::unchecked_from_js_ref(self.as_ref())
}
}

impl AsMut<#superclass> for #name {
fn as_mut(&mut self) -> &mut #superclass {
use wasm_bindgen::JsCast;
#superclass::unchecked_from_js_mut(self.as_mut())
}
}
}).to_tokens(tokens);
}
}
}

Expand Down
30 changes: 29 additions & 1 deletion crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1755,7 +1755,14 @@ impl<'a, 'b> SubContext<'a, 'b> {
format!("failed to generate bindings for JS import `{}`", s.name)
})?;
}
shared::ImportKind::Type(_) => {}
shared::ImportKind::Type(ref ty) => {
self.generate_import_type(import, ty).with_context(|_| {
format!(
"failed to generate bindings for JS import `{}`",
ty.name,
)
})?;
}
shared::ImportKind::Enum(_) => {}
}
Ok(())
Expand Down Expand Up @@ -1936,6 +1943,27 @@ impl<'a, 'b> SubContext<'a, 'b> {
Ok(())
}

fn generate_import_type(
&mut self,
info: &shared::Import,
import: &shared::ImportType,
) -> Result<(), Error> {
if !self.cx.wasm_import_needed(&import.instanceof_shim) {
return Ok(());
}
let name = self.import_name(info, &import.name)?;
self.cx.expose_get_object();
let body = format!("
function(idx) {{
return getObject(idx) instanceof {} ? 1 : 0;
}}
",
name,
);
self.cx.export(&import.instanceof_shim, &body, None);
Ok(())
}

fn generate_enum(&mut self, enum_: &shared::Enum) {
let mut variants = String::new();

Expand Down
29 changes: 25 additions & 4 deletions crates/macro-support/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ impl BindgenAttrs {
})
.next()
}

/// Return the list of classes that a type extends
fn extends(&self) -> impl Iterator<Item = &Ident> {
self.attrs
.iter()
.filter_map(|a| match a {
BindgenAttr::Extends(s) => Some(s),
_ => None,
})
}
}

impl syn::synom::Synom for BindgenAttrs {
Expand Down Expand Up @@ -217,6 +227,7 @@ pub enum BindgenAttr {
Readonly,
JsName(String),
JsClass(String),
Extends(Ident),
}

impl syn::synom::Synom for BindgenAttr {
Expand Down Expand Up @@ -295,6 +306,13 @@ impl syn::synom::Synom for BindgenAttr {
s: syn!(syn::LitStr) >>
(s.value())
)=> { BindgenAttr::JsClass }
|
do_parse!(
call!(term, "extends") >>
punct!(=) >>
ns: call!(term2ident) >>
(ns)
)=> { BindgenAttr::Extends }
));
}

Expand Down Expand Up @@ -520,15 +538,18 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a Option<String>)> for syn::ForeignItemFn
}
}

impl ConvertToAst<()> for syn::ForeignItemType {
impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
type Target = ast::ImportKind;

fn convert(self, (): ()) -> Result<Self::Target, Diagnostic> {
fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
let shim = format!("__wbg_instanceof_{}_{}", self.ident, ShortHash(&self.ident));
Ok(ast::ImportKind::Type(ast::ImportType {
vis: self.vis,
name: self.ident,
attrs: self.attrs,
doc_comment: None,
instanceof_shim: shim,
name: self.ident,
extends: attrs.extends().cloned().collect(),
}))
}
}
Expand Down Expand Up @@ -935,7 +956,7 @@ impl<'a> MacroParse<&'a BindgenAttrs> for syn::ForeignItem {
let js_namespace = item_opts.js_namespace().or(opts.js_namespace()).cloned();
let kind = match self {
syn::ForeignItem::Fn(f) => f.convert((item_opts, &module))?,
syn::ForeignItem::Type(t) => t.convert(())?,
syn::ForeignItem::Type(t) => t.convert(item_opts)?,
syn::ForeignItem::Static(s) => s.convert(item_opts)?,
_ => panic!("only foreign functions/types allowed for now"),
};
Expand Down
7 changes: 5 additions & 2 deletions crates/shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#[macro_use]
extern crate serde_derive;

pub const SCHEMA_VERSION: &str = "7";
pub const SCHEMA_VERSION: &str = "8";

#[derive(Deserialize)]
pub struct ProgramOnlySchema {
Expand Down Expand Up @@ -81,7 +81,10 @@ pub struct ImportStatic {
}

#[derive(Deserialize, Serialize)]
pub struct ImportType {}
pub struct ImportType {
pub name: String,
pub instanceof_shim: String,
}

#[derive(Deserialize, Serialize)]
pub struct ImportEnum {}
Expand Down
Loading