17
17
#include < vector>
18
18
#include " uv.h"
19
19
#include " node_api.h"
20
+ #include " node_internals.h"
21
+
22
+ #define NAPI_VERSION 1
20
23
21
24
static
22
25
napi_status napi_set_last_error (napi_env env, napi_status error_code,
@@ -31,14 +34,33 @@ struct napi_env__ {
31
34
~napi_env__ () {
32
35
last_exception.Reset ();
33
36
has_instance.Reset ();
37
+ wrap_template.Reset ();
38
+ function_data_template.Reset ();
39
+ accessor_data_template.Reset ();
34
40
}
35
41
v8::Isolate* isolate;
36
42
v8::Persistent<v8::Value> last_exception;
37
43
v8::Persistent<v8::Value> has_instance;
44
+ v8::Persistent<v8::ObjectTemplate> wrap_template;
45
+ v8::Persistent<v8::ObjectTemplate> function_data_template;
46
+ v8::Persistent<v8::ObjectTemplate> accessor_data_template;
38
47
bool has_instance_available;
39
48
napi_extended_error_info last_error;
40
49
};
41
50
51
+ #define ENV_OBJECT_TEMPLATE (env, prefix, destination, field_count ) \
52
+ do { \
53
+ if ((env)->prefix ## _template.IsEmpty ()) { \
54
+ (destination) = v8::ObjectTemplate::New (isolate); \
55
+ (destination)->SetInternalFieldCount ((field_count)); \
56
+ (env)->prefix ## _template.Reset (isolate, (destination)); \
57
+ } else { \
58
+ (destination) = v8::Local<v8::ObjectTemplate>::New ( \
59
+ isolate, env->prefix ## _template); \
60
+ } \
61
+ } while (0 )
62
+
63
+
42
64
#define RETURN_STATUS_IF_FALSE (env, condition, status ) \
43
65
do { \
44
66
if (!(condition)) { \
@@ -154,14 +176,20 @@ class HandleScopeWrapper {
154
176
// across different versions.
155
177
class EscapableHandleScopeWrapper {
156
178
public:
157
- explicit EscapableHandleScopeWrapper (v8::Isolate* isolate) : scope(isolate) {}
179
+ explicit EscapableHandleScopeWrapper (v8::Isolate* isolate)
180
+ : scope(isolate), escape_called_(false ) {}
181
+ bool escape_called () const {
182
+ return escape_called_;
183
+ }
158
184
template <typename T>
159
185
v8::Local<T> Escape (v8::Local<T> handle) {
186
+ escape_called_ = true ;
160
187
return scope.Escape (handle);
161
188
}
162
189
163
190
private:
164
191
v8::EscapableHandleScope scope;
192
+ bool escape_called_;
165
193
};
166
194
167
195
napi_handle_scope JsHandleScopeFromV8HandleScope (HandleScopeWrapper* s) {
@@ -594,8 +622,8 @@ v8::Local<v8::Object> CreateFunctionCallbackData(napi_env env,
594
622
v8::Isolate* isolate = env->isolate ;
595
623
v8::Local<v8::Context> context = isolate->GetCurrentContext ();
596
624
597
- v8::Local<v8::ObjectTemplate> otpl = v8::ObjectTemplate::New (isolate) ;
598
- otpl-> SetInternalFieldCount ( v8impl::kFunctionFieldCount );
625
+ v8::Local<v8::ObjectTemplate> otpl;
626
+ ENV_OBJECT_TEMPLATE (env, function_data, otpl, v8impl::kFunctionFieldCount );
599
627
v8::Local<v8::Object> cbdata = otpl->NewInstance (context).ToLocalChecked ();
600
628
601
629
cbdata->SetInternalField (
@@ -620,8 +648,8 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env,
620
648
v8::Isolate* isolate = env->isolate ;
621
649
v8::Local<v8::Context> context = isolate->GetCurrentContext ();
622
650
623
- v8::Local<v8::ObjectTemplate> otpl = v8::ObjectTemplate::New (isolate) ;
624
- otpl-> SetInternalFieldCount ( v8impl::kAccessorFieldCount );
651
+ v8::Local<v8::ObjectTemplate> otpl;
652
+ ENV_OBJECT_TEMPLATE (env, accessor_data, otpl, v8impl::kAccessorFieldCount );
625
653
v8::Local<v8::Object> cbdata = otpl->NewInstance (context).ToLocalChecked ();
626
654
627
655
cbdata->SetInternalField (
@@ -646,6 +674,38 @@ v8::Local<v8::Object> CreateAccessorCallbackData(napi_env env,
646
674
return cbdata;
647
675
}
648
676
677
+ // Pointer used to identify items wrapped by N-API. Used by FindWrapper and
678
+ // napi_wrap().
679
+ const char napi_wrap_name[] = " N-API Wrapper" ;
680
+
681
+ // Search the object's prototype chain for the wrapper object. Usually the
682
+ // wrapper would be the first in the chain, but it is OK for other objects to
683
+ // be inserted in the prototype chain.
684
+ bool FindWrapper (v8::Local<v8::Object> obj,
685
+ v8::Local<v8::Object>* result = nullptr ) {
686
+ v8::Local<v8::Object> wrapper = obj;
687
+
688
+ do {
689
+ v8::Local<v8::Value> proto = wrapper->GetPrototype ();
690
+ if (proto.IsEmpty () || !proto->IsObject ()) {
691
+ return false ;
692
+ }
693
+ wrapper = proto.As <v8::Object>();
694
+ if (wrapper->InternalFieldCount () == 2 ) {
695
+ v8::Local<v8::Value> external = wrapper->GetInternalField (1 );
696
+ if (external->IsExternal () &&
697
+ external.As <v8::External>()->Value () == v8impl::napi_wrap_name) {
698
+ break ;
699
+ }
700
+ }
701
+ } while (true );
702
+
703
+ if (result != nullptr ) {
704
+ *result = wrapper;
705
+ }
706
+ return true ;
707
+ }
708
+
649
709
} // end of namespace v8impl
650
710
651
711
// Intercepts the Node-V8 module registration callback. Converts parameters
@@ -716,7 +776,8 @@ const char* error_messages[] = {nullptr,
716
776
" An array was expected" ,
717
777
" Unknown failure" ,
718
778
" An exception is pending" ,
719
- " The async work item was cancelled" };
779
+ " The async work item was cancelled" ,
780
+ " napi_escape_handle already called on scope" };
720
781
721
782
static napi_status napi_clear_last_error (napi_env env) {
722
783
CHECK_ENV (env);
@@ -744,10 +805,14 @@ napi_status napi_get_last_error_info(napi_env env,
744
805
CHECK_ENV (env);
745
806
CHECK_ARG (env, result);
746
807
808
+ // you must update this assert to reference the last message
809
+ // in the napi_status enum each time a new error message is added.
810
+ // We don't have a napi_status_last as this would result in an ABI
811
+ // change each time a message was added.
747
812
static_assert (
748
- ( sizeof ( error_messages) / sizeof (*error_messages)) == napi_status_last ,
813
+ node::arraysize ( error_messages) == napi_escape_called_twice + 1 ,
749
814
" Count of error messages must match count of error values" );
750
- assert (env->last_error .error_code < napi_status_last );
815
+ assert (env->last_error .error_code <= napi_escape_called_twice );
751
816
752
817
// Wait until someone requests the last error information to fetch the error
753
818
// message string
@@ -817,9 +882,6 @@ napi_status napi_define_class(napi_env env,
817
882
v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New (
818
883
isolate, v8impl::FunctionCallbackWrapper::Invoke, cbdata);
819
884
820
- // we need an internal field to stash the wrapped object
821
- tpl->InstanceTemplate ()->SetInternalFieldCount (1 );
822
-
823
885
v8::Local<v8::String> name_string;
824
886
CHECK_NEW_FROM_UTF8 (env, name_string, utf8name);
825
887
tpl->SetClassName (name_string);
@@ -991,6 +1053,49 @@ napi_status napi_get_property(napi_env env,
991
1053
return GET_RETURN_STATUS (env);
992
1054
}
993
1055
1056
+ napi_status napi_delete_property (napi_env env,
1057
+ napi_value object,
1058
+ napi_value key,
1059
+ bool * result) {
1060
+ NAPI_PREAMBLE (env);
1061
+ CHECK_ARG (env, key);
1062
+
1063
+ v8::Isolate* isolate = env->isolate ;
1064
+ v8::Local<v8::Context> context = isolate->GetCurrentContext ();
1065
+ v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue (key);
1066
+ v8::Local<v8::Object> obj;
1067
+
1068
+ CHECK_TO_OBJECT (env, context, obj, object);
1069
+ v8::Maybe<bool > delete_maybe = obj->Delete (context, k);
1070
+ CHECK_MAYBE_NOTHING (env, delete_maybe, napi_generic_failure);
1071
+
1072
+ if (result != NULL )
1073
+ *result = delete_maybe.FromMaybe (false );
1074
+
1075
+ return GET_RETURN_STATUS (env);
1076
+ }
1077
+
1078
+ NAPI_EXTERN napi_status napi_has_own_property (napi_env env,
1079
+ napi_value object,
1080
+ napi_value key,
1081
+ bool * result) {
1082
+ NAPI_PREAMBLE (env);
1083
+ CHECK_ARG (env, key);
1084
+
1085
+ v8::Isolate* isolate = env->isolate ;
1086
+ v8::Local<v8::Context> context = isolate->GetCurrentContext ();
1087
+ v8::Local<v8::Object> obj;
1088
+
1089
+ CHECK_TO_OBJECT (env, context, obj, object);
1090
+ v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue (key);
1091
+ RETURN_STATUS_IF_FALSE (env, k->IsName (), napi_name_expected);
1092
+ v8::Maybe<bool > has_maybe = obj->HasOwnProperty (context, k.As <v8::Name>());
1093
+ CHECK_MAYBE_NOTHING (env, has_maybe, napi_generic_failure);
1094
+ *result = has_maybe.FromMaybe (false );
1095
+
1096
+ return GET_RETURN_STATUS (env);
1097
+ }
1098
+
994
1099
napi_status napi_set_named_property (napi_env env,
995
1100
napi_value object,
996
1101
const char * utf8name,
@@ -1128,6 +1233,26 @@ napi_status napi_get_element(napi_env env,
1128
1233
return GET_RETURN_STATUS (env);
1129
1234
}
1130
1235
1236
+ napi_status napi_delete_element (napi_env env,
1237
+ napi_value object,
1238
+ uint32_t index,
1239
+ bool * result) {
1240
+ NAPI_PREAMBLE (env);
1241
+
1242
+ v8::Isolate* isolate = env->isolate ;
1243
+ v8::Local<v8::Context> context = isolate->GetCurrentContext ();
1244
+ v8::Local<v8::Object> obj;
1245
+
1246
+ CHECK_TO_OBJECT (env, context, obj, object);
1247
+ v8::Maybe<bool > delete_maybe = obj->Delete (context, index);
1248
+ CHECK_MAYBE_NOTHING (env, delete_maybe, napi_generic_failure);
1249
+
1250
+ if (result != NULL )
1251
+ *result = delete_maybe.FromMaybe (false );
1252
+
1253
+ return GET_RETURN_STATUS (env);
1254
+ }
1255
+
1131
1256
napi_status napi_define_properties (napi_env env,
1132
1257
napi_value object,
1133
1258
size_t property_count,
@@ -1948,14 +2073,34 @@ napi_status napi_wrap(napi_env env,
1948
2073
CHECK_ARG (env, js_object);
1949
2074
1950
2075
v8::Isolate* isolate = env->isolate ;
1951
- v8::Local<v8::Object> obj =
1952
- v8impl::V8LocalValueFromJsValue (js_object).As <v8::Object>();
2076
+ v8::Local<v8::Context> context = isolate->GetCurrentContext ();
2077
+
2078
+ v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue (js_object);
2079
+ RETURN_STATUS_IF_FALSE (env, value->IsObject (), napi_invalid_arg);
2080
+ v8::Local<v8::Object> obj = value.As <v8::Object>();
1953
2081
1954
- // Only objects that were created from a NAPI constructor's prototype
1955
- // via napi_define_class() can be (un)wrapped.
1956
- RETURN_STATUS_IF_FALSE (env, obj->InternalFieldCount () > 0 , napi_invalid_arg);
2082
+ // If we've already wrapped this object, we error out.
2083
+ RETURN_STATUS_IF_FALSE (env, !v8impl::FindWrapper (obj), napi_invalid_arg);
1957
2084
1958
- obj->SetInternalField (0 , v8::External::New (isolate, native_object));
2085
+ // Create a wrapper object with an internal field to hold the wrapped pointer
2086
+ // and a second internal field to identify the owner as N-API.
2087
+ v8::Local<v8::ObjectTemplate> wrapper_template;
2088
+ ENV_OBJECT_TEMPLATE (env, wrap, wrapper_template, 2 );
2089
+
2090
+ auto maybe_object = wrapper_template->NewInstance (context);
2091
+ CHECK_MAYBE_EMPTY (env, maybe_object, napi_generic_failure);
2092
+
2093
+ v8::Local<v8::Object> wrapper = maybe_object.ToLocalChecked ();
2094
+ wrapper->SetInternalField (1 , v8::External::New (isolate,
2095
+ reinterpret_cast <void *>(const_cast <char *>(v8impl::napi_wrap_name))));
2096
+
2097
+ // Store the pointer as an external in the wrapper.
2098
+ wrapper->SetInternalField (0 , v8::External::New (isolate, native_object));
2099
+
2100
+ // Insert the wrapper into the object's prototype chain.
2101
+ v8::Local<v8::Value> proto = obj->GetPrototype ();
2102
+ CHECK (wrapper->SetPrototype (context, proto).FromJust ());
2103
+ CHECK (obj->SetPrototype (context, wrapper).FromJust ());
1959
2104
1960
2105
if (result != nullptr ) {
1961
2106
// The returned reference should be deleted via napi_delete_reference()
@@ -1986,11 +2131,11 @@ napi_status napi_unwrap(napi_env env, napi_value js_object, void** result) {
1986
2131
RETURN_STATUS_IF_FALSE (env, value->IsObject (), napi_invalid_arg);
1987
2132
v8::Local<v8::Object> obj = value.As <v8::Object>();
1988
2133
1989
- // Only objects that were created from a NAPI constructor's prototype
1990
- // via napi_define_class() can be (un)wrapped.
1991
- RETURN_STATUS_IF_FALSE ( env, obj-> InternalFieldCount () > 0 , napi_invalid_arg);
2134
+ v8::Local<v8::Object> wrapper;
2135
+ RETURN_STATUS_IF_FALSE (
2136
+ env, v8impl::FindWrapper (obj, &wrapper) , napi_invalid_arg);
1992
2137
1993
- v8::Local<v8::Value> unwrappedValue = obj ->GetInternalField (0 );
2138
+ v8::Local<v8::Value> unwrappedValue = wrapper ->GetInternalField (0 );
1994
2139
RETURN_STATUS_IF_FALSE (env, unwrappedValue->IsExternal (), napi_invalid_arg);
1995
2140
1996
2141
*result = unwrappedValue.As <v8::External>()->Value ();
@@ -2195,9 +2340,12 @@ napi_status napi_escape_handle(napi_env env,
2195
2340
2196
2341
v8impl::EscapableHandleScopeWrapper* s =
2197
2342
v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope (scope);
2198
- *result = v8impl::JsValueFromV8LocalValue (
2199
- s->Escape (v8impl::V8LocalValueFromJsValue (escapee)));
2200
- return napi_clear_last_error (env);
2343
+ if (!s->escape_called ()) {
2344
+ *result = v8impl::JsValueFromV8LocalValue (
2345
+ s->Escape (v8impl::V8LocalValueFromJsValue (escapee)));
2346
+ return napi_clear_last_error (env);
2347
+ }
2348
+ return napi_set_last_error (env, napi_escape_called_twice);
2201
2349
}
2202
2350
2203
2351
napi_status napi_new_instance (napi_env env,
@@ -2250,7 +2398,7 @@ napi_status napi_instanceof(napi_env env,
2250
2398
}
2251
2399
2252
2400
if (env->has_instance_available ) {
2253
- napi_value value, js_result, has_instance = nullptr ;
2401
+ napi_value value, js_result = nullptr , has_instance = nullptr ;
2254
2402
napi_status status = napi_generic_failure;
2255
2403
napi_valuetype value_type;
2256
2404
@@ -2530,7 +2678,7 @@ napi_status napi_create_arraybuffer(napi_env env,
2530
2678
v8::ArrayBuffer::New (isolate, byte_length);
2531
2679
2532
2680
// Optionally return a pointer to the buffer's data, to avoid another call to
2533
- // retreive it.
2681
+ // retrieve it.
2534
2682
if (data != nullptr ) {
2535
2683
*data = buffer->GetContents ().Data ();
2536
2684
}
@@ -2713,6 +2861,13 @@ napi_status napi_get_typedarray_info(napi_env env,
2713
2861
return napi_clear_last_error (env);
2714
2862
}
2715
2863
2864
+ napi_status napi_get_version (napi_env env, uint32_t * result) {
2865
+ CHECK_ENV (env);
2866
+ CHECK_ARG (env, result);
2867
+ *result = NAPI_VERSION;
2868
+ return napi_clear_last_error (env);
2869
+ }
2870
+
2716
2871
namespace uvimpl {
2717
2872
2718
2873
static napi_status ConvertUVErrorCode (int code) {
@@ -2781,7 +2936,7 @@ class Work {
2781
2936
// report it as a fatal exception. (There is no JavaScript on the
2782
2937
// callstack that can possibly handle it.)
2783
2938
if (!env->last_exception .IsEmpty ()) {
2784
- v8::TryCatch try_catch;
2939
+ v8::TryCatch try_catch (env-> isolate ) ;
2785
2940
env->isolate ->ThrowException (
2786
2941
v8::Local<v8::Value>::New (env->isolate , env->last_exception ));
2787
2942
node::FatalException (env->isolate , try_catch);
0 commit comments