diff --git a/doc/api/n-api.md b/doc/api/n-api.md index dd1bc884e506a8..e523c6199edae2 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3000,6 +3000,48 @@ The native string is copied. The JavaScript `string` type is described in [Section 6.1.4][] of the ECMAScript Language Specification. +#### `node_api_create_property_key_utf16` + + + +> Stability: 1 - Experimental + +```c +napi_status NAPI_CDECL node_api_create_property_key_utf16(napi_env env, + const char16_t* str, + size_t length, + napi_value* result); +``` + +* `[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. +* `[out] result`: A `napi_value` representing an optimized JavaScript `string` + to be used as a property key for objects. + +Returns `napi_ok` if the API succeeded. + +This API creates an optimized JavaScript `string` value from +a UTF16-LE-encoded C string to be used as a property key for objects. +The native string is copied. + +Many JavaScript engines including V8 use internalized strings as keys +to set and get property values. They typically use a hash table to create +and lookup such strings. While it adds some cost per key creation, it improves +the performance after that by enabling comparison of string pointers instead +of the whole strings. + +If a new JavaScript string is intended to be used as a property key, then +it is more efficient to use the `node_api_create_property_key_utf16` function. +Otherwise, for the string values use the `napi_create_string_utf16` or +`node_api_create_external_string_utf16` functions. + +The JavaScript `string` type is described in +[Section 6.1.4][] of the ECMAScript Language Specification. + ### Functions to convert from Node-API to C types #### `napi_get_array_length` diff --git a/src/js_native_api.h b/src/js_native_api.h index d665052b947552..5b8a26ed2331cf 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -110,6 +110,13 @@ node_api_create_external_string_utf16(napi_env env, napi_value* result, bool* copied); #endif // NAPI_EXPERIMENTAL + +#ifdef NAPI_EXPERIMENTAL +#define NODE_API_EXPERIMENTAL_HAS_PROPERTY_KEYS +NAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_utf16( + napi_env env, const char16_t* str, size_t length, napi_value* result); +#endif // NAPI_EXPERIMENTAL + NAPI_EXTERN napi_status NAPI_CDECL napi_create_symbol(napi_env env, napi_value description, napi_value* result); diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 044244eccfe9ea..78d6f24e8796ee 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -1651,6 +1651,18 @@ node_api_create_external_string_utf16(napi_env env, }); } +napi_status NAPI_CDECL node_api_create_property_key_utf16(napi_env env, + const char16_t* str, + size_t length, + napi_value* result) { + return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { + return v8::String::NewFromTwoByte(isolate, + reinterpret_cast(str), + v8::NewStringType::kInternalized, + static_cast(length)); + }); +} + napi_status NAPI_CDECL napi_create_double(napi_env env, double value, napi_value* result) { diff --git a/test/js-native-api/test_string/test.js b/test/js-native-api/test_string/test.js index 8926b9451175bd..ffcf0fab754e7c 100644 --- a/test/js-native-api/test_string/test.js +++ b/test/js-native-api/test_string/test.js @@ -16,6 +16,8 @@ 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.TestPropertyKeyUtf16(empty), empty); +assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(empty), empty); assert.strictEqual(test_string.Utf16Length(empty), 0); assert.strictEqual(test_string.Utf8Length(empty), 0); @@ -33,6 +35,8 @@ 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)); +assert.strictEqual(test_string.TestPropertyKeyUtf16(str1), str1); +assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(str1), str1); assert.strictEqual(test_string.Utf16Length(str1), 11); assert.strictEqual(test_string.Utf8Length(str1), 11); @@ -50,6 +54,8 @@ 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)); +assert.strictEqual(test_string.TestPropertyKeyUtf16(str2), str2); +assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(str2), str2); assert.strictEqual(test_string.Utf16Length(str2), 62); assert.strictEqual(test_string.Utf8Length(str2), 62); @@ -67,6 +73,8 @@ 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)); +assert.strictEqual(test_string.TestPropertyKeyUtf16(str3), str3); +assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(str3), str3); assert.strictEqual(test_string.Utf16Length(str3), 27); assert.strictEqual(test_string.Utf8Length(str3), 27); @@ -84,6 +92,8 @@ 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)); +assert.strictEqual(test_string.TestPropertyKeyUtf16(str4), str4); +assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(str4), str4); assert.strictEqual(test_string.Utf16Length(str4), 31); assert.strictEqual(test_string.Utf8Length(str4), 62); @@ -101,6 +111,8 @@ 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)); +assert.strictEqual(test_string.TestPropertyKeyUtf16(str5), str5); +assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(str5), str5); assert.strictEqual(test_string.Utf16Length(str5), 63); assert.strictEqual(test_string.Utf8Length(str5), 126); @@ -113,6 +125,8 @@ 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.TestPropertyKeyUtf16(str6), str6); +assert.strictEqual(test_string.TestPropertyKeyUtf16AutoLength(str6), str6); assert.strictEqual(test_string.Utf16Length(str6), 5); assert.strictEqual(test_string.Utf8Length(str6), 14); diff --git a/test/js-native-api/test_string/test_string.c b/test/js-native-api/test_string/test_string.c index b2046e3b873392..fb7102eabac2be 100644 --- a/test/js-native-api/test_string/test_string.c +++ b/test/js-native-api/test_string/test_string.c @@ -120,6 +120,22 @@ static napi_status create_external_latin1(napi_env env, return napi_ok; } +static napi_status create_property_key_utf16(napi_env env, + const char16_t* string, + size_t length, + napi_value* result) { + // Convert UTF-16 string to napi_value + napi_status status = + node_api_create_property_key_utf16(env, string, length, result); + + if (status != napi_ok) { + // Handle necessary operations in case of an error + 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) { @@ -294,6 +310,23 @@ static napi_value TestUtf16Insufficient(napi_env env, napi_callback_info info) { return output; } +static napi_value TestPropertyKeyUtf16(napi_env env, napi_callback_info info) { + return TestTwoByteImpl(env, + info, + napi_get_value_string_utf16, + node_api_create_property_key_utf16, + actual_length); +} + +static napi_value TestPropertyKeyUtf16AutoLength(napi_env env, + napi_callback_info info) { + return TestTwoByteImpl(env, + info, + napi_get_value_string_utf16, + node_api_create_property_key_utf16, + auto_length); +} + 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)); @@ -410,6 +443,9 @@ napi_value Init(napi_env env, napi_value exports) { DECLARE_NODE_API_PROPERTY("TestLargeLatin1", TestLargeLatin1), DECLARE_NODE_API_PROPERTY("TestLargeUtf16", TestLargeUtf16), DECLARE_NODE_API_PROPERTY("TestMemoryCorruption", TestMemoryCorruption), + DECLARE_NODE_API_PROPERTY("TestPropertyKeyUtf16", TestPropertyKeyUtf16), + DECLARE_NODE_API_PROPERTY("TestPropertyKeyUtf16AutoLength", + TestPropertyKeyUtf16AutoLength), }; init_test_null(env, exports);