• 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 "libabckit/include/cpp/abckit_cpp.h"
17 
18 #include "helpers/helpers.h"
19 #include "helpers/helpers_runtime.h"
20 
21 #include <gtest/gtest.h>
22 
23 #include <optional>
24 #include <string_view>
25 
26 namespace {
27 
28 class ErrorHandler final : public abckit::IErrorHandler {
HandleError(abckit::Exception && e)29     void HandleError(abckit::Exception &&e) override
30     {
31         EXPECT_TRUE(false) << "Abckit exception raised: " << e.what();
32     }
33 };
34 
EnumerateModuleFunctions(const abckit::core::Module & mod,const std::function<bool (abckit::core::Function)> & cb)35 inline void EnumerateModuleFunctions(const abckit::core::Module &mod,
36                                      const std::function<bool(abckit::core::Function)> &cb)
37 {
38     // NOTE: currently we can only enumerate class methods and top level functions. need to update.
39     mod.EnumerateTopLevelFunctions(cb);
40     mod.EnumerateClasses([&](const abckit::core::Class &klass) -> bool {
41         klass.EnumerateMethods(cb);
42         return true;
43     });
44 }
45 
EnumerateFunctionInsts(const abckit::core::Function & func,const std::function<bool (abckit::Instruction)> & cb)46 inline void EnumerateFunctionInsts(const abckit::core::Function &func,
47                                    const std::function<bool(abckit::Instruction)> &cb)
48 {
49     abckit::Graph graph = func.CreateGraph();
50     graph.EnumerateBasicBlocksRpo([&](const abckit::BasicBlock &bb) {
51         for (auto inst = bb.GetFirstInst(); inst; inst = inst.GetNext()) {
52             cb(inst);
53         }
54         return true;
55     });
56 }
57 
58 struct ApiInfo {
59     std::string source;
60     std::string objName;
61     std::string propName;
62 };
63 
64 struct UsageInfo {
65     std::string source;
66     std::string funcName;
67     size_t idx;
68 };
69 
70 using ApiUsageMap = std::unordered_map<size_t, std::vector<UsageInfo>>;
71 using SuspectsMap = std::unordered_map<abckit::core::ImportDescriptor, size_t>;
72 
GetMethodName(const abckit::core::Function & method)73 std::string GetMethodName(const abckit::core::Function &method)
74 {
75     auto methodName = method.GetName();
76     auto cls = method.GetParentClass();
77     if (cls) {
78         auto className = cls.GetName();
79         return className + '.' + methodName;
80     };
81     return methodName;
82 }
83 
IsLoadApi(const abckit::core::ImportDescriptor & id,size_t apiIndex,const abckit::Instruction & inst,std::vector<ApiInfo> & apiList)84 bool IsLoadApi(const abckit::core::ImportDescriptor &id, size_t apiIndex, const abckit::Instruction &inst,
85                std::vector<ApiInfo> &apiList)
86 {
87     // find the following pattern:
88     // 1. ldExternalModuleVar (import {ApiNamespace} from "./modules/myApi")
89     // 2. ldObjByName v1, "foo" (api.propName)
90     // or
91     // 1. ldExternalModuleVar (import {bar} from "./modules/myApi")
92 
93     if (inst.GetGraph()->DynIsa().GetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
94         return false;
95     }
96 
97     abckit::core::ImportDescriptor instId = inst.GetGraph()->DynIsa().GetImportDescriptor(inst);
98     if (instId != id) {
99         return false;
100     }
101 
102     const auto &prop = apiList[apiIndex].propName;
103     if (prop.empty()) {
104         return true;
105     }
106 
107     return !inst.VisitUsers([&](const abckit::Instruction &user) {
108         if (user.GetGraph()->DynIsa().GetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_LDOBJBYNAME) {
109             if (user.GetString() == prop) {
110                 return false;
111             }
112         }
113         return true;
114     });
115 }
116 
AddApiUsage(size_t apiIndex,const UsageInfo & usage,ApiUsageMap * apiUsages)117 void AddApiUsage(size_t apiIndex, const UsageInfo &usage, ApiUsageMap *apiUsages)
118 {
119     auto iter = apiUsages->find(apiIndex);
120     if (iter == apiUsages->end()) {
121         std::vector<UsageInfo> usages;
122         usages.emplace_back(usage);
123         apiUsages->emplace(apiIndex, usages);
124     } else {
125         iter->second.emplace_back(usage);
126     }
127 }
128 
CollectUsageInMethod(const abckit::core::Function & method,SuspectsMap & suspects,ApiUsageMap * apiUsages,std::vector<ApiInfo> & apiList)129 void CollectUsageInMethod(const abckit::core::Function &method, SuspectsMap &suspects, ApiUsageMap *apiUsages,
130                           std::vector<ApiInfo> &apiList)
131 {
132     auto methodName = GetMethodName(method);
133     auto mod = method.GetModule();
134     auto source = mod.GetName();
135     UsageInfo usage = {source, methodName, 0};
136     EnumerateFunctionInsts(method, [&](const abckit::Instruction &inst) {
137         for (const auto &[id, apiIndex] : suspects) {
138             if (IsLoadApi(id, apiIndex, inst, apiList)) {
139                 usage.idx = apiIndex;
140                 AddApiUsage(apiIndex, usage, apiUsages);
141             }
142         }
143         return true;
144     });
145 }
146 
GetSuspects(const abckit::core::Module & mod,SuspectsMap * suspects,std::vector<ApiInfo> & apiList)147 bool GetSuspects(const abckit::core::Module &mod, SuspectsMap *suspects, std::vector<ApiInfo> &apiList)
148 {
149     mod.EnumerateImports([&](const abckit::core::ImportDescriptor &id) -> bool {
150         auto importName = id.GetName();
151         auto importedModule = id.GetImportedModule();
152         auto source = importedModule.GetName();
153         for (size_t i = 0; i < apiList.size(); ++i) {
154             const auto api = apiList[i];
155             if (source != api.source) {
156                 continue;
157             }
158             if (importName == api.objName) {
159                 suspects->emplace(id, i);
160             }
161         }
162         return true;
163     });
164     return !suspects->empty();
165 }
166 
CollectApiUsages(ApiUsageMap * apiUsages,std::vector<ApiInfo> & apiList,abckit::File * file)167 void CollectApiUsages(ApiUsageMap *apiUsages, std::vector<ApiInfo> &apiList, abckit::File *file)
168 {
169     file->EnumerateModules([&](const abckit::core::Module &mod) -> bool {
170         SuspectsMap suspects = {};
171         GetSuspects(mod, &suspects, apiList);
172         if (suspects.empty()) {
173             return true;
174         }
175         EnumerateModuleFunctions(mod, [&](const abckit::core::Function &method) -> bool {
176             CollectUsageInMethod(method, suspects, apiUsages, apiList);
177             return true;
178         });
179         return true;
180     });
181 }
182 
GetApiUsages(std::vector<ApiInfo> & apiList,abckit::File * file)183 ApiUsageMap GetApiUsages(std::vector<ApiInfo> &apiList, abckit::File *file)
184 {
185     auto apiUsages = ApiUsageMap({});
186     CollectApiUsages(&apiUsages, apiList, file);
187     return apiUsages;
188 }
189 
GetQuatatedStr(const std::string & str)190 std::string GetQuatatedStr(const std::string &str)
191 {
192     const static std::string QUOTATION = "\"";
193     return QUOTATION + str + QUOTATION;
194 }
195 
ApiUsageMapGetString(const ApiUsageMap & apiUsages,std::vector<ApiInfo> & apiList)196 std::string ApiUsageMapGetString(const ApiUsageMap &apiUsages, std::vector<ApiInfo> &apiList)
197 {
198     std::stringstream ss;
199     const static std::string INDENT_1 = "  ";
200     const static std::string INDENT_2 = INDENT_1 + INDENT_1;
201     const static std::string INDENT_3 = INDENT_1 + INDENT_2;
202     const static std::string INDENT_4 = INDENT_1 + INDENT_3;
203     const static std::string LEFT_BRACKET = "{";
204     const static std::string RIGHT_BRACKET = "}";
205     const static std::string COLON = ": ";
206     const static std::string COMMA = ",";
207     const static std::string APIINFO = "APIInfo";
208     const static std::string APIINFO_SOURCE = "source";
209     const static std::string APIINFO_OBJNAME = "objName";
210     const static std::string APIINFO_PROPNAME = "propName";
211     const static std::string USAGES = "Usages";
212     const static std::string USAGES_SOURCE = "source";
213     const static std::string USAGES_FUNCNAME = "funcName";
214     const static std::string API_IDX = "indexOfAPI";
215 
216     ss << LEFT_BRACKET << std::endl;
217     for (const auto &[index, usageInfos] : apiUsages) {
218         ss << INDENT_1 << LEFT_BRACKET << std::endl;
219 
220         if (usageInfos.empty()) {
221             continue;
222         }
223         const auto &apiInfo = apiList[index];
224         ss << INDENT_2 << APIINFO << COLON << LEFT_BRACKET << std::endl;
225         ss << INDENT_3 << APIINFO_SOURCE << COLON << GetQuatatedStr(apiInfo.source) << COMMA << std::endl;
226         ss << INDENT_3 << APIINFO_OBJNAME << COLON << GetQuatatedStr(apiInfo.objName) << COMMA << std::endl;
227         ss << INDENT_3 << APIINFO_PROPNAME << COLON << GetQuatatedStr(apiInfo.propName) << COMMA << std::endl;
228         ss << INDENT_2 << RIGHT_BRACKET << COMMA << std::endl;
229 
230         ss << INDENT_2 << USAGES << COLON << LEFT_BRACKET << std::endl;
231         for (const auto &usage : usageInfos) {
232             ss << INDENT_3 << LEFT_BRACKET << std::endl;
233             ss << INDENT_4 << USAGES_SOURCE << COLON << GetQuatatedStr(usage.source) << COMMA << std::endl;
234             ss << INDENT_4 << USAGES_FUNCNAME << COLON << GetQuatatedStr(usage.funcName) << COMMA << std::endl;
235             ss << INDENT_4 << API_IDX << COLON << usage.idx << COMMA << std::endl;
236             ss << INDENT_3 << RIGHT_BRACKET << COMMA << std::endl;
237         }
238         ss << INDENT_2 << RIGHT_BRACKET << std::endl;
239 
240         ss << INDENT_1 << RIGHT_BRACKET << COMMA << std::endl;
241     }
242     ss << RIGHT_BRACKET << std::endl;
243 
244     return ss.str();
245 }
246 
IsUsagesEqual(const std::vector<UsageInfo> & otherUsages,const ApiUsageMap & apiUsages)247 bool IsUsagesEqual(const std::vector<UsageInfo> &otherUsages, const ApiUsageMap &apiUsages)
248 {
249     for (auto &otherUsage : otherUsages) {
250         auto usages = apiUsages.at(otherUsage.idx);
251         auto iter = std::find_if(usages.begin(), usages.end(), [&otherUsage](const UsageInfo &usage) {
252             return (otherUsage.funcName == usage.funcName) && (otherUsage.source == usage.source) &&
253                    (otherUsage.idx == usage.idx);
254         });
255         if (iter == usages.end()) {
256             return false;
257         }
258     }
259     return true;
260 }
261 
262 }  // namespace
263 
264 namespace libabckit::test {
265 
266 class AbckitScenarioCppTestClean : public ::testing::Test {};
267 
268 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=cpp
TEST_F(AbckitScenarioCppTestClean,LibAbcKitTestDynamicApiScannerClean)269 TEST_F(AbckitScenarioCppTestClean, LibAbcKitTestDynamicApiScannerClean)
270 {
271     // CC-OFFNXT(G.NAM.03) project code style
272 
273     const std::string testSandboxPath = ABCKIT_ABC_DIR "clean_scenarios/cpp_api/dynamic/api_scanner/";
274     const std::string inputAbcPath = testSandboxPath + "api_scanner.abc";
275 
276     abckit::File file(inputAbcPath, std::make_unique<ErrorHandler>());
277 
278     std::vector<ApiInfo> apiList = {{"modules/apiNamespace", "ApiNamespace", "foo"},
279                                     {"modules/toplevelApi", "bar", ""},
280                                     {"@ohos.geolocation", "geolocation", "getCurrentLocation"}};
281 
282     const ApiUsageMap usages = GetApiUsages(apiList, &file);
283     EXPECT_FALSE(usages.empty());
284 
285     auto usagesStr = ApiUsageMapGetString(usages, apiList);
286     std::vector<UsageInfo> expectedUsages = {
287         {"api_scanner", "useAll", 0},
288         {"api_scanner", "useFoo", 0},
289         {"api_scanner", "Person.personUseAll", 0},
290         {"api_scanner", "Person.personUseFoo", 0},
291         {"modules/src2", "Animal.animalUseFoo", 0},
292         {"modules/src2", "Animal.animalUseAll", 0},
293         {"modules/src2", "src2UseAll", 0},
294         {"modules/src2", "src2UseFoo", 0},
295         {"api_scanner", "useAll", 1},
296         {"api_scanner", "useBar", 1},
297         {"api_scanner", "Person.personUseAll", 1},
298         {"api_scanner", "Person.personUseBar", 1},
299         {"api_scanner", "useAll", 2},
300         {"api_scanner", "useLocation", 2},
301         {"api_scanner", "Person.personUseLocation", 2},
302         {"modules/src3", "src3UseLocation", 2},
303         {"modules/src3", "Cat.catUseLocation", 2},
304     };
305     EXPECT_TRUE(IsUsagesEqual(expectedUsages, usages));
306     LIBABCKIT_LOG(DEBUG) << "====================================\n";
307     LIBABCKIT_LOG(DEBUG) << usagesStr;
308 }
309 
310 }  // namespace libabckit::test
311