• 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 <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), &params);
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