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/cpp/abckit_cpp.h"
19
20 #include "helpers/helpers.h"
21 #include "helpers/helpers_runtime.h"
22 #include "libabckit/src/logger.h"
23
24 namespace {
25
26 class ErrorHandler final : public abckit::IErrorHandler {
HandleError(abckit::Exception && e)27 void HandleError(abckit::Exception &&e) override
28 {
29 EXPECT_TRUE(false) << "Abckit exception raised: " << e.what();
30 }
31 };
32
33 const std::string ROUTER_MAP_FILE_MODULE_NAME = "modules/routerMap";
34 const std::string ROUTER_ANNOTATION_NAME = "Router";
35 const std::string FUNC_MAIN_0 = "func_main_0";
36
37 struct RouterAnnotation {
38 abckit::core::Class owner;
39 abckit::core::Annotation anno;
40 std::string scheme;
41 std::string path;
42 };
43
44 struct UserData {
45 std::string classStr;
46 std::string moduleStr;
47 RouterAnnotation routerInfo;
48 };
49
FindFirstInst(const abckit::Graph & graph,AbckitIsaApiDynamicOpcode opcode)50 abckit::Instruction FindFirstInst(const abckit::Graph &graph, AbckitIsaApiDynamicOpcode opcode)
51 {
52 std::vector<abckit::BasicBlock> bbs = graph.GetBlocksRPO();
53 for (const auto &bb : bbs) {
54 auto curInst = bb.GetFirstInst();
55 while (curInst) {
56 if (curInst.GetGraph()->DynIsa().GetOpcode(curInst) == opcode) {
57 return curInst;
58 }
59 curInst = curInst.GetNext();
60 }
61 }
62 return abckit::Instruction();
63 }
64
TransformMethod(const abckit::core::Function & method,const UserData & ud,const abckit::core::ImportDescriptor & coreImport)65 void TransformMethod(const abckit::core::Function &method, const UserData &ud,
66 const abckit::core::ImportDescriptor &coreImport)
67 {
68 size_t idx = 0;
69 auto ctxG = method.CreateGraph();
70 abckit::BasicBlock startBB = ctxG.GetStartBb();
71 std::vector<abckit::BasicBlock> succBBs = startBB.GetSuccs();
72 abckit::Instruction createEmptyArray = FindFirstInst(ctxG, ABCKIT_ISA_API_DYNAMIC_OPCODE_CREATEEMPTYARRAY);
73
74 const auto &routerInfo = ud.routerInfo;
75 std::string fullPath = routerInfo.scheme + routerInfo.path;
76 auto arr = std::vector<abckit::Literal>();
77 auto filePtr = method.GetFile();
78 abckit::Literal str = filePtr->CreateLiteralString(fullPath);
79 arr.emplace_back(str);
80
81 auto litArr = filePtr->CreateLiteralArray(arr);
82 auto createArrayWithBuffer = ctxG.DynIsa().CreateCreatearraywithbuffer(litArr);
83
84 auto insertAfterInst = createEmptyArray;
85
86 auto ldExternal = ctxG.DynIsa().CreateLdexternalmodulevar(coreImport);
87 auto classThrow = ctxG.DynIsa().CreateThrowUndefinedifholewithname(ldExternal, routerInfo.owner.GetName());
88 auto newObj = ctxG.DynIsa().CreateNewobjrange(ldExternal);
89 auto stownByIndex1 = ctxG.DynIsa().CreateStownbyindex(newObj, createArrayWithBuffer, 1);
90 auto stownByIndex2 = ctxG.DynIsa().CreateStownbyindex(createArrayWithBuffer, createEmptyArray, idx++);
91
92 createArrayWithBuffer.InsertAfter(insertAfterInst);
93 ldExternal.InsertAfter(createArrayWithBuffer);
94 classThrow.InsertAfter(ldExternal);
95 newObj.InsertAfter(classThrow);
96 stownByIndex1.InsertAfter(newObj);
97 stownByIndex2.InsertAfter(stownByIndex1);
98 insertAfterInst = stownByIndex2;
99
100 method.SetGraph(ctxG);
101 }
102
ModifyRouterTable(const abckit::core::Function & method,const std::vector<UserData> & udContainer)103 void ModifyRouterTable(const abckit::core::Function &method, const std::vector<UserData> &udContainer)
104 {
105 for (const auto &ud : udContainer) {
106 std::optional<abckit::arkts::Module> foundModule;
107
108 method.GetFile()->EnumerateModules([&](const abckit::core::Module &mod) -> bool {
109 if (ud.moduleStr == mod.GetName()) {
110 foundModule = abckit::arkts::Module(mod);
111 }
112 return true;
113 });
114 EXPECT_TRUE(foundModule.has_value());
115
116 abckit::arkts::Module arktsModule = abckit::arkts::Module(method.GetModule());
117 auto newImport = arktsModule.AddImportFromArktsV1ToArktsV1(*foundModule, ud.classStr, ud.classStr);
118 auto coreImport = *static_cast<abckit::core::ImportDescriptor *>(&newImport);
119 TransformMethod(method, ud, coreImport);
120 }
121 }
122
FindMethodWithRouterTable(const abckit::File * filePtr)123 abckit::core::Function FindMethodWithRouterTable(const abckit::File *filePtr)
124 {
125 abckit::core::Function routerFunc;
126 filePtr->EnumerateModules([&](const abckit::core::Module &mod) -> bool {
127 if (mod.GetName() != ROUTER_MAP_FILE_MODULE_NAME) {
128 return true;
129 }
130 mod.EnumerateTopLevelFunctions([&](const abckit::core::Function &func) -> bool {
131 if (func.GetName() == FUNC_MAIN_0) {
132 routerFunc = func;
133 }
134 return true;
135 });
136 return true;
137 });
138 return routerFunc;
139 }
140
RemoveAnnotations(const std::vector<UserData> & udContainer)141 void RemoveAnnotations(const std::vector<UserData> &udContainer)
142 {
143 for (const auto &ud : udContainer) {
144 const auto &routerInfo = ud.routerInfo;
145 abckit::arkts::Class(routerInfo.owner).RemoveAnnotation(abckit::arkts::Annotation(routerInfo.anno));
146 }
147 }
148
CollectClassInfo(std::vector<UserData> & udContainer,const abckit::core::Module & mod,const abckit::core::Class & klass)149 void CollectClassInfo(std::vector<UserData> &udContainer, const abckit::core::Module &mod,
150 const abckit::core::Class &klass)
151 {
152 klass.EnumerateAnnotations([&](const abckit::core::Annotation &anno) -> bool {
153 auto annoClass = anno.GetInterface();
154 auto annoName = annoClass.GetName();
155 if (annoName != ROUTER_ANNOTATION_NAME) {
156 return true;
157 }
158
159 std::string scheme;
160 std::string path;
161 anno.EnumerateElements([&](const abckit::core::AnnotationElement &annoElem) -> bool {
162 auto annoElemName = annoElem.GetName();
163 auto value = annoElem.GetValue();
164 if (annoElemName == "scheme") {
165 scheme = value.GetString();
166 }
167 if (annoElemName == "path") {
168 path = value.GetString();
169 }
170 return true;
171 });
172 UserData ud {klass.GetName(), mod.GetName(), RouterAnnotation {klass, anno, scheme, path}};
173 udContainer.push_back(ud);
174 return true;
175 });
176 }
177
ClassHasAnnotation(const abckit::File * filePtr,const UserData & ud)178 bool ClassHasAnnotation(const abckit::File *filePtr, const UserData &ud)
179 {
180 bool found = false;
181
182 auto classAnnotationsEnumCb = [&](const abckit::core::Annotation &anno) -> bool {
183 auto annoClass = anno.GetInterface();
184 if (annoClass.GetName() == ROUTER_ANNOTATION_NAME) {
185 found = true;
186 }
187 return true;
188 };
189
190 filePtr->EnumerateModules([&](const abckit::core::Module &mod) -> bool {
191 if (mod.GetName() != ud.moduleStr) {
192 return true;
193 }
194 mod.EnumerateClasses([&](const abckit::core::Class &klass) -> bool {
195 if (klass.GetName() == ud.classStr) {
196 klass.EnumerateAnnotations(classAnnotationsEnumCb);
197 }
198 return true;
199 });
200 return true;
201 });
202 return found;
203 }
204
CollectClassesInfo(const abckit::File * filePtr,std::vector<UserData> & udContainer)205 void CollectClassesInfo(const abckit::File *filePtr, std::vector<UserData> &udContainer)
206 {
207 filePtr->EnumerateModules([&](const abckit::core::Module &mod) -> bool {
208 mod.EnumerateClasses([&](const abckit::core::Class &klass) -> bool {
209 CollectClassInfo(udContainer, mod, klass);
210 return true;
211 });
212 return true;
213 });
214 }
215
216 } // namespace
217
218 namespace libabckit::test {
219
220 class AbckitScenarioCppTestClean : public ::testing::Test {};
221
222 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=cpp
TEST_F(AbckitScenarioCppTestClean,LibAbcKitTestDynammicRouterTableClean)223 TEST_F(AbckitScenarioCppTestClean, LibAbcKitTestDynammicRouterTableClean)
224 {
225 const std::string testSandboxPath = ABCKIT_ABC_DIR "clean_scenarios/cpp_api/dynamic/router_table/";
226 const std::string inputPath = testSandboxPath + "router_table.abc";
227 const std::string outputPath = testSandboxPath + "router_table_modified.abc";
228
229 auto output = helpers::ExecuteDynamicAbc(inputPath, "router_table");
230 EXPECT_TRUE(helpers::Match(output, ""));
231
232 abckit::File file(inputPath, std::make_unique<ErrorHandler>());
233
234 const abckit::File *filePtr = &file;
235
236 std::vector<UserData> userData;
237
238 CollectClassesInfo(filePtr, userData);
239 RemoveAnnotations(userData);
240 auto method = FindMethodWithRouterTable(filePtr);
241 ModifyRouterTable(method, userData);
242
243 file.WriteAbc(outputPath);
244
245 for (const auto &ud : userData) {
246 ASSERT_FALSE(ClassHasAnnotation(filePtr, ud));
247 }
248
249 output = helpers::ExecuteDynamicAbc(
250 ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/router_table/router_table_modified.abc", "router_table");
251
252 EXPECT_TRUE(helpers::Match(output,
253 "xxxHandler.handle was called\n"
254 "handle1/xxx\n"));
255 }
256
257 } // namespace libabckit::test
258