/* * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANI_UTILS_H #define ANI_UTILS_H #include #include #include #include #include #include #include #include template class NativeObjectWrapper { public: static ani_object Create([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_class clazz) { T* nativePtr = new T; return Wrap(env, clazz, nativePtr); } static ani_object Wrap([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_class clazz, T* nativePtr) { ani_method ctor; if (ANI_OK != env->Class_FindMethod(clazz, "", "J:V", &ctor)) { std::cerr << "Not found ''" << std::endl; ani_object nullobj = nullptr; return nullobj; } ani_object obj; if (ANI_OK != env->Object_New(clazz, ctor, &obj, reinterpret_cast(nativePtr))) { std::cerr << "Object_New failed" << std::endl; } return obj; } static T* Unwrap(ani_env *env, ani_object object, const char* propName = "nativePtr") { ani_long nativePtr; if (ANI_OK != env->Object_GetFieldByName_Long(object, propName, &nativePtr)) { return nullptr; } return reinterpret_cast(nativePtr); } static ani_status Attach(ani_env *env, ani_object object, T* nativePtr, const char* propName = "nativePtr") { return env->Object_SetFieldByName_Long(object, propName, reinterpret_cast(nativePtr)); } }; class AniStringUtils { public: static std::string ToStd(ani_env *env, ani_string ani_str) { ani_size strSize; env->String_GetUTF8Size(ani_str, &strSize); std::vector buffer(strSize + 1); // +1 for null terminator char* utf8_buffer = buffer.data(); //String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416 ani_size bytes_written = 0; env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written); utf8_buffer[bytes_written] = '\0'; std::string content = std::string(utf8_buffer); return content; } static ani_string ToAni(ani_env* env, const std::string& str) { ani_string aniStr = nullptr; if (ANI_OK != env->String_NewUTF8(str.data(), str.size(), &aniStr)) { std::cerr << "[ANI] Unsupported ANI_VERSION_1" << std::endl; return nullptr; } return aniStr; } }; class UnionAccessor { public: UnionAccessor(ani_env *env, ani_object &obj) : env_(env), obj_(obj) { } bool IsInstanceOf(const std::string& cls_name) { ani_class cls; env_->FindClass(cls_name.c_str(), &cls); ani_boolean ret; env_->Object_InstanceOf(obj_, cls, &ret); return ret; } template bool IsInstanceOfType(); template bool TryConvert(T &value); template bool TryConvertArray(std::vector &value); private: ani_env *env_; ani_object obj_; }; template<> bool UnionAccessor::IsInstanceOfType() { return IsInstanceOf("Lstd/core/Boolean;"); } template<> bool UnionAccessor::IsInstanceOfType() { return IsInstanceOf("Lstd/core/Int;"); } template<> bool UnionAccessor::IsInstanceOfType() { return IsInstanceOf("Lstd/core/Double;"); } template<> bool UnionAccessor::IsInstanceOfType() { return IsInstanceOf("Lstd/core/String;"); } template<> bool UnionAccessor::TryConvert(bool &value) { if (!IsInstanceOfType()) { return false; } ani_boolean aniValue; auto ret = env_->Object_CallMethodByName_Boolean(obj_, "unboxed", nullptr, &aniValue); if (ret != ANI_OK) { return false; } value = static_cast(aniValue); return true; } template<> bool UnionAccessor::TryConvert(int &value) { if (!IsInstanceOfType()) { return false; } ani_int aniValue; auto ret = env_->Object_CallMethodByName_Int(obj_, "unboxed", nullptr, &aniValue); if (ret != ANI_OK) { return false; } value = static_cast(aniValue); return true; } template<> bool UnionAccessor::TryConvert(double &value) { if (!IsInstanceOfType()) { return false; } ani_double aniValue; auto ret = env_->Object_CallMethodByName_Double(obj_, "unboxed", nullptr, &aniValue); if (ret != ANI_OK) { return false; } value = static_cast(aniValue); return true; } template<> bool UnionAccessor::TryConvert(std::string &value) { if (!IsInstanceOfType()) { return false; } value = AniStringUtils::ToStd(env_, static_cast(obj_)); return true; } template<> bool UnionAccessor::TryConvertArray(std::vector &value) { ani_double length; if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; return false; } for (int i = 0; i < int(length); i++) { ani_ref ref; if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { std::cerr << "Object_GetPropertyByName_Ref failed" << std::endl; return false; } ani_boolean val; if (ANI_OK != env_->Object_CallMethodByName_Boolean(static_cast(ref), "unboxed", nullptr, &val)) { std::cerr << "Object_CallMethodByName_Double unbox failed" << std::endl; return false; } value.push_back(static_cast(val)); } return true; } template<> bool UnionAccessor::TryConvertArray(std::vector &value) { ani_double length; if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; return false; } for (int i = 0; i < int(length); i++) { ani_ref ref; if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { std::cerr << "Object_GetPropertyByName_Ref failed" << std::endl; return false; } ani_int intValue; if (ANI_OK != env_->Object_CallMethodByName_Int(static_cast(ref), "unboxed", nullptr, &intValue)) { std::cerr << "Object_CallMethodByName_Double unbox failed" << std::endl; return false; } value.push_back(static_cast(intValue)); } return true; } template<> bool UnionAccessor::TryConvertArray(std::vector &value) { ani_double length; if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; return false; } for (int i = 0; i < int(length); i++) { ani_ref ref; if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { std::cerr << "Object_GetPropertyByName_Ref failed" << std::endl; return false; } ani_double val; if (ANI_OK != env_->Object_CallMethodByName_Double(static_cast(ref), "unboxed", nullptr, &val)) { std::cerr << "Object_CallMethodByName_Double unbox failed" << std::endl; return false; } value.push_back(static_cast(val)); } return true; } template<> bool UnionAccessor::TryConvertArray(std::vector &value) { std::cout << "TryConvertArray std::vector" << std::endl; ani_ref buffer; if (ANI_OK != env_->Object_GetFieldByName_Ref(obj_, "buffer", &buffer)) { std::cout << "Object_GetFieldByName_Ref failed" << std::endl; return false; } void* data; size_t length; if (ANI_OK != env_->ArrayBuffer_GetInfo(static_cast(buffer), &data, &length)) { std::cerr << "ArrayBuffer_GetInfo failed" << std::endl; return false; } std::cout << "Length of buffer is " << length << std::endl; for (size_t i = 0; i < length; i++) { value.push_back(static_cast(data)[i]); } return true; } template<> bool UnionAccessor::TryConvertArray(std::vector &value) { ani_double length; if (ANI_OK != env_->Object_GetPropertyByName_Double(obj_, "length", &length)) { std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; return false; } for (int i = 0; i < int(length); i++) { ani_ref ref; if (ANI_OK != env_->Object_CallMethodByName_Ref(obj_, "$_get", "I:Lstd/core/Object;", &ref, (ani_int)i)) { std::cerr << "Object_GetPropertyByName_Double length failed" << std::endl; return false; } value.push_back(AniStringUtils::ToStd(env_, static_cast(ref))); } return true; } class OptionalAccessor { public: OptionalAccessor(ani_env *env, ani_object &obj) : env_(env), obj_(obj) { } bool IsUndefined() { ani_boolean isUndefined; env_->Reference_IsUndefined(obj_, &isUndefined); return isUndefined; } template std::optional Convert(); private: ani_env *env_; ani_object obj_; }; template<> std::optional OptionalAccessor::Convert() { if (IsUndefined()) { return std::nullopt; } ani_double aniValue; auto ret = env_->Object_CallMethodByName_Double(obj_, "doubleValue", nullptr, &aniValue); if (ret != ANI_OK) { return std::nullopt; } auto value = static_cast(aniValue); return value; } template<> std::optional OptionalAccessor::Convert() { if (IsUndefined()) { return std::nullopt; } ani_size strSize; env_->String_GetUTF8Size(static_cast(obj_), &strSize); std::vector buffer(strSize + 1); char* utf8_buffer = buffer.data(); ani_size bytes_written = 0; env_->String_GetUTF8(static_cast(obj_), utf8_buffer, strSize + 1, &bytes_written); utf8_buffer[bytes_written] = '\0'; std::string content = std::string(utf8_buffer); return content; } class AniLocalScopeGuard { public: AniLocalScopeGuard(ani_env *env, size_t nrRefs) : env_(env) { status_ = env_->CreateLocalScope(nrRefs); } ~AniLocalScopeGuard() { if (ANI_OK != status_) { return; } env_->DestroyLocalScope(); } bool IsStatusOK() { return ANI_OK == status_; } ani_status GetStatus() { return status_; } private: ani_env *env_ = nullptr; ani_status status_ = ANI_ERROR; }; #endif