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

napi: add bigint support #21226

Closed
wants to merge 6 commits into from
Closed

Conversation

devsnek
Copy link
Member

@devsnek devsnek commented Jun 9, 2018

Checklist

/cc @nodejs/n-api passing a bigint to a napi function still throws Invalid argument and i can't figure out why.

/cc @nodejs/v8 should we block on this until we have a way to get values from bigint?

  • 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

@devsnek devsnek added the node-api Issues and PRs related to the Node-API. label Jun 9, 2018
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Jun 9, 2018
@devsnek devsnek added wip Issues and PRs that are still a work in progress. and removed lib / src Issues and PRs related to general changes in the lib or src directory. c++ Issues and PRs that require attention from people who are familiar with C++. labels Jun 9, 2018
@hashseed
Copy link
Member

hashseed commented Jun 9, 2018

It's not entirely clear what is expected from a BigInt API in V8. You can discuss here: https://bugs.chromium.org/p/v8/issues/detail?id=7712

@mhdawson
Copy link
Member

This should also wait until we have completed #19962 which will be updated so new functions go in initially as experimental.

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.

Making as changes requested so that it does not land until we document how to guard as experimental initially.

Copy link
Member

@TimothyGu TimothyGu left a comment

Choose a reason for hiding this comment

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

Missing documentation. Particularly important is the question of endianness for the word array-based functions.

src/node_api.cc Outdated
RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected);

val.As<v8::BigInt>()->ToWordsArray(
sign_bit, reinterpret_cast<int*>(word_count), words);
Copy link
Member

Choose a reason for hiding this comment

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

The most significant part of word_count might not get cleared after the cast.

Copy link
Member Author

Choose a reason for hiding this comment

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

how should i fix this

Copy link
Member

Choose a reason for hiding this comment

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

just word_count = 0; before executing ToWordsArray

src/node_api.cc Outdated
v8::Local<v8::Context> context = env->isolate->GetCurrentContext();

v8::MaybeLocal<v8::BigInt> b = v8::BigInt::NewFromWords(
context, reinterpret_cast<int>(sign_bit), word_count, words);
Copy link
Member

Choose a reason for hiding this comment

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

We should throw when word_count > INT_MAX.

@devsnek
Copy link
Member Author

devsnek commented Jul 6, 2018

napi type checks fail between napi_valuetype and error_messages with the NAPI_EXPERIMENTAL gate. @mhdawson @kfarnung any ideas?

