You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use Symbol.hasInstance() for napi_instanceof on V8, when available #182
This can be accomplished by retaining Symbol.hasInstance() in a static Persistent at the top of node_api.cc and then calling it. If the persistent is empty, we do what we do now. The persistent is initialized from napi_module_register_cb().
... though perhaps we shouldn't store it but rather retrieve it all the time, because it could be for a different context :/ This reduces the performance of napi_instanceof() on V8, but it may still be an improvement compared to the walk up the prototype chain that it does now.
Do we actually want that behavior? Does a native addon want its instanceof checks to be overridable? Does v8::FunctionTemplate::HasInstance() use Symbol.hasInstance()? Or should we rename napi_instanceof() to something that doesn't imply exactly the same behavior as the JS instanceof operator?
What do you mean by "its instanceof checks"? An addon may check whether anything is an instance of anything else. It need not necessarily restrict itself to checks of instances against constructors it provides itself. This is to be a generic API, right?
As such, we should absolutely replicate precisely what the instanceof operator does, because the operator is fundamentally inaccessible from the native side, yet there is use for it. After all, ChakraCore provides access to it, and it likely takes into account Symbol.hasInstance ...
So, if the operator is now customizable via Symbol.hasInstance then we must capture that. Providing something that resembles instanceof but isn't complete, under any name whatsoever would be of dubious use.
Additionally, in V8 using Symbol.hasInstance is more efficient than the walk up the prototype chain because it makes fewer calls into V8 - of course depending on the depth of the prototype chain.
TBH, I was unaware of Symbol.hasInstance as a way of exposing instanceof functionality, otherwise I would've used it in the original implementation of napi_instanceof.
Of course, I say all this without having tested whether the function that is retrieved from the constructor's Symbol.hasInstance key actually provides the functionality we want. I'm working on that now. My motivation stems merely from the attitude that, if the engine provides a facility, we should not re-implement that facility.
I'm checking that too. Even with plain JS though, only classses can be customized by way of Symbol.hasInstance, not plain functions used as constructors.
Oh, actually, v8::FunctionTemplate::HasInstance() is not affected by Symbol.hasInstance AFAICT. Probably because it counts as a plain-old-function, not a class.
classMyClass{static[Symbol.hasInstance](candidate){return(candidate.mark==="is an instance");}}varx=newMyClass();vary=newMyClass();console.log(xinstanceofMyClass);console.log(yinstanceofMyClass);y.mark="is an instance";console.log(xinstanceofMyClass);console.log(yinstanceofMyClass);console.log(typeofMyClass);functionMyFunction(){}MyFunction[Symbol.hasInstance]=function(candidate){return(candidate.mark==="is an instance");}vara=newMyFunction();varb=newMyFunction();console.log(ainstanceofMyFunction);console.log(binstanceofMyFunction);
and a and b are still considered instances, despite the supposed check. So, that's why I concluded that it only works for classes. Looks like they're more than just sugar.
OK, so that was my original code. So, does the fact that it does not affect instanceof against plain-old-functions mean that there's a bug in V8 in that it does not conform to spec?
Note that for our implementation of napi_instanceof() I think we still need to go with hasInstance, because we cannot distinguish whether the constructor was created with class {} and should therefore be influenced by custom hasInstance()-ers or whether it was created with function() {} - as well we shouldn't, since it is supposed to be just sugar ...
So, since FunctionTemplate is a V8-specific construct, seeming based on the above to express only relationships between objects as stored in the VM, I still think we should encapsulate the logic of the native instanceof operator rather than of the V8-specific HasInstance() and call it napi_instanceof().
Agreed. We could still provide a separate napi_ordinary_has_instance() that matches the current implementation as suggested in this comment. But I'm not sure any native module would really depend on the distinction.
Activity
gabrielschulhof commentedon Mar 22, 2017
... though perhaps we shouldn't store it but rather retrieve it all the time, because it could be for a different context :/ This reduces the performance of
napi_instanceof()on V8, but it may still be an improvement compared to the walk up the prototype chain that it does now.jasongin commentedon Mar 22, 2017
Do we actually want that behavior? Does a native addon want its instanceof checks to be overridable? Does
v8::FunctionTemplate::HasInstance()useSymbol.hasInstance()? Or should we renamenapi_instanceof()to something that doesn't imply exactly the same behavior as the JSinstanceofoperator?gabrielschulhof commentedon Mar 22, 2017
What do you mean by "its instanceof checks"? An addon may check whether anything is an instance of anything else. It need not necessarily restrict itself to checks of instances against constructors it provides itself. This is to be a generic API, right?
As such, we should absolutely replicate precisely what the
instanceofoperator does, because the operator is fundamentally inaccessible from the native side, yet there is use for it. After all, ChakraCore provides access to it, and it likely takes into accountSymbol.hasInstance...So, if the operator is now customizable via
Symbol.hasInstancethen we must capture that. Providing something that resembles instanceof but isn't complete, under any name whatsoever would be of dubious use.Additionally, in V8 using
Symbol.hasInstanceis more efficient than the walk up the prototype chain because it makes fewer calls into V8 - of course depending on the depth of the prototype chain.TBH, I was unaware of
Symbol.hasInstanceas a way of exposinginstanceoffunctionality, otherwise I would've used it in the original implementation ofnapi_instanceof.Of course, I say all this without having tested whether the function that is retrieved from the constructor's
Symbol.hasInstancekey actually provides the functionality we want. I'm working on that now. My motivation stems merely from the attitude that, if the engine provides a facility, we should not re-implement that facility.jasongin commentedon Mar 22, 2017
Does
v8::FunctionTemplate::HasInstance()useSymbol.hasInstance()? Or is it unaffected by that customization?gabrielschulhof commentedon Mar 22, 2017
I'm checking that too. Even with plain JS though, only classses can be customized by way of
Symbol.hasInstance, not plain functions used as constructors.gabrielschulhof commentedon Mar 22, 2017
Oh, actually,
v8::FunctionTemplate::HasInstance()is not affected bySymbol.hasInstanceAFAICT. Probably because it counts as a plain-old-function, not a class.jasongin commentedon Mar 22, 2017
I don't understand -- a class in JS is just syntactic sugar for a constructor function with prototype properties.
gabrielschulhof commentedon Mar 22, 2017
Here's the JS-only code I'm using for testing:
gabrielschulhof commentedon Mar 22, 2017
For the plain-old-function I tried setting all of these as well:
and
aandbare still considered instances, despite the supposed check. So, that's why I concluded that it only works for classes. Looks like they're more than just sugar.jasongin commentedon Mar 22, 2017
It's static, so the equivalent would be:
gabrielschulhof commentedon Mar 22, 2017
OK, so that was my original code. So, does the fact that it does not affect instanceof against plain-old-functions mean that there's a bug in V8 in that it does not conform to spec?
gabrielschulhof commentedon Mar 22, 2017
Note that for our implementation of
napi_instanceof()I think we still need to go withhasInstance, because we cannot distinguish whether the constructor was created withclass {}and should therefore be influenced by customhasInstance()-ers or whether it was created withfunction() {}- as well we shouldn't, since it is supposed to be just sugar ...jasongin commentedon Mar 22, 2017
Take a look at this: http://node.green/#ES2015-built-ins-well-known-symbols-Symbol-hasInstance
Here's the code they use for the test:
V8 passes the test on Node >= 6.10.
gabrielschulhof commentedon Mar 22, 2017
Oh,
defineProperty, eh?gabrielschulhof commentedon Mar 22, 2017
Wow! OK, so if I use
defineProperty()then it affectsinstanceof.gabrielschulhof commentedon Mar 22, 2017
v8::FunctionTemplate::HasInstancedoes not take into accounthasInstance:and hello.cc:
results in
gabrielschulhof commentedon Mar 22, 2017
So, since
FunctionTemplateis a V8-specific construct, seeming based on the above to express only relationships between objects as stored in the VM, I still think we should encapsulate the logic of the nativeinstanceofoperator rather than of the V8-specificHasInstance()and call itnapi_instanceof().jasongin commentedon Mar 22, 2017
Agreed. We could still provide a separate
napi_ordinary_has_instance()that matches the current implementation as suggested in this comment. But I'm not sure any native module would really depend on the distinction.gabrielschulhof commentedon Mar 22, 2017
Yeah, let's take the lazy (i.e. let someone file a bug requesting the feature) approach with
napi_ordinary_has_instance():)napi: change napi_instanceof() to use Symbol.hasInstance
napi: change napi_instanceof() to use Symbol.hasInstance
napi: change napi_instanceof() to use Symbol.hasInstance
napi: change napi_instanceof() to use Symbol.hasInstance
napi: change napi_instanceof() to use Symbol.hasInstance
napi: change napi_instanceof() to use Symbol.hasInstance