Skip to content

Commit feb6a54

Browse files
gabrielschulhofruyadorno
authored andcommittedSep 12, 2023
node-api: implement external strings
Introduce APIs that allow for the creation of JavaScript strings without copying the underlying native string into the engine. The APIs fall back to regular string creation if the engine's external string APIs are unavailable. In this case, an optional boolean out-parameter indicates that the string was copied, and the optional finalizer is called if given. PR-URL: #48339 Fixes: #48198 Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com> Signed-off-by: Gabriel Schulhof <gabrielschulhof@gmail.com>
1 parent a30f2fb commit feb6a54

File tree

11 files changed

+713
-176
lines changed

11 files changed

+713
-176
lines changed
 

‎benchmark/napi/string/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build/

‎benchmark/napi/string/binding.c

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include <assert.h>
2+
#define NAPI_EXPERIMENTAL
3+
#include <node_api.h>
4+
5+
#define NAPI_CALL(call) \
6+
do { \
7+
napi_status status = call; \
8+
assert(status == napi_ok && #call " failed"); \
9+
} while (0);
10+
11+
#define EXPORT_FUNC(env, exports, name, func) \
12+
do { \
13+
napi_value js_func; \
14+
NAPI_CALL(napi_create_function( \
15+
(env), (name), NAPI_AUTO_LENGTH, (func), NULL, &js_func)); \
16+
NAPI_CALL(napi_set_named_property((env), (exports), (name), js_func)); \
17+
} while (0);
18+
19+
const char* one_byte_string = "The Quick Brown Fox Jumped Over The Lazy Dog.";
20+
const char16_t* two_byte_string =
21+
u"The Quick Brown Fox Jumped Over The Lazy Dog.";
22+
23+
#define DECLARE_BINDING(CapName, lowercase_name, var_name) \
24+
static napi_value CreateString##CapName(napi_env env, \
25+
napi_callback_info info) { \
26+
size_t argc = 4; \
27+
napi_value argv[4]; \
28+
uint32_t n; \
29+
uint32_t index; \
30+
napi_handle_scope scope; \
31+
napi_value js_string; \
32+
\
33+
NAPI_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); \
34+
NAPI_CALL(napi_get_value_uint32(env, argv[0], &n)); \
35+
NAPI_CALL(napi_open_handle_scope(env, &scope)); \
36+
NAPI_CALL(napi_call_function(env, argv[1], argv[2], 0, NULL, NULL)); \
37+
for (index = 0; index < n; index++) { \
38+
NAPI_CALL(napi_create_string_##lowercase_name( \
39+
env, (var_name), NAPI_AUTO_LENGTH, &js_string)); \
40+
} \
41+
NAPI_CALL(napi_call_function(env, argv[1], argv[3], 1, &argv[0], NULL)); \
42+
NAPI_CALL(napi_close_handle_scope(env, scope)); \
43+
\
44+
return NULL; \
45+
}
46+
47+
DECLARE_BINDING(Latin1, latin1, one_byte_string)
48+
DECLARE_BINDING(Utf8, utf8, one_byte_string)
49+
DECLARE_BINDING(Utf16, utf16, two_byte_string)
50+
51+
NAPI_MODULE_INIT() {
52+
EXPORT_FUNC(env, exports, "createStringLatin1", CreateStringLatin1);
53+
EXPORT_FUNC(env, exports, "createStringUtf8", CreateStringUtf8);
54+
EXPORT_FUNC(env, exports, "createStringUtf16", CreateStringUtf16);
55+
return exports;
56+
}

‎benchmark/napi/string/binding.gyp

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.c' ]
6+
}
7+
]
8+
}

‎benchmark/napi/string/index.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
const common = require('../../common.js');
3+
4+
let binding;
5+
try {
6+
binding = require(`./build/${common.buildType}/binding`);
7+
} catch {
8+
console.error(`${__filename}: Binding failed to load`);
9+
process.exit(0);
10+
}
11+
12+
const bench = common.createBenchmark(main, {
13+
n: [1e5, 1e6, 1e7],
14+
stringType: ['Latin1', 'Utf8', 'Utf16'],
15+
});
16+
17+
function main({ n, stringType }) {
18+
binding[`createString${stringType}`](n, bench, bench.start, bench.end);
19+
}

