• 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/jspandafile/quick_fix_manager.h"
16 
17 #include "ecmascript/jspandafile/js_pandafile_manager.h"
18 #include "ecmascript/mem/c_string.h"
19 
20 namespace panda::ecmascript {
~QuickFixManager()21 QuickFixManager::~QuickFixManager()
22 {
23     auto iter = quickFixLoaders_.begin();
24     while (iter != quickFixLoaders_.end()) {
25         auto &loader = iter->second;
26         if (loader != nullptr) {
27             delete loader;
28             loader = nullptr;
29         }
30         iter = quickFixLoaders_.erase(iter);
31     }
32 }
33 
RegisterQuickFixQueryFunc(const QuickFixQueryCallBack callBack)34 void QuickFixManager::RegisterQuickFixQueryFunc(const QuickFixQueryCallBack callBack)
35 {
36     callBack_ = callBack;
37 }
38 
LoadPatchIfNeeded(JSThread * thread,const std::string & baseFileName)39 void QuickFixManager::LoadPatchIfNeeded(JSThread *thread, const std::string &baseFileName)
40 {
41     // callback and load patch.
42     std::string patchFileName;
43     void *patchBuffer = nullptr;
44     size_t patchSize = 0;
45     if (HasQueryQuickFixInfoFunc()) {
46         bool needLoadPatch = callBack_(baseFileName, patchFileName, &patchBuffer, patchSize);
47         if (needLoadPatch && !HasLoadedPatch(patchFileName)) {
48             LoadPatch(thread, patchFileName, patchBuffer, patchSize, baseFileName);
49         }
50     }
51 }
52 
LoadPatch(JSThread * thread,const std::string & patchFileName,const std::string & baseFileName)53 bool QuickFixManager::LoadPatch(JSThread *thread, const std::string &patchFileName, const std::string &baseFileName)
54 {
55     if (quickFixLoaders_.find(patchFileName) != quickFixLoaders_.end()) {
56         LOG_ECMA(ERROR) << "Cannot repeat load patch";
57         return false;
58     }
59 
60     LOG_ECMA(INFO) << "Load patch, patch: " << patchFileName << ", base:" << baseFileName;
61     QuickFixLoader *loader = new QuickFixLoader();
62     bool ret = loader->LoadPatch(thread, patchFileName.c_str(), baseFileName.c_str());
63     if (!ret) {
64         LOG_ECMA(ERROR) << "Load patch fail";
65         return false;
66     }
67 
68     quickFixLoaders_.emplace(patchFileName, loader);
69     return true;
70 }
71 
LoadPatch(JSThread * thread,const std::string & patchFileName,const void * patchBuffer,size_t patchSize,const std::string & baseFileName)72 bool QuickFixManager::LoadPatch(JSThread *thread, const std::string &patchFileName,
73                                 const void *patchBuffer, size_t patchSize, const std::string &baseFileName)
74 {
75     if (quickFixLoaders_.find(patchFileName) != quickFixLoaders_.end()) {
76         LOG_ECMA(ERROR) << "Cannot repeat load patch";
77         return false;
78     }
79 
80     LOG_ECMA(INFO) << "Load patch, patch: " << patchFileName << ", base:" << baseFileName;
81     QuickFixLoader *loader = new QuickFixLoader();
82     bool ret = loader->LoadPatch(thread, patchFileName.c_str(), patchBuffer, patchSize, baseFileName.c_str());
83     if (!ret) {
84         LOG_ECMA(ERROR) << "Load patch fail";
85         return false;
86     }
87 
88     quickFixLoaders_.emplace(patchFileName, loader);
89     return true;
90 }
91 
UnloadPatch(JSThread * thread,const std::string & patchFileName)92 bool QuickFixManager::UnloadPatch(JSThread *thread, const std::string &patchFileName)
93 {
94     auto loaderIter = quickFixLoaders_.find(patchFileName);
95     if (loaderIter == quickFixLoaders_.end()) {
96         LOG_ECMA(ERROR) << "patch has not been loaded";
97         return false;
98     }
99 
100     LOG_ECMA(INFO) << "Unload patch: " << patchFileName;
101     QuickFixLoader *loader = loaderIter->second;
102     bool ret = loader->UnloadPatch(thread, patchFileName.c_str());
103     if (!ret) {
104         LOG_ECMA(ERROR) << "Unload patch fail";
105         return false;
106     }
107 
108     delete loader;
109     loader = nullptr;
110     quickFixLoaders_.erase(patchFileName);
111     return true;
112 }
113 
IsQuickFixCausedException(JSThread * thread,const JSHandle<JSTaggedValue> & exceptionInfo,const std::string & patchFileName)114 bool QuickFixManager::IsQuickFixCausedException(JSThread *thread,
115                                                const JSHandle<JSTaggedValue> &exceptionInfo,
116                                                const std::string &patchFileName)
117 {
118     JSPandaFileManager *pfManager = JSPandaFileManager::GetInstance();
119     const JSPandaFile* patchFile = pfManager->FindJSPandaFile(ConvertToString(patchFileName));
120     if (patchFile == nullptr || ConvertToString(patchFileName) != patchFile->GetJSPandaFileDesc()) {
121         return false;
122     }
123 
124     // get and parse stackinfo.
125     JSHandle<JSTaggedValue> stackKey = thread->GlobalConstants()->GetHandledStackString();
126     JSHandle<EcmaString> stack(JSObject::GetProperty(thread, exceptionInfo, stackKey).GetValue());
127     CString stackInfo = ConvertToString(*stack);
128     CUnorderedSet<CString> methodNames = ParseStackInfo(stackInfo);
129 
130     // check whether the methodNames contains a patch method name.
131     CUnorderedMap<uint32_t, MethodLiteral *> patchMethodLiterals = patchFile->GetMethodLiteralMap();
132     for (const auto &item : patchMethodLiterals) {
133         MethodLiteral *patch = item.second;
134         auto methodId = patch->GetMethodId();
135         const char *patchMethodName = MethodLiteral::GetMethodName(patchFile, methodId);
136         if (std::strcmp(patchMethodName, JSPandaFile::ENTRY_FUNCTION_NAME) != 0 &&
137             methodNames.find(CString(patchMethodName)) != methodNames.end()) {
138             return true;
139         }
140     }
141     return false;
142 }
143 
ParseStackInfo(const CString & stackInfo)144 CUnorderedSet<CString> QuickFixManager::ParseStackInfo(const CString &stackInfo)
145 {
146     const uint32_t methodNameOffsetToFirstIndex = 5; // offset of the starting position of methodname to firstIndex.
147     uint32_t lineIndex = 0; // index of "\n".
148     uint32_t firstIndex = 0; // index of "at".
149     uint32_t nextIndex = 0; // index of "(".
150 
151     CUnorderedSet<CString> methodNames {}; // method names are from exception stack information.
152     while (lineIndex != stackInfo.length() - 1) {
153         firstIndex = stackInfo.find("  at ", lineIndex + 1);
154         nextIndex = stackInfo.find("(", lineIndex + 1);
155         CString methodName = stackInfo.substr(firstIndex + methodNameOffsetToFirstIndex,
156             nextIndex - firstIndex - methodNameOffsetToFirstIndex - 1);
157         methodNames.emplace(methodName);
158         lineIndex = stackInfo.find("\n", lineIndex + 1);
159     }
160     return methodNames;
161 }
162 }  // namespace panda::ecmascript