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 #ifndef OHOS_ABILITY_RUNTIME_JS_RUNTIME_UTILS_H
17 #define OHOS_ABILITY_RUNTIME_JS_RUNTIME_UTILS_H
18 
19 #include <cstdint>
20 #include <memory>
21 #include <sstream>
22 #include <type_traits>
23 
24 #include "napi/native_api.h"
25 #include "native_engine/native_engine.h"
26 
27 #include "js_runtime.h"
28 
29 namespace OHOS {
30 namespace AbilityRuntime {
31 constexpr size_t ARGC_MAX_COUNT = 10;
32 
33 #define NAPI_CALL_NO_THROW(theCall, retVal)      \
34     do {                                         \
35         if ((theCall) != napi_ok) {              \
36             return retVal;                       \
37         }                                        \
38     } while (0)
39 
40 #define GET_CB_INFO_AND_CALL(env, info, T, func)                                       \
41     do {                                                                               \
42         size_t argc = ARGC_MAX_COUNT;                                                  \
43         napi_value argv[ARGC_MAX_COUNT] = {nullptr};                                   \
44         T* me = static_cast<T*>(GetCbInfoFromCallbackInfo(env, info, &argc, argv));    \
45         return (me != nullptr) ? me->func(env, argc, argv) : nullptr;                  \
46     } while (0)
47 
48 struct NapiCallbackInfo {
49     size_t argc = ARGC_MAX_COUNT;
50     napi_value argv[ARGC_MAX_COUNT] = {nullptr};
51     napi_value thisVar = nullptr;
52 };
53 
54 #define GET_NAPI_INFO_WITH_NAME_AND_CALL(env, info, T, func, name)                         \
55     do {                                                                                   \
56         NapiCallbackInfo napiInfo;                                                         \
57         T* me = static_cast<T*>(GetNapiCallbackInfoAndThis(env, info, napiInfo, name));    \
58         return (me != nullptr) ? me->func(env, napiInfo) : nullptr;                        \
59     } while (0)
60 
61 #define GET_NAPI_INFO_AND_CALL(env, info, T, func)                                         \
62     GET_NAPI_INFO_WITH_NAME_AND_CALL(env, info, T, func, nullptr)
63 
64 void* GetNapiCallbackInfoAndThis(
65     napi_env env, napi_callback_info info, NapiCallbackInfo& napiInfo, const char* name = nullptr);
66 
67 template<typename T, size_t N>
ArraySize(T (&)[N])68 inline constexpr size_t ArraySize(T (&)[N]) noexcept
69 {
70     return N;
71 }
72 
CreateJsUndefined(napi_env env)73 inline napi_value CreateJsUndefined(napi_env env)
74 {
75     napi_value result = nullptr;
76     napi_get_undefined(env, &result);
77     return result;
78 }
79 
CreateJsNull(napi_env env)80 inline napi_value CreateJsNull(napi_env env)
81 {
82     napi_value result = nullptr;
83     napi_get_null(env, &result);
84     return result;
85 }
86 
CreateJsNumber(napi_env env,int32_t value)87 inline napi_value CreateJsNumber(napi_env env, int32_t value)
88 {
89     napi_value result = nullptr;
90     napi_create_int32(env, value, &result);
91     return result;
92 }
93 
CreateJsNumber(napi_env env,uint32_t value)94 inline napi_value CreateJsNumber(napi_env env, uint32_t value)
95 {
96     napi_value result = nullptr;
97     napi_create_uint32(env, value, &result);
98     return result;
99 }
100 
CreateJsNumber(napi_env env,int64_t value)101 inline napi_value CreateJsNumber(napi_env env, int64_t value)
102 {
103     napi_value result = nullptr;
104     napi_create_int64(env, value, &result);
105     return result;
106 }
107 
CreateJsNumber(napi_env env,double value)108 inline napi_value CreateJsNumber(napi_env env, double value)
109 {
110     napi_value result = nullptr;
111     napi_create_double(env, value, &result);
112     return result;
113 }
114 
115 template<class T>
CreateJsValue(napi_env env,const T & value)116 inline napi_value CreateJsValue(napi_env env, const T& value)
117 {
118     using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
119     napi_value result = nullptr;
120     if constexpr (std::is_same_v<ValueType, bool>) {
121         napi_get_boolean(env, value, &result);
122         return result;
123     } else if constexpr (std::is_arithmetic_v<ValueType>) {
124         return CreateJsNumber(env, value);
125     } else if constexpr (std::is_same_v<ValueType, std::string>) {
126         napi_create_string_utf8(env, value.c_str(), value.length(), &result);
127         return result;
128     } else if constexpr (std::is_enum_v<ValueType>) {
129         return CreateJsNumber(env, static_cast<std::make_signed_t<ValueType>>(value));
130     } else if constexpr (std::is_same_v<ValueType, const char*>) {
131         (value != nullptr) ? napi_create_string_utf8(env, value, strlen(value), &result) :
132             napi_get_undefined(env, &result);
133         return result;
134     }
135 }
136 
ConvertFromJsNumber(napi_env env,napi_value jsValue,int32_t & value)137 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int32_t& value)
138 {
139     NAPI_CALL_NO_THROW(napi_get_value_int32(env, jsValue, &value), false);
140     return true;
141 }
142 
ConvertFromJsNumber(napi_env env,napi_value jsValue,uint32_t & value)143 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, uint32_t& value)
144 {
145     NAPI_CALL_NO_THROW(napi_get_value_uint32(env, jsValue, &value), false);
146     return true;
147 }
148 
ConvertFromJsNumber(napi_env env,napi_value jsValue,int64_t & value)149 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int64_t& value)
150 {
151     NAPI_CALL_NO_THROW(napi_get_value_int64(env, jsValue, &value), false);
152     return true;
153 }
154 
ConvertFromJsNumber(napi_env env,napi_value jsValue,double & value)155 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, double& value)
156 {
157     NAPI_CALL_NO_THROW(napi_get_value_double(env, jsValue, &value), false);
158     return true;
159 }
160 
161 template<class T>
ConvertFromJsValue(napi_env env,napi_value jsValue,T & value)162 inline bool ConvertFromJsValue(napi_env env, napi_value jsValue, T& value)
163 {
164     if (jsValue == nullptr) {
165         return false;
166     }
167 
168     using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
169     if constexpr (std::is_same_v<ValueType, bool>) {
170         NAPI_CALL_NO_THROW(napi_get_value_bool(env, jsValue, &value), false);
171         return true;
172     } else if constexpr (std::is_arithmetic_v<ValueType>) {
173         return ConvertFromJsNumber(env, jsValue, value);
174     } else if constexpr (std::is_same_v<ValueType, std::string>) {
175         size_t len = 0;
176         NAPI_CALL_NO_THROW(napi_get_value_string_utf8(env, jsValue, nullptr, 0, &len), false);
177         auto buffer = std::make_unique<char[]>(len + 1);
178         size_t strLength = 0;
179         NAPI_CALL_NO_THROW(napi_get_value_string_utf8(env, jsValue, buffer.get(), len + 1, &strLength), false);
180         value = buffer.get();
181         return true;
182     } else if constexpr (std::is_enum_v<ValueType>) {
183         std::make_signed_t<ValueType> numberValue = 0;
184         if (!ConvertFromJsNumber(env, jsValue, numberValue)) {
185             return false;
186         }
187         value = static_cast<ValueType>(numberValue);
188         return true;
189     }
190 }
191 
192 template<class T>
CreateNativeArray(napi_env env,const std::vector<T> & data)193 napi_value CreateNativeArray(napi_env env, const std::vector<T>& data)
194 {
195     napi_value arrayValue = nullptr;
196     napi_create_array_with_length(env, data.size(), &arrayValue);
197     uint32_t index = 0;
198     for (const T& item : data) {
199         napi_set_element(env, arrayValue, index++, CreateJsValue(env, item));
200     }
201     return arrayValue;
202 }
203 
204 napi_value CreateJsError(napi_env env, int32_t errCode, const std::string& message = std::string());
205 void BindNativeFunction(napi_env env, napi_value object, const char* name,
206     const char* moduleName, napi_callback func);
207 void BindNativeProperty(napi_env env, napi_value object, const char* name, napi_callback getter);
208 void* GetNativePointerFromCallbackInfo(napi_env env, napi_callback_info info, const char* name);
209 void* GetCbInfoFromCallbackInfo(napi_env env, napi_callback_info info, size_t* argc, napi_value* argv);
210 
211 void SetNamedNativePointer(
212     napi_env env, napi_value object, const char* name, void* ptr, napi_finalize func);
213 void* GetNamedNativePointer(napi_env env, napi_value object, const char* name);
214 
215 bool CheckTypeForNapiValue(napi_env env, napi_value param, napi_valuetype expectType);
216 
217 template<class T>
218 T* CheckParamsAndGetThis(napi_env env, napi_callback_info info, const char* name = nullptr)
219 {
220     return static_cast<T*>(GetNativePointerFromCallbackInfo(env, info, name));
221 }
222 
223 class HandleScope final {
224 public:
225     explicit HandleScope(JsRuntime& jsRuntime);
226     explicit HandleScope(napi_env env);
227     ~HandleScope();
228 
229     HandleScope(const HandleScope&) = delete;
230     HandleScope(HandleScope&&) = delete;
231     HandleScope& operator=(const HandleScope&) = delete;
232     HandleScope& operator=(HandleScope&&) = delete;
233 private:
234     napi_handle_scope scope_ = nullptr;
235     napi_env env_ = nullptr;
236 };
237 
238 class HandleEscape final {
239 public:
240     explicit HandleEscape(JsRuntime& jsRuntime);
241     explicit HandleEscape(napi_env env);
242     ~HandleEscape();
243 
244     napi_value Escape(napi_value value);
245 
246     HandleEscape(const HandleEscape&) = delete;
247     HandleEscape(HandleEscape&&) = delete;
248     HandleEscape& operator=(const HandleEscape&) = delete;
249     HandleEscape& operator=(HandleEscape&&) = delete;
250 private:
251     napi_escapable_handle_scope scope_ = nullptr;
252     napi_env env_ = nullptr;
253 };
254 
255 class NapiAsyncTask final {
256 public:
257     using ExecuteCallback = std::function<void()>;
258     using CompleteCallback = std::function<void(napi_env, NapiAsyncTask&, int32_t)>;
259 
260     static void Schedule(const std::string& name, napi_env env, std::unique_ptr<NapiAsyncTask>&& task);
261     static void ScheduleWithDefaultQos(const std::string &name, napi_env env,
262         std::unique_ptr<NapiAsyncTask>&& task);
263     static void ScheduleHighQos(const std::string& name, napi_env env, std::unique_ptr<NapiAsyncTask>&& task);
264     static void ScheduleLowQos(const std::string& name, napi_env env, std::unique_ptr<NapiAsyncTask>&& task);
265     bool StartWithDefaultQos(const std::string &name, napi_env env);
266 
267     NapiAsyncTask(napi_deferred deferred, std::unique_ptr<ExecuteCallback>&& execute,
268         std::unique_ptr<CompleteCallback>&& complete);
269     NapiAsyncTask(napi_ref callbackRef, std::unique_ptr<ExecuteCallback>&& execute,
270         std::unique_ptr<CompleteCallback>&& complete);
271     ~NapiAsyncTask();
272 
273     void Resolve(napi_env env, napi_value value);
274     void Reject(napi_env env, napi_value error);
275     void ResolveWithNoError(napi_env env, napi_value value);
276     void ResolveWithCustomize(napi_env env, napi_value error, napi_value value);
277     void RejectWithCustomize(napi_env env, napi_value error, napi_value value);
278 private:
279     static void Execute(napi_env env, void* data);
280     static void Complete(napi_env env, napi_status status, void* data);
281 
282     bool Start(const std::string &name, napi_env env);
283     bool StartHighQos(const std::string &name, napi_env env);
284     bool StartLowQos(const std::string &name, napi_env env);
285 
286     napi_deferred deferred_ = nullptr;
287     napi_ref callbackRef_ = nullptr;
288     napi_async_work work_ = nullptr;
289     std::unique_ptr<ExecuteCallback> execute_;
290     std::unique_ptr<CompleteCallback> complete_;
291     napi_env env_ = nullptr;
292 };
293 
294 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
295     NapiAsyncTask::ExecuteCallback&& execute, NapiAsyncTask::CompleteCallback&& complete, napi_value* result);
296 
297 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
298     NapiAsyncTask::ExecuteCallback&& execute, std::nullptr_t, napi_value* result);
299 
300 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
301     std::nullptr_t, NapiAsyncTask::CompleteCallback&& complete, napi_value* result);
302 
303 std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
304     std::nullptr_t, std::nullptr_t, napi_value* result);
305 
306 std::unique_ptr<NapiAsyncTask> CreateEmptyAsyncTask(napi_env env, napi_value lastParam, napi_value* result);
307 }  // namespace AbilityRuntime
308 }  // namespace OHOS
309 #endif  // OHOS_ABILITY_RUNTIME_JS_RUNTIME_UTILS_H
310