• 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 
26 namespace {
27 
28 class GTestAssertErrorHandler final : public abckit::IErrorHandler {
29 public:
HandleError(abckit::Exception && err)30     void HandleError(abckit::Exception &&err) override
31     {
32         EXPECT_TRUE(false) << "Abckit expection raised: " << err.what();
33     }
34 };
35 
36 constexpr auto API_CLASS_NAME = "ApiControl";
37 constexpr auto API_METHOD_NAME = "fixOrientationLinearLayout";
38 constexpr auto API_MODULE_NAME = "modules/ApiControl";
39 constexpr auto ANNOTATION_INTERFACE_NAME = "CallSiteReplacement";
40 constexpr auto TARGET_ORIENTATION = 5;
41 
42 struct ReplaceCommand {
43     struct Subject {
44         abckit::core::Class klass;
45         abckit::core::Function method;
46     };
47     Subject subject;
48 
49     struct Target {
50         std::string className;
51         std::string methodName;
52     };
53     Target target;
54 };
55 
HasTargetMethod(const abckit::Instruction & inst,const ReplaceCommand::Target & target)56 bool HasTargetMethod(const abckit::Instruction &inst, const ReplaceCommand::Target &target)
57 {
58     if (inst.GetGraph()->DynIsa().GetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
59         return false;
60     }
61 
62     bool hasNewObjUser = false;
63     inst.VisitUsers([&](const abckit::Instruction &user) -> bool {
64         hasNewObjUser |= user.GetGraph()->DynIsa().GetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_NEWOBJRANGE;
65         return !hasNewObjUser;
66     });
67     if (!hasNewObjUser) {
68         return false;
69     }
70 
71     bool found = false;
72     inst.VisitUsers([&](const abckit::Instruction &user) -> bool {
73         if (user.GetGraph()->DynIsa().GetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_THROW_UNDEFINEDIFHOLEWITHNAME) {
74             found |= target.className == user.GetString();
75         }
76         return !found;
77     });
78 
79     return found;
80 }
81 
HasTargetMethod(const abckit::core::Function & method,const ReplaceCommand::Target & target)82 bool HasTargetMethod(const abckit::core::Function &method, const ReplaceCommand::Target &target)
83 {
84     bool found = false;
85 
86     auto graph = method.CreateGraph();
87     graph.EnumerateBasicBlocksRpo([&](const abckit::BasicBlock &bb) -> bool {
88         for (abckit::Instruction inst = bb.GetFirstInst(); !!inst && !found; inst = inst.GetNext()) {
89             found |= HasTargetMethod(inst, target);
90         }
91         return !found;
92     });
93 
94     return found;
95 }
96 
FindFirstInst(const abckit::Graph & graph,AbckitIsaApiDynamicOpcode opcode)97 std::optional<abckit::Instruction> FindFirstInst(const abckit::Graph &graph, AbckitIsaApiDynamicOpcode opcode)
98 {
99     std::optional<abckit::Instruction> found;
100     graph.EnumerateBasicBlocksRpo([&](const abckit::BasicBlock &bb) -> bool {
101         for (auto inst = bb.GetFirstInst(); !!inst && !found; inst = inst.GetNext()) {
102             if (inst.GetGraph()->DynIsa().GetOpcode(inst) == opcode) {
103                 found = inst;
104             }
105         }
106         return !found;
107     });
108     return found;
109 }
110 
ReplaceCallSite(const abckit::core::Function & method,const ReplaceCommand & command)111 void ReplaceCallSite(const abckit::core::Function &method, const ReplaceCommand &command)
112 {
113     std::optional<abckit::arkts::Module> targetModule;
114     method.GetFile()->EnumerateModules([&](const abckit::core::Module &mod) -> bool {
115         if (mod.GetName() == API_MODULE_NAME) {
116             targetModule = abckit::arkts::Module(mod);
117             return false;
118         }
119         return true;
120     });
121     EXPECT_TRUE(targetModule.has_value());
122 
123     abckit::arkts::Module arktsMethMod(method.GetModule());
124     abckit::core::ImportDescriptor idesc =
125         arktsMethMod.AddImportFromArktsV1ToArktsV1(*targetModule, API_CLASS_NAME, API_CLASS_NAME);
126 
127     abckit::Graph graph = method.CreateGraph();
128 
129     std::optional<abckit::Instruction> maybeConstInst;
130     auto maybeNewobjInst = FindFirstInst(graph, ABCKIT_ISA_API_DYNAMIC_OPCODE_NEWOBJRANGE);
131     EXPECT_TRUE(maybeNewobjInst.has_value());
132 
133     graph.EnumerateBasicBlocksRpo([&](const abckit::BasicBlock &bb) -> bool {
134         for (abckit::Instruction inst = bb.GetFirstInst(); !!inst; inst = inst.GetNext()) {
135             if (inst.GetGraph()->DynIsa().GetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_CALLTHIS1) {
136                 continue;
137             }
138             maybeConstInst = maybeConstInst ? maybeConstInst : graph.FindOrCreateConstantI32(TARGET_ORIENTATION);
139 
140             auto ldExternal = graph.DynIsa().CreateLdexternalmodulevar(idesc).InsertAfter(inst);
141             auto classThrow = graph.DynIsa()
142                                   .CreateThrowUndefinedifholewithname(ldExternal, command.subject.klass.GetName())
143                                   .InsertAfter(ldExternal);
144             auto ldObj =
145                 graph.DynIsa().CreateLdobjbyname(ldExternal, command.subject.method.GetName()).InsertAfter(classThrow);
146             auto staticCall =
147                 graph.DynIsa().CreateCallthis2(ldObj, ldExternal, *maybeNewobjInst, *maybeConstInst).InsertAfter(ldObj);
148         }
149         return true;
150     });
151 
152     method.SetGraph(graph);
153 }
154 
ReplaceCallSite(const abckit::core::Class & klass,const ReplaceCommand & command)155 void ReplaceCallSite(const abckit::core::Class &klass, const ReplaceCommand &command)
156 {
157     klass.EnumerateMethods([&](const abckit::core::Function &method) -> bool {
158         if (HasTargetMethod(method, command.target)) {
159             ReplaceCallSite(method, command);
160         }
161         return true;
162     });
163 }
164 
165 struct AnnotationTrack {
166     abckit::core::Class klass;
167     abckit::core::Function method;
168     abckit::core::Annotation anno;
169 };
170 
GetAnnotationTracks(const abckit::core::Class & klass,const abckit::core::Function & method,std::vector<AnnotationTrack> & tracks)171 void GetAnnotationTracks(const abckit::core::Class &klass, const abckit::core::Function &method,
172                          std::vector<AnnotationTrack> &tracks)
173 {
174     method.EnumerateAnnotations([&](auto anno) {
175         if (anno.GetInterface().GetName() == ANNOTATION_INTERFACE_NAME) {
176             tracks.push_back({klass, method, anno});
177         }
178         return true;
179     });
180 }
181 
GetAnnotationTracks(const abckit::core::Module & mod)182 std::vector<AnnotationTrack> GetAnnotationTracks(const abckit::core::Module &mod)
183 {
184     std::vector<AnnotationTrack> tracks;
185     mod.EnumerateClasses([&](const auto &klass) {
186         klass.EnumerateMethods([&](const auto &method) {
187             GetAnnotationTracks(klass, method, tracks);
188             return true;
189         });
190         return true;
191     });
192     return tracks;
193 }
194 
GetReplaceCommand(const AnnotationTrack & track)195 ReplaceCommand GetReplaceCommand(const AnnotationTrack &track)
196 {
197     ReplaceCommand::Target target;
198     track.anno.EnumerateElements([&](const abckit::core::AnnotationElement &el) -> bool {
199         auto name = el.GetName();
200         if (name == "targetClass") {
201             target.className = el.GetValue().GetString();
202         } else if (name == "methodName") {
203             target.methodName = el.GetValue().GetString();
204         }
205         return true;
206     });
207     EXPECT_TRUE(!target.className.empty());
208     EXPECT_TRUE(!target.methodName.empty());
209 
210     return ReplaceCommand {ReplaceCommand::Subject {track.klass, track.method}, target};
211 }
212 
GetAnnotationInfo(const abckit::core::Module & mod,std::vector<ReplaceCommand> & replaceCommands)213 void GetAnnotationInfo(const abckit::core::Module &mod, std::vector<ReplaceCommand> &replaceCommands)
214 {
215     const std::vector<AnnotationTrack> tracks = GetAnnotationTracks(mod);
216     for (const auto &track : tracks) {
217         replaceCommands.push_back(GetReplaceCommand(track));
218     }
219 }
220 
221 }  // namespace
222 
223 namespace libabckit::test {
224 
225 class AbckitScenarioCppTestClean : public ::testing::Test {};
226 
227 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=cpp
TEST_F(AbckitScenarioCppTestClean,LibAbcKitTestCppDynamicReplaceCallSiteClean)228 TEST_F(AbckitScenarioCppTestClean, LibAbcKitTestCppDynamicReplaceCallSiteClean)
229 {
230     const std::string sandboxPath = ABCKIT_ABC_DIR "clean_scenarios/cpp_api/dynamic/replace_call_site/";
231     const std::string inputPath = sandboxPath + "replace_call_site.abc";
232     const std::string outputPath = sandboxPath + "replace_call_site_modified.abc";
233 
234     auto output = helpers::ExecuteDynamicAbc(inputPath, "replace_call_site");
235     EXPECT_EQ(output, "3\n");
236 
237     abckit::File file(inputPath, std::make_unique<GTestAssertErrorHandler>());
238 
239     std::vector<ReplaceCommand> replaceCommands;
240     file.EnumerateModules([&](const abckit::core::Module &mod) {
241         if (mod.GetName() == API_MODULE_NAME) {
242             GetAnnotationInfo(mod, replaceCommands);
243         }
244         return true;
245     });
246     EXPECT_EQ(replaceCommands.size(), 1);
247     const auto &command = replaceCommands.front();
248 
249     // "modules/ApiControl" "modules/ApiControl"
250     EXPECT_EQ(command.subject.klass.GetName(), API_CLASS_NAME);
251     EXPECT_EQ(command.subject.method.GetName(), API_METHOD_NAME);
252 
253     file.EnumerateModules([&](const abckit::core::Module &mod) {
254         if (mod.GetName() != "replace_call_site") {
255             return true;
256         }
257         mod.EnumerateClasses([&](const abckit::core::Class &klass) {
258             ReplaceCallSite(klass, command);
259             return true;
260         });
261         return true;
262     });
263 
264     file.WriteAbc(outputPath);
265 
266     output = helpers::ExecuteDynamicAbc(outputPath, "replace_call_site");
267     EXPECT_TRUE(helpers::Match(output,
268                                "fixOrientationLinearLayout was called\n"
269                                "5\n"));
270 }
271 
272 }  // namespace libabckit::test
273