• 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 <gtest/gtest.h>
17 
18 #include "libabckit/src/logger.h"
19 #include "libabckit/include/c/abckit.h"
20 #include "libabckit/include/c/ir_core.h"
21 #include "libabckit/include/c/metadata_core.h"
22 #include "libabckit/include/c/isa/isa_dynamic.h"
23 
24 namespace {
25 
26 constexpr AbckitApiVersion VERSION = ABCKIT_VERSION_RELEASE_1_0_0;
27 auto *g_impl = AbckitGetApiImpl(VERSION);
28 const AbckitInspectApi *g_implI = AbckitGetInspectApiImpl(VERSION);
29 const AbckitGraphApi *g_implG = AbckitGetGraphApiImpl(VERSION);
30 const AbckitIsaApiDynamic *g_dynG = AbckitGetIsaApiDynamicImpl(VERSION);
31 const AbckitModifyApi *g_implM = AbckitGetModifyApiImpl(VERSION);
32 
33 template <class FunctionCallBack>
EnumerateModuleTopLevelFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)34 inline void EnumerateModuleTopLevelFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
35 {
36     LIBABCKIT_LOG_FUNC;
37 
38     g_implI->moduleEnumerateTopLevelFunctions(mod, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
39         const auto &cb = *((FunctionCallBack *)data);
40         cb(method);
41         return true;
42     });
43 }
44 
45 template <class ClassCallBack>
EnumerateModuleClasses(AbckitCoreModule * mod,const ClassCallBack & cb)46 inline void EnumerateModuleClasses(AbckitCoreModule *mod, const ClassCallBack &cb)
47 {
48     LIBABCKIT_LOG_FUNC;
49 
50     g_implI->moduleEnumerateClasses(mod, (void *)(&cb), [](AbckitCoreClass *klass, void *data) {
51         const auto &cb = *((ClassCallBack *)data);
52         cb(klass);
53         return true;
54     });
55 }
56 
57 template <class MethodCallBack>
EnumerateClassMethods(AbckitCoreClass * klass,const MethodCallBack & cb)58 inline void EnumerateClassMethods(AbckitCoreClass *klass, const MethodCallBack &cb)
59 {
60     LIBABCKIT_LOG_FUNC;
61     g_implI->classEnumerateMethods(klass, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
62         const auto &cb = *((MethodCallBack *)data);
63         cb(method);
64         return true;
65     });
66 }
67 
68 template <class FunctionCallBack>
EnumerateModuleFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)69 inline void EnumerateModuleFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
70 {
71     LIBABCKIT_LOG_FUNC;
72     // NOTE: currently we can only enumerate class methods and top level functions. need to update.
73     EnumerateModuleTopLevelFunctions(mod, cb);
74     EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { EnumerateClassMethods(klass, cb); });
75 }
76 
77 template <class UserCallBack>
EnumerateInstUsers(AbckitInst * inst,const UserCallBack & cb)78 inline void EnumerateInstUsers(AbckitInst *inst, const UserCallBack &cb)
79 {
80     LIBABCKIT_LOG_FUNC;
81 
82     g_implG->iVisitUsers(inst, (void *)(&cb), [](AbckitInst *user, void *data) {
83         const auto &cb = *((UserCallBack *)data);
84         cb(user);
85         return true;
86     });
87 }
88 
89 struct CapturedData {
90     void *callback = nullptr;
91     const AbckitGraphApi *gImplG = nullptr;
92 };
93 
94 template <class InstCallBack>
VisitBlock(AbckitBasicBlock * bb,void * data)95 inline bool VisitBlock(AbckitBasicBlock *bb, void *data)
96 {
97     auto *captured = reinterpret_cast<CapturedData *>(data);
98     const auto &cb = *reinterpret_cast<InstCallBack *>(captured->callback);
99     auto *implG = captured->gImplG;
100     for (auto *inst = implG->bbGetFirstInst(bb); inst != nullptr; inst = implG->iGetNext(inst)) {
101         cb(inst);
102     }
103     return true;
104 }
105 
106 template <class InstCallBack>
EnumerateGraphInsts(AbckitGraph * graph,const InstCallBack & cb)107 inline void EnumerateGraphInsts(AbckitGraph *graph, const InstCallBack &cb)
108 {
109     LIBABCKIT_LOG_FUNC;
110 
111     CapturedData captured {(void *)(&cb), g_implG};
112     g_implG->gVisitBlocksRpo(graph, &captured,
113                              [](AbckitBasicBlock *bb, void *data) { return VisitBlock<InstCallBack>(bb, data); });
114 }
115 
116 template <class InstCallBack>
EnumerateFunctionInsts(AbckitCoreFunction * func,const InstCallBack & cb)117 inline void EnumerateFunctionInsts(AbckitCoreFunction *func, const InstCallBack &cb)
118 {
119     LIBABCKIT_LOG_FUNC;
120 
121     AbckitGraph *graph = g_implI->createGraphFromFunction(func);
122     EnumerateGraphInsts(graph, cb);
123     g_impl->destroyGraph(graph);
124 }
125 
126 template <class ImportCallBack>
EnumerateModuleImports(AbckitCoreModule * mod,const ImportCallBack & cb)127 inline void EnumerateModuleImports(AbckitCoreModule *mod, const ImportCallBack &cb)
128 {
129     LIBABCKIT_LOG_FUNC;
130 
131     g_implI->moduleEnumerateImports(mod, (void *)(&cb), [](AbckitCoreImportDescriptor *i, void *data) {
132         const auto &cb = *((ImportCallBack *)(data));
133         cb(i);
134         return true;
135     });
136 }
137 
138 template <class ModuleCallBack>
EnumerateModules(const ModuleCallBack & cb,AbckitFile * file)139 inline void EnumerateModules(const ModuleCallBack &cb, AbckitFile *file)
140 {
141     LIBABCKIT_LOG_FUNC;
142 
143     g_implI->fileEnumerateModules(file, (void *)(&cb), [](AbckitCoreModule *mod, void *data) {
144         const auto &cb = *((ModuleCallBack *)(data));
145         cb(mod);
146         return true;
147     });
148 }
149 
150 struct ApiInfo {
151     std::string source;
152     std::string objName;
153     std::string propName;
154 };
155 
156 struct UsageInfo {
157     std::string source;
158     std::string funcName;
159     size_t idx;
160 };
161 
162 using ApiUsageMap = std::unordered_map<size_t, std::vector<UsageInfo>>;
163 using SuspectsMap = std::unordered_map<AbckitCoreImportDescriptor *, size_t>;
164 
GetMethodName(AbckitCoreFunction * method)165 std::string GetMethodName(AbckitCoreFunction *method)
166 {
167     auto mname = g_implI->functionGetName(method);
168     auto methodName = g_implI->abckitStringToString(mname);
169     auto *cls = g_implI->functionGetParentClass(method);
170     if (cls == nullptr) {
171         return methodName;
172     }
173     auto cname = g_implI->classGetName(cls);
174     auto className = (std::string)g_implI->abckitStringToString(cname);
175     return className + '.' + methodName;
176 }
177 
IsLoadApi(AbckitCoreImportDescriptor * id,size_t apiIndex,AbckitInst * inst,std::vector<ApiInfo> & apiList)178 bool IsLoadApi(AbckitCoreImportDescriptor *id, size_t apiIndex, AbckitInst *inst, std::vector<ApiInfo> &apiList)
179 {
180     // find the following pattern:
181     // 1. ldExternalModuleVar (import {ApiNamespace} from "./modules/myApi")
182     // 2. ldObjByName v1, "foo" (api.propName)
183     // or
184     // 1. ldExternalModuleVar (import {bar} from "./modules/myApi")
185 
186     if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
187         return false;
188     }
189 
190     if (g_dynG->iGetImportDescriptor(inst) != id) {
191         return false;
192     }
193 
194     const auto &prop = apiList[apiIndex].propName;
195     if (prop.empty()) {
196         return true;
197     }
198 
199     bool found = false;
200     EnumerateInstUsers(inst, [&](AbckitInst *user) {
201         if (!found && g_dynG->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_LDOBJBYNAME) {
202             auto str = g_implI->abckitStringToString(g_implG->iGetString(user));
203             found = str == prop;
204         }
205     });
206 
207     return found;
208 }
209 
AddApiUsage(size_t apiIndex,const UsageInfo & usage,ApiUsageMap * apiUsages)210 void AddApiUsage(size_t apiIndex, const UsageInfo &usage, ApiUsageMap *apiUsages)
211 {
212     auto iter = apiUsages->find(apiIndex);
213     if (iter == apiUsages->end()) {
214         std::vector<UsageInfo> usages;
215         usages.emplace_back(usage);
216         apiUsages->emplace(apiIndex, usages);
217     } else {
218         iter->second.emplace_back(usage);
219     }
220 }
221 
CollectUsageInMethod(AbckitCoreFunction * method,SuspectsMap & suspects,ApiUsageMap * apiUsages,std::vector<ApiInfo> & apiList)222 void CollectUsageInMethod(AbckitCoreFunction *method, SuspectsMap &suspects, ApiUsageMap *apiUsages,
223                           std::vector<ApiInfo> &apiList)
224 {
225     auto methodName = GetMethodName(method);
226     auto *sourceRaw = g_implI->moduleGetName(g_implI->functionGetModule(method));
227     auto source = g_implI->abckitStringToString(sourceRaw);
228     UsageInfo usage = {source, methodName, 0};
229     EnumerateFunctionInsts(method, [&](AbckitInst *inst) {
230         for (const auto &[id, apiIndex] : suspects) {
231             if (IsLoadApi(id, apiIndex, inst, apiList)) {
232                 usage.idx = apiIndex;
233                 AddApiUsage(apiIndex, usage, apiUsages);
234             }
235         }
236     });
237 }
238 
GetSuspects(AbckitCoreModule * mod,SuspectsMap * suspects,std::vector<ApiInfo> & apiList)239 bool GetSuspects(AbckitCoreModule *mod, SuspectsMap *suspects, std::vector<ApiInfo> &apiList)
240 {
241     EnumerateModuleImports(mod, [&](AbckitCoreImportDescriptor *id) {
242         auto importName = g_implI->abckitStringToString(g_implI->importDescriptorGetName(id));
243         auto *importedModule = g_implI->importDescriptorGetImportedModule(id);
244         auto source = g_implI->abckitStringToString(g_implI->moduleGetName(importedModule));
245         for (size_t i = 0; i < apiList.size(); ++i) {
246             const auto api = apiList[i];
247             if (source != api.source) {
248                 continue;
249             }
250             if (importName == api.objName) {
251                 suspects->emplace(id, i);
252             }
253         }
254     });
255     return !suspects->empty();
256 }
257 
CollectApiUsages(ApiUsageMap * apiUsages,std::vector<ApiInfo> & apiList,AbckitFile * file)258 void CollectApiUsages(ApiUsageMap *apiUsages, std::vector<ApiInfo> &apiList, AbckitFile *file)
259 {
260     EnumerateModules(
261         [&](AbckitCoreModule *mod) {
262             SuspectsMap suspects = {};
263             GetSuspects(mod, &suspects, apiList);
264             if (suspects.empty()) {
265                 return;
266             }
267             EnumerateModuleFunctions(
268                 mod, [&](AbckitCoreFunction *method) { CollectUsageInMethod(method, suspects, apiUsages, apiList); });
269         },
270         file);
271 }
272 
GetApiUsages(std::vector<ApiInfo> & apiList,AbckitFile * file)273 ApiUsageMap GetApiUsages(std::vector<ApiInfo> &apiList, AbckitFile *file)
274 {
275     auto apiUsages = ApiUsageMap({});
276     CollectApiUsages(&apiUsages, apiList, file);
277     return apiUsages;
278 }
279 
GetQuatatedStr(const std::string & str)280 std::string GetQuatatedStr(const std::string &str)
281 {
282     const static std::string QUOTATION = "\"";
283     return QUOTATION + str + QUOTATION;
284 }
285 
ApiUsageMapGetString(const ApiUsageMap & apiUsages,std::vector<ApiInfo> & apiList)286 std::string ApiUsageMapGetString(const ApiUsageMap &apiUsages, std::vector<ApiInfo> &apiList)
287 {
288     std::stringstream ss;
289     const static std::string INDENT_1 = "  ";
290     const static std::string INDENT_2 = INDENT_1 + INDENT_1;
291     const static std::string INDENT_3 = INDENT_1 + INDENT_2;
292     const static std::string INDENT_4 = INDENT_1 + INDENT_3;
293     const static std::string LEFT_BRACKET = "{";
294     const static std::string RIGHT_BRACKET = "}";
295     const static std::string COLON = ": ";
296     const static std::string COMMA = ",";
297     const static std::string APIINFO = "APIInfo";
298     const static std::string APIINFO_SOURCE = "source";
299     const static std::string APIINFO_OBJNAME = "objName";
300     const static std::string APIINFO_PROPNAME = "propName";
301     const static std::string USAGES = "Usages";
302     const static std::string USAGES_SOURCE = "source";
303     const static std::string USAGES_FUNCNAME = "funcName";
304     const static std::string API_IDX = "indexOfAPI";
305 
306     ss << LEFT_BRACKET << std::endl;
307     for (const auto &[index, usageInfos] : apiUsages) {
308         ss << INDENT_1 << LEFT_BRACKET << std::endl;
309 
310         if (usageInfos.empty()) {
311             continue;
312         }
313         const auto &apiInfo = apiList[index];
314         ss << INDENT_2 << APIINFO << COLON << LEFT_BRACKET << std::endl;
315         ss << INDENT_3 << APIINFO_SOURCE << COLON << GetQuatatedStr(apiInfo.source) << COMMA << std::endl;
316         ss << INDENT_3 << APIINFO_OBJNAME << COLON << GetQuatatedStr(apiInfo.objName) << COMMA << std::endl;
317         ss << INDENT_3 << APIINFO_PROPNAME << COLON << GetQuatatedStr(apiInfo.propName) << COMMA << std::endl;
318         ss << INDENT_2 << RIGHT_BRACKET << COMMA << std::endl;
319 
320         ss << INDENT_2 << USAGES << COLON << LEFT_BRACKET << std::endl;
321         for (const auto &usage : usageInfos) {
322             ss << INDENT_3 << LEFT_BRACKET << std::endl;
323             ss << INDENT_4 << USAGES_SOURCE << COLON << GetQuatatedStr(usage.source) << COMMA << std::endl;
324             ss << INDENT_4 << USAGES_FUNCNAME << COLON << GetQuatatedStr(usage.funcName) << COMMA << std::endl;
325             ss << INDENT_4 << API_IDX << COLON << usage.idx << COMMA << std::endl;
326             ss << INDENT_3 << RIGHT_BRACKET << COMMA << std::endl;
327         }
328         ss << INDENT_2 << RIGHT_BRACKET << std::endl;
329 
330         ss << INDENT_1 << RIGHT_BRACKET << COMMA << std::endl;
331     }
332     ss << RIGHT_BRACKET << std::endl;
333 
334     return ss.str();
335 }
336 
IsUsagesEqual(const std::vector<UsageInfo> & otherUsages,const ApiUsageMap & apiUsages)337 bool IsUsagesEqual(const std::vector<UsageInfo> &otherUsages, const ApiUsageMap &apiUsages)
338 {
339     for (auto &otherUsage : otherUsages) {
340         auto usages = apiUsages.at(otherUsage.idx);
341         auto iter = std::find_if(usages.begin(), usages.end(), [&otherUsage](const UsageInfo &usage) {
342             return (otherUsage.funcName == usage.funcName) && (otherUsage.source == usage.source) &&
343                    (otherUsage.idx == usage.idx);
344         });
345         if (iter == usages.end()) {
346             return false;
347         }
348     }
349     return true;
350 }
351 
352 }  // namespace
353 
354 namespace libabckit::test {
355 
356 class AbckitScenarioCTestClean : public ::testing::Test {};
357 
358 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestDynamicApiScannerClean)359 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestDynamicApiScannerClean)
360 {
361     // CC-OFFNXT(G.NAM.03) project code style
362 
363     constexpr auto INPUT_PATH = ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/api_scanner/api_scanner.abc";
364     AbckitFile *file = g_impl->openAbc(INPUT_PATH, strlen(INPUT_PATH));
365     ASSERT_NE(file, nullptr);
366 
367     std::vector<ApiInfo> apiList = {{"modules/apiNamespace", "ApiNamespace", "foo"},
368                                     {"modules/toplevelApi", "bar", ""},
369                                     {"@ohos.geolocation", "geolocation", "getCurrentLocation"}};
370 
371     const ApiUsageMap usages = GetApiUsages(apiList, file);
372     EXPECT_FALSE(usages.empty());
373 
374     auto usagesStr = ApiUsageMapGetString(usages, apiList);
375     std::vector<UsageInfo> expectedUsages = {
376         {"api_scanner", "useAll", 0},
377         {"api_scanner", "useFoo", 0},
378         {"api_scanner", "Person.personUseAll", 0},
379         {"api_scanner", "Person.personUseFoo", 0},
380         {"modules/src2", "Animal.animalUseFoo", 0},
381         {"modules/src2", "Animal.animalUseAll", 0},
382         {"modules/src2", "src2UseAll", 0},
383         {"modules/src2", "src2UseFoo", 0},
384         {"api_scanner", "useAll", 1},
385         {"api_scanner", "useBar", 1},
386         {"api_scanner", "Person.personUseAll", 1},
387         {"api_scanner", "Person.personUseBar", 1},
388         {"api_scanner", "useAll", 2},
389         {"api_scanner", "useLocation", 2},
390         {"api_scanner", "Person.personUseLocation", 2},
391         {"modules/src3", "src3UseLocation", 2},
392         {"modules/src3", "Cat.catUseLocation", 2},
393     };
394     EXPECT_TRUE(IsUsagesEqual(expectedUsages, usages));
395     LIBABCKIT_LOG(DEBUG) << "====================================\n";
396     LIBABCKIT_LOG(DEBUG) << usagesStr;
397 
398     g_impl->closeFile(file);
399 }
400 
401 }  // namespace libabckit::test
402