Skip to content

Commit c529168

Browse files
committed
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. PR-URL: #19402 Refs: nodejs/node-chakracore#500 Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 08a36a0 commit c529168

File tree

3 files changed

+125
-48
lines changed

3 files changed

+125
-48
lines changed

doc/api/n-api.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,13 +1797,17 @@ napi_status napi_get_value_int32(napi_env env,
17971797
- `[out] result`: C int32 primitive equivalent of the given JavaScript Number.
17981798

17991799
Returns `napi_ok` if the API succeeded. If a non-number `napi_value`
1800-
is passed in `napi_number_expected .
1800+
is passed in `napi_number_expected`.
18011801

18021802
This API returns the C int32 primitive equivalent
1803-
of the given JavaScript Number. If the number exceeds the range of the
1804-
32 bit integer, then the result is truncated to the equivalent of the
1805-
bottom 32 bits. This can result in a large positive number becoming
1806-
a negative number if the value is > 2^31 -1.
1803+
of the given JavaScript Number.
1804+
1805+
If the number exceeds the range of the 32 bit integer, then the result is
1806+
truncated to the equivalent of the bottom 32 bits. This can result in a large
1807+
positive number becoming a negative number if the value is > 2^31 -1.
1808+
1809+
Non-finite number values (NaN, positive infinity, or negative infinity) set the
1810+
result to zero.
18071811

18081812
#### napi_get_value_int64
18091813
<!-- YAML
@@ -1822,8 +1826,17 @@ napi_status napi_get_value_int64(napi_env env,
18221826
Returns `napi_ok` if the API succeeded. If a non-number `napi_value`
18231827
is passed in it returns `napi_number_expected`.
18241828

1825-
This API returns the C int64 primitive equivalent of the given
1826-
JavaScript Number.
1829+
This API returns the C int64 primitive equivalent of the given JavaScript
1830+
Number.
1831+
1832+
Number values outside the range of
1833+
[`Number.MIN_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.min_safe_integer)
1834+
-(2^53 - 1) -
1835+
[`Number.MAX_SAFE_INTEGER`](https://tc39.github.io/ecma262/#sec-number.max_safe_integer)
1836+
(2^53 - 1) will lose precision.
1837+
1838+
Non-finite number values (NaN, positive infinity, or negative infinity) set the
1839+
result to zero.
18271840

18281841
#### napi_get_value_string_latin1
18291842
<!-- YAML

src/node_api.cc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,15 +2170,16 @@ napi_status napi_get_value_int64(napi_env env,
21702170

21712171
RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
21722172

2173-
// v8::Value::IntegerValue() converts NaN to INT64_MIN, inconsistent with
2174-
// v8::Value::Int32Value() that converts NaN to 0. So special-case NaN here.
2173+
// v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN,
2174+
// inconsistent with v8::Value::Int32Value() which converts those values to 0.
2175+
// Special-case all non-finite values to match that behavior.
21752176
double doubleValue = val.As<v8::Number>()->Value();
2176-
if (std::isnan(doubleValue)) {
2177-
*result = 0;
2178-
} else {
2177+
if (std::isfinite(doubleValue)) {
21792178
// Empty context: https://github.com/nodejs/node/issues/14379
21802179
v8::Local<v8::Context> context;
21812180
*result = val->IntegerValue(context).FromJust();
2181+
} else {
2182+
*result = 0;
21822183
}
21832184

21842185
return napi_clear_last_error(env);

test/addons-napi/test_number/test.js

Lines changed: 99 additions & 36 deletions
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+
testNumber(Number.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 behavior 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)