1 /*
2 * Copyright (c) 2023 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_data_utils.h"
17
18 #include "napi_queue.h"
19
20 namespace OHOS {
21 namespace UDMF {
22 constexpr int32_t STR_MAX_LENGTH = 4096;
23 constexpr size_t STR_TAIL_LENGTH = 1;
24
25 /* napi_value <-> bool */
GetValue(napi_env env,napi_value in,bool & out)26 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, bool &out)
27 {
28 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- bool");
29 return napi_get_value_bool(env, in, &out);
30 }
31
SetValue(napi_env env,const bool & in,napi_value & out)32 napi_status NapiDataUtils::SetValue(napi_env env, const bool &in, napi_value &out)
33 {
34 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> bool");
35 return napi_get_boolean(env, in, &out);
36 }
37
38 /* napi_value <-> int32_t */
GetValue(napi_env env,napi_value in,int32_t & out)39 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, int32_t &out)
40 {
41 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> int32_t");
42 return napi_get_value_int32(env, in, &out);
43 }
44
SetValue(napi_env env,const int32_t & in,napi_value & out)45 napi_status NapiDataUtils::SetValue(napi_env env, const int32_t &in, napi_value &out)
46 {
47 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- int32_t");
48 return napi_create_int32(env, in, &out);
49 }
50
51 /* napi_value <-> int64_t */
GetValue(napi_env env,napi_value in,int64_t & out)52 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, int64_t &out)
53 {
54 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> int64_t");
55 return napi_get_value_int64(env, in, &out);
56 }
57
SetValue(napi_env env,const int64_t & in,napi_value & out)58 napi_status NapiDataUtils::SetValue(napi_env env, const int64_t &in, napi_value &out)
59 {
60 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- int64_t");
61 return napi_create_int64(env, in, &out);
62 }
63
64 /* napi_value <-> float */
GetValue(napi_env env,napi_value in,float & out)65 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, float &out)
66 {
67 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> float");
68 double tmp;
69 napi_status status = napi_get_value_double(env, in, &tmp);
70 out = tmp;
71 return status;
72 }
73
SetValue(napi_env env,const float & in,napi_value & out)74 napi_status NapiDataUtils::SetValue(napi_env env, const float &in, napi_value &out)
75 {
76 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- float");
77 double tmp = in;
78 return napi_create_double(env, tmp, &out);
79 }
80
81 /* napi_value <-> double */
GetValue(napi_env env,napi_value in,double & out)82 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, double &out)
83 {
84 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> double");
85 return napi_get_value_double(env, in, &out);
86 }
87
SetValue(napi_env env,const double & in,napi_value & out)88 napi_status NapiDataUtils::SetValue(napi_env env, const double &in, napi_value &out)
89 {
90 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- double");
91 return napi_create_double(env, in, &out);
92 }
93
94 /* napi_value <-> std::string */
GetValue(napi_env env,napi_value in,std::string & out)95 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, std::string &out)
96 {
97 size_t maxLen = STR_MAX_LENGTH;
98 napi_status status = napi_get_value_string_utf8(env, in, NULL, 0, &maxLen);
99 if (maxLen == 0) {
100 GET_AND_THROW_LAST_ERROR(env);
101 return status;
102 }
103 char *buf = new (std::nothrow) char[maxLen + STR_TAIL_LENGTH];
104 if (buf != nullptr) {
105 size_t len = 0;
106 status = napi_get_value_string_utf8(env, in, buf, maxLen + STR_TAIL_LENGTH, &len);
107 if (status != napi_ok) {
108 GET_AND_THROW_LAST_ERROR(env);
109 }
110 buf[len] = 0;
111 out = std::string(buf);
112 delete[] buf;
113 } else {
114 status = napi_generic_failure;
115 }
116 return status;
117 }
118
SetValue(napi_env env,const std::string & in,napi_value & out)119 napi_status NapiDataUtils::SetValue(napi_env env, const std::string &in, napi_value &out)
120 {
121 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- std::string %{public}d", (int)in.length());
122 return napi_create_string_utf8(env, in.c_str(), in.size(), &out);
123 }
124
125 /* napi_value <-> std::vector<std::string> */
GetValue(napi_env env,napi_value in,std::vector<std::string> & out)126 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, std::vector<std::string> &out)
127 {
128 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> std::vector<std::string>");
129 bool isArray = false;
130 napi_is_array(env, in, &isArray);
131 LOG_ERROR_RETURN(isArray, "not an array", napi_invalid_arg);
132
133 uint32_t length = 0;
134 napi_status status = napi_get_array_length(env, in, &length);
135 LOG_ERROR_RETURN((status == napi_ok) && (length > 0), "get_array failed!", napi_invalid_arg);
136 for (uint32_t i = 0; i < length; ++i) {
137 napi_value item = nullptr;
138 status = napi_get_element(env, in, i, &item);
139 LOG_ERROR_RETURN((item != nullptr) && (status == napi_ok), "no element", napi_invalid_arg);
140 std::string value;
141 status = GetValue(env, item, value);
142 LOG_ERROR_RETURN(status == napi_ok, "not a string", napi_invalid_arg);
143 out.push_back(value);
144 }
145 return status;
146 }
147
SetValue(napi_env env,const std::vector<std::string> & in,napi_value & out)148 napi_status NapiDataUtils::SetValue(napi_env env, const std::vector<std::string> &in, napi_value &out)
149 {
150 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- std::vector<std::string>");
151 napi_status status = napi_create_array_with_length(env, in.size(), &out);
152 LOG_ERROR_RETURN(status == napi_ok, "create array failed!", status);
153 int index = 0;
154 for (auto &item : in) {
155 napi_value element = nullptr;
156 SetValue(env, item, element);
157 status = napi_set_element(env, out, index++, element);
158 LOG_ERROR_RETURN((status == napi_ok), "napi_set_element failed!", status);
159 }
160 return status;
161 }
162
163 /* napi_value <-> std::vector<uint8_t> */
GetValue(napi_env env,napi_value in,std::vector<uint8_t> & out)164 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, std::vector<uint8_t> &out)
165 {
166 out.clear();
167 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> std::vector<uint8_t> ");
168 napi_typedarray_type type = napi_biguint64_array;
169 size_t length = 0;
170 napi_value buffer = nullptr;
171 size_t offset = 0;
172 void *data = nullptr;
173 napi_status status = napi_get_typedarray_info(env, in, &type, &length, &data, &buffer, &offset);
174 LOG_DEBUG(UDMF_KITS_NAPI, "array type=%{public}d length=%{public}d offset=%{public}d status=%{public}d",
175 (int)type, (int)length, (int)offset, status);
176 LOG_ERROR_RETURN(status == napi_ok, "napi_get_typedarray_info failed!", napi_invalid_arg);
177 LOG_ERROR_RETURN(type == napi_uint8_array, "is not Uint8Array!", napi_invalid_arg);
178 LOG_ERROR_RETURN((length > 0) && (data != nullptr), "invalid data!", napi_invalid_arg);
179 out.assign(reinterpret_cast<uint8_t *>(data), reinterpret_cast<uint8_t *>(data) + length);
180 return status;
181 }
182
SetValue(napi_env env,const std::vector<uint8_t> & in,napi_value & out)183 napi_status NapiDataUtils::SetValue(napi_env env, const std::vector<uint8_t> &in, napi_value &out)
184 {
185 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- std::vector<uint8_t> ");
186 LOG_ERROR_RETURN(in.size() > 0, "invalid std::vector<uint8_t>", napi_invalid_arg);
187 void *data = nullptr;
188 napi_value buffer = nullptr;
189 napi_status status = napi_create_arraybuffer(env, in.size(), &data, &buffer);
190 LOG_ERROR_RETURN((status == napi_ok), "create array buffer failed!", status);
191
192 if (memcpy_s(data, in.size(), in.data(), in.size()) != EOK) {
193 LOG_ERROR(UDMF_KITS_NAPI, "memcpy_s not EOK");
194 return napi_invalid_arg;
195 }
196 status = napi_create_typedarray(env, napi_uint8_array, in.size(), buffer, 0, &out);
197 LOG_ERROR_RETURN((status == napi_ok), "napi_value <- std::vector<uint8_t> invalid value", status);
198 return status;
199 }
200
201 /* napi_value <-> std::map<std::string, int32_t> */
GetValue(napi_env env,napi_value in,std::map<std::string,int32_t> & out)202 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, std::map<std::string, int32_t> &out)
203 {
204 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> std::map<std::string, int32_t> ");
205 (void)(env);
206 (void)(in);
207 (void)(out);
208 LOG_ERROR_RETURN(false, "std::map<std::string, uint32_t> from napi_value, unsupported!", napi_invalid_arg);
209 return napi_invalid_arg;
210 }
211
SetValue(napi_env env,const std::map<std::string,int32_t> & in,napi_value & out)212 napi_status NapiDataUtils::SetValue(napi_env env, const std::map<std::string, int32_t> &in, napi_value &out)
213 {
214 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- std::map<std::string, int32_t> ");
215 napi_status status = napi_create_array_with_length(env, in.size(), &out);
216 LOG_ERROR_RETURN((status == napi_ok), "invalid object", status);
217 int index = 0;
218 for (const auto &[key, value] : in) {
219 napi_value element = nullptr;
220 napi_create_array_with_length(env, TUPLE_SIZE, &element);
221 napi_value jsKey = nullptr;
222 napi_create_string_utf8(env, key.c_str(), key.size(), &jsKey);
223 napi_set_element(env, element, TUPLE_KEY, jsKey);
224 napi_value jsValue = nullptr;
225 napi_create_int32(env, static_cast<int32_t>(value), &jsValue);
226 napi_set_element(env, element, TUPLE_VALUE, jsValue);
227 napi_set_element(env, out, index++, element);
228 }
229 return status;
230 }
231
232 /* napi_value <-> std::map<std::string, int64_t> */
GetValue(napi_env env,napi_value in,std::map<std::string,int64_t> & out)233 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, std::map<std::string, int64_t> &out)
234 {
235 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value -> std::map<std::string, int64_t> ");
236 (void)(env);
237 (void)(in);
238 (void)(out);
239 LOG_ERROR_RETURN(false, "std::map<std::string, int64_t> from napi_value, unsupported!", napi_invalid_arg);
240 return napi_invalid_arg;
241 }
242
SetValue(napi_env env,const std::map<std::string,int64_t> & in,napi_value & out)243 napi_status NapiDataUtils::SetValue(napi_env env, const std::map<std::string, int64_t> &in, napi_value &out)
244 {
245 LOG_DEBUG(UDMF_KITS_NAPI, "napi_value <- std::map<std::string, int64_t> ");
246 napi_status status = napi_create_array_with_length(env, in.size(), &out);
247 LOG_ERROR_RETURN((status == napi_ok), "invalid object", status);
248 int index = 0;
249 for (const auto &[key, value] : in) {
250 napi_value element = nullptr;
251 napi_create_array_with_length(env, TUPLE_SIZE, &element);
252 napi_value jsKey = nullptr;
253 napi_create_string_utf8(env, key.c_str(), key.size(), &jsKey);
254 napi_set_element(env, element, TUPLE_KEY, jsKey);
255 napi_value jsValue = nullptr;
256 napi_create_int64(env, static_cast<int64_t>(value), &jsValue);
257 napi_set_element(env, element, TUPLE_VALUE, jsValue);
258 napi_set_element(env, out, index++, element);
259 }
260 return status;
261 }
262
263 /* napi_value <-> UDVariant */
GetValue(napi_env env,napi_value in,UDVariant & out)264 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, UDVariant &out)
265 {
266 napi_valuetype type = napi_undefined;
267 napi_status status = napi_typeof(env, in, &type);
268 LOG_ERROR_RETURN((status == napi_ok), "invalid type", status);
269 switch (type) {
270 case napi_boolean: {
271 bool vBool = false;
272 status = GetValue(env, in, vBool);
273 out = vBool;
274 break;
275 }
276 case napi_number: {
277 double vNum = 0.0f;
278 status = GetValue(env, in, vNum);
279 out = vNum;
280 break;
281 }
282 case napi_string: {
283 std::string vString;
284 status = GetValue(env, in, vString);
285 out = vString;
286 break;
287 }
288 case napi_object: {
289 std::vector<uint8_t> vct;
290 status = GetValue(env, in, vct);
291 out = vct;
292 break;
293 }
294 default:
295 LOG_ERROR(UDMF_KITS_NAPI,
296 "napi_value <- UDVariant not [Uint8Array | string | boolean | number] type=%{public}d", type);
297 status = napi_invalid_arg;
298 break;
299 }
300 return status;
301 }
302
SetValue(napi_env env,const UDVariant & in,napi_value & out)303 napi_status NapiDataUtils::SetValue(napi_env env, const UDVariant &in, napi_value &out)
304 {
305 auto strValue = std::get_if<std::string>(&in);
306 if (strValue != nullptr) {
307 return SetValue(env, *strValue, out);
308 }
309 auto intValue = std::get_if<int32_t>(&in);
310 if (intValue != nullptr) {
311 return SetValue(env, *intValue, out);
312 }
313 auto pUint8 = std::get_if<std::vector<uint8_t>>(&in);
314 if (pUint8 != nullptr) {
315 return SetValue(env, *pUint8, out);
316 }
317 auto boolValue = std::get_if<bool>(&in);
318 if (boolValue != nullptr) {
319 return SetValue(env, *boolValue, out);
320 }
321 auto dblValue = std::get_if<double>(&in);
322 if (dblValue != nullptr) {
323 return SetValue(env, *dblValue, out);
324 }
325
326 LOG_ERROR(UDMF_KITS_NAPI, "napi_value <- UDVariant INVALID value type");
327 return napi_invalid_arg;
328 }
329
330 /* napi_value <-> UDDetails */
GetValue(napi_env env,napi_value in,UDDetails & out)331 napi_status NapiDataUtils::GetValue(napi_env env, napi_value in, UDDetails &out)
332 {
333 if (!IsTypeForNapiValue(env, in, napi_object)) {
334 return napi_invalid_arg;
335 }
336 napi_value jsProNameList = nullptr;
337 uint32_t jsProCount = 0;
338
339 NAPI_CALL_BASE(env, napi_get_property_names(env, in, &jsProNameList), napi_invalid_arg);
340 NAPI_CALL_BASE(env, napi_get_array_length(env, jsProNameList, &jsProCount), napi_invalid_arg);
341
342 napi_value jsProName = nullptr;
343 napi_value jsProValue = nullptr;
344 for (uint32_t index = 0; index < jsProCount; index++) {
345 NAPI_CALL_BASE(env, napi_get_element(env, jsProNameList, index, &jsProName), napi_invalid_arg);
346 if (!IsTypeForNapiValue(env, jsProName, napi_string)) {
347 return napi_invalid_arg;
348 }
349 std::string strProName;
350 GetValue(env, jsProName, strProName);
351
352 NAPI_CALL_BASE(env, napi_get_named_property(env, in, strProName.c_str(), &jsProValue), napi_invalid_arg);
353 UDVariant natValue;
354 GetValue(env, jsProValue, natValue);
355 out[strProName] = natValue;
356 }
357 return napi_ok;
358 }
359
SetValue(napi_env env,const UDDetails & in,napi_value & out)360 napi_status NapiDataUtils::SetValue(napi_env env, const UDDetails &in, napi_value &out)
361 {
362 NAPI_CALL_BASE(env, napi_create_object(env, &out), napi_invalid_arg);
363 for (std::pair<std::string, UDVariant> prop : in) {
364 napi_value jsProValue = nullptr;
365 SetValue(env, prop.second, jsProValue);
366 NAPI_CALL_BASE(env, napi_set_named_property(env, out, prop.first.c_str(), jsProValue), napi_invalid_arg);
367 }
368 return napi_ok;
369 }
370
IsTypeForNapiValue(napi_env env,napi_value param,napi_valuetype expectType)371 bool NapiDataUtils::IsTypeForNapiValue(napi_env env, napi_value param, napi_valuetype expectType)
372 {
373 napi_valuetype valueType = napi_undefined;
374
375 if (param == nullptr) {
376 return false;
377 }
378
379 if (napi_typeof(env, param, &valueType) != napi_ok) {
380 return false;
381 }
382
383 return valueType == expectType;
384 }
385
IsNull(napi_env env,napi_value value)386 bool NapiDataUtils::IsNull(napi_env env, napi_value value)
387 {
388 napi_valuetype type = napi_undefined;
389 napi_status status = napi_typeof(env, value, &type);
390 if (status == napi_ok && (type == napi_undefined || type == napi_null)) {
391 return true;
392 }
393 if (type == napi_string) {
394 size_t len;
395 napi_get_value_string_utf8(env, value, NULL, 0, &len);
396 return len == 0;
397 }
398 return false;
399 }
400
DefineClass(napi_env env,const std::string & name,const napi_property_descriptor * properties,size_t count,napi_callback newcb)401 napi_value NapiDataUtils::DefineClass(napi_env env, const std::string &name,
402 const napi_property_descriptor *properties, size_t count, napi_callback newcb)
403 {
404 // base64("data.udmf") as rootPropName, i.e. global.<root>
405 const std::string rootPropName = "ZGF0YS51ZG1m";
406 napi_value root = nullptr;
407 bool hasRoot = false;
408 napi_value global = nullptr;
409 napi_get_global(env, &global);
410 napi_has_named_property(env, global, rootPropName.c_str(), &hasRoot);
411 if (hasRoot) {
412 napi_get_named_property(env, global, rootPropName.c_str(), &root);
413 } else {
414 napi_create_object(env, &root);
415 napi_set_named_property(env, global, rootPropName.c_str(), root);
416 }
417
418 std::string propName = "constructor_of_" + name;
419 napi_value constructor = nullptr;
420 bool hasProp = false;
421 napi_has_named_property(env, root, propName.c_str(), &hasProp);
422 if (hasProp) {
423 napi_get_named_property(env, root, propName.c_str(), &constructor);
424 if (constructor != nullptr) {
425 LOG_DEBUG(UDMF_KITS_NAPI, "got data.distributeddata.%{public}s as constructor", propName.c_str());
426 return constructor;
427 }
428 hasProp = false; // no constructor.
429 }
430
431 NAPI_CALL_BASE(env,
432 napi_define_class(env, name.c_str(), name.size(), newcb, nullptr, count, properties, &constructor),
433 nullptr);
434 NAPI_ASSERT(env, constructor != nullptr, "napi_define_class failed!");
435
436 if (!hasProp) {
437 napi_set_named_property(env, root, propName.c_str(), constructor);
438 LOG_DEBUG(UDMF_KITS_NAPI, "save constructor to data.distributeddata.%{public}s", propName.c_str());
439 }
440 return constructor;
441 }
442 } // namespace UDMF
443 } // namespace OHOS
444