• 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 "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