@@ -68,6 +75,9 @@ typedef enum {
napi_name_expected,
napi_function_expected,
napi_number_expected,
#ifdef NAPI_EXPERIMENTAL
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it's really necessary to gate them with NAPI_EXPERIMENTAL, just the new functions.

Do you agree @mhdawson @digitalinfinity @gabrielschulhof

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps we must not gate here because if two APIs (such as thread_safe and bigint) add new status codes while in experimental, they will have to come out of experimental in the order their added status codes appear, otherwise their emergence from experimental will itself constitute an ABI break, since their status codes would have to be moved before the bracket.

Copy link
Contributor

Choose a reason for hiding this comment

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

/me go PR the removal of the gate now 🙂

Copy link
Contributor

Choose a reason for hiding this comment

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

This also means that if we backport bigint but not thread_safe, we'll have to backport the status codes added by thread_safe so that the status codes for bigint will have the same value betweeen the backport and the current version.

@@ -68,6 +75,9 @@ typedef enum {
napi_name_expected,
napi_function_expected,
napi_number_expected,
#ifdef NAPI_EXPERIMENTAL
napi_bigint_expected,
#endif // NAPI_EXPERIMENTAL
Copy link
Contributor

Choose a reason for hiding this comment

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

We should only ever add enum values to the bottom since they need to be stable over time.

@@ -41,6 +41,9 @@ typedef enum {
napi_null,
napi_boolean,
napi_number,
#ifdef NAPI_EXPERIMENTAL
napi_bigint,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, we want existing values to remain unchanged.

src/node_api.h Outdated
@@ -635,6 +635,34 @@ NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env,

#ifdef NAPI_EXPERIMENTAL

NAPI_EXTERN napi_status napi_create_bigint_int64(napi_env env,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can we add these after the existing ones?

doc/api/n-api.md Outdated
@@ -105,6 +105,9 @@ typedef enum {
napi_name_expected,
napi_function_expected,
napi_number_expected,
#ifdef NAPI_EXPERIMENTAL
napi_bigint_expected,
Copy link
Contributor

Choose a reason for hiding this comment

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

This value should go to the end of the enum, otherwise we break ABI. Once you've moved it to the end of the enum, you also need to append a string explaining the error message to the end of the array of error messages, otherwise you get a failed assertion.

doc/api/n-api.md Outdated
@@ -1220,6 +1223,9 @@ typedef enum {
napi_null,
napi_boolean,
napi_number,
#ifdef NAPI_EXPERIMENTAL
napi_bigint,
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

Copy link
Contributor

@kfarnung kfarnung left a comment

Choose a reason for hiding this comment

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

We should only be adding to existing enums, not inserting new values.

Copy link
Member

@TimothyGu TimothyGu left a comment

Choose a reason for hiding this comment

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

I'd also like to mention in the docs that the output may be undefined if a method fails. Consider napi_get_value_bigint_words(). I'd assume the content of sign_bit to be indeterminate if that function fails for whatever reason.

doc/api/n-api.md Outdated

Returns `napi_ok` if the API succeeded.

This API is used to convert the C `int64_t` type to the JavaScript `BigInt`
Copy link
Member

Choose a reason for hiding this comment

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

s/is used to convert/converts/

Ditto below.

doc/api/n-api.md Outdated

The resulting number is calculated as:

`(-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...)`
Copy link
Member

Choose a reason for hiding this comment

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

The format could probably be improved a bit:

(–1)sign_bit (words[0] × (264)0 + words[1] × (264)1 + …)

TBH I'd still like to see a declarative explanation of what this means ("words is expressed in little endian").

doc/api/n-api.md Outdated
- `[out] result`: A `napi_value` representing a JavaScript `BigInt`.

Returns `napi_ok` if the API succeeded.

Copy link
Member

Choose a reason for hiding this comment

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

Missing a small blurb of what the function does, like the other functions do.

This API converts an array of unsigned 64-bit words into a single BigInt value.

doc/api/n-api.md Outdated
- `[in] value`: `napi_value` represenging JavaScript `BigInt`.
- `[out] result`: C int64_t primitive equivalent of the given JavaScript
`BigInt`.
- `[out] lossless`: Indicates if the bigint value was truncated losslessly.
Copy link
Member

Choose a reason for hiding this comment

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

Inconsistent capitalization: decide on BigInt or BigInt.

doc/api/n-api.md Outdated
```

- `[in] env`: The environment that the API is invoked under
- `[in] value`: `napi_value` represenging JavaScript `BigInt`.
Copy link
Member

Choose a reason for hiding this comment

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

typo: representing

doc/api/n-api.md Outdated
`napi_bigint_expected`.

This API returns the C int64_t primitive equivalent of the given JavaScript
`BigInt`. If needed it will truncate the value, setting `lossless` to `true`.
Copy link
Member

Choose a reason for hiding this comment

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

This behavior sounds a bit counterintuitive to me: doesn't truncation lose some value? Why is lossless set to true then?

Copy link
Member Author

Choose a reason for hiding this comment

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

typo :P

doc/api/n-api.md Outdated
```C
napi_status napi_get_bigint_word_count(napi_env env,
napi_value value,
size_t* word_count);
Copy link
Member

Choose a reason for hiding this comment

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

Instead of a separate API, could we instead make the functionality included in napi_get_value_bigint_words() when words pointer is set to NULL?

Copy link
Member Author

@devsnek devsnek Jul 6, 2018

Choose a reason for hiding this comment

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

this would require also having to set sign_bit to NULL but i like the idea, will work on it.

doc/api/n-api.md Outdated
- `[out] words`: Pointer to 64-bit word array.

Returns `napi_ok` if the API succeeded.

Copy link
Member

Choose a reason for hiding this comment

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

Also missing a sentence describing what the function does. I'd rather the longer descriptions in the parameter list be moved down here.

doc/api/n-api.md Outdated
- `[in] value`: `napi_value` representing JavaScript `BigInt`.
- `[out] sign_bit`: Integer representing if the JavaScript `BigInt` is positive
or negative.
- `[out] word_count`: Must be initialized to the length of the `words` array.
Copy link
Member

Choose a reason for hiding this comment

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

I know this convention hasn't been used in the document yet, but [in/out]?

src/node_api.cc Outdated
v8::Local<v8::Context> context = env->isolate->GetCurrentContext();

if (word_count > INT_MAX) {
return napi_set_last_error(env, napi_generic_failure);
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather this be consistent with what V8 does when the word count is too big, which is to throw a RangeError.

Copy link
Member Author

@devsnek devsnek Jul 6, 2018

Choose a reason for hiding this comment

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

if v8 handles it, all we have to do is nothing... right? the try_catch should carry the error

Copy link
Member

Choose a reason for hiding this comment

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

No, V8 only handles the error conditions within the range of int. We still need to handle the cases of greater than INT_MAX.

@devsnek devsnek force-pushed the feature/napi-bigint branch 2 times, most recently from fe73e59 to 5102bd1 Compare July 6, 2018 03:31
@devsnek
Copy link
Member Author

devsnek commented Jul 6, 2018

not really sure what happened but its back to throwing Invalid argument when a bigint is passed

doc/api/n-api.md Outdated
- `[in/out] word_count`: Must be initialized to the length of the `words`
array. Upon return, it will be set to the actual number of words that
would be needed to store this `BigInt`.
- `[out] words`: Pointer to 64-bit word array.
Copy link
Contributor

Choose a reason for hiding this comment

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

It should probably indicate that the words array needs to be pre-allocated for clarity.

assert.strictEqual(num, TestUint64(num));
}
assert.strictEqual(num, TestWords(num));
});
Copy link
Contributor

Choose a reason for hiding this comment

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

It would probably be good to have a test or two for "lossy" conversions.

@devsnek
Copy link
Member Author

devsnek commented Jul 12, 2018

@devsnek
Copy link
Member Author

devsnek commented Jul 12, 2018

landing tomorrow afternoon (cst) if no one objects by then

/cc @addaleax @TimothyGu @mhdawson

@devsnek
Copy link
Member Author

devsnek commented Jul 12, 2018

landed in 1849a2b

@devsnek devsnek closed this Jul 12, 2018
devsnek added a commit that referenced this pull request Jul 12, 2018
PR-URL: #21226
Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
Reviewed-By: Kyle Farnung <kfarnung@microsoft.com>
@devsnek devsnek deleted the feature/napi-bigint branch July 12, 2018 18:03
@devsnek devsnek added notable-change PRs with changes that should be highlighted in changelogs. and removed wip Issues and PRs that are still a work in progress. labels Jul 12, 2018
targos pushed a commit that referenced this pull request Jul 14, 2018
PR-URL: #21226
Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
Reviewed-By: Kyle Farnung <kfarnung@microsoft.com>
@targos targos added the semver-minor PRs that contain new features and should be released in the next minor version. label Jul 17, 2018
targos added a commit that referenced this pull request Jul 17, 2018
Notable changes:

* console:
  * The `console.timeLog()` method has been implemented. (#21312)
* deps:
  * Upgrade to libuv 1.22.0. (#21731)
  * Upgrade to ICU 62.1 (Unicode 11, CLDR 33.1). (#21728)
* http:
  * Added support for passing both `timeout` and `agent` options to
    `http.request`. (#21204)
* napi:
  * Added experimental support for functions dealing with bigint numbers. (#21226)
* process:
  * The `process.hrtime.bigint()` method has been implemented. (#21256)
  * Added the `--title` command line argument to set the process title on
    startup. (#21477)
* trace_events:
  * Added process_name metadata. (#21477)
@targos targos mentioned this pull request Jul 17, 2018
targos added a commit that referenced this pull request Jul 18, 2018
Notable changes:

* console:
  * The `console.timeLog()` method has been implemented. (#21312)
* deps:
  * Upgrade to libuv 1.22.0. (#21731)
  * Upgrade to ICU 62.1 (Unicode 11, CLDR 33.1). (#21728)
* http:
  * Added support for passing both `timeout` and `agent` options to
    `http.request`. (#21204)
* napi:
  * Added experimental support for functions dealing with bigint numbers. (#21226)
* process:
  * The `process.hrtime.bigint()` method has been implemented. (#21256)
  * Added the `--title` command line argument to set the process title on
    startup. (#21477)
* trace_events:
  * Added process_name metadata. (#21477)
* Added new collaborators
  * codebytere - Shelley Vohr

PR-URL: #21851
targos added a commit that referenced this pull request Jul 18, 2018
Notable changes:

* console:
  * The `console.timeLog()` method has been implemented.
    (#21312)
* deps:
  * Upgrade to libuv 1.22.0. (#21731)
  * Upgrade to ICU 62.1 (Unicode 11, CLDR 33.1).
    (#21728)
* http:
  * Added support for passing both `timeout` and `agent` options to
    `http.request`. (#21204)
* inspector:
  * Expose the original console API in `require('inspector').console`.
    (#21659)
* napi:
  * Added experimental support for functions dealing with bigint numbers.
    (#21226)
* process:
  * The `process.hrtime.bigint()` method has been implemented.
    (#21256)
  * Added the `--title` command line argument to set the process title on
    startup. (#21477)
* trace_events:
  * Added process_name metadata.
    (#21477)
* Added new collaborators
  * codebytere - Shelley Vohr

PR-URL: #21851
@devsnek devsnek restored the feature/napi-bigint branch July 18, 2018 15:52
targos added a commit that referenced this pull request Jul 18, 2018
Notable changes:

* console:
  * The `console.timeLog()` method has been implemented.
    (#21312)
* deps:
  * Upgrade to libuv 1.22.0. (#21731)
  * Upgrade to ICU 62.1 (Unicode 11, CLDR 33.1).
    (#21728)
* http:
  * Added support for passing both `timeout` and `agent` options to
    `http.request`. (#21204)
* inspector:
  * Expose the original console API in `require('inspector').console`.
    (#21659)
* napi:
  * Added experimental support for functions dealing with bigint numbers.
    (#21226)
* process:
  * The `process.hrtime.bigint()` method has been implemented.
    (#21256)
  * Added the `--title` command line argument to set the process title on
    startup. (#21477)
* trace_events:
  * Added process_name metadata.
    (#21477)
* Added new collaborators
  * codebytere - Shelley Vohr

PR-URL: #21851
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
node-api Issues and PRs related to the Node-API. notable-change PRs with changes that should be highlighted in changelogs. 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.

None yet

10 participants