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

n-api: add ability to remove a wrapping #14658

Closed

Conversation

gabrielschulhof
Copy link
Contributor

Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

Fixes: nodejs/abi-stable-node#266

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

n-api

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. node-api Issues and PRs related to the Node-API. labels Aug 7, 2017
doc/api/n-api.md Outdated

Retrieves a native instance that was previously wrapped in the JavaScript
object `js_object` using `napi_wrap()` and removes the wrapping, thereby
restoring the JavaScript object's prototype chain.
Copy link
Member

Choose a reason for hiding this comment

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

Does this invoke the finalizer callback of the previous wrapping? Should it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My intention was that it not call the finalizer callback. I need to test this.

Copy link
Member

Choose a reason for hiding this comment

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

Why shouldn't it?

Either way, the behavior needs to be documented.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If it calls the finalizer callback it's kinda useless to return the pointer, since it's already freed. I guess my concept was that by removing the wrapping, you resume responsibility for freeing the pointer, having previously assigned such responsibility to the VM.

... and I need to update the documentation ...


- `[in] env`: The environment that the API is invoked under.
- `[in] js_object`: The object associated with the native instance.
- `[out] result`: Pointer to the wrapped native instance.
Copy link
Member

Choose a reason for hiding this comment

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

Why also return the pointer here? That is already available via napi_wrap(). Or, maybe these two APIs could be combined, with a boolean parameter that indicates whether to remove it at the same time. (I'm not sure I actually prefer that though.)

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 guess it's to avoid having to make two API calls. In nodejs/abi-stable-node#266 @sampsongao is trying to do a napi_unwrap() followed unconditionally by a napi_wrap(). Knowing that an unconditional napi_wrap() will follow would allow him to drop in napi_remove_wrap() instead of napi_unwrap().

Copy link
Member

@jasongin jasongin Aug 7, 2017

Choose a reason for hiding this comment

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

If you're concerned about performance, why not make it work in one napi_wrap() call? Maybe a boolean to allow overwriting a previous wrap? Or return a previous wrap value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mhdawson wanted an explicit napi_remove_wrap(), but, I guess an explicit true/false parameter is also, well, explicit.

If we do this within napi_unwrap(), though, we're treading into semver-major territory - which I OK while in experimental, but still, to be discussed, right?


napi_status napi_remove_wrap(napi_env env, napi_value obj, void** result) {
NAPI_PREAMBLE(env);
Copy link
Member

Choose a reason for hiding this comment

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

Is a try-catch needed here?

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 took it as a hint from V8 that an exception may be thrown in ->SetPrototype() because it returns a v8::Maybe<bool> - much like .

In fact, we should re-examine how we deal with v8::Maybe(Local)? return values as we concurrently deal with exceptions. I get the impression that an empty/nothing return value happens iff there was an exception. If that's the case then we should perhaps not return napi_generic_failure if the v8::Maybe(Local)? is empty, but we should rather check for an exception first and return napi_exception_pending if we find one, and only return napi_generic_failure if we don't.

Copy link
Member

@jasongin jasongin Aug 7, 2017

Choose a reason for hiding this comment

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

I don't think a v8::Maybe return type always means there is a possibility of an exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK, then we need to check whether there is such a possibility.

Copy link
Member

Choose a reason for hiding this comment

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

Most of the Maybe errors can be triggered through Proxies; for example, SetPrototype() fails when a setPrototype() trap throws. It can also throw e.g. when you try to set up a circular prototype chain or something like that, but I don’t think that would happen here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@addaleax out of curiosity and AFAUK, does V8 have any APIs which return v8::Maybes or v8::MaybeLocals even though there is no chance that an exception will be thrown in their execution?

The reason I ask is that the only ones I've seen are property get/set/has/delete and SetPrototype, all of which can throw exceptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wait, NM. v8::String::NewFromUtf8.

Copy link
Member

Choose a reason for hiding this comment

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

Yes. I was also thinking of the number APIs like v8::Value::Int32Value(context), that returns a maybe but doesn't throw.

Copy link
Member

@addaleax addaleax Aug 7, 2017

Choose a reason for hiding this comment

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

@jasongin But that returns a Maybe because it can throw, Symbol.toPrimitive says hi. ;)

Copy link
Member

Choose a reason for hiding this comment

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

Oh. Good to know. :)

@gabrielschulhof
Copy link
Contributor Author

