• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #include "ecmascript/patch/quick_fix_manager.h"
16 
17 #include "ecmascript/jspandafile/js_pandafile_manager.h"
18 #include "ecmascript/mem/c_string.h"
19 #include "ecmascript/napi/include/jsnapi.h"
20 
21 namespace panda::ecmascript {
~QuickFixManager()22 QuickFixManager::~QuickFixManager()
23 {
24     methodInfos_.clear();
25 }
26 
RegisterQuickFixQueryFunc(const std::function<bool (std::string baseFileName,std::string & patchFileName,void ** patchBuffer,size_t & patchSize)> callBack)27 void QuickFixManager::RegisterQuickFixQueryFunc(const std::function<bool(std::string baseFileName,
28                         std::string &patchFileName,
29                         void **patchBuffer,
30                         size_t &patchSize)> callBack)
31 {
32     callBack_ = callBack;
33 }
34 
LoadPatchIfNeeded(JSThread * thread,const JSPandaFile * baseFile)35 void QuickFixManager::LoadPatchIfNeeded(JSThread *thread, const JSPandaFile *baseFile)
36 {
37     // callback and load patch.
38     if (!HasQueryQuickFixInfoFunc()) {
39         return;
40     }
41 
42     std::string patchFileName;
43     void *patchBuffer = nullptr;
44     size_t patchSize = 0;
45     CString baseFileName = baseFile->GetJSPandaFileDesc();
46     bool needLoadPatch = callBack_(baseFileName.c_str(), patchFileName, &patchBuffer, patchSize);
47     if (!needLoadPatch) {
48         LOG_ECMA(INFO) << "Do not need load patch of: " << baseFileName;
49         return;
50     }
51 
52     if (methodInfos_.find(baseFileName) != methodInfos_.end()) {
53         LOG_ECMA(DEBUG) << "Cannot repeat load patch of: " << baseFileName;
54         return;
55     }
56 
57     std::shared_ptr<JSPandaFile> patchFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
58         thread, patchFileName.c_str(), "", patchBuffer, patchSize);
59     if (patchFile == nullptr) {
60         LOG_ECMA(ERROR) << "load patch jsPandafile failed of: " << baseFileName;
61         return;
62     }
63 
64     PatchInfo patchInfo;
65     auto ret = PatchLoader::LoadPatchInternal(thread, baseFile, patchFile.get(), patchInfo);
66     if (ret != PatchErrorCode::SUCCESS) {
67         LOG_ECMA(ERROR) << "Load patch fail of: " << baseFileName;
68         return;
69     }
70 
71     methodInfos_.emplace(baseFileName, patchInfo);
72 }
73 
LoadPatch(JSThread * thread,const std::string & patchFileName,const std::string & baseFileName)74 PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread, const std::string &patchFileName,
75                                           const std::string &baseFileName)
76 {
77     LOG_ECMA(INFO) << "Load patch, patch: " << patchFileName << ", base:" << baseFileName;
78     if (methodInfos_.find(baseFileName.c_str()) != methodInfos_.end()) {
79         LOG_ECMA(ERROR) << "Cannot repeat load patch!";
80         return PatchErrorCode::PATCH_HAS_LOADED;
81     }
82 
83     std::shared_ptr<JSPandaFile> baseFile =
84         JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, baseFileName.c_str(), "");
85     if (baseFile == nullptr) {
86         LOG_ECMA(ERROR) << "find base jsPandafile failed";
87         return PatchErrorCode::FILE_NOT_FOUND;
88     }
89 
90     // The entry point is not work for merge abc.
91     std::shared_ptr<JSPandaFile> patchFile =
92         JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, patchFileName.c_str(), "");
93     if (patchFile == nullptr) {
94         LOG_ECMA(ERROR) << "load patch jsPandafile failed";
95         return PatchErrorCode::FILE_NOT_FOUND;
96     }
97 
98     PatchInfo patchInfo;
99     auto ret = PatchLoader::LoadPatchInternal(thread, baseFile.get(), patchFile.get(), patchInfo);
100     if (ret != PatchErrorCode::SUCCESS) {
101         LOG_ECMA(ERROR) << "Load patch fail!";
102         return ret;
103     }
104 
105     methodInfos_.emplace(baseFileName.c_str(), patchInfo);
106     LOG_ECMA(INFO) << "Load patch success!";
107     return PatchErrorCode::SUCCESS;
108 }
109 
LoadPatch(JSThread * thread,const std::string & patchFileName,const void * patchBuffer,size_t patchSize,const std::string & baseFileName,const void * baseBuffer,size_t baseSize)110 PatchErrorCode QuickFixManager::LoadPatch(JSThread *thread,
111                                           const std::string &patchFileName, const void *patchBuffer, size_t patchSize,
112                                           const std::string &baseFileName, const void *baseBuffer, size_t baseSize)
113 {
114     LOG_ECMA(INFO) << "Load patch, patch: " << patchFileName << ", base:" << baseFileName;
115     if (methodInfos_.find(baseFileName.c_str()) != methodInfos_.end()) {
116         LOG_ECMA(ERROR) << "Cannot repeat load patch!";
117         return PatchErrorCode::PATCH_HAS_LOADED;
118     }
119 
120     std::shared_ptr<JSPandaFile> baseFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
121         thread, baseFileName.c_str(), "", baseBuffer, baseSize);
122     if (baseFile == nullptr) {
123         LOG_ECMA(ERROR) << "find base jsPandafile failed";
124         return PatchErrorCode::FILE_NOT_FOUND;
125     }
126 
127     std::shared_ptr<JSPandaFile> patchFile = JSPandaFileManager::GetInstance()->LoadJSPandaFile(
128         thread, patchFileName.c_str(), "", patchBuffer, patchSize);
129     if (patchFile == nullptr) {
130         LOG_ECMA(ERROR) << "load patch jsPandafile failed";
131         return PatchErrorCode::FILE_NOT_FOUND;
132     }
133 
134     PatchInfo patchInfo;
135     auto ret = PatchLoader::LoadPatchInternal(thread, baseFile.get(), patchFile.get(), patchInfo);
136     if (ret != PatchErrorCode::SUCCESS) {
137         LOG_ECMA(ERROR) << "Load patch fail!";
138         return ret;
139     }
140 
141     methodInfos_.emplace(baseFileName.c_str(), patchInfo);
142     LOG_ECMA(INFO) << "Load patch success!";
143     return PatchErrorCode::SUCCESS;
144 }
145 
UnloadPatch(JSThread * thread,const std::string & patchFileName)146 PatchErrorCode QuickFixManager::UnloadPatch(JSThread *thread, const std::string &patchFileName)
147 {
148     LOG_ECMA(INFO) << "Unload patch, patch: " << patchFileName;
149     CString baseFileName;
150     for (const auto &item : methodInfos_) {
151         if (item.second.patchFileName == patchFileName.c_str()) {
152             baseFileName = item.first;
153         }
154     }
155     if (baseFileName.empty()) {
156         LOG_ECMA(ERROR) << "patch has not been loaded!";
157         return PatchErrorCode::PATCH_NOT_LOADED;
158     }
159 
160     PatchInfo &patchInfo = methodInfos_.find(baseFileName)->second;
161     auto ret = PatchLoader::UnloadPatchInternal(thread, patchFileName.c_str(), baseFileName.c_str(), patchInfo);
162     if (ret != PatchErrorCode::SUCCESS) {
163         LOG_ECMA(ERROR) << "Unload patch fail!";
164         return ret;
165     }
166 
167     methodInfos_.erase(baseFileName.c_str());
168     LOG_ECMA(INFO) << "Unload patch success!";
169     return PatchErrorCode::SUCCESS;
170 }
171 
CheckAndGetPatch(JSThread * thread,const JSPandaFile * baseFile,EntityId baseMethodId)172 JSTaggedValue QuickFixManager::CheckAndGetPatch(JSThread *thread, const JSPandaFile *baseFile, EntityId baseMethodId)
173 {
174     if (methodInfos_.empty()) {
175         return JSTaggedValue::Hole();
176     }
177 
178     auto iter = methodInfos_.find(baseFile->GetJSPandaFileDesc());
179     if (iter == methodInfos_.end()) {
180         return JSTaggedValue::Hole();
181     }
182 
183     PatchInfo patchInfo = iter->second;
184     MethodLiteral *patchMethodLiteral = PatchLoader::FindSameMethod(patchInfo, baseFile, baseMethodId);
185     if (patchMethodLiteral == nullptr) {
186         return JSTaggedValue::Hole();
187     }
188 
189     // Generate patch constpool.
190     CString patchFileName = patchInfo.patchFileName;
191     std::shared_ptr<JSPandaFile> patchFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(patchFileName);
192     ASSERT(patchFile != nullptr);
193 
194     EcmaVM *vm = thread->GetEcmaVM();
195     JSHandle<Method> method = vm->GetFactory()->NewMethod(patchMethodLiteral);
196     JSHandle<ConstantPool> newConstpool = thread->GetCurrentEcmaContext()->FindOrCreateConstPool(
197         patchFile.get(), patchMethodLiteral->GetMethodId());
198     method->SetConstantPool(thread, newConstpool);
199     return method.GetTaggedValue();
200 }
201 
IsQuickFixCausedException(JSThread * thread,const JSHandle<JSTaggedValue> & exceptionInfo,const std::string & patchFileName)202 bool QuickFixManager::IsQuickFixCausedException(JSThread *thread,
203                                                 const JSHandle<JSTaggedValue> &exceptionInfo,
204                                                 const std::string &patchFileName)
205 {
206     JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
207     std::shared_ptr<JSPandaFile> patchFile = pfManager->FindJSPandaFile(ConvertToString(patchFileName));
208     if (patchFile == nullptr || ConvertToString(patchFileName) != patchFile->GetJSPandaFileDesc()) {
209         return false;
210     }
211 
212     // get and parse stackinfo.
213     JSHandle<JSTaggedValue> stackKey = thread->GlobalConstants()->GetHandledStackString();
214     JSHandle<EcmaString> stack(JSObject::GetProperty(thread, exceptionInfo, stackKey).GetValue());
215     CString stackInfo = ConvertToString(*stack);
216     CUnorderedSet<CString> methodNames = ParseStackInfo(stackInfo);
217 
218     // check whether the methodNames contains a patch method name.
219     CUnorderedMap<uint32_t, MethodLiteral *> patchMethodLiterals = patchFile->GetMethodLiteralMap();
220     for (const auto &item : patchMethodLiterals) {
221         MethodLiteral *patch = item.second;
222         auto methodId = patch->GetMethodId();
223         const char *patchMethodName = MethodLiteral::GetMethodName(patchFile.get(), methodId);
224         if (std::strcmp(patchMethodName, JSPandaFile::ENTRY_FUNCTION_NAME) != 0 &&
225             methodNames.find(CString(patchMethodName)) != methodNames.end()) {
226             return true;
227         }
228     }
229     return false;
230 }
231 
ParseStackInfo(const CString & stackInfo)232 CUnorderedSet<CString> QuickFixManager::ParseStackInfo(const CString &stackInfo)
233 {
234     const uint32_t methodNameOffsetToFirstIndex = 5; // offset of the starting position of methodname to firstIndex.
235     size_t lineIndex = 0; // index of "\n".
236     size_t firstIndex = 0; // index of "at".
237     size_t nextIndex = 0; // index of "(".
238 
239     CUnorderedSet<CString> methodNames {}; // method names are from exception stack information.
240     while (lineIndex != stackInfo.length() - 1) {
241         firstIndex = stackInfo.find("  at ", lineIndex + 1);
242         nextIndex = stackInfo.find("(", lineIndex + 1);
243         CString methodName = stackInfo.substr(firstIndex + methodNameOffsetToFirstIndex,
244             nextIndex - firstIndex - methodNameOffsetToFirstIndex - 1);
245         methodNames.emplace(methodName);
246         lineIndex = stackInfo.find("\n", lineIndex + 1);
247     }
248     return methodNames;
249 }
250 }  // namespace panda::ecmascript