From bdefbb063709b925f1e843731b02feabe8c2973f Mon Sep 17 00:00:00 2001 From: JckXia Date: Sat, 17 Apr 2021 22:06:39 -0400 Subject: [PATCH] Finished first set of symbol tests Add new static method to symbol and updated tests Updated Symbol.md 's docs, added function overload for Symbol class and new tests Remove extra space and comments Remove un-necssary string initialization Update PR based on comments received Add test checking if it's possible to pass nullptr to Symbol::For method Update Symbol::For implementations and added new tests Remove default parameter Update documentation for Symbol Fixing merge conflicts --- 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 72ac4c948..e2332674d 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 1f6563fea..69d9fa2fe 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -998,6 +998,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 8e9f5214a..c3e02cb88 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 f450c71d2..09bd110bc 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 562288c04..5cef552c8 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 000000000..8fdebce6d --- /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 000000000..b6f0f98bd --- /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()); +}