• 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 "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), &params);
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