1 /*
2 * Copyright (c) 2021-2024 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 "js_runtime_utils.h"
17
18 #include "hilog_tag_wrapper.h"
19
20 namespace OHOS {
21 namespace AbilityRuntime {
22 #define ARGS_MAX_COUNT 10
23 namespace {
CreateAsyncTaskWithLastParam(napi_env env,napi_value lastParam,std::unique_ptr<NapiAsyncTask::ExecuteCallback> && execute,std::unique_ptr<NapiAsyncTask::CompleteCallback> && complete,napi_value * result)24 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
25 std::unique_ptr<NapiAsyncTask::ExecuteCallback> &&execute,
26 std::unique_ptr<NapiAsyncTask::CompleteCallback> &&complete, napi_value* result)
27 {
28 napi_valuetype type = napi_undefined;
29 napi_typeof(env, lastParam, &type);
30 if (lastParam == nullptr || type != napi_function) {
31 napi_deferred nativeDeferred = nullptr;
32 napi_create_promise(env, &nativeDeferred, result);
33 return std::make_unique<NapiAsyncTask>(nativeDeferred, std::move(execute), std::move(complete));
34 } else {
35 napi_get_undefined(env, result);
36 napi_ref callbackRef = nullptr;
37 napi_create_reference(env, lastParam, 1, &callbackRef);
38 return std::make_unique<NapiAsyncTask>(callbackRef, std::move(execute), std::move(complete));
39 }
40 }
41 } // namespace
42
43 // Help Functions
CreateJsError(napi_env env,int32_t errCode,const std::string & message)44 napi_value CreateJsError(napi_env env, int32_t errCode, const std::string &message)
45 {
46 napi_value result = nullptr;
47 napi_create_error(env, CreateJsValue(env, errCode), CreateJsValue(env, message), &result);
48 return result;
49 }
50
BindNativeFunction(napi_env env,napi_value object,const char * name,const char * moduleName,napi_callback func)51 void BindNativeFunction(napi_env env, napi_value object, const char *name,
52 const char *moduleName, napi_callback func)
53 {
54 std::string fullName(moduleName);
55 fullName += ".";
56 fullName += name;
57 napi_value result = nullptr;
58 napi_create_function(env, fullName.c_str(), fullName.length(), func, nullptr, &result);
59 napi_set_named_property(env, object, name, result);
60 }
61
BindNativeProperty(napi_env env,napi_value object,const char * name,napi_callback getter)62 void BindNativeProperty(napi_env env, napi_value object, const char *name, napi_callback getter)
63 {
64 napi_property_descriptor properties[1];
65 properties[0].utf8name = name;
66 properties[0].name = nullptr;
67 properties[0].method = nullptr;
68 properties[0].getter = getter;
69 properties[0].setter = nullptr;
70 properties[0].value = nullptr;
71 properties[0].attributes = napi_default;
72 properties[0].data = nullptr;
73 napi_define_properties(env, object, 1, properties);
74 }
75
GetNativePointerFromCallbackInfo(const napi_env env,napi_callback_info info,const char * name)76 void *GetNativePointerFromCallbackInfo(const napi_env env, napi_callback_info info, const char *name)
77 {
78 size_t argcAsync = ARGS_MAX_COUNT;
79 napi_value args[ARGS_MAX_COUNT] = {nullptr};
80 napi_value thisVar = nullptr;
81 NAPI_CALL_NO_THROW(napi_get_cb_info(env, info, &argcAsync, args, &thisVar, nullptr), nullptr);
82 if (name != nullptr) {
83 napi_get_named_property(env, thisVar, name, &thisVar);
84 }
85 void* result = nullptr;
86 NAPI_CALL_NO_THROW(napi_unwrap(env, thisVar, &result), nullptr);
87 return result;
88 }
89
GetNapiCallbackInfoAndThis(napi_env env,napi_callback_info info,NapiCallbackInfo & napiInfo,const char * name)90 void* GetNapiCallbackInfoAndThis(napi_env env, napi_callback_info info, NapiCallbackInfo& napiInfo, const char* name)
91 {
92 NAPI_CALL_NO_THROW(napi_get_cb_info(
93 env, info, &napiInfo.argc, napiInfo.argv, &napiInfo.thisVar, nullptr), nullptr);
94 napi_value value = napiInfo.thisVar;
95 if (name != nullptr) {
96 napi_get_named_property(env, value, name, &value);
97 }
98 void* result = nullptr;
99 NAPI_CALL_NO_THROW(napi_unwrap(env, value, &result), nullptr);
100 return result;
101 }
102
SetNamedNativePointer(napi_env env,napi_value object,const char * name,void * ptr,napi_finalize func)103 void SetNamedNativePointer(napi_env env, napi_value object, const char *name, void *ptr, napi_finalize func)
104 {
105 napi_value objValue = nullptr;
106 napi_create_object(env, &objValue);
107 napi_wrap(env, objValue, ptr, func, nullptr, nullptr);
108 napi_set_named_property(env, object, name, objValue);
109 }
110
GetNamedNativePointer(napi_env env,napi_value object,const char * name)111 void *GetNamedNativePointer(napi_env env, napi_value object, const char *name)
112 {
113 napi_value proValue = nullptr;
114 napi_get_named_property(env, object, name, &proValue);
115 void* result = nullptr;
116 napi_unwrap(env, proValue, &result);
117 return result;
118 }
119
CheckTypeForNapiValue(napi_env env,napi_value param,napi_valuetype expectType)120 bool CheckTypeForNapiValue(napi_env env, napi_value param, napi_valuetype expectType)
121 {
122 napi_valuetype valueType = napi_undefined;
123 if (napi_typeof(env, param, &valueType) != napi_ok) {
124 return false;
125 }
126 return valueType == expectType;
127 }
128
129 // Async Task
NapiAsyncTask(napi_deferred deferred,std::unique_ptr<NapiAsyncTask::ExecuteCallback> && execute,std::unique_ptr<NapiAsyncTask::CompleteCallback> && complete)130 NapiAsyncTask::NapiAsyncTask(napi_deferred deferred, std::unique_ptr<NapiAsyncTask::ExecuteCallback> &&execute,
131 std::unique_ptr<NapiAsyncTask::CompleteCallback> &&complete)
132 : deferred_(deferred), execute_(std::move(execute)), complete_(std::move(complete))
133 {}
134
NapiAsyncTask(napi_ref callbackRef,std::unique_ptr<NapiAsyncTask::ExecuteCallback> && execute,std::unique_ptr<NapiAsyncTask::CompleteCallback> && complete)135 NapiAsyncTask::NapiAsyncTask(napi_ref callbackRef, std::unique_ptr<NapiAsyncTask::ExecuteCallback> &&execute,
136 std::unique_ptr<NapiAsyncTask::CompleteCallback> &&complete)
137 : callbackRef_(callbackRef), execute_(std::move(execute)), complete_(std::move(complete))
138 {}
139
140 NapiAsyncTask::~NapiAsyncTask() = default;
141
Schedule(const std::string & name,napi_env env,std::unique_ptr<NapiAsyncTask> && task)142 void NapiAsyncTask::Schedule(const std::string &name, napi_env env, std::unique_ptr<NapiAsyncTask> &&task)
143 {
144 if (task && task->Start(name, env)) {
145 task.release();
146 }
147 }
148
Resolve(napi_env env,napi_value value)149 void NapiAsyncTask::Resolve(napi_env env, napi_value value)
150 {
151 TAG_LOGD(AAFwkTag::ABILITY_SIM, "called");
152 if (deferred_) {
153 napi_resolve_deferred(env, deferred_, value);
154 deferred_ = nullptr;
155 }
156 if (callbackRef_) {
157 napi_value argv[] = {
158 CreateJsError(env, 0),
159 value,
160 };
161 napi_value func = nullptr;
162 napi_get_reference_value(env, callbackRef_, &func);
163 napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
164 napi_delete_reference(env, callbackRef_);
165 callbackRef_ = nullptr;
166 }
167 }
168
ResolveWithNoError(napi_env env,napi_value value)169 void NapiAsyncTask::ResolveWithNoError(napi_env env, napi_value value)
170 {
171 TAG_LOGD(AAFwkTag::ABILITY_SIM, "called");
172 if (deferred_) {
173 napi_resolve_deferred(env, deferred_, value);
174 deferred_ = nullptr;
175 }
176 if (callbackRef_) {
177 napi_value argv[] = {
178 CreateJsNull(env),
179 value,
180 };
181 napi_value func = nullptr;
182 napi_get_reference_value(env, callbackRef_, &func);
183 napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
184 napi_delete_reference(env, callbackRef_);
185 callbackRef_ = nullptr;
186 }
187 }
188
Reject(napi_env env,napi_value error)189 void NapiAsyncTask::Reject(napi_env env, napi_value error)
190 {
191 if (deferred_) {
192 napi_reject_deferred(env, deferred_, error);
193 deferred_ = nullptr;
194 }
195 if (callbackRef_) {
196 napi_value argv[] = {
197 error,
198 CreateJsUndefined(env),
199 };
200 napi_value func = nullptr;
201 napi_get_reference_value(env, callbackRef_, &func);
202 napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
203 napi_delete_reference(env, callbackRef_);
204 callbackRef_ = nullptr;
205 }
206 }
207
ResolveWithCustomize(napi_env env,napi_value error,napi_value value)208 void NapiAsyncTask::ResolveWithCustomize(napi_env env, napi_value error, napi_value value)
209 {
210 TAG_LOGD(AAFwkTag::ABILITY_SIM, "called");
211 if (deferred_) {
212 napi_resolve_deferred(env, deferred_, value);
213 deferred_ = nullptr;
214 }
215 if (callbackRef_) {
216 napi_value argv[] = {
217 error,
218 value,
219 };
220 napi_value func = nullptr;
221 napi_get_reference_value(env, callbackRef_, &func);
222 napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
223 napi_delete_reference(env, callbackRef_);
224 callbackRef_ = nullptr;
225 }
226 }
227
RejectWithCustomize(napi_env env,napi_value error,napi_value value)228 void NapiAsyncTask::RejectWithCustomize(napi_env env, napi_value error, napi_value value)
229 {
230 TAG_LOGD(AAFwkTag::ABILITY_SIM, "called");
231 if (deferred_) {
232 napi_reject_deferred(env, deferred_, error);
233 deferred_ = nullptr;
234 }
235 if (callbackRef_) {
236 napi_value argv[] = {
237 error,
238 value,
239 };
240 napi_value func = nullptr;
241 napi_get_reference_value(env, callbackRef_, &func);
242 napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
243 napi_delete_reference(env, callbackRef_);
244 callbackRef_ = nullptr;
245 }
246 }
247
Execute(napi_env env,void * data)248 void NapiAsyncTask::Execute(napi_env env, void *data)
249 {
250 if (env == nullptr || data == nullptr) {
251 return;
252 }
253 auto me = static_cast<NapiAsyncTask*>(data);
254 if (me->execute_ && *(me->execute_)) {
255 (*me->execute_)();
256 }
257 }
258
Complete(napi_env env,napi_status status,void * data)259 void NapiAsyncTask::Complete(napi_env env, napi_status status, void *data)
260 {
261 if (data == nullptr) {
262 return;
263 }
264 std::unique_ptr<NapiAsyncTask> me(static_cast<NapiAsyncTask*>(data));
265 if (me->complete_ && *(me->complete_)) {
266 (*me->complete_)(env, *me, static_cast<int32_t>(status));
267 }
268 }
269
Start(const std::string & name,napi_env env)270 bool NapiAsyncTask::Start(const std::string &name, napi_env env)
271 {
272 if (work_) {
273 napi_delete_async_work(env, work_);
274 work_ = nullptr;
275 }
276 if (env == nullptr) {
277 return false;
278 }
279 NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
280 work_ = reinterpret_cast<napi_async_work>(engine->CreateAsyncWork(name,
281 reinterpret_cast<NativeAsyncExecuteCallback>(Execute),
282 reinterpret_cast<NativeAsyncCompleteCallback>(Complete), this));
283 napi_queue_async_work(env, work_);
284 return true;
285 }
286
CreateAsyncTaskWithLastParam(napi_env env,napi_value lastParam,NapiAsyncTask::ExecuteCallback && execute,NapiAsyncTask::CompleteCallback && complete,napi_value * result)287 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
288 NapiAsyncTask::ExecuteCallback &&execute, NapiAsyncTask::CompleteCallback &&complete, napi_value *result)
289 {
290 return CreateAsyncTaskWithLastParam(env, lastParam,
291 std::make_unique<NapiAsyncTask::ExecuteCallback>(std::move(execute)),
292 std::make_unique<NapiAsyncTask::CompleteCallback>(std::move(complete)), result);
293 }
294
CreateAsyncTaskWithLastParam(napi_env env,napi_value lastParam,NapiAsyncTask::ExecuteCallback && execute,std::nullptr_t,napi_value * result)295 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
296 NapiAsyncTask::ExecuteCallback &&execute, std::nullptr_t, napi_value *result)
297 {
298 return CreateAsyncTaskWithLastParam(
299 env, lastParam, std::make_unique<NapiAsyncTask::ExecuteCallback>(std::move(execute)), nullptr, result);
300 }
301
CreateAsyncTaskWithLastParam(napi_env env,napi_value lastParam,std::nullptr_t,NapiAsyncTask::CompleteCallback && complete,napi_value * result)302 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
303 std::nullptr_t, NapiAsyncTask::CompleteCallback &&complete, napi_value *result)
304 {
305 return CreateAsyncTaskWithLastParam(
306 env, lastParam, nullptr, std::make_unique<NapiAsyncTask::CompleteCallback>(std::move(complete)), result);
307 }
308
CreateAsyncTaskWithLastParam(napi_env env,napi_value lastParam,std::nullptr_t,std::nullptr_t,napi_value * result)309 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
310 std::nullptr_t, std::nullptr_t, napi_value *result)
311 {
312 return CreateAsyncTaskWithLastParam(env, lastParam, std::unique_ptr<NapiAsyncTask::ExecuteCallback>(),
313 std::unique_ptr<NapiAsyncTask::CompleteCallback>(), result);
314 }
315
SendNapiEvent(napi_env env,napi_event_priority eventPriority)316 bool NapiAsyncTask::SendNapiEvent(napi_env env, napi_event_priority eventPriority)
317 {
318 if (napi_send_event(
319 env,
320 [env, this]() {
321 napi_status status = napi_ok;
322 Complete(env, status, this);
323 },
324 eventPriority) != napi_ok) {
325 TAG_LOGE(AAFwkTag::ABILITY_SIM, "SendNapiEvent failed");
326 return false;
327 }
328 return true;
329 }
330
ScheduleHighQos(const std::string & name,napi_env env,std::unique_ptr<NapiAsyncTask> && task)331 void NapiAsyncTask::ScheduleHighQos(const std::string &name, napi_env env, std::unique_ptr<NapiAsyncTask> &&task)
332 {
333 if (task && task->StartHighQos(name, env)) {
334 task.release();
335 }
336 }
337
StartHighQos(const std::string & name,napi_env env)338 bool NapiAsyncTask::StartHighQos(const std::string &name, napi_env env)
339 {
340 if (work_) {
341 napi_delete_async_work(env, work_);
342 work_ = nullptr;
343 }
344 if (env == nullptr) {
345 return false;
346 }
347 if (execute_ == nullptr) {
348 return SendNapiEvent(env, napi_eprio_immediate);
349 }
350 NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
351 work_ = reinterpret_cast<napi_async_work>(engine->CreateAsyncWork(name,
352 reinterpret_cast<NativeAsyncExecuteCallback>(Execute),
353 reinterpret_cast<NativeAsyncCompleteCallback>(Complete), this));
354 napi_queue_async_work_with_qos(env, work_, napi_qos_user_initiated);
355 return true;
356 }
357 } // namespace AbilityRuntime
358 } // namespace OHOS