Skip to content

Commit

Permalink
add unit test for pure finalizers
Browse files Browse the repository at this point in the history
  • Loading branch information
vmoroz committed Jun 23, 2023
1 parent f4e3bd2 commit fee7037
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
12 changes: 12 additions & 0 deletions test/js-native-api/test_finalizer/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"targets": [
{
"target_name": "test_finalizer",
"defines": [ "NAPI_EXPERIMENTAL" ],
"sources": [
"../entry_point.c",
"test_finalizer.c"
]
}
]
}
35 changes: 35 additions & 0 deletions test/js-native-api/test_finalizer/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';
// Flags: --expose-gc

const common = require('../../common');
const test_finalizer = require(`./build/${common.buildType}/test_finalizer`);
const assert = require('assert');

function addFinalizer() {
// Define obj in a function context to let it GC-collected.
const obj = {};
test_finalizer.addFinalizer(obj);
}

addFinalizer();

for (let i = 0; i < 1000; ++i) {
global.gc();
if (test_finalizer.getFinalizerCallCount() === 1) {
break;
}
}

assert(test_finalizer.getFinalizerCallCount() === 1);

async function runAsyncTests() {
let js_is_called = false;
(() => {
const obj = {};
test_finalizer.addFinalizerWithJS(obj, () => { js_is_called = true; });
})();
await common.gcUntil('test JS finalizer',
() => (test_finalizer.getFinalizerCallCount() === 2));
assert(js_is_called);
}
runAsyncTests();
111 changes: 111 additions & 0 deletions test/js-native-api/test_finalizer/test_finalizer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include <js_native_api.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common.h"

typedef struct {
int32_t finalize_count;
napi_ref js_func;
} FinalizerData;

static void finalizerOnlyCallback(napi_env env,
void* finalize_data,
void* finalize_hint) {
FinalizerData* data = (FinalizerData*)finalize_data;
++data->finalize_count;
}

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(napi_env env,
void* finalize_data,
void* finalize_hint) {
FinalizerData* data = (FinalizerData*)finalize_data;
NODE_API_CALL_RETURN_VOID(env, node_api_post_finalizer(
env, finalizerCallingJSCallback, finalize_data, finalize_hint));
}

static napi_value addFinalizer(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1];
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, &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
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, &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;
}

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, &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("getFinalizerCallCount",
getFinalizerCallCount)};

NODE_API_CALL(
env,
napi_define_properties(env,
exports,
sizeof(descriptors) / sizeof(*descriptors),
descriptors));

return exports;
}
EXTERN_C_END

0 comments on commit fee7037

Please sign in to comment.