| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4dea9e0 commit 4bf8b6a
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3431,6 +3431,42 @@ is sufficient and appropriate. Use of the `napi_make_callback` function | |||
| 3431 | 3431 | may be required when implementing custom async behavior that does not use | |
| 3432 | 3432 | `napi_create_async_work`. | |
| 3433 | 3433 | ||
| 3434 | + ### *napi_open_callback_scope* | ||
| 3435 | + <!-- YAML | ||
| 3436 | + added: REPLACEME | ||
| 3437 | + --> | ||
| 3438 | + ```C | ||
| 3439 | + NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, | ||
| 3440 | + napi_value resource_object, | ||
| 3441 | + napi_async_context context, | ||
| 3442 | + napi_callback_scope* result) | ||
| 3443 | + ``` | ||
| 3444 | + - `[in] env`: The environment that the API is invoked under. | ||
| 3445 | + - `[in] resource_object`: An optional object associated with the async work | ||
| 3446 | + that will be passed to possible async_hooks [`init` hooks][]. | ||
| 3447 | + - `[in] context`: Context for the async operation that is | ||
| 3448 | + invoking the callback. This should be a value previously obtained | ||
| 3449 | + from [`napi_async_init`][]. | ||
| 3450 | + - `[out] result`: The newly created scope. | ||
| 3451 | + | ||
| 3452 | + There are cases(for example resolving promises) where it is | ||
| 3453 | + necessary to have the equivalent of the scope associated with a callback | ||
| 3454 | + in place when making certain N-API calls. If there is no other script on | ||
| 3455 | + the stack the [`napi_open_callback_scope`][] and | ||
| 3456 | + [`napi_close_callback_scope`][] functions can be used to open/close | ||
| 3457 | + the required scope. | ||
| 3458 | + | ||
| 3459 | + ### *napi_close_callback_scope* | ||
| 3460 | + <!-- YAML | ||
| 3461 | + added: REPLACEME | ||
| 3462 | + --> | ||
| 3463 | + ```C | ||
| 3464 | + NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, | ||
| 3465 | + napi_callback_scope scope) | ||
| 3466 | + ``` | ||
| 3467 | + - `[in] env`: The environment that the API is invoked under. | ||
| 3468 | + - `[in] scope`: The scope to be closed. | ||
| 3469 | + | ||
| 3434 | 3470 | ## Version Management | |
| 3435 | 3471 | ||
| 3436 | 3472 | ### napi_get_node_version | |
@@ -3716,6 +3752,7 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, | |||
| 3716 | 3752 | [`napi_async_init`]: #n_api_napi_async_init | |
| 3717 | 3753 | [`napi_cancel_async_work`]: #n_api_napi_cancel_async_work | |
| 3718 | 3754 | [`napi_close_escapable_handle_scope`]: #n_api_napi_close_escapable_handle_scope | |
| 3755 | + [`napi_close_callback_scope`]: #n_api_napi_close_callback_scope | ||
| 3719 | 3756 | [`napi_close_handle_scope`]: #n_api_napi_close_handle_scope | |
| 3720 | 3757 | [`napi_create_async_work`]: #n_api_napi_create_async_work | |
| 3721 | 3758 | [`napi_create_error`]: #n_api_napi_create_error | |
@@ -3741,6 +3778,7 @@ NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, | |||
| 3741 | 3778 | [`napi_get_last_error_info`]: #n_api_napi_get_last_error_info | |
| 3742 | 3779 | [`napi_get_and_clear_last_exception`]: #n_api_napi_get_and_clear_last_exception | |
| 3743 | 3780 | [`napi_make_callback`]: #n_api_napi_make_callback | |
| 3781 | + [`napi_open_callback_scope`]: #n_api_napi_open_callback_scope | ||
| 3744 | 3782 | [`napi_open_escapable_handle_scope`]: #n_api_napi_open_escapable_handle_scope | |
| 3745 | 3783 | [`napi_open_handle_scope`]: #n_api_napi_open_handle_scope | |
| 3746 | 3784 | [`napi_property_descriptor`]: #n_api_napi_property_descriptor | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -44,6 +44,7 @@ struct napi_env__ { | |||
| 44 | 44 | v8::Persistent<v8::ObjectTemplate> accessor_data_template; | |
| 45 | 45 | napi_extended_error_info last_error; | |
| 46 | 46 | int open_handle_scopes = 0; | |
| 47 | + int open_callback_scopes = 0; | ||
| 47 | 48 | uv_loop_t* loop = nullptr; | |
| 48 | 49 | }; | |
| 49 | 50 | ||
@@ -253,6 +254,18 @@ V8EscapableHandleScopeFromJsEscapableHandleScope( | |||
| 253 | 254 | return reinterpret_cast<EscapableHandleScopeWrapper*>(s); | |
| 254 | 255 | } | |
| 255 | 256 | ||
| 257 | + static | ||
| 258 | + napi_callback_scope JsCallbackScopeFromV8CallbackScope( | ||
| 259 | + node::CallbackScope* s) { | ||
| 260 | + return reinterpret_cast<napi_callback_scope>(s); | ||
| 261 | + } | ||
| 262 | + | ||
| 263 | + static | ||
| 264 | + node::CallbackScope* V8CallbackScopeFromJsCallbackScope( | ||
| 265 | + napi_callback_scope s) { | ||
| 266 | + return reinterpret_cast<node::CallbackScope*>(s); | ||
| 267 | + } | ||
| 268 | + | ||
| 256 | 269 | //=== Conversion between V8 Handles and napi_value ======================== | |
| 257 | 270 | ||
| 258 | 271 | // This asserts v8::Local<> will always be implemented with a single | |
@@ -544,6 +557,7 @@ class CallbackWrapperBase : public CallbackWrapper { | |||
| 544 | 557 | napi_clear_last_error(env); | |
| 545 | 558 | ||
| 546 | 559 | int open_handle_scopes = env->open_handle_scopes; | |
| 560 | + int open_callback_scopes = env->open_callback_scopes; | ||
| 547 | 561 | ||
| 548 | 562 | napi_value result = cb(env, cbinfo_wrapper); | |
| 549 | 563 | ||
@@ -552,6 +566,7 @@ class CallbackWrapperBase : public CallbackWrapper { | |||
| 552 | 566 | } | |
| 553 | 567 | ||
| 554 | 568 | CHECK_EQ(env->open_handle_scopes, open_handle_scopes); | |
| 569 | + CHECK_EQ(env->open_callback_scopes, open_callback_scopes); | ||
| 555 | 570 | ||
| 556 | 571 | if (!env->last_exception.IsEmpty()) { | |
| 557 | 572 | isolate->ThrowException( | |
@@ -911,7 +926,8 @@ const char* error_messages[] = {nullptr, | |||
| 911 | 926 | "An exception is pending", | |
| 912 | 927 | "The async work item was cancelled", | |
| 913 | 928 | "napi_escape_handle already called on scope", | |
| 914 | - "Invalid handle scope usage"}; | ||
| 929 | + "Invalid handle scope usage", | ||
| 930 | + "Invalid callback scope usage"}; | ||
| 915 | 931 | ||
| 916 | 932 | static inline napi_status napi_clear_last_error(napi_env env) { | |
| 917 | 933 | env->last_error.error_code = napi_ok; | |
@@ -942,9 +958,9 @@ napi_status napi_get_last_error_info(napi_env env, | |||
| 942 | 958 | // We don't have a napi_status_last as this would result in an ABI | |
| 943 | 959 | // change each time a message was added. | |
| 944 | 960 | static_assert( | |
| 945 | - node::arraysize(error_messages) == napi_handle_scope_mismatch + 1, | ||
| 961 | + node::arraysize(error_messages) == napi_callback_scope_mismatch + 1, | ||
| 946 | 962 | "Count of error messages must match count of error values"); | |
| 947 | - CHECK_LE(env->last_error.error_code, napi_handle_scope_mismatch); | ||
| 963 | + CHECK_LE(env->last_error.error_code, napi_callback_scope_mismatch); | ||
| 948 | 964 | ||
| 949 | 965 | // Wait until someone requests the last error information to fetch the error | |
| 950 | 966 | // message string | |
@@ -2633,6 +2649,46 @@ napi_status napi_escape_handle(napi_env env, | |||
| 2633 | 2649 | return napi_set_last_error(env, napi_escape_called_twice); | |
| 2634 | 2650 | } | |
| 2635 | 2651 | ||
| 2652 | + napi_status napi_open_callback_scope(napi_env env, | ||
| 2653 | + napi_value resource_object, | ||
| 2654 | + napi_async_context async_context_handle, | ||
| 2655 | + napi_callback_scope* result) { | ||
| 2656 | + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw | ||
| 2657 | + // JS exceptions. | ||
| 2658 | + CHECK_ENV(env); | ||
| 2659 | + CHECK_ARG(env, result); | ||
| 2660 | + | ||
| 2661 | + v8::Local<v8::Context> context = env->isolate->GetCurrentContext(); | ||
| 2662 | + | ||
| 2663 | + node::async_context* node_async_context = | ||
| 2664 | + reinterpret_cast<node::async_context*>(async_context_handle); | ||
| 2665 | + | ||
| 2666 | + v8::Local<v8::Object> resource; | ||
| 2667 | + CHECK_TO_OBJECT(env, context, resource, resource_object); | ||
| 2668 | + | ||
| 2669 | + *result = v8impl::JsCallbackScopeFromV8CallbackScope( | ||
| 2670 | + new node::CallbackScope(env->isolate, | ||
| 2671 | + resource, | ||
| 2672 | + *node_async_context)); | ||
| 2673 | + | ||
| 2674 | + env->open_callback_scopes++; | ||
| 2675 | + return napi_clear_last_error(env); | ||
| 2676 | + } | ||
| 2677 | + | ||
| 2678 | + napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope) { | ||
| 2679 | + // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw | ||
| 2680 | + // JS exceptions. | ||
| 2681 | + CHECK_ENV(env); | ||
| 2682 | + CHECK_ARG(env, scope); | ||
| 2683 | + if (env->open_callback_scopes == 0) { | ||
| 2684 | + return napi_callback_scope_mismatch; | ||
| 2685 | + } | ||
| 2686 | + | ||
| 2687 | + env->open_callback_scopes--; | ||
| 2688 | + delete v8impl::V8CallbackScopeFromJsCallbackScope(scope); | ||
| 2689 | + return napi_clear_last_error(env); | ||
| 2690 | + } | ||
| 2691 | + | ||
| 2636 | 2692 | napi_status napi_new_instance(napi_env env, | |
| 2637 | 2693 | napi_value constructor, | |
| 2638 | 2694 | size_t argc, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -424,6 +424,14 @@ NAPI_EXTERN napi_status napi_escape_handle(napi_env env, | |||
| 424 | 424 | napi_value escapee, | |
| 425 | 425 | napi_value* result); | |
| 426 | 426 | ||
| 427 | + NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, | ||
| 428 | + napi_value resource_object, | ||
| 429 | + napi_async_context context, | ||
| 430 | + napi_callback_scope* result); | ||
| 431 | + | ||
| 432 | + NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, | ||
| 433 | + napi_callback_scope scope); | ||
| 434 | + | ||
| 427 | 435 | // Methods to support error handling | |
| 428 | 436 | NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); | |
| 429 | 437 | NAPI_EXTERN napi_status napi_throw_error(napi_env env, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,6 +15,7 @@ typedef struct napi_value__ *napi_value; | |||
| 15 | 15 | typedef struct napi_ref__ *napi_ref; | |
| 16 | 16 | typedef struct napi_handle_scope__ *napi_handle_scope; | |
| 17 | 17 | typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope; | |
| 18 | + typedef struct napi_callback_scope__ *napi_callback_scope; | ||
| 18 | 19 | typedef struct napi_callback_info__ *napi_callback_info; | |
| 19 | 20 | typedef struct napi_async_context__ *napi_async_context; | |
| 20 | 21 | typedef struct napi_async_work__ *napi_async_work; | |
@@ -70,7 +71,8 @@ typedef enum { | |||
| 70 | 71 | napi_pending_exception, | |
| 71 | 72 | napi_cancelled, | |
| 72 | 73 | napi_escape_called_twice, | |
| 73 | - napi_handle_scope_mismatch | ||
| 74 | + napi_handle_scope_mismatch, | ||
| 75 | + napi_callback_scope_mismatch | ||
| 74 | 76 | } napi_status; | |
| 75 | 77 | ||
| 76 | 78 | typedef napi_value (*napi_callback)(napi_env env, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,138 @@ | |||
| 1 | + #include "node_api.h" | ||
| 2 | + #include "uv.h" | ||
| 3 | + #include "../common.h" | ||
| 4 | + | ||
| 5 | + namespace { | ||
| 6 | + | ||
| 7 | + // the test needs to fake out the async structure, so we need to use | ||
| 8 | + // the raw structure here and then cast as done behind the scenes | ||
| 9 | + // in napi calls. | ||
| 10 | + struct async_context { | ||
| 11 | + double async_id; | ||
| 12 | + double trigger_async_id; | ||
| 13 | + }; | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + napi_value RunInCallbackScope(napi_env env, napi_callback_info info) { | ||
| 17 | + size_t argc; | ||
| 18 | + napi_value args[4]; | ||
| 19 | + | ||
| 20 | + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr)); | ||
| 21 | + NAPI_ASSERT(env, argc == 4 , "Wrong number of arguments"); | ||
| 22 | + | ||
| 23 | + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); | ||
| 24 | + | ||
| 25 | + napi_valuetype valuetype; | ||
| 26 | + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype)); | ||
| 27 | + NAPI_ASSERT(env, valuetype == napi_object, | ||
| 28 | + "Wrong type of arguments. Expects an object as first argument."); | ||
| 29 | + | ||
| 30 | + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype)); | ||
| 31 | + NAPI_ASSERT(env, valuetype == napi_number, | ||
| 32 | + "Wrong type of arguments. Expects a number as second argument."); | ||
| 33 | + | ||
| 34 | + NAPI_CALL(env, napi_typeof(env, args[2], &valuetype)); | ||
| 35 | + NAPI_ASSERT(env, valuetype == napi_number, | ||
| 36 | + "Wrong type of arguments. Expects a number as third argument."); | ||
| 37 | + | ||
| 38 | + NAPI_CALL(env, napi_typeof(env, args[3], &valuetype)); | ||
| 39 | + NAPI_ASSERT(env, valuetype == napi_function, | ||
| 40 | + "Wrong type of arguments. Expects a function as third argument."); | ||
| 41 | + | ||
| 42 | + struct async_context context; | ||
| 43 | + NAPI_CALL(env, napi_get_value_double(env, args[1], &context.async_id)); | ||
| 44 | + NAPI_CALL(env, | ||
| 45 | + napi_get_value_double(env, args[2], &context.trigger_async_id)); | ||
| 46 | + | ||
| 47 | + napi_callback_scope scope = nullptr; | ||
| 48 | + NAPI_CALL( | ||
| 49 | + env, | ||
| 50 | + napi_open_callback_scope(env, | ||
| 51 | + args[0], | ||
| 52 | + reinterpret_cast<napi_async_context>(&context), | ||
| 53 | + &scope)); | ||
| 54 | + | ||
| 55 | + // if the function has an exception pending after the call that is ok | ||
| 56 | + // so we don't use NAPI_CALL as we must close the callback scope regardless | ||
| 57 | + napi_value result = nullptr; | ||
| 58 | + napi_status function_call_result = | ||
| 59 | + napi_call_function(env, args[0], args[3], 0, nullptr, &result); | ||
| 60 | + if (function_call_result != napi_ok) { | ||
| 61 | + GET_AND_THROW_LAST_ERROR((env)); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + NAPI_CALL(env, napi_close_callback_scope(env, scope)); | ||
| 65 | + | ||
| 66 | + return result; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + static napi_env shared_env = nullptr; | ||
| 70 | + static napi_deferred deferred = nullptr; | ||
| 71 | + | ||
| 72 | + static void Callback(uv_work_t* req, int ignored) { | ||
| 73 | + napi_env env = shared_env; | ||
| 74 | + | ||
| 75 | + napi_handle_scope handle_scope = nullptr; | ||
| 76 | + NAPI_CALL_RETURN_VOID(env, napi_open_handle_scope(env, &handle_scope)); | ||
| 77 | + | ||
| 78 | + napi_value resource_name; | ||
| 79 | + NAPI_CALL_RETURN_VOID(env, napi_create_string_utf8( | ||
| 80 | + env, "test", NAPI_AUTO_LENGTH, &resource_name)); | ||
| 81 | + napi_async_context context; | ||
| 82 | + NAPI_CALL_RETURN_VOID(env, | ||
| 83 | + napi_async_init(env, nullptr, resource_name, &context)); | ||
| 84 | + | ||
| 85 | + napi_value resource_object; | ||
| 86 | + NAPI_CALL_RETURN_VOID(env, napi_create_object(env, &resource_object)); | ||
| 87 | + | ||
| 88 | + napi_value undefined_value; | ||
| 89 | + NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined_value)); | ||
| 90 | + | ||
| 91 | + napi_callback_scope scope = nullptr; | ||
| 92 | + NAPI_CALL_RETURN_VOID(env, napi_open_callback_scope(env, | ||
| 93 | + resource_object, | ||
| 94 | + context, | ||
| 95 | + &scope)); | ||
| 96 | + | ||
| 97 | + NAPI_CALL_RETURN_VOID(env, | ||
| 98 | + napi_resolve_deferred(env, deferred, undefined_value)); | ||
| 99 | + | ||
| 100 | + NAPI_CALL_RETURN_VOID(env, napi_close_callback_scope(env, scope)); | ||
| 101 | + | ||
| 102 | + NAPI_CALL_RETURN_VOID(env, napi_close_handle_scope(env, handle_scope)); | ||
| 103 | + delete req; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + napi_value TestResolveAsync(napi_env env, napi_callback_info info) { | ||
| 107 | + napi_value promise = nullptr; | ||
| 108 | + if (deferred == nullptr) { | ||
| 109 | + shared_env = env; | ||
| 110 | + NAPI_CALL(env, napi_create_promise(env, &deferred, &promise)); | ||
| 111 | + | ||
| 112 | + uv_loop_t* loop = nullptr; | ||
| 113 | + NAPI_CALL(env, napi_get_uv_event_loop(env, &loop)); | ||
| 114 | + | ||
| 115 | + uv_work_t* req = new uv_work_t(); | ||
| 116 | + uv_queue_work(loop, | ||
| 117 | + req, | ||
| 118 | + [](uv_work_t*) {}, | ||
| 119 | + Callback); | ||
| 120 | + } | ||
| 121 | + return promise; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + napi_value Init(napi_env env, napi_value exports) { | ||
| 125 | + napi_property_descriptor descriptors[] = { | ||
| 126 | + DECLARE_NAPI_PROPERTY("runInCallbackScope", RunInCallbackScope), | ||
| 127 | + DECLARE_NAPI_PROPERTY("testResolveAsync", TestResolveAsync) | ||
| 128 | + }; | ||
| 129 | + | ||
| 130 | + NAPI_CALL(env, napi_define_properties( | ||
| 131 | + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); | ||
| 132 | + | ||
| 133 | + return exports; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + } // anonymous namespace | ||
| 137 | + | ||
| 138 | + NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,9 @@ | |||
| 1 | + { | ||
| 2 | + 'targets': [ | ||
| 3 | + { | ||
| 4 | + 'target_name': 'binding', | ||
| 5 | + 'defines': [ 'V8_DEPRECATION_WARNINGS=1' ], | ||
| 6 | + 'sources': [ 'binding.cc' ] | ||
| 7 | + } | ||
| 8 | + ] | ||
| 9 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,29 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../../common'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + const async_hooks = require('async_hooks'); | ||
| 6 | + | ||
| 7 | + // The async_hook that we enable would register the process.emitWarning() | ||
| 8 | + // call from loading the N-API addon as asynchronous activity because | ||
| 9 | + // it contains a process.nextTick() call. Monkey patch it to be a no-op | ||
| 10 | + // before we load the addon in order to avoid this. | ||
| 11 | + process.emitWarning = () => {}; | ||
| 12 | + | ||
| 13 | + const { runInCallbackScope } = require(`./build/${common.buildType}/binding`); | ||
| 14 | + | ||
| 15 | + let insideHook = false; | ||
| 16 | + async_hooks.createHook({ | ||
| 17 | + before: common.mustCall((id) => { | ||
| 18 | + assert.strictEqual(id, 1000); | ||
| 19 | + insideHook = true; | ||
| 20 | + }), | ||
| 21 | + after: common.mustCall((id) => { | ||
| 22 | + assert.strictEqual(id, 1000); | ||
| 23 | + insideHook = false; | ||
| 24 | + }) | ||
| 25 | + }).enable(); | ||
| 26 | + | ||
| 27 | + runInCallbackScope({}, 1000, 1000, () => { | ||
| 28 | + assert(insideHook); | ||
| 29 | + }); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,13 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const common = require('../../common'); | ||
| 4 | + const assert = require('assert'); | ||
| 5 | + const { testResolveAsync } = require(`./build/${common.buildType}/binding`); | ||
| 6 | + | ||
| 7 | + let called = false; | ||
| 8 | + testResolveAsync().then(common.mustCall(() => { | ||
| 9 | + called = true; | ||
| 10 | + })); | ||
| 11 | + | ||
| 12 | + setTimeout(common.mustCall(() => { assert(called); }), | ||
| 13 | + common.platformTimeout(20)); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments