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