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 "libabckit/include/cpp/abckit_cpp.h"
17
18 #include "helpers/helpers.h"
19 #include "helpers/helpers_runtime.h"
20 #include "libabckit/src/logger.h"
21
22 #include <gtest/gtest.h>
23
24 #include <optional>
25 #include <string_view>
26
27 namespace libabckit::test {
28
29 namespace {
30
31 using FunctionCallback = std::function<bool(abckit::core::Function)>;
32 using ClassCallback = std::function<bool(abckit::core::Class)>;
33 using NamespaceCallback = std::function<bool(abckit::core::Namespace)>;
34
35 class GTestAssertErrorHandler final : public abckit::IErrorHandler {
36 public:
HandleError(abckit::Exception && err)37 void HandleError(abckit::Exception &&err) override
38 {
39 EXPECT_TRUE(false) << "Abckit expection raised: " << err.what();
40 }
41 };
42
43 struct UserData {
44 std::string_view print;
45 std::string_view date;
46 std::string_view getTime;
47 std::string_view str;
48 std::string_view consume;
49 };
50
GetMethodName(const abckit::core::Function & method)51 std::string GetMethodName(const abckit::core::Function &method)
52 {
53 const auto name = method.GetName();
54 return name.substr(0, name.find(':'));
55 }
56
EnumerateAllMethods(const abckit::File & file,const FunctionCallback & fnUserCallback)57 void EnumerateAllMethods(const abckit::File &file, const FunctionCallback &fnUserCallback)
58 {
59 ClassCallback clsCallback;
60
61 FunctionCallback fnCallback = [&](const abckit::core::Function &fun) -> bool {
62 if (!fnUserCallback(fun)) {
63 return false;
64 }
65 fun.EnumerateNestedFunctions(fnCallback);
66 fun.EnumerateNestedClasses(clsCallback);
67 return true;
68 };
69
70 clsCallback = [&](const abckit::core::Class &cls) -> bool {
71 cls.EnumerateMethods(fnCallback);
72 return true;
73 };
74
75 NamespaceCallback nsCallback = [&](const abckit::core::Namespace &ns) -> bool {
76 ns.EnumerateNamespaces(nsCallback);
77 ns.EnumerateClasses(clsCallback);
78 ns.EnumerateTopLevelFunctions(fnCallback);
79 return true;
80 };
81
82 file.EnumerateModules([&](const abckit::core::Module &mod) -> bool {
83 mod.EnumerateNamespaces(nsCallback);
84 mod.EnumerateClasses(clsCallback);
85 mod.EnumerateTopLevelFunctions(fnCallback);
86 return true;
87 });
88 }
89
CreateProlog(abckit::Graph & graph,const UserData & userData)90 abckit::Instruction CreateProlog(abckit::Graph &graph, const UserData &userData)
91 {
92 const abckit::BasicBlock startBB = graph.GetStartBb();
93 EXPECT_GT(startBB.GetSuccCount(), 0);
94 auto bb = startBB.GetSuccByIdx(0);
95
96 auto iStr = graph.DynIsa().CreateLoadString(userData.str);
97 bb.AddInstFront(iStr);
98
99 auto iPrint = graph.DynIsa().CreateTryldglobalbyname(userData.print).InsertAfter(iStr);
100 auto iCallArg = graph.DynIsa().CreateCallarg1(iPrint, iStr).InsertAfter(iPrint);
101 auto iDateClass = graph.DynIsa().CreateTryldglobalbyname(userData.date).InsertAfter(iCallArg);
102 auto iDateObj = graph.DynIsa().CreateNewobjrange(iDateClass).InsertAfter(iDateClass);
103 auto iGetTime = graph.DynIsa().CreateLdobjbyname(iDateObj, userData.getTime).InsertAfter(iDateObj);
104 auto iTimeStart = graph.DynIsa().CreateCallthis0(iGetTime, iDateObj).InsertAfter(iGetTime);
105 return iTimeStart;
106 }
107
CreateEpilog(abckit::Graph & graph,const abckit::BasicBlock & bb,const abckit::Instruction & iTimeStart,const UserData & userData)108 void CreateEpilog(abckit::Graph &graph, const abckit::BasicBlock &bb, const abckit::Instruction &iTimeStart,
109 const UserData &userData)
110 {
111 for (abckit::Instruction inst = bb.GetFirstInst(); !!inst; inst = inst.GetNext()) {
112 if (inst.GetGraph()->DynIsa().GetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_RETURNUNDEFINED) {
113 continue;
114 }
115
116 auto iDateClass = graph.DynIsa().CreateTryldglobalbyname(userData.date).InsertBefore(inst);
117
118 auto iDateObj = graph.DynIsa().CreateNewobjrange(iDateClass).InsertAfter(iDateClass);
119 auto iGetTime = graph.DynIsa().CreateLdobjbyname(iDateObj, userData.getTime).InsertAfter(iDateObj);
120 auto iTimeEnd = graph.DynIsa().CreateCallthis0(iGetTime, iDateObj).InsertAfter(iGetTime);
121 auto iConsume = graph.DynIsa().CreateLoadString(userData.consume).InsertAfter(iTimeEnd);
122 auto iPrint = graph.DynIsa().CreateTryldglobalbyname(userData.print).InsertAfter(iConsume);
123 auto iCallPrintConsume = graph.DynIsa().CreateCallarg1(iPrint, iConsume).InsertAfter(iPrint);
124 auto iSub = graph.DynIsa().CreateSub2(iTimeStart, iTimeEnd).InsertAfter(iCallPrintConsume);
125 auto iCallPrintSub = graph.DynIsa().CreateCallarg1(iPrint, iSub).InsertAfter(iSub);
126 }
127 }
128
TransformIr(abckit::Graph & graph,const UserData & userData)129 void TransformIr(abckit::Graph &graph, const UserData &userData)
130 {
131 const abckit::Instruction iTimeStart = CreateProlog(graph, userData);
132 graph.EnumerateBasicBlocksRpo([&](const abckit::BasicBlock &bb) {
133 CreateEpilog(graph, bb, iTimeStart, userData);
134 return true;
135 });
136 }
137
138 } // namespace
139
140 class AbckitScenarioCppTestClean : public ::testing::Test {};
141
142 // CC-OFFNXT(huge_method, C_RULE_ID_FUNCTION_SIZE) test, solid logic
143 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=cpp
TEST_F(AbckitScenarioCppTestClean,LibAbcKitTestDynamicAddLogClean)144 TEST_F(AbckitScenarioCppTestClean, LibAbcKitTestDynamicAddLogClean)
145 {
146 const std::string testSandboxPath = ABCKIT_ABC_DIR "clean_scenarios/cpp_api/dynamic/add_log/";
147 const std::string srcAbcPath = testSandboxPath + "add_log_dynamic.abc";
148 const std::string dstAbcPath = testSandboxPath + "add_log_dynamic_modified.abc";
149
150 const UserData data {
151 "print", // .print
152 "Date", // .date
153 "getTime", // .getTime
154 "file: src/MyClass, function: MyClass.handle", // .str
155 "Ellapsed time:", // .consume
156 };
157
158 // ExecuteDynamicAbc is helper function needed for testing. Вoes not affect the logic of IR transformation
159 auto output = helpers::ExecuteDynamicAbc(srcAbcPath, "add_log_dynamic");
160 EXPECT_TRUE(helpers::Match(output, "abckit\n"));
161
162 {
163 abckit::File file(srcAbcPath, std::make_unique<GTestAssertErrorHandler>());
164
165 std::optional<abckit::core::Function> handleMethod;
166 EnumerateAllMethods(file, [&](const abckit::core::Function &method) -> bool {
167 auto methodName = GetMethodName(method);
168 if (methodName == "handle") {
169 handleMethod = method;
170 return false;
171 }
172 return true;
173 });
174 EXPECT_TRUE(handleMethod.has_value());
175
176 abckit::Graph graph = handleMethod->CreateGraph();
177 TransformIr(graph, data);
178 handleMethod->SetGraph(graph);
179
180 file.WriteAbc(dstAbcPath);
181 }
182
183 output = helpers::ExecuteDynamicAbc(dstAbcPath, "add_log_dynamic");
184 EXPECT_TRUE(helpers::Match(output,
185 "file: src/MyClass, function: MyClass.handle\n"
186 "abckit\n"
187 "Ellapsed time:\n"
188 "\\d+\n"));
189 }
190
191 } // namespace libabckit::test
192