n-api: do not call into JS when that is not allowed

Check whether calling into JS is allowed before doing so.

PR-URL: https://github.com/nodejs/node/pull/26127
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
This commit is contained in:
Anna Henningsen 2019-02-15 11:48:56 +01:00
parent 783c65ebc4
commit 441ef4d7f0
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
5 changed files with 84 additions and 5 deletions

View File

@ -11,6 +11,7 @@ struct napi_env__ {
context_persistent(isolate, context) {
CHECK_EQ(isolate, context->GetIsolate());
}
virtual ~napi_env__() {}
v8::Isolate* const isolate; // Shortcut for context()->GetIsolate()
v8impl::Persistent<v8::Context> context_persistent;
@ -21,6 +22,8 @@ struct napi_env__ {
inline void Ref() { refs++; }
inline void Unref() { if ( --refs == 0) delete this; }
virtual bool can_call_into_js() const { return true; }
v8impl::Persistent<v8::Value> last_exception;
napi_extended_error_info last_error;
int open_handle_scopes = 0;
@ -68,11 +71,12 @@ napi_status napi_set_last_error(napi_env env, napi_status error_code,
RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status))
// NAPI_PREAMBLE is not wrapped in do..while: try_catch must have function scope
#define NAPI_PREAMBLE(env) \
CHECK_ENV((env)); \
RETURN_STATUS_IF_FALSE((env), (env)->last_exception.IsEmpty(), \
napi_pending_exception); \
napi_clear_last_error((env)); \
#define NAPI_PREAMBLE(env) \
CHECK_ENV((env)); \
RETURN_STATUS_IF_FALSE((env), \
(env)->last_exception.IsEmpty() && (env)->can_call_into_js(), \
napi_pending_exception); \
napi_clear_last_error((env)); \
v8impl::TryCatch try_catch((env))
#define CHECK_TO_TYPE(env, type, context, result, src, status) \

View File

@ -12,9 +12,14 @@ struct node_napi_env__ : public napi_env__ {
napi_env__(context) {
CHECK_NOT_NULL(node_env());
}
inline node::Environment* node_env() const {
return node::Environment::GetCurrent(context());
}
bool can_call_into_js() const override {
return node_env()->can_call_into_js();
}
};
typedef node_napi_env__* node_napi_env;

View File

@ -0,0 +1,8 @@
{
"targets": [
{
"target_name": "test_worker_terminate",
"sources": [ "test_worker_terminate.c" ]
}
]
}

View File

@ -0,0 +1,23 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const { Worker, isMainThread, workerData } = require('worker_threads');
if (isMainThread) {
const counter = new Int32Array(new SharedArrayBuffer(4));
const worker = new Worker(__filename, { workerData: { counter } });
worker.on('exit', common.mustCall(() => {
assert.strictEqual(counter[0], 1);
}));
worker.on('error', common.mustNotCall());
} else {
const { Test } = require(`./build/${common.buildType}/test_worker_terminate`);
const { counter } = workerData;
// Test() tries to call a function twice and asserts that the second call does
// not work because of a pending exception.
Test(() => {
Atomics.add(counter, 0, 1);
process.exit();
});
}

View File

@ -0,0 +1,39 @@
#include <stdio.h>
#include <node_api.h>
#include <assert.h>
#include "../../js-native-api/common.h"
napi_value Test(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value recv;
napi_value argv[1];
napi_status status;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &recv, NULL));
NAPI_ASSERT(env, argc >= 1, "Not enough arguments, expected 1.");
napi_valuetype t;
NAPI_CALL(env, napi_typeof(env, argv[0], &t));
NAPI_ASSERT(env, t == napi_function,
"Wrong first argument, function expected.");
status = napi_call_function(env, recv, argv[0], 0, NULL, NULL);
assert(status == napi_ok);
status = napi_call_function(env, recv, argv[0], 0, NULL, NULL);
assert(status == napi_pending_exception);
return NULL;
}
napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor properties[] = {
DECLARE_NAPI_PROPERTY("Test", Test)
};
NAPI_CALL(env, napi_define_properties(
env, exports, sizeof(properties) / sizeof(*properties), properties));
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)