Skip to content

Commit

Permalink
napi: add bigint support
Browse files Browse the repository at this point in the history
PR-URL: #21226
Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com>
Reviewed-By: Kyle Farnung <kfarnung@microsoft.com>
  • Loading branch information
devsnek authored and targos committed Jul 14, 2018
1 parent 27d17d4 commit 3096ee5
Show file tree
Hide file tree
Showing 8 changed files with 523 additions and 6 deletions.
164 changes: 162 additions & 2 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1225,6 +1224,7 @@ typedef enum {
napi_object,
napi_function,
napi_external,
napi_bigint,
} napi_valuetype;
```

Expand All @@ -1250,6 +1250,8 @@ typedef enum {
napi_uint32_array,
napi_float32_array,
napi_float64_array,
napi_bigint64_array,
napi_biguint64_array,
} napi_typedarray_type;
```

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 `false`.
#### 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 `false`.


#### 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.
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
Expand Down
137 changes: 135 additions & 2 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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");
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);
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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) {
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);

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.
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
}

Expand Down
Loading

0 comments on commit 3096ee5

Please sign in to comment.