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 
NapiCallback(napi_env env,napi_value callback)53 NapiCallback::NapiCallback(napi_env env, napi_value callback) : env_(env)
54 {
55     auto status = napi_create_reference(env, callback, 1, &callbackRef_);
56     if (status != napi_ok) {
57         HILOGE("napi_create_reference failed, status: %{public}d", status);
58     }
59 }
~NapiCallback()60 NapiCallback::~NapiCallback()
61 {
62     auto status = napi_delete_reference(env_, callbackRef_);
63     if (status != napi_ok) {
64         HILOGE("napi_delete_reference failed, status: %{public}d", status);
65     }
66 }
67 
68 namespace {
NapiCallFunction(napi_env env,napi_ref callbackRef,napi_value * argv,size_t argc)69 void NapiCallFunction(napi_env env, napi_ref callbackRef, napi_value *argv, size_t argc)
70 {
71     napi_value undefined = nullptr;
72     napi_value callRet = nullptr;
73     napi_value callback = nullptr;
74     auto status = napi_get_reference_value(env, callbackRef, &callback);
75     if (status != napi_ok) {
76         HILOGE("napi_get_reference_value failed, status: %{public}d", status);
77         return;
78     }
79 
80     status = napi_call_function(env, undefined, callback, argc, argv, &callRet);
81     if (status != napi_ok) {
82         HILOGE("napi_call_function failed, status: %{public}d", status);
83     }
84 
85     // Check whether the JS application triggers an exception in callback. If it is, clear it.
86     bool isExist = false;
87     status = napi_is_exception_pending(env, &isExist);
88     HILOGD("napi_is_exception_pending status: %{public}d, isExist: %{public}d", status, isExist);
89     if (isExist) {
90         HILOGI("Clear JS application's exception");
91         napi_value exception = nullptr;
92         status = napi_get_and_clear_last_exception(env, &exception);
93         HILOGD("napi_get_and_clear_last_exception status: %{public}d, exception: %{public}p", status, exception);
94     }
95 }
96 }  // namespace {}
97 
CallFunction(const std::shared_ptr<NapiNativeObject> & object)98 void NapiCallback::CallFunction(const std::shared_ptr<NapiNativeObject> &object)
99 {
100     if (object == nullptr) {
101         HILOGE("napi native object is nullptr");
102         return;
103     }
104 
105     NapiHandleScope scope(env_);
106     napi_value val = object->ToNapiValue(env_);
107     NapiCallFunction(env_, callbackRef_, &val, ARGS_SIZE_ONE);
108 }
109 
CallFunction(int errCode,const std::shared_ptr<NapiNativeObject> & object)110 void NapiCallback::CallFunction(int errCode, const std::shared_ptr<NapiNativeObject> &object)
111 {
112     if (object == nullptr) {
113         HILOGE("napi native object is nullptr");
114         return;
115     }
116 
117     NapiHandleScope scope(env_);
118     napi_value code = GetCallbackErrorValue(env_, errCode);
119     napi_value val = object->ToNapiValue(env_);
120     napi_value argv[ARGS_SIZE_TWO] = {code, val};
121     NapiCallFunction(env_, callbackRef_, argv, ARGS_SIZE_TWO);
122 }
123 
GetNapiEnv(void)124 napi_env NapiCallback::GetNapiEnv(void)
125 {
126     return env_;
127 }
128 
Equal(napi_value & callback) const129 bool NapiCallback::Equal(napi_value &callback) const
130 {
131     NapiHandleScope scope(env_);
132     napi_value storedCallback = nullptr;
133     napi_get_reference_value(env_, callbackRef_, &storedCallback);
134 
135     bool isEqual = false;
136     napi_strict_equals(env_, storedCallback, callback, &isEqual);
137     return isEqual;
138 }
139 
NapiPromise(napi_env env)140 NapiPromise::NapiPromise(napi_env env) : env_(env)
141 {
142     auto status = napi_create_promise(env, &deferred_, &promise_);
143     if (status != napi_ok) {
144         HILOGE("napi_create_promise failed, status: %{public}d", status);
145     }
146 }
147 
ResolveOrReject(int errCode,const std::shared_ptr<NapiNativeObject> & object)148 void NapiPromise::ResolveOrReject(int errCode, const std::shared_ptr<NapiNativeObject> &object)
149 {
150     if (object == nullptr) {
151         HILOGE("napi native object is nullptr");
152         return;
153     }
154 
155     if (isResolvedOrRejected_) {
156         HILOGE("napi Resolved Or Rejected");
157         return;
158     }
159 
160     NapiHandleScope scope(env_);
161 
162     if (errCode == BT_NO_ERROR) {
163         napi_value val = object->ToNapiValue(env_);
164         Resolve(val);
165     } else {
166         napi_value code = GetCallbackErrorValue(env_, errCode);
167         Reject(code);
168     }
169     isResolvedOrRejected_ = true;
170 }
171 
Resolve(napi_value resolution)172 void NapiPromise::Resolve(napi_value resolution)
173 {
174     auto status = napi_resolve_deferred(env_, deferred_, resolution);
175     if (status != napi_ok) {
176         HILOGE("napi_resolve_deferred failed, status: %{public}d", status);
177     }
178 }
Reject(napi_value rejection)179 void NapiPromise::Reject(napi_value rejection)
180 {
181     auto status = napi_reject_deferred(env_, deferred_, rejection);
182     if (status != napi_ok) {
183         HILOGE("napi_reject_deferred failed, status: %{public}d", status);
184     }
185 }
GetPromise(void) const186 napi_value NapiPromise::GetPromise(void) const
187 {
188     return promise_;
189 }
190 
NapiHandleScope(napi_env env)191 NapiHandleScope::NapiHandleScope(napi_env env) : env_(env)
192 {
193     napi_status status = napi_open_handle_scope(env_, &scope_);
194     if (status != napi_ok) {
195         HILOGE("napi_open_handle_scope failed, status(%{public}d)", status);
196     }
197 }
198 
~NapiHandleScope()199 NapiHandleScope::~NapiHandleScope()
200 {
201     napi_status status = napi_close_handle_scope(env_, scope_);
202     if (status != napi_ok) {
203         HILOGE("napi_close_handle_scope failed, status(%{public}d)", status);
204     }
205 }
206 
207 }  // namespace Bluetooth
208 }  // namespace OHOS
209