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 <cstdlib>
19 #include <cstring>
20 #include <initializer_list>
21 #include <memory>
22 #include <algorithm>
23 #include <new>
24 #include <string>
25 #include <vector>
26
27 #include "securec.h"
28 #include "napi/native_api.h"
29 #include "napi/native_common.h"
30 #include "node_api.h"
31 #include "base_context.h"
32
33 namespace OHOS::NetStack::NapiUtils {
34 static constexpr const char *GLOBAL_JSON = "JSON";
35
36 static constexpr const char *GLOBAL_JSON_STRINGIFY = "stringify";
37
38 static constexpr const char *GLOBAL_JSON_PARSE = "parse";
39
40 static constexpr const char *CODE = "code";
41
42 static constexpr const char *MSG = "message";
43
GetValueType(napi_env env,napi_value value)44 napi_valuetype GetValueType(napi_env env, napi_value value)
45 {
46 if (value == nullptr) {
47 return napi_undefined;
48 }
49
50 napi_valuetype valueType = napi_undefined;
51 NAPI_CALL_BASE(env, napi_typeof(env, value, &valueType), napi_undefined);
52 return valueType;
53 }
54
55 /* named property */
HasNamedProperty(napi_env env,napi_value object,const std::string & propertyName)56 bool HasNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
57 {
58 if (GetValueType(env, object) != napi_object) {
59 return false;
60 }
61
62 bool hasProperty = false;
63 NAPI_CALL_BASE(env, napi_has_named_property(env, object, propertyName.c_str(), &hasProperty), false);
64 return hasProperty;
65 }
66
GetNamedProperty(napi_env env,napi_value object,const std::string & propertyName)67 napi_value GetNamedProperty(napi_env env, napi_value object, const std::string &propertyName)
68 {
69 if (GetValueType(env, object) != napi_object) {
70 return GetUndefined(env);
71 }
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 if (GetValueType(env, object) != napi_object) {
81 return;
82 }
83
84 napi_set_named_property(env, object, name.c_str(), value);
85 }
86
GetPropertyNames(napi_env env,napi_value object)87 std::vector<std::string> GetPropertyNames(napi_env env, napi_value object)
88 {
89 if (GetValueType(env, object) != napi_object) {
90 return {};
91 }
92
93 std::vector<std::string> ret;
94 napi_value names = nullptr;
95 NAPI_CALL_BASE(env, napi_get_property_names(env, object, &names), ret);
96 uint32_t length = 0;
97 NAPI_CALL_BASE(env, napi_get_array_length(env, names, &length), ret);
98 for (uint32_t index = 0; index < length; ++index) {
99 napi_value name = nullptr;
100 if (napi_get_element(env, names, index, &name) != napi_ok) {
101 continue;
102 }
103 if (GetValueType(env, name) != napi_string) {
104 continue;
105 }
106 ret.emplace_back(GetStringFromValueUtf8(env, name));
107 }
108 return ret;
109 }
110
111 /* UINT32 */
CreateUint32(napi_env env,uint32_t code)112 napi_value CreateUint32(napi_env env, uint32_t code)
113 {
114 napi_value value = nullptr;
115 if (napi_create_uint32(env, code, &value) != napi_ok) {
116 return nullptr;
117 }
118 return value;
119 }
120
121 /* UINT64 */
CreateUint64(napi_env env,uint64_t code)122 napi_value CreateUint64(napi_env env, uint64_t code)
123 {
124 napi_value value = nullptr;
125 if (napi_create_bigint_uint64(env, code, &value) != napi_ok) {
126 return nullptr;
127 }
128 return value;
129 }
130
GetUint32FromValue(napi_env env,napi_value value)131 uint32_t GetUint32FromValue(napi_env env, napi_value value)
132 {
133 if (GetValueType(env, value) != napi_number) {
134 return 0;
135 }
136
137 uint32_t ret = 0;
138 NAPI_CALL_BASE(env, napi_get_value_uint32(env, value, &ret), 0);
139 return ret;
140 }
141
GetUint32Property(napi_env env,napi_value object,const std::string & propertyName)142 uint32_t GetUint32Property(napi_env env, napi_value object, const std::string &propertyName)
143 {
144 if (!HasNamedProperty(env, object, propertyName)) {
145 return 0;
146 }
147 napi_value value = GetNamedProperty(env, object, propertyName);
148 return GetUint32FromValue(env, value);
149 }
150
SetUint32Property(napi_env env,napi_value object,const std::string & name,uint32_t value)151 void SetUint32Property(napi_env env, napi_value object, const std::string &name, uint32_t value)
152 {
153 napi_value jsValue = CreateUint32(env, value);
154 if (GetValueType(env, jsValue) != napi_number) {
155 return;
156 }
157
158 napi_set_named_property(env, object, name.c_str(), jsValue);
159 }
160
SetUint64Property(napi_env env,napi_value object,const std::string & name,uint64_t value)161 void SetUint64Property(napi_env env, napi_value object, const std::string &name, uint64_t value)
162 {
163 napi_value jsValue = CreateUint64(env, value);
164 if (GetValueType(env, jsValue) != napi_bigint) {
165 return;
166 }
167
168 napi_set_named_property(env, object, name.c_str(), jsValue);
169 }
170
171 /* INT32 */
CreateInt32(napi_env env,int32_t code)172 napi_value CreateInt32(napi_env env, int32_t code)
173 {
174 napi_value value = nullptr;
175 if (napi_create_int32(env, code, &value) != napi_ok) {
176 return nullptr;
177 }
178 return value;
179 }
180
GetInt32FromValue(napi_env env,napi_value value)181 int32_t GetInt32FromValue(napi_env env, napi_value value)
182 {
183 if (GetValueType(env, value) != napi_number) {
184 return 0;
185 }
186
187 int32_t ret = 0;
188 NAPI_CALL_BASE(env, napi_get_value_int32(env, value, &ret), 0);
189 return ret;
190 }
191
GetInt32Property(napi_env env,napi_value object,const std::string & propertyName)192 int32_t GetInt32Property(napi_env env, napi_value object, const std::string &propertyName)
193 {
194 if (!HasNamedProperty(env, object, propertyName)) {
195 return 0;
196 }
197 napi_value value = GetNamedProperty(env, object, propertyName);
198 return GetInt32FromValue(env, value);
199 }
200
SetInt32Property(napi_env env,napi_value object,const std::string & name,int32_t value)201 void SetInt32Property(napi_env env, napi_value object, const std::string &name, int32_t value)
202 {
203 napi_value jsValue = CreateInt32(env, value);
204 if (GetValueType(env, jsValue) != napi_number) {
205 return;
206 }
207
208 napi_set_named_property(env, object, name.c_str(), jsValue);
209 }
210
211 /* String UTF8 */
CreateStringUtf8(napi_env env,const std::string & str)212 napi_value CreateStringUtf8(napi_env env, const std::string &str)
213 {
214 napi_value value = nullptr;
215 if (napi_create_string_utf8(env, str.c_str(), strlen(str.c_str()), &value) != napi_ok) {
216 return nullptr;
217 }
218 return value;
219 }
220
GetStringFromValueUtf8(napi_env env,napi_value value)221 std::string GetStringFromValueUtf8(napi_env env, napi_value value)
222 {
223 if (GetValueType(env, value) != napi_string) {
224 return {};
225 }
226
227 std::string result;
228 size_t stringLength = 0;
229 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, nullptr, 0, &stringLength), result);
230 if (stringLength == 0) {
231 return result;
232 }
233
234 auto deleter = [](char *s) { free(reinterpret_cast<void *>(s)); };
235 std::unique_ptr<char, decltype(deleter)> str(static_cast<char *>(malloc(stringLength + 1)), deleter);
236 if (memset_s(str.get(), stringLength + 1, 0, stringLength + 1) != EOK) {
237 return result;
238 }
239 size_t length = 0;
240 NAPI_CALL_BASE(env, napi_get_value_string_utf8(env, value, str.get(), stringLength + 1, &length), result);
241 if (length > 0) {
242 result.append(str.get(), 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
SetStringPropertyUtf8(napi_env env,napi_value object,const std::string & name,const std::string & value)256 void SetStringPropertyUtf8(napi_env env, napi_value object, const std::string &name, const std::string &value)
257 {
258 napi_value jsValue = CreateStringUtf8(env, value);
259 if (GetValueType(env, jsValue) != napi_string) {
260 return;
261 }
262 napi_set_named_property(env, object, name.c_str(), jsValue);
263 }
264
265 /* array buffer */
ValueIsArrayBuffer(napi_env env,napi_value value)266 bool ValueIsArrayBuffer(napi_env env, napi_value value)
267 {
268 if (value == nullptr) {
269 return false;
270 }
271 bool isArrayBuffer = false;
272 NAPI_CALL_BASE(env, napi_is_arraybuffer(env, value, &isArrayBuffer), false);
273 return isArrayBuffer;
274 }
275
GetInfoFromArrayBufferValue(napi_env env,napi_value value,size_t * length)276 void *GetInfoFromArrayBufferValue(napi_env env, napi_value value, size_t *length)
277 {
278 if (length == nullptr) {
279 return nullptr;
280 }
281
282 void *data = nullptr;
283 NAPI_CALL(env, napi_get_arraybuffer_info(env, value, &data, length));
284 return data;
285 }
286
CreateArrayBuffer(napi_env env,size_t length,void ** data)287 napi_value CreateArrayBuffer(napi_env env, size_t length, void **data)
288 {
289 if (length == 0) {
290 return nullptr;
291 }
292 napi_value result = nullptr;
293 NAPI_CALL(env, napi_create_arraybuffer(env, length, data, &result));
294 return result;
295 }
296
297 /* object */
CreateObject(napi_env env)298 napi_value CreateObject(napi_env env)
299 {
300 napi_value object = nullptr;
301 NAPI_CALL(env, napi_create_object(env, &object));
302 return object;
303 }
304
305 /* undefined */
GetUndefined(napi_env env)306 napi_value GetUndefined(napi_env env)
307 {
308 napi_value undefined = nullptr;
309 NAPI_CALL(env, napi_get_undefined(env, &undefined));
310 return undefined;
311 }
312
313 /* function */
CallFunction(napi_env env,napi_value recv,napi_value func,size_t argc,const napi_value * argv)314 napi_value CallFunction(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv)
315 {
316 napi_value res = nullptr;
317 NAPI_CALL(env, napi_call_function(env, recv, func, argc, argv, &res));
318 return res;
319 }
320
CreateFunction(napi_env env,const std::string & name,napi_callback func,void * arg)321 napi_value CreateFunction(napi_env env, const std::string &name, napi_callback func, void *arg)
322 {
323 napi_value res = nullptr;
324 NAPI_CALL(env, napi_create_function(env, name.c_str(), strlen(name.c_str()), func, arg, &res));
325 return res;
326 }
327
328 /* reference */
CreateReference(napi_env env,napi_value callback)329 napi_ref CreateReference(napi_env env, napi_value callback)
330 {
331 napi_ref callbackRef = nullptr;
332 NAPI_CALL(env, napi_create_reference(env, callback, 1, &callbackRef));
333 return callbackRef;
334 }
335
GetReference(napi_env env,napi_ref callbackRef)336 napi_value GetReference(napi_env env, napi_ref callbackRef)
337 {
338 napi_value callback = nullptr;
339 NAPI_CALL(env, napi_get_reference_value(env, callbackRef, &callback));
340 return callback;
341 }
342
DeleteReference(napi_env env,napi_ref callbackRef)343 void DeleteReference(napi_env env, napi_ref callbackRef)
344 {
345 (void)napi_delete_reference(env, callbackRef);
346 }
347
348 /* boolean */
GetBooleanProperty(napi_env env,napi_value object,const std::string & propertyName)349 bool GetBooleanProperty(napi_env env, napi_value object, const std::string &propertyName)
350 {
351 if (!HasNamedProperty(env, object, propertyName)) {
352 return false;
353 }
354 napi_value value = GetNamedProperty(env, object, propertyName);
355 bool ret = false;
356 NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), false);
357 return ret;
358 }
359
SetBooleanProperty(napi_env env,napi_value object,const std::string & name,bool value)360 void SetBooleanProperty(napi_env env, napi_value object, const std::string &name, bool value)
361 {
362 napi_value jsValue = nullptr;
363 NAPI_CALL_RETURN_VOID(env, napi_get_boolean(env, value, &jsValue));
364 if (GetValueType(env, jsValue) != napi_boolean) {
365 return;
366 }
367
368 napi_set_named_property(env, object, name.c_str(), jsValue);
369 }
370
GetBoolean(napi_env env,bool value)371 napi_value GetBoolean(napi_env env, bool value)
372 {
373 napi_value jsValue = nullptr;
374 NAPI_CALL(env, napi_get_boolean(env, value, &jsValue));
375 return jsValue;
376 }
377
GetBooleanFromValue(napi_env env,napi_value value)378 bool GetBooleanFromValue(napi_env env, napi_value value)
379 {
380 if (GetValueType(env, value) != napi_boolean) {
381 return GetUndefined(env);
382 }
383
384 bool ret = false;
385 NAPI_CALL_BASE(env, napi_get_value_bool(env, value, &ret), false);
386 return ret;
387 }
388
389 /* define properties */
DefineProperties(napi_env env,napi_value object,const std::initializer_list<napi_property_descriptor> & properties)390 void DefineProperties(napi_env env, napi_value object,
391 const std::initializer_list<napi_property_descriptor> &properties)
392 {
393 napi_property_descriptor descriptors[properties.size()];
394 std::copy(properties.begin(), properties.end(), descriptors);
395
396 (void)napi_define_properties(env, object, properties.size(), descriptors);
397 }
398
399 /* array */
CreateArray(napi_env env,size_t length)400 napi_value CreateArray(napi_env env, size_t length)
401 {
402 if (length == 0) {
403 napi_value res = nullptr;
404 NAPI_CALL(env, napi_create_array(env, &res));
405 return res;
406 }
407 napi_value res = nullptr;
408 NAPI_CALL(env, napi_create_array_with_length(env, length, &res));
409 return res;
410 }
411
SetArrayElement(napi_env env,napi_value array,uint32_t index,napi_value value)412 void SetArrayElement(napi_env env, napi_value array, uint32_t index, napi_value value)
413 {
414 (void)napi_set_element(env, array, index, value);
415 }
416
IsArray(napi_env env,napi_value value)417 bool IsArray(napi_env env, napi_value value)
418 {
419 bool result = false;
420 NAPI_CALL_BASE(env, napi_is_array(env, value, &result), false);
421 return result;
422 }
423
GetArrayLength(napi_env env,napi_value arr)424 uint32_t GetArrayLength(napi_env env, napi_value arr)
425 {
426 uint32_t arrayLength = 0;
427 NAPI_CALL_BASE(env, napi_get_array_length(env, arr, &arrayLength), 0);
428 return arrayLength;
429 }
430
GetArrayElement(napi_env env,napi_value arr,uint32_t index)431 napi_value GetArrayElement(napi_env env, napi_value arr, uint32_t index)
432 {
433 napi_value elementValue = nullptr;
434 NAPI_CALL(env, napi_get_element(env, arr, index, &elementValue));
435 return elementValue;
436 }
437
438 /* JSON */
JsonStringify(napi_env env,napi_value object)439 napi_value JsonStringify(napi_env env, napi_value object)
440 {
441 napi_value undefined = GetUndefined(env);
442
443 if (GetValueType(env, object) != napi_object) {
444 return undefined;
445 }
446
447 napi_value global = nullptr;
448 NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined);
449 napi_value json = nullptr;
450 NAPI_CALL_BASE(env, napi_get_named_property(env, global, GLOBAL_JSON, &json), undefined);
451 napi_value stringify = nullptr;
452 NAPI_CALL_BASE(env, napi_get_named_property(env, json, GLOBAL_JSON_STRINGIFY, &stringify), undefined);
453 if (GetValueType(env, stringify) != napi_function) {
454 return undefined;
455 }
456
457 napi_value res = nullptr;
458 napi_value argv[1] = {object};
459 NAPI_CALL_BASE(env, napi_call_function(env, json, stringify, 1, argv, &res), undefined);
460 return res;
461 }
462
JsonParse(napi_env env,napi_value str)463 napi_value JsonParse(napi_env env, napi_value str)
464 {
465 napi_value undefined = GetUndefined(env);
466
467 if (GetValueType(env, str) != napi_string) {
468 return undefined;
469 }
470
471 napi_value global = nullptr;
472 NAPI_CALL_BASE(env, napi_get_global(env, &global), undefined);
473 napi_value json = nullptr;
474 NAPI_CALL_BASE(env, napi_get_named_property(env, global, GLOBAL_JSON, &json), undefined);
475 napi_value parse = nullptr;
476 NAPI_CALL_BASE(env, napi_get_named_property(env, json, GLOBAL_JSON_PARSE, &parse), undefined);
477 if (GetValueType(env, parse) != napi_function) {
478 return undefined;
479 }
480
481 napi_value res = nullptr;
482 napi_value argv[1] = {str};
483 NAPI_CALL_BASE(env, napi_call_function(env, json, parse, 1, argv, &res), undefined);
484 return res;
485 }
486
487 /* libuv */
CreateUvQueueWork(napi_env env,void * data,void (handler)(uv_work_t *,int status))488 void CreateUvQueueWork(napi_env env, void *data, void(handler)(uv_work_t *, int status))
489 {
490 uv_loop_s *loop = nullptr;
491 NAPI_CALL_RETURN_VOID(env, napi_get_uv_event_loop(env, &loop));
492
493 auto work = new uv_work_t;
494 work->data = data;
495
496 (void)uv_queue_work(
497 loop, work, [](uv_work_t *) {}, handler);
498 }
499
500 /* scope */
OpenScope(napi_env env)501 napi_handle_scope OpenScope(napi_env env)
502 {
503 napi_handle_scope scope = nullptr;
504 NAPI_CALL(env, napi_open_handle_scope(env, &scope));
505 return scope;
506 }
507
CloseScope(napi_env env,napi_handle_scope scope)508 void CloseScope(napi_env env, napi_handle_scope scope)
509 {
510 (void)napi_close_handle_scope(env, scope);
511 }
512
CreateUvQueueWorkEnhanced(napi_env env,void * data,void (* handler)(napi_env env,napi_status status,void * data))513 void CreateUvQueueWorkEnhanced(napi_env env, void *data, void (*handler)(napi_env env, napi_status status, void *data))
514 {
515 uv_loop_s *loop = nullptr;
516 NAPI_CALL_RETURN_VOID(env, napi_get_uv_event_loop(env, &loop));
517
518 class WorkData {
519 public:
520 WorkData() = delete;
521
522 WorkData(napi_env env, void *data, void (*handler)(napi_env env, napi_status status, void *data))
523 : env_(env), data_(data), handler_(handler)
524 {
525 }
526
527 napi_env env_;
528 void *data_;
529 void (*handler_)(napi_env env, napi_status status, void *data);
530 };
531
532 auto workData = new WorkData(env, data, handler);
533
534 auto work = new uv_work_t;
535 work->data = reinterpret_cast<void *>(workData);
536
537 auto callback = [](uv_work_t *work, int status) {
538 auto workData = static_cast<WorkData *>(work->data);
539 if (!workData) {
540 delete work;
541 return;
542 }
543
544 if (!workData->env_ || !workData->data_ || !workData->handler_) {
545 delete workData;
546 delete work;
547 return;
548 }
549
550 napi_env env = workData->env_;
551 auto closeScope = [env](napi_handle_scope scope) { NapiUtils::CloseScope(env, scope); };
552 std::unique_ptr<napi_handle_scope__, decltype(closeScope)> scope(NapiUtils::OpenScope(env), closeScope);
553
554 workData->handler_(workData->env_, static_cast<napi_status>(status), workData->data_);
555
556 delete workData;
557 delete work;
558 };
559
560 (void)uv_queue_work(
561 loop, work, [](uv_work_t *) {}, callback);
562 }
563
564 /* error */
CreateErrorMessage(napi_env env,int32_t errorCode,const std::string & errorMessage)565 napi_value CreateErrorMessage(napi_env env, int32_t errorCode, const std::string &errorMessage)
566 {
567 napi_value result = nullptr;
568 result = CreateObject(env);
569 SetNamedProperty(env, result, CODE, CreateInt32(env, errorCode));
570 SetNamedProperty(env, result, MSG, CreateStringUtf8(env, errorMessage));
571 return result;
572 }
573 } // namespace OHOS::NetStack::NapiUtils
574