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