Skip to content

Commit

Permalink
Add support for features
Browse files Browse the repository at this point in the history
  • Loading branch information
vmoroz committed Sep 30, 2022
1 parent 64e73e3 commit 70a98de
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 9 deletions.
6 changes: 6 additions & 0 deletions src/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,12 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env,
napi_value object);
#endif // NAPI_VERSION >= 8

#ifdef NAPI_EXPERIMENTAL
NAPI_EXTERN napi_status NAPI_CDECL napi_has_feature(napi_env env,
napi_feature feature,
bool* result);
#endif // NAPI_EXPERIMENTAL

EXTERN_C_END

#endif // SRC_JS_NATIVE_API_H_
15 changes: 15 additions & 0 deletions src/js_native_api_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,21 @@ typedef enum {
// * the definition of `napi_status` in doc/api/n-api.md to reflect the newly
// added value(s).

#ifdef NAPI_EXPERIMENTAL
// Features allow changing internal behavior of existing Node-API functions.
// We set them by defining array of features in NAPI_FEATURES_DEFINITON macro.
// The NAPI_FEATURES_DEFINITON is used during module initialization in
// NAPI_MODULE_X macro.
// To override the set of default feature you could define
// NAPI_FEATURES_DEFINITON macro before the node_api.h is included.
typedef enum {
// We use it as the end terminator in the sequence of enabled features.
napi_feature_none,
// Use napi_ref for all value types and not only object and functions.
napi_feature_ref_all_value_types,
} napi_feature;
#endif

typedef napi_value(NAPI_CDECL* napi_callback)(napi_env env,
napi_callback_info info);
typedef void(NAPI_CDECL* napi_finalize)(napi_env env,
Expand Down
16 changes: 16 additions & 0 deletions src/js_native_api_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2499,6 +2499,13 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env,
CHECK_ARG(env, result);

v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(value);
if (!env->HasFeature(napi_feature_ref_all_value_types)) {
if (!(v8_value->IsObject() || v8_value->IsFunction() ||
v8_value->IsSymbol())) {
return napi_set_last_error(env, napi_invalid_arg);
}
}

v8impl::Reference* reference =
v8impl::Reference::New(env, v8_value, initial_refcount, false);

Expand Down Expand Up @@ -3256,3 +3263,12 @@ napi_status NAPI_CDECL napi_is_detached_arraybuffer(napi_env env,

return napi_clear_last_error(env);
}

napi_status NAPI_CDECL napi_has_feature(napi_env env,
napi_feature feature,
bool* result) {
CHECK_ENV(env);
CHECK_ARG(env, result);
*result = env->HasFeature(feature);
return napi_clear_last_error(env);
}
31 changes: 30 additions & 1 deletion src/js_native_api_v8.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef SRC_JS_NATIVE_API_V8_H_
#define SRC_JS_NATIVE_API_V8_H_

#include <array>
#include "js_native_api_types.h"
#include "js_native_api_v8_internals.h"

Expand Down Expand Up @@ -50,9 +51,10 @@ class RefTracker {
} // end of namespace v8impl

struct napi_env__ {
explicit napi_env__(v8::Local<v8::Context> context)
explicit napi_env__(v8::Local<v8::Context> context, napi_feature* features)
: isolate(context->GetIsolate()), context_persistent(isolate, context) {
CHECK_EQ(isolate, context->GetIsolate());
SetFeatures(features);
napi_clear_last_error(this);
}

Expand Down Expand Up @@ -96,6 +98,28 @@ struct napi_env__ {
CallIntoModule([&](napi_env env) { cb(env, data, hint); });
}

bool HasFeature(napi_feature feature) {
if (feature > 0 && feature < _featureCount) {
return _features[feature];
}
return false;
}

void SetFeatures(napi_feature* features) {
if (features == nullptr) {
return;
}
// For protection in case if the array is not zero terminated.
size_t featureCount = _featureCount;
for (napi_feature* feature = features;
*feature != napi_feature_none && featureCount > 0;
++feature, --featureCount) {
if (*feature > 0 && *feature < _featureCount) {
_features[*feature] = true;
}
}
}

virtual void DeleteMe() {
// First we must finalize those references that have `napi_finalizer`
// callbacks. The reason is that addons might store other references which
Expand Down Expand Up @@ -123,6 +147,11 @@ struct napi_env__ {
int refs = 1;
void* instance_data = nullptr;

// _featureCount is based on the biggest available feature value
static const size_t _featureCount =
static_cast<size_t>(napi_feature_ref_all_value_types) + 1;
std::array<bool, _featureCount> _features = {{false}};

protected:
// Should not be deleted directly. Delete with `napi_env__::DeleteMe()`
// instead.
Expand Down
29 changes: 24 additions & 5 deletions src/node_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
#include <memory>

node_napi_env__::node_napi_env__(v8::Local<v8::Context> context,
const std::string& module_filename)
: napi_env__(context), filename(module_filename) {
const std::string& module_filename,
napi_feature* features)
: napi_env__(context, features), filename(module_filename) {
CHECK_NOT_NULL(node_env());
}

Expand Down Expand Up @@ -126,10 +127,11 @@ class BufferFinalizer : private Finalizer {
};

inline napi_env NewEnv(v8::Local<v8::Context> context,
const std::string& module_filename) {
const std::string& module_filename,
napi_feature* features) {
node_napi_env result;

result = new node_napi_env__(context, module_filename);
result = new node_napi_env__(context, module_filename, features);
// TODO(addaleax): There was previously code that tried to delete the
// napi_env when its v8::Context was garbage collected;
// However, as long as N-API addons using this napi_env are in place,
Expand Down Expand Up @@ -586,6 +588,13 @@ class AsyncContext {

} // end of namespace v8impl

void napi_module_register_by_symbol_with_features(
v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
napi_addon_register_func init,
napi_feature* features);

// Intercepts the Node-V8 module registration callback. Converts parameters
// to NAPI equivalents and then calls the registration callback specified
// by the NAPI module.
Expand All @@ -604,6 +613,16 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
napi_addon_register_func init) {
napi_module_register_by_symbol_with_features(
exports, module, context, init, nullptr);
}

void napi_module_register_by_symbol_with_features(
v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
napi_addon_register_func init,
napi_feature* features) {
node::Environment* node_env = node::Environment::GetCurrent(context);
std::string module_filename = "";
if (init == nullptr) {
Expand Down Expand Up @@ -631,7 +650,7 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
}

// Create a new napi_env for this specific module.
napi_env env = v8impl::NewEnv(context, module_filename);
napi_env env = v8impl::NewEnv(context, module_filename, features);

napi_value _exports;
env->CallIntoModule([&](napi_env env) {
Expand Down
29 changes: 27 additions & 2 deletions src/node_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ typedef struct napi_module {
napi_addon_register_func nm_register_func;
const char* nm_modname;
void* nm_priv;
void* reserved[4];
#ifdef NAPI_EXPERIMENTAL
napi_feature* nm_features;
#else
void* reserved0;
#endif
void* reserved[3];
} napi_module;

#define NAPI_MODULE_VERSION 1
Expand Down Expand Up @@ -73,18 +78,38 @@ typedef struct napi_module {
static void fn(void)
#endif

#ifdef NAPI_EXPERIMENTAL
#ifndef NAPI_FEATURES_VAR
#define NAPI_FEATURES_VAR _module_features
#endif // NAPI_FEATURES_VAR
#ifndef NAPI_FEATURES_DEFINITON
#define NAPI_FEATURES_DEFINITON \
static napi_feature NAPI_FEATURES_VAR[] = { \
napi_feature_ref_all_value_types, \
napi_feature_none, \
};
#endif // NAPI_FEATURES_DEFINITON
#else
#define NAPI_FEATURES_VAR NULL
#define NAPI_FEATURES_DEFINITON
#endif // NAPI_EXPERIMENTAL

#define NAPI_MODULE_X(modname, regfunc, priv, flags) \
EXTERN_C_START \
NAPI_FEATURES_DEFINITON \
static napi_module _module = { \
NAPI_MODULE_VERSION, \
flags, \
__FILE__, \
regfunc, \
#modname, \
priv, \
NAPI_FEATURES_VAR, \
{0}, \
}; \
NAPI_C_CTOR(_register_##modname) { napi_module_register(&_module); } \
NAPI_C_CTOR(_register_##modname) { \
napi_module_register(&_module); \
} \
EXTERN_C_END

#define NAPI_MODULE_INITIALIZER_X(base, version) \
Expand Down
3 changes: 2 additions & 1 deletion src/node_api_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

struct node_napi_env__ : public napi_env__ {
node_napi_env__(v8::Local<v8::Context> context,
const std::string& module_filename);
const std::string& module_filename,
napi_feature* features);

bool can_call_into_js() const override;
v8::Maybe<bool> mark_arraybuffer_as_untransferable(
Expand Down
1 change: 1 addition & 0 deletions test/js-native-api/entry_point.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#define NAPI_EXPERIMENTAL
#include <node_api.h>

EXTERN_C_START
Expand Down

0 comments on commit 70a98de

Please sign in to comment.