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