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