• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-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 PANDA_PLUGINS_ETS_ANI_GTEST_H
17 #define PANDA_PLUGINS_ETS_ANI_GTEST_H
18 
19 #include <gtest/gtest.h>
20 #include <cstdlib>
21 #include <optional>
22 #include <vector>
23 
24 #include "libpandabase/macros.h"
25 #include "plugins/ets/runtime/ani/ani.h"
26 #include "plugins/ets/runtime/ani/scoped_objects_fix.h"
27 
28 namespace ark::ets::ani::testing {
29 
30 class AniTest : public ::testing::Test {
31 public:
SetUp()32     void SetUp() override
33     {
34         const char *stdlib = std::getenv("ARK_ETS_STDLIB_PATH");
35         ASSERT_NE(stdlib, nullptr);
36 
37         // Create boot-panda-files options
38         std::string bootFileString = "--ext:boot-panda-files=" + std::string(stdlib);
39         const char *abcPath = std::getenv("ANI_GTEST_ABC_PATH");
40         if (abcPath != nullptr) {
41             bootFileString += ":";
42             bootFileString += abcPath;
43         }
44 
45         ani_option bootFileOption = {bootFileString.data(), nullptr};
46 
47         std::vector<ani_option> options;
48         options.push_back(bootFileOption);
49 
50         // Add extra test-specific ANI options
51         for (auto &aniOpt : GetExtraAniOptions()) {
52             options.push_back(aniOpt);
53         }
54 
55         ani_options optionsPtr = {options.size(), options.data()};
56         ASSERT_TRUE(ANI_CreateVM(&optionsPtr, ANI_VERSION_1, &vm_) == ANI_OK);
57 
58         // Get ANI API
59         ASSERT_TRUE(vm_->GetEnv(ANI_VERSION_1, &env_) == ANI_OK) << "Cannot get ani env";
60         uint32_t aniVersion;
61         ASSERT_TRUE(env_->GetVersion(&aniVersion) == ANI_OK) << "Cannot get ani version";
62         ASSERT_TRUE(aniVersion == ANI_VERSION_1) << "Incorrect ani version";
63     }
64 
TearDown()65     void TearDown() override
66     {
67         ASSERT_TRUE(vm_->DestroyVM() == ANI_OK) << "Cannot destroy ANI VM";
68     }
69 
70     /// Call function with name `fnName` from module denoted by `moduleDescriptor`
71     template <typename R, typename... Args>
CallEtsFunction(const std::string & moduleName,const std::string & fnName,Args &&...args)72     R CallEtsFunction(const std::string &moduleName, const std::string &fnName, Args &&...args)
73     {
74         std::optional<R> result;
75         auto moduleDescriptor = "L" + moduleName + ";";
76         CallEtsFunctionImpl(&result, moduleDescriptor, fnName, std::forward<Args>(args)...);
77         if constexpr (!std::is_same_v<R, void>) {
78             return result.value();
79         }
80     }
81 
82     class NativeFunction {
83     public:
84         template <typename FuncT>
NativeFunction(const char * moduleDescriptor,const char * functionName,FuncT nativeFunction)85         NativeFunction(const char *moduleDescriptor, const char *functionName, FuncT nativeFunction)
86             : moduleDescriptor_(moduleDescriptor),
87               functionName_(functionName),
88               nativeFunction_(reinterpret_cast<void *>(nativeFunction))
89         {
90         }
91 
GetModule()92         const char *GetModule() const
93         {
94             return moduleDescriptor_;
95         }
96 
GetName()97         const char *GetName() const
98         {
99             return functionName_;
100         }
101 
GetNativePtr()102         void *GetNativePtr() const
103         {
104             return nativeFunction_;
105         }
106 
107     private:
108         const char *moduleDescriptor_ {nullptr};
109         const char *functionName_ {nullptr};
110         void *nativeFunction_ {nullptr};
111     };
112 
113     template <typename R, typename... Args>
CallEtsNativeMethod(const NativeFunction & fn,Args &&...args)114     R CallEtsNativeMethod(const NativeFunction &fn, Args &&...args)
115     {
116         std::optional<R> result;
117 
118         CallEtsNativeMethodImpl(&result, fn, std::forward<Args>(args)...);
119 
120         if constexpr (!std::is_same_v<R, void>) {
121             return result.value();
122         }
123     }
124 
GetStdString(ani_string str,std::string & result)125     void GetStdString(ani_string str, std::string &result)
126     {
127         GetStdString(env_, str, result);
128     }
129 
GetStdString(ani_env * env,ani_string str,std::string & result)130     static void GetStdString(ani_env *env, ani_string str, std::string &result)
131     {
132         ani_size sz {};
133         ASSERT_EQ(env->String_GetUTF8Size(str, &sz), ANI_OK);
134 
135         result.resize(sz + 1);
136         ASSERT_EQ(env->String_GetUTF8SubString(str, 0, sz, result.data(), result.size(), &sz), ANI_OK);
137         result.resize(sz);
138     }
139 
140     bool IsRuntimeClassInitialized(std::string_view classDescriptor, bool isClass = true)
141     {
142         PandaString desc;
143         if (isClass) {
144             desc = Mangle::ConvertDescriptor(classDescriptor, true);
145         } else {
146             desc = Mangle::ConvertDescriptor(std::string(classDescriptor) + ".ETSGLOBAL", true);
147         }
148         PandaEnv *pandaEnv = PandaEnv::FromAniEnv(env_);
149         ScopedManagedCodeFix s(pandaEnv);
150 
151         EtsClassLinker *classLinker = pandaEnv->GetEtsVM()->GetClassLinker();
152         EtsClass *klass = classLinker->GetClass(desc.c_str(), true, GetClassLinkerContext(s.GetCoroutine()));
153         ASSERT(klass);
154 
155         return klass->IsInitialized();
156     }
157 
158 protected:
GetExtraAniOptions()159     virtual std::vector<ani_option> GetExtraAniOptions()
160     {
161         return {};
162     }
163 
164 private:
GetFindModuleFailureMsg(const std::string & moduleDescriptor)165     static std::string GetFindModuleFailureMsg(const std::string &moduleDescriptor)
166     {
167         std::stringstream ss;
168         ss << "Failed to find module `" << moduleDescriptor << "`.";
169         return ss.str();
170     }
171 
GetFindFunctionFailureMsg(const std::string & moduleDescriptor,const std::string & fnName)172     static std::string GetFindFunctionFailureMsg(const std::string &moduleDescriptor, const std::string &fnName)
173     {
174         std::stringstream ss;
175         ss << "Failed to find function `" << fnName << "` in `" << moduleDescriptor << "`.";
176         return ss.str();
177     }
178 
179     template <typename R, typename... Args>
CallEtsFunctionImpl(std::optional<R> * result,const std::string & moduleDescriptor,const std::string & fnName,Args &&...args)180     void CallEtsFunctionImpl(std::optional<R> *result, const std::string &moduleDescriptor, const std::string &fnName,
181                              Args &&...args)
182     {
183         ani_module mod {};
184         ASSERT_EQ(env_->FindModule(moduleDescriptor.c_str(), &mod), ANI_OK)
185             << GetFindModuleFailureMsg(moduleDescriptor);
186         ani_function fn {};
187         ASSERT_EQ(env_->Module_FindFunction(mod, fnName.c_str(), nullptr, &fn), ANI_OK)
188             << GetFindFunctionFailureMsg(moduleDescriptor, fnName);
189         DoCallFunction(result, fn, std::forward<Args>(args)...);
190     }
191 
192     template <typename R, typename... Args>
CallEtsNativeMethodImpl(std::optional<R> * result,const NativeFunction & fn,Args &&...args)193     void CallEtsNativeMethodImpl(std::optional<R> *result, const NativeFunction &fn, Args &&...args)
194     {
195         const auto moduleDescriptor = std::string("L") + fn.GetModule() + ";";
196         const auto *fnName = fn.GetName();
197         ani_module mod {};
198         ASSERT_EQ(env_->FindModule(moduleDescriptor.c_str(), &mod), ANI_OK)
199             << GetFindModuleFailureMsg(moduleDescriptor);
200         ani_function aniFn {};
201         ASSERT_EQ(env_->Module_FindFunction(mod, fnName, nullptr, &aniFn), ANI_OK)
202             << GetFindFunctionFailureMsg(moduleDescriptor, fnName);
203 
204         ani_native_function nativeFn = {fnName, nullptr, fn.GetNativePtr()};
205 
206         ASSERT_EQ(env_->Module_BindNativeFunctions(mod, &nativeFn, 1), ANI_OK)
207             << "Failed to register native function `" << fnName << "` in module " << moduleDescriptor << ".";
208 
209         DoCallFunction(result, aniFn, std::forward<Args>(args)...);
210     }
211 
212     // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg)
213     template <typename R, typename... Args>
DoCallFunction(std::optional<R> * result,ani_function fn,Args &&...args)214     void DoCallFunction(std::optional<R> *result, ani_function fn, Args &&...args)
215     {
216         std::conditional_t<std::is_same_v<R, void>, std::nullopt_t, R> value {};
217         ani_status status;
218 
219         if constexpr (std::is_same_v<R, ani_boolean>) {
220             status = env_->Function_Call_Boolean(fn, &value, std::forward<Args>(args)...);
221         } else if constexpr (std::is_same_v<R, ani_byte>) {
222             status = env_->Function_Call_Byte(fn, &value, std::forward<Args>(args)...);
223         } else if constexpr (std::is_same_v<R, ani_char>) {
224             status = env_->Function_Call_Char(fn, &value, std::forward<Args>(args)...);
225         } else if constexpr (std::is_same_v<R, ani_short>) {
226             status = env_->Function_Call_Short(fn, &value, std::forward<Args>(args)...);
227         } else if constexpr (std::is_same_v<R, ani_int>) {
228             status = env_->Function_Call_Int(fn, &value, std::forward<Args>(args)...);
229         } else if constexpr (std::is_same_v<R, ani_long>) {
230             status = env_->Function_Call_Long(fn, &value, std::forward<Args>(args)...);
231         } else if constexpr (std::is_same_v<R, ani_float>) {
232             status = env_->Function_Call_Float(fn, &value, std::forward<Args>(args)...);
233         } else if constexpr (std::is_same_v<R, ani_double>) {
234             status = env_->Function_Call_Double(fn, &value, std::forward<Args>(args)...);
235         } else if constexpr (std::is_same_v<R, void>) {
236             status = env_->Function_Call_Void(fn, std::forward<Args>(args)...);
237             value = std::nullopt;
238         } else if constexpr (std::is_same_v<R, ani_ref> || std::is_same_v<R, ani_tuple_value> ||
239                              std::is_same_v<R, ani_object>) {
240             ani_ref resultRef {};
241             status = env_->Function_Call_Ref(fn, &resultRef, std::forward<Args>(args)...);
242             value = static_cast<R>(resultRef);
243         } else {
244             enum { INCORRECT_TEMPLATE_TYPE = false };
245             static_assert(INCORRECT_TEMPLATE_TYPE, "Incorrect template type");
246         }
247 
248         if (status == ANI_PENDING_ERROR) {
249             ASSERT_EQ(env_->DescribeError(), ANI_OK);
250         }
251         ASSERT_EQ(status, ANI_OK);
252 
253         result->emplace(value);
254     }
255     // NOLINTEND(cppcoreguidelines-pro-type-vararg)
256 
GetClassLinkerContext(ark::ets::EtsCoroutine * coroutine)257     static ClassLinkerContext *GetClassLinkerContext(ark::ets::EtsCoroutine *coroutine)
258     {
259         auto stack = StackWalker::Create(coroutine);
260         if (!stack.HasFrame()) {
261             return nullptr;
262         }
263 
264         auto *method = ark::ets::EtsMethod::FromRuntimeMethod(stack.GetMethod());
265         if (method != nullptr) {
266             return method->GetClass()->GetLoadContext();
267         }
268         return nullptr;
269     }
270 
271 protected:
272     ani_env *env_ {nullptr};  // NOLINT(misc-non-private-member-variables-in-classes)
273     ani_vm *vm_ {nullptr};    // NOLINT(misc-non-private-member-variables-in-classes)
274 };
275 
276 }  // namespace ark::ets::ani::testing
277 
278 #endif  // PANDA_PLUGINS_ETS_ANI_GTEST_H
279