‎doc/api/n-api.md

+108-1
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ napiVersion: 1
801801

802802
Function pointer type for add-on provided functions that allow the user to be
803803
notified when externally-owned data is ready to be cleaned up because the
804-
object with which it was associated with, has been garbage-collected. The user
804+
object with which it was associated with has been garbage-collected. The user
805805
must provide a function satisfying the following signature which would get
806806
called upon the object's collection. Currently, `napi_finalize` can be used for
807807
finding out when objects that have external data are collected.
@@ -819,6 +819,11 @@ Since these functions may be called while the JavaScript engine is in a state
819819
where it cannot execute JavaScript code, some Node-API calls may return
820820
`napi_pending_exception` even when there is no exception pending.
821821

822+
In the case of [`node_api_create_external_string_latin1`][] and
823+
[`node_api_create_external_string_utf16`][] the `env` parameter may be null,
824+
because external strings can be collected during the latter part of environment
825+
shutdown.
826+
822827
Change History:
823828

824829
* experimental (`NAPI_EXPERIMENTAL` is defined):
@@ -2882,6 +2887,56 @@ string. The native string is copied.
28822887
The JavaScript `string` type is described in
28832888
[Section 6.1.4][] of the ECMAScript Language Specification.
28842889

2890+
#### `node_api_create_external_string_latin1`
2891+
2892+
<!-- YAML
2893+
added: REPLACEME
2894+
-->
2895+
2896+
> Stability: 1 - Experimental
2897+
2898+
```c
2899+
napi_status
2900+
node_api_create_external_string_latin1(napi_env env,
2901+
char* str,
2902+
size_t length,
2903+
napi_finalize finalize_callback,
2904+
void* finalize_hint,
2905+
napi_value* result,
2906+
bool* copied);
2907+
```
2908+
2909+
* `[in] env`: The environment that the API is invoked under.
2910+
* `[in] str`: Character buffer representing an ISO-8859-1-encoded string.
2911+
* `[in] length`: The length of the string in bytes, or `NAPI_AUTO_LENGTH` if it
2912+
is null-terminated.
2913+
* `[in] finalize_callback`: The function to call when the string is being
2914+
collected. The function will be called with the following parameters:
2915+
* `[in] env`: The environment in which the add-on is running. This value
2916+
may be null if the string is being collected as part of the termination
2917+
of the worker or the main Node.js instance.
2918+
* `[in] data`: This is the value `str` as a `void*` pointer.
2919+
* `[in] finalize_hint`: This is the value `finalize_hint` that was given
2920+
to the API.
2921+
[`napi_finalize`][] provides more details.
2922+
This parameter is optional. Passing a null value means that the add-on
2923+
doesn't need to be notified when the corresponding JavaScript string is
2924+
collected.
2925+
* `[in] finalize_hint`: Optional hint to pass to the finalize callback during
2926+
collection.
2927+
* `[out] result`: A `napi_value` representing a JavaScript `string`.
2928+
* `[out] copied`: Whether the string was copied. If it was, the finalizer will
2929+
already have been invoked to destroy `str`.
2930+
2931+
Returns `napi_ok` if the API succeeded.
2932+
2933+
This API creates a JavaScript `string` value from an ISO-8859-1-encoded C
2934+
string. The native string may not be copied and must thus exist for the entire
2935+
life cycle of the JavaScript value.
2936+
2937+
The JavaScript `string` type is described in
2938+
[Section 6.1.4][] of the ECMAScript Language Specification.
2939+
28852940
#### `napi_create_string_utf16`
28862941

28872942
<!-- YAML
@@ -2910,6 +2965,56 @@ The native string is copied.
29102965
The JavaScript `string` type is described in
29112966
[Section 6.1.4][] of the ECMAScript Language Specification.
29122967

