1 /*
2  * Copyright (c) 2022-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 "napi_utils.h"
17 
18 #include <cstring>
19 #include <initializer_list>
20 #include <memory>
21 #include <mutex>
22 #include <unordered_set>
23 
24 #include "netmanager_base_log.h"
25 #include "securec.h"
26 
27 namespace OHOS {
28 namespace NetManagerStandard {
29 namespace NapiUtils {
30 namespace {
31 static constexpr const int MAX_STRING_LENGTH = 65536;
32 constexpr const char *CODE = "code";
33 constexpr const char *MSG = "message";
34 } // namespace
35 
36 static std::unordered_set<napi_env> unorderedSetEnv;
37 static std::recursive_mutex mutexForEnv;
38 
39 class WorkData {
40 public:
41     WorkData() = delete;
42 
WorkData(napi_env env,void * data,void (* handler)(napi_env env,napi_status status,void * data))43     WorkData(napi_env env, void *data, void (*handler)(napi_env env, napi_status status, void *data))
44         : env_(env), data_(data), handler_(handler) {}
45 
46     napi_env env_;
47     void *data_;
48     void (*handler_)(napi_env env, napi_status status, void *data);
49 };
50 
51 
GetValueType(napi_env env,napi_value value)52 napi_valuetype GetValueType(napi_env env, napi_value value)
53 {
54     if (value == nullptr) {
55         return napi_undefined;
56     }
57 
58     napi_valuetype valueType = napi_undefined;
59     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined);
60     return valueType;
61 }
62 
63 /* named property */
HasNamedProperty(napi_env env,napi_value object,const std::string & propertyName)64 bool HasNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
65 {
66     bool hasProperty = false;
67     NAPI_CALL_BASE(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty), false);
68     return hasProperty;
69 }
70 
GetNamedProperty(napi_env env,napi_value object,const std::string & propertyName)71 napi_value GetNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
72 {
73     napi_value value = nullptr;
74     NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &value));
75     return value;
76 }
77 
SetNamedProperty(napi_env env,napi_value object,const std::string & name,napi_value value)78 void SetNamedProperty(napi_env env, napi_value object, const std::string &name, napi_value value)
79 {
80     NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), value));
81 }
82 
GetPropertyNames(napi_env env,napi_value object)83 std::vector<std::string> GetPropertyNames(napi_env env, napi_value object)
84 {
85     std::vector<std::string> ret;
86     napi_value names = nullptr;
87     NAPI_CALL_BASE(env, napi_get_property_names(env, object, &names), ret);
88     uint32_t length = 0;
89     NAPI_CALL_BASE(env, napi_get_array_length(env, names, &length), ret);
90     for (uint32_t index = 0; index < length; ++index) {
91         napi_value name = nullptr;
92         if (napi_get_element(env, names, index, &name) != napi_ok) {
93             continue;
94         }
95         if (GetValueType(env, name) != napi_string) {
96             continue;
97         }
98         ret.emplace_back(GetStringFromValueUtf8(env, name));
99     }
100     return ret;
101 }
102 
103 /* UINT32 */
CreateUint32(napi_env env,uint32_t code)104 napi_value CreateUint32(napi_env env, uint32_t code)
105 {
106     napi_value value = nullptr;
107     if (napi_create_uint32(env, code, &value) != napi_ok) {
108         return nullptr;
109     }
110     return value;
111 }
112 
GetUint32FromValue(napi_env env,napi_value value)113 uint32_t GetUint32FromValue(napi_env env, napi_value value)
114 {
115     uint32_t ret = 0;
116     NAPI_CALL_BASE(env, napi_get_value_uint32(env, value, &ret), 0);
117     return ret;
118 }
119 
GetUint32Property(napi_env env,napi_value object,const std::string & propertyName)120 uint32_t GetUint32Property(napi_env env, napi_value object, const std::string &propertyName)
121 {
122     if (!HasNamedProperty(env, object, propertyName)) {
123         return 0;
124     }
125     napi_value value = GetNamedProperty(env, object, propertyName);
126     return GetUint32FromValue(env, value);
127 }
128 
SetUint32Property(napi_env env,napi_value object,const std::string & name,uint32_t value)129 void SetUint32Property(napi_env env, napi_value object, const std::string &name, uint32_t value)
130 {
131     napi_value jsValue = CreateUint32(env, value);
132     if (GetValueType(env, jsValue) != napi_number) {
133         return;
134     }
135 
136     napi_set_named_property(env, object, name.c_str(), jsValue);
137 }
138 
139 /* INT32 */
CreateInt32(napi_env env,int32_t code)140 napi_value CreateInt32(napi_env env, int32_t code)
141 {
142     napi_value value = nullptr;
143     if (napi_create_int32(env, code, &value) != napi_ok) {
144         return nullptr;
145     }
146     return value;
147 }
148 
GetInt32FromValue(napi_env env,napi_value value)149 int32_t GetInt32FromValue(napi_env env, napi_value value)
150 {
151     int32_t ret = 0;
152     NAPI_CALL_BASE(env, napi_get_value_int32(env, value, &ret), 0);
153     return ret;
154 }
155 
GetInt32Property(napi_env env,napi_value object,const std::string & propertyName)156 int32_t GetInt32Property(napi_env env, napi_value object, const std::string &propertyName)
157 {
158     if (!HasNamedProperty(env, object, propertyName)) {
159         return 0;
160     }
161     napi_value value = GetNamedProperty(env, object, propertyName);
162     return GetInt32FromValue(env, value);
163 }
164 
SetInt32Property(napi_env env,napi_value object,const std::string & name,int32_t value)165 void SetInt32Property(napi_env env, napi_value object, const std::string &name, int32_t value)
166 {
167     napi_value jsValue = CreateInt32(env, value);
168     if (GetValueType(env, jsValue) != napi_number) {
169         return;
170     }
171 
172     napi_set_named_property(env, object, name.c_str(), jsValue);
173 }
174 
175 /* INT64 */
CreateInt64(napi_env env,int64_t code)176 napi_value CreateInt64(napi_env env, int64_t code)
177 {
178     napi_value value = nullptr;
179     if (napi_create_int64(env, code, &value) != napi_ok) {
180         return nullptr;
181     }
182     return value;
183 }
184 
GetInt64Property(napi_env env,napi_value object,const std::string & propertyName)185 int64_t GetInt64Property(napi_env env, napi_value object, const std::string &propertyName)
186 {
187     if (!HasNamedProperty(env, object, propertyName)) {
188         return 0;
189     }
190     napi_value value = GetNamedProperty(env, object, propertyName);
191     return GetInt64FromValue(env, value);
192 }
GetInt64FromValue(napi_env env,napi_value value)193 int64_t GetInt64FromValue(napi_env env, napi_value value)
194 {
195     int64_t ret = 0;
196     NAPI_CALL_BASE(env, napi_get_value_int64(env, value, &ret), 0);
197     return ret;
198 }
SetInt64Property(napi_env env,napi_value object,const std::string & name,int64_t value)199 void SetInt64Property(napi_env env, napi_value object, const std::string &name, int64_t value)
200 {
201     napi_value jsValue = CreateInt64(env, value);
202     if (GetValueType(env, jsValue) != napi_number) {
203         return;
204     }
205 
206     napi_set_named_property(env, object, name.c_str(), jsValue);
207 }
208 
209 /* String UTF8 */
CreateStringUtf8(napi_env env,const std::string & str)210 napi_value CreateStringUtf8(napi_env env, const std::string &str)
211 {
212     napi_value value = nullptr;
213     if (napi_create_string_utf8(env, str.c_str(), strlen(str.c_str()), &value) != napi_ok) {
214         return nullptr;
215     }
216     return value;
217 }
218 
GetStringFromValueUtf8(napi_env env,napi_value value)219 std::string GetStringFromValueUtf8(napi_env env, napi_value value)
220 {
221     std::string result;
222     char str[MAX_STRING_LENGTH] = {0};
223     size_t length = 0;
224     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length), result);
225     if (length > MAX_STRING_LENGTH) {
226         result.append(str, MAX_STRING_LENGTH);
227         return result;
228     }
229     if (length > 0) {
230         return result.append(str, length);
231     }
232     return result;
233 }
234 
GetSecureDataFromValueUtf8(napi_env env,napi_value value)235 SecureData GetSecureDataFromValueUtf8(napi_env env, napi_value value)
236 {
237     SecureData result;
238     char str[MAX_STRING_LENGTH] = {0};
239     size_t length = 0;
240     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length), result);
241     if (length > 0) {
242         result.append(str, length);
243     }
244     return result;
245 }
246 
GetStringPropertyUtf8(napi_env env,napi_value object,const std::string & propertyName)247 std::string GetStringPropertyUtf8(napi_env env, napi_value object, const std::string &propertyName)
248 {
249     if (!HasNamedProperty(env, object, propertyName)) {
250         return "";
251     }
252     napi_value value = GetNamedProperty(env, object, propertyName);
253     return GetStringFromValueUtf8(env, value);
254 }
255 
GetSecureDataPropertyUtf8(napi_env env,napi_value object,const std::string & propertyName)256 SecureData GetSecureDataPropertyUtf8(napi_env env, napi_value object, const std::string &propertyName)
257 {
258     napi_value value = GetNamedProperty(env, object, propertyName);
259     return GetSecureDataFromValueUtf8(env, value);
260 }
261 
SetStringPropertyUtf8(napi_env env,napi_value object,const std::string & name,const std::string & value)262 void SetStringPropertyUtf8(napi_env env, napi_value object, const std::string &name, const std::string &value)
263 {
264     napi_value jsValue = CreateStringUtf8(env, value);
265     if (GetValueType(env, jsValue) != napi_string) {
266         return;
267     }
268     napi_set_named_property(env, object, name.c_str(), jsValue);
269 }
270 
271 /* array buffer */
ValueIsArrayBuffer(napi_env env,napi_value value)272 bool ValueIsArrayBuffer(napi_env env, napi_value value)
273 {
274     bool isArrayBuffer = false;
275     NAPI_CALL_BASE(env, napi_is_arraybuffer(env, value, &isArrayBuffer), false);
276     return isArrayBuffer;
277 }
278 
GetInfoFromArrayBufferValue(napi_env env,napi_value value,size_t * length)279 void *GetInfoFromArrayBufferValue(napi_env env, napi_value value, size_t *length)
280 {
281     if (length == nullptr) {
282         return nullptr;
283     }
284 
285     void *data = nullptr;
286     NAPI_CALL(env, napi_get_arraybuffer_info(env, value, &data, length));
287     return data;
288 }
289 
CreateArrayBuffer(napi_env env,size_t length,void ** data)290 napi_value CreateArrayBuffer(napi_env env, size_t length, void **data)
291 {
292     if (length == 0) {
293         return nullptr;
294     }
295     napi_value result = nullptr;
296     NAPI_CALL(env, napi_create_arraybuffer(env, length, data, &result));
297     return result;
298 }
299 
300 /* object */
CreateObject(napi_env env)301 napi_value CreateObject(napi_env env)
302 {
303     napi_value object = nullptr;
304     NAPI_CALL(env, napi_create_object(env, &object));
305     return object;
306 }
307 
308 /* undefined */
GetUndefined(napi_env env)309 napi_value GetUndefined(napi_env env)
310 {
311     napi_value undefined = nullptr;
312     NAPI_CALL(env, napi_get_undefined(env, &undefined));
313     return undefined;
314 }
315 
316 /* function */
CallFunction(napi_env env,napi_value recv,napi_value func,size_t argc,const napi_value * argv)317 napi_value CallFunction(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv)
318 {
319     napi_value res = nullptr;
320     NAPI_CALL(env, napi_call_function(env, recv, func, argc, argv, &res));
321     return res;
322 }
323 
CreateFunction(napi_env env,const std::string & name,napi_callback func,void * arg)324 napi_value CreateFunction(napi_env env, const std::string &name, napi_callback func, void *arg)
325 {
326     napi_value res = nullptr;
327     NAPI_CALL(env, napi_create_function(env, name.c_str(), strlen(name.c_str()), func, arg, &res));
328     return res;
329 }
330 
331 /* reference */
CreateReference(napi_env env,napi_value callback)332 napi_ref CreateReference(napi_env env, napi_value callback)
333 {
334     napi_ref callbackRef = nullptr;
335     NAPI_CALL(env, napi_create_reference(env, callback, 1, &callbackRef));
336     return callbackRef;
337 }
338 
GetReference(napi_env env,napi_ref callbackRef)339 napi_value GetReference(napi_env env, napi_ref callbackRef)
340 {
341     napi_value callback = nullptr;
342     NAPI_CALL(env, napi_get_reference_value(env, callbackRef, &callback));
343     return callback;
344 }
345 
DeleteReference(napi_env env,napi_ref callbackRef)346 void DeleteReference(napi_env env, napi_ref callbackRef)
347 {
348     (void)napi_delete_reference(env, callbackRef);
349 }
350 
351 /* boolean */
GetBooleanProperty(napi_env env,napi_value object,const std::string & propertyName)352 bool GetBooleanProperty(napi_env env, napi_value object, const std::string &propertyName)
353 {
354     if (!HasNamedProperty(env, object, propertyName)) {
355         return false;
356     }
357     napi_value value = GetNamedProperty(env, object, propertyName);
358     bool ret = false;
359     NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), false);
360     return ret;
361 }
362 
SetBooleanProperty(napi_env env,napi_value object,const std::string & name,bool value)363 void SetBooleanProperty(napi_env env, napi_value object, const std::string &name, bool value)
364 {
365     napi_value jsValue = nullptr;
366     NAPI_CALL_RETURN_VOID(env, napi_get_boolean(env, value, &jsValue));
367     if (GetValueType(env, jsValue) != napi_boolean) {
368         return;
369     }
370 
371     napi_set_named_property(env, object, name.c_str(), jsValue);
372 }
373 
GetBoolean(napi_env env,bool value)374 napi_value GetBoolean(napi_env env, bool value)
375 {
376     napi_value jsValue = nullptr;
377     NAPI_CALL(env, napi_get_boolean(env, value, &jsValue));
378     return jsValue;
379 }
380 
GetBooleanValue(napi_env env,napi_value value)381 bool GetBooleanValue(napi_env env, napi_value value)
382 {
383     bool ret = false;
384     NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), 0);
385     return ret;
386 }
387 
388 /* define properties */
DefineProperties(napi_env env,napi_value object,const std::initializer_list<napi_property_descriptor> & properties)389 void DefineProperties(napi_env env, napi_value object,
390                       const std::initializer_list<napi_property_descriptor> &properties)
391 {
392     napi_property_descriptor descriptors[properties.size()];
393     std::copy(properties.begin(), properties.end(), descriptors);
394 
395     (void)napi_define_properties(env, object, properties.size(), descriptors);
396 }
397 
398 /* array */
CreateArray(napi_env env,size_t length)399 napi_value CreateArray(napi_env env, size_t length)
400 {
401     if (length == 0) {
402         napi_value res = nullptr;
403         NAPI_CALL(env, napi_create_array(env, &res));
404         return res;
405     }
406     napi_value res = nullptr;
407     NAPI_CALL(env, napi_create_array_with_length(env, length, &res));
408     return res;
409 }
410 
SetArrayElement(napi_env env,napi_value array,uint32_t index,napi_value value)411 void SetArrayElement(napi_env env, napi_value array, uint32_t index, napi_value value)
412 {
413     (void)napi_set_element(env, array, index, value);
414 }
415 
IsArray(napi_env env,napi_value value)416 bool IsArray(napi_env env, napi_value value)
417 {
418     bool result = false;
419     NAPI_CALL_BASE(env, napi_is_array(env, value, &result), false);
420     return result;
421 }
422 
GetArrayLength(napi_env env,napi_value arr)423 uint32_t GetArrayLength(napi_env env, napi_value arr)
424 {
425     uint32_t arrayLength = 0;
426     NAPI_CALL_BASE(env, napi_get_array_length(env, arr, &arrayLength), 0);
427     return arrayLength;
428 }
429 
GetArrayElement(napi_env env,napi_value arr,uint32_t index)430 napi_value GetArrayElement(napi_env env, napi_value arr, uint32_t index)
431 {
432     napi_value elementValue = nullptr;
433     NAPI_CALL(env, napi_get_element(env, arr, index, &elementValue));
434     return elementValue;
435 }
436 
437 /* libuv */
CreateUvQueueWork(napi_env env,void * data,void (handler)(uv_work_t *,int status))438 void CreateUvQueueWork(napi_env env, void *data, void(handler)(uv_work_t *, int status))
439 {
440     uv_loop_s *loop = nullptr;
441     if (!IsEnvValid(env)) {
442         NETMANAGER_BASE_LOGE("the env is invalid");
443         return;
444     }
445     napi_get_uv_event_loop(env, &loop);
446     if (!loop) {
447         NETMANAGER_BASE_LOGE("napi get uv event loop is null");
448         return;
449     }
450     auto work = new uv_work_t;
451     work->data = data;
452 
453     (void)uv_queue_work_with_qos(
454         loop, work, [](uv_work_t *) {}, handler, uv_qos_default);
455 }
456 
457 /* scope */
OpenScope(napi_env env)458 napi_handle_scope OpenScope(napi_env env)
459 {
460     napi_handle_scope scope = nullptr;
461     NAPI_CALL(env, napi_open_handle_scope(env, &scope));
462     return scope;
463 }
464 
CloseScope(napi_env env,napi_handle_scope scope)465 void CloseScope(napi_env env, napi_handle_scope scope)
466 {
467     (void)napi_close_handle_scope(env, scope);
468 }
469 
CreateEnumConstructor(napi_env env,napi_callback_info info)470 napi_value CreateEnumConstructor(napi_env env, napi_callback_info info)
471 {
472     napi_value thisArg = nullptr;
473     void *data = nullptr;
474     napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
475     napi_value global = nullptr;
476     napi_get_global(env, &global);
477     return thisArg;
478 }
479 
480 /* error */
CreateErrorMessage(napi_env env,int32_t errorCode,const std::string & errorMessage)481 napi_value CreateErrorMessage(napi_env env, int32_t errorCode, const std::string &errorMessage)
482 {
483     napi_value result = CreateObject(env);
484     SetNamedProperty(env, result, CODE, CreateInt32(env, errorCode));
485     SetNamedProperty(env, result, MSG, CreateStringUtf8(env, errorMessage));
486     return result;
487 }
488 
HookForEnvCleanup(void * data)489 void HookForEnvCleanup(void *data)
490 {
491     std::lock_guard<std::recursive_mutex> lock(mutexForEnv);
492     auto env = static_cast<napi_env>(data);
493     auto pos = unorderedSetEnv.find(env);
494     if (pos == unorderedSetEnv.end()) {
495         NETMANAGER_BASE_LOGE("The env is not in the unordered set");
496         return;
497     }
498     NETMANAGER_BASE_LOGD("env clean up, erase from the unordered set");
499     unorderedSetEnv.erase(pos);
500 }
501 
SetEnvValid(napi_env env)502 void SetEnvValid(napi_env env)
503 {
504     std::lock_guard<std::recursive_mutex> lock(mutexForEnv);
505     unorderedSetEnv.emplace(env);
506 }
507 
IsEnvValid(napi_env env)508 bool IsEnvValid(napi_env env)
509 {
510     std::lock_guard<std::recursive_mutex> lock(mutexForEnv);
511     auto pos = unorderedSetEnv.find(env);
512     if (pos == unorderedSetEnv.end()) {
513         return false;
514     }
515     return true;
516 }
517 } // namespace NapiUtils
518 } // namespace NetManagerStandard
519 } // namespace OHOS
520