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/extensions/arkts/metadata_arkts.h"
19 #include "helpers/helpers.h"
20 #include "helpers/helpers_runtime.h"
21 #include "tests/helpers/visit_helper/visit_helper-inl.h"
22 #include "metadata_inspect_impl.h"
23 #include "libabckit/src/logger.h"
24
25 namespace {
26
27 auto g_impl = AbckitGetApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
28 auto g_implI = AbckitGetInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
29 auto g_implM = AbckitGetModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
30 auto g_implG = AbckitGetGraphApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
31 auto g_dynG = AbckitGetIsaApiDynamicImpl(ABCKIT_VERSION_RELEASE_1_0_0);
32 auto g_implArkI = AbckitGetArktsInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
33 auto g_implArkM = AbckitGetArktsModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
34
35 const std::string ROUTER_MAP_FILE_MODULE_NAME = "modules/routerMap";
36 const std::string ROUTER_ANNOTATION_NAME = "Router";
37 const std::string FUNC_MAIN_0 = "func_main_0";
38
39 template <class ModuleCallBack>
EnumerateModules(const ModuleCallBack & cb,AbckitFile * file)40 inline void EnumerateModules(const ModuleCallBack &cb, AbckitFile *file)
41 {
42 LIBABCKIT_LOG_FUNC;
43
44 g_implI->fileEnumerateModules(file, (void *)(&cb), [](AbckitCoreModule *mod, void *data) {
45 const auto &cb = *((ModuleCallBack *)(data));
46 cb(mod);
47 return true;
48 });
49 }
50
51 template <class ClassCallBack>
EnumerateModuleClasses(AbckitCoreModule * mod,const ClassCallBack & cb)52 inline void EnumerateModuleClasses(AbckitCoreModule *mod, const ClassCallBack &cb)
53 {
54 LIBABCKIT_LOG_FUNC;
55
56 g_implI->moduleEnumerateClasses(mod, (void *)(&cb), [](AbckitCoreClass *klass, void *data) {
57 const auto &cb = *((ClassCallBack *)data);
58 cb(klass);
59 return true;
60 });
61 }
62
63 template <class AnnotationCallBack>
EnumerateClassAnnotations(AbckitCoreClass * klass,const AnnotationCallBack & cb)64 inline void EnumerateClassAnnotations(AbckitCoreClass *klass, const AnnotationCallBack &cb)
65 {
66 g_implI->classEnumerateAnnotations(klass, (void *)(&cb), [](AbckitCoreAnnotation *an, void *data) {
67 const auto &cb = *((AnnotationCallBack *)data);
68 cb(an);
69 return true;
70 });
71 }
72
73 template <class AnnotationElementCallBack>
EnumerateAnnotationElements(AbckitCoreAnnotation * an,const AnnotationElementCallBack & cb)74 inline void EnumerateAnnotationElements(AbckitCoreAnnotation *an, const AnnotationElementCallBack &cb)
75 {
76 g_implI->annotationEnumerateElements(an, (void *)(&cb), [](AbckitCoreAnnotationElement *ele, void *data) {
77 const auto &cb = *((AnnotationElementCallBack *)data);
78 cb(ele);
79 return true;
80 });
81 }
82
83 struct RouterAnnotation {
84 AbckitCoreClass *owner;
85 AbckitCoreAnnotation *ptrToAnno;
86 AbckitString *scheme;
87 AbckitString *path;
88 };
89
90 struct UserData {
91 AbckitString *classStr;
92 AbckitString *moduleStr;
93 RouterAnnotation routerInfo;
94 };
95
96 struct LocalData {
97 size_t idx;
98 AbckitInst *insertAfterInst;
99 AbckitCoreImportDescriptor *coreImport;
100 };
101
FindFirstInst(AbckitGraph * graph,AbckitIsaApiDynamicOpcode opcode)102 AbckitInst *FindFirstInst(AbckitGraph *graph, AbckitIsaApiDynamicOpcode opcode)
103 {
104 std::vector<AbckitBasicBlock *> bbs;
105 g_implG->gVisitBlocksRpo(graph, &bbs, [](AbckitBasicBlock *bb, void *data) {
106 reinterpret_cast<std::vector<AbckitBasicBlock *> *>(data)->emplace_back(bb);
107 return true;
108 });
109 for (auto *bb : bbs) {
110 auto *curInst = g_implG->bbGetFirstInst(bb);
111 while (curInst != nullptr) {
112 if (g_dynG->iGetOpcode(curInst) == opcode) {
113 return curInst;
114 }
115 curInst = g_implG->iGetNext(curInst);
116 }
117 }
118 return nullptr;
119 }
120
TransformMethod(AbckitCoreFunction * method,VisitHelper & visitor,const UserData * ud,LocalData * ld)121 void TransformMethod(AbckitCoreFunction *method, VisitHelper &visitor, const UserData *ud, LocalData *ld)
122 {
123 visitor.TransformMethod(method, [&](AbckitFile *file, AbckitCoreFunction *method) {
124 auto ctxG = g_implI->createGraphFromFunction(method);
125 AbckitBasicBlock *startBB = g_implG->gGetStartBasicBlock(ctxG);
126 std::vector<AbckitBasicBlock *> succBBs;
127 g_implG->bbVisitSuccBlocks(startBB, &succBBs, [](AbckitBasicBlock *succBasicBlock, void *d) {
128 auto *succs = reinterpret_cast<std::vector<AbckitBasicBlock *> *>(d);
129 succs->emplace_back(succBasicBlock);
130 return true;
131 });
132 AbckitInst *createEmptyArray = FindFirstInst(ctxG, ABCKIT_ISA_API_DYNAMIC_OPCODE_CREATEEMPTYARRAY);
133
134 const auto &routerInfo = ud->routerInfo;
135 std::string fullPath =
136 std::string(visitor.GetString(routerInfo.scheme)) + std::string(visitor.GetString(routerInfo.path));
137 auto arr = std::vector<AbckitLiteral *>();
138 AbckitLiteral *str = g_implM->createLiteralString(file, fullPath.data(), fullPath.size());
139 arr.emplace_back(str);
140
141 auto *litArr = g_implM->createLiteralArray(file, arr.data(), arr.size());
142 auto *createArrayWithBuffer = g_dynG->iCreateCreatearraywithbuffer(ctxG, litArr);
143
144 if (ld->insertAfterInst == nullptr) {
145 ld->insertAfterInst = createEmptyArray;
146 }
147
148 auto *ldExternal = g_dynG->iCreateLdexternalmodulevar(ctxG, ld->coreImport);
149
150 auto *classThrow =
151 g_dynG->iCreateThrowUndefinedifholewithname(ctxG, ldExternal, g_implI->classGetName(routerInfo.owner));
152
153 auto *newObj = g_dynG->iCreateNewobjrange(ctxG, 1, ldExternal);
154
155 auto *stownByIndex1 = g_dynG->iCreateStownbyindex(ctxG, newObj, createArrayWithBuffer, 1);
156
157 auto *stownByIndex2 = g_dynG->iCreateStownbyindex(ctxG, createArrayWithBuffer, createEmptyArray, ld->idx++);
158
159 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
160
161 g_implG->iInsertAfter(createArrayWithBuffer, ld->insertAfterInst);
162 g_implG->iInsertAfter(ldExternal, createArrayWithBuffer);
163 g_implG->iInsertAfter(classThrow, ldExternal);
164 g_implG->iInsertAfter(newObj, classThrow);
165 g_implG->iInsertAfter(stownByIndex1, newObj);
166 g_implG->iInsertAfter(stownByIndex2, stownByIndex1);
167
168 ld->insertAfterInst = stownByIndex2;
169
170 g_implM->functionSetGraph(method, ctxG);
171 g_impl->destroyGraph(ctxG);
172 });
173 }
174
175 struct ModuleByNameContext {
176 AbckitCoreModule *module;
177 const char *name;
178 };
179
ModuleByNameFinder(AbckitCoreModule * module,void * data)180 bool ModuleByNameFinder(AbckitCoreModule *module, void *data)
181 {
182 auto ctxFinder = reinterpret_cast<ModuleByNameContext *>(data);
183 auto name = g_implI->abckitStringToString(g_implI->moduleGetName(module));
184 if (strcmp(name, ctxFinder->name) == 0) {
185 ctxFinder->module = module;
186 return false;
187 }
188
189 return true;
190 }
191
ModifyRouterTable(AbckitCoreFunction * method,VisitHelper & visitor,const std::vector<UserData> & udContainer)192 void ModifyRouterTable(AbckitCoreFunction *method, VisitHelper &visitor, const std::vector<UserData> &udContainer)
193 {
194 LocalData ld {};
195 ld.idx = 0;
196 ld.insertAfterInst = nullptr;
197 for (const auto &ud : udContainer) {
198 const auto &className = visitor.GetString(ud.classStr);
199 AbckitArktsImportFromDynamicModuleCreateParams params {};
200 params.name = className.data();
201 params.alias = className.data();
202
203 const auto &moduleName = visitor.GetString(ud.moduleStr);
204 ModuleByNameContext ctxFinder = {nullptr, moduleName.data()};
205 g_implI->fileEnumerateModules(g_implI->functionGetFile(method), &ctxFinder, ModuleByNameFinder);
206 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
207 ASSERT_NE(ctxFinder.module, nullptr);
208 auto *newImport = g_implArkM->moduleAddImportFromArktsV1ToArktsV1(
209 g_implArkI->coreModuleToArktsModule(g_implI->functionGetModule(method)),
210 g_implArkI->coreModuleToArktsModule(ctxFinder.module), ¶ms);
211 ld.coreImport = g_implArkI->arktsImportDescriptorToCoreImportDescriptor(newImport);
212 TransformMethod(method, visitor, &ud, &ld);
213 }
214 }
215
FindMethodWithRouterTable(VisitHelper & visitor)216 AbckitCoreFunction *FindMethodWithRouterTable(VisitHelper &visitor)
217 {
218 AbckitCoreFunction *routerFunc = nullptr;
219 visitor.EnumerateModules([&](AbckitCoreModule *mod) {
220 auto moduleName = visitor.GetString(g_implI->moduleGetName(mod));
221 if (moduleName != ROUTER_MAP_FILE_MODULE_NAME) {
222 return;
223 }
224 visitor.EnumerateModuleTopLevelFunctions(mod, [&](AbckitCoreFunction *func) {
225 auto funcName = visitor.GetString(g_implI->functionGetName(func));
226 if (funcName != FUNC_MAIN_0) {
227 return;
228 }
229 routerFunc = func;
230 });
231 });
232 return routerFunc;
233 }
234
RemoveAnnotations(const std::vector<UserData> & udContainer)235 void RemoveAnnotations(const std::vector<UserData> &udContainer)
236 {
237 for (const auto &ud : udContainer) {
238 const auto &routerInfo = ud.routerInfo;
239 g_implArkM->classRemoveAnnotation(g_implArkI->coreClassToArktsClass(routerInfo.owner),
240 g_implArkI->coreAnnotationToArktsAnnotation(routerInfo.ptrToAnno));
241 }
242 }
243
CollectClassInfo(std::vector<UserData> & udContainer,AbckitCoreModule * mod,AbckitCoreClass * klass)244 void CollectClassInfo(std::vector<UserData> &udContainer, AbckitCoreModule *mod, AbckitCoreClass *klass)
245 {
246 EnumerateClassAnnotations(klass, [&](AbckitCoreAnnotation *anno) {
247 auto *annoClass = g_implI->annotationGetInterface(anno);
248 auto annoName = g_implI->abckitStringToString(g_implI->annotationInterfaceGetName(annoClass));
249 if (annoName != ROUTER_ANNOTATION_NAME) {
250 return;
251 }
252 UserData ud {};
253 ud.classStr = g_implI->classGetName(klass);
254 ud.moduleStr = g_implI->moduleGetName(mod);
255 auto &routerInfo = ud.routerInfo;
256 routerInfo.ptrToAnno = anno;
257 routerInfo.owner = klass;
258 EnumerateAnnotationElements(anno, [&](AbckitCoreAnnotationElement *annoElem) {
259 auto annoElemName = g_implI->abckitStringToString(g_implI->annotationElementGetName(annoElem));
260 auto *value = g_implI->annotationElementGetValue(annoElem);
261 if (std::string_view(annoElemName) == "scheme") {
262 routerInfo.scheme = g_implI->valueGetString(value);
263 }
264 if (std::string_view(annoElemName) == "path") {
265 routerInfo.path = g_implI->valueGetString(value);
266 }
267 });
268 udContainer.push_back(ud);
269 });
270 }
271
CollectClassesInfo(std::vector<UserData> & udContainer,AbckitFile * file)272 void CollectClassesInfo(std::vector<UserData> &udContainer, AbckitFile *file)
273 {
274 EnumerateModules(
275 [&](AbckitCoreModule *mod) {
276 EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { CollectClassInfo(udContainer, mod, klass); });
277 },
278 file);
279 }
280
ClassHasAnnotation(VisitHelper & visitor,const UserData & ud)281 bool ClassHasAnnotation(VisitHelper &visitor, const UserData &ud)
282 {
283 bool found = false;
284
285 auto classAnnotationsEnumCb = [&](AbckitCoreAnnotation *anno) {
286 auto *annoClass = g_implI->annotationGetInterface(anno);
287 auto annoName = visitor.GetString(g_implI->annotationInterfaceGetName(annoClass));
288 if (annoName == ROUTER_ANNOTATION_NAME) {
289 found = true;
290 }
291 };
292
293 visitor.EnumerateModules([&](AbckitCoreModule *mod) {
294 if (visitor.GetString(g_implI->moduleGetName(mod)) != visitor.GetString(ud.moduleStr)) {
295 return;
296 }
297 visitor.EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) {
298 if (visitor.GetString(g_implI->classGetName(klass)) != visitor.GetString(ud.classStr)) {
299 return;
300 }
301 visitor.EnumerateClassAnnotations(klass, classAnnotationsEnumCb);
302 });
303 });
304 return found;
305 }
306 } // namespace
307
308 namespace libabckit::test {
309
310 class AbckitScenarioCTestClean : public ::testing::Test {};
311
312 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestDynamicRouterTableClean)313 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestDynamicRouterTableClean)
314 {
315 std::string inputPath = ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/router_table/router_table.abc";
316 std::string outputPath = ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/router_table/router_table_modified.abc";
317
318 auto output = helpers::ExecuteDynamicAbc(inputPath, "router_table");
319 EXPECT_TRUE(helpers::Match(output, ""));
320
321 AbckitFile *file = g_impl->openAbc(inputPath.c_str(), inputPath.size());
322 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
323
324 auto visitor = VisitHelper(file, g_impl, g_implI, g_implG, g_dynG);
325
326 std::vector<UserData> userData;
327
328 CollectClassesInfo(userData, file);
329 RemoveAnnotations(userData);
330 auto *method = FindMethodWithRouterTable(visitor);
331 ModifyRouterTable(method, visitor, userData);
332
333 g_impl->writeAbc(file, outputPath.c_str(), outputPath.size());
334 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
335
336 for (const auto &ud : userData) {
337 ASSERT_FALSE(ClassHasAnnotation(visitor, ud));
338 }
339
340 output = helpers::ExecuteDynamicAbc(outputPath, "router_table");
341
342 g_impl->closeFile(file);
343
344 EXPECT_TRUE(helpers::Match(output,
345 "xxxHandler.handle was called\n"
346 "handle1/xxx\n"));
347 }
348
349 } // namespace libabckit::test
350