2968+
#### `node_api_create_external_string_utf16`
2969+
2970+
<!-- YAML
2971+
added: REPLACEME
2972+
-->
2973+
2974+
> Stability: 1 - Experimental
2975+
2976+
```c
2977+
napi_status
2978+
node_api_create_external_string_utf16(napi_env env,
2979+
char16_t* str,
2980+
size_t length,
2981+
napi_finalize finalize_callback,
2982+
void* finalize_hint,
2983+
napi_value* result,
2984+
bool* copied);
2985+
```
2986+
2987+
* `[in] env`: The environment that the API is invoked under.
2988+
* `[in] str`: Character buffer representing a UTF16-LE-encoded string.
2989+
* `[in] length`: The length of the string in two-byte code units, or
2990+
`NAPI_AUTO_LENGTH` if it is null-terminated.
2991+
* `[in] finalize_callback`: The function to call when the string is being
2992+
collected. The function will be called with the following parameters:
2993+
* `[in] env`: The environment in which the add-on is running. This value
2994+
may be null if the string is being collected as part of the termination
2995+
of the worker or the main Node.js instance.
2996+
* `[in] data`: This is the value `str` as a `void*` pointer.
2997+
* `[in] finalize_hint`: This is the value `finalize_hint` that was given
2998+
to the API.
2999+
[`napi_finalize`][] provides more details.
3000+
This parameter is optional. Passing a null value means that the add-on
3001+
doesn't need to be notified when the corresponding JavaScript string is
3002+
collected.
3003+
* `[in] finalize_hint`: Optional hint to pass to the finalize callback during
3004+
collection.
3005+
* `[out] result`: A `napi_value` representing a JavaScript `string`.
3006+
* `[out] copied`: Whether the string was copied. If it was, the finalizer will
3007+
already have been invoked to destroy `str`.
3008+
3009+
Returns `napi_ok` if the API succeeded.
3010+
3011+
This API creates a JavaScript `string` value from a UTF16-LE-encoded C string.
3012+
The native string may not be copied and must thus exist for the entire life
3013+
cycle of the JavaScript value.
3014+
3015+
The JavaScript `string` type is described in
3016+
[Section 6.1.4][] of the ECMAScript Language Specification.
3017+
29133018
#### `napi_create_string_utf8`
29143019

29153020
<!-- YAML
@@ -6472,6 +6577,8 @@ the add-on's file name during loading.
64726577
[`napi_wrap`]: #napi_wrap
64736578
[`node-addon-api`]: https://github.com/nodejs/node-addon-api
64746579
[`node_api.h`]: https://github.com/nodejs/node/blob/HEAD/src/node_api.h
6580+
[`node_api_create_external_string_latin1`]: #node_api_create_external_string_latin1
6581+
[`node_api_create_external_string_utf16`]: #node_api_create_external_string_utf16
64756582
[`node_api_create_syntax_error`]: #node_api_create_syntax_error
64766583
[`node_api_throw_syntax_error`]: #node_api_throw_syntax_error
64776584
[`process.release`]: process.md#processrelease

‎src/js_native_api.h

+18
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,24 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf16(napi_env env,
9292
const char16_t* str,
9393
size_t length,
9494
napi_value* result);
95+
#ifdef NAPI_EXPERIMENTAL
96+
NAPI_EXTERN napi_status NAPI_CDECL
97+
node_api_create_external_string_latin1(napi_env env,
98+
char* str,
99+
size_t length,
100+
napi_finalize finalize_callback,
101+
void* finalize_hint,
102+
napi_value* result,
103+
bool* copied);
104+
NAPI_EXTERN napi_status NAPI_CDECL
105+
node_api_create_external_string_utf16(napi_env env,
106+
char16_t* str,
107+
size_t length,
108+
napi_finalize finalize_callback,
109+
void* finalize_hint,
110+
napi_value* result,
111+
bool* copied);
112+
#endif // NAPI_EXPERIMENTAL
95113
NAPI_EXTERN napi_status NAPI_CDECL napi_create_symbol(napi_env env,
96114
napi_value description,
97115
napi_value* result);

‎src/js_native_api_v8.cc

