-
Notifications
You must be signed in to change notification settings - Fork 29.9k
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
napi: add bigint support #21226
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -113,10 +113,9 @@ typedef enum { | |
napi_escape_called_twice, | ||
napi_handle_scope_mismatch, | ||
napi_callback_scope_mismatch, | ||
#ifdef NAPI_EXPERIMENTAL | ||
napi_queue_full, | ||
napi_closing, | ||
#endif // NAPI_EXPERIMENTAL | ||
napi_bigint_expected, | ||
} napi_status; | ||
``` | ||
If additional information is required upon an API returning a failed status, | ||
|
@@ -1225,6 +1224,7 @@ typedef enum { | |
napi_object, | ||
napi_function, | ||
napi_external, | ||
napi_bigint, | ||
} napi_valuetype; | ||
``` | ||
|
||
|
@@ -1250,6 +1250,8 @@ typedef enum { | |
napi_uint32_array, | ||
napi_float32_array, | ||
napi_float64_array, | ||
napi_bigint64_array, | ||
napi_biguint64_array, | ||
} napi_typedarray_type; | ||
``` | ||
|
||
|
@@ -1691,6 +1693,78 @@ This API is used to convert from the C `double` type to the JavaScript | |
The JavaScript `Number` type is described in | ||
[Section 6.1.6][] of the ECMAScript Language Specification. | ||
|
||
#### napi_create_bigint_int64 | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
> Stability: 1 - Experimental | ||
|
||
```C | ||
napi_status napi_create_bigint_int64(napi_env env, | ||
int64_t value, | ||
napi_value* result); | ||
``` | ||
|
||
- `[in] env`: The environment that the API is invoked under. | ||
- `[in] value`: Integer value to be represented in JavaScript. | ||
- `[out] result`: A `napi_value` representing a JavaScript `BigInt`. | ||
|
||
Returns `napi_ok` if the API succeeded. | ||
|
||
This API converts the C `int64_t` type to the JavaScript `BigInt` type. | ||
|
||
#### napi_create_bigint_uint64 | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
> Stability: 1 - Experimental | ||
|
||
```C | ||
napi_status napi_create_bigint_uint64(napi_env env, | ||
uint64_t vaue, | ||
napi_value* result); | ||
``` | ||
|
||
- `[in] env`: The environment that the API is invoked under. | ||
- `[in] value`: Unsigned integer value to be represented in JavaScript. | ||
- `[out] result`: A `napi_value` representing a JavaScript `BigInt`. | ||
|
||
Returns `napi_ok` if the API succeeded. | ||
|
||
This API converts the C `uint64_t` type to the JavaScript `BigInt` type. | ||
|
||
#### napi_create_bigint_words | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
> Stability: 1 - Experimental | ||
|
||
```C | ||
napi_status napi_create_bigint_words(napi_env env, | ||
int sign_bit, | ||
size_t word_count, | ||
const uint64_t* words, | ||
napi_value* result); | ||
``` | ||
|
||
- `[in] env`: The environment that the API is invoked under. | ||
- `[in] sign_bit`: Determines if the resulting `BigInt` will be positive or | ||
negative. | ||
- `[in] word_count`: The length of the `words` array. | ||
- `[in] words`: An array of `uint64_t` little-endian 64-bit words. | ||
- `[out] result`: A `napi_value` representing a JavaScript `BigInt`. | ||
|
||
Returns `napi_ok` if the API succeeded. | ||
|
||
This API converts an array of unsigned 64-bit words into a single `BigInt` | ||
value. | ||
|
||
The resulting `BigInt` is calculated as: (–1)<sup>`sign_bit`</sup> (`words[0]` | ||
× (2<sup>64</sup>)<sup>0</sup> + `words[1]` × (2<sup>64</sup>)<sup>1</sup> + …) | ||
|
||
#### napi_create_string_latin1 | ||
<!-- YAML | ||
added: v8.0.0 | ||
|
@@ -1975,6 +2049,92 @@ in it returns `napi_number_expected`. | |
This API returns the C double primitive equivalent of the given JavaScript | ||
`Number`. | ||
|
||
#### napi_get_value_bigint_int64 | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
> Stability: 1 - Experimental | ||
|
||
```C | ||
napi_status napi_get_value_bigint_int64(napi_env env, | ||
napi_value value, | ||
int64_t* result, | ||
bool* lossless); | ||
``` | ||
|
||
- `[in] env`: The environment that the API is invoked under | ||
- `[in] value`: `napi_value` representing JavaScript `BigInt`. | ||
- `[out] result`: C `int64_t` primitive equivalent of the given JavaScript | ||
`BigInt`. | ||
- `[out] lossless`: Indicates whether the `BigInt` value was converted | ||
losslessly. | ||
|
||
Returns `napi_ok` if the API succeeded. If a non-`BigInt` is passed in it | ||
returns `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`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo :P |
||
|
||
|
||
#### napi_get_value_bigint_uint64 | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
> Stability: 1 - Experimental | ||
|
||
```C | ||
napi_status napi_get_value_bigint_uint64(napi_env env, | ||
napi_value value, | ||
uint64_t* result, | ||
bool* lossless); | ||
``` | ||
|
||
- `[in] env`: The environment that the API is invoked under. | ||
- `[in] value`: `napi_value` representing JavaScript `BigInt`. | ||
- `[out] result`: C `uint64_t` primitive equivalent of the given JavaScript | ||
`BigInt`. | ||
- `[out] lossless`: Indicates whether the `BigInt` value was converted | ||
losslessly. | ||
|
||
Returns `napi_ok` if the API succeeded. If a non-`BigInt` is passed in it | ||
returns `napi_bigint_expected`. | ||
|
||
This API returns the C `uint64_t` primitive equivalent of the given JavaScript | ||
`BigInt`. If needed it will truncate the value, setting `lossless` to `true`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this sentence is backwards, it should set it to |
||
|
||
|
||
#### napi_get_value_bigint_words | ||
<!-- YAML | ||
added: REPLACEME | ||
--> | ||
|
||
> Stability: 1 - Experimental | ||
|
||
```C | ||
napi_status napi_get_value_bigint_words(napi_env env, | ||
napi_value value, | ||
size_t* word_count, | ||
int* sign_bit, | ||
uint64_t* words); | ||
``` | ||
|
||
- `[in] env`: The environment that the API is invoked under. | ||
- `[in] value`: `napi_value` representing JavaScript `BigInt`. | ||
- `[out] sign_bit`: Integer representing if the JavaScript `BigInt` is positive | ||
or negative. | ||
- `[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 a pre-allocated 64-bit word array. | ||
|
||
Returns `napi_ok` if the API succeeded. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
This API converts a single `BigInt` value into a sign bit, 64-bit little-endian | ||
array, and the number of elements in the array. `sign_bit` and `words` may be | ||
both set to `NULL`, in order to get only `word_count`. | ||
|
||
#### napi_get_value_external | ||
<!-- YAML | ||
added: v8.0.0 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -926,7 +926,8 @@ const char* error_messages[] = {nullptr, | |
"Invalid handle scope usage", | ||
"Invalid callback scope usage", | ||
"Thread-safe function queue is full", | ||
"Thread-safe function handle is closing" | ||
"Thread-safe function handle is closing", | ||
"A bigint was expected", | ||
}; | ||
|
||
static inline napi_status napi_clear_last_error(napi_env env) { | ||
|
@@ -958,7 +959,7 @@ napi_status napi_get_last_error_info(napi_env env, | |
// We don't have a napi_status_last as this would result in an ABI | ||
// change each time a message was added. | ||
static_assert( | ||
node::arraysize(error_messages) == napi_closing + 1, | ||
node::arraysize(error_messages) == napi_bigint_expected + 1, | ||
"Count of error messages must match count of error values"); | ||
CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); | ||
|
||
|
@@ -1713,6 +1714,58 @@ napi_status napi_create_int64(napi_env env, | |
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_create_bigint_int64(napi_env env, | ||
int64_t value, | ||
napi_value* result) { | ||
CHECK_ENV(env); | ||
CHECK_ARG(env, result); | ||
|
||
*result = v8impl::JsValueFromV8LocalValue( | ||
v8::BigInt::New(env->isolate, value)); | ||
|
||
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_create_bigint_uint64(napi_env env, | ||
uint64_t value, | ||
napi_value* result) { | ||
CHECK_ENV(env); | ||
CHECK_ARG(env, result); | ||
|
||
*result = v8impl::JsValueFromV8LocalValue( | ||
v8::BigInt::NewFromUnsigned(env->isolate, value)); | ||
|
||
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_create_bigint_words(napi_env env, | ||
int sign_bit, | ||
size_t word_count, | ||
const uint64_t* words, | ||
napi_value* result) { | ||
NAPI_PREAMBLE(env); | ||
CHECK_ARG(env, words); | ||
CHECK_ARG(env, result); | ||
|
||
v8::Local<v8::Context> context = env->isolate->GetCurrentContext(); | ||
|
||
if (word_count > INT_MAX) { | ||
napi_throw_range_error(env, nullptr, "Maximum BigInt size exceeded"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this tested? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, this should probably just return If we do throw an error, we should introduce a new error code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See #21226 (comment). The rationale is that there should be no difference in how |
||
return napi_set_last_error(env, napi_pending_exception); | ||
} | ||
|
||
v8::MaybeLocal<v8::BigInt> b = v8::BigInt::NewFromWords( | ||
context, sign_bit, word_count, words); | ||
|
||
if (try_catch.HasCaught()) { | ||
return napi_set_last_error(env, napi_pending_exception); | ||
} else { | ||
CHECK_MAYBE_EMPTY(env, b, napi_generic_failure); | ||
*result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); | ||
return napi_clear_last_error(env); | ||
} | ||
} | ||
|
||
napi_status napi_get_boolean(napi_env env, bool value, napi_value* result) { | ||
CHECK_ENV(env); | ||
CHECK_ARG(env, result); | ||
|
@@ -1878,6 +1931,8 @@ napi_status napi_typeof(napi_env env, | |
|
||
if (v->IsNumber()) { | ||
*result = napi_number; | ||
} else if (v->IsBigInt()) { | ||
*result = napi_bigint; | ||
} else if (v->IsString()) { | ||
*result = napi_string; | ||
} else if (v->IsFunction()) { | ||
|
@@ -2201,6 +2256,72 @@ napi_status napi_get_value_int64(napi_env env, | |
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_get_value_bigint_int64(napi_env env, | ||
napi_value value, | ||
int64_t* result, | ||
bool* lossless) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @nodejs/n-api Side note … did we make a conscious decision to allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what did C use before There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @devsnek Any integral type, and macros representing true (usually 1) and false (0). |
||
CHECK_ENV(env); | ||
CHECK_ARG(env, value); | ||
CHECK_ARG(env, result); | ||
CHECK_ARG(env, lossless); | ||
|
||
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); | ||
|
||
RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); | ||
|
||
*result = val.As<v8::BigInt>()->Int64Value(lossless); | ||
|
||
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_get_value_bigint_uint64(napi_env env, | ||
napi_value value, | ||
uint64_t* result, | ||
bool* lossless) { | ||
CHECK_ENV(env); | ||
CHECK_ARG(env, value); | ||
CHECK_ARG(env, result); | ||
CHECK_ARG(env, lossless); | ||
|
||
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); | ||
|
||
RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); | ||
|
||
*result = val.As<v8::BigInt>()->Uint64Value(lossless); | ||
|
||
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_get_value_bigint_words(napi_env env, | ||
napi_value value, | ||
int* sign_bit, | ||
size_t* word_count, | ||
uint64_t* words) { | ||
CHECK_ENV(env); | ||
CHECK_ARG(env, value); | ||
CHECK_ARG(env, word_count); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value); | ||
|
||
RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), napi_bigint_expected); | ||
|
||
v8::Local<v8::BigInt> big = val.As<v8::BigInt>(); | ||
|
||
int word_count_int = *word_count; | ||
|
||
if (sign_bit == nullptr && words == nullptr) { | ||
word_count_int = big->WordCount(); | ||
} else { | ||
CHECK_ARG(env, sign_bit); | ||
CHECK_ARG(env, words); | ||
big->ToWordsArray(sign_bit, &word_count_int, words); | ||
} | ||
|
||
*word_count = word_count_int; | ||
|
||
return napi_clear_last_error(env); | ||
} | ||
|
||
napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result) { | ||
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw | ||
// JS exceptions. | ||
|
@@ -3139,6 +3260,14 @@ napi_status napi_create_typedarray(napi_env env, | |
CREATE_TYPED_ARRAY( | ||
env, Float64Array, 8, buffer, byte_offset, length, typedArray); | ||
break; | ||
case napi_bigint64_array: | ||
CREATE_TYPED_ARRAY( | ||
env, BigInt64Array, 8, buffer, byte_offset, length, typedArray); | ||
break; | ||
case napi_biguint64_array: | ||
CREATE_TYPED_ARRAY( | ||
env, BigUint64Array, 8, buffer, byte_offset, length, typedArray); | ||
break; | ||
default: | ||
return napi_set_last_error(env, napi_invalid_arg); | ||
} | ||
|
@@ -3181,6 +3310,10 @@ napi_status napi_get_typedarray_info(napi_env env, | |
*type = napi_float32_array; | ||
} else if (value->IsFloat64Array()) { | ||
*type = napi_float64_array; | ||
} else if (value->IsBigInt64Array()) { | ||
*type = napi_bigint64_array; | ||
} else if (value->IsBigUint64Array()) { | ||
*type = napi_biguint64_array; | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
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.