@jasongin @nodejs/addon-api
Here's what I have so far:

  • napi_remove_wrap() is a separate function that does the same thing as napi_unwrap(), except it also restores the JavaScript object to its pristine state so it can be wrapped again.
  • After calling napi_remove_wrap() the user becomes responsible for freeing the pointer that was being handled by a finalize callback.
  • We need a NAPI_PREAMBLE(env) for napi_remove_wrap() because setting the prototype may throw. In fact, we already have NAPI_PREAMBLE(env) for napi_wrap(), which also manipulates the prototype chain, so I guess the need for NAPI_PREAMBLE(env) in napi_remove_wrap() is for the same reason.

Question:

Do we want to render the functionality provided by napi_remove_wrap() as a new boolean parameter to napi_unwrap() such that true indicates that you wish to retrieve the pointer and detach it from the JavaScript object, and false indicates that you merely wish to retrieve the pointer? This would entail an additional ABI break - which is theoretically OK(ish) in experimental.

@gabrielschulhof
Copy link
Contributor Author

@addaleax

@gabrielschulhof gabrielschulhof added the semver-minor PRs that contain new features and should be released in the next minor version. label Aug 9, 2017
@gabrielschulhof
Copy link
Contributor Author

During tonight's N-API meeting we decided to avoid the ABI break. Let's keep napi_remove_wrap() as a separate function.

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

@gabrielschulhof
Copy link
Contributor Author

Gabriel Schulhof added 3 commits August 17, 2017 14:34
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

Fixes: nodejs/abi-stable-node#266
Mention that pointer ownership returns to the application after
napi_remove_wrap() and add test making sure the finalize callback gets
called when it needs to, and that it does not get called when it
shouldn't.
@gabrielschulhof
Copy link
Contributor Author

@mhdawson
Copy link
Member

AIX issue in CI was know unrelated issue.

@mhdawson
Copy link
Member

mhdawson commented Aug 18, 2017

Arm failure was a build failure so likely unrelated, but due to failures on other platforms lets try the CI again: https://ci.nodejs.org/job/node-test-commit/11887/

@mhdawson
Copy link
Member

OSX and fips failures in latest run were infra related and they had run/passed in earlier CI runs to validate this PR so I think we are good to land.

@mhdawson
Copy link
Member

@addaleax @jasongin I think your comments/concerns have been addressed so I'll plan to land on Monday unless I hear otherwise from you.

gabrielschulhof pushed a commit that referenced this pull request Aug 22, 2017
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

PR-URL: #14658
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Fixes: nodejs/abi-stable-node#266
@gabrielschulhof
Copy link
Contributor Author

Landed in 70664bf.

@gabrielschulhof gabrielschulhof deleted the napi-remove-wrap branch August 22, 2017 09:36
addaleax pushed a commit to addaleax/ayo that referenced this pull request Aug 25, 2017
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

PR-URL: nodejs/node#14658
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Fixes: nodejs/abi-stable-node#266
addaleax pushed a commit to ayojs/ayo that referenced this pull request Aug 28, 2017
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

PR-URL: nodejs/node#14658
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Fixes: nodejs/abi-stable-node#266
MylesBorins pushed a commit that referenced this pull request Sep 10, 2017
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

PR-URL: #14658
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Fixes: nodejs/abi-stable-node#266
@MylesBorins MylesBorins mentioned this pull request Sep 10, 2017
MylesBorins pushed a commit that referenced this pull request Sep 12, 2017
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

PR-URL: #14658
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Fixes: nodejs/abi-stable-node#266
gabrielschulhof pushed a commit to gabrielschulhof/node that referenced this pull request Apr 10, 2018
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

PR-URL: nodejs#14658
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Fixes: nodejs/abi-stable-node#266
MylesBorins pushed a commit that referenced this pull request Apr 16, 2018
Calling napi_wrap() twice on the same object has the result of returning
napi_invalid_arg on the second call. However, sometimes it is necessary
to replace the native pointer associated with a given object. This new
API allows one to remove an existing pointer, returning the object to
its pristine, non-wrapped state.

Backport-PR-URL: #19447
PR-URL: #14658
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Fixes: nodejs/abi-stable-node#266
@MylesBorins MylesBorins mentioned this pull request Apr 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. node-api Issues and PRs related to the Node-API. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

napi_wrap only allow one wrapper to one object
6 participants