diff --git a/src/node_api.cc b/src/node_api.cc index 90ec85e65149dc..1a2bf75c63b3bc 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -48,6 +48,7 @@ struct napi_env__ { v8::Persistent accessor_data_template; bool has_instance_available; napi_extended_error_info last_error; + int open_handle_scopes = 0; }; #define ENV_OBJECT_TEMPLATE(env, prefix, destination, field_count) \ @@ -508,12 +509,16 @@ class CallbackWrapperBase : public CallbackWrapper { // Make sure any errors encountered last time we were in N-API are gone. napi_clear_last_error(env); + int open_handle_scopes = env->open_handle_scopes; + napi_value result = cb(env, cbinfo_wrapper); if (result != nullptr) { this->SetReturnValue(result); } + CHECK_EQ(env->open_handle_scopes, open_handle_scopes); + if (!env->last_exception.IsEmpty()) { isolate->ThrowException( v8::Local::New(isolate, env->last_exception)); @@ -2587,6 +2592,7 @@ napi_status napi_open_handle_scope(napi_env env, napi_handle_scope* result) { *result = v8impl::JsHandleScopeFromV8HandleScope( new v8impl::HandleScopeWrapper(env->isolate)); + env->open_handle_scopes++; return napi_clear_last_error(env); } @@ -2595,7 +2601,11 @@ napi_status napi_close_handle_scope(napi_env env, napi_handle_scope scope) { // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, scope); + if (env->open_handle_scopes == 0) { + return napi_handle_scope_mismatch; + } + env->open_handle_scopes--; delete v8impl::V8HandleScopeFromJsHandleScope(scope); return napi_clear_last_error(env); } @@ -2610,6 +2620,7 @@ napi_status napi_open_escapable_handle_scope( *result = v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope( new v8impl::EscapableHandleScopeWrapper(env->isolate)); + env->open_handle_scopes++; return napi_clear_last_error(env); } @@ -2620,8 +2631,12 @@ napi_status napi_close_escapable_handle_scope( // JS exceptions. CHECK_ENV(env); CHECK_ARG(env, scope); + if (env->open_handle_scopes == 0) { + return napi_handle_scope_mismatch; + } delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + env->open_handle_scopes--; return napi_clear_last_error(env); } diff --git a/src/node_api_types.h b/src/node_api_types.h index 574cb6ff984f16..230c1f4ae3446f 100644 --- a/src/node_api_types.h +++ b/src/node_api_types.h @@ -69,7 +69,8 @@ typedef enum { napi_generic_failure, napi_pending_exception, napi_cancelled, - napi_escape_called_twice + napi_escape_called_twice, + napi_handle_scope_mismatch } napi_status; typedef napi_value (*napi_callback)(napi_env env, diff --git a/test/addons-napi/test_handle_scope/test.js b/test/addons-napi/test_handle_scope/test.js index cb687d0bcbf413..53abfe178c8d7d 100644 --- a/test/addons-napi/test_handle_scope/test.js +++ b/test/addons-napi/test_handle_scope/test.js @@ -10,11 +10,7 @@ testHandleScope.NewScope(); assert.ok(testHandleScope.NewScopeEscape() instanceof Object); -assert.throws( - () => { - testHandleScope.NewScopeEscapeTwice(); - }, - Error); +testHandleScope.NewScopeEscapeTwice(); assert.throws( () => { diff --git a/test/addons-napi/test_handle_scope/test_handle_scope.c b/test/addons-napi/test_handle_scope/test_handle_scope.c index 353a8a44031221..102abb2be612f0 100644 --- a/test/addons-napi/test_handle_scope/test_handle_scope.c +++ b/test/addons-napi/test_handle_scope/test_handle_scope.c @@ -33,13 +33,15 @@ napi_value NewScopeEscapeTwice(napi_env env, napi_callback_info info) { napi_escapable_handle_scope scope; napi_value output = NULL; napi_value escapee = NULL; + napi_status status; NAPI_CALL(env, napi_open_escapable_handle_scope(env, &scope)); NAPI_CALL(env, napi_create_object(env, &output)); NAPI_CALL(env, napi_escape_handle(env, scope, output, &escapee)); - NAPI_CALL(env, napi_escape_handle(env, scope, output, &escapee)); + status = napi_escape_handle(env, scope, output, &escapee); + NAPI_ASSERT(env, status == napi_escape_called_twice, "Escaping twice fails"); NAPI_CALL(env, napi_close_escapable_handle_scope(env, scope)); - return escapee; + return NULL; } napi_value NewScopeWithException(napi_env env, napi_callback_info info) {