node/test/js-native-api/test_string/test_string.c
legendecas 178dff255f test: avoid copying test source files
Converting the helper functions to be inlined and making the helper file
header only.

PR-URL: https://github.com/nodejs/node/pull/49515
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Gabriel Schulhof <gabrielschulhof@gmail.com>
2023-09-10 06:11:36 +00:00

425 lines
14 KiB
C

#include <limits.h> // INT_MAX
#include <stdlib.h>
#include <string.h>
#define NAPI_EXPERIMENTAL
#include <js_native_api.h>
#include "../common.h"
#include "../entry_point.h"
#include "test_null.h"
enum length_type { actual_length, auto_length };
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_CHECK_STATUS(napi_typeof(env, *arg, &valuetype));
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, get_api(env, args[0], buffer, buffer_size, &copied));
napi_value output;
if (length_mode == auto_length) {
copied = NAPI_AUTO_LENGTH;
}
NODE_API_CALL(env, create_api(env, buffer, copied, &output));
return output;
}
// 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, 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, get_api(env, args[0], buffer, buffer_size, &copied));
napi_value output;
if (length_mode == auto_length) {
copied = NAPI_AUTO_LENGTH;
}
NODE_API_CALL(env, create_api(env, buffer, copied, &output));
return output;
}
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, validate_and_retrieve_single_string_arg(env, info, args));
char buffer[4];
size_t buffer_size = 4;
size_t copied;
NODE_API_CALL(
env,
napi_get_value_string_latin1(env, args[0], buffer, buffer_size, &copied));
napi_value output;
NODE_API_CALL(env, napi_create_string_latin1(env, buffer, copied, &output));
return output;
}
static napi_value TestUtf8Insufficient(napi_env env, napi_callback_info info) {
napi_value args[1];
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,
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 TestUtf16Insufficient(napi_env env, napi_callback_info info) {
napi_value args[1];
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,
napi_get_value_string_utf16(env, args[0], buffer, buffer_size, &copied));
napi_value output;
NODE_API_CALL(env, napi_create_string_utf16(env, buffer, copied, &output));
return output;
}
static napi_value Utf16Length(napi_env env, napi_callback_info info) {
napi_value args[1];
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));
napi_value output;
NODE_API_CALL(env, napi_create_uint32(env, (uint32_t)length, &output));
return output;
}
static napi_value Utf8Length(napi_env env, napi_callback_info info) {
napi_value args[1];
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_value output;
NODE_API_CALL(env, napi_create_uint32(env, (uint32_t)length, &output));
return output;
}
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));
} else {
// just throw the expected error as there is nothing to test
// in this case since we can't overflow
NODE_API_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
}
return output;
}
static napi_value TestLargeLatin1(napi_env env, napi_callback_info info) {
napi_value output;
if (SIZE_MAX > INT_MAX) {
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
// in this case since we can't overflow
NODE_API_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
}
return output;
}
static napi_value TestLargeUtf16(napi_env env, napi_callback_info info) {
napi_value output;
if (SIZE_MAX > INT_MAX) {
NODE_API_CALL(
env,
napi_create_string_utf16(
env, ((const char16_t*)""), ((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
NODE_API_CALL(env, napi_throw_error(env, NULL, "Invalid argument"));
}
return output;
}
static napi_value TestMemoryCorruption(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");
char buf[10] = {0};
NODE_API_CALL(env, napi_get_value_string_utf8(env, args[0], buf, 0, NULL));
char zero[10] = {0};
if (memcmp(buf, zero, sizeof(buf)) != 0) {
NODE_API_CALL(env, napi_throw_error(env, NULL, "Buffer overwritten"));
}
return NULL;
}
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("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));
return exports;
}
EXTERN_C_END