Where Online Learning is simpler!
The C and C++ Include Header Files
cat -n /usr/include/nodejs/src/js_native_api_v8.h
1 #ifndef SRC_JS_NATIVE_API_V8_H_ 2 #define SRC_JS_NATIVE_API_V8_H_ 3 4 #include "js_native_api_types.h" 5 #include "js_native_api_v8_internals.h" 6 7 inline napi_status napi_clear_last_error(node_api_basic_env env); 8 9 namespace v8impl { 10 11 // Base class to track references and finalizers in a doubly linked list. 12 class RefTracker { 13 public: 14 using RefList = RefTracker; 15 16 RefTracker() = default; 17 virtual ~RefTracker() = default; 18 virtual void Finalize() {} 19 20 inline void Link(RefList* list) { 21 prev_ = list; 22 next_ = list->next_; 23 if (next_ != nullptr) { 24 next_->prev_ = this; 25 } 26 list->next_ = this; 27 } 28 29 inline void Unlink() { 30 if (prev_ != nullptr) { 31 prev_->next_ = next_; 32 } 33 if (next_ != nullptr) { 34 next_->prev_ = prev_; 35 } 36 prev_ = nullptr; 37 next_ = nullptr; 38 } 39 40 static void FinalizeAll(RefList* list) { 41 while (list->next_ != nullptr) { 42 list->next_->Finalize(); 43 } 44 } 45 46 private: 47 RefList* next_ = nullptr; 48 RefList* prev_ = nullptr; 49 }; 50 51 } // end of namespace v8impl 52 53 struct napi_env__ { 54 explicit napi_env__(v8::Local<v8::Context> context, 55 int32_t module_api_version) 56 : isolate(context->GetIsolate()), 57 context_persistent(isolate, context), 58 module_api_version(module_api_version) { 59 napi_clear_last_error(this); 60 } 61 62 inline v8::Local<v8::Context> context() const { 63 return v8impl::PersistentToLocal::Strong(context_persistent); 64 } 65 66 inline void Ref() { refs++; } 67 inline void Unref() { 68 if (--refs == 0) DeleteMe(); 69 } 70 71 virtual bool can_call_into_js() const { return true; } 72 73 static inline void HandleThrow(napi_env env, v8::Local<v8::Value> value) { 74 if (env->terminatedOrTerminating()) { 75 return; 76 } 77 env->isolate->ThrowException(value); 78 } 79 80 // i.e. whether v8 exited or is about to exit 81 inline bool terminatedOrTerminating() { 82 return this->isolate->IsExecutionTerminating() || !can_call_into_js(); 83 } 84 85 // v8 uses a special exception to indicate termination, the 86 // `handle_exception` callback should identify such case using 87 // terminatedOrTerminating() before actually handle the exception 88 template <typename T, typename U = decltype(HandleThrow)> 89 inline void CallIntoModule(T&& call, U&& handle_exception = HandleThrow) { 90 int open_handle_scopes_before = open_handle_scopes; 91 int open_callback_scopes_before = open_callback_scopes; 92 napi_clear_last_error(this); 93 call(this); 94 CHECK_EQ(open_handle_scopes, open_handle_scopes_before); 95 CHECK_EQ(open_callback_scopes, open_callback_scopes_before); 96 if (!last_exception.IsEmpty()) { 97 handle_exception(this, last_exception.Get(this->isolate)); 98 last_exception.Reset(); 99 } 100 } 101 102 virtual void CallFinalizer(napi_finalize cb, void* data, void* hint) = 0; 103 104 // Invoke finalizer from V8 garbage collector. 105 void InvokeFinalizerFromGC(v8impl::RefTracker* finalizer); 106 107 // Enqueue the finalizer to the napi_env's own queue of the second pass 108 // weak callback. 109 // Implementation should drain the queue at the time it is safe to call 110 // into JavaScript. 111 virtual void EnqueueFinalizer(v8impl::RefTracker* finalizer) { 112 pending_finalizers.emplace(finalizer); 113 } 114 115 // Remove the finalizer from the scheduled second pass weak callback queue. 116 // The finalizer can be deleted after this call. 117 virtual void DequeueFinalizer(v8impl::RefTracker* finalizer) { 118 pending_finalizers.erase(finalizer); 119 } 120 121 virtual void DeleteMe() { 122 // First we must finalize those references that have `napi_finalizer` 123 // callbacks. The reason is that addons might store other references which 124 // they delete during their `napi_finalizer` callbacks. If we deleted such 125 // references here first, they would be doubly deleted when the 126 // `napi_finalizer` deleted them subsequently. 127 v8impl::RefTracker::FinalizeAll(&finalizing_reflist); 128 v8impl::RefTracker::FinalizeAll(&reflist); 129 delete this; 130 } 131 132 void CheckGCAccess() { 133 if (module_api_version == NAPI_VERSION_EXPERIMENTAL && in_gc_finalizer) { 134 v8impl::OnFatalError( 135 nullptr, 136 "Finalizer is calling a function that may affect GC state.\n" 137 "The finalizers are run directly from GC and must not affect GC " 138 "state.\n" 139 "Use `node_api_post_finalizer` from inside of the finalizer to work " 140 "around this issue.\n" 141 "It schedules the call as a new task in the event loop."); 142 } 143 } 144 145 v8::Isolate* const isolate; // Shortcut for context()->GetIsolate() 146 v8impl::Persistent<v8::Context> context_persistent; 147 148 v8impl::Persistent<v8::Value> last_exception; 149 150 // We store references in two different lists, depending on whether they have 151 // `napi_finalizer` callbacks, because we must first finalize the ones that 152 // have such a callback. See `~napi_env__()` above for details. 153 v8impl::RefTracker::RefList reflist; 154 v8impl::RefTracker::RefList finalizing_reflist; 155 // The invocation order of the finalizers is not determined. 156 std::unordered_set<v8impl::RefTracker*> pending_finalizers; 157 napi_extended_error_info last_error; 158 int open_handle_scopes = 0; 159 int open_callback_scopes = 0; 160 int refs = 1; 161 void* instance_data = nullptr; 162 int32_t module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION; 163 bool in_gc_finalizer = false; 164 165 protected: 166 // Should not be deleted directly. Delete with `napi_env__::DeleteMe()` 167 // instead. 168 virtual ~napi_env__() = default; 169 }; 170 171 inline napi_status napi_clear_last_error(node_api_basic_env basic_env) { 172 napi_env env = const_cast<napi_env>(basic_env); 173 env->last_error.error_code = napi_ok; 174 env->last_error.engine_error_code = 0; 175 env->last_error.engine_reserved = nullptr; 176 env->last_error.error_message = nullptr; 177 return napi_ok; 178 } 179 180 inline napi_status napi_set_last_error(node_api_basic_env basic_env, 181 napi_status error_code, 182 uint32_t engine_error_code = 0, 183 void* engine_reserved = nullptr) { 184 napi_env env = const_cast<napi_env>(basic_env); 185 env->last_error.error_code = error_code; 186 env->last_error.engine_error_code = engine_error_code; 187 env->last_error.engine_reserved = engine_reserved; 188 return error_code; 189 } 190 191 #define RETURN_STATUS_IF_FALSE(env, condition, status) \ 192 do { \ 193 if (!(condition)) { \ 194 return napi_set_last_error((env), (status)); \ 195 } \ 196 } while (0) 197 198 #define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status) \ 199 do { \ 200 if (!(condition)) { \ 201 return napi_set_last_error( \ 202 (env), try_catch.HasCaught() ? napi_pending_exception : (status)); \ 203 } \ 204 } while (0) 205 206 #define CHECK_ENV(env) \ 207 do { \ 208 if ((env) == nullptr) { \ 209 return napi_invalid_arg; \ 210 } \ 211 } while (0) 212 213 #define CHECK_ENV_NOT_IN_GC(env) \ 214 do { \ 215 CHECK_ENV((env)); \ 216 (env)->CheckGCAccess(); \ 217 } while (0) 218 219 #define CHECK_ARG(env, arg) \ 220 RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), napi_invalid_arg) 221 222 #define CHECK_ARG_WITH_PREAMBLE(env, arg) \ 223 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( \ 224 (env), ((arg) != nullptr), napi_invalid_arg) 225 226 #define CHECK_MAYBE_EMPTY(env, maybe, status) \ 227 RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) 228 229 #define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \ 230 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status)) 231 232 // NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope 233 #define NAPI_PREAMBLE(env) \ 234 CHECK_ENV_NOT_IN_GC((env)); \ 235 RETURN_STATUS_IF_FALSE( \ 236 (env), (env)->last_exception.IsEmpty(), napi_pending_exception); \ 237 RETURN_STATUS_IF_FALSE( \ 238 (env), \ 239 (env)->can_call_into_js(), \ 240 (env->module_api_version >= 10 ? napi_cannot_run_js \ 241 : napi_pending_exception)); \ 242 napi_clear_last_error((env)); \ 243 v8impl::TryCatch try_catch((env)) 244 245 #define CHECK_TO_TYPE(env, type, context, result, src, status) \ 246 do { \ 247 CHECK_ARG((env), (src)); \ 248 auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ 249 CHECK_MAYBE_EMPTY((env), maybe, (status)); \ 250 (result) = maybe.ToLocalChecked(); \ 251 } while (0) 252 253 #define CHECK_TO_TYPE_WITH_PREAMBLE(env, type, context, result, src, status) \ 254 do { \ 255 CHECK_ARG_WITH_PREAMBLE((env), (src)); \ 256 auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ 257 CHECK_MAYBE_EMPTY_WITH_PREAMBLE((env), maybe, (status)); \ 258 (result) = maybe.ToLocalChecked(); \ 259 } while (0) 260 261 #define CHECK_TO_FUNCTION(env, result, src) \ 262 do { \ 263 CHECK_ARG((env), (src)); \ 264 v8::Local<v8::Value> v8value = v8impl::V8LocalValueFromJsValue((src)); \ 265 RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), napi_invalid_arg); \ 266 (result) = v8value.As<v8::Function>(); \ 267 } while (0) 268 269 #define CHECK_TO_OBJECT(env, context, result, src) \ 270 CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected) 271 272 #define CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, result, src) \ 273 CHECK_TO_TYPE_WITH_PREAMBLE( \ 274 (env), Object, (context), (result), (src), napi_object_expected) 275 276 #define CHECK_TO_STRING(env, context, result, src) \ 277 CHECK_TO_TYPE((env), String, (context), (result), (src), napi_string_expected) 278 279 #define GET_RETURN_STATUS(env) \ 280 (!try_catch.HasCaught() \ 281 ? napi_ok \ 282 : napi_set_last_error((env), napi_pending_exception)) 283 284 #define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ 285 do { \ 286 if (!(condition)) { \ 287 napi_throw_range_error((env), (error), (message)); \ 288 return napi_set_last_error((env), napi_generic_failure); \ 289 } \ 290 } while (0) 291 292 #define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \ 293 RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status)) 294 295 #define STATUS_CALL(call) \ 296 do { \ 297 napi_status status = (call); \ 298 if (status != napi_ok) return status; \ 299 } while (0) 300 301 namespace v8impl { 302 303 //=== Conversion between V8 Handles and napi_value ======================== 304 305 // This asserts v8::Local<> will always be implemented with a single 306 // pointer field so that we can pass it around as a void*. 307 static_assert(sizeof(v8::Local<v8::Value>) == sizeof(napi_value), 308 "Cannot convert between v8::Local<v8::Value> and napi_value"); 309 310 inline napi_value JsValueFromV8LocalValue(v8::Local<v8::Value> local) { 311 return reinterpret_cast<napi_value>(*local); 312 } 313 314 inline v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) { 315 v8::Local<v8::Value> local; 316 memcpy(static_cast<void*>(&local), &v, sizeof(v)); 317 return local; 318 } 319 320 // Adapter for napi_finalize callbacks. 321 class Finalizer { 322 public: 323 Finalizer(napi_env env, 324 napi_finalize finalize_callback, 325 void* finalize_data, 326 void* finalize_hint) 327 : env_(env), 328 finalize_callback_(finalize_callback), 329 finalize_data_(finalize_data), 330 finalize_hint_(finalize_hint) {} 331 332 napi_env env() { return env_; } 333 void* data() { return finalize_data_; } 334 335 void ResetEnv(); 336 void ResetFinalizer(); 337 void CallFinalizer(); 338 339 private: 340 napi_env env_; 341 napi_finalize finalize_callback_; 342 void* finalize_data_; 343 void* finalize_hint_; 344 }; 345 346 class TryCatch : public v8::TryCatch { 347 public: 348 explicit TryCatch(napi_env env) : v8::TryCatch(env->isolate), _env(env) {} 349 350 ~TryCatch() { 351 if (HasCaught()) { 352 _env->last_exception.Reset(_env->isolate, Exception()); 353 } 354 } 355 356 private: 357 napi_env _env; 358 }; 359 360 // Wrapper around Finalizer that can be tracked. 361 class TrackedFinalizer final : public RefTracker { 362 public: 363 static TrackedFinalizer* New(napi_env env, 364 napi_finalize finalize_callback, 365 void* finalize_data, 366 void* finalize_hint); 367 ~TrackedFinalizer() override; 368 369 void* data() { return finalizer_.data(); } 370 371 private: 372 TrackedFinalizer(napi_env env, 373 napi_finalize finalize_callback, 374 void* finalize_data, 375 void* finalize_hint); 376 void Finalize() override; 377 378 private: 379 Finalizer finalizer_; 380 }; 381 382 // Ownership of a reference. 383 enum class ReferenceOwnership : uint8_t { 384 // The reference is owned by the runtime. No userland call is needed to 385 // destruct the reference. 386 kRuntime, 387 // The reference is owned by the userland. User code is responsible to delete 388 // the reference with appropriate node-api calls. 389 kUserland, 390 }; 391 392 // Wrapper around v8impl::Persistent. 393 class Reference : public RefTracker { 394 public: 395 static Reference* New(napi_env env, 396 v8::Local<v8::Value> value, 397 uint32_t initial_refcount, 398 ReferenceOwnership ownership); 399 ~Reference() override; 400 401 uint32_t Ref(); 402 uint32_t Unref(); 403 v8::Local<v8::Value> Get(napi_env env); 404 405 virtual void ResetFinalizer() {} 406 virtual void* Data() { return nullptr; } 407 408 uint32_t refcount() const { return refcount_; } 409 ReferenceOwnership ownership() { return ownership_; } 410 411 protected: 412 Reference(napi_env env, 413 v8::Local<v8::Value> value, 414 uint32_t initial_refcount, 415 ReferenceOwnership ownership); 416 virtual void CallUserFinalizer() {} 417 virtual void InvokeFinalizerFromGC(); 418 419 private: 420 static void WeakCallback(const v8::WeakCallbackInfo<Reference>& data); 421 void SetWeak(); 422 void Finalize() override; 423 424 private: 425 v8impl::Persistent<v8::Value> persistent_; 426 uint32_t refcount_; 427 ReferenceOwnership ownership_; 428 bool can_be_weak_; 429 }; 430 431 // Reference that can store additional data. 432 class ReferenceWithData final : public Reference { 433 public: 434 static ReferenceWithData* New(napi_env env, 435 v8::Local<v8::Value> value, 436 uint32_t initial_refcount, 437 ReferenceOwnership ownership, 438 void* data); 439 440 void* Data() override { return data_; } 441 442 private: 443 ReferenceWithData(napi_env env, 444 v8::Local<v8::Value> value, 445 uint32_t initial_refcount, 446 ReferenceOwnership ownership, 447 void* data); 448 449 private: 450 void* data_; 451 }; 452 453 // Reference that has a user finalizer callback. 454 class ReferenceWithFinalizer final : public Reference { 455 public: 456 static ReferenceWithFinalizer* New(napi_env env, 457 v8::Local<v8::Value> value, 458 uint32_t initial_refcount, 459 ReferenceOwnership ownership, 460 napi_finalize finalize_callback, 461 void* finalize_data, 462 void* finalize_hint); 463 ~ReferenceWithFinalizer() override; 464 465 void ResetFinalizer() override { finalizer_.ResetFinalizer(); } 466 void* Data() override { return finalizer_.data(); } 467 468 private: 469 ReferenceWithFinalizer(napi_env env, 470 v8::Local<v8::Value> value, 471 uint32_t initial_refcount, 472 ReferenceOwnership ownership, 473 napi_finalize finalize_callback, 474 void* finalize_data, 475 void* finalize_hint); 476 void CallUserFinalizer() override; 477 void InvokeFinalizerFromGC() override; 478 479 private: 480 Finalizer finalizer_; 481 }; 482 483 } // end of namespace v8impl 484 485 #endif // SRC_JS_NATIVE_API_V8_H_