+195-42
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,133 @@ namespace v8impl {
6161

6262
namespace {
6363

64+
template <typename CCharType, typename StringMaker>
65+
napi_status NewString(napi_env env,
66+
const CCharType* str,
67+
size_t length,
68+
napi_value* result,
69+
StringMaker string_maker) {
70+
CHECK_ENV(env);
71+
if (length > 0) CHECK_ARG(env, str);
72+
CHECK_ARG(env, result);
73+
RETURN_STATUS_IF_FALSE(
74+
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
75+
76+
auto isolate = env->isolate;
77+
auto str_maybe = string_maker(isolate);
78+
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
79+
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
80+
return napi_clear_last_error(env);
81+
}
82+
83+
template <typename CharType, typename CreateAPI, typename StringMaker>
84+
napi_status NewExternalString(napi_env env,
85+
CharType* str,
86+
size_t length,
87+
napi_finalize finalize_callback,
88+
void* finalize_hint,
89+
napi_value* result,
90+
bool* copied,
91+
CreateAPI create_api,
92+
StringMaker string_maker) {
93+
napi_status status;
94+
#if defined(V8_ENABLE_SANDBOX)
95+
status = create_api(env, str, length, result);
96+
if (status == napi_ok) {
97+
if (copied != nullptr) {
98+
*copied = true;
99+
}
100+
if (finalize_callback) {
101+
env->CallFinalizer(
102+
finalize_callback, static_cast<CharType*>(str), finalize_hint);
103+
}
104+
}
105+
#else
106+
status = NewString(env, str, length, result, string_maker);
107+
if (status == napi_ok && copied != nullptr) {
108+
*copied = false;
109+
}
110+
#endif // V8_ENABLE_SANDBOX
111+
return status;
112+
}
113+
114+
class TrackedStringResource : public Finalizer, RefTracker {
115+
public:
116+
TrackedStringResource(napi_env env,
117+
napi_finalize finalize_callback,
118+
void* data,
119+
void* finalize_hint)
120+
: Finalizer(env, finalize_callback, data, finalize_hint) {
121+
Link(finalize_callback == nullptr ? &env->reflist
122+
: &env->finalizing_reflist);
123+
}
124+
125+
protected:
126+
// The only time Finalize() gets called before Dispose() is if the
127+
// environment is dying. Finalize() expects that the item will be unlinked,
128+
// so we do it here. V8 will still call Dispose() on us later, so we don't do
129+
// any deleting here. We just null out env_ to avoid passing a stale pointer
130+
// to the user's finalizer when V8 does finally call Dispose().
131+
void Finalize() override {
132+
Unlink();
133+
env_ = nullptr;
134+
}
135+
136+
~TrackedStringResource() {
137+
if (finalize_callback_ == nullptr) return;
138+
if (env_ == nullptr) {
139+
// The environment is dead. Call the finalizer directly.
140+
finalize_callback_(nullptr, finalize_data_, finalize_hint_);
141+
} else {
142+
// The environment is still alive. Let's remove ourselves from its list
143+
// of references and call the user's finalizer.
144+
Unlink();
145+
env_->CallFinalizer(finalize_callback_, finalize_data_, finalize_hint_);
146+
}
147+
}
148+
};
149+
150+
class ExternalOneByteStringResource
151+
: public v8::String::ExternalOneByteStringResource,
152+
TrackedStringResource {
153+
public:
154+
ExternalOneByteStringResource(napi_env env,
155+
char* string,
156+
const size_t length,
157+
napi_finalize finalize_callback,
158+
void* finalize_hint)
159+
: TrackedStringResource(env, finalize_callback, string, finalize_hint),
160+
string_(string),
161+
length_(length) {}
162+
163+
const char* data() const override { return string_; }
164+
size_t length() const override { return length_; }
165+
166+
private:
167+
const char* string_;
168+
const size_t length_;
169+
};
170+
171+
class ExternalStringResource : public v8::String::ExternalStringResource,
172+
TrackedStringResource {
173+
public:
174+
ExternalStringResource(napi_env env,
175+
char16_t* string,
176+
const size_t length,
177+
napi_finalize finalize_callback,
178+
void* finalize_hint)
179+
: TrackedStringResource(env, finalize_callback, string, finalize_hint),
180+
string_(reinterpret_cast<uint16_t*>(string)),
181+
length_(length) {}
182+
183+
const uint16_t* data() const override { return string_; }
184+
size_t length() const override { return length_; }
185+
186+
private:
187+
const uint16_t* string_;
188+
const size_t length_;
189+
};
190+
64191
inline napi_status V8NameFromPropertyDescriptor(
65192
napi_env env,
66193
const napi_property_descriptor* p,
@@ -1389,62 +1516,88 @@ napi_status NAPI_CDECL napi_create_string_latin1(napi_env env,
13891516
const char* str,
13901517
size_t length,
13911518
napi_value* result) {
1392-
CHECK_ENV(env);
1393-
if (length > 0) CHECK_ARG(env, str);
1394-
CHECK_ARG(env, result);
1395-
RETURN_STATUS_IF_FALSE(
1396-
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
1397-
1398-
auto isolate = env->isolate;
1399-
auto str_maybe =
1400-
v8::String::NewFromOneByte(isolate,
1401-
reinterpret_cast<const uint8_t*>(str),
1402-
v8::NewStringType::kNormal,
1403-
length);
1404-
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
1405-
1406-
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
1407-
return napi_clear_last_error(env);
1519+
return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
1520+
return v8::String::NewFromOneByte(isolate,
1521+
reinterpret_cast<const uint8_t*>(str),
1522+
v8::NewStringType::kNormal,
1523+
length);
1524+
});
14081525
}
14091526

14101527
napi_status NAPI_CDECL napi_create_string_utf8(napi_env env,
14111528
const char* str,
14121529
size_t length,
14131530
napi_value* result) {
1414-
CHECK_ENV(env);
1415-
if (length > 0) CHECK_ARG(env, str);
1416-
CHECK_ARG(env, result);
1417-
RETURN_STATUS_IF_FALSE(
1418-
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
1419-
1420-
auto isolate = env->isolate;
1421-
auto str_maybe = v8::String::NewFromUtf8(
1422-
isolate, str, v8::NewStringType::kNormal, static_cast<int>(length));
1423-
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
1424-
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
1425-
return napi_clear_last_error(env);
1531+
return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
1532+
return v8::String::NewFromUtf8(
1533+
isolate, str, v8::NewStringType::kNormal, static_cast<int>(length));
1534+
});
14261535
}
14271536

14281537
napi_status NAPI_CDECL napi_create_string_utf16(napi_env env,
14291538
const char16_t* str,
14301539
size_t length,
14311540
napi_value* result) {
1432-
CHECK_ENV(env);
1433-
if (length > 0) CHECK_ARG(env, str);
1434-
CHECK_ARG(env, result);
1435-
RETURN_STATUS_IF_FALSE(
1436-
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
1541+
return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
1542+
return v8::String::NewFromTwoByte(isolate,
1543+
reinterpret_cast<const uint16_t*>(str),
1544+
v8::NewStringType::kNormal,
1545+
length);
1546+
});
1547+
}
14371548

1438-
auto isolate = env->isolate;
1439-
auto str_maybe =
1440-
v8::String::NewFromTwoByte(isolate,
1441-
reinterpret_cast<const uint16_t*>(str),
1442-
v8::NewStringType::kNormal,
1443-
length);
1444-
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
1549+
napi_status NAPI_CDECL
1550+
node_api_create_external_string_latin1(napi_env env,
1551+
char* str,
1552+
size_t length,
1553+
napi_finalize finalize_callback,
1554+
void* finalize_hint,
1555+
napi_value* result,
1556+
bool* copied) {
1557+
return v8impl::NewExternalString(
1558+
env,
1559+
str,
1560+
length,
1561+
finalize_callback,
1562+
finalize_hint,
1563+
result,
1564+
copied,
1565+
napi_create_string_latin1,
1566+
[&](v8::Isolate* isolate) {
1567+
if (length == NAPI_AUTO_LENGTH) {
1568+
length = (std::string_view(str)).length();
1569+
}
1570+
auto resource = new v8impl::ExternalOneByteStringResource(
1571+
env, str, length, finalize_callback, finalize_hint);
1572+
return v8::String::NewExternalOneByte(isolate, resource);
1573+
});
1574+
}
14451575

1446-
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
1447-
return napi_clear_last_error(env);
1576+
napi_status NAPI_CDECL
1577+
node_api_create_external_string_utf16(napi_env env,
1578+
char16_t* str,
1579+
size_t length,
1580+
napi_finalize finalize_callback,
1581+
void* finalize_hint,
1582+
napi_value* result,
1583+
bool* copied) {
1584+
return v8impl::NewExternalString(
1585+
env,
1586+
str,
1587+
length,
1588+
finalize_callback,
1589+
finalize_hint,
1590+
result,
1591+
copied,
1592+
napi_create_string_utf16,
1593+
[&](v8::Isolate* isolate) {
1594+
if (length == NAPI_AUTO_LENGTH) {
1595+
length = (std::u16string_view(str)).length();
1596+
}
1597+
auto resource = new v8impl::ExternalStringResource(
1598+
env, str, length, finalize_callback, finalize_hint);
1599+
return v8::String::NewExternalTwoByte(isolate, resource);
1600+
});
14481601
}
14491602

14501603
napi_status NAPI_CDECL napi_create_double(napi_env env,

‎test/js-native-api/common.h

+11
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@
5656
#define NODE_API_CALL_RETURN_VOID(env, the_call) \
5757
NODE_API_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)
5858

59+
#define NODE_API_CHECK_STATUS(the_call) \
60+
do { \
61+
napi_status status = (the_call); \
62+
if (status != napi_ok) { \
63+
return status; \
64+
} \
65+
} while (0)
66+
67+
#define NODE_API_ASSERT_STATUS(env, assertion, message) \
68+
NODE_API_ASSERT_BASE(env, assertion, message, napi_generic_failure)
69+
5970
#define DECLARE_NODE_API_PROPERTY(name, func) \
6071
{ (name), NULL, (func), NULL, NULL, NULL, napi_default, NULL }
6172

‎test/js-native-api/test_string/test.js

+46
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,27 @@ const empty = '';
99
assert.strictEqual(test_string.TestLatin1(empty), empty);
1010
assert.strictEqual(test_string.TestUtf8(empty), empty);
1111
assert.strictEqual(test_string.TestUtf16(empty), empty);
12+
assert.strictEqual(test_string.TestLatin1AutoLength(empty), empty);
13+
assert.strictEqual(test_string.TestUtf8AutoLength(empty), empty);
14+
assert.strictEqual(test_string.TestUtf16AutoLength(empty), empty);
15+
assert.strictEqual(test_string.TestLatin1External(empty), empty);
16+
assert.strictEqual(test_string.TestUtf16External(empty), empty);
17+
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(empty), empty);
18+
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(empty), empty);
1219
assert.strictEqual(test_string.Utf16Length(empty), 0);
1320
assert.strictEqual(test_string.Utf8Length(empty), 0);
1421

