11cb0ef41Sopenharmony_ci#include <js_native_api.h>
21cb0ef41Sopenharmony_ci#include <stdint.h>
31cb0ef41Sopenharmony_ci#include <stdio.h>
41cb0ef41Sopenharmony_ci#include <stdlib.h>
51cb0ef41Sopenharmony_ci#include <string.h>
61cb0ef41Sopenharmony_ci#include "../common.h"
71cb0ef41Sopenharmony_ci#include "../entry_point.h"
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_citypedef struct {
101cb0ef41Sopenharmony_ci  int32_t finalize_count;
111cb0ef41Sopenharmony_ci  napi_ref js_func;
121cb0ef41Sopenharmony_ci} FinalizerData;
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_cistatic void finalizerOnlyCallback(node_api_nogc_env env,
151cb0ef41Sopenharmony_ci                                  void* finalize_data,
161cb0ef41Sopenharmony_ci                                  void* finalize_hint) {
171cb0ef41Sopenharmony_ci  FinalizerData* data = (FinalizerData*)finalize_data;
181cb0ef41Sopenharmony_ci  int32_t count = ++data->finalize_count;
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ci  // It is safe to access instance data
211cb0ef41Sopenharmony_ci  NODE_API_NOGC_CALL_RETURN_VOID(env,
221cb0ef41Sopenharmony_ci                                 napi_get_instance_data(env, (void**)&data));
231cb0ef41Sopenharmony_ci  NODE_API_NOGC_ASSERT_RETURN_VOID(count = data->finalize_count,
241cb0ef41Sopenharmony_ci                                   "Expected to be the same FinalizerData");
251cb0ef41Sopenharmony_ci}
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_cistatic void finalizerCallingJSCallback(napi_env env,
281cb0ef41Sopenharmony_ci                                       void* finalize_data,
291cb0ef41Sopenharmony_ci                                       void* finalize_hint) {
301cb0ef41Sopenharmony_ci  napi_value js_func, undefined;
311cb0ef41Sopenharmony_ci  FinalizerData* data = (FinalizerData*)finalize_data;
321cb0ef41Sopenharmony_ci  NODE_API_CALL_RETURN_VOID(
331cb0ef41Sopenharmony_ci      env, napi_get_reference_value(env, data->js_func, &js_func));
341cb0ef41Sopenharmony_ci  NODE_API_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined));
351cb0ef41Sopenharmony_ci  NODE_API_CALL_RETURN_VOID(
361cb0ef41Sopenharmony_ci      env, napi_call_function(env, undefined, js_func, 0, NULL, NULL));
371cb0ef41Sopenharmony_ci  NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, data->js_func));
381cb0ef41Sopenharmony_ci  data->js_func = NULL;
391cb0ef41Sopenharmony_ci  ++data->finalize_count;
401cb0ef41Sopenharmony_ci}
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ci// Schedule async finalizer to run JavaScript-touching code.
431cb0ef41Sopenharmony_cistatic void finalizerWithJSCallback(node_api_nogc_env env,
441cb0ef41Sopenharmony_ci                                    void* finalize_data,
451cb0ef41Sopenharmony_ci                                    void* finalize_hint) {
461cb0ef41Sopenharmony_ci  NODE_API_NOGC_CALL_RETURN_VOID(
471cb0ef41Sopenharmony_ci      env,
481cb0ef41Sopenharmony_ci      node_api_post_finalizer(
491cb0ef41Sopenharmony_ci          env, finalizerCallingJSCallback, finalize_data, finalize_hint));
501cb0ef41Sopenharmony_ci}
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_cistatic void finalizerWithFailedJSCallback(node_api_nogc_env nogc_env,
531cb0ef41Sopenharmony_ci                                          void* finalize_data,
541cb0ef41Sopenharmony_ci                                          void* finalize_hint) {
551cb0ef41Sopenharmony_ci  // Intentionally cast to a napi_env to test the fatal failure.
561cb0ef41Sopenharmony_ci  napi_env env = (napi_env)nogc_env;
571cb0ef41Sopenharmony_ci  napi_value obj;
581cb0ef41Sopenharmony_ci  FinalizerData* data = (FinalizerData*)finalize_data;
591cb0ef41Sopenharmony_ci  ++data->finalize_count;
601cb0ef41Sopenharmony_ci  NODE_API_CALL_RETURN_VOID(env, napi_create_object(env, &obj));
611cb0ef41Sopenharmony_ci}
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_cistatic napi_value addFinalizer(napi_env env, napi_callback_info info) {
641cb0ef41Sopenharmony_ci  size_t argc = 1;
651cb0ef41Sopenharmony_ci  napi_value argv[1] = {0};
661cb0ef41Sopenharmony_ci  FinalizerData* data;
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
691cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
701cb0ef41Sopenharmony_ci  NODE_API_CALL(env,
711cb0ef41Sopenharmony_ci                napi_add_finalizer(
721cb0ef41Sopenharmony_ci                    env, argv[0], data, finalizerOnlyCallback, NULL, NULL));
731cb0ef41Sopenharmony_ci  return NULL;
741cb0ef41Sopenharmony_ci}
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci// This finalizer is going to call JavaScript from finalizer and succeed.
771cb0ef41Sopenharmony_cistatic napi_value addFinalizerWithJS(napi_env env, napi_callback_info info) {
781cb0ef41Sopenharmony_ci  size_t argc = 2;
791cb0ef41Sopenharmony_ci  napi_value argv[2] = {0};
801cb0ef41Sopenharmony_ci  napi_valuetype arg_type;
811cb0ef41Sopenharmony_ci  FinalizerData* data;
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
841cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
851cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_typeof(env, argv[1], &arg_type));
861cb0ef41Sopenharmony_ci  NODE_API_ASSERT(
871cb0ef41Sopenharmony_ci      env, arg_type == napi_function, "Expected function as the second arg");
881cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &data->js_func));
891cb0ef41Sopenharmony_ci  NODE_API_CALL(env,
901cb0ef41Sopenharmony_ci                napi_add_finalizer(
911cb0ef41Sopenharmony_ci                    env, argv[0], data, finalizerWithJSCallback, NULL, NULL));
921cb0ef41Sopenharmony_ci  return NULL;
931cb0ef41Sopenharmony_ci}
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci// This finalizer is going to call JavaScript from finalizer and fail.
961cb0ef41Sopenharmony_cistatic napi_value addFinalizerFailOnJS(napi_env env, napi_callback_info info) {
971cb0ef41Sopenharmony_ci  size_t argc = 1;
981cb0ef41Sopenharmony_ci  napi_value argv[1] = {0};
991cb0ef41Sopenharmony_ci  FinalizerData* data;
1001cb0ef41Sopenharmony_ci
1011cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
1021cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
1031cb0ef41Sopenharmony_ci  NODE_API_CALL(
1041cb0ef41Sopenharmony_ci      env,
1051cb0ef41Sopenharmony_ci      napi_add_finalizer(
1061cb0ef41Sopenharmony_ci          env, argv[0], data, finalizerWithFailedJSCallback, NULL, NULL));
1071cb0ef41Sopenharmony_ci  return NULL;
1081cb0ef41Sopenharmony_ci}
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_cistatic napi_value getFinalizerCallCount(napi_env env, napi_callback_info info) {
1111cb0ef41Sopenharmony_ci  size_t argc = 1;
1121cb0ef41Sopenharmony_ci  napi_value argv[1];
1131cb0ef41Sopenharmony_ci  FinalizerData* data;
1141cb0ef41Sopenharmony_ci  napi_value result;
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
1171cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_get_instance_data(env, (void**)&data));
1181cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_create_int32(env, data->finalize_count, &result));
1191cb0ef41Sopenharmony_ci  return result;
1201cb0ef41Sopenharmony_ci}
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_cistatic void finalizeData(napi_env env, void* data, void* hint) {
1231cb0ef41Sopenharmony_ci  free(data);
1241cb0ef41Sopenharmony_ci}
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ciEXTERN_C_START
1271cb0ef41Sopenharmony_cinapi_value Init(napi_env env, napi_value exports) {
1281cb0ef41Sopenharmony_ci  FinalizerData* data = (FinalizerData*)malloc(sizeof(FinalizerData));
1291cb0ef41Sopenharmony_ci  NODE_API_ASSERT(env, data != NULL, "Failed to allocate memory");
1301cb0ef41Sopenharmony_ci  memset(data, 0, sizeof(FinalizerData));
1311cb0ef41Sopenharmony_ci  NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, NULL));
1321cb0ef41Sopenharmony_ci  napi_property_descriptor descriptors[] = {
1331cb0ef41Sopenharmony_ci      DECLARE_NODE_API_PROPERTY("addFinalizer", addFinalizer),
1341cb0ef41Sopenharmony_ci      DECLARE_NODE_API_PROPERTY("addFinalizerWithJS", addFinalizerWithJS),
1351cb0ef41Sopenharmony_ci      DECLARE_NODE_API_PROPERTY("addFinalizerFailOnJS", addFinalizerFailOnJS),
1361cb0ef41Sopenharmony_ci      DECLARE_NODE_API_PROPERTY("getFinalizerCallCount",
1371cb0ef41Sopenharmony_ci                                getFinalizerCallCount)};
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  NODE_API_CALL(
1401cb0ef41Sopenharmony_ci      env,
1411cb0ef41Sopenharmony_ci      napi_define_properties(env,
1421cb0ef41Sopenharmony_ci                             exports,
1431cb0ef41Sopenharmony_ci                             sizeof(descriptors) / sizeof(*descriptors),
1441cb0ef41Sopenharmony_ci                             descriptors));
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci  return exports;
1471cb0ef41Sopenharmony_ci}
1481cb0ef41Sopenharmony_ciEXTERN_C_END
149