• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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