From 022af08c62674d4b67c3f63eeb931360532e6726 Mon Sep 17 00:00:00 2001 From: Roy Wright Date: Sat, 17 Apr 2021 22:06:39 -0400 Subject: [PATCH] test: add first set of symbol tests PR-URL: /~https://github.com/nodejs/node-addon-api/pull/972 Reviewed-By: Michael Dawson Reviewed-By: Nicola Del Gobbo --- doc/symbol.md | 17 +++++++++-- napi-inl.h | 21 +++++++++++++ napi.h | 12 ++++++++ test/binding.cc | 2 ++ test/binding.gyp | 1 + test/symbol.cc | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ test/symbol.js | 70 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 test/symbol.cc create mode 100644 test/symbol.js diff --git a/doc/symbol.md b/doc/symbol.md index 72ac4c9..e233267 100644 --- a/doc/symbol.md +++ b/doc/symbol.md @@ -34,7 +34,7 @@ If an error occurs, a `Napi::Error` will get thrown. If C++ exceptions are not being used, callers should check the result of `Napi::Env::IsExceptionPending` before attempting to use the returned value. -### Utf8Value +### WellKnown ```cpp static Napi::Symbol Napi::Symbol::WellKnown(napi_env env, const std::string& name); ``` @@ -45,4 +45,17 @@ static Napi::Symbol Napi::Symbol::WellKnown(napi_env env, const std::string& nam Returns a `Napi::Symbol` representing a well-known `Symbol` from the `Symbol` registry. -[`Napi::Name`]: ./name.md +### For +```cpp +static Napi::Symbol Napi::Symbol::For(napi_env env, const std::string& description); +static Napi::Symbol Napi::Symbol::For(napi_env env, const char* description); +static Napi::Symbol Napi::Symbol::For(napi_env env, String description); +static Napi::Symbol Napi::Symbol::For(napi_env env, napi_value description); +``` + +- `[in] env`: The `napi_env` environment in which to construct the `Napi::Symbol` object. +- `[in] description`: The C++ string representing the `Napi::Symbol` in the global registry to retrieve. + +Searches in the global registry for existing symbol with the given name. If the symbol already exist it will be returned, otherwise a new symbol will be created in the registry. It's equivalent to Symbol.for() called from JavaScript. + +[`Napi::Name`]: ./name.md \ No newline at end of file diff --git a/napi-inl.h b/napi-inl.h index 93cef59..663f229 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -1003,6 +1003,27 @@ inline Symbol Symbol::WellKnown(napi_env env, const std::string& name) { return Napi::Env(env).Global().Get("Symbol").As().Get(name).As(); } +inline Symbol Symbol::For(napi_env env, const std::string& description) { + napi_value descriptionValue = String::New(env, description); + return Symbol::For(env, descriptionValue); +} + +inline Symbol Symbol::For(napi_env env, const char* description) { + napi_value descriptionValue = String::New(env, description); + return Symbol::For(env, descriptionValue); +} + +inline Symbol Symbol::For(napi_env env, String description) { + return Symbol::For(env, static_cast(description)); +} + +inline Symbol Symbol::For(napi_env env, napi_value description) { + Object symbObject = Napi::Env(env).Global().Get("Symbol").As(); + auto forSymb = + symbObject.Get("for").As().Call(symbObject, {description}); + return forSymb.As(); +} + inline Symbol::Symbol() : Name() { } diff --git a/napi.h b/napi.h index 8e9f521..c3e02cb 100644 --- a/napi.h +++ b/napi.h @@ -538,6 +538,18 @@ namespace Napi { /// Get a public Symbol (e.g. Symbol.iterator). static Symbol WellKnown(napi_env, const std::string& name); + // Create a symbol in the global registry, UTF-8 Encoded cpp string + static Symbol For(napi_env env, const std::string& description); + + // Create a symbol in the global registry, C style string (null terminated) + static Symbol For(napi_env env, const char* description); + + // Create a symbol in the global registry, String value describing the symbol + static Symbol For(napi_env env, String description); + + // Create a symbol in the global registry, napi_value describing the symbol + static Symbol For(napi_env env, napi_value description); + Symbol(); ///< Creates a new _empty_ Symbol instance. Symbol(napi_env env, napi_value value); ///< Wraps a Node-API value primitive. diff --git a/test/binding.cc b/test/binding.cc index f450c71..09bd110 100644 --- a/test/binding.cc +++ b/test/binding.cc @@ -58,6 +58,7 @@ Object InitTypedThreadSafeFunctionSum(Env env); Object InitTypedThreadSafeFunctionUnref(Env env); Object InitTypedThreadSafeFunction(Env env); #endif +Object InitSymbol(Env env); Object InitTypedArray(Env env); Object InitGlobalObject(Env env); Object InitObjectWrap(Env env); @@ -117,6 +118,7 @@ Object Init(Env env, Object exports) { #endif // !NODE_ADDON_API_DISABLE_DEPRECATED exports.Set("promise", InitPromise(env)); exports.Set("run_script", InitRunScript(env)); + exports.Set("symbol", InitSymbol(env)); #if (NAPI_VERSION > 3) exports.Set("threadsafe_function_ctx", InitThreadSafeFunctionCtx(env)); exports.Set("threadsafe_function_existing_tsfn", InitThreadSafeFunctionExistingTsfn(env)); diff --git a/test/binding.gyp b/test/binding.gyp index 562288c..5cef552 100644 --- a/test/binding.gyp +++ b/test/binding.gyp @@ -46,6 +46,7 @@ 'object/subscript_operator.cc', 'promise.cc', 'run_script.cc', + "symbol.cc", 'threadsafe_function/threadsafe_function_ctx.cc', 'threadsafe_function/threadsafe_function_existing_tsfn.cc', 'threadsafe_function/threadsafe_function_ptr.cc', diff --git a/test/symbol.cc b/test/symbol.cc new file mode 100644 index 0000000..8fdebce --- /dev/null +++ b/test/symbol.cc @@ -0,0 +1,77 @@ +#include +using namespace Napi; + +Symbol CreateNewSymbolWithNoArgs(const Napi::CallbackInfo&) { + return Napi::Symbol(); +} + +Symbol CreateNewSymbolWithCppStrDesc(const Napi::CallbackInfo& info) { + String cppStrKey = info[0].As(); + return Napi::Symbol::New(info.Env(), cppStrKey.Utf8Value()); +} + +Symbol CreateNewSymbolWithCStrDesc(const Napi::CallbackInfo& info) { + String cStrKey = info[0].As(); + return Napi::Symbol::New(info.Env(), cStrKey.Utf8Value().c_str()); +} + +Symbol CreateNewSymbolWithNapiString(const Napi::CallbackInfo& info) { + String strKey = info[0].As(); + return Napi::Symbol::New(info.Env(), strKey); +} + +Symbol GetWellknownSymbol(const Napi::CallbackInfo& info) { + String registrySymbol = info[0].As(); + return Napi::Symbol::WellKnown(info.Env(), + registrySymbol.Utf8Value().c_str()); +} + +Symbol FetchSymbolFromGlobalRegistry(const Napi::CallbackInfo& info) { + String registrySymbol = info[0].As(); + return Napi::Symbol::For(info.Env(), registrySymbol); +} + +Symbol FetchSymbolFromGlobalRegistryWithCppKey(const Napi::CallbackInfo& info) { + String cppStringKey = info[0].As(); + return Napi::Symbol::For(info.Env(), cppStringKey.Utf8Value()); +} + +Symbol FetchSymbolFromGlobalRegistryWithCKey(const Napi::CallbackInfo& info) { + String cppStringKey = info[0].As(); + return Napi::Symbol::For(info.Env(), cppStringKey.Utf8Value().c_str()); +} + +Symbol TestUndefinedSymbolsCanBeCreated(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + return Napi::Symbol::For(env, env.Undefined()); +} + +Symbol TestNullSymbolsCanBeCreated(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + return Napi::Symbol::For(env, env.Null()); +} + +Object InitSymbol(Env env) { + Object exports = Object::New(env); + + exports["createNewSymbolWithNoArgs"] = + Function::New(env, CreateNewSymbolWithNoArgs); + exports["createNewSymbolWithCppStr"] = + Function::New(env, CreateNewSymbolWithCppStrDesc); + exports["createNewSymbolWithCStr"] = + Function::New(env, CreateNewSymbolWithCStrDesc); + exports["createNewSymbolWithNapi"] = + Function::New(env, CreateNewSymbolWithNapiString); + exports["getWellKnownSymbol"] = Function::New(env, GetWellknownSymbol); + exports["getSymbolFromGlobalRegistry"] = + Function::New(env, FetchSymbolFromGlobalRegistry); + exports["getSymbolFromGlobalRegistryWithCKey"] = + Function::New(env, FetchSymbolFromGlobalRegistryWithCKey); + exports["getSymbolFromGlobalRegistryWithCppKey"] = + Function::New(env, FetchSymbolFromGlobalRegistryWithCppKey); + exports["testUndefinedSymbolCanBeCreated"] = + Function::New(env, TestUndefinedSymbolsCanBeCreated); + exports["testNullSymbolCanBeCreated"] = + Function::New(env, TestNullSymbolsCanBeCreated); + return exports; +} diff --git a/test/symbol.js b/test/symbol.js new file mode 100644 index 0000000..171f81e --- /dev/null +++ b/test/symbol.js @@ -0,0 +1,70 @@ +'use strict'; + +const buildType = process.config.target_defaults.default_configuration; +const assert = require('assert'); + +test(require(`./build/${buildType}/binding.node`)); +test(require(`./build/${buildType}/binding_noexcept.node`)); + + +async function test(binding) +{ + + const wellKnownSymbolFunctions = ['asyncIterator','hasInstance','isConcatSpreadable', 'iterator','match','matchAll','replace','search','split','species','toPrimitive','toStringTag','unscopables']; + + function assertCanCreateSymbol(symbol) + { + assert(binding.symbol.createNewSymbolWithCppStr(symbol) !== null); + assert(binding.symbol.createNewSymbolWithCStr(symbol) !== null); + assert(binding.symbol.createNewSymbolWithNapi(symbol) !== null); + } + + function assertSymbolAreUnique(symbol) + { + const symbolOne = binding.symbol.createNewSymbolWithCppStr(symbol); + const symbolTwo = binding.symbol.createNewSymbolWithCppStr(symbol); + + assert(symbolOne !== symbolTwo); + } + + function assertSymbolIsWellknown(symbol) + { + const symbOne = binding.symbol.getWellKnownSymbol(symbol); + const symbTwo = binding.symbol.getWellKnownSymbol(symbol); + assert(symbOne && symbTwo); + assert(symbOne === symbTwo); + } + + function assertSymbolIsNotWellknown(symbol) + { + const symbolTest = binding.symbol.getWellKnownSymbol(symbol); + assert(symbolTest === undefined); + } + + function assertCanCreateOrFetchGlobalSymbols(symbol, fetchFunction) + { + const symbOne = fetchFunction(symbol); + const symbTwo = fetchFunction(symbol); + assert(symbOne && symbTwo); + assert(symbOne === symbTwo); + } + + assertCanCreateSymbol("testing"); + assertSymbolAreUnique("symbol"); + assertSymbolIsNotWellknown("testing"); + + for(const wellknownProperty of wellKnownSymbolFunctions) + { + assertSymbolIsWellknown(wellknownProperty); + } + + assertCanCreateOrFetchGlobalSymbols("data", binding.symbol.getSymbolFromGlobalRegistry); + assertCanCreateOrFetchGlobalSymbols("CppKey", binding.symbol.getSymbolFromGlobalRegistryWithCppKey); + assertCanCreateOrFetchGlobalSymbols("CKey", binding.symbol.getSymbolFromGlobalRegistryWithCKey); + + assert(binding.symbol.createNewSymbolWithNoArgs() === undefined); + + assert(binding.symbol.testNullSymbolCanBeCreated() === binding.symbol.testNullSymbolCanBeCreated()); + assert(binding.symbol.testUndefinedSymbolCanBeCreated() === binding.symbol.testUndefinedSymbolCanBeCreated()); + assert(binding.symbol.testUndefinedSymbolCanBeCreated() !== binding.symbol.testNullSymbolCanBeCreated()); +}