1522
const str1 = 'hello world';
1623
assert.strictEqual(test_string.TestLatin1(str1), str1);
1724
assert.strictEqual(test_string.TestUtf8(str1), str1);
1825
assert.strictEqual(test_string.TestUtf16(str1), str1);
26+
assert.strictEqual(test_string.TestLatin1AutoLength(str1), str1);
27+
assert.strictEqual(test_string.TestUtf8AutoLength(str1), str1);
28+
assert.strictEqual(test_string.TestUtf16AutoLength(str1), str1);
29+
assert.strictEqual(test_string.TestLatin1External(str1), str1);
30+
assert.strictEqual(test_string.TestUtf16External(str1), str1);
31+
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str1), str1);
32+
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str1), str1);
1933
assert.strictEqual(test_string.TestLatin1Insufficient(str1), str1.slice(0, 3));
2034
assert.strictEqual(test_string.TestUtf8Insufficient(str1), str1.slice(0, 3));
2135
assert.strictEqual(test_string.TestUtf16Insufficient(str1), str1.slice(0, 3));
@@ -26,6 +40,13 @@ const str2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
2640
assert.strictEqual(test_string.TestLatin1(str2), str2);
2741
assert.strictEqual(test_string.TestUtf8(str2), str2);
2842
assert.strictEqual(test_string.TestUtf16(str2), str2);
43+
assert.strictEqual(test_string.TestLatin1AutoLength(str2), str2);
44+
assert.strictEqual(test_string.TestUtf8AutoLength(str2), str2);
45+
assert.strictEqual(test_string.TestUtf16AutoLength(str2), str2);
46+
assert.strictEqual(test_string.TestLatin1External(str2), str2);
47+
assert.strictEqual(test_string.TestUtf16External(str2), str2);
48+
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str2), str2);
49+
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str2), str2);
2950
assert.strictEqual(test_string.TestLatin1Insufficient(str2), str2.slice(0, 3));
3051
assert.strictEqual(test_string.TestUtf8Insufficient(str2), str2.slice(0, 3));
3152
assert.strictEqual(test_string.TestUtf16Insufficient(str2), str2.slice(0, 3));
@@ -36,6 +57,13 @@ const str3 = '?!@#$%^&*()_+-=[]{}/.,<>\'"\\';
3657
assert.strictEqual(test_string.TestLatin1(str3), str3);
3758
assert.strictEqual(test_string.TestUtf8(str3), str3);
3859
assert.strictEqual(test_string.TestUtf16(str3), str3);
60+
assert.strictEqual(test_string.TestLatin1AutoLength(str3), str3);
61+
assert.strictEqual(test_string.TestUtf8AutoLength(str3), str3);
62+
assert.strictEqual(test_string.TestUtf16AutoLength(str3), str3);
63+
assert.strictEqual(test_string.TestLatin1External(str3), str3);
64+
assert.strictEqual(test_string.TestUtf16External(str3), str3);
65+
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str3), str3);
66+
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str3), str3);
3967
assert.strictEqual(test_string.TestLatin1Insufficient(str3), str3.slice(0, 3));
4068
assert.strictEqual(test_string.TestUtf8Insufficient(str3), str3.slice(0, 3));
4169
assert.strictEqual(test_string.TestUtf16Insufficient(str3), str3.slice(0, 3));
@@ -46,6 +74,13 @@ const str4 = '¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿';
4674
assert.strictEqual(test_string.TestLatin1(str4), str4);
4775
assert.strictEqual(test_string.TestUtf8(str4), str4);
4876
assert.strictEqual(test_string.TestUtf16(str4), str4);
77+
assert.strictEqual(test_string.TestLatin1AutoLength(str4), str4);
78+
assert.strictEqual(test_string.TestUtf8AutoLength(str4), str4);
79+
assert.strictEqual(test_string.TestUtf16AutoLength(str4), str4);
80+
assert.strictEqual(test_string.TestLatin1External(str4), str4);
81+
assert.strictEqual(test_string.TestUtf16External(str4), str4);
82+
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str4), str4);
83+
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str4), str4);
4984
assert.strictEqual(test_string.TestLatin1Insufficient(str4), str4.slice(0, 3));
5085
assert.strictEqual(test_string.TestUtf8Insufficient(str4), str4.slice(0, 1));
5186
assert.strictEqual(test_string.TestUtf16Insufficient(str4), str4.slice(0, 3));
@@ -56,6 +91,13 @@ const str5 = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßà
5691
assert.strictEqual(test_string.TestLatin1(str5), str5);
5792
assert.strictEqual(test_string.TestUtf8(str5), str5);
5893
assert.strictEqual(test_string.TestUtf16(str5), str5);
94+
assert.strictEqual(test_string.TestLatin1AutoLength(str5), str5);
95+
assert.strictEqual(test_string.TestUtf8AutoLength(str5), str5);
96+
assert.strictEqual(test_string.TestUtf16AutoLength(str5), str5);
97+
assert.strictEqual(test_string.TestLatin1External(str5), str5);
98+
assert.strictEqual(test_string.TestUtf16External(str5), str5);
99+
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str5), str5);
100+
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str5), str5);
59101
assert.strictEqual(test_string.TestLatin1Insufficient(str5), str5.slice(0, 3));
60102
assert.strictEqual(test_string.TestUtf8Insufficient(str5), str5.slice(0, 1));
61103
assert.strictEqual(test_string.TestUtf16Insufficient(str5), str5.slice(0, 3));
@@ -65,6 +107,10 @@ assert.strictEqual(test_string.Utf8Length(str5), 126);
65107
const str6 = '\u{2003}\u{2101}\u{2001}\u{202}\u{2011}';
66108
assert.strictEqual(test_string.TestUtf8(str6), str6);
67109
assert.strictEqual(test_string.TestUtf16(str6), str6);
110+
assert.strictEqual(test_string.TestUtf8AutoLength(str6), str6);
111+
assert.strictEqual(test_string.TestUtf16AutoLength(str6), str6);
112+
assert.strictEqual(test_string.TestUtf16External(str6), str6);
113+
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str6), str6);
68114
assert.strictEqual(test_string.TestUtf8Insufficient(str6), str6.slice(0, 1));
69115
assert.strictEqual(test_string.TestUtf16Insufficient(str6), str6.slice(0, 3));
70116
assert.strictEqual(test_string.Utf16Length(str6), 5);

