Skip to content

Commit 3aa5b7d

Browse files
kfarnungMylesBorins
authored andcommittedApr 16, 2018
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: #19447 PR-URL: #19402 Refs: nodejs/node-chakracore#500 Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent abd9fd6 commit 3aa5b7d

File tree

3 files changed

+135
-50
lines changed

3 files changed

+135
-50
lines changed
 

‎doc/api/n-api.md

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

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

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

18171821
#### napi_get_value_int64
18181822
<!-- YAML
@@ -1831,8 +1835,17 @@ napi_status napi_get_value_int64(napi_env env,
18311835
Returns `napi_ok` if the API succeeded. If a non-number `napi_value`
18321836
is passed in it returns `napi_number_expected`.
18331837

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

18371850
#### napi_get_value_string_latin1
18381851
<!-- YAML

‎src/node_api.cc

+16-7
Original file line numberDiff line numberDiff line change
@@ -2193,15 +2193,24 @@ napi_status napi_get_value_int64(napi_env env,
21932193

21942194
RETURN_STATUS_IF_FALSE(env, val->IsNumber(), napi_number_expected);
21952195

2196-
// v8::Value::IntegerValue() converts NaN to INT64_MIN, inconsistent with
2197-
// v8::Value::Int32Value() that converts NaN to 0. So special-case NaN here.
2196+
// v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN,
2197+
// inconsistent with v8::Value::Int32Value() which converts those values to 0.
2198+
// Special-case all non-finite values to match that behavior.
21982199
double doubleValue = val.As<v8::Number>()->Value();
2199-
if (std::isnan(doubleValue)) {
2200-
*result = 0;
2200+
if (std::isfinite(doubleValue)) {
2201+
// v8::Value::IntegerValue() as shipped with v6.x returns inconsistent
2202+
// values outside of the int64_t range. We rectify that here.
2203+
if (doubleValue >= static_cast<double>(INT64_MAX)) {
2204+
*result = INT64_MAX;
2205+
} else if (doubleValue <= static_cast<double>((int64_t)-INT64_MAX - 1)) {
2206+
*result = -INT64_MAX - 1;
2207+
} else {
2208+
// Empty context: https://github.com/nodejs/node/issues/14379
2209+
v8::Local<v8::Context> context;
2210+
*result = val->IntegerValue(context).FromJust();
2211+
}
22012212
} else {
2202-
// Empty context: https://github.com/nodejs/node/issues/14379
2203-
v8::Local<v8::Context> context;
2204-
*result = val->IntegerValue(context).FromJust();
2213+
*result = 0;
22052214
}
22062215

22072216
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)
Please sign in to comment.