mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
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: https://github.com/nodejs/node/pull/48339 Fixes: https://github.com/nodejs/node/issues/48198 Reviewed-By: Daeyeon Jeong <daeyeon.dev@gmail.com> Signed-off-by: Gabriel Schulhof <gabrielschulhof@gmail.com>
This commit is contained in:
parent
ac0853c4ee
commit
60d9aed307
1
benchmark/napi/string/.gitignore
vendored
Normal file
1
benchmark/napi/string/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build/
|
56
benchmark/napi/string/binding.c
Normal file
56
benchmark/napi/string/binding.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include <assert.h>
|
||||
#define NAPI_EXPERIMENTAL
|
||||
#include <node_api.h>
|
||||
|
||||
#define NAPI_CALL(call) \
|
||||
do { \
|
||||
napi_status status = call; \
|
||||
assert(status == napi_ok && #call " failed"); \
|
||||
} while (0);
|
||||
|
||||
#define EXPORT_FUNC(env, exports, name, func) \
|
||||
do { \
|
||||
napi_value js_func; \
|
||||
NAPI_CALL(napi_create_function( \
|
||||
(env), (name), NAPI_AUTO_LENGTH, (func), NULL, &js_func)); \
|
||||
NAPI_CALL(napi_set_named_property((env), (exports), (name), js_func)); \
|
||||
} while (0);
|
||||
|
||||
const char* one_byte_string = "The Quick Brown Fox Jumped Over The Lazy Dog.";
|
||||
const char16_t* two_byte_string =
|
||||
u"The Quick Brown Fox Jumped Over The Lazy Dog.";
|
||||
|
||||
#define DECLARE_BINDING(CapName, lowercase_name, var_name) \
|
||||
static napi_value CreateString##CapName(napi_env env, \
|
||||
napi_callback_info info) { \
|
||||
size_t argc = 4; \
|
||||
napi_value argv[4]; \
|
||||
uint32_t n; \
|
||||
uint32_t index; \
|
||||
napi_handle_scope scope; \
|
||||
napi_value js_string; \
|
||||
\
|
||||
NAPI_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); \
|
||||
NAPI_CALL(napi_get_value_uint32(env, argv[0], &n)); \
|
||||
NAPI_CALL(napi_open_handle_scope(env, &scope)); \
|
||||
NAPI_CALL(napi_call_function(env, argv[1], argv[2], 0, NULL, NULL)); \
|
||||
for (index = 0; index < n; index++) { \
|
||||
NAPI_CALL(napi_create_string_##lowercase_name( \
|
||||
env, (var_name), NAPI_AUTO_LENGTH, &js_string)); \
|
||||
} \
|
||||
NAPI_CALL(napi_call_function(env, argv[1], argv[3], 1, &argv[0], NULL)); \
|
||||
NAPI_CALL(napi_close_handle_scope(env, scope)); \
|
||||
\
|
||||
return NULL; \
|
||||
}
|
||||
|
||||
DECLARE_BINDING(Latin1, latin1, one_byte_string)
|
||||
DECLARE_BINDING(Utf8, utf8, one_byte_string)
|
||||
DECLARE_BINDING(Utf16, utf16, two_byte_string)
|
||||
|
||||
NAPI_MODULE_INIT() {
|
||||
EXPORT_FUNC(env, exports, "createStringLatin1", CreateStringLatin1);
|
||||
EXPORT_FUNC(env, exports, "createStringUtf8", CreateStringUtf8);
|
||||
EXPORT_FUNC(env, exports, "createStringUtf16", CreateStringUtf16);
|
||||
return exports;
|
||||
}
|
8
benchmark/napi/string/binding.gyp
Normal file
8
benchmark/napi/string/binding.gyp
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'binding',
|
||||
'sources': [ 'binding.c' ]
|
||||
}
|
||||
]
|
||||
}
|
19
benchmark/napi/string/index.js
Normal file
19
benchmark/napi/string/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
const common = require('../../common.js');
|
||||
|
||||
let binding;
|
||||
try {
|
||||
binding = require(`./build/${common.buildType}/binding`);
|
||||
} catch {
|
||||
console.error(`${__filename}: Binding failed to load`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const bench = common.createBenchmark(main, {
|
||||
n: [1e5, 1e6, 1e7],
|
||||
stringType: ['Latin1', 'Utf8', 'Utf16'],
|
||||
});
|
||||
|
||||
function main({ n, stringType }) {
|
||||
binding[`createString${stringType}`](n, bench, bench.start, bench.end);
|
||||
}
|
109
doc/api/n-api.md
109
doc/api/n-api.md
@ -801,7 +801,7 @@ napiVersion: 1
|
||||
|
||||
Function pointer type for add-on provided functions that allow the user to be
|
||||
notified when externally-owned data is ready to be cleaned up because the
|
||||
object with which it was associated with, has been garbage-collected. The user
|
||||
object with which it was associated with has been garbage-collected. The user
|
||||
must provide a function satisfying the following signature which would get
|
||||
called upon the object's collection. Currently, `napi_finalize` can be used for
|
||||
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
|
||||
where it cannot execute JavaScript code, some Node-API calls may return
|
||||
`napi_pending_exception` even when there is no exception pending.
|
||||
|
||||
In the case of [`node_api_create_external_string_latin1`][] and
|
||||
[`node_api_create_external_string_utf16`][] the `env` parameter may be null,
|
||||
because external strings can be collected during the latter part of environment
|
||||
shutdown.
|
||||
|
||||
Change History:
|
||||
|
||||
* experimental (`NAPI_EXPERIMENTAL` is defined):
|
||||
@ -2886,6 +2891,56 @@ string. The native string is copied.
|
||||
The JavaScript `string` type is described in
|
||||
[Section 6.1.4][] of the ECMAScript Language Specification.
|
||||
|
||||
#### `node_api_create_external_string_latin1`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
```c
|
||||
napi_status
|
||||
node_api_create_external_string_latin1(napi_env env,
|
||||
char* str,
|
||||
size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint,
|
||||
napi_value* result,
|
||||
bool* copied);
|
||||
```
|
||||
|
||||
* `[in] env`: The environment that the API is invoked under.
|
||||
* `[in] str`: Character buffer representing an ISO-8859-1-encoded string.
|
||||
* `[in] length`: The length of the string in bytes, or `NAPI_AUTO_LENGTH` if it
|
||||
is null-terminated.
|
||||
* `[in] finalize_callback`: The function to call when the string is being
|
||||
collected. The function will be called with the following parameters:
|
||||
* `[in] env`: The environment in which the add-on is running. This value
|
||||
may be null if the string is being collected as part of the termination
|
||||
of the worker or the main Node.js instance.
|
||||
* `[in] data`: This is the value `str` as a `void*` pointer.
|
||||
* `[in] finalize_hint`: This is the value `finalize_hint` that was given
|
||||
to the API.
|
||||
[`napi_finalize`][] provides more details.
|
||||
This parameter is optional. Passing a null value means that the add-on
|
||||
doesn't need to be notified when the corresponding JavaScript string is
|
||||
collected.
|
||||
* `[in] finalize_hint`: Optional hint to pass to the finalize callback during
|
||||
collection.
|
||||
* `[out] result`: A `napi_value` representing a JavaScript `string`.
|
||||
* `[out] copied`: Whether the string was copied. If it was, the finalizer will
|
||||
already have been invoked to destroy `str`.
|
||||
|
||||
Returns `napi_ok` if the API succeeded.
|
||||
|
||||
This API creates a JavaScript `string` value from an ISO-8859-1-encoded C
|
||||
string. The native string may not be copied and must thus exist for the entire
|
||||
life cycle of the JavaScript value.
|
||||
|
||||
The JavaScript `string` type is described in
|
||||
[Section 6.1.4][] of the ECMAScript Language Specification.
|
||||
|
||||
#### `napi_create_string_utf16`
|
||||
|
||||
<!-- YAML
|
||||
@ -2914,6 +2969,56 @@ The native string is copied.
|
||||
The JavaScript `string` type is described in
|
||||
[Section 6.1.4][] of the ECMAScript Language Specification.
|
||||
|
||||
#### `node_api_create_external_string_utf16`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
```c
|
||||
napi_status
|
||||
node_api_create_external_string_utf16(napi_env env,
|
||||
char16_t* str,
|
||||
size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint,
|
||||
napi_value* result,
|
||||
bool* copied);
|
||||
```
|
||||
|
||||
* `[in] env`: The environment that the API is invoked under.
|
||||
* `[in] str`: Character buffer representing a UTF16-LE-encoded string.
|
||||
* `[in] length`: The length of the string in two-byte code units, or
|
||||
`NAPI_AUTO_LENGTH` if it is null-terminated.
|
||||
* `[in] finalize_callback`: The function to call when the string is being
|
||||
collected. The function will be called with the following parameters:
|
||||
* `[in] env`: The environment in which the add-on is running. This value
|
||||
may be null if the string is being collected as part of the termination
|
||||
of the worker or the main Node.js instance.
|
||||
* `[in] data`: This is the value `str` as a `void*` pointer.
|
||||
* `[in] finalize_hint`: This is the value `finalize_hint` that was given
|
||||
to the API.
|
||||
[`napi_finalize`][] provides more details.
|
||||
This parameter is optional. Passing a null value means that the add-on
|
||||
doesn't need to be notified when the corresponding JavaScript string is
|
||||
collected.
|
||||
* `[in] finalize_hint`: Optional hint to pass to the finalize callback during
|
||||
collection.
|
||||
* `[out] result`: A `napi_value` representing a JavaScript `string`.
|
||||
* `[out] copied`: Whether the string was copied. If it was, the finalizer will
|
||||
already have been invoked to destroy `str`.
|
||||
|
||||
Returns `napi_ok` if the API succeeded.
|
||||
|
||||
This API creates a JavaScript `string` value from a UTF16-LE-encoded C string.
|
||||
The native string may not be copied and must thus exist for the entire life
|
||||
cycle of the JavaScript value.
|
||||
|
||||
The JavaScript `string` type is described in
|
||||
[Section 6.1.4][] of the ECMAScript Language Specification.
|
||||
|
||||
#### `napi_create_string_utf8`
|
||||
|
||||
<!-- YAML
|
||||
@ -6476,6 +6581,8 @@ the add-on's file name during loading.
|
||||
[`napi_wrap`]: #napi_wrap
|
||||
[`node-addon-api`]: https://github.com/nodejs/node-addon-api
|
||||
[`node_api.h`]: https://github.com/nodejs/node/blob/HEAD/src/node_api.h
|
||||
[`node_api_create_external_string_latin1`]: #node_api_create_external_string_latin1
|
||||
[`node_api_create_external_string_utf16`]: #node_api_create_external_string_utf16
|
||||
[`node_api_create_syntax_error`]: #node_api_create_syntax_error
|
||||
[`node_api_throw_syntax_error`]: #node_api_throw_syntax_error
|
||||
[`process.release`]: process.md#processrelease
|
||||
|
@ -92,6 +92,24 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf16(napi_env env,
|
||||
const char16_t* str,
|
||||
size_t length,
|
||||
napi_value* result);
|
||||
#ifdef NAPI_EXPERIMENTAL
|
||||
NAPI_EXTERN napi_status NAPI_CDECL
|
||||
node_api_create_external_string_latin1(napi_env env,
|
||||
char* str,
|
||||
size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint,
|
||||
napi_value* result,
|
||||
bool* copied);
|
||||
NAPI_EXTERN napi_status NAPI_CDECL
|
||||
node_api_create_external_string_utf16(napi_env env,
|
||||
char16_t* str,
|
||||
size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint,
|
||||
napi_value* result,
|
||||
bool* copied);
|
||||
#endif // NAPI_EXPERIMENTAL
|
||||
NAPI_EXTERN napi_status NAPI_CDECL napi_create_symbol(napi_env env,
|
||||
napi_value description,
|
||||
napi_value* result);
|
||||
|
@ -61,6 +61,133 @@ namespace v8impl {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename CCharType, typename StringMaker>
|
||||
napi_status NewString(napi_env env,
|
||||
const CCharType* str,
|
||||
size_t length,
|
||||
napi_value* result,
|
||||
StringMaker string_maker) {
|
||||
CHECK_ENV(env);
|
||||
if (length > 0) CHECK_ARG(env, str);
|
||||
CHECK_ARG(env, result);
|
||||
RETURN_STATUS_IF_FALSE(
|
||||
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
|
||||
|
||||
auto isolate = env->isolate;
|
||||
auto str_maybe = string_maker(isolate);
|
||||
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
|
||||
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
|
||||
return napi_clear_last_error(env);
|
||||
}
|
||||
|
||||
template <typename CharType, typename CreateAPI, typename StringMaker>
|
||||
napi_status NewExternalString(napi_env env,
|
||||
CharType* str,
|
||||
size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint,
|
||||
napi_value* result,
|
||||
bool* copied,
|
||||
CreateAPI create_api,
|
||||
StringMaker string_maker) {
|
||||
napi_status status;
|
||||
#if defined(V8_ENABLE_SANDBOX)
|
||||
status = create_api(env, str, length, result);
|
||||
if (status == napi_ok) {
|
||||
if (copied != nullptr) {
|
||||
*copied = true;
|
||||
}
|
||||
if (finalize_callback) {
|
||||
env->CallFinalizer(
|
||||
finalize_callback, static_cast<CharType*>(str), finalize_hint);
|
||||
}
|
||||
}
|
||||
#else
|
||||
status = NewString(env, str, length, result, string_maker);
|
||||
if (status == napi_ok && copied != nullptr) {
|
||||
*copied = false;
|
||||
}
|
||||
#endif // V8_ENABLE_SANDBOX
|
||||
return status;
|
||||
}
|
||||
|
||||
class TrackedStringResource : public Finalizer, RefTracker {
|
||||
public:
|
||||
TrackedStringResource(napi_env env,
|
||||
napi_finalize finalize_callback,
|
||||
void* data,
|
||||
void* finalize_hint)
|
||||
: Finalizer(env, finalize_callback, data, finalize_hint) {
|
||||
Link(finalize_callback == nullptr ? &env->reflist
|
||||
: &env->finalizing_reflist);
|
||||
}
|
||||
|
||||
protected:
|
||||
// The only time Finalize() gets called before Dispose() is if the
|
||||
// environment is dying. Finalize() expects that the item will be unlinked,
|
||||
// so we do it here. V8 will still call Dispose() on us later, so we don't do
|
||||
// any deleting here. We just null out env_ to avoid passing a stale pointer
|
||||
// to the user's finalizer when V8 does finally call Dispose().
|
||||
void Finalize() override {
|
||||
Unlink();
|
||||
env_ = nullptr;
|
||||
}
|
||||
|
||||
~TrackedStringResource() {
|
||||
if (finalize_callback_ == nullptr) return;
|
||||
if (env_ == nullptr) {
|
||||
// The environment is dead. Call the finalizer directly.
|
||||
finalize_callback_(nullptr, finalize_data_, finalize_hint_);
|
||||
} else {
|
||||
// The environment is still alive. Let's remove ourselves from its list
|
||||
// of references and call the user's finalizer.
|
||||
Unlink();
|
||||
env_->CallFinalizer(finalize_callback_, finalize_data_, finalize_hint_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ExternalOneByteStringResource
|
||||
: public v8::String::ExternalOneByteStringResource,
|
||||
TrackedStringResource {
|
||||
public:
|
||||
ExternalOneByteStringResource(napi_env env,
|
||||
char* string,
|
||||
const size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint)
|
||||
: TrackedStringResource(env, finalize_callback, string, finalize_hint),
|
||||
string_(string),
|
||||
length_(length) {}
|
||||
|
||||
const char* data() const override { return string_; }
|
||||
size_t length() const override { return length_; }
|
||||
|
||||
private:
|
||||
const char* string_;
|
||||
const size_t length_;
|
||||
};
|
||||
|
||||
class ExternalStringResource : public v8::String::ExternalStringResource,
|
||||
TrackedStringResource {
|
||||
public:
|
||||
ExternalStringResource(napi_env env,
|
||||
char16_t* string,
|
||||
const size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint)
|
||||
: TrackedStringResource(env, finalize_callback, string, finalize_hint),
|
||||
string_(reinterpret_cast<uint16_t*>(string)),
|
||||
length_(length) {}
|
||||
|
||||
const uint16_t* data() const override { return string_; }
|
||||
size_t length() const override { return length_; }
|
||||
|
||||
private:
|
||||
const uint16_t* string_;
|
||||
const size_t length_;
|
||||
};
|
||||
|
||||
inline napi_status V8NameFromPropertyDescriptor(
|
||||
napi_env env,
|
||||
const napi_property_descriptor* p,
|
||||
@ -1389,62 +1516,88 @@ napi_status NAPI_CDECL napi_create_string_latin1(napi_env env,
|
||||
const char* str,
|
||||
size_t length,
|
||||
napi_value* result) {
|
||||
CHECK_ENV(env);
|
||||
if (length > 0) CHECK_ARG(env, str);
|
||||
CHECK_ARG(env, result);
|
||||
RETURN_STATUS_IF_FALSE(
|
||||
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
|
||||
|
||||
auto isolate = env->isolate;
|
||||
auto str_maybe =
|
||||
v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(str),
|
||||
v8::NewStringType::kNormal,
|
||||
length);
|
||||
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
|
||||
|
||||
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
|
||||
return napi_clear_last_error(env);
|
||||
return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
|
||||
return v8::String::NewFromOneByte(isolate,
|
||||
reinterpret_cast<const uint8_t*>(str),
|
||||
v8::NewStringType::kNormal,
|
||||
length);
|
||||
});
|
||||
}
|
||||
|
||||
napi_status NAPI_CDECL napi_create_string_utf8(napi_env env,
|
||||
const char* str,
|
||||
size_t length,
|
||||
napi_value* result) {
|
||||
CHECK_ENV(env);
|
||||
if (length > 0) CHECK_ARG(env, str);
|
||||
CHECK_ARG(env, result);
|
||||
RETURN_STATUS_IF_FALSE(
|
||||
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
|
||||
|
||||
auto isolate = env->isolate;
|
||||
auto str_maybe = v8::String::NewFromUtf8(
|
||||
isolate, str, v8::NewStringType::kNormal, static_cast<int>(length));
|
||||
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
|
||||
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
|
||||
return napi_clear_last_error(env);
|
||||
return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
|
||||
return v8::String::NewFromUtf8(
|
||||
isolate, str, v8::NewStringType::kNormal, static_cast<int>(length));
|
||||
});
|
||||
}
|
||||
|
||||
napi_status NAPI_CDECL napi_create_string_utf16(napi_env env,
|
||||
const char16_t* str,
|
||||
size_t length,
|
||||
napi_value* result) {
|
||||
CHECK_ENV(env);
|
||||
if (length > 0) CHECK_ARG(env, str);
|
||||
CHECK_ARG(env, result);
|
||||
RETURN_STATUS_IF_FALSE(
|
||||
env, (length == NAPI_AUTO_LENGTH) || length <= INT_MAX, napi_invalid_arg);
|
||||
return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) {
|
||||
return v8::String::NewFromTwoByte(isolate,
|
||||
reinterpret_cast<const uint16_t*>(str),
|
||||
v8::NewStringType::kNormal,
|
||||
length);
|
||||
});
|
||||
}
|
||||
|
||||
auto isolate = env->isolate;
|
||||
auto str_maybe =
|
||||
v8::String::NewFromTwoByte(isolate,
|
||||
reinterpret_cast<const uint16_t*>(str),
|
||||
v8::NewStringType::kNormal,
|
||||
length);
|
||||
CHECK_MAYBE_EMPTY(env, str_maybe, napi_generic_failure);
|
||||
napi_status NAPI_CDECL
|
||||
node_api_create_external_string_latin1(napi_env env,
|
||||
char* str,
|
||||
size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint,
|
||||
napi_value* result,
|
||||
bool* copied) {
|
||||
return v8impl::NewExternalString(
|
||||
env,
|
||||
str,
|
||||
length,
|
||||
finalize_callback,
|
||||
finalize_hint,
|
||||
result,
|
||||
copied,
|
||||
napi_create_string_latin1,
|
||||
[&](v8::Isolate* isolate) {
|
||||
if (length == NAPI_AUTO_LENGTH) {
|
||||
length = (std::string_view(str)).length();
|
||||
}
|
||||
auto resource = new v8impl::ExternalOneByteStringResource(
|
||||
env, str, length, finalize_callback, finalize_hint);
|
||||
return v8::String::NewExternalOneByte(isolate, resource);
|
||||
});
|
||||
}
|
||||
|
||||
*result = v8impl::JsValueFromV8LocalValue(str_maybe.ToLocalChecked());
|
||||
return napi_clear_last_error(env);
|
||||
napi_status NAPI_CDECL
|
||||
node_api_create_external_string_utf16(napi_env env,
|
||||
char16_t* str,
|
||||
size_t length,
|
||||
napi_finalize finalize_callback,
|
||||
void* finalize_hint,
|
||||
napi_value* result,
|
||||
bool* copied) {
|
||||
return v8impl::NewExternalString(
|
||||
env,
|
||||
str,
|
||||
length,
|
||||
finalize_callback,
|
||||
finalize_hint,
|
||||
result,
|
||||
copied,
|
||||
napi_create_string_utf16,
|
||||
[&](v8::Isolate* isolate) {
|
||||
if (length == NAPI_AUTO_LENGTH) {
|
||||
length = (std::u16string_view(str)).length();
|
||||
}
|
||||
auto resource = new v8impl::ExternalStringResource(
|
||||
env, str, length, finalize_callback, finalize_hint);
|
||||
return v8::String::NewExternalTwoByte(isolate, resource);
|
||||
});
|
||||
}
|
||||
|
||||
napi_status NAPI_CDECL napi_create_double(napi_env env,
|
||||
|
@ -56,6 +56,17 @@
|
||||
#define NODE_API_CALL_RETURN_VOID(env, the_call) \
|
||||
NODE_API_CALL_BASE(env, the_call, NODE_API_RETVAL_NOTHING)
|
||||
|
||||
#define NODE_API_CHECK_STATUS(the_call) \
|
||||
do { \
|
||||
napi_status status = (the_call); \
|
||||
if (status != napi_ok) { \
|
||||
return status; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define NODE_API_ASSERT_STATUS(env, assertion, message) \
|
||||
NODE_API_ASSERT_BASE(env, assertion, message, napi_generic_failure)
|
||||
|
||||
#define DECLARE_NODE_API_PROPERTY(name, func) \
|
||||
{ (name), NULL, (func), NULL, NULL, NULL, napi_default, NULL }
|
||||
|
||||
|
@ -9,6 +9,13 @@ const empty = '';
|
||||
assert.strictEqual(test_string.TestLatin1(empty), empty);
|
||||
assert.strictEqual(test_string.TestUtf8(empty), empty);
|
||||
assert.strictEqual(test_string.TestUtf16(empty), empty);
|
||||
assert.strictEqual(test_string.TestLatin1AutoLength(empty), empty);
|
||||
assert.strictEqual(test_string.TestUtf8AutoLength(empty), empty);
|
||||
assert.strictEqual(test_string.TestUtf16AutoLength(empty), empty);
|
||||
assert.strictEqual(test_string.TestLatin1External(empty), empty);
|
||||
assert.strictEqual(test_string.TestUtf16External(empty), empty);
|
||||
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(empty), empty);
|
||||
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(empty), empty);
|
||||
assert.strictEqual(test_string.Utf16Length(empty), 0);
|
||||
assert.strictEqual(test_string.Utf8Length(empty), 0);
|
||||
|
||||
@ -16,6 +23,13 @@ const str1 = 'hello world';
|
||||
assert.strictEqual(test_string.TestLatin1(str1), str1);
|
||||
assert.strictEqual(test_string.TestUtf8(str1), str1);
|
||||
assert.strictEqual(test_string.TestUtf16(str1), str1);
|
||||
assert.strictEqual(test_string.TestLatin1AutoLength(str1), str1);
|
||||
assert.strictEqual(test_string.TestUtf8AutoLength(str1), str1);
|
||||
assert.strictEqual(test_string.TestUtf16AutoLength(str1), str1);
|
||||
assert.strictEqual(test_string.TestLatin1External(str1), str1);
|
||||
assert.strictEqual(test_string.TestUtf16External(str1), str1);
|
||||
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str1), str1);
|
||||
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str1), str1);
|
||||
assert.strictEqual(test_string.TestLatin1Insufficient(str1), str1.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf8Insufficient(str1), str1.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf16Insufficient(str1), str1.slice(0, 3));
|
||||
@ -26,6 +40,13 @@ const str2 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
assert.strictEqual(test_string.TestLatin1(str2), str2);
|
||||
assert.strictEqual(test_string.TestUtf8(str2), str2);
|
||||
assert.strictEqual(test_string.TestUtf16(str2), str2);
|
||||
assert.strictEqual(test_string.TestLatin1AutoLength(str2), str2);
|
||||
assert.strictEqual(test_string.TestUtf8AutoLength(str2), str2);
|
||||
assert.strictEqual(test_string.TestUtf16AutoLength(str2), str2);
|
||||
assert.strictEqual(test_string.TestLatin1External(str2), str2);
|
||||
assert.strictEqual(test_string.TestUtf16External(str2), str2);
|
||||
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str2), str2);
|
||||
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str2), str2);
|
||||
assert.strictEqual(test_string.TestLatin1Insufficient(str2), str2.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf8Insufficient(str2), str2.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf16Insufficient(str2), str2.slice(0, 3));
|
||||
@ -36,6 +57,13 @@ const str3 = '?!@#$%^&*()_+-=[]{}/.,<>\'"\\';
|
||||
assert.strictEqual(test_string.TestLatin1(str3), str3);
|
||||
assert.strictEqual(test_string.TestUtf8(str3), str3);
|
||||
assert.strictEqual(test_string.TestUtf16(str3), str3);
|
||||
assert.strictEqual(test_string.TestLatin1AutoLength(str3), str3);
|
||||
assert.strictEqual(test_string.TestUtf8AutoLength(str3), str3);
|
||||
assert.strictEqual(test_string.TestUtf16AutoLength(str3), str3);
|
||||
assert.strictEqual(test_string.TestLatin1External(str3), str3);
|
||||
assert.strictEqual(test_string.TestUtf16External(str3), str3);
|
||||
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str3), str3);
|
||||
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str3), str3);
|
||||
assert.strictEqual(test_string.TestLatin1Insufficient(str3), str3.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf8Insufficient(str3), str3.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf16Insufficient(str3), str3.slice(0, 3));
|
||||
@ -46,6 +74,13 @@ const str4 = '¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿';
|
||||
assert.strictEqual(test_string.TestLatin1(str4), str4);
|
||||
assert.strictEqual(test_string.TestUtf8(str4), str4);
|
||||
assert.strictEqual(test_string.TestUtf16(str4), str4);
|
||||
assert.strictEqual(test_string.TestLatin1AutoLength(str4), str4);
|
||||
assert.strictEqual(test_string.TestUtf8AutoLength(str4), str4);
|
||||
assert.strictEqual(test_string.TestUtf16AutoLength(str4), str4);
|
||||
assert.strictEqual(test_string.TestLatin1External(str4), str4);
|
||||
assert.strictEqual(test_string.TestUtf16External(str4), str4);
|
||||
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str4), str4);
|
||||
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str4), str4);
|
||||
assert.strictEqual(test_string.TestLatin1Insufficient(str4), str4.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf8Insufficient(str4), str4.slice(0, 1));
|
||||
assert.strictEqual(test_string.TestUtf16Insufficient(str4), str4.slice(0, 3));
|
||||
@ -56,6 +91,13 @@ const str5 = 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßà
|
||||
assert.strictEqual(test_string.TestLatin1(str5), str5);
|
||||
assert.strictEqual(test_string.TestUtf8(str5), str5);
|
||||
assert.strictEqual(test_string.TestUtf16(str5), str5);
|
||||
assert.strictEqual(test_string.TestLatin1AutoLength(str5), str5);
|
||||
assert.strictEqual(test_string.TestUtf8AutoLength(str5), str5);
|
||||
assert.strictEqual(test_string.TestUtf16AutoLength(str5), str5);
|
||||
assert.strictEqual(test_string.TestLatin1External(str5), str5);
|
||||
assert.strictEqual(test_string.TestUtf16External(str5), str5);
|
||||
assert.strictEqual(test_string.TestLatin1ExternalAutoLength(str5), str5);
|
||||
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str5), str5);
|
||||
assert.strictEqual(test_string.TestLatin1Insufficient(str5), str5.slice(0, 3));
|
||||
assert.strictEqual(test_string.TestUtf8Insufficient(str5), str5.slice(0, 1));
|
||||
assert.strictEqual(test_string.TestUtf16Insufficient(str5), str5.slice(0, 3));
|
||||
@ -65,6 +107,10 @@ assert.strictEqual(test_string.Utf8Length(str5), 126);
|
||||
const str6 = '\u{2003}\u{2101}\u{2001}\u{202}\u{2011}';
|
||||
assert.strictEqual(test_string.TestUtf8(str6), str6);
|
||||
assert.strictEqual(test_string.TestUtf16(str6), str6);
|
||||
assert.strictEqual(test_string.TestUtf8AutoLength(str6), str6);
|
||||
assert.strictEqual(test_string.TestUtf16AutoLength(str6), str6);
|
||||
assert.strictEqual(test_string.TestUtf16External(str6), str6);
|
||||
assert.strictEqual(test_string.TestUtf16ExternalAutoLength(str6), str6);
|
||||
assert.strictEqual(test_string.TestUtf8Insufficient(str6), str6.slice(0, 1));
|
||||
assert.strictEqual(test_string.TestUtf16Insufficient(str6), str6.slice(0, 3));
|
||||
assert.strictEqual(test_string.Utf16Length(str6), 5);
|
||||
|
@ -1,106 +1,254 @@
|
||||
#include <limits.h> // INT_MAX
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define NAPI_EXPERIMENTAL
|
||||
#include <js_native_api.h>
|
||||
#include "../common.h"
|
||||
#include "test_null.h"
|
||||
|
||||
static napi_value TestLatin1(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
enum length_type { actual_length, auto_length };
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
static napi_status validate_and_retrieve_single_string_arg(
|
||||
napi_env env, napi_callback_info info, napi_value* arg) {
|
||||
size_t argc = 1;
|
||||
NODE_API_CHECK_STATUS(napi_get_cb_info(env, info, &argc, arg, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT_STATUS(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
NODE_API_CHECK_STATUS(napi_typeof(env, *arg, &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
NODE_API_ASSERT_STATUS(env,
|
||||
valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
// These help us factor out code that is common between the bindings.
|
||||
typedef napi_status (*OneByteCreateAPI)(napi_env,
|
||||
const char*,
|
||||
size_t,
|
||||
napi_value*);
|
||||
typedef napi_status (*OneByteGetAPI)(
|
||||
napi_env, napi_value, char*, size_t, size_t*);
|
||||
typedef napi_status (*TwoByteCreateAPI)(napi_env,
|
||||
const char16_t*,
|
||||
size_t,
|
||||
napi_value*);
|
||||
typedef napi_status (*TwoByteGetAPI)(
|
||||
napi_env, napi_value, char16_t*, size_t, size_t*);
|
||||
|
||||
// Test passing back the one-byte string we got from JS.
|
||||
static napi_value TestOneByteImpl(napi_env env,
|
||||
napi_callback_info info,
|
||||
OneByteGetAPI get_api,
|
||||
OneByteCreateAPI create_api,
|
||||
enum length_type length_mode) {
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
|
||||
|
||||
char buffer[128];
|
||||
size_t buffer_size = 128;
|
||||
size_t copied;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied));
|
||||
NODE_API_CALL(env, get_api(env, args[0], buffer, buffer_size, &copied));
|
||||
|
||||
napi_value output;
|
||||
NODE_API_CALL(env, napi_create_string_latin1(env, buffer, copied, &output));
|
||||
if (length_mode == auto_length) {
|
||||
copied = NAPI_AUTO_LENGTH;
|
||||
}
|
||||
NODE_API_CALL(env, create_api(env, buffer, copied, &output));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static napi_value TestUtf8(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
// Test passing back the two-byte string we got from JS.
|
||||
static napi_value TestTwoByteImpl(napi_env env,
|
||||
napi_callback_info info,
|
||||
TwoByteGetAPI get_api,
|
||||
TwoByteCreateAPI create_api,
|
||||
enum length_type length_mode) {
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
|
||||
char buffer[128];
|
||||
size_t buffer_size = 128;
|
||||
size_t copied;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
napi_get_value_string_utf8(env, args[0], buffer, buffer_size, &copied));
|
||||
|
||||
napi_value output;
|
||||
NODE_API_CALL(env, napi_create_string_utf8(env, buffer, copied, &output));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static napi_value TestUtf16(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
|
||||
|
||||
char16_t buffer[128];
|
||||
size_t buffer_size = 128;
|
||||
size_t copied;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied));
|
||||
NODE_API_CALL(env, get_api(env, args[0], buffer, buffer_size, &copied));
|
||||
|
||||
napi_value output;
|
||||
NODE_API_CALL(env, napi_create_string_utf16(env, buffer, copied, &output));
|
||||
if (length_mode == auto_length) {
|
||||
copied = NAPI_AUTO_LENGTH;
|
||||
}
|
||||
NODE_API_CALL(env, create_api(env, buffer, copied, &output));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static napi_value
|
||||
TestLatin1Insufficient(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
static void free_string(napi_env env, void* data, void* hint) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
static napi_status create_external_latin1(napi_env env,
|
||||
const char* string,
|
||||
size_t length,
|
||||
napi_value* result) {
|
||||
napi_status status;
|
||||
// Initialize to true, because that is the value we don't want.
|
||||
bool copied = true;
|
||||
char* string_copy;
|
||||
const size_t actual_length =
|
||||
(length == NAPI_AUTO_LENGTH ? strlen(string) : length);
|
||||
const size_t length_bytes = (actual_length + 1) * sizeof(*string_copy);
|
||||
string_copy = malloc(length_bytes);
|
||||
memcpy(string_copy, string, length_bytes);
|
||||
string_copy[actual_length] = 0;
|
||||
|
||||
status = node_api_create_external_string_latin1(
|
||||
env, string_copy, length, free_string, NULL, result, &copied);
|
||||
// We do not want the string to be copied.
|
||||
if (copied) {
|
||||
return napi_generic_failure;
|
||||
}
|
||||
if (status != napi_ok) {
|
||||
free(string_copy);
|
||||
return status;
|
||||
}
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
// strlen for char16_t. Needed in case we're copying a string of length
|
||||
// NAPI_AUTO_LENGTH.
|
||||
static size_t strlen16(const char16_t* string) {
|
||||
for (const char16_t* iter = string;; iter++) {
|
||||
if (*iter == 0) {
|
||||
return iter - string;
|
||||
}
|
||||
}
|
||||
// We should never get here.
|
||||
abort();
|
||||
}
|
||||
|
||||
static napi_status create_external_utf16(napi_env env,
|
||||
const char16_t* string,
|
||||
size_t length,
|
||||
napi_value* result) {
|
||||
napi_status status;
|
||||
// Initialize to true, because that is the value we don't want.
|
||||
bool copied = true;
|
||||
char16_t* string_copy;
|
||||
const size_t actual_length =
|
||||
(length == NAPI_AUTO_LENGTH ? strlen16(string) : length);
|
||||
const size_t length_bytes = (actual_length + 1) * sizeof(*string_copy);
|
||||
string_copy = malloc(length_bytes);
|
||||
memcpy(string_copy, string, length_bytes);
|
||||
string_copy[actual_length] = 0;
|
||||
|
||||
status = node_api_create_external_string_utf16(
|
||||
env, string_copy, length, free_string, NULL, result, &copied);
|
||||
if (status != napi_ok) {
|
||||
free(string_copy);
|
||||
return status;
|
||||
}
|
||||
|
||||
return napi_ok;
|
||||
}
|
||||
|
||||
static napi_value TestLatin1(napi_env env, napi_callback_info info) {
|
||||
return TestOneByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_latin1,
|
||||
napi_create_string_latin1,
|
||||
actual_length);
|
||||
}
|
||||
|
||||
static napi_value TestUtf8(napi_env env, napi_callback_info info) {
|
||||
return TestOneByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_utf8,
|
||||
napi_create_string_utf8,
|
||||
actual_length);
|
||||
}
|
||||
|
||||
static napi_value TestUtf16(napi_env env, napi_callback_info info) {
|
||||
return TestTwoByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_utf16,
|
||||
napi_create_string_utf16,
|
||||
actual_length);
|
||||
}
|
||||
|
||||
static napi_value TestLatin1AutoLength(napi_env env, napi_callback_info info) {
|
||||
return TestOneByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_latin1,
|
||||
napi_create_string_latin1,
|
||||
auto_length);
|
||||
}
|
||||
|
||||
static napi_value TestUtf8AutoLength(napi_env env, napi_callback_info info) {
|
||||
return TestOneByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_utf8,
|
||||
napi_create_string_utf8,
|
||||
auto_length);
|
||||
}
|
||||
|
||||
static napi_value TestUtf16AutoLength(napi_env env, napi_callback_info info) {
|
||||
return TestTwoByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_utf16,
|
||||
napi_create_string_utf16,
|
||||
auto_length);
|
||||
}
|
||||
|
||||
static napi_value TestLatin1External(napi_env env, napi_callback_info info) {
|
||||
return TestOneByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_latin1,
|
||||
create_external_latin1,
|
||||
actual_length);
|
||||
}
|
||||
|
||||
static napi_value TestUtf16External(napi_env env, napi_callback_info info) {
|
||||
return TestTwoByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_utf16,
|
||||
create_external_utf16,
|
||||
actual_length);
|
||||
}
|
||||
|
||||
static napi_value TestLatin1ExternalAutoLength(napi_env env,
|
||||
napi_callback_info info) {
|
||||
return TestOneByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_latin1,
|
||||
create_external_latin1,
|
||||
auto_length);
|
||||
}
|
||||
|
||||
static napi_value TestUtf16ExternalAutoLength(napi_env env,
|
||||
napi_callback_info info) {
|
||||
return TestTwoByteImpl(env,
|
||||
info,
|
||||
napi_get_value_string_utf16,
|
||||
create_external_utf16,
|
||||
auto_length);
|
||||
}
|
||||
|
||||
static napi_value TestLatin1Insufficient(napi_env env,
|
||||
napi_callback_info info) {
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
|
||||
|
||||
char buffer[4];
|
||||
size_t buffer_size = 4;
|
||||
size_t copied;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
NODE_API_CALL(
|
||||
env,
|
||||
napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied));
|
||||
|
||||
napi_value output;
|
||||
@ -110,23 +258,15 @@ TestLatin1Insufficient(napi_env env, napi_callback_info info) {
|
||||
}
|
||||
|
||||
static napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
|
||||
|
||||
char buffer[4];
|
||||
size_t buffer_size = 4;
|
||||
size_t copied;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
NODE_API_CALL(
|
||||
env,
|
||||
napi_get_value_string_utf8(env, args[0], buffer, buffer_size, &copied));
|
||||
|
||||
napi_value output;
|
||||
@ -136,23 +276,15 @@ static napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) {
|
||||
}
|
||||
|
||||
static napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
|
||||
|
||||
char16_t buffer[4];
|
||||
size_t buffer_size = 4;
|
||||
size_t copied;
|
||||
|
||||
NODE_API_CALL(env,
|
||||
NODE_API_CALL(
|
||||
env,
|
||||
napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied));
|
||||
|
||||
napi_value output;
|
||||
@ -162,20 +294,12 @@ static napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) {
|
||||
}
|
||||
|
||||
static napi_value Utf16Length(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
|
||||
|
||||
size_t length;
|
||||
NODE_API_CALL(env, napi_get_value_string_utf16(env, args[0], NULL, 0, &length));
|
||||
NODE_API_CALL(env,
|
||||
napi_get_value_string_utf16(env, args[0], NULL, 0, &length));
|
||||
|
||||
napi_value output;
|
||||
NODE_API_CALL(env, napi_create_uint32(env, (uint32_t)length, &output));
|
||||
@ -184,21 +308,12 @@ static napi_value Utf16Length(napi_env env, napi_callback_info info) {
|
||||
}
|
||||
|
||||
static napi_value Utf8Length(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1];
|
||||
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT(env, argc >= 1, "Wrong number of arguments");
|
||||
|
||||
napi_valuetype valuetype;
|
||||
NODE_API_CALL(env, napi_typeof(env, args[0], &valuetype));
|
||||
|
||||
NODE_API_ASSERT(env, valuetype == napi_string,
|
||||
"Wrong type of argment. Expects a string.");
|
||||
NODE_API_CALL(env, validate_and_retrieve_single_string_arg(env, info, args));
|
||||
|
||||
size_t length;
|
||||
NODE_API_CALL(env,
|
||||
napi_get_value_string_utf8(env, args[0], NULL, 0, &length));
|
||||
napi_get_value_string_utf8(env, args[0], NULL, 0, &length));
|
||||
|
||||
napi_value output;
|
||||
NODE_API_CALL(env, napi_create_uint32(env, (uint32_t)length, &output));
|
||||
@ -209,8 +324,8 @@ static napi_value Utf8Length(napi_env env, napi_callback_info info) {
|
||||
static napi_value TestLargeUtf8(napi_env env, napi_callback_info info) {
|
||||
napi_value output;
|
||||
if (SIZE_MAX > INT_MAX) {
|
||||
NODE_API_CALL(env,
|
||||
napi_create_string_utf8(env, "", ((size_t)INT_MAX) + 1, &output));
|
||||
NODE_API_CALL(
|
||||
env, napi_create_string_utf8(env, "", ((size_t)INT_MAX) + 1, &output));
|
||||
} else {
|
||||
// just throw the expected error as there is nothing to test
|
||||
// in this case since we can't overflow
|
||||
@ -223,7 +338,8 @@ static napi_value TestLargeUtf8(napi_env env, napi_callback_info info) {
|
||||
static napi_value TestLargeLatin1(napi_env env, napi_callback_info info) {
|
||||
napi_value output;
|
||||
if (SIZE_MAX > INT_MAX) {
|
||||
NODE_API_CALL(env,
|
||||
NODE_API_CALL(
|
||||
env,
|
||||
napi_create_string_latin1(env, "", ((size_t)INT_MAX) + 1, &output));
|
||||
} else {
|
||||
// just throw the expected error as there is nothing to test
|
||||
@ -237,7 +353,8 @@ static napi_value TestLargeLatin1(napi_env env, napi_callback_info info) {
|
||||
static napi_value TestLargeUtf16(napi_env env, napi_callback_info info) {
|
||||
napi_value output;
|
||||
if (SIZE_MAX > INT_MAX) {
|
||||
NODE_API_CALL(env,
|
||||
NODE_API_CALL(
|
||||
env,
|
||||
napi_create_string_utf16(
|
||||
env, ((const char16_t*)""), ((size_t)INT_MAX) + 1, &output));
|
||||
} else {
|
||||
@ -256,10 +373,10 @@ static napi_value TestMemoryCorruption(napi_env env, napi_callback_info info) {
|
||||
|
||||
NODE_API_ASSERT(env, argc == 1, "Wrong number of arguments");
|
||||
|
||||
char buf[10] = { 0 };
|
||||
char buf[10] = {0};
|
||||
NODE_API_CALL(env, napi_get_value_string_utf8(env, args[0], buf, 0, NULL));
|
||||
|
||||
char zero[10] = { 0 };
|
||||
char zero[10] = {0};
|
||||
if (memcmp(buf, zero, sizeof(buf)) != 0) {
|
||||
NODE_API_CALL(env, napi_throw_error(env, NULL, "Buffer overwritten"));
|
||||
}
|
||||
@ -270,24 +387,36 @@ static napi_value TestMemoryCorruption(napi_env env, napi_callback_info info) {
|
||||
EXTERN_C_START
|
||||
napi_value Init(napi_env env, napi_value exports) {
|
||||
napi_property_descriptor properties[] = {
|
||||
DECLARE_NODE_API_PROPERTY("TestLatin1", TestLatin1),
|
||||
DECLARE_NODE_API_PROPERTY("TestLatin1Insufficient", TestLatin1Insufficient),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf8", TestUtf8),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf8Insufficient", TestUtf8Insufficient),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf16", TestUtf16),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf16Insufficient", TestUtf16Insufficient),
|
||||
DECLARE_NODE_API_PROPERTY("Utf16Length", Utf16Length),
|
||||
DECLARE_NODE_API_PROPERTY("Utf8Length", Utf8Length),
|
||||
DECLARE_NODE_API_PROPERTY("TestLargeUtf8", TestLargeUtf8),
|
||||
DECLARE_NODE_API_PROPERTY("TestLargeLatin1", TestLargeLatin1),
|
||||
DECLARE_NODE_API_PROPERTY("TestLargeUtf16", TestLargeUtf16),
|
||||
DECLARE_NODE_API_PROPERTY("TestMemoryCorruption", TestMemoryCorruption),
|
||||
DECLARE_NODE_API_PROPERTY("TestLatin1", TestLatin1),
|
||||
DECLARE_NODE_API_PROPERTY("TestLatin1AutoLength", TestLatin1AutoLength),
|
||||
DECLARE_NODE_API_PROPERTY("TestLatin1External", TestLatin1External),
|
||||
DECLARE_NODE_API_PROPERTY("TestLatin1ExternalAutoLength",
|
||||
TestLatin1ExternalAutoLength),
|
||||
DECLARE_NODE_API_PROPERTY("TestLatin1Insufficient",
|
||||
TestLatin1Insufficient),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf8", TestUtf8),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf8AutoLength", TestUtf8AutoLength),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf8Insufficient", TestUtf8Insufficient),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf16", TestUtf16),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf16AutoLength", TestUtf16AutoLength),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf16External", TestUtf16External),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf16ExternalAutoLength",
|
||||
TestUtf16ExternalAutoLength),
|
||||
DECLARE_NODE_API_PROPERTY("TestUtf16Insufficient", TestUtf16Insufficient),
|
||||
DECLARE_NODE_API_PROPERTY("Utf16Length", Utf16Length),
|
||||
DECLARE_NODE_API_PROPERTY("Utf8Length", Utf8Length),
|
||||
DECLARE_NODE_API_PROPERTY("TestLargeUtf8", TestLargeUtf8),
|
||||
DECLARE_NODE_API_PROPERTY("TestLargeLatin1", TestLargeLatin1),
|
||||
DECLARE_NODE_API_PROPERTY("TestLargeUtf16", TestLargeUtf16),
|
||||
DECLARE_NODE_API_PROPERTY("TestMemoryCorruption", TestMemoryCorruption),
|
||||
};
|
||||
|
||||
init_test_null(env, exports);
|
||||
|
||||
NODE_API_CALL(env, napi_define_properties(
|
||||
env, exports, sizeof(properties) / sizeof(*properties), properties));
|
||||
NODE_API_CALL(
|
||||
env,
|
||||
napi_define_properties(
|
||||
env, exports, sizeof(properties) / sizeof(*properties), properties));
|
||||
|
||||
return exports;
|
||||
}
|
||||
|
@ -2,17 +2,6 @@
|
||||
#include "../../js-native-api/common.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
#define NODE_API_ASSERT_STATUS(env, assertion, message) \
|
||||
NODE_API_ASSERT_BASE(env, assertion, message, napi_generic_failure)
|
||||
|
||||
#define NODE_API_CHECK_STATUS(env, the_call) \
|
||||
do { \
|
||||
napi_status status = (the_call); \
|
||||
if (status != napi_ok) { \
|
||||
return status; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static uint32_t finalizeCount = 0;
|
||||
|
||||
static void FreeData(napi_env env, void* data, void* hint) {
|
||||
@ -29,7 +18,7 @@ static napi_status GetArgValue(napi_env env,
|
||||
napi_value* argValue) {
|
||||
size_t argc = 1;
|
||||
NODE_API_CHECK_STATUS(
|
||||
env, napi_get_cb_info(env, info, &argc, argValue, NULL, NULL));
|
||||
napi_get_cb_info(env, info, &argc, argValue, NULL, NULL));
|
||||
|
||||
NODE_API_ASSERT_STATUS(env, argc == 1, "Expects one arg.");
|
||||
return napi_ok;
|
||||
@ -39,10 +28,10 @@ static napi_status GetArgValueAsIndex(napi_env env,
|
||||
napi_callback_info info,
|
||||
uint32_t* index) {
|
||||
napi_value argValue;
|
||||
NODE_API_CHECK_STATUS(env, GetArgValue(env, info, &argValue));
|
||||
NODE_API_CHECK_STATUS(GetArgValue(env, info, &argValue));
|
||||
|
||||
napi_valuetype valueType;
|
||||
NODE_API_CHECK_STATUS(env, napi_typeof(env, argValue, &valueType));
|
||||
NODE_API_CHECK_STATUS(napi_typeof(env, argValue, &valueType));
|
||||
NODE_API_ASSERT_STATUS(
|
||||
env, valueType == napi_number, "Argument must be a number.");
|
||||
|
||||
@ -53,10 +42,10 @@ static napi_status GetRef(napi_env env,
|
||||
napi_callback_info info,
|
||||
napi_ref* ref) {
|
||||
uint32_t index;
|
||||
NODE_API_CHECK_STATUS(env, GetArgValueAsIndex(env, info, &index));
|
||||
NODE_API_CHECK_STATUS(GetArgValueAsIndex(env, info, &index));
|
||||
|
||||
napi_ref* refValues;
|
||||
NODE_API_CHECK_STATUS(env, napi_get_instance_data(env, (void**)&refValues));
|
||||
NODE_API_CHECK_STATUS(napi_get_instance_data(env, (void**)&refValues));
|
||||
NODE_API_ASSERT_STATUS(env, refValues != NULL, "Cannot get instance data.");
|
||||
|
||||
*ref = refValues[index];
|
||||
|
Loading…
Reference in New Issue
Block a user