-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add support for customising instanceof
behaviour
#1405
Conversation
Heh, looks like some tests actually check this. What do we want to do about this? Do we want to add such support to corresponding |
It also seems strangely inconsistent that |
Ideally we would have some raw layer that just does the I'm not sure how to do this in a backwards compat way. |
We can surely separate them by adding trait JsCast {
// ...existing definitions
fn is_a(val: &JsValue) -> bool {
Self::instanceof()
}
} and then overriding this extra method via attribute, but I see little benefit in that because |
Can we perhaps draw inspiration from JS somehow? How does JS typically handle these differences idiomatically? Can we basically make the idiomatic thing in JS the same idiomatic thing in Rust? |
That's what this PR attempts to do. Unfortunately, despite few proposals in the past, there is no central API for cross-realm checks, but the common consensus is that you should use them where available (like in case with For other objects, which don't have corresponding method, the common alternative is to use string tag checks like |
@RReverser is correct: there are many ways to check whether an object is a particular type, so there isn't a single "correct" way to do it in JS. Instead you need to use However, since |
I think that sounds like the best of all worlds yeah where we:
How's that sound? |
That plan sounds great to me. In an ideal world, I'd change the semantics of |
@alexcrichton Sounds perfectly reasonable to me! I'll update the PR. |
@fitzgen I think that's what @alexcrichton suggests too? (keeping |
We implement |
Ohh. Yeah, I think these would have to be deprecated. |
Btw, do we want to actually call it |
Personally, I don't see any use for On the other hand, a impl JsValue {
pub fn instanceof(&self, class: &JsValue) -> bool {
// intrinsic which uses `instanceof` in JS
}
} This would be useful as a low-level building block, but isn't intended to be used directly (instead |
I was thinking about it too, but for that we need to solve #1406 first (I raised it with this particular issue in mind too). |
@RReverser Right, but My point was basically just that |
Maybe better to deprecate in the same PR as |
Also, as far as I can tell, for now we don't want to use |
@RReverser Except right now
Why don't we want to use |
See above:
Aside from perf impact, toString is also not 100% reliable, although for different reasons. |
Aside from |
I think just
I created some benchmarks: https://jsperf.com/array-type-checking3 The builtin gets ~32,891,703 ops/sec. Using A cost? Sure, but that's still 22 million ops/sec. So in the situations where a builtin doesn't exist, correctness is more important in my opinion.
This is the first time I've heard of that, could you explain more? |
@RReverser trying to follow the discussion, were you on board with the previous plan? I was looking at #893 again today and realized that this'd be a great way to fix that, so was curious if you're still up for pursuing this change! |
I was :) See #1405 (comment) There have been just few follow-up questions which can be summarised as:
|
Since this is in theory not called too much (rather the |
Oh, actually, this brings me back to another question from the beginning of this PR - do we really want to continue to support boxed variants of Given that we don't support boxed variant of |
So long as "general idiomatic patterns continue to work" I don't have strong feelings one way or another. I don't really know what it means to not support them or to to support them. |
@alexcrichton In JS there is a distinction between primitive and boxed values:
Most of the time they behave the same, but there are some differences: typeof "foo" === "string"
typeof new String("foo") === "object" "foo" instanceof String === false
new String("foo") instanceof String === true On the other hand, these are both true: {}.toString.call("foo") === "[object String]"
{}.toString.call(new String("foo")) === "[object String]" So you can use that to reliably detect both. Right now, since If it used If it used Boxed strings are very rare in JS. So using either Since On the other hand, some types in JS don't work with |
Sorry for the delay here - I think we agreed with the plan for this PR for now, I just need few days to finish as working on few projects in parallel :) (as far as I understand, there is no rush since no one raised these issues before anyway, so hopefully few days is fine) |
Ok I fixed issues and decided to rename the method to I've also added a default fallback to existing |
One more note: I haven't added custom check to |
Noticed that, just like |
Nice! Could this perhaps switch the direction of the arguments though? Instead of Additionally, could the documentation be updated to indicate when you might want to use |
Yeah this was the primary reason I didn't do it - from the discussion above I've got an impression that we don't want to encourage this method to be often called directly? Also note that semantically it's in But, anyway, I guess we could add |
I don't really mind too much about the naming, but I suspect that we'll want to recommend that this is called almost all the time instead of |
Ok I can add |
(this was just a rebase to fix conflicts for now) |
@alexcrichton Ok done and looks like CI passed. |
Thanks @RReverser! |
This allows types to define custom Rust code as an override for
JsCast::instanceof
.dyn_ref
to improve value detection #1367 for types that have built-in cross-realm checks, allowingdyn_ref
anddyn_into
to accept values from other realms. In particular:.instanceof::<Array>()
will now automatically useArray::is_array
instead of actual... instanceof Array
..instanceof::<Function>()
will useJsValue::is_function
. This also simplifiesFunction::try_from
to being an alias fordyn_ref::<Function>()
..dyn_into::<JsString>()
,.dyn_into::<Number>()
,.dyn_into::<Boolean>()
will also now use corresponding checks fromJsValue
.In theory, these 3 can break checks if someone has been using boxed primitives (
new String(...)
,new Number(...)
,new Boolean(...)
), but these are extremely rare in real-world JS code and are already not support byJsValue
methods. If we do want to support them, I believe it should be done at theJsValue
level inas_f64
,as_bool
,as_string
too..dyn_into::<Symbol>
/ `.dyn_ref::<Symbol> don't work as expected #1370 -.instanceof::<Symbol>()
will now useJsValue::is_symbol
- previously it would returnfalse
for any value, even actualSymbol
s, making it impossible to use.dyn_ref::<Symbol>()
and.dyn_into::<Symbol>()
.cc @alexcrichton @fitzgen