• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 #ifndef LOG_TAG
16 #define LOG_TAG "bt_napi_async_callback"
17 #endif
18 
19 #include "napi_async_callback.h"
20 #include "bluetooth_errorcode.h"
21 #include "bluetooth_log.h"
22 
23 namespace OHOS {
24 namespace Bluetooth {
CallFunction(int errCode,const std::shared_ptr<NapiNativeObject> & object)25 void NapiAsyncCallback::CallFunction(int errCode, const std::shared_ptr<NapiNativeObject> &object)
26 {
27     if (callback == nullptr && promise == nullptr) {
28         HILOGE("callback & promise is nullptr");
29         return;
30     }
31     if (object == nullptr) {
32         HILOGE("napi native object is nullptr");
33         return;
34     }
35 
36     if (callback) {
37         callback->CallFunction(errCode, object);
38         return;
39     }
40     if (promise) {
41         promise->ResolveOrReject(errCode, object);
42     }
43 }
44 
GetRet(void)45 napi_value NapiAsyncCallback::GetRet(void)
46 {
47     if (promise) {
48         return promise->GetPromise();
49     }
50     return NapiGetUndefinedRet(env);
51 }
52 
53 /*************************** env_cleanup_hook ********************************/
NapiCallbackEnvCleanupHook(void * data)54 void NapiCallbackEnvCleanupHook(void *data)
55 {
56     CHECK_AND_RETURN_LOG(data, "data is nullptr");
57 
58     NapiCallback *callback = static_cast<NapiCallback *>(data);
59     callback->SetNapiEnvValidity(false);
60 }
61 /*************************** env_cleanup_hook ********************************/
62 
NapiCallback(napi_env env,napi_value callback)63 NapiCallback::NapiCallback(napi_env env, napi_value callback) : env_(env)
64 {
65     // Use ID to identify NAPI callback.
66     static int idCount = 0;
67     id_ = idCount++;
68 
69     auto status = napi_create_reference(env, callback, 1, &callbackRef_);
70     if (status != napi_ok) {
71         HILOGE("napi_create_reference failed, status: %{public}d", status);
72     }
73     // Used to clean up resources when env exit
74     napi_add_env_cleanup_hook(env, NapiCallbackEnvCleanupHook, this);
75 }
~NapiCallback()76 NapiCallback::~NapiCallback()
77 {
78     if (!IsValidNapiEnv()) {
79         return;
80     }
81     auto status = napi_delete_reference(env_, callbackRef_);
82     if (status != napi_ok) {
83         HILOGE("napi_delete_reference failed, status: %{public}d", status);
84     }
85     napi_remove_env_cleanup_hook(env_, NapiCallbackEnvCleanupHook, this);
86 }
87 
88 namespace {
NapiCallFunction(napi_env env,napi_ref callbackRef,napi_value * argv,size_t argc)89 void NapiCallFunction(napi_env env, napi_ref callbackRef, napi_value *argv, size_t argc)
90 {
91     napi_value undefined = nullptr;
92     napi_value callRet = nullptr;
93     napi_value callback = nullptr;
94     auto status = napi_get_reference_value(env, callbackRef, &callback);
95     if (status != napi_ok) {
96         HILOGE("napi_get_reference_value failed, status: %{public}d", status);
97         return;
98     }
99 
100     status = napi_call_function(env, undefined, callback, argc, argv, &callRet);
101     if (status != napi_ok) {
102         HILOGE("napi_call_function failed, status: %{public}d", status);
103     }
104 
105     // Check whether the JS application triggers an exception in callback. If it is, clear it.
106     bool isExist = false;
107     status = napi_is_exception_pending(env, &isExist);
108     HILOGD("napi_is_exception_pending status: %{public}d, isExist: %{public}d", status, isExist);
109     if (isExist) {
110         HILOGI("Clear JS application's exception");
111         napi_value exception = nullptr;
112         status = napi_get_and_clear_last_exception(env, &exception);
113         HILOGD("napi_get_and_clear_last_exception status: %{public}d", status);
114     }
115 }
116 }  // namespace {}
117 
CallFunction(const std::shared_ptr<NapiNativeObject> & object)118 void NapiCallback::CallFunction(const std::shared_ptr<NapiNativeObject> &object)
119 {
120     if (!IsValidNapiEnv()) {
121         HILOGW("napi env is exit");
122         return;
123     }
124     if (object == nullptr) {
125         HILOGE("napi native object is nullptr");
126         return;
127     }
128 
129     NapiHandleScope scope(env_);
130     napi_value val = object->ToNapiValue(env_);
131     NapiCallFunction(env_, callbackRef_, &val, ARGS_SIZE_ONE);
132 }
133 
CallFunction(int errCode,const std::shared_ptr<NapiNativeObject> & object)134 void NapiCallback::CallFunction(int errCode, const std::shared_ptr<NapiNativeObject> &object)
135 {
136     if (!IsValidNapiEnv()) {
137         HILOGW("napi env is exit");
138         return;
139     }
140     if (object == nullptr) {
141         HILOGE("napi native object is nullptr");
142         return;
143     }
144 
145     NapiHandleScope scope(env_);
146     napi_value code = GetCallbackErrorValue(env_, errCode);
147     napi_value val = object->ToNapiValue(env_);
148     napi_value argv[ARGS_SIZE_TWO] = {code, val};
149     NapiCallFunction(env_, callbackRef_, argv, ARGS_SIZE_TWO);
150 }
151 
GetNapiEnv(void)152 napi_env NapiCallback::GetNapiEnv(void)
153 {
154     return env_;
155 }
156 
Equal(napi_env env,napi_value & callback) const157 bool NapiCallback::Equal(napi_env env, napi_value &callback) const
158 {
159     if (!IsValidNapiEnv()) {
160         HILOGW("napi env is exit");
161         return false;
162     }
163     if (env != env_) {
164         HILOGD("Callback is not in the same thread, not uqual");
165         return false;
166     }
167     NapiHandleScope scope(env_);
168     napi_value storedCallback = nullptr;
169     napi_get_reference_value(env_, callbackRef_, &storedCallback);
170 
171     bool isEqual = false;
172     napi_strict_equals(env_, storedCallback, callback, &isEqual);
173     return isEqual;
174 }
175 
ToLogString(void) const176 std::string NapiCallback::ToLogString(void) const
177 {
178     return "callbackId: " + std::to_string(id_);
179 }
180 
181 /*************************** env_cleanup_hook ********************************/
NapiPromiseEnvCleanupHook(void * data)182 void NapiPromiseEnvCleanupHook(void *data)
183 {
184     CHECK_AND_RETURN_LOG(data, "data is nullptr");
185 
186     NapiPromise *callback = static_cast<NapiPromise *>(data);
187     callback->SetNapiEnvValidity(false);
188 }
189 /*************************** env_cleanup_hook ********************************/
190 
NapiPromise(napi_env env)191 NapiPromise::NapiPromise(napi_env env) : env_(env)
192 {
193     auto status = napi_create_promise(env, &deferred_, &promise_);
194     if (status != napi_ok) {
195         HILOGE("napi_create_promise failed, status: %{public}d", status);
196     }
197     // Used to clean up resources when env exit
198     napi_add_env_cleanup_hook(env, NapiPromiseEnvCleanupHook, this);
199 }
200 
~NapiPromise()201 NapiPromise::~NapiPromise()
202 {
203     if (!IsValidNapiEnv()) {
204         return;
205     }
206     napi_remove_env_cleanup_hook(env_, NapiPromiseEnvCleanupHook, this);
207 }
208 
ResolveOrReject(int errCode,const std::shared_ptr<NapiNativeObject> & object)209 void NapiPromise::ResolveOrReject(int errCode, const std::shared_ptr<NapiNativeObject> &object)
210 {
211     if (!IsValidNapiEnv()) {
212         HILOGW("napi env is exit");
213         return;
214     }
215     if (object == nullptr) {
216         HILOGE("napi native object is nullptr");
217         return;
218     }
219 
220     if (isResolvedOrRejected_) {
221         HILOGE("napi Resolved Or Rejected");
222         return;
223     }
224 
225     NapiHandleScope scope(env_);
226 
227     if (errCode == BT_NO_ERROR) {
228         napi_value val = object->ToNapiValue(env_);
229         Resolve(val);
230     } else {
231         napi_value code = GetCallbackErrorValue(env_, errCode);
232         Reject(code);
233     }
234     isResolvedOrRejected_ = true;
235 }
236 
Resolve(napi_value resolution)237 void NapiPromise::Resolve(napi_value resolution)
238 {
239     auto status = napi_resolve_deferred(env_, deferred_, resolution);
240     if (status != napi_ok) {
241         HILOGE("napi_resolve_deferred failed, status: %{public}d", status);
242     }
243 }
Reject(napi_value rejection)244 void NapiPromise::Reject(napi_value rejection)
245 {
246     auto status = napi_reject_deferred(env_, deferred_, rejection);
247     if (status != napi_ok) {
248         HILOGE("napi_reject_deferred failed, status: %{public}d", status);
249     }
250 }
GetPromise(void) const251 napi_value NapiPromise::GetPromise(void) const
252 {
253     return promise_;
254 }
255 
NapiHandleScope(napi_env env)256 NapiHandleScope::NapiHandleScope(napi_env env) : env_(env)
257 {
258     napi_status status = napi_open_handle_scope(env_, &scope_);
259     if (status != napi_ok) {
260         HILOGE("napi_open_handle_scope failed, status(%{public}d)", status);
261     }
262 }
263 
~NapiHandleScope()264 NapiHandleScope::~NapiHandleScope()
265 {
266     napi_status status = napi_close_handle_scope(env_, scope_);
267     if (status != napi_ok) {
268         HILOGE("napi_close_handle_scope failed, status(%{public}d)", status);
269     }
270 }
271 
272 }  // namespace Bluetooth
273 }  // namespace OHOS
274