1 /*
2 * Copyright (c) 2024 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 <sstream>
17
18 #include <gtest/gtest.h>
19
20 #include "libabckit/include/c/abckit.h"
21 #include "libabckit/include/c/isa/isa_dynamic.h"
22 #include "libabckit/include/c/metadata_core.h"
23 #include "libabckit/include/c/ir_core.h"
24 #include "libabckit/src/logger.h"
25
26 #ifndef ABCKIT_ABC_DIR
27 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
28 #define ABCKIT_ABC_DIR ""
29 #endif
30
31 namespace {
32
33 constexpr AbckitApiVersion VERSION = ABCKIT_VERSION_RELEASE_1_0_0;
34 auto *g_impl = AbckitGetApiImpl(VERSION);
35 const AbckitInspectApi *g_implI = AbckitGetInspectApiImpl(VERSION);
36 const AbckitGraphApi *g_implG = AbckitGetGraphApiImpl(VERSION);
37 const AbckitIsaApiDynamic *g_dynG = AbckitGetIsaApiDynamicImpl(VERSION);
38
39 struct ClassInfo {
40 std::string path;
41 std::string className;
42 };
43
44 struct CapturedData {
45 void *callback = nullptr;
46 const AbckitGraphApi *gImplG = nullptr;
47 };
48
49 template <class ModuleCallBack>
EnumerateModules(const ModuleCallBack & cb,AbckitFile * file)50 inline void EnumerateModules(const ModuleCallBack &cb, AbckitFile *file)
51 {
52 LIBABCKIT_LOG_FUNC;
53
54 g_implI->fileEnumerateModules(file, (void *)(&cb), [](AbckitCoreModule *mod, void *data) {
55 const auto &cb = *((ModuleCallBack *)(data));
56 cb(mod);
57 return true;
58 });
59 }
60
61 template <class FunctionCallBack>
EnumerateModuleTopLevelFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)62 inline void EnumerateModuleTopLevelFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
63 {
64 LIBABCKIT_LOG_FUNC;
65
66 g_implI->moduleEnumerateTopLevelFunctions(mod, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
67 const auto &cb = *((FunctionCallBack *)data);
68 cb(method);
69 return true;
70 });
71 }
72
73 template <class ClassCallBack>
EnumerateModuleClasses(AbckitCoreModule * mod,const ClassCallBack & cb)74 inline void EnumerateModuleClasses(AbckitCoreModule *mod, const ClassCallBack &cb)
75 {
76 LIBABCKIT_LOG_FUNC;
77
78 g_implI->moduleEnumerateClasses(mod, (void *)(&cb), [](AbckitCoreClass *klass, void *data) {
79 const auto &cb = *((ClassCallBack *)data);
80 cb(klass);
81 return true;
82 });
83 }
84
85 template <class ImportCallBack>
EnumerateModuleImports(AbckitCoreModule * mod,const ImportCallBack & cb)86 inline void EnumerateModuleImports(AbckitCoreModule *mod, const ImportCallBack &cb)
87 {
88 LIBABCKIT_LOG_FUNC;
89
90 g_implI->moduleEnumerateImports(mod, (void *)(&cb), [](AbckitCoreImportDescriptor *i, void *data) {
91 const auto &cb = *((ImportCallBack *)(data));
92 cb(i);
93 return true;
94 });
95 }
96
GetImportDescriptors(AbckitCoreModule * mod,std::vector<std::pair<AbckitCoreImportDescriptor *,size_t>> * importDescriptors,std::vector<ClassInfo> * baseClasses)97 void GetImportDescriptors(AbckitCoreModule *mod,
98 std::vector<std::pair<AbckitCoreImportDescriptor *, size_t>> *importDescriptors,
99 std::vector<ClassInfo> *baseClasses)
100 {
101 LIBABCKIT_LOG_FUNC;
102
103 EnumerateModuleImports(mod, [&](AbckitCoreImportDescriptor *id) {
104 auto importName = g_implI->abckitStringToString(g_implI->importDescriptorGetName(id));
105 auto *importedModule = g_implI->importDescriptorGetImportedModule(id);
106 auto source = g_implI->abckitStringToString(g_implI->moduleGetName(importedModule));
107
108 for (size_t i = 0; i < baseClasses->size(); ++i) {
109 const auto baseClass = (*baseClasses)[i];
110 if (source != baseClass.path) {
111 continue;
112 }
113 if (importName == baseClass.className) {
114 importDescriptors->emplace_back(id, i);
115 }
116 }
117 });
118 }
119
120 template <class InstCallBack>
VisitBlock(AbckitBasicBlock * bb,void * data)121 inline bool VisitBlock(AbckitBasicBlock *bb, void *data)
122 {
123 auto *captured = reinterpret_cast<CapturedData *>(data);
124 const auto &cb = *reinterpret_cast<InstCallBack *>(captured->callback);
125 auto *implG = captured->gImplG;
126 for (auto *inst = implG->bbGetFirstInst(bb); inst != nullptr; inst = implG->iGetNext(inst)) {
127 cb(inst);
128 }
129 return true;
130 }
131
132 template <class InstCallBack>
EnumerateGraphInsts(AbckitGraph * graph,const InstCallBack & cb)133 inline void EnumerateGraphInsts(AbckitGraph *graph, const InstCallBack &cb)
134 {
135 LIBABCKIT_LOG_FUNC;
136
137 CapturedData captured {(void *)(&cb), g_implG};
138
139 g_implG->gVisitBlocksRpo(graph, &captured,
140 [](AbckitBasicBlock *bb, void *data) { return VisitBlock<InstCallBack>(bb, data); });
141 }
142
143 template <class InstCallBack>
EnumerateFunctionInsts(AbckitCoreFunction * func,const InstCallBack & cb)144 inline void EnumerateFunctionInsts(AbckitCoreFunction *func, const InstCallBack &cb)
145 {
146 LIBABCKIT_LOG_FUNC;
147
148 AbckitGraph *graph = g_implI->createGraphFromFunction(func);
149 EnumerateGraphInsts(graph, cb);
150 g_impl->destroyGraph(graph);
151 }
152
153 template <class UserCallBack>
EnumerateInstUsers(AbckitInst * inst,const UserCallBack & cb)154 inline void EnumerateInstUsers(AbckitInst *inst, const UserCallBack &cb)
155 {
156 LIBABCKIT_LOG_FUNC;
157
158 g_implG->iVisitUsers(inst, (void *)(&cb), [](AbckitInst *user, void *data) {
159 const auto &cb = *((UserCallBack *)data);
160 cb(user);
161 return true;
162 });
163 }
164
IsLoadApi(AbckitCoreImportDescriptor * id,AbckitInst * inst,ClassInfo & subclassInfo)165 bool IsLoadApi(AbckitCoreImportDescriptor *id, AbckitInst *inst, ClassInfo &subclassInfo)
166 {
167 LIBABCKIT_LOG_FUNC;
168
169 if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
170 return false;
171 }
172
173 if (g_dynG->iGetImportDescriptor(inst) != id) {
174 return false;
175 }
176
177 bool found = false;
178 EnumerateInstUsers(inst, [&](AbckitInst *user) {
179 if (g_dynG->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_DEFINECLASSWITHBUFFER) {
180 auto method = g_implG->iGetFunction(user);
181 auto klass = g_implI->functionGetParentClass(method);
182 auto module = g_implI->classGetModule(klass);
183 subclassInfo.className = g_implI->abckitStringToString(g_implI->classGetName(klass));
184 subclassInfo.path = g_implI->abckitStringToString(g_implI->moduleGetName(module));
185 found = true;
186 }
187 });
188
189 return found;
190 }
191
CollectSubClasses(AbckitCoreFunction * method,const std::vector<std::pair<AbckitCoreImportDescriptor *,size_t>> & impDescrs,std::vector<ClassInfo> * subClasses)192 void CollectSubClasses(AbckitCoreFunction *method,
193 const std::vector<std::pair<AbckitCoreImportDescriptor *, size_t>> &impDescrs,
194 std::vector<ClassInfo> *subClasses)
195 {
196 LIBABCKIT_LOG_FUNC;
197
198 EnumerateFunctionInsts(method, [&](AbckitInst *inst) {
199 for (const auto &[impDescr, idx] : impDescrs) {
200 ClassInfo classInfo;
201 if (IsLoadApi(impDescr, inst, classInfo)) {
202 subClasses->emplace_back(classInfo);
203 }
204 }
205 });
206 }
207
208 template <class MethodCallBack>
EnumerateClassMethods(AbckitCoreClass * klass,const MethodCallBack & cb)209 inline void EnumerateClassMethods(AbckitCoreClass *klass, const MethodCallBack &cb)
210 {
211 LIBABCKIT_LOG_FUNC;
212 g_implI->classEnumerateMethods(klass, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
213 const auto &cb = *((MethodCallBack *)data);
214 cb(method);
215 return true;
216 });
217 }
218
219 template <class FunctionCallBack>
EnumerateModuleFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)220 inline void EnumerateModuleFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
221 {
222 LIBABCKIT_LOG_FUNC;
223 // NOTE: currently we can only enumerate class methods and top level functions. need to update.
224 EnumerateModuleTopLevelFunctions(mod, cb);
225 EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { EnumerateClassMethods(klass, cb); });
226 }
227
IsEqualsSubClasses(const std::vector<ClassInfo> & otherSubClasses,std::vector<ClassInfo> & subClasses)228 bool IsEqualsSubClasses(const std::vector<ClassInfo> &otherSubClasses, std::vector<ClassInfo> &subClasses)
229 {
230 LIBABCKIT_LOG_FUNC;
231
232 for (auto &otherSubClass : otherSubClasses) {
233 auto iter = std::find_if(subClasses.begin(), subClasses.end(), [&otherSubClass](const ClassInfo &classInfo) {
234 return (otherSubClass.className == classInfo.className) && (otherSubClass.path == classInfo.path);
235 });
236 if (iter == subClasses.end()) {
237 return false;
238 }
239 }
240 return true;
241 }
242
243 } // namespace
244
245 namespace libabckit::test {
246
247 class AbckitScenarioCTestClean : public ::testing::Test {};
248
249 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestScanSubclassesClean)250 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestScanSubclassesClean)
251 {
252 // CC-OFFNXT(G.NAM.03) project code style
253 AbckitFile *file =
254 g_impl->openAbc(ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/scan_subclasses/scan_subclasses.abc",
255 strlen(ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/scan_subclasses/scan_subclasses.abc"));
256 ASSERT_NE(file, nullptr);
257
258 std::vector<ClassInfo> subClasses;
259 std::vector<ClassInfo> baseClasses = {{"modules/base", "Base"}};
260
261 EnumerateModules(
262 [&](AbckitCoreModule *mod) {
263 std::vector<std::pair<AbckitCoreImportDescriptor *, size_t>> impDescriptors;
264 GetImportDescriptors(mod, &impDescriptors, &baseClasses);
265 if (impDescriptors.empty()) {
266 return;
267 }
268 EnumerateModuleFunctions(
269 mod, [&](AbckitCoreFunction *method) { CollectSubClasses(method, impDescriptors, &subClasses); });
270 },
271 file);
272
273 ASSERT_FALSE(subClasses.empty());
274
275 const std::vector<ClassInfo> expectedSubClasses = {{"scan_subclasses", "Child1"}, {"scan_subclasses", "Child2"}};
276
277 ASSERT_TRUE(IsEqualsSubClasses(expectedSubClasses, subClasses));
278 g_impl->closeFile(file);
279 }
280
281 } // namespace libabckit::test
282