1 /*
2 * Copyright (c) 2025 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 #define LOG_TAG "AniUtils"
17 #include "ani_utils.h"
18 #include "asset_value.h"
19 #include "big_integer.h"
20 #include "values_bucket.h"
21 #include "logger.h"
22
23 using namespace OHOS::NativeRdb;
24 using namespace OHOS::Rdb;
25
26
27 template<>
IsInstanceOfType()28 bool UnionAccessor::IsInstanceOfType<bool>()
29 {
30 return IsInstanceOf("Lstd/core/Boolean;");
31 }
32
33 template<>
IsInstanceOfType()34 bool UnionAccessor::IsInstanceOfType<int>()
35 {
36 return IsInstanceOf("Lstd/core/Int;");
37 }
38
39 template<>
IsInstanceOfType()40 bool UnionAccessor::IsInstanceOfType<double>()
41 {
42 return IsInstanceOf("Lstd/core/Double;");
43 }
44
45 template<>
IsInstanceOfType()46 bool UnionAccessor::IsInstanceOfType<std::string>()
47 {
48 return IsInstanceOf("Lstd/core/String;");
49 }
50
51 template<>
TryConvertArray(std::vector<ani_ref> & value)52 bool UnionAccessor::TryConvertArray<ani_ref>(std::vector<ani_ref> &value)
53 {
54 ani_double length;
55 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
56 LOG_ERROR("Object_GetPropertyByName_Double length failed");
57 return false;
58 }
59 for (int i = 0; i < int(length); i++) {
60 ani_ref ref;
61 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) {
62 LOG_ERROR("Object_GetPropertyByName_Ref failed");
63 return false;
64 }
65 value.push_back(ref);
66 }
67 LOG_DEBUG("convert ref array ok.");
68 return true;
69 }
70
71 template<>
TryConvertArray(std::vector<bool> & value)72 bool UnionAccessor::TryConvertArray<bool>(std::vector<bool> &value)
73 {
74 ani_double length;
75 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
76 LOG_ERROR("Object_GetPropertyByName_Double length failed");
77 return false;
78 }
79 for (int i = 0; i < int(length); i++) {
80 ani_ref ref;
81 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) {
82 LOG_ERROR("Object_GetPropertyByName_Ref failed");
83 return false;
84 }
85 ani_boolean val;
86 if (ANI_OK != env_->Object_CallMethodByName_Boolean(static_cast<ani_object>(ref), "unboxed", nullptr, &val)) {
87 LOG_ERROR("Object_CallMethodByName_Boolean unbox failed");
88 return false;
89 }
90 value.push_back(static_cast<bool>(val));
91 }
92 LOG_DEBUG("convert bool array ok.");
93 return true;
94 }
95
96 template<>
TryConvertArray(std::vector<int> & value)97 bool UnionAccessor::TryConvertArray<int>(std::vector<int> &value)
98 {
99 ani_double length;
100 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
101 LOG_ERROR("Object_GetPropertyByName_Double length failed");
102 return false;
103 }
104 for (int i = 0; i < int(length); i++) {
105 ani_ref ref;
106 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) {
107 LOG_ERROR("Object_GetPropertyByName_Ref failed");
108 return false;
109 }
110 ani_int intValue;
111 if (ANI_OK != env_->Object_CallMethodByName_Int(static_cast<ani_object>(ref), "unboxed", nullptr, &intValue)) {
112 LOG_ERROR("Object_CallMethodByName_Int unbox failed");
113 return false;
114 }
115 value.push_back(static_cast<int>(intValue));
116 }
117 LOG_DEBUG("convert int array ok.");
118 return true;
119 }
120
121 template<>
TryConvertArray(std::vector<double> & value)122 bool UnionAccessor::TryConvertArray<double>(std::vector<double> &value)
123 {
124 ani_double length;
125 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
126 LOG_ERROR("Object_GetPropertyByName_Double length failed");
127 return false;
128 }
129 for (int i = 0; i < int(length); i++) {
130 ani_ref ref;
131 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) {
132 LOG_ERROR("Object_GetPropertyByName_Ref failed");
133 return false;
134 }
135 ani_double val;
136 if (ANI_OK != env_->Object_CallMethodByName_Double(static_cast<ani_object>(ref), "unboxed", nullptr, &val)) {
137 LOG_ERROR("Object_CallMethodByName_Double unbox failed");
138 return false;
139 }
140 value.push_back(static_cast<double>(val));
141 }
142 LOG_DEBUG("convert double array ok.");
143 return true;
144 }
145
146 template<>
TryConvertArray(std::vector<uint8_t> & value)147 bool UnionAccessor::TryConvertArray<uint8_t>(std::vector<uint8_t> &value)
148 {
149 ani_ref buffer;
150 if (ANI_OK != env_->Object_GetFieldByName_Ref(obj_, "buffer", &buffer)) {
151 LOG_ERROR("Object_GetFieldByName_Ref failed");
152 return false;
153 }
154 void* data;
155 size_t size;
156 if (ANI_OK != env_->ArrayBuffer_GetInfo(static_cast<ani_arraybuffer>(buffer), &data, &size)) {
157 LOG_ERROR("ArrayBuffer_GetInfo failed");
158 return false;
159 }
160 for (size_t i = 0; i < size; i++) {
161 value.push_back(static_cast<uint8_t*>(data)[i]);
162 }
163 LOG_DEBUG("convert uint8 array ok.");
164 return true;
165 }
166
167 template<>
TryConvertArray(std::vector<float> & value)168 bool UnionAccessor::TryConvertArray<float>(std::vector<float> &value)
169 {
170 ani_ref buffer;
171 if (ANI_OK != env_->Object_GetFieldByName_Ref(obj_, "buffer", &buffer)) {
172 LOG_ERROR("Object_GetFieldByName_Ref failed");
173 return false;
174 }
175 void* data;
176 size_t size;
177 if (ANI_OK != env_->ArrayBuffer_GetInfo(static_cast<ani_arraybuffer>(buffer), &data, &size)) {
178 LOG_ERROR("ArrayBuffer_GetInfo failed");
179 return false;
180 }
181 auto count = size / sizeof(float);
182 for (size_t i = 0; i < count; i++) {
183 value.push_back(static_cast<uint8_t*>(data)[i]);
184 }
185 LOG_DEBUG("convert float array ok.");
186 return true;
187 }
188
189 template<>
TryConvertArray(std::vector<std::string> & value)190 bool UnionAccessor::TryConvertArray<std::string>(std::vector<std::string> &value)
191 {
192 ani_double length;
193 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
194 LOG_ERROR("Object_GetPropertyByName_Double length failed");
195 return false;
196 }
197
198 for (int i = 0; i < int(length); i++) {
199 ani_ref ref;
200 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) {
201 LOG_ERROR("Object_CallMethodByName_Ref failed");
202 return false;
203 }
204 value.push_back(AniStringUtils::ToStd(env_, static_cast<ani_string>(ref)));
205 }
206 LOG_DEBUG("convert string array ok.");
207 return true;
208 }
209
210 template<>
TryConvert(int & value)211 bool UnionAccessor::TryConvert<int>(int &value)
212 {
213 if (!IsInstanceOfType<int>()) {
214 return false;
215 }
216
217 ani_int aniValue;
218 auto ret = env_->Object_CallMethodByName_Int(obj_, "unboxed", nullptr, &aniValue);
219 if (ret != ANI_OK) {
220 return false;
221 }
222 value = static_cast<int>(aniValue);
223 LOG_DEBUG("convert int ok.");
224 return true;
225 }
226
227 template<>
TryConvert(std::monostate & value)228 bool UnionAccessor::TryConvert<std::monostate>(std::monostate &value)
229 {
230 ani_boolean isNull = false;
231 auto status = env_->Reference_IsNull(static_cast<ani_ref>(obj_), &isNull);
232 if (ANI_OK == status) {
233 if (isNull) {
234 value = std::monostate();
235 LOG_DEBUG("convert null ok.");
236 return true;
237 }
238 }
239 return false;
240 }
241
242 template<>
TryConvert(int64_t & value)243 bool UnionAccessor::TryConvert<int64_t>(int64_t &value)
244 {
245 return false;
246 }
247
248 template<>
TryConvert(double & value)249 bool UnionAccessor::TryConvert<double>(double &value)
250 {
251 if (!IsInstanceOfType<double>()) {
252 return false;
253 }
254
255 ani_double aniValue;
256 auto ret = env_->Object_CallMethodByName_Double(obj_, "unboxed", nullptr, &aniValue);
257 if (ret != ANI_OK) {
258 return false;
259 }
260 value = static_cast<double>(aniValue);
261 LOG_DEBUG("convert double ok.");
262 return true;
263 }
264
265 template<>
TryConvert(std::string & value)266 bool UnionAccessor::TryConvert<std::string>(std::string &value)
267 {
268 if (!IsInstanceOfType<std::string>()) {
269 return false;
270 }
271
272 value = AniStringUtils::ToStd(env_, static_cast<ani_string>(obj_));
273 LOG_DEBUG("convert string ok.");
274 return true;
275 }
276
277 template<>
TryConvert(bool & value)278 bool UnionAccessor::TryConvert<bool>(bool &value)
279 {
280 if (!IsInstanceOfType<bool>()) {
281 return false;
282 }
283
284 ani_boolean aniValue;
285 auto ret = env_->Object_CallMethodByName_Boolean(obj_, "unboxed", nullptr, &aniValue);
286 if (ret != ANI_OK) {
287 return false;
288 }
289 value = static_cast<bool>(aniValue);
290 LOG_DEBUG("convert bool ok.");
291 return true;
292 }
293
294 template<>
TryConvert(std::vector<uint8_t> & value)295 bool UnionAccessor::TryConvert<std::vector<uint8_t>>(std::vector<uint8_t> &value)
296 {
297 if (!IsInstanceOf("Lescompat/Uint8Array;")) {
298 return false;
299 }
300 return TryConvertArray(value);
301 }
302
GetObjectRefPropertyByName(std::string clsName,const char * name,ani_ref & val)303 bool UnionAccessor::GetObjectRefPropertyByName(std::string clsName, const char *name, ani_ref &val)
304 {
305 ani_class cls;
306 env_->FindClass(clsName.c_str(), &cls);
307 ani_method getter;
308 if (ANI_OK != env_->Class_FindGetter(cls, name, &getter)) {
309 LOG_ERROR("GetObjectRefPropertyByName Class_FindGetter failed");
310 return false;
311 }
312 ani_ref ref;
313 if (ANI_OK != env_->Object_CallMethod_Ref(obj_, getter, &ref)) {
314 LOG_ERROR("GetObjectRefPropertyByName Object_CallMethod_Ref failed");
315 return false;
316 }
317 val = ref;
318 return true;
319 }
320
GetObjectStringPropertyByName(std::string clsName,const char * name,std::string & val)321 bool UnionAccessor::GetObjectStringPropertyByName(std::string clsName, const char *name, std::string &val)
322 {
323 ani_ref ref;
324 auto isOk = GetObjectRefPropertyByName(clsName, name, ref);
325 if (!isOk) {
326 LOG_ERROR("GetObjectRefPropertyByName failed");
327 return false;
328 }
329 val = AniStringUtils::ToStd(env_, static_cast<ani_string>(ref));
330 return true;
331 }
332
GetObjectEnumValuePropertyByName(std::string clsName,const char * name,ani_int & val,bool optional)333 bool UnionAccessor::GetObjectEnumValuePropertyByName(
334 std::string clsName, const char *name, ani_int &val, bool optional)
335 {
336 ani_ref ref;
337 auto isOk = GetObjectRefPropertyByName(clsName, name, ref);
338 if (!isOk) {
339 LOG_ERROR("GetObjectRefPropertyByName failed");
340 return false;
341 }
342 if (optional) {
343 ani_boolean isUndefined;
344 auto err = env_->Reference_IsUndefined(ref, &isUndefined);
345 if (err != ANI_OK) {
346 LOG_ERROR("Reference_IsUndefined fail.");
347 return false;
348 }
349 if (isUndefined) {
350 LOG_DEBUG("Optional enum item undefined.");
351 return true;
352 }
353 }
354 ani_int enumValue;
355 auto status = env_->EnumItem_GetValue_Int(static_cast<ani_enum_item>(ref), &enumValue);
356 if (status != ANI_OK) {
357 LOG_ERROR("EnumItem_GetValue_Int failed");
358 return false;
359 }
360 val = enumValue;
361 return true;
362 }
363
364 template<>
TryConvert(AssetValue & value)365 bool UnionAccessor::TryConvert<AssetValue>(AssetValue &value)
366 {
367 std::string clsName = "L@ohos/data/relationalStore/relationalStore/Asset;";
368 if (!IsInstanceOf(clsName)) {
369 return false;
370 }
371 auto isOk = GetObjectStringPropertyByName(clsName, "name", value.name);
372 if (!isOk) {
373 return false;
374 }
375 isOk = GetObjectStringPropertyByName(clsName, "uri", value.uri);
376 if (!isOk) {
377 return false;
378 }
379 isOk = GetObjectStringPropertyByName(clsName, "path", value.path);
380 if (!isOk) {
381 return false;
382 }
383 isOk = GetObjectStringPropertyByName(clsName, "createTime", value.createTime);
384 if (!isOk) {
385 return false;
386 }
387 isOk = GetObjectStringPropertyByName(clsName, "modifyTime", value.modifyTime);
388 if (!isOk) {
389 return false;
390 }
391 isOk = GetObjectStringPropertyByName(clsName, "size", value.size);
392 if (!isOk) {
393 return false;
394 }
395 ani_int enumVal = 0;
396 isOk = GetObjectEnumValuePropertyByName(clsName, "status", enumVal, true);
397 if (!isOk) {
398 return false;
399 }
400 value.status = static_cast<AssetValue::Status>(enumVal);
401 if (value.status != AssetValue::STATUS_DELETE) {
402 value.status = AssetValue::STATUS_UNKNOWN;
403 }
404 value.hash = value.modifyTime + "_" + value.size;
405 LOG_DEBUG("convert asset ok.");
406 return true;
407 }
408
409 template<>
TryConvert(std::vector<AssetValue> & value)410 bool UnionAccessor::TryConvert<std::vector<AssetValue>>(std::vector<AssetValue> &value)
411 {
412 std::string clsName = "[L@ohos/data/relationalStore/relationalStore/Asset;";
413 if (!IsInstanceOf(clsName)) {
414 return false;
415 }
416 ani_size arrayLength;
417 auto status = env_->Array_GetLength(static_cast<ani_array>(obj_), &arrayLength);
418 if (status != ANI_OK) {
419 LOG_ERROR("Array_GetLength failed");
420 return false;
421 }
422 for (int i = 0; i < int(arrayLength); i++) {
423 ani_ref result;
424 status = env_->Array_Get_Ref(static_cast<ani_array_ref>(obj_), i, &result);
425 if (status != ANI_OK) {
426 LOG_ERROR("Array_Get_Ref failed");
427 return false;
428 }
429 ani_object asset = static_cast<ani_object>(result);
430 UnionAccessor sub(env_, asset);
431 AssetValue val;
432 auto isOk = sub.TryConvert(val);
433 if (!isOk) {
434 return false;
435 }
436 value.push_back(val);
437 }
438 LOG_DEBUG("convert assets ok.");
439 return true;
440 }
441
442 template<>
TryConvert(std::vector<float> & value)443 bool UnionAccessor::TryConvert<std::vector<float>>(std::vector<float> &value)
444 {
445 if (!IsInstanceOf("Lescompat/Float32Array;")) {
446 return false;
447 }
448 return TryConvertArray(value);
449 }
450
451 template<>
TryConvert(BigInteger & value)452 bool UnionAccessor::TryConvert<BigInteger>(BigInteger &value)
453 {
454 std::string clsName = "Lescompat/BigInt;";
455 ani_class cls;
456 auto status = env_->FindClass(clsName.c_str(), &cls);
457 if (status != ANI_OK) {
458 LOG_ERROR("FindClass failed");
459 return false;
460 }
461
462 ani_boolean ret;
463 env_->Object_InstanceOf(obj_, cls, &ret);
464 if (!ret) {
465 return false;
466 }
467
468 ani_method getLongMethod;
469 if (ANI_OK != env_->Class_FindMethod(cls, "getLong", ":J", &getLongMethod)) {
470 LOG_ERROR("Class_FindMethod failed");
471 return false;
472 }
473
474 ani_long longNum;
475 if (ANI_OK != env_->Object_CallMethod_Long(obj_, getLongMethod, &longNum)) {
476 LOG_ERROR("Object_CallMethod_Long failed");
477 return false;
478 }
479 value = BigInteger(longNum);
480 LOG_DEBUG("convert bigint ok.");
481 return true;
482 }
483
484 template<>
TryConvert(std::vector<ani_ref> & value)485 bool UnionAccessor::TryConvert<std::vector<ani_ref>>(std::vector<ani_ref> &value)
486 {
487 if (!IsInstanceOf("Lescompat/Array;")) {
488 return false;
489 }
490 return TryConvertArray(value);
491 }
492
AniIteratorNext(ani_ref interator,bool & isSuccess)493 ani_ref UnionAccessor::AniIteratorNext(ani_ref interator, bool &isSuccess)
494 {
495 ani_ref next;
496 ani_boolean done;
497 if (ANI_OK != env_->Object_CallMethodByName_Ref(static_cast<ani_object>(interator), "next", nullptr, &next)) {
498 LOG_ERROR("Failed to get next key");
499 isSuccess = false;
500 return nullptr;
501 }
502
503 if (ANI_OK != env_->Object_GetFieldByName_Boolean(static_cast<ani_object>(next), "done", &done)) {
504 LOG_ERROR("Failed to check iterator done");
505 isSuccess = false;
506 return nullptr;
507 }
508 if (done) {
509 return nullptr;
510 }
511 return next;
512 }
513
514 template<>
TryConvert(ValuesBucket & value)515 bool UnionAccessor::TryConvert<ValuesBucket>(ValuesBucket &value)
516 {
517 if (!IsInstanceOf("Lescompat/Record;")) {
518 return false;
519 }
520 ani_ref keys;
521 auto status = env_->Object_CallMethodByName_Ref(obj_, "keys", ":Lescompat/IterableIterator;", &keys);
522 if (status != ANI_OK) {
523 LOG_ERROR("Object_CallMethodByName_Ref failed");
524 }
525 bool success = true;
526 ani_ref next = AniIteratorNext(keys, success);
527 while (next) {
528 ani_ref key_value;
529 if (ANI_OK != env_->Object_GetFieldByName_Ref(static_cast<ani_object>(next), "value", &key_value)) {
530 LOG_ERROR("Failed to get key value");
531 success = false;
532 break;
533 }
534
535 ani_ref valueObj;
536 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", nullptr, &valueObj, key_value)) {
537 LOG_ERROR("Failed to get value for key");
538 success = false;
539 break;
540 }
541 ani_object recordValue = static_cast<ani_object>(valueObj);
542 UnionAccessor sub(env_, recordValue);
543 ValueObject val;
544 success = sub.TryConvertVariant(val.value);
545 if (success) {
546 value.Put(AniStringUtils::ToStd(env_, static_cast<ani_string>(key_value)), val);
547 } else {
548 LOG_ERROR("Failed to convert AssetValue");
549 break;
550 }
551 next = AniIteratorNext(keys, success);
552 }
553 return success;
554 }
555
556 template<>
Convert()557 std::optional<double> OptionalAccessor::Convert<double>()
558 {
559 if (IsUndefined()) {
560 return std::nullopt;
561 }
562
563 ani_double aniValue;
564 auto ret = env_->Object_CallMethodByName_Double(obj_, "doubleValue", nullptr, &aniValue);
565 if (ret != ANI_OK) {
566 return std::nullopt;
567 }
568 auto value = static_cast<double>(aniValue);
569 return value;
570 }
571
572 template<>
Convert()573 std::optional<std::string> OptionalAccessor::Convert<std::string>()
574 {
575 if (IsUndefined()) {
576 return std::nullopt;
577 }
578
579 ani_size strSize;
580 env_->String_GetUTF8Size(static_cast<ani_string>(obj_), &strSize);
581
582 std::vector<char> buffer(strSize + 1);
583 char* utf8_buffer = buffer.data();
584
585 ani_size bytes_written = 0;
586 env_->String_GetUTF8(static_cast<ani_string>(obj_), utf8_buffer, strSize + 1, &bytes_written);
587
588 utf8_buffer[bytes_written] = '\0';
589 std::string content = std::string(utf8_buffer);
590 return content;
591 }
592
CleanNativePtr(ani_env * env,ani_object object)593 static void CleanNativePtr(ani_env *env, ani_object object)
594 {
595 if (env == nullptr) {
596 LOG_ERROR("env is nullptr.");
597 return;
598 }
599 ani_long ptr = 0;
600 if (ANI_OK != env->Object_GetFieldByName_Long(object, "targetPtr", &ptr)) {
601 LOG_ERROR("Can not get targetPtr.");
602 return;
603 }
604 delete reinterpret_cast<NativeObject *>(ptr);
605 }
606
CleanerInit(ani_env * env)607 ani_status CleanerInit(ani_env *env)
608 {
609 if (env == nullptr) {
610 LOG_ERROR("env is nullptr.");
611 return ANI_ERROR;
612 }
613
614 static const char *namespaceName = "L@ohos/data/relationalStore/relationalStore;";
615 ani_namespace ns;
616 if (ANI_OK != env->FindNamespace(namespaceName, &ns)) {
617 LOG_ERROR("Not found '%{public}s", namespaceName);
618 return ANI_ERROR;
619 }
620
621 ani_class cls;
622 const char *className = "LCleaner;";
623 if (ANI_OK != env->Namespace_FindClass(ns, className, &cls)) {
624 LOG_ERROR("Not found '%{public}s", className);
625 return ANI_ERROR;
626 }
627
628 std::array methods = {
629 ani_native_function {"clean", nullptr, reinterpret_cast<void *>(CleanNativePtr)},
630 };
631 if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
632 LOG_ERROR("Cannot bind native methods to '%{public}s", className);
633 return ANI_ERROR;
634 }
635 return ANI_OK;
636 }
637