1 /*
2  * Copyright (c) 2021-2022 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 <algorithm>
19 #include <cmath>
20 #include <cstdlib>
21 #include <cstring>
22 #include <initializer_list>
23 #include <memory>
24 #include <new>
25 #include <string>
26 #include <vector>
27 #include <map>
28 
29 #include "securec.h"
30 #include "napi/native_api.h"
31 #include "napi/native_common.h"
32 #include "node_api.h"
33 #include "base_context.h"
34 #include "cJSON.h"
35 #include "netstack_log.h"
36 
37 namespace OHOS::NetStack::NapiUtils {
38 static constexpr const char *GLOBAL_JSON = "JSON";
39 
40 static constexpr const char *GLOBAL_JSON_STRINGIFY = "stringify";
41 
42 static constexpr const char *GLOBAL_JSON_PARSE = "parse";
43 
44 static constexpr const char *CODE = "code";
45 
46 static constexpr const char *MSG = "message";
47 
48 static std::mutex g_mutex;
49 static std::mutex g_mutexForModuleId;
50 static std::map<uint64_t, std::shared_ptr<UvHandlerQueue>> g_handlerQueueMap;
51 static const char *const HTTP_UV_SYNC_QUEUE_NAME = "HTTP_UV_SYNC_QUEUE_NAME";
52 
53 static std::unordered_set<napi_env> unorderedSetEnv;
54 static std::mutex mutexForEnv;
55 
56 class WorkData {
57 public:
58     WorkData() = delete;
59 
WorkData(napi_env env,void * data,void (* handler)(napi_env env,napi_status status,void * data))60     WorkData(napi_env env, void *data, void (*handler)(napi_env env, napi_status status, void *data))
61         : env_(env), data_(data), handler_(handler)
62     {
63     }
64 
65     napi_env env_;
66     void *data_;
67     void (*handler_)(napi_env env, napi_status status, void *data);
68 };
69 
GetValueType(napi_env env,napi_value value)70 napi_valuetype GetValueType(napi_env env, napi_value value)
71 {
72     if (value == nullptr) {
73         return napi_undefined;
74     }
75 
76     napi_valuetype valueType = napi_undefined;
77     NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined);
78     return valueType;
79 }
80 
IsInstanceOf(napi_env env,napi_value object,const std::string & name)81 bool IsInstanceOf(napi_env env, napi_value object, const std::string &name)
82 {
83     if (GetValueType(env, object) != napi_object) {
84         return false;
85     }
86 
87     auto global = GetGlobal(env);
88     napi_value constructor = GetNamedProperty(env, global, name);
89     if (GetValueType(env, constructor) == napi_undefined) {
90         return false;
91     }
92 
93     bool isInstance = false;
94     NAPI_CALL_BASE(env, napi_instanceof(env, object, constructor, &isInstance), false);
95     return isInstance;
96 }
97 
98 /* named property */
HasNamedProperty(napi_env env,napi_value object,const std::string & propertyName)99 bool HasNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
100 {
101     if (GetValueType(env, object) != napi_object) {
102         return false;
103     }
104 
105     bool hasProperty = false;
106     NAPI_CALL_BASE(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty), false);
107     return hasProperty;
108 }
109 
GetNamedProperty(napi_env env,napi_value object,const std::string & propertyName)110 napi_value GetNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
111 {
112     if (GetValueType(env, object) != napi_object) {
113         return GetUndefined(env);
114     }
115 
116     napi_value value = nullptr;
117     NAPI_CALL(env, napi_get_named_property(env, object, propertyName.c_str(), &value));
118     return value;
119 }
120 
SetNamedProperty(napi_env env,napi_value object,const std::string & name,napi_value value)121 void SetNamedProperty(napi_env env, napi_value object, const std::string &name, napi_value value)
122 {
123     if (GetValueType(env, object) != napi_object) {
124         return;
125     }
126 
127     napi_set_named_property(env, object, name.c_str(), value);
128 }
129 
GetPropertyNames(napi_env env,napi_value object)130 std::vector<std::string> GetPropertyNames(napi_env env, napi_value object)
131 {
132     if (GetValueType(env, object) != napi_object) {
133         return {};
134     }
135 
136     std::vector<std::string> ret;
137     napi_value names = nullptr;
138     NAPI_CALL_BASE(env, napi_get_property_names(env, object, &names), ret);
139     uint32_t length = 0;
140     NAPI_CALL_BASE(env, napi_get_array_length(env, names, &length), ret);
141     for (uint32_t index = 0; index < length; ++index) {
142         napi_value name = nullptr;
143         if (napi_get_element(env, names, index, &name) != napi_ok) {
144             continue;
145         }
146         if (GetValueType(env, name) != napi_string) {
147             continue;
148         }
149         ret.emplace_back(GetStringFromValueUtf8(env, name));
150     }
151     return ret;
152 }
153 
154 /* UINT32 */
CreateUint32(napi_env env,uint32_t code)155 napi_value CreateUint32(napi_env env, uint32_t code)
156 {
157     napi_value value = nullptr;
158     if (napi_create_uint32(env, code, &value) != napi_ok) {
159         return nullptr;
160     }
161     return value;
162 }
163 
164 /* UINT64 */
CreateUint64(napi_env env,uint64_t code)165 napi_value CreateUint64(napi_env env, uint64_t code)
166 {
167     napi_value value = nullptr;
168     if (napi_create_bigint_uint64(env, code, &value) != napi_ok) {
169         return nullptr;
170     }
171     return value;
172 }
173 
GetInt64FromValue(napi_env env,napi_value value)174 int64_t GetInt64FromValue(napi_env env, napi_value value)
175 {
176     if (GetValueType(env, value) != napi_number) {
177         return 0;
178     }
179     int64_t ret = 0;
180     NAPI_CALL_BASE(env, napi_get_value_int64(env, value, &ret), 0);
181     return ret;
182 }
183 
GetInt64Property(napi_env env,napi_value object,const std::string & propertyName)184 int64_t GetInt64Property(napi_env env, napi_value object, const std::string &propertyName)
185 {
186     if (!HasNamedProperty(env, object, propertyName)) {
187         return 0;
188     }
189     napi_value value = GetNamedProperty(env, object, propertyName);
190     return GetInt64FromValue(env, value);
191 }
192 
GetUint32FromValue(napi_env env,napi_value value)193 uint32_t GetUint32FromValue(napi_env env, napi_value value)
194 {
195     if (GetValueType(env, value) != napi_number) {
196         return 0;
197     }
198 
199     uint32_t ret = 0;
200     NAPI_CALL_BASE(env, napi_get_value_uint32(env, value, &ret), 0);
201     return ret;
202 }
203 
GetUint32Property(napi_env env,napi_value object,const std::string & propertyName)204 uint32_t GetUint32Property(napi_env env, napi_value object, const std::string &propertyName)
205 {
206     if (!HasNamedProperty(env, object, propertyName)) {
207         return 0;
208     }
209     napi_value value = GetNamedProperty(env, object, propertyName);
210     return GetUint32FromValue(env, value);
211 }
212 
SetUint32Property(napi_env env,napi_value object,const std::string & name,uint32_t value)213 void SetUint32Property(napi_env env, napi_value object, const std::string &name, uint32_t value)
214 {
215     napi_value jsValue = CreateUint32(env, value);
216     if (GetValueType(env, jsValue) != napi_number) {
217         return;
218     }
219 
220     napi_set_named_property(env, object, name.c_str(), jsValue);
221 }
222 
SetUint64Property(napi_env env,napi_value object,const std::string & name,uint64_t value)223 void SetUint64Property(napi_env env, napi_value object, const std::string &name, uint64_t value)
224 {
225     napi_value jsValue = CreateUint64(env, value);
226     if (GetValueType(env, jsValue) != napi_bigint) {
227         return;
228     }
229 
230     napi_set_named_property(env, object, name.c_str(), jsValue);
231 }
232 
233 /* INT32 */
CreateInt32(napi_env env,int32_t code)234 napi_value CreateInt32(napi_env env, int32_t code)
235 {
236     napi_value value = nullptr;
237     if (napi_create_int32(env, code, &value) != napi_ok) {
238         return nullptr;
239     }
240     return value;
241 }
242 
GetInt32FromValue(napi_env env,napi_value value)243 int32_t GetInt32FromValue(napi_env env, napi_value value)
244 {
245     if (GetValueType(env, value) != napi_number) {
246         return 0;
247     }
248 
249     int32_t ret = 0;
250     NAPI_CALL_BASE(env, napi_get_value_int32(env, value, &ret), 0);
251     return ret;
252 }
253 
GetInt32Property(napi_env env,napi_value object,const std::string & propertyName)254 int32_t GetInt32Property(napi_env env, napi_value object, const std::string &propertyName)
255 {
256     if (!HasNamedProperty(env, object, propertyName)) {
257         return 0;
258     }
259     napi_value value = GetNamedProperty(env, object, propertyName);
260     return GetInt32FromValue(env, value);
261 }
262 
SetInt32Property(napi_env env,napi_value object,const std::string & name,int32_t value)263 void SetInt32Property(napi_env env, napi_value object, const std::string &name, int32_t value)
264 {
265     napi_value jsValue = CreateInt32(env, value);
266     if (GetValueType(env, jsValue) != napi_number) {
267         return;
268     }
269 
270     napi_set_named_property(env, object, name.c_str(), jsValue);
271 }
272 
SetDoubleProperty(napi_env env,napi_value object,const std::string & name,double value)273 void SetDoubleProperty(napi_env env, napi_value object, const std::string &name, double value)
274 {
275     napi_value jsValue;
276     if (napi_create_double(env, value, &jsValue) != napi_ok) {
277         return;
278     }
279     if (GetValueType(env, jsValue) != napi_number) {
280         return;
281     }
282     napi_set_named_property(env, object, name.c_str(), jsValue);
283 }
284 
285 /* String UTF8 */
CreateStringUtf8(napi_env env,const std::string & str)286 napi_value CreateStringUtf8(napi_env env, const std::string &str)
287 {
288     napi_value value = nullptr;
289     if (napi_create_string_utf8(env, str.c_str(), strlen(str.c_str()), &value) != napi_ok) {
290         return nullptr;
291     }
292     return value;
293 }
294 
GetStringFromValueUtf8(napi_env env,napi_value value)295 std::string GetStringFromValueUtf8(napi_env env, napi_value value)
296 {
297     if (GetValueType(env, value) != napi_string) {
298         return {};
299     }
300 
301     std::string result;
302     size_t stringLength = 0;
303     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, nullptr, 0, &stringLength), result);
304     if (stringLength == 0) {
305         return result;
306     }
307 
308     auto deleter = [](char *s) { free(reinterpret_cast<void *>(s)); };
309     std::unique_ptr<char, decltype(deleter)> str(static_cast<char *>(malloc(stringLength + 1)), deleter);
310     if (str == nullptr || memset_s(str.get(), stringLength + 1, 0, stringLength + 1) != EOK) {
311         return result;
312     }
313     size_t length = 0;
314     NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str.get(), stringLength + 1, &length), result);
315     if (length > 0) {
316         result.append(str.get(), length);
317     }
318     return result;
319 }
320 
GetStringPropertyUtf8(napi_env env,napi_value object,const std::string & propertyName)321 std::string GetStringPropertyUtf8(napi_env env, napi_value object, const std::string &propertyName)
322 {
323     if (!HasNamedProperty(env, object, propertyName)) {
324         return "";
325     }
326     napi_value value = GetNamedProperty(env, object, propertyName);
327     return GetStringFromValueUtf8(env, value);
328 }
329 
NapiValueToString(napi_env env,napi_value value)330 std::string NapiValueToString(napi_env env, napi_value value)
331 {
332     napi_status status;
333     napi_valuetype valueType;
334     status = napi_typeof(env, value, &valueType);
335     if (status != napi_ok) {
336         return "";
337     }
338     switch (valueType) {
339         case napi_undefined:
340         case napi_null:
341             break;
342         case napi_boolean:
343             bool boolValue;
344             status = napi_get_value_bool(env, value, &boolValue);
345             if (status == napi_ok) {
346                 return boolValue ? "true" : "false";
347             }
348             break;
349         case napi_number:
350             double doubleValue;
351             status = napi_get_value_double(env, value, &doubleValue);
352             if (status == napi_ok) {
353                 if (doubleValue == std::floor(doubleValue)) {
354                     return std::to_string(static_cast<int>(doubleValue));
355                 }
356                 return std::to_string(doubleValue);
357             }
358             break;
359         case napi_string:
360             return GetStringFromValueUtf8(env, value);
361         default:
362             break;
363     }
364     return "";
365 }
366 
SetStringPropertyUtf8(napi_env env,napi_value object,const std::string & name,const std::string & value)367 void SetStringPropertyUtf8(napi_env env, napi_value object, const std::string &name, const std::string &value)
368 {
369     napi_value jsValue = CreateStringUtf8(env, value);
370     if (GetValueType(env, jsValue) != napi_string) {
371         return;
372     }
373     napi_set_named_property(env, object, name.c_str(), jsValue);
374 }
375 
376 /* array buffer */
ValueIsArrayBuffer(napi_env env,napi_value value)377 bool ValueIsArrayBuffer(napi_env env, napi_value value)
378 {
379     if (value == nullptr) {
380         return false;
381     }
382     bool isArrayBuffer = false;
383     NAPI_CALL_BASE(env, napi_is_arraybuffer(env, value, &isArrayBuffer), false);
384     return isArrayBuffer;
385 }
386 
GetInfoFromArrayBufferValue(napi_env env,napi_value value,size_t * length)387 void *GetInfoFromArrayBufferValue(napi_env env, napi_value value, size_t *length)
388 {
389     if (length == nullptr) {
390         return nullptr;
391     }
392 
393     void *data = nullptr;
394     NAPI_CALL(env, napi_get_arraybuffer_info(env, value, &data, length));
395     return data;
396 }
397 
CreateArrayBuffer(napi_env env,size_t length,void ** data)398 napi_value CreateArrayBuffer(napi_env env, size_t length, void **data)
399 {
400     if (length == 0) {
401         return nullptr;
402     }
403     napi_value result = nullptr;
404     NAPI_CALL(env, napi_create_arraybuffer(env, length, data, &result));
405     return result;
406 }
407 
408 /* object */
CreateObject(napi_env env)409 napi_value CreateObject(napi_env env)
410 {
411     napi_value object = nullptr;
412     NAPI_CALL(env, napi_create_object(env, &object));
413     return object;
414 }
415 
416 /* undefined */
GetUndefined(napi_env env)417 napi_value GetUndefined(napi_env env)
418 {
419     napi_value undefined = nullptr;
420     NAPI_CALL(env, napi_get_undefined(env, &undefined));
421     return undefined;
422 }
423 
424 /* function */
CallFunction(napi_env env,napi_value recv,napi_value func,size_t argc,const napi_value * argv)425 napi_value CallFunction(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv)
426 {
427     napi_value res = nullptr;
428     NAPI_CALL(env, napi_call_function(env, recv, func, argc, argv, &res));
429     return res;
430 }
431 
CreateFunction(napi_env env,const std::string & name,napi_callback func,void * arg)432 napi_value CreateFunction(napi_env env, const std::string &name, napi_callback func, void *arg)
433 {
434     napi_value res = nullptr;
435     NAPI_CALL(env, napi_create_function(env, name.c_str(), strlen(name.c_str()), func, arg, &res));
436     return res;
437 }
438 
439 /* reference */
CreateReference(napi_env env,napi_value callback)440 napi_ref CreateReference(napi_env env, napi_value callback)
441 {
442     napi_ref callbackRef = nullptr;
443     NAPI_CALL(env, napi_create_reference(env, callback, 1, &callbackRef));
444     return callbackRef;
445 }
446 
GetReference(napi_env env,napi_ref callbackRef)447 napi_value GetReference(napi_env env, napi_ref callbackRef)
448 {
449     napi_value callback = nullptr;
450     NAPI_CALL(env, napi_get_reference_value(env, callbackRef, &callback));
451     return callback;
452 }
453 
DeleteReference(napi_env env,napi_ref callbackRef)454 void DeleteReference(napi_env env, napi_ref callbackRef)
455 {
456     (void)napi_delete_reference(env, callbackRef);
457 }
458 
459 /* boolean */
GetBooleanProperty(napi_env env,napi_value object,const std::string & propertyName)460 bool GetBooleanProperty(napi_env env, napi_value object, const std::string &propertyName)
461 {
462     if (!HasNamedProperty(env, object, propertyName)) {
463         return false;
464     }
465     napi_value value = GetNamedProperty(env, object, propertyName);
466     bool ret = false;
467     NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), false);
468     return ret;
469 }
470 
SetBooleanProperty(napi_env env,napi_value object,const std::string & name,bool value)471 void SetBooleanProperty(napi_env env, napi_value object, const std::string &name, bool value)
472 {
473     napi_value jsValue = nullptr;
474     NAPI_CALL_RETURN_VOID(env, napi_get_boolean(env, value, &jsValue));
475     if (GetValueType(env, jsValue) != napi_boolean) {
476         return;
477     }
478 
479     napi_set_named_property(env, object, name.c_str(), jsValue);
480 }
481 
GetBoolean(napi_env env,bool value)482 napi_value GetBoolean(napi_env env, bool value)
483 {
484     napi_value jsValue = nullptr;
485     NAPI_CALL(env, napi_get_boolean(env, value, &jsValue));
486     return jsValue;
487 }
488 
GetBooleanFromValue(napi_env env,napi_value value)489 bool GetBooleanFromValue(napi_env env, napi_value value)
490 {
491     if (GetValueType(env, value) != napi_boolean) {
492         return GetUndefined(env);
493     }
494 
495     bool ret = false;
496     NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), false);
497     return ret;
498 }
499 
500 /* define properties */
DefineProperties(napi_env env,napi_value object,const std::initializer_list<napi_property_descriptor> & properties)501 void DefineProperties(napi_env env, napi_value object,
502                       const std::initializer_list<napi_property_descriptor> &properties)
503 {
504     napi_property_descriptor descriptors[properties.size()];
505     std::copy(properties.begin(), properties.end(), descriptors);
506 
507     (void)napi_define_properties(env, object, properties.size(), descriptors);
508 }
509 
510 /* array */
CreateArray(napi_env env,size_t length)511 napi_value CreateArray(napi_env env, size_t length)
512 {
513     if (length == 0) {
514         napi_value res = nullptr;
515         NAPI_CALL(env, napi_create_array(env, &res));
516         return res;
517     }
518     napi_value res = nullptr;
519     NAPI_CALL(env, napi_create_array_with_length(env, length, &res));
520     return res;
521 }
522 
SetArrayElement(napi_env env,napi_value array,uint32_t index,napi_value value)523 void SetArrayElement(napi_env env, napi_value array, uint32_t index, napi_value value)
524 {
525     (void)napi_set_element(env, array, index, value);
526 }
527 
IsArray(napi_env env,napi_value value)528 bool IsArray(napi_env env, napi_value value)
529 {
530     bool result = false;
531     NAPI_CALL_BASE(env, napi_is_array(env, value, &result), false);
532     return result;
533 }
534 
SetArrayProperty(napi_env env,napi_value object,const std::string & name,napi_value value)535 void SetArrayProperty(napi_env env, napi_value object, const std::string &name, napi_value value)
536 {
537     if (!IsArray(env, value)) {
538         return;
539     }
540     napi_set_named_property(env, object, name.c_str(), value);
541 }
542 
GetArrayLength(napi_env env,napi_value arr)543 uint32_t GetArrayLength(napi_env env, napi_value arr)
544 {
545     uint32_t arrayLength = 0;
546     NAPI_CALL_BASE(env, napi_get_array_length(env, arr, &arrayLength), 0);
547     return arrayLength;
548 }
549 
GetArrayElement(napi_env env,napi_value arr,uint32_t index)550 napi_value GetArrayElement(napi_env env, napi_value arr, uint32_t index)
551 {
552     napi_value elementValue = nullptr;
553     NAPI_CALL(env, napi_get_element(env, arr, index, &elementValue));
554     return elementValue;
555 }
556 
557 /* JSON */
JsonStringify(napi_env env,napi_value object)558 napi_value JsonStringify(napi_env env, napi_value object)
559 {
560     napi_value undefined = GetUndefined(env);
561 
562     if (GetValueType(env, object) != napi_object) {
563         return undefined;
564     }
565 
566     napi_value global = nullptr;
567     NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined);
568     napi_value json = nullptr;
569     NAPI_CALL_BASE(env, napi_get_named_property(env, global, GLOBAL_JSON, &json), undefined);
570     napi_value stringify = nullptr;
571     NAPI_CALL_BASE(env, napi_get_named_property(env, json, GLOBAL_JSON_STRINGIFY, &stringify), undefined);
572     if (GetValueType(env, stringify) != napi_function) {
573         return undefined;
574     }
575 
576     napi_value res = nullptr;
577     napi_value argv[1] = {object};
578     NAPI_CALL_BASE(env, napi_call_function(env, json, stringify, 1, argv, &res), undefined);
579     return res;
580 }
581 
JsonParse(napi_env env,const std::string & inStr)582 napi_value JsonParse(napi_env env, const std::string &inStr)
583 {
584     napi_value undefined = GetUndefined(env);
585     cJSON *valueJson = cJSON_Parse(inStr.c_str());
586     if (valueJson == nullptr) {
587         NETSTACK_LOGE("cJSON_Parse error");
588         return undefined;
589     }
590     cJSON_Delete(valueJson);
591 
592     auto str = NapiUtils::CreateStringUtf8(env, inStr);
593     if (GetValueType(env, str) != napi_string) {
594         return undefined;
595     }
596 
597     napi_value global = nullptr;
598     NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined);
599     napi_value json = nullptr;
600     NAPI_CALL_BASE(env, napi_get_named_property(env, global, GLOBAL_JSON, &json), undefined);
601     napi_value parse = nullptr;
602     NAPI_CALL_BASE(env, napi_get_named_property(env, json, GLOBAL_JSON_PARSE, &parse), undefined);
603     if (GetValueType(env, parse) != napi_function) {
604         return undefined;
605     }
606 
607     napi_value res = nullptr;
608     napi_value argv[1] = {str};
609     NAPI_CALL_BASE(env, napi_call_function(env, json, parse, 1, argv, &res), undefined);
610     return res;
611 }
612 
613 /* libuv */
CreateUvQueueWork(napi_env env,void * data,void (handler)(uv_work_t *,int status))614 void CreateUvQueueWork(napi_env env, void *data, void(handler)(uv_work_t *, int status))
615 {
616     uv_loop_s *loop = nullptr;
617     if (!IsEnvValid(env)) {
618         NETSTACK_LOGE("the env is invalid");
619         return;
620     }
621     napi_get_uv_event_loop(env, &loop);
622     if (!loop) {
623         NETSTACK_LOGE("napi get uv event loop is null");
624         return;
625     }
626 
627     auto work = new uv_work_t;
628     work->data = data;
629 
630     int ret = uv_queue_work_with_qos(
631         loop, work, [](uv_work_t *) {}, handler, uv_qos_default);
632     if (ret != 0) {
633         NETSTACK_LOGE("uv_queue_work_with_qos error = %{public}d, manual delete", ret);
634         delete static_cast<UvWorkWrapper *>(work->data);
635         delete work;
636     }
637 }
638 
639 /* scope */
OpenScope(napi_env env)640 napi_handle_scope OpenScope(napi_env env)
641 {
642     napi_handle_scope scope = nullptr;
643     NAPI_CALL(env, napi_open_handle_scope(env, &scope));
644     return scope;
645 }
646 
CloseScope(napi_env env,napi_handle_scope scope)647 void CloseScope(napi_env env, napi_handle_scope scope)
648 {
649     (void)napi_close_handle_scope(env, scope);
650 }
651 
UvQueueWorkCallback(uv_work_t * work,int status)652 static void UvQueueWorkCallback(uv_work_t *work, int status)
653 {
654     auto workData = static_cast<WorkData *>(work->data);
655     if (!workData) {
656         delete work;
657         return;
658     }
659 
660     if (!workData->env_ || !workData->data_ || !workData->handler_) {
661         delete workData;
662         delete work;
663         return;
664     }
665 
666     napi_env env = workData->env_;
667     auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); };
668     std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope);
669 
670     workData->handler_(workData->env_, static_cast<napi_status>(status), workData->data_);
671 
672     delete workData;
673     delete work;
674 };
675 
CreateUvQueueWorkEnhanced(napi_env env,void * data,void (* handler)(napi_env env,napi_status status,void * data))676 void CreateUvQueueWorkEnhanced(napi_env env, void *data, void (*handler)(napi_env env, napi_status status, void *data))
677 {
678     uv_loop_s *loop = nullptr;
679     if (!IsEnvValid(env)) {
680         NETSTACK_LOGE("the env is invalid");
681         return;
682     }
683     napi_get_uv_event_loop(env, &loop);
684 
685     if (loop == nullptr) {
686         NETSTACK_LOGE("napi get uv event loop is nullptr");
687         return;
688     }
689 
690     auto workData = new WorkData(env, data, handler);
691 
692     auto work = new (std::nothrow) uv_work_t;
693     if (work == nullptr) {
694         return;
695     }
696     work->data = reinterpret_cast<void *>(workData);
697 
698     int ret = uv_queue_work_with_qos(
699         loop, work, [](uv_work_t *) {}, UvQueueWorkCallback, uv_qos_default);
700     if (ret != 0) {
701         NETSTACK_LOGE("uv_queue_work_with_qos error = %{public}d, manual release", ret);
702         delete static_cast<WorkData *>(work->data);
703         delete work;
704     }
705 }
706 
707 /* error */
CreateErrorMessage(napi_env env,int32_t errorCode,const std::string & errorMessage)708 napi_value CreateErrorMessage(napi_env env, int32_t errorCode, const std::string &errorMessage)
709 {
710     napi_value result = nullptr;
711     result = CreateObject(env);
712     SetNamedProperty(env, result, CODE, CreateInt32(env, errorCode));
713     SetNamedProperty(env, result, MSG, CreateStringUtf8(env, errorMessage));
714     return result;
715 }
716 
GetGlobal(napi_env env)717 napi_value GetGlobal(napi_env env)
718 {
719     napi_value undefined = GetUndefined(env);
720     napi_value global = nullptr;
721     NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined);
722     return global;
723 }
724 
CreateUvHandlerQueue(napi_env env)725 uint64_t CreateUvHandlerQueue(napi_env env)
726 {
727     static std::atomic<uint64_t> id = 1; // start from 1
728     uint64_t newId = 0;
729     {
730         std::lock_guard<std::mutex> lock(g_mutexForModuleId);
731         newId = id.load();
732         ++id;
733     }
734     NETSTACK_LOGI("newId = %{public}s, id = %{public}s",
735                   std::to_string(newId).c_str(), std::to_string(id).c_str());
736 
737     auto global = GetGlobal(env);
738     auto queueWrapper = CreateObject(env);
739     SetNamedProperty(env, global, HTTP_UV_SYNC_QUEUE_NAME, queueWrapper);
740     {
741         std::lock_guard lock(g_mutex);
742         g_handlerQueueMap.emplace(newId, std::make_shared<UvHandlerQueue>());
743     }
744     napi_wrap(
745         env, queueWrapper, reinterpret_cast<void *>(newId),
746         [](napi_env env, void *data, void *) {
747             auto id = reinterpret_cast<uint64_t>(data);
748             std::lock_guard lock(g_mutex);
749             g_handlerQueueMap.erase(id);
750         },
751         nullptr, nullptr);
752     return newId;
753 }
754 
GetValueFromGlobal(napi_env env,const std::string & className)755 napi_value GetValueFromGlobal(napi_env env, const std::string &className)
756 {
757     auto global = NapiUtils::GetGlobal(env);
758     if (NapiUtils::GetValueType(env, global) == napi_undefined) {
759         return GetUndefined(env);
760     }
761     return NapiUtils::GetNamedProperty(env, global, className);
762 }
763 
MakeUvCallback()764 static uv_after_work_cb MakeUvCallback()
765 {
766     return [](uv_work_t *work, int status) {
767         if (!work) {
768             return;
769         }
770         std::unique_ptr<uv_work_t> workHandle(work);
771 
772         if (!work->data) {
773             return;
774         }
775         auto env = reinterpret_cast<napi_env>(work->data);
776         if (!env) {
777             return;
778         }
779 
780         auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); };
781         std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope);
782         auto queueWrapper = NapiUtils::GetValueFromGlobal(env, HTTP_UV_SYNC_QUEUE_NAME);
783         if (!queueWrapper) {
784             return;
785         }
786         void *theId = nullptr;
787         napi_unwrap(env, queueWrapper, &theId);
788         if (!theId) { // that is why moduleId is started from 1
789             return;
790         }
791         UvHandler handler;
792         decltype(g_handlerQueueMap.end()) it;
793         {
794             std::lock_guard lock(g_mutex);
795             it = g_handlerQueueMap.find(reinterpret_cast<uint64_t>(theId));
796             if (it == g_handlerQueueMap.end()) {
797                 return;
798             }
799             handler = it->second->Pop();
800         }
801         if (handler) {
802             handler();
803         }
804     };
805 }
806 
CreateUvQueueWorkByModuleId(napi_env env,const UvHandler & handler,uint64_t id)807 void CreateUvQueueWorkByModuleId(napi_env env, const UvHandler &handler, uint64_t id)
808 {
809     uv_loop_s *loop = nullptr;
810     if (!IsEnvValid(env)) {
811         NETSTACK_LOGE("the env is invalid");
812         return;
813     }
814     napi_get_uv_event_loop(env, &loop);
815     if (!loop) {
816         return;
817     }
818     decltype(g_handlerQueueMap.end()) it;
819     {
820         std::lock_guard lock(g_mutex);
821         it = g_handlerQueueMap.find(id);
822         if (it == g_handlerQueueMap.end()) {
823             return;
824         }
825     }
826 
827     auto work = new uv_work_t;
828     work->data = env;
829     it->second->Push(handler);
830     (void)uv_queue_work_with_qos(
831         loop, work, [](uv_work_t *) {}, MakeUvCallback(), uv_qos_default);
832 }
833 
Pop()834 UvHandler UvHandlerQueue::Pop()
835 {
836     std::lock_guard lock(mutex);
837     if (empty()) {
838         return {};
839     }
840     auto s = front();
841     pop();
842     return s;
843 }
844 
Push(const UvHandler & handler)845 void UvHandlerQueue::Push(const UvHandler &handler)
846 {
847     std::lock_guard lock(mutex);
848     push(handler);
849 }
850 
HookForEnvCleanup(void * data)851 void HookForEnvCleanup(void *data)
852 {
853     std::lock_guard<std::mutex> lock(mutexForEnv);
854     auto env = static_cast<napi_env>(data);
855     auto pos = unorderedSetEnv.find(env);
856     if (pos == unorderedSetEnv.end()) {
857         NETSTACK_LOGE("The env is not in the unordered set");
858         return;
859     }
860     NETSTACK_LOGD("env clean up, erase from the unordered set");
861     unorderedSetEnv.erase(pos);
862 }
863 
SetEnvValid(napi_env env)864 void SetEnvValid(napi_env env)
865 {
866     std::lock_guard<std::mutex> lock(mutexForEnv);
867     unorderedSetEnv.emplace(env);
868 }
869 
IsEnvValid(napi_env env)870 bool IsEnvValid(napi_env env)
871 {
872     std::lock_guard<std::mutex> lock(mutexForEnv);
873     auto pos = unorderedSetEnv.find(env);
874     if (pos == unorderedSetEnv.end()) {
875         NETSTACK_LOGE("The env is not in the unordered set");
876         return false;
877     }
878     return true;
879 }
880 } // namespace OHOS::NetStack::NapiUtils
881