node-api: add node_api_symbol_for()

Fixes: https://github.com/nodejs/node/issues/41294

Signed-off-by: Darshan Sen <raisinten@gmail.com>

PR-URL: https://github.com/nodejs/node/pull/41329
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Darshan Sen 2021-12-26 17:30:56 +05:30 committed by Michael Dawson
parent 3657c14598
commit 726711fe4e
7 changed files with 135 additions and 6 deletions

View File

@ -2492,6 +2492,38 @@ This API creates a JavaScript `symbol` value from a UTF8-encoded C string.
The JavaScript `symbol` type is described in [Section 19.4][]
of the ECMAScript Language Specification.
#### `node_api_symbol_for`
<!-- YAML
added: REPLACEME
napiVersion: REPLACEME
-->
> Stability: 1 - Experimental
```c
napi_status node_api_symbol_for(napi_env env,
const char* utf8description,
size_t length,
napi_value* result)
```
* `[in] env`: The environment that the API is invoked under.
* `[in] utf8description`: UTF-8 C string representing the text to be used as the
description for the symbol.
* `[in] length`: The length of the description string in bytes, or
`NAPI_AUTO_LENGTH` if it is null-terminated.
* `[out] result`: A `napi_value` representing a JavaScript `symbol`.
Returns `napi_ok` if the API succeeded.
This API searches in the global registry for an existing symbol with the given
description. If the symbol already exists it will be returned, otherwise a new
symbol will be created in the registry.
The JavaScript `symbol` type is described in [Section 19.4][] of the ECMAScript
Language Specification.
#### `napi_create_typedarray`
<!-- YAML

View File

