mirror of
https://github.com/nodejs/node.git
synced 2025-05-11 19:16:32 +00:00

We define a new type called `node_api_nogc_env` as the `const` version of `napi_env` and `node_api_nogc_finalize` as a variant of `napi_finalize` that accepts a `node_api_nogc_env` as its first argument. We then modify those APIs which do not affect GC state as accepting a `node_api_nogc_env`. APIs accepting finalizer callbacks are modified to accept `node_api_nogc_finalize` callbacks. Thus, the only way to attach a `napi_finalize` callback, wherein Node-APIs affecting GC state may be called is to call `node_api_post_finalizer` from a `node_api_nogc_finalize` callback. In keeping with the process of introducing new Node-APIs, this feature is guarded by `NAPI_EXPERIMENTAL`. Since this feature modifies APIs already marked as stable, it is additionally guared by `NODE_API_EXPERIMENTAL_NOGC_ENV`, so as to provide a further buffer to adoption. Nevertheless, both guards must be removed upon releasing a new version of Node-API. PR-URL: https://github.com/nodejs/node/pull/50060 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Vladimir Morozov <vmorozov@microsoft.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
149 lines
5.4 KiB
C
149 lines
5.4 KiB
C
#include <js_native_api.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "../common.h"
|
|
#include "../entry_point.h"
|
|
|
|
typedef struct {
|
|
int32_t finalize_count;
|
|
napi_ref js_func;
|
|
} FinalizerData;
|
|
|
|
static void finalizerOnlyCallback(node_api_nogc_env env,
|
|
void* finalize_data,
|
|
void* finalize_hint) {
|
|
FinalizerData* data = (FinalizerData*)finalize_data;
|
|
int32_t count = ++data->finalize_count;
|
|
|
|
// It is safe to access instance data
|
|
NODE_API_NOGC_CALL_RETURN_VOID(env,
|
|
napi_get_instance_data(env, (void**)&data));
|
|
NODE_API_NOGC_ASSERT_RETURN_VOID(count = data->finalize_count,
|
|
"Expected to be the same FinalizerData");
|
|
}
|
|
|
|
static void finalizerCallingJSCallback(napi_env env,
|
|
void* finalize_data,
|
|
void* finalize_hint) {
|
|
napi_value js_func, undefined;
|
|
FinalizerData* data = (FinalizerData*)finalize_data;
|
|
NODE_API_CALL_RETURN_VOID(
|
|
env, napi_get_reference_value(env, data->js_func, &js_func));
|
|
NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
|
|
NODE_API_CALL_RETURN_VOID(
|
|
env, napi_call_function(env, undefined, js_func, 0, NULL, NULL));
|
|
NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_func));
|
|
data->js_func = NULL;
|
|
++data->finalize_count;
|
|
}
|
|
|
|
// Schedule async finalizer to run JavaScript-touching code.
|
|
static void finalizerWithJSCallback(node_api_nogc_env env,
|
|
void* finalize_data,
|
|
void* finalize_hint) {
|
|
NODE_API_NOGC_CALL_RETURN_VOID(
|
|
env,
|
|
node_api_post_finalizer(
|
|
env, finalizerCallingJSCallback, finalize_data, finalize_hint));
|
|
}
|
|
|
|
static void finalizerWithFailedJSCallback(node_api_nogc_env nogc_env,
|
|
void* finalize_data,
|
|
void* finalize_hint) {
|
|
// Intentionally cast to a napi_env to test the fatal failure.
|
|
napi_env env = (napi_env)nogc_env;
|
|
napi_value obj;
|
|
FinalizerData* data = (FinalizerData*)finalize_data;
|
|
++data->finalize_count;
|
|
NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &obj));
|
|
}
|
|
|
|
static napi_value addFinalizer(napi_env env, napi_callback_info info) {
|
|
size_t argc = 1;
|
|
napi_value argv[1] = {0};
|
|
FinalizerData* data;
|
|
|
|
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
|
|
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
|
|
NODE_API_CALL(env,
|
|
napi_add_finalizer(
|
|
env, argv[0], data, finalizerOnlyCallback, NULL, NULL));
|
|
return NULL;
|
|
}
|
|
|
|
// This finalizer is going to call JavaScript from finalizer and succeed.
|
|
static napi_value addFinalizerWithJS(napi_env env, napi_callback_info info) {
|
|
size_t argc = 2;
|
|
napi_value argv[2] = {0};
|
|
napi_valuetype arg_type;
|
|
FinalizerData* data;
|
|
|
|
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
|
|
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
|
|
NODE_API_CALL(env, napi_typeof(env, argv[1], &arg_type));
|
|
NODE_API_ASSERT(
|
|
env, arg_type == napi_function, "Expected function as the second arg");
|
|
NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &data->js_func));
|
|
NODE_API_CALL(env,
|
|
napi_add_finalizer(
|
|
env, argv[0], data, finalizerWithJSCallback, NULL, NULL));
|
|
return NULL;
|
|
}
|
|
|
|
// This finalizer is going to call JavaScript from finalizer and fail.
|
|
static napi_value addFinalizerFailOnJS(napi_env env, napi_callback_info info) {
|
|
size_t argc = 1;
|
|
napi_value argv[1] = {0};
|
|
FinalizerData* data;
|
|
|
|
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
|
|
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
|
|
NODE_API_CALL(
|
|
env,
|
|
napi_add_finalizer(
|
|
env, argv[0], data, finalizerWithFailedJSCallback, NULL, NULL));
|
|
return NULL;
|
|
}
|
|
|
|
static napi_value getFinalizerCallCount(napi_env env, napi_callback_info info) {
|
|
size_t argc = 1;
|
|
napi_value argv[1];
|
|
FinalizerData* data;
|
|
napi_value result;
|
|
|
|
NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
|
|
NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
|
|
NODE_API_CALL(env, napi_create_int32(env, data->finalize_count, &result));
|
|
return result;
|
|
}
|
|
|
|
static void finalizeData(napi_env env, void* data, void* hint) {
|
|
free(data);
|
|
}
|
|
|
|
EXTERN_C_START
|
|
napi_value Init(napi_env env, napi_value exports) {
|
|
FinalizerData* data = (FinalizerData*)malloc(sizeof(FinalizerData));
|
|
NODE_API_ASSERT(env, data != NULL, "Failed to allocate memory");
|
|
memset(data, 0, sizeof(FinalizerData));
|
|
NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, NULL));
|
|
napi_property_descriptor descriptors[] = {
|
|
DECLARE_NODE_API_PROPERTY("addFinalizer", addFinalizer),
|
|
DECLARE_NODE_API_PROPERTY("addFinalizerWithJS", addFinalizerWithJS),
|
|
DECLARE_NODE_API_PROPERTY("addFinalizerFailOnJS", addFinalizerFailOnJS),
|
|
DECLARE_NODE_API_PROPERTY("getFinalizerCallCount",
|
|
getFinalizerCallCount)};
|
|
|
|
NODE_API_CALL(
|
|
env,
|
|
napi_define_properties(env,
|
|
exports,
|
|
sizeof(descriptors) / sizeof(*descriptors),
|
|
descriptors));
|
|
|
|
return exports;
|
|
}
|
|
EXTERN_C_END
|