Skip to content

Commit

Permalink
Merge pull request #640 from alexcrichton/jscast
Browse files Browse the repository at this point in the history
Implement RFC #2 - casting hierarchy between JS values
  • Loading branch information
alexcrichton authored Aug 7, 2018
2 parents 4363697 + bd15db4 commit 5b93552
Show file tree
Hide file tree
Showing 14 changed files with 581 additions and 65 deletions.
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: () = {
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

0 comments on commit 5b93552

Please sign in to comment.