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 "helpers/helpers.h"
19 #include "helpers/helpers_runtime.h"
20 #include "libabckit/include/c/ir_core.h"
21 #include "libabckit/include/c/isa/isa_dynamic.h"
22 #include "libabckit/include/c/metadata_core.h"
23 #include "libabckit/include/c/extensions/arkts/metadata_arkts.h"
24 #include "metadata_inspect_impl.h"
25 #include "libabckit/src/logger.h"
26
27 #include <cstring>
28
29 namespace {
30
31 auto g_impl = AbckitGetApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
32 auto g_implI = AbckitGetInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
33 auto g_implArkI = AbckitGetArktsInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
34 auto g_implM = AbckitGetModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
35 auto g_implG = AbckitGetGraphApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
36 auto g_dynG = AbckitGetIsaApiDynamicImpl(ABCKIT_VERSION_RELEASE_1_0_0);
37 auto g_implArkM = AbckitGetArktsModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
38
39 struct CapturedData {
40 void *callback = nullptr;
41 const AbckitGraphApi *gImplG = nullptr;
42 };
43
44 template <class InstCallBack>
VisitInst(AbckitBasicBlock * bb,void * data)45 inline bool VisitInst(AbckitBasicBlock *bb, void *data)
46 {
47 auto *captured = reinterpret_cast<CapturedData *>(data);
48 const auto &cb = *reinterpret_cast<InstCallBack *>(captured->callback);
49 auto *implG = captured->gImplG;
50 for (auto *inst = implG->bbGetFirstInst(bb); inst != nullptr; inst = implG->iGetNext(inst)) {
51 cb(inst);
52 }
53 return true;
54 }
55
56 template <class InstCallBack>
EnumerateGraphInsts(AbckitGraph * graph,const InstCallBack & cb)57 inline void EnumerateGraphInsts(AbckitGraph *graph, const InstCallBack &cb)
58 {
59 LIBABCKIT_LOG_FUNC;
60
61 CapturedData captured {(void *)(&cb), g_implG};
62
63 g_implG->gVisitBlocksRpo(graph, &captured,
64 [](AbckitBasicBlock *bb, void *data) { return VisitInst<InstCallBack>(bb, data); });
65 }
66
67 template <class InstCallBack>
EnumerateFunctionInsts(AbckitCoreFunction * func,const InstCallBack & cb)68 inline void EnumerateFunctionInsts(AbckitCoreFunction *func, const InstCallBack &cb)
69 {
70 LIBABCKIT_LOG_FUNC;
71
72 AbckitGraph *graph = g_implI->createGraphFromFunction(func);
73 EnumerateGraphInsts(graph, cb);
74 g_impl->destroyGraph(graph);
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 template <class ClassCallBack>
EnumerateModuleClasses(AbckitCoreModule * mod,const ClassCallBack & cb)90 inline void EnumerateModuleClasses(AbckitCoreModule *mod, const ClassCallBack &cb)
91 {
92 LIBABCKIT_LOG_FUNC;
93
94 g_implI->moduleEnumerateClasses(mod, (void *)(&cb), [](AbckitCoreClass *klass, void *data) {
95 const auto &cb = *((ClassCallBack *)data);
96 cb(klass);
97 return true;
98 });
99 }
100
101 template <class MethodCallBack>
EnumerateClassMethods(AbckitCoreClass * klass,const MethodCallBack & cb)102 inline void EnumerateClassMethods(AbckitCoreClass *klass, const MethodCallBack &cb)
103 {
104 LIBABCKIT_LOG_FUNC;
105 g_implI->classEnumerateMethods(klass, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
106 const auto &cb = *((MethodCallBack *)data);
107 cb(method);
108 return true;
109 });
110 }
111
112 template <class AnnotationCallBack>
EnumerateMethodAnnotations(AbckitCoreFunction * m,const AnnotationCallBack & cb)113 inline void EnumerateMethodAnnotations(AbckitCoreFunction *m, const AnnotationCallBack &cb)
114 {
115 g_implI->functionEnumerateAnnotations(m, (void *)(&cb), [](AbckitCoreAnnotation *an, void *data) {
116 const auto &cb = *((AnnotationCallBack *)data);
117 cb(an);
118 return true;
119 });
120 }
121
122 template <class AnnotationElementCallBack>
EnumerateAnnotationElements(AbckitCoreAnnotation * an,const AnnotationElementCallBack & cb)123 inline void EnumerateAnnotationElements(AbckitCoreAnnotation *an, const AnnotationElementCallBack &cb)
124 {
125 g_implI->annotationEnumerateElements(an, (void *)(&cb), [](AbckitCoreAnnotationElement *ele, void *data) {
126 const auto &cb = *((AnnotationElementCallBack *)data);
127 cb(ele);
128 return true;
129 });
130 }
131
132 template <class ModuleCallBack>
EnumerateModules(const ModuleCallBack & cb,AbckitFile * file)133 inline void EnumerateModules(const ModuleCallBack &cb, AbckitFile *file)
134 {
135 LIBABCKIT_LOG_FUNC;
136
137 g_implI->fileEnumerateModules(file, (void *)(&cb), [](AbckitCoreModule *mod, void *data) {
138 const auto &cb = *((ModuleCallBack *)(data));
139 cb(mod);
140 return true;
141 });
142 }
143
144 constexpr auto API_CLASS_NAME = "ApiControl";
145 constexpr auto API_METHOD_NAME = "fixOrientationLinearLayout";
146 constexpr auto API_MODULE_NAME = "modules/ApiControl";
147 constexpr auto ANNOTATION_INTERFACE_NAME = "CallSiteReplacement";
148
149 struct UserData {
150 AbckitString *targetClass;
151 AbckitString *methodName;
152 AbckitCoreClass *classToReplace;
153 AbckitCoreFunction *methodToReplace;
154 };
155
HasSearchedMethod(AbckitCoreFunction * method,UserData & userData)156 bool HasSearchedMethod(AbckitCoreFunction *method, UserData &userData)
157 {
158 bool isFind = false;
159 EnumerateFunctionInsts(method, [&](AbckitInst *inst) {
160 if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
161 return;
162 }
163 bool findNew = false;
164 EnumerateInstUsers(inst, [&](AbckitInst *user) {
165 if (g_dynG->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_NEWOBJRANGE) {
166 findNew = true;
167 }
168 });
169 EnumerateInstUsers(inst, [&](AbckitInst *user) {
170 if (findNew && !isFind &&
171 g_dynG->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_THROW_UNDEFINEDIFHOLEWITHNAME) {
172 auto str = g_implI->abckitStringToString(g_implG->iGetString(user));
173 auto targetClass = g_implI->abckitStringToString(userData.targetClass);
174 isFind = targetClass == str;
175 }
176 });
177 });
178
179 return isFind;
180 }
181
182 struct ModuleByNameContext {
183 AbckitCoreModule *module;
184 const char *name;
185 };
186
ModuleByNameFinder(AbckitCoreModule * module,void * data)187 bool ModuleByNameFinder(AbckitCoreModule *module, void *data)
188 {
189 auto ctxFinder = reinterpret_cast<ModuleByNameContext *>(data);
190 auto name = g_implI->abckitStringToString(g_implI->moduleGetName(module));
191 if (strcmp(name, ctxFinder->name) == 0) {
192 ctxFinder->module = module;
193 return false;
194 }
195
196 return true;
197 }
198
FindFirstInst(AbckitGraph * graph,AbckitIsaApiDynamicOpcode opcode)199 AbckitInst *FindFirstInst(AbckitGraph *graph, AbckitIsaApiDynamicOpcode opcode)
200 {
201 std::vector<AbckitBasicBlock *> bbs;
202 g_implG->gVisitBlocksRpo(graph, &bbs, [](AbckitBasicBlock *bb, void *data) {
203 reinterpret_cast<std::vector<AbckitBasicBlock *> *>(data)->emplace_back(bb);
204 return true;
205 });
206 for (auto *bb : bbs) {
207 auto *curInst = g_implG->bbGetFirstInst(bb);
208 while (curInst != nullptr) {
209 if (g_dynG->iGetOpcode(curInst) == opcode) {
210 return curInst;
211 }
212 curInst = g_implG->iGetNext(curInst);
213 }
214 }
215 return nullptr;
216 }
217
218 struct VisitData {
219 AbckitGraph *ctxG = nullptr;
220 UserData *ud = nullptr;
221 AbckitCoreImportDescriptor *ci = nullptr;
222 AbckitInst *newInst = nullptr;
223 };
224
VisitBlock(AbckitBasicBlock * bb,void * data)225 bool VisitBlock(AbckitBasicBlock *bb, void *data)
226 {
227 auto *vData = reinterpret_cast<VisitData *>(data);
228 auto *inst = g_implG->bbGetFirstInst(bb);
229 while (inst != nullptr) {
230 if (g_dynG->iGetOpcode(inst) == ABCKIT_ISA_API_DYNAMIC_OPCODE_CALLTHIS1) {
231 auto *ldExternal = g_dynG->iCreateLdexternalmodulevar(vData->ctxG, vData->ci);
232 auto *classThrow = g_dynG->iCreateThrowUndefinedifholewithname(
233 vData->ctxG, ldExternal, g_implI->classGetName(vData->ud->classToReplace));
234 auto *constInst = g_implG->gFindOrCreateConstantI32(vData->ctxG, 5);
235 auto *ldobj = g_dynG->iCreateLdobjbyname(vData->ctxG, ldExternal,
236 g_implI->functionGetName(vData->ud->methodToReplace));
237
238 auto *staticCall = g_dynG->iCreateCallthis2(vData->ctxG, ldobj, ldExternal, vData->newInst, constInst);
239
240 if (inst == nullptr) {
241 return false;
242 }
243
244 g_implG->iInsertAfter(ldExternal, inst);
245 g_implG->iInsertAfter(classThrow, ldExternal);
246 g_implG->iInsertAfter(ldobj, classThrow);
247 g_implG->iInsertAfter(staticCall, ldobj);
248 }
249 inst = g_implG->iGetNext(inst);
250 }
251 return true;
252 }
253
ReplaceCallSite(AbckitCoreFunction * method,UserData & userData)254 void ReplaceCallSite(AbckitCoreFunction *method, UserData &userData)
255 {
256 // Create import of modules/ApiControl module and import ApiControl from there
257 AbckitArktsImportFromDynamicModuleCreateParams params {};
258 params.name = API_CLASS_NAME;
259 params.alias = API_CLASS_NAME;
260
261 ModuleByNameContext ctxFinder = {nullptr, API_MODULE_NAME};
262 g_implI->fileEnumerateModules(g_implI->functionGetFile(method), &ctxFinder, ModuleByNameFinder);
263 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
264 ASSERT_NE(ctxFinder.module, nullptr);
265 auto *newImport = g_implArkM->moduleAddImportFromArktsV1ToArktsV1(
266 g_implArkI->coreModuleToArktsModule(g_implI->functionGetModule(method)),
267 g_implArkI->coreModuleToArktsModule(ctxFinder.module), ¶ms);
268
269 auto *coreImport = g_implArkI->arktsImportDescriptorToCoreImportDescriptor(newImport);
270 auto ctxG = g_implI->createGraphFromFunction(method);
271
272 VisitData vd;
273 vd.ctxG = ctxG;
274 vd.ci = coreImport;
275 vd.ud = &userData;
276 vd.newInst = FindFirstInst(ctxG, ABCKIT_ISA_API_DYNAMIC_OPCODE_NEWOBJRANGE);
277
278 g_implG->gVisitBlocksRpo(ctxG, &vd, [](AbckitBasicBlock *bb, void *data) -> bool { return VisitBlock(bb, data); });
279 g_implM->functionSetGraph(method, ctxG);
280 g_impl->destroyGraph(ctxG);
281 }
282
CollectAnnoInfo(std::vector<std::tuple<AbckitCoreClass *,AbckitCoreFunction *,AbckitCoreAnnotation * >> & infos,AbckitCoreClass * klass)283 void CollectAnnoInfo(std::vector<std::tuple<AbckitCoreClass *, AbckitCoreFunction *, AbckitCoreAnnotation *>> &infos,
284 AbckitCoreClass *klass)
285 {
286 EnumerateClassMethods(klass, [&](AbckitCoreFunction *method) {
287 EnumerateMethodAnnotations(method, [&](AbckitCoreAnnotation *anno) {
288 auto *annoClass = g_implI->annotationGetInterface(anno);
289 auto annoName = g_implI->abckitStringToString(g_implI->annotationInterfaceGetName(annoClass));
290 if (strcmp(annoName, ANNOTATION_INTERFACE_NAME) != 0) {
291 return;
292 }
293 infos.emplace_back(klass, method, anno);
294 });
295 });
296 }
297
FillAnnotationInfo(AbckitCoreModule * mod,UserData & ud)298 void FillAnnotationInfo(AbckitCoreModule *mod, UserData &ud)
299 {
300 std::vector<std::tuple<AbckitCoreClass *, AbckitCoreFunction *, AbckitCoreAnnotation *>> infos;
301 EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { CollectAnnoInfo(infos, klass); });
302 for (auto &[klass, method, anno] : infos) {
303 EnumerateAnnotationElements(anno, [&](AbckitCoreAnnotationElement *annoElem) {
304 auto annoElemName = g_implI->abckitStringToString(g_implI->annotationElementGetName(annoElem));
305 auto *value = g_implI->annotationElementGetValue(annoElem);
306 if (std::string_view(annoElemName) == "targetClass") {
307 ud.targetClass = g_implI->valueGetString(value);
308 }
309 if (std::string_view(annoElemName) == "methodName") {
310 ud.methodName = g_implI->valueGetString(value);
311 }
312 });
313 ud.classToReplace = klass;
314 ud.methodToReplace = method;
315 }
316 }
317 } // namespace
318
319 namespace libabckit::test {
320
321 class AbckitScenarioCTestClean : public ::testing::Test {};
322
ClassReplaceCallSite(UserData & ud,AbckitCoreClass * klass)323 static void ClassReplaceCallSite(UserData &ud, AbckitCoreClass *klass)
324 {
325 EnumerateClassMethods(klass, [&](AbckitCoreFunction *method) {
326 if (!HasSearchedMethod(method, ud)) {
327 return;
328 }
329 ReplaceCallSite(method, ud);
330 });
331 }
332
333 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestDynamicReplaceCallSiteClean)334 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestDynamicReplaceCallSiteClean)
335 {
336 std::string inputPath = ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/replace_call_site/replace_call_site.abc";
337 std::string outputPath =
338 ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/replace_call_site/replace_call_site_modified.abc";
339
340 auto output = helpers::ExecuteDynamicAbc(inputPath, "replace_call_site");
341 EXPECT_TRUE(helpers::Match(output, "3\n"));
342
343 AbckitFile *file = g_impl->openAbc(inputPath.c_str(), inputPath.size());
344 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
345
346 UserData ud {};
347
348 EnumerateModules(
349 [&](AbckitCoreModule *mod) {
350 auto moduleName = g_implI->abckitStringToString(g_implI->moduleGetName(mod));
351 if (std::strcmp(moduleName, API_MODULE_NAME) != 0) {
352 return;
353 }
354
355 FillAnnotationInfo(mod, ud);
356 },
357 file);
358
359 // "modules/ApiControl" "modules/ApiControl"
360 ASSERT_EQ((std::string)g_implI->abckitStringToString(g_implI->classGetName(ud.classToReplace)),
361 (std::string)API_CLASS_NAME);
362 ASSERT_EQ((std::string)g_implI->abckitStringToString(g_implI->functionGetName(ud.methodToReplace)),
363 (std::string)API_METHOD_NAME);
364
365 EnumerateModules(
366 [&](AbckitCoreModule *mod) {
367 auto moduleName = g_implI->abckitStringToString(g_implI->moduleGetName(mod));
368 if (std::string_view(moduleName) != "replace_call_site") {
369 return;
370 }
371 EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { ClassReplaceCallSite(ud, klass); });
372 },
373 file);
374
375 g_impl->writeAbc(file, outputPath.c_str(), outputPath.size());
376 g_impl->closeFile(file);
377 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
378
379 output = helpers::ExecuteDynamicAbc(outputPath, "replace_call_site");
380 EXPECT_TRUE(helpers::Match(output,
381 "fixOrientationLinearLayout was called\n"
382 "5\n"));
383 }
384 } // namespace libabckit::test
385