@ -93,6 +93,12 @@ NAPI_EXTERN napi_status napi_create_string_utf16(napi_env env,
NAPI_EXTERN napi_status napi_create_symbol(napi_env env,
napi_value description,
napi_value* result);
#ifdef NAPI_EXPERIMENTAL
NAPI_EXTERN napi_status node_api_symbol_for(napi_env env,
const char* utf8description,
size_t length,
napi_value* result);
#endif // NAPI_EXPERIMENTAL
NAPI_EXTERN napi_status napi_create_function(napi_env env,
const char* utf8name,
size_t length,

View File

@ -1662,6 +1662,27 @@ napi_status napi_create_symbol(napi_env env,
return napi_clear_last_error(env);
}
napi_status node_api_symbol_for(napi_env env,
const char* utf8description,
size_t length,
napi_value* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
napi_value js_description_string;
STATUS_CALL(napi_create_string_utf8(env,
utf8description,
length,
&js_description_string));
v8::Local<v8::String> description_string =
v8impl::V8LocalValueFromJsValue(js_description_string).As<v8::String>();
*result = v8impl::JsValueFromV8LocalValue(
v8::Symbol::For(env->isolate, description_string));
return napi_clear_last_error(env);
}
static inline napi_status set_error_code(napi_env env,
v8::Local<v8::Value> error,
napi_value code,

View File

@ -35,12 +35,11 @@ assert.ok(!propertyNames.includes('readwriteAccessor2'));
assert.ok(!propertyNames.includes('readonlyAccessor1'));
assert.ok(!propertyNames.includes('readonlyAccessor2'));
// Validate property created with symbol
const start = 'Symbol('.length;
const end = start + 'NameKeySymbol'.length;
const symbolDescription =
String(Object.getOwnPropertySymbols(test_object)[0]).slice(start, end);
assert.strictEqual(symbolDescription, 'NameKeySymbol');
// Validate properties created with symbol
const propertySymbols = Object.getOwnPropertySymbols(test_object);
assert.strictEqual(propertySymbols[0].toString(), 'Symbol(NameKeySymbol)');
assert.strictEqual(propertySymbols[1].toString(), 'Symbol()');
assert.strictEqual(propertySymbols[2], Symbol.for('NameKeySymbolFor'));
// The napi_writable attribute should be ignored for accessors.
const readwriteAccessor1Descriptor =

View File

@ -1,3 +1,4 @@
#define NAPI_EXPERIMENTAL
#include <js_native_api.h>
#include "../common.h"
@ -77,6 +78,16 @@ napi_value Init(napi_env env, napi_value exports) {
NODE_API_CALL(env,
napi_create_symbol(env, symbol_description, &name_symbol));
napi_value name_symbol_descriptionless;
NODE_API_CALL(env,
napi_create_symbol(env, NULL, &name_symbol_descriptionless));
napi_value name_symbol_for;
NODE_API_CALL(env, node_api_symbol_for(env,
"NameKeySymbolFor",
NAPI_AUTO_LENGTH,
&name_symbol_for));
napi_property_descriptor properties[] = {
{ "echo", 0, Echo, 0, 0, 0, napi_enumerable, 0 },
{ "readwriteValue", 0, 0, 0, 0, number, napi_enumerable | napi_writable, 0 },
@ -84,6 +95,8 @@ napi_value Init(napi_env env, napi_value exports) {
{ "hiddenValue", 0, 0, 0, 0, number, napi_default, 0},
{ NULL, name_value, 0, 0, 0, number, napi_enumerable, 0},
{ NULL, name_symbol, 0, 0, 0, number, napi_enumerable, 0},
{ NULL, name_symbol_descriptionless, 0, 0, 0, number, napi_enumerable, 0},
{ NULL, name_symbol_for, 0, 0, 0, number, napi_enumerable, 0},
{ "readwriteAccessor1", 0, 0, GetValue, SetValue, 0, napi_default, 0},
{ "readwriteAccessor2", 0, 0, GetValue, SetValue, 0, napi_writable, 0},
{ "readonlyAccessor1", 0, 0, GetValue, NULL, 0, napi_default, 0},

View File

@ -21,6 +21,25 @@ async function runTests() {
})();
test_reference.deleteReference();
(() => {
const symbol = test_reference.createSymbolFor('testSymFor');
test_reference.createReference(symbol, 0);
assert.strictEqual(test_reference.referenceValue, symbol);
assert.strictEqual(test_reference.referenceValue, Symbol.for('testSymFor'));
})();
test_reference.deleteReference();
(() => {
const symbol = test_reference.createSymbolForEmptyString();
test_reference.createReference(symbol, 0);
assert.strictEqual(test_reference.referenceValue, symbol);
assert.strictEqual(test_reference.referenceValue, Symbol.for(''));
})();
test_reference.deleteReference();
assert.throws(() => test_reference.createSymbolForIncorrectLength(),
/Invalid argument/);
(() => {
const value = test_reference.createExternal();
assert.strictEqual(test_reference.finalizeCount, 0);

View File

@ -1,3 +1,4 @@
#define NAPI_EXPERIMENTAL
#include <stdlib.h>
#include <assert.h>
#include <js_native_api.h>
@ -49,6 +50,41 @@ static napi_value CreateSymbol(napi_env env, napi_callback_info info) {
return result_symbol;
}
static napi_value CreateSymbolFor(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1];
char description[256];
size_t description_length;
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL,NULL));
NODE_API_ASSERT(env, argc == 1, "Expect one argument only (symbol description)");
NODE_API_CALL(env, napi_get_value_string_utf8(env, args[0], description, sizeof(description), &description_length));
NODE_API_ASSERT(env, description_length <= 255, "Cannot accommodate descriptions longer than 255 bytes");
napi_value result_symbol;
NODE_API_CALL(env, node_api_symbol_for(env,
description,
description_length,
&result_symbol));
return result_symbol;
}
static napi_value CreateSymbolForEmptyString(napi_env env, napi_callback_info info) {
napi_value result_symbol;
NODE_API_CALL(env, node_api_symbol_for(env, NULL, 0, &result_symbol));
return result_symbol;
}
static napi_value CreateSymbolForIncorrectLength(napi_env env, napi_callback_info info) {
napi_value result_symbol;
NODE_API_CALL(env, node_api_symbol_for(env, NULL, 5, &result_symbol));
return result_symbol;
}
static napi_value
CreateExternalWithFinalize(napi_env env, napi_callback_info info) {
napi_value result;
@ -190,6 +226,9 @@ napi_value Init(napi_env env, napi_value exports) {
DECLARE_NODE_API_PROPERTY("checkExternal", CheckExternal),
DECLARE_NODE_API_PROPERTY("createReference", CreateReference),
DECLARE_NODE_API_PROPERTY("createSymbol", CreateSymbol),
DECLARE_NODE_API_PROPERTY("createSymbolFor", CreateSymbolFor),
DECLARE_NODE_API_PROPERTY("createSymbolForEmptyString", CreateSymbolForEmptyString),
DECLARE_NODE_API_PROPERTY("createSymbolForIncorrectLength", CreateSymbolForIncorrectLength),
DECLARE_NODE_API_PROPERTY("deleteReference", DeleteReference),
DECLARE_NODE_API_PROPERTY("incrementRefcount", IncrementRefcount),
DECLARE_NODE_API_PROPERTY("decrementRefcount", DecrementRefcount),