1/*
2 * Copyright (c) 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#include "napi_util.h"
16
17#include <unordered_map>
18
19#include "hiappevent_base.h"
20#include "hilog/log.h"
21
22#undef LOG_DOMAIN
23#define LOG_DOMAIN 0xD002D07
24
25#undef LOG_TAG
26#define LOG_TAG "NapiUtil"
27
28namespace OHOS {
29namespace HiviewDFX {
30namespace NapiUtil {
31namespace {
32const std::string DOMAIN_PROPERTY = "domain";
33const std::string NAME_PROPERTY = "name";
34const std::string EVENT_TYPE_PROPERTY = "eventType";
35const std::string PARAM_PROPERTY = "params";
36const std::string EVENT_INFOS_PROPERTY = "appEventInfos";
37}
38
39bool IsNull(const napi_env env, const napi_value value)
40{
41    return GetType(env, value) == napi_null;
42}
43
44bool IsBoolean(const napi_env env, const napi_value value)
45{
46    return GetType(env, value) == napi_boolean;
47}
48
49bool IsNumber(const napi_env env, const napi_value value)
50{
51    return GetType(env, value) == napi_number;
52}
53
54bool IsString(const napi_env env, const napi_value value)
55{
56    return GetType(env, value) == napi_string;
57}
58
59bool IsObject(const napi_env env, const napi_value value)
60{
61    return GetType(env, value) == napi_object;
62}
63
64bool IsFunction(const napi_env env, const napi_value value)
65{
66    return GetType(env, value) == napi_function;
67}
68
69bool IsArray(const napi_env env, const napi_value value)
70{
71    bool result = false;
72    if (napi_is_array(env, value, &result) != napi_ok) {
73        HILOG_ERROR(LOG_CORE, "failed to check array type");
74        return false;
75    }
76    return result;
77}
78
79bool IsArrayType(const napi_env env, const napi_value value, napi_valuetype type)
80{
81    if (!IsArray(env, value)) {
82        return false;
83    }
84    if (GetArrayLength(env, value) == 0) {
85        return true;
86    }
87    return GetArrayType(env, value) == type;
88}
89
90napi_valuetype GetType(const napi_env env, const napi_value value)
91{
92    napi_valuetype type;
93    if (napi_typeof(env, value, &type) != napi_ok) {
94        HILOG_ERROR(LOG_CORE, "failed to get value type");
95        return napi_undefined;
96    }
97    return type;
98}
99
100napi_valuetype GetArrayType(const napi_env env, const napi_value arr)
101{
102    uint32_t result = 0;
103    if (napi_get_array_length(env, arr, &result) != napi_ok) {
104        HILOG_ERROR(LOG_CORE, "failed to get the length of array");
105        return napi_undefined;
106    }
107
108    napi_valuetype type = napi_null; // note: empty array returns null type
109    for (size_t i = 0; i < result; ++i) {
110        napi_value element = nullptr;
111        if (napi_get_element(env, arr, i, &element) != napi_ok) {
112            HILOG_ERROR(LOG_CORE, "failed to get the element of array");
113            return napi_undefined;
114        }
115        if (i == 0) {
116            type = GetType(env, element);
117            continue;
118        }
119        if (type != GetType(env, element)) {
120            HILOG_ERROR(LOG_CORE, "array has different element types");
121            return napi_undefined;
122        }
123    }
124    return type;
125}
126
127uint32_t GetArrayLength(const napi_env env, const napi_value arr)
128{
129    uint32_t result = 0;
130    if (napi_get_array_length(env, arr, &result) != napi_ok) {
131        HILOG_ERROR(LOG_CORE, "failed to get the length of array");
132        return 0;
133    }
134    return result;
135}
136
137napi_value GetElement(const napi_env env, const napi_value arr, uint32_t index)
138{
139    napi_value element = nullptr;
140    if (napi_get_element(env, arr, index, &element) != napi_ok) {
141        HILOG_ERROR(LOG_CORE, "failed to get the element of array.");
142        return nullptr;
143    }
144    return element;
145}
146
147bool GetBoolean(const napi_env env, const napi_value value)
148{
149    bool bValue = false;
150    if (napi_get_value_bool(env, value, &bValue) != napi_ok) {
151        HILOG_ERROR(LOG_CORE, "failed to get bool value");
152        return false;
153    }
154    return bValue;
155}
156
157void GetBooleans(const napi_env env, const napi_value arr, std::vector<bool>& bools)
158{
159    uint32_t len = GetArrayLength(env, arr);
160    for (size_t i = 0; i < len; ++i) {
161        napi_value element = GetElement(env, arr, i);
162        if (element == nullptr) {
163            continue;
164        }
165        bools.push_back(GetBoolean(env, element));
166    }
167}
168
169int32_t GetInt32(const napi_env env, const napi_value value)
170{
171    int32_t iValue = 0;
172    if (napi_get_value_int32(env, value, &iValue) != napi_ok) {
173        HILOG_ERROR(LOG_CORE, "failed to get int32 value");
174        return 0;
175    }
176    return iValue;
177}
178
179int64_t GetInt64(const napi_env env, const napi_value value)
180{
181    int64_t iValue = 0;
182    if (napi_get_value_int64(env, value, &iValue) != napi_ok) {
183        HILOG_ERROR(LOG_CORE, "failed to get int64 value");
184        return 0;
185    }
186    return iValue;
187}
188
189void GetInt32s(const napi_env env, const napi_value arr, std::vector<int32_t>& ints)
190{
191    uint32_t len = GetArrayLength(env, arr);
192    for (size_t i = 0; i < len; ++i) {
193        napi_value element = GetElement(env, arr, i);
194        if (element == nullptr) {
195            continue;
196        }
197        ints.push_back(GetInt32(env, element));
198    }
199}
200
201double GetDouble(const napi_env env, const napi_value value)
202{
203    double dValue = 0;
204    if (napi_get_value_double(env, value, &dValue) != napi_ok) {
205        HILOG_ERROR(LOG_CORE, "failed to get double value");
206        return 0;
207    }
208    return dValue;
209}
210
211void GetDoubles(const napi_env env, const napi_value arr, std::vector<double>& doubles)
212{
213    uint32_t len = GetArrayLength(env, arr);
214    for (size_t i = 0; i < len; ++i) {
215        napi_value element = GetElement(env, arr, i);
216        if (element == nullptr) {
217            continue;
218        }
219        doubles.push_back(GetDouble(env, element));
220    }
221}
222
223std::string GetString(const napi_env env, const napi_value value)
224{
225    size_t bufsize = 0;
226    if (napi_get_value_string_utf8(env, value, nullptr, 0, &bufsize) != napi_ok) {
227        HILOG_ERROR(LOG_CORE, "failed to get string length");
228        return "";
229    }
230    std::vector<char> charVec(bufsize + 1); // 1 for '\0'
231    if (napi_get_value_string_utf8(env, value, charVec.data(), bufsize + 1, &bufsize) != napi_ok) {
232        HILOG_ERROR(LOG_CORE, "failed to get string value");
233        return "";
234    }
235    return charVec.data();
236}
237
238void GetStrings(const napi_env env, const napi_value arr, std::vector<std::string>& strs)
239{
240    uint32_t len = GetArrayLength(env, arr);
241    for (size_t i = 0; i < len; ++i) {
242        napi_value element = GetElement(env, arr, i);
243        if (element == nullptr) {
244            continue;
245        }
246        strs.push_back(GetString(env, element));
247    }
248}
249
250void GetStringsToSet(const napi_env env, const napi_value arr, std::unordered_set<std::string>& strs)
251{
252    uint32_t len = GetArrayLength(env, arr);
253    for (size_t i = 0; i < len; ++i) {
254        napi_value element = GetElement(env, arr, i);
255        if (element == nullptr) {
256            continue;
257        }
258        strs.insert(GetString(env, element));
259    }
260}
261
262bool HasProperty(const napi_env env, const napi_value object, const std::string& name)
263{
264    bool result = false;
265    if (napi_has_named_property(env, object, name.c_str(), &result) != napi_ok) {
266        HILOG_ERROR(LOG_CORE, "failed to check whether the object has the named property");
267        return false;
268    }
269    return result;
270}
271
272napi_value GetProperty(const napi_env env, const napi_value object, const std::string& name)
273{
274    if (!HasProperty(env, object, name)) {
275        return nullptr;
276    }
277    napi_value value = nullptr;
278    if (napi_get_named_property(env, object, name.c_str(), &value) != napi_ok) {
279        HILOG_ERROR(LOG_CORE, "failed to get property from object");
280        return nullptr;
281    }
282    return value;
283}
284
285void GetPropertyNames(const napi_env env, const napi_value object, std::vector<std::string>& names)
286{
287    napi_value propertyNames = nullptr;
288    if (napi_get_property_names(env, object, &propertyNames) != napi_ok) {
289        HILOG_ERROR(LOG_CORE, "failed to get property names.");
290        return;
291    }
292    uint32_t len = 0;
293    if (napi_get_array_length(env, propertyNames, &len) != napi_ok) {
294        HILOG_ERROR(LOG_CORE, "failed to get array length");
295        return;
296    }
297    for (uint32_t i = 0; i < len; ++i) {
298        napi_value element = nullptr;
299        if (napi_get_element(env, propertyNames, i, &element) != napi_ok) {
300            HILOG_ERROR(LOG_CORE, "failed to get the element of array");
301            continue;
302        }
303        names.push_back(GetString(env, element));
304    }
305}
306
307napi_value GetReferenceValue(const napi_env env, const napi_ref funcRef)
308{
309    napi_value refValue = nullptr;
310    if (napi_get_reference_value(env, funcRef, &refValue) != napi_ok) {
311        HILOG_ERROR(LOG_CORE, "failed to get reference value");
312        return nullptr;
313    }
314    return refValue;
315}
316
317size_t GetCbInfo(const napi_env env, napi_callback_info info, napi_value argv[], size_t argc)
318{
319    size_t paramNum = argc;
320    if (napi_get_cb_info(env, info, &paramNum, argv, nullptr, nullptr) != napi_ok) {
321        HILOG_ERROR(LOG_CORE, "failed to get callback info");
322        return 0;
323    }
324    return paramNum;
325}
326
327napi_ref CreateReference(const napi_env env, const napi_value func)
328{
329    napi_ref ref = nullptr;
330    if (napi_create_reference(env, func, 1, &ref) != napi_ok) { // 1 means initial reference count
331        HILOG_ERROR(LOG_CORE, "failed to create reference");
332        return nullptr;
333    }
334    return ref;
335}
336
337napi_value CreateNull(const napi_env env)
338{
339    napi_value nullValue = nullptr;
340    if (napi_get_null(env, &nullValue) != napi_ok) {
341        HILOG_ERROR(LOG_CORE, "failed to create null");
342        return nullptr;
343    }
344    return nullValue;
345}
346
347napi_value CreateUndefined(const napi_env env)
348{
349    napi_value undefinedValue = nullptr;
350    if (napi_get_undefined(env, &undefinedValue) != napi_ok) {
351        HILOG_ERROR(LOG_CORE, "failed to create undefined");
352        return nullptr;
353    }
354    return undefinedValue;
355}
356
357napi_value CreateBoolean(const napi_env env, bool bValue)
358{
359    napi_value boolValue = nullptr;
360    if (napi_get_boolean(env, bValue, &boolValue) != napi_ok) {
361        HILOG_ERROR(LOG_CORE, "failed to create boolean");
362        return nullptr;
363    }
364    return boolValue;
365}
366
367napi_value CreateInt32(const napi_env env, int32_t num)
368{
369    napi_value intValue = nullptr;
370    if (napi_create_int32(env, num, &intValue) != napi_ok) {
371        HILOG_ERROR(LOG_CORE, "failed to create int32");
372        return nullptr;
373    }
374    return intValue;
375}
376
377napi_value CreateInt64(const napi_env env, int64_t num)
378{
379    napi_value intValue = nullptr;
380    if (napi_create_int64(env, num, &intValue) != napi_ok) {
381        HILOG_ERROR(LOG_CORE, "failed to create int64");
382        return nullptr;
383    }
384    return intValue;
385}
386
387napi_value CreateString(const napi_env env, const std::string& str)
388{
389    napi_value strValue = nullptr;
390    if (napi_create_string_utf8(env, str.c_str(), NAPI_AUTO_LENGTH, &strValue) != napi_ok) {
391        HILOG_ERROR(LOG_CORE, "failed to create string");
392        return nullptr;
393    }
394    return strValue;
395}
396
397napi_value CreateStrings(const napi_env env, const std::vector<std::string>& strs)
398{
399    napi_value arr = CreateArray(env);
400    for (size_t i = 0; i < strs.size(); ++i) {
401        SetElement(env, arr, i, CreateString(env, strs[i]));
402    }
403    return arr;
404}
405
406napi_value CreateObject(const napi_env env)
407{
408    napi_value obj = nullptr;
409    if (napi_create_object(env, &obj) != napi_ok) {
410        HILOG_ERROR(LOG_CORE, "failed to create object");
411        return nullptr;
412    }
413    return obj;
414}
415
416napi_value CreateObject(const napi_env env, const std::string& key, const napi_value value)
417{
418    napi_value obj = nullptr;
419    if (napi_create_object(env, &obj) != napi_ok) {
420        HILOG_ERROR(LOG_CORE, "failed to create object");
421        return nullptr;
422    }
423    if (napi_set_named_property(env, obj, key.c_str(), value) != napi_ok) {
424        HILOG_ERROR(LOG_CORE, "failed to set property");
425        return nullptr;
426    }
427    return obj;
428}
429
430napi_value CreateArray(const napi_env env)
431{
432    napi_value arr = nullptr;
433    if (napi_create_array(env, &arr) != napi_ok) {
434        HILOG_ERROR(LOG_CORE, "failed to create array");
435        return nullptr;
436    }
437    return arr;
438}
439
440napi_value CreateDouble(const napi_env env, double dValue)
441{
442    napi_value doubleValue = nullptr;
443    if (napi_create_double(env, dValue, &doubleValue) != napi_ok) {
444        HILOG_ERROR(LOG_CORE, "failed to create double");
445        return nullptr;
446    }
447    return doubleValue;
448}
449
450void SetElement(const napi_env env, const napi_value obj, uint32_t index, const napi_value value)
451{
452    if (napi_set_element(env, obj, index, value) != napi_ok) {
453        HILOG_ERROR(LOG_CORE, "failed to set element");
454    }
455}
456
457void SetNamedProperty(const napi_env env, const napi_value obj, const std::string& key, const napi_value value)
458{
459    if (napi_set_named_property(env, obj, key.c_str(), value) != napi_ok) {
460        HILOG_ERROR(LOG_CORE, "failed to set property");
461    }
462}
463
464std::string ConvertToString(const napi_env env, const napi_value value)
465{
466    napi_valuetype type = GetType(env, value);
467    if (type == napi_undefined) {
468        return "";
469    }
470
471    std::string result = "";
472    switch (type) {
473        case napi_boolean:
474            result = GetBoolean(env, value) ? "true" : "false";
475            break;
476        case napi_number:
477            result = std::to_string(GetDouble(env, value));
478            break;
479        case napi_string:
480            result = GetString(env, value);
481            break;
482        default:
483            break;
484    }
485    return result;
486}
487
488void ThrowError(napi_env env, int code, const std::string& msg, bool isThrow)
489{
490    // no error needs to be thrown before api 9
491    if (!isThrow) {
492        return;
493    }
494
495    if (napi_throw_error(env, std::to_string(code).c_str(), msg.c_str()) != napi_ok) {
496        HILOG_ERROR(LOG_CORE, "failed to throw error, code=%{public}d, msg=%{public}s", code, msg.c_str());
497    }
498}
499
500napi_value CreateError(napi_env env, int code, const std::string& msg)
501{
502    napi_value err = nullptr;
503    if (napi_create_error(env, CreateString(env, std::to_string(code)), CreateString(env, msg), &err) != napi_ok) {
504        HILOG_ERROR(LOG_CORE, "failed to create error");
505        return nullptr;
506    }
507    return err;
508}
509
510std::string CreateErrMsg(const std::string& name)
511{
512    return "Parameter error. The " + name + " parameter is mandatory.";
513}
514
515std::string CreateErrMsg(const std::string& name, const std::string& type)
516{
517    return "Parameter error. The type of " + name + " must be " + type + ".";
518}
519std::string CreateErrMsg(const std::string& name, const napi_valuetype type)
520{
521    std::string typeStr = "";
522    switch (type) {
523        case napi_boolean:
524            typeStr = "boolean";
525            break;
526        case napi_number:
527            typeStr = "number";
528            break;
529        case napi_string:
530            typeStr = "string";
531            break;
532        case napi_object:
533            typeStr = "object";
534            break;
535        default:
536            break;
537    }
538    return CreateErrMsg(name, typeStr);
539}
540
541napi_value CreateBaseValueByJson(const napi_env env, const Json::Value& jsonValue)
542{
543    if (jsonValue.isBool()) {
544        return CreateBoolean(env, jsonValue.asBool());
545    }
546    if (jsonValue.isInt()) {
547        return CreateInt32(env, jsonValue.asInt());
548    }
549    if (jsonValue.isInt64() && jsonValue.type() != Json::ValueType::uintValue) {
550        return CreateInt64(env, jsonValue.asInt64());
551    }
552    if (jsonValue.isDouble()) {
553        return CreateDouble(env, jsonValue.asDouble());
554    }
555    if (jsonValue.isString()) {
556        return CreateString(env, jsonValue.asString());
557    }
558    return nullptr;
559}
560
561napi_value CreateValueByJson(napi_env env, const Json::Value& jsonValue)
562{
563    if (jsonValue.isArray()) {
564        napi_value array = CreateArray(env);
565        for (size_t i = 0; i < jsonValue.size(); ++i) {
566            SetElement(env, array, i, CreateValueByJson(env, jsonValue[static_cast<int>(i)]));
567        }
568        return array;
569    }
570    if (jsonValue.isObject()) {
571        napi_value obj = CreateObject(env);
572        auto eventNameList = jsonValue.getMemberNames();
573        for (auto it = eventNameList.cbegin(); it != eventNameList.cend(); ++it) {
574            auto propertyName = *it;
575            SetNamedProperty(env, obj, propertyName, CreateValueByJson(env, jsonValue[propertyName]));
576        }
577        return obj;
578    }
579    return CreateBaseValueByJson(env, jsonValue);
580}
581
582napi_value CreateValueByJsonStr(napi_env env, const std::string& jsonStr)
583{
584    Json::Value jsonValue;
585    Json::Reader reader(Json::Features::strictMode());
586    if (!reader.parse(jsonStr, jsonValue)) {
587        HILOG_ERROR(LOG_CORE, "parse event detail info failed, please check the style of json");
588        return nullptr;
589    }
590    return CreateValueByJson(env, jsonValue);
591}
592
593napi_value CreateEventInfo(napi_env env, std::shared_ptr<AppEventPack> event)
594{
595    napi_value obj = CreateObject(env);
596    SetNamedProperty(env, obj, DOMAIN_PROPERTY, CreateString(env, event->GetDomain()));
597    SetNamedProperty(env, obj, NAME_PROPERTY, CreateString(env, event->GetName()));
598    SetNamedProperty(env, obj, EVENT_TYPE_PROPERTY, CreateInt32(env, event->GetType()));
599    SetNamedProperty(env, obj, PARAM_PROPERTY, CreateValueByJsonStr(env, event->GetParamStr()));
600    return obj;
601}
602
603napi_value CreateEventInfoArray(napi_env env, const std::vector<std::shared_ptr<AppEventPack>>& events)
604{
605    napi_value arr = CreateArray(env);
606    for (size_t i = 0; i < events.size(); ++i) {
607        SetElement(env, arr, i, CreateEventInfo(env, events[i]));
608    }
609    return arr;
610}
611
612napi_value CreateEventGroups(napi_env env, const std::vector<std::shared_ptr<AppEventPack>>& events)
613{
614    std::unordered_map<std::string, std::vector<std::shared_ptr<AppEventPack>>> eventMap;
615    for (auto event : events) {
616        eventMap[event->GetName()].emplace_back(event);
617    }
618
619    napi_value eventGroups = CreateArray(env);
620    size_t index = 0;
621    for (auto it = eventMap.begin(); it != eventMap.end(); ++it) {
622        napi_value eventInfos = CreateArray(env);
623        for (size_t i = 0; i < it->second.size(); ++i) {
624            SetElement(env, eventInfos, i, CreateEventInfo(env, it->second[i]));
625        }
626        napi_value obj = CreateObject(env);
627        SetNamedProperty(env, obj, NAME_PROPERTY, CreateString(env, it->first));
628        SetNamedProperty(env, obj, EVENT_INFOS_PROPERTY, eventInfos);
629        SetElement(env, eventGroups, index, obj);
630        ++index;
631    }
632    return eventGroups;
633}
634} // namespace NapiUtil
635} // namespace HiviewDFX
636} // namespace OHOS
637