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
16 #include "ecmascript/jspandafile/js_pandafile_manager.h"
17
18 #include "ecmascript/aot_file_manager.h"
19 #include "ecmascript/jspandafile/program_object.h"
20 #include "ecmascript/js_file_path.h"
21 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
22
23 namespace panda::ecmascript {
24 static const size_t MALLOC_SIZE_LIMIT = 2147483648; // Max internal memory used by the VM declared in options
25
GetInstance()26 JSPandaFileManager *JSPandaFileManager::GetInstance()
27 {
28 static JSPandaFileManager jsFileManager;
29 return &jsFileManager;
30 }
31
~JSPandaFileManager()32 JSPandaFileManager::~JSPandaFileManager()
33 {
34 os::memory::LockHolder lock(jsPandaFileLock_);
35 auto pos = extractors_.begin();
36 while (pos != extractors_.end()) {
37 pos = extractors_.erase(pos);
38 }
39
40 auto iterOld = oldJSPandaFiles_.begin();
41 while (iterOld != oldJSPandaFiles_.end()) {
42 const JSPandaFile *jsPandaFile = iterOld->first;
43 ReleaseJSPandaFile(jsPandaFile);
44 iterOld = oldJSPandaFiles_.erase(iterOld);
45 }
46 auto iter = loadedJSPandaFiles_.begin();
47 while (iter != loadedJSPandaFiles_.end()) {
48 const JSPandaFile *jsPandaFile = iter->second.first;
49 ReleaseJSPandaFile(jsPandaFile);
50 iter = loadedJSPandaFiles_.erase(iter);
51 }
52 }
53
LoadJSPandaFile(JSThread * thread,const CString & filename,std::string_view entryPoint,bool needUpdate)54 const JSPandaFile *JSPandaFileManager::LoadJSPandaFile(JSThread *thread, const CString &filename,
55 std::string_view entryPoint, bool needUpdate)
56 {
57 {
58 os::memory::LockHolder lock(jsPandaFileLock_);
59 const JSPandaFile *jsPandaFile = nullptr;
60 if (needUpdate) {
61 auto pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE);
62 if (pf == nullptr) {
63 LOG_ECMA(ERROR) << "open file " << filename << " error";
64 return nullptr;
65 }
66 jsPandaFile = FindJSPandaFileWithChecksum(filename, pf->GetHeader()->checksum);
67 } else {
68 jsPandaFile = FindJSPandaFileUnlocked(filename);
69 }
70 if (jsPandaFile != nullptr) {
71 if (!thread->GetEcmaVM()->HasCachedConstpool(jsPandaFile)) {
72 IncreaseRefJSPandaFileUnlocked(jsPandaFile);
73 }
74 return jsPandaFile;
75 }
76 }
77 auto pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE);
78 if (pf == nullptr) {
79 LOG_ECMA(ERROR) << "open file " << filename << " error";
80 return nullptr;
81 }
82
83 return GenerateJSPandaFile(thread, pf.release(), filename, entryPoint);
84 }
85
LoadJSPandaFile(JSThread * thread,const CString & filename,std::string_view entryPoint,const void * buffer,size_t size,bool needUpdate)86 const JSPandaFile *JSPandaFileManager::LoadJSPandaFile(JSThread *thread, const CString &filename,
87 std::string_view entryPoint, const void *buffer, size_t size, bool needUpdate)
88 {
89 if (buffer == nullptr || size == 0) {
90 return nullptr;
91 }
92 {
93 os::memory::LockHolder lock(jsPandaFileLock_);
94 const JSPandaFile *jsPandaFile = nullptr;
95 if (needUpdate) {
96 auto pf = panda_file::OpenPandaFileFromMemory(buffer, size);
97 if (pf == nullptr) {
98 LOG_ECMA(ERROR) << "open file " << filename << " error";
99 return nullptr;
100 }
101 jsPandaFile = FindJSPandaFileWithChecksum(filename, pf->GetHeader()->checksum);
102 } else {
103 jsPandaFile = FindJSPandaFileUnlocked(filename);
104 }
105 if (jsPandaFile != nullptr) {
106 if (!thread->GetEcmaVM()->HasCachedConstpool(jsPandaFile)) {
107 IncreaseRefJSPandaFileUnlocked(jsPandaFile);
108 }
109 return jsPandaFile;
110 }
111 }
112
113 auto pf = panda_file::OpenPandaFileFromMemory(buffer, size);
114 if (pf == nullptr) {
115 LOG_ECMA(ERROR) << "open file " << filename << " error";
116 return nullptr;
117 }
118 return GenerateJSPandaFile(thread, pf.release(), filename, entryPoint);
119 }
120
GenerateProgram(EcmaVM * vm,const JSPandaFile * jsPandaFile,std::string_view entryPoint)121 JSHandle<Program> JSPandaFileManager::GenerateProgram(
122 EcmaVM *vm, const JSPandaFile *jsPandaFile, std::string_view entryPoint)
123 {
124 ASSERT(GetJSPandaFile(jsPandaFile->GetPandaFile()) != nullptr);
125 if (AnFileDataManager::GetInstance()->IsEnable()) {
126 vm->GetAOTFileManager()->LoadAiFile(jsPandaFile);
127 }
128
129 return PandaFileTranslator::GenerateProgram(vm, jsPandaFile, entryPoint);
130 }
131
FindJSPandaFileWithChecksum(const CString & filename,uint32_t checksum)132 const JSPandaFile *JSPandaFileManager::FindJSPandaFileWithChecksum(const CString &filename, uint32_t checksum)
133 {
134 const JSPandaFile *jsPandaFile = FindJSPandaFileUnlocked(filename);
135 if (jsPandaFile != nullptr) {
136 if (checksum == jsPandaFile->GetChecksum()) {
137 return jsPandaFile;
138 } else {
139 LOG_FULL(INFO) << "reload " << filename << " with new checksum";
140 ObsoleteLoadedJSPandaFile(filename);
141 }
142 }
143 return nullptr;
144 }
145
FindJSPandaFileUnlocked(const CString & filename)146 const JSPandaFile *JSPandaFileManager::FindJSPandaFileUnlocked(const CString &filename)
147 {
148 if (filename.empty()) {
149 return nullptr;
150 }
151 auto const iter = loadedJSPandaFiles_.find(filename);
152 if (iter == loadedJSPandaFiles_.end()) {
153 return nullptr;
154 }
155 return iter->second.first;
156 }
157
FindJSPandaFile(const CString & filename)158 const JSPandaFile *JSPandaFileManager::FindJSPandaFile(const CString &filename)
159 {
160 os::memory::LockHolder lock(jsPandaFileLock_);
161 return FindJSPandaFileUnlocked(filename);
162 }
163
GetJSPandaFile(const panda_file::File * pf)164 const JSPandaFile *JSPandaFileManager::GetJSPandaFile(const panda_file::File *pf)
165 {
166 os::memory::LockHolder lock(jsPandaFileLock_);
167 for (const auto &iter : loadedJSPandaFiles_) {
168 const JSPandaFile *jsPandafile = iter.second.first;
169 if (jsPandafile->GetPandaFile() == pf) {
170 return jsPandafile;
171 }
172 }
173 return nullptr;
174 }
175
InsertJSPandaFile(const JSPandaFile * jsPandaFile)176 void JSPandaFileManager::InsertJSPandaFile(const JSPandaFile *jsPandaFile)
177 {
178 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
179 std::pair<const JSPandaFile *, uint32_t> pandaFileRecord = std::make_pair(jsPandaFile, 1);
180 os::memory::LockHolder lock(jsPandaFileLock_);
181 ASSERT(loadedJSPandaFiles_.find(filename) == loadedJSPandaFiles_.end());
182 loadedJSPandaFiles_[filename] = pandaFileRecord;
183 }
184
IncreaseRefJSPandaFileUnlocked(const JSPandaFile * jsPandaFile)185 void JSPandaFileManager::IncreaseRefJSPandaFileUnlocked(const JSPandaFile *jsPandaFile)
186 {
187 auto const filename = jsPandaFile->GetJSPandaFileDesc();
188 auto iter = loadedJSPandaFiles_.find(filename);
189 ASSERT(iter != loadedJSPandaFiles_.end());
190 iter->second.second++;
191 }
192
DecreaseRefJSPandaFile(const JSPandaFile * jsPandaFile)193 void JSPandaFileManager::DecreaseRefJSPandaFile(const JSPandaFile *jsPandaFile)
194 {
195 os::memory::LockHolder lock(jsPandaFileLock_);
196 auto iterOld = oldJSPandaFiles_.find(jsPandaFile);
197 if (iterOld != oldJSPandaFiles_.end()) {
198 if (iterOld->second > 1) {
199 iterOld->second--;
200 return;
201 }
202 oldJSPandaFiles_.erase(iterOld);
203 } else {
204 const auto &filename = jsPandaFile->GetJSPandaFileDesc();
205 auto iter = loadedJSPandaFiles_.find(filename);
206 if (iter != loadedJSPandaFiles_.end()) {
207 if (iter->second.second > 1) {
208 iter->second.second--;
209 return;
210 }
211 loadedJSPandaFiles_.erase(iter);
212 }
213 }
214 extractors_.erase(jsPandaFile);
215 ReleaseJSPandaFile(jsPandaFile);
216 }
217
ObsoleteLoadedJSPandaFile(const CString & filename)218 void JSPandaFileManager::ObsoleteLoadedJSPandaFile(const CString &filename)
219 {
220 auto iter = loadedJSPandaFiles_.find(filename);
221 ASSERT(iter != loadedJSPandaFiles_.end());
222 const JSPandaFile *jsPandaFile = iter->second.first;
223 if (oldJSPandaFiles_.find(jsPandaFile) == oldJSPandaFiles_.end()) {
224 oldJSPandaFiles_.emplace(jsPandaFile, iter->second.second);
225 } else {
226 oldJSPandaFiles_[jsPandaFile] += iter->second.second;
227 }
228 loadedJSPandaFiles_.erase(iter);
229 }
230
ClearCache()231 void JSPandaFileManager::ClearCache()
232 {
233 loadedJSPandaFiles_.clear();
234 }
235
OpenJSPandaFile(const CString & filename)236 JSPandaFile *JSPandaFileManager::OpenJSPandaFile(const CString &filename)
237 {
238 auto pf = panda_file::OpenPandaFileOrZip(filename, panda_file::File::READ_WRITE);
239 if (pf == nullptr) {
240 LOG_ECMA(ERROR) << "open file " << filename << " error";
241 return nullptr;
242 }
243
244 JSPandaFile *jsPandaFile = NewJSPandaFile(pf.release(), filename);
245 return jsPandaFile;
246 }
247
NewJSPandaFile(const panda_file::File * pf,const CString & desc)248 JSPandaFile *JSPandaFileManager::NewJSPandaFile(const panda_file::File *pf, const CString &desc)
249 {
250 auto jsPandaFile = new JSPandaFile(pf, desc);
251 PGOProfilerManager::GetInstance()->SamplePandaFileInfo(jsPandaFile->GetChecksum());
252 return jsPandaFile;
253 }
254
ReleaseJSPandaFile(const JSPandaFile * jsPandaFile)255 void JSPandaFileManager::ReleaseJSPandaFile(const JSPandaFile *jsPandaFile)
256 {
257 if (jsPandaFile == nullptr) {
258 return;
259 }
260 LOG_ECMA(DEBUG) << "ReleaseJSPandaFile " << jsPandaFile->GetJSPandaFileDesc();
261 delete jsPandaFile;
262 jsPandaFile = nullptr;
263 }
264
GetJSPtExtractor(const JSPandaFile * jsPandaFile)265 DebugInfoExtractor *JSPandaFileManager::GetJSPtExtractor(const JSPandaFile *jsPandaFile)
266 {
267 LOG_ECMA_IF(jsPandaFile == nullptr, FATAL) << "GetJSPtExtractor error, js pandafile is nullptr";
268
269 os::memory::LockHolder lock(jsPandaFileLock_);
270 [[maybe_unused]] auto const &filename = jsPandaFile->GetJSPandaFileDesc();
271 ASSERT(loadedJSPandaFiles_.find(filename) != loadedJSPandaFiles_.end());
272
273 auto iter = extractors_.find(jsPandaFile);
274 if (iter == extractors_.end()) {
275 auto extractorPtr = std::make_unique<DebugInfoExtractor>(jsPandaFile);
276 DebugInfoExtractor *extractor = extractorPtr.get();
277 extractors_[jsPandaFile] = std::move(extractorPtr);
278 return extractor;
279 }
280
281 return iter->second.get();
282 }
283
GenerateJSPandaFile(JSThread * thread,const panda_file::File * pf,const CString & desc,std::string_view entryPoint)284 const JSPandaFile *JSPandaFileManager::GenerateJSPandaFile(JSThread *thread, const panda_file::File *pf,
285 const CString &desc, std::string_view entryPoint)
286 {
287 ASSERT(GetJSPandaFile(pf) == nullptr);
288 JSPandaFile *newJsPandaFile = NewJSPandaFile(pf, desc);
289 auto aotFM = thread->GetEcmaVM()->GetAOTFileManager();
290 EcmaVM *vm = thread->GetEcmaVM();
291 if (aotFM->IsLoad(newJsPandaFile)) {
292 uint32_t index = aotFM->GetAnFileIndex(newJsPandaFile);
293 newJsPandaFile->SetAOTFileInfoIndex(index);
294 }
295
296 CString methodName = entryPoint.data();
297 if (newJsPandaFile->IsBundlePack()) {
298 // entryPoint maybe is _GLOBAL::func_main_watch to execute func_main_watch
299 auto pos = entryPoint.find_last_of("::");
300 if (pos != std::string_view::npos) {
301 methodName = entryPoint.substr(pos + 1);
302 } else {
303 // default use func_main_0 as entryPoint
304 methodName = JSPandaFile::ENTRY_FUNCTION_NAME;
305 }
306 }
307 PandaFileTranslator::TranslateClasses(newJsPandaFile, methodName);
308 {
309 os::memory::LockHolder lock(jsPandaFileLock_);
310 const JSPandaFile *jsPandaFile = FindJSPandaFileUnlocked(desc);
311 if (jsPandaFile != nullptr) {
312 if (!vm->HasCachedConstpool(jsPandaFile)) {
313 IncreaseRefJSPandaFileUnlocked(jsPandaFile);
314 }
315 ReleaseJSPandaFile(newJsPandaFile);
316 return jsPandaFile;
317 }
318 InsertJSPandaFile(newJsPandaFile);
319 }
320
321 return newJsPandaFile;
322 }
323
AllocateBuffer(size_t size)324 void *JSPandaFileManager::AllocateBuffer(size_t size)
325 {
326 return JSPandaFileAllocator::AllocateBuffer(size);
327 }
328
AllocateBuffer(size_t size)329 void *JSPandaFileManager::JSPandaFileAllocator::AllocateBuffer(size_t size)
330 {
331 if (size == 0) {
332 LOG_ECMA_MEM(FATAL) << "size must have a size bigger than 0";
333 UNREACHABLE();
334 }
335 if (size >= MALLOC_SIZE_LIMIT) {
336 LOG_ECMA_MEM(FATAL) << "size must be less than the maximum";
337 UNREACHABLE();
338 }
339 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
340 void *ptr = malloc(size);
341 if (ptr == nullptr) {
342 LOG_ECMA_MEM(FATAL) << "malloc failed";
343 UNREACHABLE();
344 }
345 #if ECMASCRIPT_ENABLE_ZAP_MEM
346 if (memset_s(ptr, size, INVALID_VALUE, size) != EOK) {
347 LOG_ECMA_MEM(FATAL) << "memset_s failed";
348 UNREACHABLE();
349 }
350 #endif
351 return ptr;
352 }
353
FreeBuffer(void * mem)354 void JSPandaFileManager::FreeBuffer(void *mem)
355 {
356 JSPandaFileAllocator::FreeBuffer(mem);
357 }
358
FreeBuffer(void * mem)359 void JSPandaFileManager::JSPandaFileAllocator::FreeBuffer(void *mem)
360 {
361 if (mem == nullptr) {
362 return;
363 }
364 // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
365 free(mem);
366 }
367
RemoveJSPandaFile(void * pointer)368 void JSPandaFileManager::RemoveJSPandaFile(void *pointer)
369 {
370 if (pointer == nullptr) {
371 return;
372 }
373 auto jsPandaFile = static_cast<JSPandaFile *>(pointer);
374 // dec ref in filemanager
375 JSPandaFileManager::GetInstance()->DecreaseRefJSPandaFile(jsPandaFile);
376 }
377 } // namespace panda::ecmascript
378