Skip to content

Commit 70a98de

Browse files
committed
Add support for features
1 parent 64e73e3 commit 70a98de

8 files changed

+121
-9
lines changed

src/js_native_api.h

+6
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,12 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env,
566566
napi_value object);
567567
#endif // NAPI_VERSION >= 8
568568

569+
#ifdef NAPI_EXPERIMENTAL
570+
NAPI_EXTERN napi_status NAPI_CDECL napi_has_feature(napi_env env,
571+
napi_feature feature,
572+
bool* result);
573+
#endif // NAPI_EXPERIMENTAL
574+
569575
EXTERN_C_END
570576

571577
#endif // SRC_JS_NATIVE_API_H_

src/js_native_api_types.h

+15
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ typedef enum {
108108
// * the definition of `napi_status` in doc/api/n-api.md to reflect the newly
109109
// added value(s).
110110

111+
#ifdef NAPI_EXPERIMENTAL
112+
// Features allow changing internal behavior of existing Node-API functions.
113+
// We set them by defining array of features in NAPI_FEATURES_DEFINITON macro.
114+
// The NAPI_FEATURES_DEFINITON is used during module initialization in
115+
// NAPI_MODULE_X macro.
116+
// To override the set of default feature you could define
117+
// NAPI_FEATURES_DEFINITON macro before the node_api.h is included.
118+
typedef enum {
119+
// We use it as the end terminator in the sequence of enabled features.
120+
napi_feature_none,
121+
// Use napi_ref for all value types and not only object and functions.
122+
napi_feature_ref_all_value_types,
123+
} napi_feature;
124+
#endif
125+
111126
typedef napi_value(NAPI_CDECL* napi_callback)(napi_env env,
112127
napi_callback_info info);
113128
typedef void(NAPI_CDECL* napi_finalize)(napi_env env,

src/js_native_api_v8.cc

+16
Original file line numberDiff line numberDiff line change
@@ -2499,6 +2499,13 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env,
24992499
CHECK_ARG(env, result);
25002500

25012501
v8::Local<v8::Value> v8_value = v8impl::V8LocalValueFromJsValue(value);
2502+
if (!env->HasFeature(napi_feature_ref_all_value_types)) {
2503+
if (!(v8_value->IsObject() || v8_value->IsFunction() ||
2504+
v8_value->IsSymbol())) {
2505+
return napi_set_last_error(env, napi_invalid_arg);
2506+
}
2507+
}
2508+
25022509
v8impl::Reference* reference =
25032510
v8impl::Reference::New(env, v8_value, initial_refcount, false);
25042511

@@ -3256,3 +3263,12 @@ napi_status NAPI_CDECL napi_is_detached_arraybuffer(napi_env env,
32563263

32573264
return napi_clear_last_error(env);
32583265
}
3266+
3267+
napi_status NAPI_CDECL napi_has_feature(napi_env env,
3268+
napi_feature feature,
3269+
bool* result) {
3270+
CHECK_ENV(env);
3271+
CHECK_ARG(env, result);
3272+
*result = env->HasFeature(feature);
3273+
return napi_clear_last_error(env);
3274+
}

src/js_native_api_v8.h

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef SRC_JS_NATIVE_API_V8_H_
22
#define SRC_JS_NATIVE_API_V8_H_
33

4+
#include <array>
45
#include "js_native_api_types.h"
56
#include "js_native_api_v8_internals.h"
67

@@ -50,9 +51,10 @@ class RefTracker {
5051
} // end of namespace v8impl
5152

5253
struct napi_env__ {
53-
explicit napi_env__(v8::Local<v8::Context> context)
54+
explicit napi_env__(v8::Local<v8::Context> context, napi_feature* features)
5455
: isolate(context->GetIsolate()), context_persistent(isolate, context) {
5556
CHECK_EQ(isolate, context->GetIsolate());
57+
SetFeatures(features);
5658
napi_clear_last_error(this);
5759
}
5860

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

101+
bool HasFeature(napi_feature feature) {
102+
if (feature > 0 && feature < _featureCount) {
103+
return _features[feature];
104+
}
105+
return false;
106+
}
107+
108+
void SetFeatures(napi_feature* features) {
109+
if (features == nullptr) {
110+
return;
111+
}
112+
// For protection in case if the array is not zero terminated.
113+
size_t featureCount = _featureCount;
114+
for (napi_feature* feature = features;
115+
*feature != napi_feature_none && featureCount > 0;
116+
++feature, --featureCount) {
117+
if (*feature > 0 && *feature < _featureCount) {
118+
_features[*feature] = true;
119+
}
120+
}
121+
}
122+
99123
virtual void DeleteMe() {
100124
// First we must finalize those references that have `napi_finalizer`
101125
// callbacks. The reason is that addons might store other references which
@@ -123,6 +147,11 @@ struct napi_env__ {
123147
int refs = 1;
124148
void* instance_data = nullptr;
125149

150+
// _featureCount is based on the biggest available feature value
151+
static const size_t _featureCount =
152+
static_cast<size_t>(napi_feature_ref_all_value_types) + 1;
153+
std::array<bool, _featureCount> _features = {{false}};
154+
126155
protected:
127156
// Should not be deleted directly. Delete with `napi_env__::DeleteMe()`
128157
// instead.

src/node_api.cc

+24-5
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
#include <memory>
2121

2222
node_napi_env__::node_napi_env__(v8::Local<v8::Context> context,
23-
const std::string& module_filename)
24-
: napi_env__(context), filename(module_filename) {
23+
const std::string& module_filename,
24+
napi_feature* features)
25+
: napi_env__(context, features), filename(module_filename) {
2526
CHECK_NOT_NULL(node_env());
2627
}
2728

@@ -126,10 +127,11 @@ class BufferFinalizer : private Finalizer {
126127
};
127128

128129
inline napi_env NewEnv(v8::Local<v8::Context> context,
129-
const std::string& module_filename) {
130+
const std::string& module_filename,
131+
napi_feature* features) {
130132
node_napi_env result;
131133

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

587589
} // end of namespace v8impl
588590

591+
void napi_module_register_by_symbol_with_features(
592+
v8::Local<v8::Object> exports,
593+
v8::Local<v8::Value> module,
594+
v8::Local<v8::Context> context,
595+
napi_addon_register_func init,
596+
napi_feature* features);
597+
589598
// Intercepts the Node-V8 module registration callback. Converts parameters
590599
// to NAPI equivalents and then calls the registration callback specified
591600
// by the NAPI module.
@@ -604,6 +613,16 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
604613
v8::Local<v8::Value> module,
605614
v8::Local<v8::Context> context,
606615
napi_addon_register_func init) {
616+
napi_module_register_by_symbol_with_features(
617+
exports, module, context, init, nullptr);
618+
}
619+
620+
void napi_module_register_by_symbol_with_features(
621+
v8::Local<v8::Object> exports,
622+
v8::Local<v8::Value> module,
623+
v8::Local<v8::Context> context,
624+
napi_addon_register_func init,
625+
napi_feature* features) {
607626
node::Environment* node_env = node::Environment::GetCurrent(context);
608627
std::string module_filename = "";
609628
if (init == nullptr) {
@@ -631,7 +650,7 @@ void napi_module_register_by_symbol(v8::Local<v8::Object> exports,
631650
}
632651

633652
// Create a new napi_env for this specific module.
634-
napi_env env = v8impl::NewEnv(context, module_filename);
653+
napi_env env = v8impl::NewEnv(context, module_filename, features);
635654

636655
napi_value _exports;
637656
env->CallIntoModule([&](napi_env env) {

src/node_api.h

+27-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ typedef struct napi_module {
3838
napi_addon_register_func nm_register_func;
3939
const char* nm_modname;
4040
void* nm_priv;
41-
void* reserved[4];
41+
#ifdef NAPI_EXPERIMENTAL
42+
napi_feature* nm_features;
43+
#else
44+
void* reserved0;
45+
#endif
46+
void* reserved[3];
4247
} napi_module;
4348

4449
#define NAPI_MODULE_VERSION 1
@@ -73,18 +78,38 @@ typedef struct napi_module {
7378
static void fn(void)
7479
#endif
7580

81+
#ifdef NAPI_EXPERIMENTAL
82+
#ifndef NAPI_FEATURES_VAR
83+
#define NAPI_FEATURES_VAR _module_features
84+
#endif // NAPI_FEATURES_VAR
85+
#ifndef NAPI_FEATURES_DEFINITON
86+
#define NAPI_FEATURES_DEFINITON \
87+
static napi_feature NAPI_FEATURES_VAR[] = { \
88+
napi_feature_ref_all_value_types, \
89+
napi_feature_none, \
90+
};
91+
#endif // NAPI_FEATURES_DEFINITON
92+
#else
93+
#define NAPI_FEATURES_VAR NULL
94+
#define NAPI_FEATURES_DEFINITON
95+
#endif // NAPI_EXPERIMENTAL
96+
7697
#define NAPI_MODULE_X(modname, regfunc, priv, flags) \
7798
EXTERN_C_START \
99+
NAPI_FEATURES_DEFINITON \
78100
static napi_module _module = { \
79101
NAPI_MODULE_VERSION, \
80102
flags, \
81103
__FILE__, \
82104
regfunc, \
83105
#modname, \
84106
priv, \
107+
NAPI_FEATURES_VAR, \
85108
{0}, \
86109
}; \
87-
NAPI_C_CTOR(_register_##modname) { napi_module_register(&_module); } \
110+
NAPI_C_CTOR(_register_##modname) { \
111+
napi_module_register(&_module); \
112+
} \
88113
EXTERN_C_END
89114

90115
#define NAPI_MODULE_INITIALIZER_X(base, version) \

src/node_api_internals.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
struct node_napi_env__ : public napi_env__ {
1212
node_napi_env__(v8::Local<v8::Context> context,
13-
const std::string& module_filename);
13+
const std::string& module_filename,
14+
napi_feature* features);
1415

1516
bool can_call_into_js() const override;
1617
v8::Maybe<bool> mark_arraybuffer_as_untransferable(

test/js-native-api/entry_point.c

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#define NAPI_EXPERIMENTAL
12
#include <node_api.h>
23

34
EXTERN_C_START

0 commit comments

Comments
 (0)