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 #ifndef PREFERENCES_ANI_COMMON_INCLUDE_ANI_UTILS_H
17 #define PREFERENCES_ANI_COMMON_INCLUDE_ANI_UTILS_H
18
19 #include <ani.h>
20
21 #include <memory>
22 #include <mutex>
23 #include <string>
24 #include <unordered_map>
25 #include <vector>
26
27 template<typename T>
28 class NativeObjectWrapper {
29 public:
Create(ani_env * env,ani_class clazz)30 static ani_object Create([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_class clazz)
31 {
32 T* nativePtr = new (std::nothrow) T();
33 if (!nativePtr) {
34 return nullptr;
35 }
36 return Wrap(env, clazz, nativePtr);
37 }
38
Wrap(ani_env * env,ani_class clazz,T * nativePtr)39 static ani_object Wrap([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_class clazz, T* nativePtr)
40 {
41 ani_method ctor;
42 if (ANI_OK != env->Class_FindMethod(clazz, "<ctor>", "J:V", &ctor)) {
43 return nullptr;
44 }
45
46 ani_object obj = nullptr;
47 if (ANI_OK != env->Object_New(clazz, ctor, &obj, reinterpret_cast<ani_long>(nativePtr))) {
48 return nullptr;
49 }
50 return obj;
51 }
52
53 static T* Unwrap(ani_env *env, ani_object object, const char* propName = "nativePtr")
54 {
55 ani_long nativePtr;
56 if (ANI_OK != env->Object_GetFieldByName_Long(object, propName, &nativePtr)) {
57 return nullptr;
58 }
59 return reinterpret_cast<T*>(nativePtr);
60 }
61 };
62
AniStringToStdStr(ani_env * env,ani_string aniStr)63 std::string AniStringToStdStr(ani_env *env, ani_string aniStr)
64 {
65 ani_size strSize;
66 env->String_GetUTF8Size(aniStr, &strSize);
67 if (strSize == 0) {
68 return "";
69 }
70 std::vector<char> buffer(strSize + 1);
71 char* utf8Buffer = buffer.data();
72
73 ani_size bytesWritten = 0;
74 env->String_GetUTF8(aniStr, utf8Buffer, strSize + 1, &bytesWritten);
75 if (bytesWritten > strSize) {
76 bytesWritten = strSize;
77 }
78 utf8Buffer[bytesWritten] = '\0';
79 std::string content = std::string(utf8Buffer);
80 return content;
81 }
82
ANIUtils_UnionIsInstanceOf(ani_env * env,ani_object unionObj,std::string & clsName)83 bool ANIUtils_UnionIsInstanceOf(ani_env *env, ani_object unionObj, std::string& clsName)
84 {
85 ani_class cls;
86 env->FindClass(clsName.c_str(), &cls);
87
88 ani_boolean ret;
89 env->Object_InstanceOf(unionObj, cls, &ret);
90 return ret;
91 }
92
93 class UnionAccessor {
94 public:
UnionAccessor(ani_env * env,ani_object & obj)95 UnionAccessor(ani_env *env, ani_object &obj)
96 : env_(env), obj_(obj) {}
97
IsInstanceOf(std::string && clsName)98 bool IsInstanceOf(std::string&& clsName)
99 {
100 ani_class cls;
101 env_->FindClass(clsName.c_str(), &cls);
102
103 ani_boolean ret;
104 env_->Object_InstanceOf(obj_, cls, &ret);
105 return ret;
106 }
107
IsInstanceOf(std::string && clsName,ani_object obj)108 bool IsInstanceOf(std::string&& clsName, ani_object obj)
109 {
110 ani_class cls;
111 env_->FindClass(clsName.c_str(), &cls);
112
113 ani_boolean ret;
114 env_->Object_InstanceOf(obj, cls, &ret);
115 return ret;
116 }
117
118 template<typename T>
119 bool TryConvert(T &value);
120
121 private:
122 ani_env *env_;
123 ani_object obj_;
124 };
125
126 template<>
127 bool UnionAccessor::TryConvert<ani_boolean>(ani_boolean &value)
128 {
129 if (!IsInstanceOf("Lstd/core/Boolean;")) {
130 return false;
131 }
132 return ANI_OK == env_->Object_CallMethodByName_Boolean(obj_, "booleanValue", nullptr, &value);
133 }
134
135 template<>
136 bool UnionAccessor::TryConvert<ani_double>(ani_double &value)
137 {
138 if (!IsInstanceOf("Lstd/core/Double;")) {
139 return false;
140 }
141 return ANI_OK == env_->Object_CallMethodByName_Double(obj_, "unboxed", nullptr, &value);
142 }
143
144 template<>
145 bool UnionAccessor::TryConvert<std::string>(std::string &value)
146 {
147 if (!IsInstanceOf("Lstd/core/String;")) {
148 return false;
149 }
150 value = AniStringToStdStr(env_, static_cast<ani_string>(obj_));
151 return true;
152 }
153
154 template<>
155 bool UnionAccessor::TryConvert<ani_long>(ani_long &value)
156 {
157 if (!IsInstanceOf("Lescompat/BigInt;")) {
158 return false;
159 }
160 ani_class bigIntCls;
161 const char* className = "Lescompat/BigInt;";
162 if (ANI_OK != env_->FindClass(className, &bigIntCls)) {
163 return false;
164 }
165 ani_method getLongMethod;
166 if (ANI_OK != env_->Class_FindMethod(bigIntCls, "getLong", ":J", &getLongMethod)) {
167 return false;
168 }
169
170 if (ANI_OK != env_->Object_CallMethod_Long(obj_, getLongMethod, &value)) {
171 return false;
172 }
173 return true;
174 }
175
176 template<>
177 bool UnionAccessor::TryConvert<std::vector<double>>(std::vector<double> &value)
178 {
179 ani_double length;
180 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
181 return false;
182 }
183 if (length > static_cast<ani_double>(INT_MAX)) {
184 return false;
185 }
186 for (int i = 0; i < static_cast<int>(length); i++) {
187 ani_ref arrayRef;
188 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &arrayRef, (ani_int)i)) {
189 return false;
190 }
191 if (!IsInstanceOf("Lstd/core/Double;", static_cast<ani_object>(arrayRef))) {
192 return false;
193 }
194 ani_double doubleValue;
195 if (ANI_OK != env_->Object_CallMethodByName_Double(static_cast<ani_object>(arrayRef), "unboxed",
196 nullptr, &doubleValue)) {
197 return false;
198 }
199 value.push_back(static_cast<double>(doubleValue));
200 }
201 return true;
202 }
203
204 template<>
205 bool UnionAccessor::TryConvert<std::vector<std::string>>(std::vector<std::string> &value)
206 {
207 ani_double length;
208 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
209 return false;
210 }
211 for (int i = 0; i < int(length); i++) {
212 ani_ref arrayRef;
213 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &arrayRef, (ani_int)i)) {
214 return false;
215 }
216 if (!IsInstanceOf("Lstd/core/String;", static_cast<ani_object>(arrayRef))) {
217 return false;
218 }
219 value.push_back(AniStringToStdStr(env_, static_cast<ani_string>(arrayRef)));
220 }
221 return true;
222 }
223
224 template<>
225 bool UnionAccessor::TryConvert<std::vector<bool>>(std::vector<bool> &value)
226 {
227 ani_double length;
228 if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) {
229 return false;
230 }
231 for (int i = 0; i < int(length); i++) {
232 ani_ref arrayRef;
233 if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &arrayRef, (ani_int)i)) {
234 return false;
235 }
236 if (!IsInstanceOf("Lstd/core/Boolean;", static_cast<ani_object>(arrayRef))) {
237 return false;
238 }
239 ani_boolean boolValue;
240 if (ANI_OK != env_->Object_CallMethodByName_Boolean(static_cast<ani_object>(arrayRef), "unboxed", nullptr,
241 &boolValue)) {
242 return false;
243 }
244 value.push_back(static_cast<bool>(boolValue));
245 }
246 return true;
247 }
248
249 template<>
250 bool UnionAccessor::TryConvert<std::vector<uint8_t>>(std::vector<uint8_t> &value)
251 {
252 ani_ref buffer;
253 if (ANI_OK != env_->Object_GetFieldByName_Ref(obj_, "buffer", &buffer)) {
254 return false;
255 }
256 void* data;
257 size_t length;
258 if (ANI_OK != env_->ArrayBuffer_GetInfo(static_cast<ani_arraybuffer>(buffer), &data, &length)) {
259 return false;
260 }
261 for (size_t i = 0; i < length; i++) {
262 value.emplace_back(static_cast<uint8_t*>(data)[i]);
263 }
264 return true;
265 }
266 #endif // PREFERENCES_ANI_COMMON_INCLUDE_ANI_UTILS_H
267