mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-04-25 06:02:13 +00:00
Merge pull request #1405 from RReverser/instanceof
Add support for customising `instanceof` behaviour
This commit is contained in:
commit
657b97b6d8
@ -183,6 +183,7 @@ pub struct ImportType {
|
|||||||
pub attrs: Vec<syn::Attribute>,
|
pub attrs: Vec<syn::Attribute>,
|
||||||
pub doc_comment: Option<String>,
|
pub doc_comment: Option<String>,
|
||||||
pub instanceof_shim: String,
|
pub instanceof_shim: String,
|
||||||
|
pub is_type_of: Option<syn::Expr>,
|
||||||
pub extends: Vec<syn::Path>,
|
pub extends: Vec<syn::Path>,
|
||||||
pub vendor_prefixes: Vec<Ident>,
|
pub vendor_prefixes: Vec<Ident>,
|
||||||
}
|
}
|
||||||
|
@ -588,6 +588,13 @@ impl ToTokens for ast::ImportType {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let is_type_of = self.is_type_of.as_ref().map(|is_type_of| quote! {
|
||||||
|
fn is_type_of(val: &JsValue) -> bool {
|
||||||
|
let is_type_of: fn(&JsValue) -> bool = #is_type_of;
|
||||||
|
is_type_of(val)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
(quote! {
|
(quote! {
|
||||||
#[allow(bad_style)]
|
#[allow(bad_style)]
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
@ -720,6 +727,8 @@ impl ToTokens for ast::ImportType {
|
|||||||
panic!("cannot check instanceof on non-wasm targets");
|
panic!("cannot check instanceof on non-wasm targets");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#is_type_of
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn unchecked_from_js(val: JsValue) -> Self {
|
fn unchecked_from_js(val: JsValue) -> Self {
|
||||||
#rust_name { obj: val.into() }
|
#rust_name { obj: val.into() }
|
||||||
|
@ -126,7 +126,7 @@ extern "C" {
|
|||||||
// Array
|
// Array
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(extends = Object)]
|
#[wasm_bindgen(extends = Object, is_type_of = Array::is_array)]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub type Array;
|
pub type Array;
|
||||||
|
|
||||||
@ -466,7 +466,7 @@ extern "C" {
|
|||||||
// Boolean
|
// Boolean
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(extends = Object)]
|
#[wasm_bindgen(extends = Object, is_type_of = |v| v.as_bool().is_some())]
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub type Boolean;
|
pub type Boolean;
|
||||||
|
|
||||||
@ -801,7 +801,7 @@ extern "C" {
|
|||||||
// Function
|
// Function
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(extends = Object)]
|
#[wasm_bindgen(extends = Object, is_type_of = JsValue::is_function)]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub type Function;
|
pub type Function;
|
||||||
|
|
||||||
@ -897,11 +897,7 @@ impl Function {
|
|||||||
/// If this JS value is not an instance of a function then this returns
|
/// If this JS value is not an instance of a function then this returns
|
||||||
/// `None`.
|
/// `None`.
|
||||||
pub fn try_from(val: &JsValue) -> Option<&Function> {
|
pub fn try_from(val: &JsValue) -> Option<&Function> {
|
||||||
if val.is_function() {
|
val.dyn_ref()
|
||||||
Some(val.unchecked_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1141,7 +1137,10 @@ pub fn try_iter(val: &JsValue) -> Result<Option<IntoIter>, JsValue> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let iter_fn: Function = iter_fn.unchecked_into();
|
let iter_fn: Function = match iter_fn.dyn_into() {
|
||||||
|
Ok(iter_fn) => iter_fn,
|
||||||
|
Err(_) => return Ok(None)
|
||||||
|
};
|
||||||
let it = iter_fn.call0(val)?;
|
let it = iter_fn.call0(val)?;
|
||||||
if !it.is_object() {
|
if !it.is_object() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -1434,7 +1433,7 @@ extern "C" {
|
|||||||
// Number.
|
// Number.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(extends = Object)]
|
#[wasm_bindgen(extends = Object, is_type_of = |v| v.as_f64().is_some())]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub type Number;
|
pub type Number;
|
||||||
|
|
||||||
@ -3127,7 +3126,7 @@ extern "C" {
|
|||||||
// JsString
|
// JsString
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[wasm_bindgen(js_name = String, extends = Object)]
|
#[wasm_bindgen(js_name = String, extends = Object, is_type_of = JsValue::is_string)]
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub type JsString;
|
pub type JsString;
|
||||||
|
|
||||||
@ -3587,11 +3586,7 @@ impl JsString {
|
|||||||
/// If this JS value is not an instance of a string then this returns
|
/// If this JS value is not an instance of a string then this returns
|
||||||
/// `None`.
|
/// `None`.
|
||||||
pub fn try_from(val: &JsValue) -> Option<&JsString> {
|
pub fn try_from(val: &JsValue) -> Option<&JsString> {
|
||||||
if val.is_string() {
|
val.dyn_ref()
|
||||||
Some(val.unchecked_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this string is a valid UTF-16 string.
|
/// Returns whether this string is a valid UTF-16 string.
|
||||||
@ -3683,6 +3678,7 @@ impl fmt::Debug for JsString {
|
|||||||
// Symbol
|
// Symbol
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#[wasm_bindgen(is_type_of = JsValue::is_symbol)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub type Symbol;
|
pub type Symbol;
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ macro_rules! attrgen {
|
|||||||
(readonly, Readonly(Span)),
|
(readonly, Readonly(Span)),
|
||||||
(js_name, JsName(Span, String, Span)),
|
(js_name, JsName(Span, String, Span)),
|
||||||
(js_class, JsClass(Span, String, Span)),
|
(js_class, JsClass(Span, String, Span)),
|
||||||
|
(is_type_of, IsTypeOf(Span, syn::Expr)),
|
||||||
(extends, Extends(Span, syn::Path)),
|
(extends, Extends(Span, syn::Path)),
|
||||||
(vendor_prefix, VendorPrefix(Span, Ident)),
|
(vendor_prefix, VendorPrefix(Span, Ident)),
|
||||||
(variadic, Variadic(Span)),
|
(variadic, Variadic(Span)),
|
||||||
@ -240,6 +241,11 @@ impl Parse for BindgenAttr {
|
|||||||
return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
|
return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
(@parser $variant:ident(Span, syn::Expr)) => ({
|
||||||
|
input.parse::<Token![=]>()?;
|
||||||
|
return Ok(BindgenAttr::$variant(attr_span, input.parse()?));
|
||||||
|
});
|
||||||
|
|
||||||
(@parser $variant:ident(Span, String, Span)) => ({
|
(@parser $variant:ident(Span, String, Span)) => ({
|
||||||
input.parse::<Token![=]>()?;
|
input.parse::<Token![=]>()?;
|
||||||
let (val, span) = match input.parse::<syn::LitStr>() {
|
let (val, span) = match input.parse::<syn::LitStr>() {
|
||||||
@ -515,6 +521,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
|
|||||||
.js_name()
|
.js_name()
|
||||||
.map(|s| s.0)
|
.map(|s| s.0)
|
||||||
.map_or_else(|| self.ident.to_string(), |s| s.to_string());
|
.map_or_else(|| self.ident.to_string(), |s| s.to_string());
|
||||||
|
let is_type_of = attrs.is_type_of().cloned();
|
||||||
let shim = format!("__wbg_instanceof_{}_{}", self.ident, ShortHash(&self.ident));
|
let shim = format!("__wbg_instanceof_{}_{}", self.ident, ShortHash(&self.ident));
|
||||||
let mut extends = Vec::new();
|
let mut extends = Vec::new();
|
||||||
let mut vendor_prefixes = Vec::new();
|
let mut vendor_prefixes = Vec::new();
|
||||||
@ -537,6 +544,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
|
|||||||
attrs: self.attrs,
|
attrs: self.attrs,
|
||||||
doc_comment: None,
|
doc_comment: None,
|
||||||
instanceof_shim: shim,
|
instanceof_shim: shim,
|
||||||
|
is_type_of,
|
||||||
rust_name: self.ident,
|
rust_name: self.ident,
|
||||||
js_name,
|
js_name,
|
||||||
extends,
|
extends,
|
||||||
|
@ -514,6 +514,7 @@ impl<'src> FirstPassRecord<'src> {
|
|||||||
attrs,
|
attrs,
|
||||||
doc_comment: None,
|
doc_comment: None,
|
||||||
instanceof_shim: format!("__widl_instanceof_{}", name),
|
instanceof_shim: format!("__widl_instanceof_{}", name),
|
||||||
|
is_type_of: None,
|
||||||
extends: Vec::new(),
|
extends: Vec::new(),
|
||||||
vendor_prefixes: Vec::new(),
|
vendor_prefixes: Vec::new(),
|
||||||
};
|
};
|
||||||
|
35
src/cast.rs
35
src/cast.rs
@ -20,6 +20,9 @@ where
|
|||||||
///
|
///
|
||||||
/// This method performs a dynamic check (at runtime) using the JS
|
/// This method performs a dynamic check (at runtime) using the JS
|
||||||
/// `instanceof` operator. This method returns `self instanceof T`.
|
/// `instanceof` operator. This method returns `self instanceof T`.
|
||||||
|
///
|
||||||
|
/// Note that `instanceof` does not work with primitive values or across
|
||||||
|
/// different realms (e.g. iframes). Prefer using `has_type` instead.
|
||||||
fn is_instance_of<T>(&self) -> bool
|
fn is_instance_of<T>(&self) -> bool
|
||||||
where
|
where
|
||||||
T: JsCast,
|
T: JsCast,
|
||||||
@ -27,17 +30,28 @@ where
|
|||||||
T::instanceof(self.as_ref())
|
T::instanceof(self.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test whether this JS value has a type `T`.
|
||||||
|
///
|
||||||
|
/// Unlike `is_instance_of`, the type can override this to a specialised
|
||||||
|
/// check that works reliably with primitives and across realms.
|
||||||
|
fn has_type<T>(&self) -> bool
|
||||||
|
where
|
||||||
|
T: JsCast,
|
||||||
|
{
|
||||||
|
T::is_type_of(self.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs a dynamic cast (checked at runtime) of this value into the
|
/// Performs a dynamic cast (checked at runtime) of this value into the
|
||||||
/// target type `T`.
|
/// target type `T`.
|
||||||
///
|
///
|
||||||
/// This method will return `Err(self)` if `self.is_instance_of::<T>()`
|
/// This method will return `Err(self)` if `self.has_type::<T>()`
|
||||||
/// returns `false`, and otherwise it will return `Ok(T)` manufactured with
|
/// returns `false`, and otherwise it will return `Ok(T)` manufactured with
|
||||||
/// an unchecked cast (verified correct via the `instanceof` operation).
|
/// an unchecked cast (verified correct via the `instanceof` operation).
|
||||||
fn dyn_into<T>(self) -> Result<T, Self>
|
fn dyn_into<T>(self) -> Result<T, Self>
|
||||||
where
|
where
|
||||||
T: JsCast,
|
T: JsCast,
|
||||||
{
|
{
|
||||||
if self.is_instance_of::<T>() {
|
if self.has_type::<T>() {
|
||||||
Ok(self.unchecked_into())
|
Ok(self.unchecked_into())
|
||||||
} else {
|
} else {
|
||||||
Err(self)
|
Err(self)
|
||||||
@ -47,14 +61,14 @@ where
|
|||||||
/// Performs a dynamic cast (checked at runtime) of this value into the
|
/// Performs a dynamic cast (checked at runtime) of this value into the
|
||||||
/// target type `T`.
|
/// target type `T`.
|
||||||
///
|
///
|
||||||
/// This method will return `None` if `self.is_instance_of::<T>()`
|
/// This method will return `None` if `self.has_type::<T>()`
|
||||||
/// returns `false`, and otherwise it will return `Some(&T)` manufactured
|
/// returns `false`, and otherwise it will return `Some(&T)` manufactured
|
||||||
/// with an unchecked cast (verified correct via the `instanceof` operation).
|
/// with an unchecked cast (verified correct via the `instanceof` operation).
|
||||||
fn dyn_ref<T>(&self) -> Option<&T>
|
fn dyn_ref<T>(&self) -> Option<&T>
|
||||||
where
|
where
|
||||||
T: JsCast,
|
T: JsCast,
|
||||||
{
|
{
|
||||||
if self.is_instance_of::<T>() {
|
if self.has_type::<T>() {
|
||||||
Some(self.unchecked_ref())
|
Some(self.unchecked_ref())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -100,6 +114,19 @@ where
|
|||||||
/// won't need to call this.
|
/// won't need to call this.
|
||||||
fn instanceof(val: &JsValue) -> bool;
|
fn instanceof(val: &JsValue) -> bool;
|
||||||
|
|
||||||
|
/// Performs a dynamic check to see whether the `JsValue` provided
|
||||||
|
/// is a value of this type.
|
||||||
|
///
|
||||||
|
/// Unlike `instanceof`, this can be specialised to use a custom check by
|
||||||
|
/// adding a `#[wasm_bindgen(is_type_of = callback)]` attribute to the
|
||||||
|
/// type import declaration.
|
||||||
|
///
|
||||||
|
/// Other than that, this is intended to be an internal implementation
|
||||||
|
/// detail of `has_type` and you likely won't need to call this.
|
||||||
|
fn is_type_of(val: &JsValue) -> bool {
|
||||||
|
Self::instanceof(val)
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs a zero-cost unchecked conversion from a `JsValue` into an
|
/// Performs a zero-cost unchecked conversion from a `JsValue` into an
|
||||||
/// instance of `Self`
|
/// instance of `Self`
|
||||||
///
|
///
|
||||||
|
Loading…
x
Reference in New Issue
Block a user