1 /**
2 * Copyright (c) 2024-2025 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 "libabckit/include/c/abckit.h"
17 #include "libabckit/include/c/metadata_core.h"
18 #include "libabckit/include/c/ir_core.h"
19 #include "libabckit/include/c/isa/isa_static.h"
20
21 #include "helpers/helpers.h"
22 #include "helpers/helpers_runtime.h"
23 #include "libabckit/src/logger.h"
24
25 #include <string>
26 #include <vector>
27 #include <functional>
28 #include "metadata_inspect_impl.h"
29
30 #include <gtest/gtest.h>
31 #include <cstddef>
32
33 // NOTE:
34 // * Printed filename is "NOTE", should be real name
35 // * Use actual stdlib calls (instead of user's DateGetTime, ConsoleLogNum, ConsoleLogStr)
36 // * There are several issues related to SaveState in this test:
37 // * Start calls are inserted after first SaveState (not at the beginning of function)
38 // * SaveStates are manipulated by user explicitly
39 // * SaveStates are "INVALID" operations in validation schema
40
41 namespace {
42
43 auto g_impl = AbckitGetApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
44 auto g_implI = AbckitGetInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
45 auto g_implM = AbckitGetModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
46 auto g_implG = AbckitGetGraphApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
47 auto g_statG = AbckitGetIsaApiStaticImpl(ABCKIT_VERSION_RELEASE_1_0_0);
48
49 using CB = std::function<void(AbckitCoreFunction *)>;
50
EnumerateAllMethodsInModule(AbckitFile * file,std::function<void (AbckitCoreNamespace *)> & cbNamespace,std::function<void (AbckitCoreClass *)> & cbClass,std::function<void (AbckitCoreFunction *)> & cbFunc)51 void EnumerateAllMethodsInModule(AbckitFile *file, std::function<void(AbckitCoreNamespace *)> &cbNamespace,
52 std::function<void(AbckitCoreClass *)> &cbClass,
53 std::function<void(AbckitCoreFunction *)> &cbFunc)
54 {
55 std::function<void(AbckitCoreModule *)> cbModule = [&](AbckitCoreModule *m) {
56 g_implI->moduleEnumerateNamespaces(m, &cbNamespace, [](AbckitCoreNamespace *n, void *cb) {
57 (*reinterpret_cast<std::function<void(AbckitCoreNamespace *)> *>(cb))(n);
58 return true;
59 });
60 g_implI->moduleEnumerateClasses(m, &cbClass, [](AbckitCoreClass *c, void *cb) {
61 (*reinterpret_cast<std::function<void(AbckitCoreClass *)> *>(cb))(c);
62 return true;
63 });
64 g_implI->moduleEnumerateTopLevelFunctions(m, &cbFunc, [](AbckitCoreFunction *m, void *cb) {
65 (*reinterpret_cast<std::function<void(AbckitCoreFunction *)> *>(cb))(m);
66 return true;
67 });
68 };
69
70 g_implI->fileEnumerateModules(file, &cbModule, [](AbckitCoreModule *m, void *cb) {
71 (*reinterpret_cast<std::function<void(AbckitCoreModule *)> *>(cb))(m);
72 return true;
73 });
74 }
75
EnumerateAllMethods(AbckitFile * file,const CB & cbUserFunc)76 void EnumerateAllMethods(AbckitFile *file, const CB &cbUserFunc)
77 {
78 CB cbFunc;
79 std::function<void(AbckitCoreClass *)> cbClass;
80
81 cbFunc = [&](AbckitCoreFunction *f) {
82 cbUserFunc(f);
83 g_implI->functionEnumerateNestedFunctions(f, &cbFunc, [](AbckitCoreFunction *f, void *cb) {
84 (*reinterpret_cast<CB *>(cb))(f);
85 return true;
86 });
87 g_implI->functionEnumerateNestedClasses(f, &cbClass, [](AbckitCoreClass *c, void *cb) {
88 (*reinterpret_cast<std::function<void(AbckitCoreClass *)> *>(cb))(c);
89 return true;
90 });
91 };
92
93 cbClass = [&](AbckitCoreClass *c) {
94 g_implI->classEnumerateMethods(c, &cbFunc, [](AbckitCoreFunction *m, void *cb) {
95 (*reinterpret_cast<CB *>(cb))(m);
96 return true;
97 });
98 };
99
100 std::function<void(AbckitCoreNamespace *)> cbNamespace = [&](AbckitCoreNamespace *n) {
101 g_implI->namespaceEnumerateNamespaces(n, &cbNamespace, [](AbckitCoreNamespace *n, void *cb) {
102 (*reinterpret_cast<std::function<void(AbckitCoreNamespace *)> *>(cb))(n);
103 return true;
104 });
105 g_implI->namespaceEnumerateClasses(n, &cbClass, [](AbckitCoreClass *c, void *cb) {
106 (*reinterpret_cast<std::function<void(AbckitCoreClass *)> *>(cb))(c);
107 return true;
108 });
109 g_implI->namespaceEnumerateTopLevelFunctions(n, &cbFunc, [](AbckitCoreFunction *f, void *cb) {
110 (*reinterpret_cast<CB *>(cb))(f);
111 return true;
112 });
113 };
114
115 EnumerateAllMethodsInModule(file, cbNamespace, cbClass, cbFunc);
116 }
117
118 struct UserData {
119 AbckitString *str1 = nullptr;
120 AbckitString *str2 = nullptr;
121 AbckitCoreFunction *consoleLogStr = nullptr;
122 AbckitCoreFunction *consoleLogNum = nullptr;
123 AbckitCoreFunction *dateGetTime = nullptr;
124 AbckitCoreFunction *handle = nullptr;
125 AbckitInst *startTime = nullptr;
126 };
127
TransformIr(AbckitGraph * graph,UserData * userData)128 void TransformIr(AbckitGraph *graph, UserData *userData)
129 {
130 // find first inst with opcode ABCKIT_ISA_API_STATIC_OPCODE_CALL_STATIC
131 AbckitInst *callOp = nullptr;
132 std::vector<AbckitBasicBlock *> bbs;
133 g_implG->gVisitBlocksRpo(graph, &bbs, [](AbckitBasicBlock *bb, void *data) {
134 reinterpret_cast<std::vector<AbckitBasicBlock *> *>(data)->emplace_back(bb);
135 return true;
136 });
137 for (auto *bb : bbs) {
138 auto *curInst = g_implG->bbGetFirstInst(bb);
139 while (curInst != nullptr) {
140 if (g_statG->iGetOpcode(curInst) == ABCKIT_ISA_API_STATIC_OPCODE_CALL_STATIC) {
141 callOp = curInst;
142 }
143 curInst = g_implG->iGetNext(curInst);
144 }
145 }
146
147 // console.log("file: FileName; function: FunctionName")
148 AbckitInst *loadString = g_statG->iCreateLoadString(graph, userData->str1);
149 g_implG->iInsertBefore(loadString, callOp);
150 AbckitInst *log1 = g_statG->iCreateCallStatic(graph, userData->consoleLogStr, 1, loadString);
151 g_implG->iInsertAfter(log1, loadString);
152
153 // const start = Date().getTime()
154 userData->startTime = g_statG->iCreateCallStatic(graph, userData->dateGetTime, 0);
155 g_implG->iInsertAfter(userData->startTime, log1);
156
157 g_implG->gVisitBlocksRpo(graph, userData, [](AbckitBasicBlock *bb, void *data) {
158 auto *inst = g_implG->bbGetFirstInst(bb);
159 auto *userData = reinterpret_cast<UserData *>(data);
160 auto *graph = g_implG->bbGetGraph(bb);
161 while (inst != nullptr) {
162 if (g_statG->iGetOpcode(inst) == ABCKIT_ISA_API_STATIC_OPCODE_RETURN_VOID) {
163 // const end = Date().getTime()
164 AbckitInst *endTime = g_statG->iCreateCallStatic(graph, userData->dateGetTime, 0);
165 g_implG->iInsertBefore(endTime, inst);
166
167 // console.log("Elapsed time:")
168 auto *loadString = g_statG->iCreateLoadString(graph, userData->str2);
169 g_implG->iInsertAfter(loadString, endTime);
170 AbckitInst *log = g_statG->iCreateCallStatic(graph, userData->consoleLogStr, 1, loadString);
171 g_implG->iInsertAfter(log, loadString);
172
173 // console.log(end - start)
174 AbckitInst *sub = g_statG->iCreateSub(graph, endTime, userData->startTime);
175 g_implG->iInsertAfter(sub, log);
176 AbckitInst *log2 = g_statG->iCreateCallStatic(graph, userData->consoleLogNum, 1, sub);
177 g_implG->iInsertAfter(log2, sub);
178 }
179 inst = g_implG->iGetNext(inst);
180 }
181 return true;
182 });
183 }
184
GetMethodName(AbckitCoreFunction * method)185 std::string GetMethodName(AbckitCoreFunction *method)
186 {
187 auto mname = g_implI->functionGetName(method);
188 std::string fullSig = g_implI->abckitStringToString(mname);
189 auto fullName = fullSig.substr(0, fullSig.find(':'));
190 return fullName;
191 }
192 } // namespace
193
194 namespace libabckit::test {
195
196 class AbckitScenarioCTestClean : public ::testing::Test {};
197
198 // Test: test-kind=scenario, abc-kind=ArkTS2, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestStaticAddLogClean)199 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestStaticAddLogClean)
200 {
201 auto output = helpers::ExecuteStaticAbc(ABCKIT_ABC_DIR "clean_scenarios/c_api/static/add_log/add_log_static.abc",
202 "add_log_static/ETSGLOBAL", "main");
203 EXPECT_TRUE(helpers::Match(output, "buisiness logic...\n"));
204
205 constexpr auto INPUT_PATH = ABCKIT_ABC_DIR "clean_scenarios/c_api/static/add_log/add_log_static.abc";
206 AbckitFile *file = g_impl->openAbc(INPUT_PATH, strlen(INPUT_PATH));
207 UserData userData;
208 EnumerateAllMethods(file, [&](AbckitCoreFunction *method) {
209 auto methodName = GetMethodName(method);
210 if (methodName == "ConsoleLogStr") {
211 userData.consoleLogStr = method;
212 }
213 if (methodName == "ConsoleLogNum") {
214 userData.consoleLogNum = method;
215 }
216 if (methodName == "DateGetTime") {
217 userData.dateGetTime = method;
218 }
219 if (methodName == "handle") {
220 userData.handle = method;
221 }
222 });
223
224 std::string startMsg;
225 EnumerateAllMethods(file, [&](AbckitCoreFunction *method) {
226 auto methodName = GetMethodName(method);
227 if (methodName != "handle") {
228 return;
229 }
230 startMsg = "file: NOTE; function: " + methodName;
231 userData.str1 = g_implM->createString(file, startMsg.c_str(), startMsg.size());
232 userData.str2 = g_implM->createString(file, "Elapsed time:", strlen("Elapsed time:"));
233
234 AbckitGraph *graph = g_implI->createGraphFromFunction(method);
235 TransformIr(graph, &userData);
236 g_implM->functionSetGraph(method, graph);
237 g_impl->destroyGraph(graph);
238 });
239
240 constexpr auto OUTPUT_PATH = ABCKIT_ABC_DIR "clean_scenarios/c_api/static/add_log/add_log_static_modified.abc";
241 g_impl->writeAbc(file, OUTPUT_PATH, strlen(OUTPUT_PATH));
242 g_impl->closeFile(file);
243
244 output = helpers::ExecuteStaticAbc(OUTPUT_PATH, "add_log_static/ETSGLOBAL", "main");
245 EXPECT_TRUE(helpers::Match(output,
246 "file: NOTE; function: handle\n"
247 "buisiness logic...\n"
248 "Elapsed time:\n"
249 "\\d+\n"));
250 }
251
252 } // namespace libabckit::test
253