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
First of all, great work! I'm migrating a Nan based native addon to node-addon-api, and so far, the code is much cleaner than before. I do no longer need to copy past fragments as I now can remember the signatures and requires far less boilerplate code.
Right now I'm migrating my LLVM Wrapper for Node.js to napi and face the issue that I don't see a way to implement a class hierarchy using Napi::ObjectWrap. I need class hierarchies to correctly model the LLVM Value API.
Inheritance with the v8 API can be achieved by using the Inherit Function of the FunctionTemplate.
Is this use case yet supported or are there ideas how to implement it in node-addon-api?
Is this use case yet supported or are there ideas how to implement it in node-addon-api?
I don’t think it’s explicitly part of the API right now, not even the C one. :/ What exactly do you need from such an API? Exact 1:1 correspondence to V8’s Inherit?
How far do you think you could get with classical JS inheritance, that is, extending the prototype chain and calling the superclass constructor from the subclasses?
I need class hierarchies to correctly model the LLVM Value API.
Which project are you talking about? I’d be curious to look at it if it’s open source. :)
I don’t think it’s explicitly part of the API right now, not even the C one. :/ What exactly do you need from such an API? Exact 1:1 correspondence to V8’s Inherit?
No, I do not need a 1:1 correspondence to the V8 Inherit function.
From the consumer perspective, I would like that instanceof operator is working as expected. E.g. ConstantInt instanceof Value should return true.
As the maintainer, I would prefer if I can use ObjectWrap and that I do not have to reimplement the functions of the superclass, e.g., that Constant does not need to reimplement the methods of Value.
How far do you think you could get with classical JS inheritance, that is, extending the prototype chain and calling the superclass constructor from the subclasses?
I was also thinking about creating a prototype chain but am unsure how to achieve this with the new API (that's the reason I ask). Could you help me out with a small example or some ideas?
Which project are you talking about? I’d be curious to look at it if it’s open source. :)
From the consumer perspective, I would like that instanceof operator is working as expected. E.g. ConstantInt instanceof Value should return true.
Prototype manipulation should be able to do that. :)
As the maintainer, I would prefer if I can use ObjectWrap and that I do not have to reimplement the functions of the superclass, e.g., that Constant does not need to reimplement the methods of Value.
I think that’s not quite working yet. Making the necessary adjustments to the C++ wrapper should be pretty doable, but I think there’s at least one thing we can’t emulate: How V8 does signature checking for the invoked methods.
When invoking a subclass method, I don’t think how we could easily tell that the this object actually is an instance of that subclass… we could either ignore that and let the user deal with invalid casts, or add something like an instanceof check (that has runtime overhead + can be fooled by userland too).
Prototype manipulation should be able to do that. :)
How would you do that? By setting the Prototype property on the constructor function and call the parents constructor in the subclasses constructor? If I know how to achieve this I could try it shortly if it is working.
I see the pros and cons we are facing with adjusting the ObjectWrap class and I think paying the overhead in all other cases where inheritance isn't needed is not really an option. The question is, might we expose an ObjectWrapper like class especially for the inheritance use case? I'm sorry that I'm not a great help here. What are the specifics I could study to get a better overview of the topic so that I can be of more help?
Thanks. This helps to set up the prototype chain correctly.
The issue that remains from my point of view is that I do not know how to call the super constructor from the child class. This is needed in my case to initialize some private fields that are then accessed in the methods.
@gabrielschulhof thanks for your example. However, I'm using the node-addon api (C++ object wrappers) and I don't believe I can call the super constructor in any way (since I cannot inherit).
You could have the superclass constructor call a static function to do the
actual initialization. The subclass constructor could then call the same
static function to chain up.
I believe this is not possible when the constructor initializes private fields (these are not static). Except if you store these as JavaScript values (but then you lose the benefits of using ObjectWrap in the first place).
I tried to follow the instructions from @gabrielschulhof but I still believe that the proposed solution does not work with ObjectWrap and, therefore, no real substitution for the functionality of v8:Inherit and nan::ObjectWrap exist.
I still try to migrate some llvm bindings for node from nan to napi and node-addons-api. I struggle to migrate the PointerType class of llvm that inherits from type.
If I use the proposed napi_inherits function the prototype chain is set up correctly. But since the PointerTypeWrapper class does not inherit from the TypeWrapper class all calls to methods defined in the Type class on instances of PointerType fail.
When I used nan, the PointerTypeWrapper class inherited from the TypeWrapper class. However, with node-addon-api this seems no longer possible.
So my question is: Is there a way to implement class inheritance using node-addon-api and ObjectWrap or can I not make use of ObjectWrap at all in cases where inheritance is needed?
But since the PointerTypeWrapper class does not inherit from the TypeWrapper class all calls to methods defined in the Type class on instances of PointerType fail.
Yes, something like that is what I meant. If it's performance sensitive and
is a common pattern I see no issue with having a V8 API that does exactly
that on Function either.
@gabrielschulhof one concern I have is that if we are asking for additional V8 functions, what is the likelyhood that other JS engines have the same functionality. We don't want to add something that precludes running on other engines...
@mhdawson given that this can be implemented without support from the engine, as nodejs/node#36148 shows, I think it would be absolutely beneficial to have engine support for extending arbitrary JS classes.
@verwaest-zz@hashseed I'm not sure you are any longer involved with this subject, but hopefully you can ping the right folks to show them nodejs/node#36148 as an example of the kind of functionality we would need from V8. Basically, nodejs/node#36148 implements #229 (comment), but "a clear API on Function" would still help immensely to reduce the complexity of the implementation, not least because the extra indirection in nodejs/node#36148 reduces the performance of calling the native function even further.
This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.
I've managed to make inheritance work in two steps.
First, make one ObjectWrap hold all native objects by a base class pointer. Do not inherit different ObjectWrap, otherwise you will encounter the problem of diamond inheritance. And in this way, the property descriptors of the parent class can be copied into the child class safely.
To make constructor and other things work correctly, class meta data should be attached as the data pointer of DefineClass call. I've created a lib node-addon-api-helper to simplify these things and hope it will help.
Activity
addaleax commentedon Feb 19, 2018
I don’t think it’s explicitly part of the API right now, not even the C one. :/ What exactly do you need from such an API? Exact 1:1 correspondence to V8’s
Inherit?How far do you think you could get with classical JS inheritance, that is, extending the prototype chain and calling the superclass constructor from the subclasses?
Which project are you talking about? I’d be curious to look at it if it’s open source. :)
MichaReiser commentedon Feb 19, 2018
Hy Anna,
Thanks for your super fast response.
No, I do not need a 1:1 correspondence to the V8 Inherit function.
From the consumer perspective, I would like that
instanceofoperator is working as expected. E.g.ConstantInt instanceof Valueshould returntrue.As the maintainer, I would prefer if I can use
ObjectWrapand that I do not have to reimplement the functions of the superclass, e.g., thatConstantdoes not need to reimplement the methods ofValue.I was also thinking about creating a prototype chain but am unsure how to achieve this with the new API (that's the reason I ask). Could you help me out with a small example or some ideas?
It is open source. You can find it on GitHub.
addaleax commentedon Feb 19, 2018
Prototype manipulation should be able to do that. :)
I think that’s not quite working yet. Making the necessary adjustments to the C++ wrapper should be pretty doable, but I think there’s at least one thing we can’t emulate: How V8 does signature checking for the invoked methods.
When invoking a subclass method, I don’t think how we could easily tell that the
thisobject actually is an instance of that subclass… we could either ignore that and let the user deal with invalid casts, or add something like aninstanceofcheck (that has runtime overhead + can be fooled by userland too).I’m not sure what to do. :/
MichaReiser commentedon Feb 21, 2018
How would you do that? By setting the
Prototypeproperty on the constructor function and call the parents constructor in the subclasses constructor? If I know how to achieve this I could try it shortly if it is working.I see the pros and cons we are facing with adjusting the ObjectWrap class and I think paying the overhead in all other cases where inheritance isn't needed is not really an option. The question is, might we expose an ObjectWrapper like class especially for the inheritance use case? I'm sorry that I'm not a great help here. What are the specifics I could study to get a better overview of the topic so that I can be of more help?
gabrielschulhof commentedon Apr 23, 2018
From nodejs/node#4179 it seems that full inheritance can be accomplished with
This can be done with existing N-API calls (untested, and ignoring return status):
iclosure commentedon Apr 23, 2018
Thank you very much!
MichaReiser commentedon Apr 24, 2018
Thanks. This helps to set up the prototype chain correctly.
The issue that remains from my point of view is that I do not know how to call the super constructor from the child class. This is needed in my case to initialize some private fields that are then accessed in the methods.
gabrielschulhof commentedon Apr 24, 2018
@MichaReiser In my example I simply call one binding from the other.
MichaReiser commentedon Apr 25, 2018
@gabrielschulhof thanks for your example. However, I'm using the node-addon api (C++ object wrappers) and I don't believe I can call the super constructor in any way (since I cannot inherit).
gabrielschulhof commentedon Apr 25, 2018
MichaReiser commentedon Apr 25, 2018
I believe this is not possible when the constructor initializes private fields (these are not static). Except if you store these as JavaScript values (but then you lose the benefits of using
ObjectWrapin the first place).gabrielschulhof commentedon Apr 25, 2018
IIRC if the static function is part of the same class then it has access to the private fields:
mhdawson commentedon Jun 14, 2018
Any update on the discussion here?
MichaReiser commentedon Jul 8, 2018
I tried to follow the instructions from @gabrielschulhof but I still believe that the proposed solution does not work with ObjectWrap and, therefore, no real substitution for the functionality of v8:Inherit and nan::ObjectWrap exist.
I still try to migrate some llvm bindings for node from nan to napi and node-addons-api. I struggle to migrate the PointerType class of llvm that inherits from type.
I implemented the Type (header) and PointerType (header) classes using node-addons-api without inheritance.
If I use the proposed
napi_inheritsfunction the prototype chain is set up correctly. But since thePointerTypeWrapperclass does not inherit from theTypeWrapperclass all calls to methods defined in theTypeclass on instances ofPointerTypefail.When I used nan, the
PointerTypeWrapperclass inherited from theTypeWrapperclass. However, with node-addon-api this seems no longer possible.So my question is: Is there a way to implement class inheritance using node-addon-api and ObjectWrap or can I not make use of ObjectWrap at all in cases where inheritance is needed?
Thanks,
Micha
gabrielschulhof commentedon Jul 9, 2018
@MichaReiser
How do you mean "fail"?
21 remaining items
verwaest-zz commentedon Sep 20, 2018
gabrielschulhof commentedon Sep 20, 2018
@verwaest I can certainly implement that, but then I have to write the methods of the subclass in JavaScript.
So, a V8 API on
Functionthat would be super awesome would bev8::Local<v8::FunctionTemplate> v8::Function::Extend(...);gabrielschulhof commentedon Sep 20, 2018
@verwaest I guess the parameters for the new API would be the same or almost the same as for
v8::FunctionTemplate::New().maierfelix commentedon Sep 19, 2019
Any updates on this?
mhdawson commentedon Sep 20, 2019
@gabrielschulhof one concern I have is that if we are asking for additional V8 functions, what is the likelyhood that other JS engines have the same functionality. We don't want to add something that precludes running on other engines...
n-api: add napi_define_subclass
gabrielschulhof commentedon Nov 18, 2020
@mhdawson given that this can be implemented without support from the engine, as nodejs/node#36148 shows, I think it would be absolutely beneficial to have engine support for extending arbitrary JS classes.
@verwaest-zz @hashseed I'm not sure you are any longer involved with this subject, but hopefully you can ping the right folks to show them nodejs/node#36148 as an example of the kind of functionality we would need from V8. Basically, nodejs/node#36148 implements #229 (comment), but "a clear API on Function" would still help immensely to reduce the complexity of the implementation, not least because the extra indirection in nodejs/node#36148 reduces the performance of calling the native function even further.
gabrielschulhof commentedon Nov 19, 2020
CC: @verwaest
github-actions commentedon Feb 18, 2021
This issue is stale because it has been open many days with no activity. It will be closed soon unless the stale label is removed or a comment is made.
ajihyf commentedon Apr 27, 2022
I've managed to make inheritance work in two steps.
First, make one
ObjectWraphold all native objects by a base class pointer. Do not inherit differentObjectWrap, otherwise you will encounter the problem of diamond inheritance. And in this way, the property descriptors of the parent class can be copied into the child class safely.And then, monkey patch the prototype to make
instanceofworksTo make constructor and other things work correctly, class meta data should be attached as the data pointer of
DefineClasscall. I've created a lib node-addon-api-helper to simplify these things and hope it will help.ApsarasX commentedon Apr 29, 2022
@ajihyf The example C++ code in your comment cannot work well (some compilation error).
ajihyf commentedon Apr 30, 2022
Sorry it's a code snippet I copied from my lib to show the basic idea and I didn't check its compilation outside the lib. It should work now.
mmomtchev commentedon Jan 14, 2023
@devongovett, I have settled on a method which I think is the least bad one: https://mmomtchev.medium.com/c-class-inheritance-with-node-api-and-node-addon-api-c180334d9902