Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Node-API] Symbol.toStringTag support #41358

Closed
motla opened this issue Dec 30, 2021 · 4 comments
Closed

[Node-API] Symbol.toStringTag support #41358

motla opened this issue Dec 30, 2021 · 4 comments
Labels
feature request Issues that request new features to be added to Node.js. node-api Issues and PRs related to the Node-API.

Comments

@motla
Copy link

motla commented Dec 30, 2021

What is the problem this feature will solve?

Using Node-API, I would like my C/C++ addon to generate a JavaScript object decorated with a tag name, like this:

const obj = {
  name: "john",
  [Symbol.toStringTag]: "MyTag"
};

So I can get:
image

I believe this is not possible to set the Symbol.toStringTag property to an object created with the current Node-API.

What is the feature you are proposing to solve the problem?

I guess we need access to the global Symbol.toStringTag and other well-known Symbols defined in JavaScript.
Below is the Node.js example to create a new Symbol and set it as a property for an object (along other properties):

napi_value symbol_description;
napi_value name_symbol;
NODE_API_CALL(env,
napi_create_string_utf8(
env, "NameKeySymbol", NAPI_AUTO_LENGTH, &symbol_description));
NODE_API_CALL(env,
napi_create_symbol(env, symbol_description, &name_symbol));
napi_property_descriptor properties[] = {
{ "echo", 0, Echo, 0, 0, 0, napi_enumerable, 0 },
{ "readwriteValue", 0, 0, 0, 0, number, napi_enumerable | napi_writable, 0 },
{ "readonlyValue", 0, 0, 0, 0, number, napi_enumerable, 0},
{ "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},
{ "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},
{ "readonlyAccessor2", 0, 0, GetValue, NULL, 0, napi_writable, 0},
{ "hasNamedProperty", 0, HasNamedProperty, 0, 0, 0, napi_default, 0 },
};
NODE_API_CALL(env, napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));

Instead of creating a new Symbol we would have to use the existing Symbol.toStringTag.
Maybe you could consider to have a method like napi_get_symbol_tostringtag(env, &name_symbol) to replace napi_create_symbol(.. used on line 78.

What alternatives have you considered?

Explored the Node-API but I have no clue for a workaround in the C/C++ code.

For a root object returned by the add-on, one may use the add-on main.js file to add the property dynamically in JavaScript before the export, but it's not pretty.

@motla motla added the feature request Issues that request new features to be added to Node.js. label Dec 30, 2021
@motla motla changed the title [Node-API] Symbol.toStringTag support in created objects [Node-API] Symbol.toStringTag support Dec 30, 2021
@Mesteery Mesteery added the node-api Issues and PRs related to the Node-API. label Dec 30, 2021
RaisinTen added a commit to RaisinTen/node that referenced this issue Jan 1, 2022
@RaisinTen
Copy link
Contributor

PR: #41370

@addaleax
Copy link
Member

addaleax commented Jan 1, 2022

You can use napi_get_global to access the global variable of the current context; and then use napi_get_property to access Symbol.toStringTag.

Using the C++ wrapper API, this is a one-liner: env.Global().Get("Symbol").As<Napi::Object>().Get("toStringTag").

I would strongly recommend doing that instead of adding a new functionality to get a small, specific, not very well-defined JS built-in values like #41370 would suggest. Using the existing mechanisms also has the advantage that it already works just fine, and you don’t need to wait for a new Node-API version in order to use this feature.

@motla
Copy link
Author

motla commented Jan 2, 2022

Well that works perfectly. Thanks @addaleax.
Indeed, no need for change on the Node-API with your solution.

Actually I printed global in Node but global.Symbol was not enumerated so I thought it was not available there.

Maybe we can write an example in the docs or somewhere? Because I never came across any solution to do this.

Here is another example that works in C (no C++ wrapper API):

static napi_value init_module(napi_env env, napi_value exports) {
  napi_value global, symbol, symbol_to_string_tag, tag_name;

  // Get JavaScript global Symbol.toStringTag
  if(napi_get_global(env, &global) != napi_ok) return NULL;
  if(napi_get_named_property(env, global, "Symbol", &symbol) != napi_ok) return NULL;
  if(napi_get_named_property(env, symbol, "toStringTag", &symbol_to_string_tag) != napi_ok) return NULL;

  // Declare JavaScript tag name
  if(napi_create_string_utf8(env, "My Module", NAPI_AUTO_LENGTH, &tag_name) != napi_ok) return NULL;

  // Declare properties for JavaScript `exports` object
  napi_property_descriptor props[] = {
    { NULL, symbol_to_string_tag, NULL, NULL, NULL, tag_name, napi_enumerable, NULL },
    // ...
  };

  // Write properties to JavaScript `exports` object
  size_t nb_props = sizeof(props) / sizeof(napi_property_descriptor);
  if(napi_define_properties(env, exports, nb_props, props) != napi_ok) return NULL;
  
  return exports;
}

NAPI_MODULE(my_module, init_module)

@motla motla closed this as completed Jan 2, 2022
@RaisinTen
Copy link
Contributor

Using the C++ wrapper API, this is a one-liner: env.Global().Get("Symbol").AsNapi::Object().Get("toStringTag").

This already takes care of getting the Symbol object from the global object:

Napi::Symbol::WellKnown(env, "toStringTag");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Issues that request new features to be added to Node.js. node-api Issues and PRs related to the Node-API.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants