1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include <functional> 17#include <vector> 18#include "native_parameters_js.h" 19#include "uv.h" 20 21using namespace OHOS::system; 22static constexpr int ARGC_NUMBER = 2; 23static constexpr int BUF_LENGTH = 128; 24 25static napi_ref g_paramWatchRef; 26 27using ParamAsyncContext = struct ParamAsyncContext { 28 napi_env env = nullptr; 29 napi_async_work work = nullptr; 30 31 char key[BUF_LENGTH] = { 0 }; 32 size_t keyLen = 0; 33 char value[BUF_LENGTH] = { 0 }; 34 size_t valueLen = 0; 35 int32_t timeout = 0; 36 napi_deferred deferred = nullptr; 37 napi_ref callbackRef = nullptr; 38 39 int status = -1; 40 std::string getValue; 41}; 42 43using ParamWatcher = struct ParamWatcher { 44 napi_env env = nullptr; 45 napi_ref thisVarRef = nullptr; 46 char keyPrefix[BUF_LENGTH] = { 0 }; 47 size_t keyLen = 0; 48 bool notifySwitch = false; 49 bool startWatch = false; 50 std::mutex mutex {}; 51 napi_ref currCallbackRef = nullptr; 52 std::map<uint32_t, napi_ref> callbackReferences {}; 53}; 54 55using ParamWatcherWork = struct ParamWatcherWork { 56 napi_async_work work = nullptr; 57 ParamWatcher *watcher = nullptr; 58 bool startWatch = false; 59}; 60 61using ParamChangeValue = struct ParamChangeValue { 62 uv_work_t work; 63 ParamWatcher *watcher; 64 std::string key; 65 std::string value; 66}; 67 68using ParamAsyncContextPtr = ParamAsyncContext *; 69using ParamWatcherPtr = ParamWatcher *; 70 71static napi_value NapiGetNull(napi_env env) 72{ 73 napi_value result = 0; 74 napi_get_null(env, &result); 75 return result; 76} 77 78static napi_value GetNapiValue(napi_env env, int val) 79{ 80 napi_value result = nullptr; 81 napi_create_int32(env, val, &result); 82 return result; 83} 84 85static int GetParamValue(napi_env env, napi_value arg, napi_valuetype valueType, char *buffer, size_t &buffLen) 86{ 87 napi_valuetype type = napi_null; 88 napi_typeof(env, arg, &type); 89 PARAM_JS_CHECK(type == valueType, return -1, "Invalid type %d %d", type, valueType); 90 napi_status status = napi_ok; 91 if (valueType == napi_string) { 92 status = napi_get_value_string_utf8(env, arg, buffer, buffLen, &buffLen); 93 } else if (valueType == napi_number) { 94 status = napi_get_value_int32(env, arg, reinterpret_cast<int *>(buffer)); 95 } 96 return status; 97} 98 99static void WaitCallbackWork(napi_env env, ParamAsyncContextPtr asyncContext) 100{ 101 napi_value resource = nullptr; 102 napi_create_string_utf8(env, "JSStartupGet", NAPI_AUTO_LENGTH, &resource); 103 napi_create_async_work( 104 env, nullptr, resource, 105 [](napi_env env, void *data) { 106 ParamAsyncContext *asyncContext = reinterpret_cast<ParamAsyncContext *>(data); 107 asyncContext->status = WaitParameter(asyncContext->key, asyncContext->value, asyncContext->timeout); 108 PARAM_JS_LOGV("JSApp Wait status: %d, key: %s", asyncContext->status, asyncContext->key); 109 }, 110 [](napi_env env, napi_status status, void *data) { 111 ParamAsyncContext *asyncContext = reinterpret_cast<ParamAsyncContext *>(data); 112 napi_value result[ARGC_NUMBER] = { 0 }; 113 napi_value message = nullptr; 114 napi_create_object(env, &result[0]); 115 napi_create_int32(env, asyncContext->status, &message); 116 napi_set_named_property(env, result[0], "code", message); 117 napi_get_undefined(env, &result[1]); // only one param 118 119 PARAM_JS_LOGV("JSApp Wait status: %d, key: %s ", asyncContext->status, asyncContext->key); 120 if (asyncContext->deferred) { 121 if (asyncContext->status == 0) { 122 napi_resolve_deferred(env, asyncContext->deferred, result[1]); 123 } else { 124 napi_reject_deferred(env, asyncContext->deferred, result[0]); 125 } 126 } else { 127 napi_value callbackRef = nullptr; 128 napi_value callResult = nullptr; 129 napi_status status = napi_get_reference_value(env, asyncContext->callbackRef, &callbackRef); 130 PARAM_JS_CHECK(status == 0 && callbackRef != nullptr, return, "Failed to get reference "); 131 napi_value undefined; 132 napi_get_undefined(env, &undefined); 133 napi_call_function(env, undefined, callbackRef, ARGC_NUMBER, result, &callResult); 134 napi_delete_reference(env, asyncContext->callbackRef); 135 } 136 napi_delete_async_work(env, asyncContext->work); 137 delete asyncContext; 138 }, 139 reinterpret_cast<void *>(asyncContext), &asyncContext->work); 140 napi_queue_async_work(env, asyncContext->work); 141} 142 143napi_value ParamWait(napi_env env, napi_callback_info info) 144{ 145 constexpr int PARAM_TIMEOUT_INDEX = 2; 146 constexpr int ARGC_THREE_NUMBER = 3; 147 size_t argc = ARGC_THREE_NUMBER + 1; 148 napi_value argv[ARGC_THREE_NUMBER + 1]; 149 napi_value thisVar = nullptr; 150 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr); 151 PARAM_JS_CHECK(status == napi_ok, return GetNapiValue(env, status), "Failed to get cb info"); 152 PARAM_JS_CHECK(argc >= ARGC_THREE_NUMBER, return GetNapiValue(env, status), "Failed to get argc"); 153 154 ParamAsyncContextPtr asyncContext = new ParamAsyncContext(); 155 PARAM_JS_CHECK(asyncContext != nullptr, return GetNapiValue(env, status), "Failed to create context"); 156 asyncContext->env = env; 157 158 // get param key 159 asyncContext->keyLen = BUF_LENGTH - 1; 160 asyncContext->valueLen = BUF_LENGTH - 1; 161 size_t len = sizeof(asyncContext->timeout); 162 int ret = GetParamValue(env, argv[0], napi_string, asyncContext->key, asyncContext->keyLen); 163 PARAM_JS_CHECK(ret == 0, delete asyncContext; 164 return GetNapiValue(env, ret), "Invalid param for wait"); 165 ret = GetParamValue(env, argv[1], napi_string, asyncContext->value, asyncContext->valueLen); 166 PARAM_JS_CHECK(ret == 0, delete asyncContext; 167 return GetNapiValue(env, ret), "Invalid param for wait"); 168 ret = GetParamValue(env, argv[PARAM_TIMEOUT_INDEX], napi_number, (char *)&asyncContext->timeout, len); 169 PARAM_JS_CHECK(ret == 0, delete asyncContext; 170 return GetNapiValue(env, ret), "Invalid param for wait"); 171 if (argc > ARGC_THREE_NUMBER) { 172 napi_valuetype valueType = napi_null; 173 napi_typeof(env, argv[ARGC_THREE_NUMBER], &valueType); 174 PARAM_JS_CHECK(valueType == napi_function, delete asyncContext; 175 return GetNapiValue(env, ret), "Invalid param for wait callbackRef"); 176 napi_create_reference(env, argv[ARGC_THREE_NUMBER], 1, &asyncContext->callbackRef); 177 } 178 PARAM_JS_LOGV("JSApp Wait key: %s, value: %s timeout %d.", 179 asyncContext->key, asyncContext->value, asyncContext->timeout); 180 181 napi_value result = nullptr; 182 if (asyncContext->callbackRef == nullptr) { 183 napi_create_promise(env, &asyncContext->deferred, &result); 184 } else { 185 result = GetNapiValue(env, 0); 186 } 187 WaitCallbackWork(env, asyncContext); 188 return result; 189} 190 191static bool GetFristRefence(ParamWatcherPtr watcher, uint32_t &next) 192{ 193 std::lock_guard<std::mutex> lock(watcher->mutex); 194 auto iter = watcher->callbackReferences.begin(); 195 if (iter != watcher->callbackReferences.end()) { 196 next = watcher->callbackReferences.begin()->first; 197 return true; 198 } 199 return false; 200} 201 202static napi_ref GetWatcherReference(ParamWatcherPtr watcher, uint32_t next) 203{ 204 std::lock_guard<std::mutex> lock(watcher->mutex); 205 auto iter = watcher->callbackReferences.find(next); 206 if (iter != watcher->callbackReferences.end()) { 207 return iter->second; 208 } 209 return nullptr; 210} 211 212static uint32_t GetNextRefence(ParamWatcherPtr watcher, uint32_t &next) 213{ 214 std::lock_guard<std::mutex> lock(watcher->mutex); 215 auto iter = watcher->callbackReferences.upper_bound(next); 216 if (iter == watcher->callbackReferences.end()) { 217 return false; 218 } 219 next = iter->first; 220 return true; 221} 222 223static void AddWatcherCallback(ParamWatcherPtr watcher, napi_ref callbackRef) 224{ 225 static uint32_t watcherId = 0; 226 std::lock_guard<std::mutex> lock(watcher->mutex); 227 watcherId++; 228 watcher->callbackReferences[watcherId] = callbackRef; 229 PARAM_JS_LOGV("JSApp watcher add watcher callback %s %u.", 230 watcher->keyPrefix, watcherId); 231} 232 233static void DelWatcherCallback(ParamWatcherPtr watcher, uint32_t next) 234{ 235 PARAM_JS_LOGV("JSApp watcher key %s delete callback %u", watcher->keyPrefix, next); 236 std::lock_guard<std::mutex> lock(watcher->mutex); 237 watcher->callbackReferences.erase(next); 238} 239 240static void DelCallbackRef(napi_env env, ParamWatcherPtr watcher, napi_ref callbackRef, uint32_t id) 241{ 242 std::lock_guard<std::mutex> lock(watcher->mutex); 243 if (callbackRef == watcher->currCallbackRef) { 244 PARAM_JS_LOGV("JSApp watcher key %s has been callbacked %u.", 245 watcher->keyPrefix, id); 246 watcher->currCallbackRef = nullptr; 247 } else { 248 napi_delete_reference(env, callbackRef); 249 } 250 watcher->callbackReferences.erase(id); 251} 252 253static void DelCallback(napi_env env, napi_value callback, ParamWatcherPtr watcher) 254{ 255 bool isEquals = false; 256 uint32_t next = 0; 257 bool ret = GetFristRefence(watcher, next); 258 while (ret) { 259 napi_ref callbackRef = GetWatcherReference(watcher, next); 260 if (callbackRef == nullptr) { 261 PARAM_JS_LOGV("JSApp watcher key %s callbackRef has been deleted %d.", watcher->keyPrefix, next); 262 DelWatcherCallback(watcher, next); 263 } else if (callback != nullptr) { 264 napi_value handler = nullptr; 265 napi_get_reference_value(env, callbackRef, &handler); 266 napi_strict_equals(env, handler, callback, &isEquals); 267 if (isEquals) { 268 DelCallbackRef(env, watcher, callbackRef, next); 269 break; 270 } 271 } else { 272 DelCallbackRef(env, watcher, callbackRef, next); 273 } 274 ret = GetNextRefence(watcher, next); 275 } 276} 277 278static bool CheckCallbackEqual(napi_env env, napi_value callback, ParamWatcherPtr watcher) 279{ 280 bool isEquals = false; 281 uint32_t next = 0; 282 bool ret = GetFristRefence(watcher, next); 283 while (ret) { 284 napi_ref callbackRef = GetWatcherReference(watcher, next); 285 if (callbackRef != nullptr) { 286 napi_value handler = nullptr; 287 napi_get_reference_value(env, callbackRef, &handler); 288 napi_strict_equals(env, handler, callback, &isEquals); 289 if (isEquals) { 290 return true; 291 } 292 } 293 ret = GetNextRefence(watcher, next); 294 } 295 return false; 296} 297 298static napi_value ParamWatchConstructor(napi_env env, napi_callback_info info) 299{ 300 size_t argc = 1; 301 napi_value argv[1]; 302 napi_value thisVar = nullptr; 303 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr)); 304 ParamWatcherPtr watcher = new ParamWatcher(); 305 PARAM_JS_CHECK(watcher != nullptr, return NapiGetNull(env), "Failed to create param watcher"); 306 napi_status status = napi_create_reference(env, thisVar, 1, &watcher->thisVarRef); 307 PARAM_JS_CHECK(status == 0, delete watcher; 308 return NapiGetNull(env), "Failed to create reference %d", status); 309 310 napi_wrap( 311 env, thisVar, watcher, 312 [](napi_env env, void *data, void *hint) { 313 ParamWatcherPtr watcher = static_cast<ParamWatcherPtr>(data); 314 if (watcher) { 315 DelCallback(env, nullptr, watcher); 316 WatchParameter(watcher->keyPrefix, nullptr, nullptr); 317 delete watcher; 318 watcher = nullptr; 319 } 320 }, 321 nullptr, nullptr); 322 return thisVar; 323} 324 325napi_value GetWatcher(napi_env env, napi_callback_info info) 326{ 327 size_t argc = 1; 328 napi_value argv[1]; 329 napi_value thisVar = nullptr; 330 void *data = nullptr; 331 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data)); 332 333 napi_value obj = thisVar; 334 ParamWatcherPtr watcher = nullptr; 335 napi_unwrap(env, thisVar, (void **)&watcher); 336 if (watcher == nullptr) { // check if watcher exist 337 napi_value constructor = nullptr; 338 int status = napi_get_reference_value(env, g_paramWatchRef, &constructor); 339 PARAM_JS_CHECK(status == 0, return NapiGetNull(env), "Failed to get reference"); 340 status = napi_new_instance(env, constructor, 0, nullptr, &obj); 341 PARAM_JS_CHECK(status == 0, return NapiGetNull(env), "Failed to create instance for watcher"); 342 napi_unwrap(env, obj, (void **)&watcher); 343 } 344 if (watcher != nullptr) { 345 watcher->keyLen = BUF_LENGTH; 346 int ret = GetParamValue(env, argv[0], napi_string, watcher->keyPrefix, watcher->keyLen); 347 PARAM_JS_CHECK(ret == 0, return NapiGetNull(env), "Failed to get key prefix"); 348 PARAM_JS_LOGV("JSApp watcher keyPrefix = %s ", watcher->keyPrefix); 349 ret = WatchParameter(watcher->keyPrefix, nullptr, watcher); 350 PARAM_JS_CHECK(ret == 0, return NapiGetNull(env), "Failed to get watcher ret %d", ret); 351 } 352 return obj; 353} 354 355static ParamWatcherPtr GetWatcherInfo(napi_env env, napi_callback_info info, napi_value *callback) 356{ 357 size_t argc = ARGC_NUMBER; 358 napi_value argv[ARGC_NUMBER]; 359 napi_value thisVar = nullptr; 360 void *data = nullptr; 361 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); 362 363 size_t typeLen = BUF_LENGTH - 1; 364 std::vector<char> eventType(typeLen, 0); 365 int ret = GetParamValue(env, argv[0], napi_string, eventType.data(), typeLen); 366 PARAM_JS_CHECK(ret == 0, return nullptr, "Failed to get event type"); 367 if (strcmp(eventType.data(), "valueChange") != 0) { 368 return nullptr; 369 } 370 // argv[1]:callbackRef 371 if (argc > 1) { 372 napi_valuetype valuetype; 373 napi_status status = napi_typeof(env, argv[1], &valuetype); 374 PARAM_JS_CHECK(status == 0, return nullptr, "Failed to get type"); 375 PARAM_JS_CHECK(valuetype == napi_function, return nullptr, "Invalid type %d", valuetype); 376 *callback = argv[1]; 377 } 378 ParamWatcherPtr watcher = nullptr; 379 napi_unwrap(env, thisVar, (void **)&watcher); 380 return watcher; 381} 382 383static void NotifyValueChange(ParamWatcherPtr watcher, uint32_t id, napi_value thisVar, const napi_value result[]) 384{ 385 napi_ref callbackRef = GetWatcherReference(watcher, id); 386 PARAM_JS_CHECK(callbackRef != nullptr, return, 387 "Failed to get callback for %s %d", watcher->keyPrefix, id); 388 napi_value callbackFunc = nullptr; 389 napi_status status = napi_get_reference_value(watcher->env, callbackRef, &callbackFunc); 390 PARAM_JS_CHECK(status == 0 && callbackFunc != nullptr, return, 391 "Failed to get callback for %s %d", watcher->keyPrefix, id); 392 { 393 std::lock_guard<std::mutex> lock(watcher->mutex); 394 watcher->currCallbackRef = callbackRef; 395 } 396 397 napi_value callbackResult = nullptr; 398 napi_call_function(watcher->env, thisVar, callbackFunc, ARGC_NUMBER, result, &callbackResult); 399 400 { 401 std::lock_guard<std::mutex> lock(watcher->mutex); 402 if (watcher->currCallbackRef == nullptr) { 403 PARAM_JS_LOGV("JSApp watcher notify key %s callback deleted watcherId %u", 404 watcher->keyPrefix, id); 405 napi_delete_reference(watcher->env, callbackRef); 406 } 407 watcher->currCallbackRef = nullptr; 408 } 409} 410 411static void HandleParameterChange(ParamWatcherPtr watcher, const char *key, const char *value) 412{ 413 napi_handle_scope scope = nullptr; 414 napi_status status = napi_open_handle_scope(watcher->env, &scope); 415 PARAM_JS_CHECK(status == 0, return, "Failed to get reference "); 416 napi_value result[ARGC_NUMBER] = { 0 }; 417 napi_create_string_utf8(watcher->env, key, strlen(key), &result[0]); 418 napi_create_string_utf8(watcher->env, value, strlen(value), &result[1]); 419 napi_value thisVar = nullptr; 420 status = napi_get_reference_value(watcher->env, watcher->thisVarRef, &thisVar); 421 PARAM_JS_CHECK(status == 0 && thisVar != nullptr, napi_close_handle_scope(watcher->env, scope); 422 return, "Failed to get reference "); 423 uint32_t next = 0; 424 bool ret = GetFristRefence(watcher, next); 425 while (ret) { 426 NotifyValueChange(watcher, next, thisVar, result); 427 ret = GetNextRefence(watcher, next); 428 } 429 napi_close_handle_scope(watcher->env, scope); 430 PARAM_JS_LOGV("JSApp watcher ProcessParamChange %s finish", key); 431} 432 433static void ProcessParamChange(const char *key, const char *value, void *context) 434{ 435 ParamWatcherPtr watcher = static_cast<ParamWatcherPtr>(context); 436 PARAM_JS_CHECK(watcher != nullptr && watcher->env != nullptr, return, "Invalid param"); 437 PARAM_JS_CHECK(watcher->callbackReferences.size() > 0, return, "No callback for watcher"); 438 439 uv_loop_s *loop = nullptr; 440 napi_get_uv_event_loop(watcher->env, &loop); 441 PARAM_JS_CHECK(loop != nullptr, return, "Failed to get loop for %s", watcher->keyPrefix); 442 ParamChangeValue *change = new (std::nothrow) ParamChangeValue; 443 PARAM_JS_CHECK(change != nullptr, return, "Failed to get change for %s", watcher->keyPrefix); 444 change->work.data = reinterpret_cast<void *>(change); 445 change->watcher = watcher; 446 change->key = std::string(key); 447 change->value = std::string(value); 448 int ret = uv_queue_work(loop, &change->work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int result) { 449 ParamChangeValue *change = reinterpret_cast<ParamChangeValue *>(work->data); 450 HandleParameterChange(change->watcher, change->key.c_str(), change->value.c_str()); 451 delete change; 452 change = nullptr; 453 }); 454 PARAM_JS_CHECK(ret == 0, delete change; 455 return, "Failed to start work for %s", watcher->keyPrefix); 456} 457 458static void WatchCallbackWork(napi_env env, ParamWatcherPtr watcher) 459{ 460 PARAM_JS_LOGV("JSApp WatchCallbackWork key: %s", watcher->keyPrefix); 461 ParamWatcherWork *worker = new ParamWatcherWork(); 462 PARAM_JS_CHECK(worker != nullptr, return, "Failed to create worker "); 463 worker->watcher = watcher; 464 worker->work = nullptr; 465 worker->startWatch = watcher->startWatch; 466 467 napi_value resource = nullptr; 468 napi_create_string_utf8(env, "JSStartupWatch", NAPI_AUTO_LENGTH, &resource); 469 napi_create_async_work(env, nullptr, resource, 470 [](napi_env env, void *data) { 471 ParamWatcherWork *worker = reinterpret_cast<ParamWatcherWork *>(data); 472 PARAM_JS_CHECK(worker != nullptr && worker->watcher != nullptr, return, "Invalid worker "); 473 int status = WatchParameter(worker->watcher->keyPrefix, 474 worker->startWatch ? ProcessParamChange : nullptr, worker->watcher); 475 PARAM_JS_LOGV("JSApp WatchCallbackWork %s status: %d, key: %s", 476 worker->startWatch ? "on" : "off", status, worker->watcher->keyPrefix); 477 }, 478 [](napi_env env, napi_status status, void *data) { 479 ParamWatcherWork *worker = reinterpret_cast<ParamWatcherWork *>(data); 480 PARAM_JS_LOGV("JSApp WatchCallbackWork delete %s key: %s", 481 worker->startWatch ? "on" : "off", worker->watcher->keyPrefix); 482 napi_delete_async_work(env, worker->work); 483 delete worker; 484 }, 485 reinterpret_cast<void *>(worker), &worker->work); 486 napi_queue_async_work(env, worker->work); 487} 488 489static napi_value SwithWatchOn(napi_env env, napi_callback_info info) 490{ 491 napi_value callback = nullptr; 492 ParamWatcherPtr watcher = GetWatcherInfo(env, info, &callback); 493 PARAM_JS_CHECK(watcher != nullptr, return GetNapiValue(env, -1), "Failed to get watcher switch param"); 494 495 if (CheckCallbackEqual(env, callback, watcher)) { 496 PARAM_JS_LOGE("JSApp watcher repeater switch on %s", watcher->keyPrefix); 497 return 0; 498 } 499 PARAM_JS_LOGV("JSApp watcher on %s", watcher->keyPrefix); 500 // save callback 501 napi_ref callbackRef; 502 napi_create_reference(env, callback, 1, &callbackRef); 503 AddWatcherCallback(watcher, callbackRef); 504 watcher->env = env; 505 { 506 std::lock_guard<std::mutex> lock(watcher->mutex); 507 if (watcher->startWatch) { 508 return GetNapiValue(env, 0); 509 } 510 watcher->startWatch = true; 511 } 512 513 PARAM_JS_LOGV("JSApp watcher add %s", watcher->keyPrefix); 514 WatchCallbackWork(env, watcher); 515 PARAM_JS_LOGV("JSApp watcher on %s finish", watcher->keyPrefix); 516 return GetNapiValue(env, 0); 517} 518 519static napi_value SwithWatchOff(napi_env env, napi_callback_info info) 520{ 521 napi_value callback = nullptr; 522 ParamWatcherPtr watcher = GetWatcherInfo(env, info, &callback); 523 PARAM_JS_CHECK(watcher != nullptr, return GetNapiValue(env, -1), "Failed to get watcher"); 524 PARAM_JS_LOGV("JSApp watcher off %s", watcher->keyPrefix); 525 DelCallback(env, callback, watcher); 526 { 527 std::lock_guard<std::mutex> lock(watcher->mutex); 528 if (watcher->callbackReferences.size() == 0) { 529 watcher->startWatch = false; 530 WatchCallbackWork(env, watcher); 531 } 532 } 533 return GetNapiValue(env, 0); 534} 535 536napi_value RegisterWatcher(napi_env env, napi_value exports) 537{ 538 napi_property_descriptor properties[] = { 539 DECLARE_NAPI_FUNCTION("on", SwithWatchOn), 540 DECLARE_NAPI_FUNCTION("off", SwithWatchOff), 541 }; 542 543 napi_value result = nullptr; 544 NAPI_CALL(env, 545 napi_define_class(env, 546 "paramWatcher", 547 NAPI_AUTO_LENGTH, 548 ParamWatchConstructor, 549 nullptr, 550 sizeof(properties) / sizeof(*properties), 551 properties, 552 &result)); 553 napi_set_named_property(env, exports, "paramWatcher", result); 554 napi_create_reference(env, result, 1, &g_paramWatchRef); 555 return exports; 556} 557