‎test/js-native-api/test_string/test_string.c

+246-117
Large diffs are not rendered by default.

‎test/node-api/test_reference_by_node_api_version/test_reference_by_node_api_version.c

+5-16
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,6 @@
22
#include "../../js-native-api/common.h"
33
#include "stdlib.h"
44

5-
#define NODE_API_ASSERT_STATUS(env, assertion, message) \
6-
NODE_API_ASSERT_BASE(env, assertion, message, napi_generic_failure)
7-
8-
#define NODE_API_CHECK_STATUS(env, the_call) \
9-
do { \
10-
napi_status status = (the_call); \
11-
if (status != napi_ok) { \
12-
return status; \
13-
} \
14-
} while (0)
15-
165
static uint32_t finalizeCount = 0;
176

187
static void FreeData(napi_env env, void* data, void* hint) {
@@ -29,7 +18,7 @@ static napi_status GetArgValue(napi_env env,
2918
napi_value* argValue) {
3019
size_t argc = 1;
3120
NODE_API_CHECK_STATUS(
32-
env, napi_get_cb_info(env, info, &argc, argValue, NULL, NULL));
21+
napi_get_cb_info(env, info, &argc, argValue, NULL, NULL));
3322

3423
NODE_API_ASSERT_STATUS(env, argc == 1, "Expects one arg.");
3524
return napi_ok;
@@ -39,10 +28,10 @@ static napi_status GetArgValueAsIndex(napi_env env,
3928
napi_callback_info info,
4029
uint32_t* index) {
4130
napi_value argValue;
42-
NODE_API_CHECK_STATUS(env, GetArgValue(env, info, &argValue));
31+
NODE_API_CHECK_STATUS(GetArgValue(env, info, &argValue));
4332

4433
napi_valuetype valueType;
45-
NODE_API_CHECK_STATUS(env, napi_typeof(env, argValue, &valueType));
34+
NODE_API_CHECK_STATUS(napi_typeof(env, argValue, &valueType));
4635
NODE_API_ASSERT_STATUS(
4736
env, valueType == napi_number, "Argument must be a number.");
4837

@@ -53,10 +42,10 @@ static napi_status GetRef(napi_env env,
5342
napi_callback_info info,
5443
napi_ref* ref) {
5544
uint32_t index;
56-
NODE_API_CHECK_STATUS(env, GetArgValueAsIndex(env, info, &index));
45+
NODE_API_CHECK_STATUS(GetArgValueAsIndex(env, info, &index));
5746

5847
napi_ref* refValues;
59-
NODE_API_CHECK_STATUS(env, napi_get_instance_data(env, (void**)&refValues));
48+
NODE_API_CHECK_STATUS(napi_get_instance_data(env, (void**)&refValues));
6049
NODE_API_ASSERT_STATUS(env, refValues != NULL, "Cannot get instance data.");
6150

6251
*ref = refValues[index];

0 commit comments

Comments
 (0)
Please sign in to comment.