• 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 
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