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 "helpers/visit_helper/visit_helper-inl.h"
19 #include "helpers/helpers.h"
20 #include "helpers/helpers_runtime.h"
21 #include "helpers/visit_helper/visit_helper.h"
22 #include "libabckit/include/c/ir_core.h"
23 #include "libabckit/include/c/isa/isa_dynamic.h"
24 #include "libabckit/include/c/metadata_core.h"
25 #include "libabckit/include/c/extensions/arkts/metadata_arkts.h"
26 #include "metadata_inspect_impl.h"
27
28 namespace libabckit::test {
29
30 static auto g_impl = AbckitGetApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
31 static auto g_implI = AbckitGetInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
32 static auto g_implArkI = AbckitGetArktsInspectApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
33 static auto g_implM = AbckitGetModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
34 static auto g_implG = AbckitGetGraphApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
35 static auto g_dynG = AbckitGetIsaApiDynamicImpl(ABCKIT_VERSION_RELEASE_1_0_0);
36 static auto g_implArkM = AbckitGetArktsModifyApiImpl(ABCKIT_VERSION_RELEASE_1_0_0);
37
38 class AbckitScenarioTest : public ::testing::Test {};
39
40 constexpr auto API_CLASS_NAME = "ApiControl";
41 constexpr auto API_METHOD_NAME = "fixOrientationLinearLayout";
42 constexpr auto API_MODULE_NAME = "modules/ApiControl";
43 constexpr auto ANNOTATION_INTERFACE_NAME = "CallSiteReplacement";
44
45 struct UserData {
46 AbckitString *targetClass;
47 AbckitString *methodName;
48 AbckitCoreClass *classToReplace;
49 AbckitCoreFunction *methodToReplace;
50 };
51
HasSearchedMethod(VisitHelper & visitor,AbckitCoreFunction * method,UserData & userData)52 static bool HasSearchedMethod(VisitHelper &visitor, AbckitCoreFunction *method, UserData &userData)
53 {
54 bool isFind = false;
55 visitor.EnumerateFunctionInsts(method, [&](AbckitInst *inst) {
56 if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
57 return;
58 }
59 bool findNew = false;
60 visitor.EnumerateInstUsers(inst, [&](AbckitInst *user) {
61 if (g_dynG->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_NEWOBJRANGE) {
62 findNew = true;
63 }
64 });
65 visitor.EnumerateInstUsers(inst, [&](AbckitInst *user) {
66 if (findNew && !isFind &&
67 g_dynG->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_THROW_UNDEFINEDIFHOLEWITHNAME) {
68 auto str = visitor.GetString(g_implG->iGetString(user));
69 auto targetClass = visitor.GetString(userData.targetClass);
70 isFind = targetClass == str;
71 }
72 });
73 });
74
75 return isFind;
76 }
77
78 struct VisitData {
79 AbckitGraph *ctxG = nullptr;
80 UserData *ud = nullptr;
81 AbckitCoreImportDescriptor *ci = nullptr;
82 AbckitInst *newInst = nullptr;
83 };
84
VisitBlock(AbckitBasicBlock * bb,void * data)85 bool VisitBlock(AbckitBasicBlock *bb, void *data)
86 {
87 auto *vData = reinterpret_cast<VisitData *>(data);
88 auto *inst = g_implG->bbGetFirstInst(bb);
89 while (inst != nullptr) {
90 if (g_dynG->iGetOpcode(inst) == ABCKIT_ISA_API_DYNAMIC_OPCODE_CALLTHIS1) {
91 auto *ldExternal = g_dynG->iCreateLdexternalmodulevar(vData->ctxG, vData->ci);
92 auto *classThrow = g_dynG->iCreateThrowUndefinedifholewithname(
93 vData->ctxG, ldExternal, g_implI->classGetName(vData->ud->classToReplace));
94 auto *constInst = g_implG->gFindOrCreateConstantI32(vData->ctxG, 5);
95 auto *ldobj = g_dynG->iCreateLdobjbyname(vData->ctxG, ldExternal,
96 g_implI->functionGetName(vData->ud->methodToReplace));
97
98 auto *staticCall = g_dynG->iCreateCallthis2(vData->ctxG, ldobj, ldExternal, vData->newInst, constInst);
99
100 if (inst == nullptr) {
101 return false;
102 }
103
104 g_implG->iInsertAfter(ldExternal, inst);
105 g_implG->iInsertAfter(classThrow, ldExternal);
106 g_implG->iInsertAfter(ldobj, classThrow);
107 g_implG->iInsertAfter(staticCall, ldobj);
108 }
109 inst = g_implG->iGetNext(inst);
110 }
111 return true;
112 }
113
ReplaceCallSite(AbckitCoreFunction * method,UserData & userData)114 void ReplaceCallSite(AbckitCoreFunction *method, UserData &userData)
115 {
116 // Create import of modules/ApiControl module and import ApiControl from there
117 AbckitArktsImportFromDynamicModuleCreateParams params {};
118 params.name = API_CLASS_NAME;
119 params.alias = API_CLASS_NAME;
120
121 helpers::ModuleByNameContext ctxFinder = {nullptr, API_MODULE_NAME};
122 g_implI->fileEnumerateModules(g_implI->functionGetFile(method), &ctxFinder, helpers::ModuleByNameFinder);
123 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
124 ASSERT_NE(ctxFinder.module, nullptr);
125 auto *newImport = g_implArkM->moduleAddImportFromArktsV1ToArktsV1(
126 g_implArkI->coreModuleToArktsModule(g_implI->functionGetModule(method)),
127 g_implArkI->coreModuleToArktsModule(ctxFinder.module), ¶ms);
128
129 auto *coreImport = g_implArkI->arktsImportDescriptorToCoreImportDescriptor(newImport);
130 auto ctxG = g_implI->createGraphFromFunction(method);
131
132 VisitData vd;
133 vd.ctxG = ctxG;
134 vd.ci = coreImport;
135 vd.ud = &userData;
136 vd.newInst = helpers::FindFirstInst(ctxG, ABCKIT_ISA_API_DYNAMIC_OPCODE_NEWOBJRANGE);
137
138 g_implG->gVisitBlocksRpo(ctxG, &vd, [](AbckitBasicBlock *bb, void *data) -> bool { return VisitBlock(bb, data); });
139 g_implM->functionSetGraph(method, ctxG);
140 g_impl->destroyGraph(ctxG);
141 }
142
CollectAnnoInfo(VisitHelper & visitor,std::vector<std::tuple<AbckitCoreClass *,AbckitCoreFunction *,AbckitCoreAnnotation * >> & infos,AbckitCoreClass * klass,AbckitCoreFunction * method)143 static void CollectAnnoInfo(
144 VisitHelper &visitor,
145 std::vector<std::tuple<AbckitCoreClass *, AbckitCoreFunction *, AbckitCoreAnnotation *>> &infos,
146 AbckitCoreClass *klass, AbckitCoreFunction *method)
147 {
148 visitor.EnumerateMethodAnnotations(method, [&](AbckitCoreAnnotation *anno) {
149 auto *annoClass = g_implI->annotationGetInterface(anno);
150 auto annoName = visitor.GetString(g_implI->annotationInterfaceGetName(annoClass));
151 if (annoName != ANNOTATION_INTERFACE_NAME) {
152 return;
153 }
154 infos.emplace_back(klass, method, anno);
155 });
156 }
157
FillAnnotationInfo(VisitHelper & visitor,AbckitCoreModule * mod,UserData & ud)158 static void FillAnnotationInfo(VisitHelper &visitor, AbckitCoreModule *mod, UserData &ud)
159 {
160 std::vector<std::tuple<AbckitCoreClass *, AbckitCoreFunction *, AbckitCoreAnnotation *>> infos;
161 visitor.EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) {
162 visitor.EnumerateClassMethods(
163 klass, [&](AbckitCoreFunction *method) { CollectAnnoInfo(visitor, infos, klass, method); });
164 });
165 for (auto &[klass, method, anno] : infos) {
166 visitor.EnumerateAnnotationElements(anno, [&](AbckitCoreAnnotationElement *annoElem) {
167 auto annoElemName = visitor.GetString(g_implI->annotationElementGetName(annoElem));
168 auto *value = g_implI->annotationElementGetValue(annoElem);
169 if (std::string_view(annoElemName) == "targetClass") {
170 ud.targetClass = g_implI->valueGetString(value);
171 }
172 if (std::string_view(annoElemName) == "methodName") {
173 ud.methodName = g_implI->valueGetString(value);
174 }
175 });
176 ud.classToReplace = klass;
177 ud.methodToReplace = method;
178 }
179 }
180
ClassReplaceCallSite(VisitHelper & visitor,UserData & ud,AbckitCoreClass * klass)181 static void ClassReplaceCallSite(VisitHelper &visitor, UserData &ud, AbckitCoreClass *klass)
182 {
183 visitor.EnumerateClassMethods(klass, [&](AbckitCoreFunction *method) {
184 if (!HasSearchedMethod(visitor, method, ud)) {
185 return;
186 }
187 ReplaceCallSite(method, ud);
188 });
189 }
190
191 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioTest,LibAbcKitTestDynamicReplaceCallSite)192 TEST_F(AbckitScenarioTest, LibAbcKitTestDynamicReplaceCallSite)
193 {
194 std::string inputPath = ABCKIT_ABC_DIR "scenarios/replace_call_site/dynamic/replace_call_site.abc";
195 std::string outputPath = ABCKIT_ABC_DIR "scenarios/replace_call_site/dynamic/replace_call_site_modified.abc";
196
197 auto output = helpers::ExecuteDynamicAbc(inputPath, "replace_call_site");
198 EXPECT_TRUE(helpers::Match(output, "3\n"));
199
200 AbckitFile *ctxI = g_impl->openAbc(inputPath.c_str(), inputPath.size());
201 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
202
203 auto visitor = VisitHelper(ctxI, g_impl, g_implI, g_implG, g_dynG);
204
205 UserData ud {};
206
207 visitor.EnumerateModules([&](AbckitCoreModule *mod) {
208 if (visitor.GetString(g_implI->moduleGetName(mod)) != API_MODULE_NAME) {
209 return;
210 }
211 FillAnnotationInfo(visitor, mod, ud);
212 });
213
214 ASSERT_EQ(visitor.GetString(g_implI->classGetName(ud.classToReplace)), API_CLASS_NAME);
215 ASSERT_EQ(visitor.GetString(g_implI->functionGetName(ud.methodToReplace)), API_METHOD_NAME);
216
217 visitor.EnumerateModules([&](AbckitCoreModule *mod) {
218 auto moduleName = visitor.GetString(g_implI->moduleGetName(mod));
219 if (std::string_view(moduleName) != "replace_call_site") {
220 return;
221 }
222 visitor.EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { ClassReplaceCallSite(visitor, ud, klass); });
223 });
224
225 g_impl->writeAbc(ctxI, outputPath.c_str(), outputPath.size());
226 g_impl->closeFile(ctxI);
227 ASSERT_EQ(g_impl->getLastError(), ABCKIT_STATUS_NO_ERROR);
228
229 output = helpers::ExecuteDynamicAbc(outputPath, "replace_call_site");
230 EXPECT_TRUE(helpers::Match(output,
231 "fixOrientationLinearLayout was called\n"
232 "5\n"));
233 }
234
235 } // namespace libabckit::test
236