Skip to content
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 NonConstructor to allow customization of ObjectWrap behavior #1125

Closed
wants to merge 4 commits into from

Conversation

p120ph37
Copy link
Contributor

Addresses #1123 by moving the error-throw into an overridable static method. Default behavior remains exactly the same, but now a developer can override NonConstructor in their ObjectWrap subclass in order to define alternate behavior. As a trivial example of behavior a developer might implement, this code in an ObjectWrap subclass would support the old (ugly) idiom of instantiating a class even when the function is called without the new keyword:

static Napi::Value MyObject::NonConstructor(const Napi::CallbackInfo& info) {
  std::vector<napi_value> args;
  for(size_t i = 0; i < info.Length(); i++) args.push_back(info[i]);
  return GetConstructor(info.Env()).New(args);
}

Which then allows this usage:

const MyObject = binding.MyObject;
const newConstructed = new MyObject();
const factoryConstructed = MyObject();
assert(newConstructed instanceof MyObject);
assert(factoryConstructed instanceof MyObject);

I've also included a new test case, which amounts to basically the above, and also tests that exceptions thrown inside the non-constructor are handled appropriately.

}

static std::unordered_map<napi_env, Napi::FunctionReference*> constructors;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is leaky without an env-shutdown-hook, but it's good enough to run the tests without clobbering Napi::Addon<T> via env.SetInstanceData() as would be the typical idiom.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably add a comment to the source so nobody thinks it’s a good idea to copy paste this into a real project…

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added an env cleanup-hook so this test-code shouldn't leak anymore and should be adaptable to real-world projects.

@p120ph37 p120ph37 force-pushed the issue-1123 branch 4 times, most recently from 5d75653 to 1d1ad35 Compare January 11, 2022 23:32
@mhdawson
Copy link
Member

mhdawson commented Feb 4, 2022

We discussed in the node-api team meeting today.

We agreed giving developers the option makes sense. We would like the PR to include updating the documentation as well.

We would prefer that instead of using NonConstructor that we use CreateViaFunctionCall

@p120ph37
Copy link
Contributor Author

p120ph37 commented Feb 4, 2022

Sounds reasonable to me - I'll make the requested adjustments.

@p120ph37
Copy link
Contributor Author

p120ph37 commented Feb 4, 2022

I'm happy to name it CreateViaFunctionCall if that's the consensus, but I do feel like that's a little bit misleading because the actual action may be something other than creating an object -- that's just one possible implementation for the method. All I intended to convey with my original NonConstructor name was that it handles the case where the new keyword was not used. Absent the new keyword, the function is technically allowed to do anything it wants, including returning an object of some completely different type, or returning a "builder" object, or an object-factory, etc. So calling it Create... seems a little presumptive ;-)

@vmoroz
Copy link
Member

vmoroz commented Feb 4, 2022

I'm happy to name it CreateViaFunctionCall if that's the consensus, but I do feel like that's a little bit misleading because the actual action may be something other than creating an object -- that's just one possible implementation for the method. All I intended to convey with my original NonConstructor name was that it handles the case where the new keyword was not used. Absent the new keyword, the function is technically allowed to do anything it wants, including returning an object of some completely different type, or returning a "builder" object, or an object-factory, etc. So calling it Create... seems a little presumptive ;-)

+@mhdawson and @gabrielschulhof

@p120ph37, it is a great point! There were a few points for the alternative name:

  • NonConstructor sounds a little bit alien and does not communicate the function purpose clearly.
  • What if we use the same terms as in the JS docs? E.g. in MDN Date they use the phrase When called as a function, returns a string representation of the current date and time .... Thus, we thought that the name should have something from the called as function phrase.
  • The method name is better start with a verb.

Since Create... seems to be wrong here (even the Date() call does not create anything), could we use something like HandleWhenCalledAsFunction, OnCalledAsFunction, or something else along these lines? E.g. to communicate that this method handles the situation when the JS function representing the native class was called as a function rather than as a constructor. It could probably help to write the docs about the function first and see which function name represents best what was described in the doc.

@p120ph37
Copy link
Contributor Author

p120ph37 commented Feb 4, 2022

I like OnCalledAsFunction - it's not too verbose, and the On prefix is already familiar and implies the mechanism of action: the developer should define the method in order to change the default behavior. I'll go ahead with that name first, and we can see what the doc looks like.

@vmoroz
Copy link
Member

vmoroz commented Feb 4, 2022

I like OnCalledAsFunction - it's not too verbose, and the On prefix is already familiar and implies the mechanism of action: the developer should define the method in order to change the default behavior. I'll go ahead with that name first, and we can see what the doc looks like.

I like it too! Though, the final decision is from @mhdawson and @gabrielschulhof.

@mhdawson
Copy link
Member

mhdawson commented Feb 7, 2022

adding my +1 for OnCalledAsFunction

Copy link
Member

@mhdawson mhdawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

mhdawson pushed a commit that referenced this pull request Feb 17, 2022
Add new method to allow customization of ObjectWrap behavior

PR-URL: #1125
Reviewed-By: Michael Dawson <midawson@redhat.com
@mhdawson
Copy link
Member

Landed as cee899a

@mhdawson mhdawson closed this Feb 17, 2022
kevindavies8 added a commit to kevindavies8/node-addon-api-Develop that referenced this pull request Aug 24, 2022
Add new method to allow customization of ObjectWrap behavior

PR-URL: nodejs/node-addon-api#1125
Reviewed-By: Michael Dawson <midawson@redhat.com
Marlyfleitas added a commit to Marlyfleitas/node-api-addon-Development that referenced this pull request Aug 26, 2022
Add new method to allow customization of ObjectWrap behavior

PR-URL: nodejs/node-addon-api#1125
Reviewed-By: Michael Dawson <midawson@redhat.com
wroy7860 added a commit to wroy7860/addon-api-benchmark-node that referenced this pull request Sep 19, 2022
Add new method to allow customization of ObjectWrap behavior

PR-URL: nodejs/node-addon-api#1125
Reviewed-By: Michael Dawson <midawson@redhat.com
johnfrench3 pushed a commit to johnfrench3/node-addon-api-git that referenced this pull request Aug 11, 2023
Add new method to allow customization of ObjectWrap behavior

PR-URL: nodejs/node-addon-api#1125
Reviewed-By: Michael Dawson <midawson@redhat.com
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants