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