1#include <js_native_api.h> 2#include <stdint.h> 3#include <stdio.h> 4#include <stdlib.h> 5#include <string.h> 6#include "../common.h" 7#include "../entry_point.h" 8 9typedef struct { 10 int32_t finalize_count; 11 napi_ref js_func; 12} FinalizerData; 13 14static void finalizerOnlyCallback(node_api_nogc_env env, 15 void* finalize_data, 16 void* finalize_hint) { 17 FinalizerData* data = (FinalizerData*)finalize_data; 18 int32_t count = ++data->finalize_count; 19 20 // It is safe to access instance data 21 NODE_API_NOGC_CALL_RETURN_VOID(env, 22 napi_get_instance_data(env, (void**)&data)); 23 NODE_API_NOGC_ASSERT_RETURN_VOID(count = data->finalize_count, 24 "Expected to be the same FinalizerData"); 25} 26 27static void finalizerCallingJSCallback(napi_env env, 28 void* finalize_data, 29 void* finalize_hint) { 30 napi_value js_func, undefined; 31 FinalizerData* data = (FinalizerData*)finalize_data; 32 NODE_API_CALL_RETURN_VOID( 33 env, napi_get_reference_value(env, data->js_func, &js_func)); 34 NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined)); 35 NODE_API_CALL_RETURN_VOID( 36 env, napi_call_function(env, undefined, js_func, 0, NULL, NULL)); 37 NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_func)); 38 data->js_func = NULL; 39 ++data->finalize_count; 40} 41 42// Schedule async finalizer to run JavaScript-touching code. 43static void finalizerWithJSCallback(node_api_nogc_env env, 44 void* finalize_data, 45 void* finalize_hint) { 46 NODE_API_NOGC_CALL_RETURN_VOID( 47 env, 48 node_api_post_finalizer( 49 env, finalizerCallingJSCallback, finalize_data, finalize_hint)); 50} 51 52static void finalizerWithFailedJSCallback(node_api_nogc_env nogc_env, 53 void* finalize_data, 54 void* finalize_hint) { 55 // Intentionally cast to a napi_env to test the fatal failure. 56 napi_env env = (napi_env)nogc_env; 57 napi_value obj; 58 FinalizerData* data = (FinalizerData*)finalize_data; 59 ++data->finalize_count; 60 NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &obj)); 61} 62 63static napi_value addFinalizer(napi_env env, napi_callback_info info) { 64 size_t argc = 1; 65 napi_value argv[1] = {0}; 66 FinalizerData* data; 67 68 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); 69 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data)); 70 NODE_API_CALL(env, 71 napi_add_finalizer( 72 env, argv[0], data, finalizerOnlyCallback, NULL, NULL)); 73 return NULL; 74} 75 76// This finalizer is going to call JavaScript from finalizer and succeed. 77static napi_value addFinalizerWithJS(napi_env env, napi_callback_info info) { 78 size_t argc = 2; 79 napi_value argv[2] = {0}; 80 napi_valuetype arg_type; 81 FinalizerData* data; 82 83 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); 84 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data)); 85 NODE_API_CALL(env, napi_typeof(env, argv[1], &arg_type)); 86 NODE_API_ASSERT( 87 env, arg_type == napi_function, "Expected function as the second arg"); 88 NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &data->js_func)); 89 NODE_API_CALL(env, 90 napi_add_finalizer( 91 env, argv[0], data, finalizerWithJSCallback, NULL, NULL)); 92 return NULL; 93} 94 95// This finalizer is going to call JavaScript from finalizer and fail. 96static napi_value addFinalizerFailOnJS(napi_env env, napi_callback_info info) { 97 size_t argc = 1; 98 napi_value argv[1] = {0}; 99 FinalizerData* data; 100 101 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); 102 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data)); 103 NODE_API_CALL( 104 env, 105 napi_add_finalizer( 106 env, argv[0], data, finalizerWithFailedJSCallback, NULL, NULL)); 107 return NULL; 108} 109 110static napi_value getFinalizerCallCount(napi_env env, napi_callback_info info) { 111 size_t argc = 1; 112 napi_value argv[1]; 113 FinalizerData* data; 114 napi_value result; 115 116 NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); 117 NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data)); 118 NODE_API_CALL(env, napi_create_int32(env, data->finalize_count, &result)); 119 return result; 120} 121 122static void finalizeData(napi_env env, void* data, void* hint) { 123 free(data); 124} 125 126EXTERN_C_START 127napi_value Init(napi_env env, napi_value exports) { 128 FinalizerData* data = (FinalizerData*)malloc(sizeof(FinalizerData)); 129 NODE_API_ASSERT(env, data != NULL, "Failed to allocate memory"); 130 memset(data, 0, sizeof(FinalizerData)); 131 NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, NULL)); 132 napi_property_descriptor descriptors[] = { 133 DECLARE_NODE_API_PROPERTY("addFinalizer", addFinalizer), 134 DECLARE_NODE_API_PROPERTY("addFinalizerWithJS", addFinalizerWithJS), 135 DECLARE_NODE_API_PROPERTY("addFinalizerFailOnJS", addFinalizerFailOnJS), 136 DECLARE_NODE_API_PROPERTY("getFinalizerCallCount", 137 getFinalizerCallCount)}; 138 139 NODE_API_CALL( 140 env, 141 napi_define_properties(env, 142 exports, 143 sizeof(descriptors) / sizeof(*descriptors), 144 descriptors)); 145 146 return exports; 147} 148EXTERN_C_END 149