Skip to content

Commit 07a6770

Browse files
kfarnungMylesBorins
authored andcommitted
n-api: add more int64_t tests
* Updated tests for `Number` and `int32_t` * Added new tests for `int64_t` * Updated N-API `int64_t` behavior to return zero for all non-finite numbers * Clarified the documentation for these calls. Backport-PR-URL: #19265 PR-URL: #19402 Refs: nodejs/node-chakracore#500 Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 8b3ef46 commit 07a6770

File tree

3 files changed

+125
-48
lines changed

3 files changed

+125
-48
lines changed

doc/api/n-api.md

+20-7
Original file line numberDiff line numberDiff line change
@@ -1808,13 +1808,17 @@ napi_status napi_get_value_int32(napi_env env,
18081808
- `[out] result`: C int32 primitive equivalent of the given JavaScript Number.
18091809

18101810
Returns `napi_ok` if the API succeeded. If a non-number `napi_value`
1811-
is passed in `napi_number_expected .
1811+
is passed in `napi_number_expected`.
18121812

18131813
This API returns the C int32 primitive equivalent
1814-
of the given JavaScript Number. If the number exceeds the range of the
1815-
32 bit integer, then the result is truncated to the equivalent of the
1816-
bottom 32 bits. This can result in a large positive number becoming
1817-
a negative number if the value is > 2^31 -1.
1814+
of the given JavaScript Number.
1815+
1816+
If the number exceeds the range of the 32 bit integer, then the result is
1817+
truncated to the equivalent of the bottom 32 bits. This can result in a large
1818+
positive number becoming a negative number if the value is > 2^31 -1.
1819+
1820+
Non-finite number values (NaN, positive infinity, or negative infinity) set the
1821+
result to zero.
18181822

18191823
#### napi_get_value_int64
18201824
<!-- YAML
@@ -1833,8 +1837,17 @@ napi_status napi_get_value_int64(napi_env env,
18331837
Returns `napi_ok` if the API succeeded. If a non-number `napi_value`
18341838
is passed in it returns `napi_number_expected`.
18351839

1836-
This API returns the C int64 primitive equivalent of the given
1837-
JavaScript Number.
1840+
This API returns the C int64 primitive equivalent of the given JavaScript
1841+
Number.
1842+
1843+
Number values outside the range of
1844+
[`Number.MIN_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.min_safe_integer)
1845+
-(2^53 - 1) -
1846+
[`Number.MAX_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.max_safe_integer)
1847+
(2^53 - 1) will lose precision.
1848+
1849+
Non-finite number values (NaN, positive infinity, or negative infinity) set the
1850+
result to zero.
18381851

18391852
#### napi_get_value_string_latin1
18401853
<!-- YAML

src/node_api.cc

+6-5
Original file line numberDiff line numberDiff line change
@@ -2197,15 +2197,16 @@ napi_status napi_get_value_int64(napi_env env,
21972197

21982198
RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
21992199

2200-
// v8::Value::IntegerValue() converts NaN to INT64_MIN, inconsistent with
2201-
// v8::Value::Int32Value() that converts NaN to 0. So special-case NaN here.
2200+
// v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN,
2201+
// inconsistent with v8::Value::Int32Value() which converts those values to 0.
2202+
// Special-case all non-finite values to match that behavior.
22022203
double doubleValue = val.As<v8::Number>()->Value();
2203-
if (std::isnan(doubleValue)) {
2204-
*result = 0;
2205-
} else {
2204+
if (std::isfinite(doubleValue)) {
22062205
// Empty context: https://github.com/nodejs/node/issues/14379
22072206
v8::Local<v8::Context> context;
22082207
*result = val->IntegerValue(context).FromJust();
2208+
} else {
2209+
*result = 0;
22092210
}
22102211

22112212
return napi_clear_last_error(env);

test/addons-napi/test_number/test.js

+99-36
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,113 @@ const test_number = require(`./build/${common.buildType}/test_number`);
55

66

77
// testing api calls for number
8-
assert.strictEqual(0, test_number.Test(0));
9-
assert.strictEqual(1, test_number.Test(1));
10-
assert.strictEqual(-1, test_number.Test(-1));
11-
assert.strictEqual(100, test_number.Test(100));
12-
assert.strictEqual(2121, test_number.Test(2121));
13-
assert.strictEqual(-1233, test_number.Test(-1233));
14-
assert.strictEqual(986583, test_number.Test(986583));
15-
assert.strictEqual(-976675, test_number.Test(-976675));
8+
function testNumber(num) {
9+
assert.strictEqual(num, test_number.Test(num));
10+
}
1611

17-
const num1 = 98765432213456789876546896323445679887645323232436587988766545658;
18-
assert.strictEqual(num1, test_number.Test(num1));
12+
testNumber(0);
13+
testNumber(-0);
14+
testNumber(1);
15+
testNumber(-1);
16+
testNumber(100);
17+
testNumber(2121);
18+
testNumber(-1233);
19+
testNumber(986583);
20+
testNumber(-976675);
1921

20-
const num2 = -4350987086545760976737453646576078997096876957864353245245769809;
21-
assert.strictEqual(num2, test_number.Test(num2));
22+
testNumber(
23+
98765432213456789876546896323445679887645323232436587988766545658);
24+
testNumber(
25+
-4350987086545760976737453646576078997096876957864353245245769809);
26+
testNumber(Number.MIN_SAFE_INTEGER);
27+
testNumber(Number.MAX_SAFE_INTEGER);
28+
testNumber(Number.MAX_SAFE_INTEGER + 10);
2229

23-
const num3 = Number.MAX_SAFE_INTEGER;
24-
assert.strictEqual(num3, test_number.Test(num3));
30+
testNumber(Number.MIN_VALUE);
31+
testNumber(Number.MAX_VALUE);
32+
testNumber(Number.MAX_VALUE + 10);
2533

26-
const num4 = Number.MAX_SAFE_INTEGER + 10;
27-
assert.strictEqual(num4, test_number.Test(num4));
34+
testNumber(Number.POSITIVE_INFINITY);
35+
testNumber(Number.NEGATIVE_INFINITY);
36+
assert(Object.is(NaN, test_number.Test(NaN)));
2837

29-
const num5 = Number.MAX_VALUE;
30-
assert.strictEqual(num5, test_number.Test(num5));
38+
// validate documented behavior when value is retrieved as 32-bit integer with
39+
// `napi_get_value_int32`
40+
function testInt32(input, expected = input) {
41+
assert.strictEqual(expected, test_number.TestInt32Truncation(input));
42+
}
3143

32-
const num6 = Number.MAX_VALUE + 10;
33-
assert.strictEqual(num6, test_number.Test(num6));
44+
// Test zero
45+
testInt32(0.0, 0);
46+
testInt32(-0.0, 0);
3447

35-
const num7 = Number.POSITIVE_INFINITY;
36-
assert.strictEqual(num7, test_number.Test(num7));
48+
// Test min/max int32 range
49+
testInt32(-Math.pow(2, 31));
50+
testInt32(Math.pow(2, 31) - 1);
3751

38-
const num8 = Number.NEGATIVE_INFINITY;
39-
assert.strictEqual(num8, test_number.Test(num8));
52+
// Test overflow scenarios
53+
testInt32(4294967297, 1);
54+
testInt32(4294967296, 0);
55+
testInt32(4294967295, -1);
56+
testInt32(4294967296 * 5 + 3, 3);
4057

58+
// Test min/max safe integer range
59+
testInt32(Number.MIN_SAFE_INTEGER, 1);
60+
testInt32(Number.MAX_SAFE_INTEGER, -1);
4161

42-
// validate documented behaviour when value is retrieved
43-
// as 32 bit integer with napi_get_value_int32
44-
assert.strictEqual(1, test_number.TestInt32Truncation(4294967297));
45-
assert.strictEqual(0, test_number.TestInt32Truncation(4294967296));
46-
assert.strictEqual(-1, test_number.TestInt32Truncation(4294967295));
47-
assert.strictEqual(3, test_number.TestInt32Truncation(4294967296 * 5 + 3));
62+
// Test within int64_t range (with precision loss)
63+
testInt32(-Math.pow(2, 63) + (Math.pow(2, 9) + 1), 1024);
64+
testInt32(Math.pow(2, 63) - (Math.pow(2, 9) + 1), -1024);
4865

49-
// validate that the boundaries of safe integer can be passed through
50-
// successfully
51-
assert.strictEqual(Number.MAX_SAFE_INTEGER,
52-
test_number.TestInt64Truncation(Number.MAX_SAFE_INTEGER));
53-
assert.strictEqual(Number.MIN_SAFE_INTEGER,
54-
test_number.TestInt64Truncation(Number.MIN_SAFE_INTEGER));
66+
// Test min/max double value
67+
testInt32(-Number.MIN_VALUE, 0);
68+
testInt32(Number.MIN_VALUE, 0);
69+
testInt32(-Number.MAX_VALUE, 0);
70+
testInt32(Number.MAX_VALUE, 0);
71+
72+
// Test outside int64_t range
73+
testInt32(-Math.pow(2, 63) + (Math.pow(2, 9)), 0);
74+
testInt32(Math.pow(2, 63) - (Math.pow(2, 9)), 0);
75+
76+
// Test non-finite numbers
77+
testInt32(Number.POSITIVE_INFINITY, 0);
78+
testInt32(Number.NEGATIVE_INFINITY, 0);
79+
testInt32(Number.NaN, 0);
80+
81+
// validate documented behavior when value is retrieved as 64-bit integer with
82+
// `napi_get_value_int64`
83+
function testInt64(input, expected = input) {
84+
assert.strictEqual(expected, test_number.TestInt64Truncation(input));
85+
}
86+
87+
// Both V8 and ChakraCore return a sentinel value of `0x8000000000000000` when
88+
// the conversion goes out of range, but V8 treats it as unsigned in some cases.
89+
const RANGEERROR_POSITIVE = Math.pow(2, 63);
90+
const RANGEERROR_NEGATIVE = -Math.pow(2, 63);
91+
92+
// Test zero
93+
testInt64(0.0, 0);
94+
testInt64(-0.0, 0);
95+
96+
// Test min/max safe integer range
97+
testInt64(Number.MIN_SAFE_INTEGER);
98+
testInt64(Number.MAX_SAFE_INTEGER);
99+
100+
// Test within int64_t range (with precision loss)
101+
testInt64(-Math.pow(2, 63) + (Math.pow(2, 9) + 1));
102+
testInt64(Math.pow(2, 63) - (Math.pow(2, 9) + 1));
103+
104+
// Test min/max double value
105+
testInt64(-Number.MIN_VALUE, 0);
106+
testInt64(Number.MIN_VALUE, 0);
107+
testInt64(-Number.MAX_VALUE, RANGEERROR_NEGATIVE);
108+
testInt64(Number.MAX_VALUE, RANGEERROR_POSITIVE);
109+
110+
// Test outside int64_t range
111+
testInt64(-Math.pow(2, 63) + (Math.pow(2, 9)), RANGEERROR_NEGATIVE);
112+
testInt64(Math.pow(2, 63) - (Math.pow(2, 9)), RANGEERROR_POSITIVE);
113+
114+
// Test non-finite numbers
115+
testInt64(Number.POSITIVE_INFINITY, 0);
116+
testInt64(Number.NEGATIVE_INFINITY, 0);
117+
testInt64(Number.NaN, 0);

0 commit comments

Comments
 (0)