• 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/include/c/abckit.h"
19 #include "libabckit/include/c/metadata_core.h"
20 
21 #include "helpers/helpers.h"
22 #include "helpers/helpers_runtime.h"
23 #include "libabckit/src/logger.h"
24 
25 namespace {
26 
27 constexpr AbckitApiVersion VERSION = ABCKIT_VERSION_RELEASE_1_0_0;
28 auto *g_impl = AbckitGetApiImpl(VERSION);
29 const AbckitInspectApi *g_implI = AbckitGetInspectApiImpl(VERSION);
30 const AbckitGraphApi *g_implG = AbckitGetGraphApiImpl(VERSION);
31 const AbckitIsaApiDynamic *g_dynG = AbckitGetIsaApiDynamicImpl(VERSION);
32 const AbckitModifyApi *g_implM = AbckitGetModifyApiImpl(VERSION);
33 
34 template <class FunctionCallBack>
TransformMethod(AbckitCoreFunction * f,const FunctionCallBack & cb)35 inline void TransformMethod(AbckitCoreFunction *f, const FunctionCallBack &cb)
36 {
37     cb(g_implI->functionGetFile(f), f);
38 }
39 
AddParamChecker(AbckitCoreFunction * method)40 void AddParamChecker(AbckitCoreFunction *method)
41 {
42     AbckitGraph *graph = g_implI->createGraphFromFunction(method);
43 
44     TransformMethod(method, [&](AbckitFile *file, AbckitCoreFunction *method) {
45         AbckitBasicBlock *startBB = g_implG->gGetStartBasicBlock(graph);
46         AbckitInst *idx = g_implG->bbGetLastInst(startBB);
47         AbckitInst *arr = g_implG->iGetPrev(idx);
48 
49         std::vector<AbckitBasicBlock *> succBBs;
50         g_implG->bbVisitSuccBlocks(startBB, &succBBs, [](AbckitBasicBlock *succBasicBlock, void *d) {
51             auto *succs = reinterpret_cast<std::vector<AbckitBasicBlock *> *>(d);
52             succs->emplace_back(succBasicBlock);
53             return true;
54         });
55 
56         AbckitString *str = g_implM->createString(file, "length", strlen("length"));
57 
58         AbckitInst *constant = g_implG->gFindOrCreateConstantI32(graph, -1);
59         AbckitInst *arrLength = g_dynG->iCreateLdobjbyname(graph, arr, str);
60 
61         AbckitBasicBlock *trueBB = succBBs[0];
62         g_implG->bbDisconnectSuccBlock(startBB, ABCKIT_TRUE_SUCC_IDX);
63         AbckitBasicBlock *falseBB = g_implG->bbCreateEmpty(graph);
64         g_implG->bbAppendSuccBlock(falseBB, g_implG->gGetEndBasicBlock(graph));
65         g_implG->bbAddInstBack(falseBB, g_dynG->iCreateReturn(graph, constant));
66         AbckitBasicBlock *ifBB = g_implG->bbCreateEmpty(graph);
67         AbckitInst *intrinsicGreatereq = g_dynG->iCreateGreatereq(graph, arrLength, idx);
68         AbckitInst *ifInst = g_dynG->iCreateIf(graph, intrinsicGreatereq, ABCKIT_ISA_API_DYNAMIC_CONDITION_CODE_CC_EQ);
69         g_implG->bbAddInstBack(ifBB, arrLength);
70         g_implG->bbAddInstBack(ifBB, intrinsicGreatereq);
71         g_implG->bbAddInstBack(ifBB, ifInst);
72         g_implG->bbAppendSuccBlock(startBB, ifBB);
73         g_implG->bbAppendSuccBlock(ifBB, trueBB);
74         g_implG->bbAppendSuccBlock(ifBB, falseBB);
75 
76         g_implM->functionSetGraph(method, graph);
77         g_impl->destroyGraph(graph);
78     });
79 }
80 
81 struct CapturedData {
82     void *callback = nullptr;
83     const AbckitGraphApi *gImplG = nullptr;
84 };
85 
86 struct MethodInfo {
87     std::string path;
88     std::string className;
89     std::string methodName;
90 };
91 
92 template <class ImportCallBack>
EnumerateModuleImports(AbckitCoreModule * mod,const ImportCallBack & cb)93 inline void EnumerateModuleImports(AbckitCoreModule *mod, const ImportCallBack &cb)
94 {
95     LIBABCKIT_LOG_FUNC;
96 
97     g_implI->moduleEnumerateImports(mod, (void *)(&cb), [](AbckitCoreImportDescriptor *i, void *data) {
98         const auto &cb = *((ImportCallBack *)(data));
99         cb(i);
100         return true;
101     });
102 }
103 
104 template <class ModuleCallBack>
EnumerateModules(const ModuleCallBack & cb,AbckitFile * file)105 inline void EnumerateModules(const ModuleCallBack &cb, AbckitFile *file)
106 {
107     LIBABCKIT_LOG_FUNC;
108 
109     g_implI->fileEnumerateModules(file, (void *)(&cb), [](AbckitCoreModule *mod, void *data) {
110         const auto &cb = *((ModuleCallBack *)(data));
111         cb(mod);
112         return true;
113     });
114 }
115 
116 template <class FunctionCallBack>
EnumerateModuleTopLevelFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)117 inline void EnumerateModuleTopLevelFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
118 {
119     LIBABCKIT_LOG_FUNC;
120 
121     g_implI->moduleEnumerateTopLevelFunctions(mod, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
122         const auto &cb = *((FunctionCallBack *)data);
123         cb(method);
124         return true;
125     });
126 }
127 
GetImportDescriptor(AbckitCoreModule * module,MethodInfo & methodInfo)128 AbckitCoreImportDescriptor *GetImportDescriptor(AbckitCoreModule *module, MethodInfo &methodInfo)
129 {
130     AbckitCoreImportDescriptor *impDescriptor = nullptr;
131     EnumerateModuleImports(module, [&](AbckitCoreImportDescriptor *id) {
132         auto importName = g_implI->abckitStringToString(g_implI->importDescriptorGetName(id));
133         auto *importedModule = g_implI->importDescriptorGetImportedModule(id);
134         auto source = g_implI->abckitStringToString(g_implI->moduleGetName(importedModule));
135         if (source != methodInfo.path) {
136             return false;
137         }
138         if (importName == methodInfo.className) {
139             impDescriptor = id;
140             return true;
141         }
142         return false;
143     });
144     return impDescriptor;
145 }
146 
147 template <class MethodCallBack>
EnumerateClassMethods(AbckitCoreClass * klass,const MethodCallBack & cb)148 inline void EnumerateClassMethods(AbckitCoreClass *klass, const MethodCallBack &cb)
149 {
150     LIBABCKIT_LOG_FUNC;
151     g_implI->classEnumerateMethods(klass, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
152         const auto &cb = *((MethodCallBack *)data);
153         cb(method);
154         return true;
155     });
156 }
157 
158 template <class ClassCallBack>
EnumerateModuleClasses(AbckitCoreModule * mod,const ClassCallBack & cb)159 inline void EnumerateModuleClasses(AbckitCoreModule *mod, const ClassCallBack &cb)
160 {
161     LIBABCKIT_LOG_FUNC;
162 
163     g_implI->moduleEnumerateClasses(mod, (void *)(&cb), [](AbckitCoreClass *klass, void *data) {
164         const auto &cb = *((ClassCallBack *)data);
165         cb(klass);
166         return true;
167     });
168 }
169 
170 template <class FunctionCallBack>
EnumerateModuleFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)171 inline void EnumerateModuleFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
172 {
173     LIBABCKIT_LOG_FUNC;
174     // NOTE: currently we can only enumerate class methods and top level functions. need to update.
175     EnumerateModuleTopLevelFunctions(mod, cb);
176     EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { EnumerateClassMethods(klass, cb); });
177 }
178 
179 template <class InstCallBack>
EnumerateFunctionInsts(AbckitCoreFunction * func,const InstCallBack & cb)180 inline void EnumerateFunctionInsts(AbckitCoreFunction *func, const InstCallBack &cb)
181 {
182     LIBABCKIT_LOG_FUNC;
183 
184     AbckitGraph *graph = g_implI->createGraphFromFunction(func);
185     EnumerateGraphInsts(graph, cb);
186     g_impl->destroyGraph(graph);
187 }
188 
189 template <class InstCallBack>
VisitBlock(AbckitBasicBlock * bb,void * data)190 inline bool VisitBlock(AbckitBasicBlock *bb, void *data)
191 {
192     auto *captured = reinterpret_cast<CapturedData *>(data);
193     const auto &cb = *reinterpret_cast<InstCallBack *>(captured->callback);
194     auto *implG = captured->gImplG;
195     for (auto *inst = implG->bbGetFirstInst(bb); inst != nullptr; inst = implG->iGetNext(inst)) {
196         cb(inst);
197     }
198     return true;
199 }
200 
201 template <class InstCallBack>
EnumerateGraphInsts(AbckitGraph * graph,const InstCallBack & cb)202 inline void EnumerateGraphInsts(AbckitGraph *graph, const InstCallBack &cb)
203 {
204     LIBABCKIT_LOG_FUNC;
205 
206     CapturedData captured {(void *)(&cb), g_implG};
207 
208     g_implG->gVisitBlocksRpo(graph, &captured,
209                              [](AbckitBasicBlock *bb, void *data) { return VisitBlock<InstCallBack>(bb, data); });
210 }
211 
212 template <class UserCallBack>
EnumerateInstUsers(AbckitInst * inst,const UserCallBack & cb)213 inline void EnumerateInstUsers(AbckitInst *inst, const UserCallBack &cb)
214 {
215     LIBABCKIT_LOG_FUNC;
216 
217     g_implG->iVisitUsers(inst, (void *)(&cb), [](AbckitInst *user, void *data) {
218         const auto &cb = *((UserCallBack *)data);
219         cb(user);
220         return true;
221     });
222 }
223 
GetMethodToModify(AbckitCoreClass * klass,MethodInfo & methodInfo)224 AbckitCoreFunction *GetMethodToModify(AbckitCoreClass *klass, MethodInfo &methodInfo)
225 {
226     AbckitCoreFunction *foundMethod = nullptr;
227     EnumerateClassMethods(klass, [&](AbckitCoreFunction *method) {
228         auto name = g_implI->abckitStringToString(g_implI->functionGetName(method));
229         if (name == methodInfo.methodName) {
230             foundMethod = method;
231             return true;
232         }
233         return false;
234     });
235     return foundMethod;
236 }
237 
GetSubclassMethod(AbckitCoreImportDescriptor * id,AbckitInst * inst,MethodInfo & methodInfo)238 AbckitCoreFunction *GetSubclassMethod(AbckitCoreImportDescriptor *id, AbckitInst *inst, MethodInfo &methodInfo)
239 {
240     if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
241         return nullptr;
242     }
243 
244     if (g_dynG->iGetImportDescriptor(inst) != id) {
245         return nullptr;
246     }
247 
248     AbckitCoreFunction *foundMethod = nullptr;
249     EnumerateInstUsers(inst, [&](AbckitInst *user) {
250         if (g_dynG->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_DEFINECLASSWITHBUFFER) {
251             auto method = g_implG->iGetFunction(user);
252             auto klass = g_implI->functionGetParentClass(method);
253             foundMethod = GetMethodToModify(klass, methodInfo);
254         }
255     });
256 
257     return foundMethod;
258 }
ModifyFunction(AbckitCoreFunction * method,AbckitCoreImportDescriptor * id,MethodInfo & methodInfo)259 void ModifyFunction(AbckitCoreFunction *method, AbckitCoreImportDescriptor *id, MethodInfo &methodInfo)
260 {
261     EnumerateFunctionInsts(method, [&](AbckitInst *inst) {
262         auto subclassMethod = GetSubclassMethod(id, inst, methodInfo);
263         if (subclassMethod != nullptr) {
264             AddParamChecker(subclassMethod);
265         }
266     });
267 }
268 
Modify(MethodInfo & methodInfo,AbckitFile * file)269 void Modify(MethodInfo &methodInfo, AbckitFile *file)
270 {
271     EnumerateModules(
272         [&](AbckitCoreModule *mod) {
273             AbckitCoreImportDescriptor *impDescriptor = GetImportDescriptor(mod, methodInfo);
274             if (impDescriptor == nullptr) {
275                 return;
276             }
277             EnumerateModuleFunctions(mod, [&](AbckitCoreFunction *method) {
278                 ModifyFunction(method, impDescriptor, methodInfo);
279                 return true;
280             });
281         },
282         file);
283 }
284 
285 }  // namespace
286 
287 namespace libabckit::test {
288 
289 class AbckitScenarioCTestClean : public ::testing::Test {};
290 
291 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestDynamicParameterCheckClean)292 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestDynamicParameterCheckClean)
293 {
294     constexpr const auto INPUT_PATH =
295         ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/parameter_check/parameter_check.abc";
296     constexpr const auto OUTPUT_PATH =
297         ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/parameter_check/parameter_check_modified.abc";
298     const std::string entryPoint = "parameter_check";
299 
300     AbckitFile *file = g_impl->openAbc(INPUT_PATH, strlen(INPUT_PATH));
301     ASSERT_NE(file, nullptr);
302 
303     auto output = helpers::ExecuteDynamicAbc(INPUT_PATH, entryPoint);
304     EXPECT_TRUE(helpers::Match(output,
305                                "str1\n"
306                                "str2\n"
307                                "str3\n"
308                                "undefined\n"
309                                "str3\n"
310                                "str2\n"
311                                "str4\n"
312                                "undefined\n"));
313 
314     MethodInfo method = {"modules/base", "Handler", "handle"};
315 
316     Modify(method, file);
317 
318     g_impl->writeAbc(file, OUTPUT_PATH, strlen(OUTPUT_PATH));
319     g_impl->closeFile(file);
320 
321     output = helpers::ExecuteDynamicAbc(OUTPUT_PATH, entryPoint);
322     EXPECT_TRUE(helpers::Match(output,
323                                "str1\n"
324                                "str2\n"
325                                "str3\n"
326                                "-1\n"
327                                "str3\n"
328                                "str2\n"
329                                "str4\n"
330                                "-1\n"));
331 }
332 
333 }  // namespace libabckit::test
334