1 /* 2 * Copyright (c) 2022-2024 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 #ifndef ECMASCRIPT_JSPANDAFILE_JS_PANDAFILE_H 17 #define ECMASCRIPT_JSPANDAFILE_JS_PANDAFILE_H 18 19 #include "common_components/taskpool/task.h" 20 #include "ecmascript/base/string_helper.h" 21 #include "ecmascript/common.h" 22 #include "ecmascript/jspandafile/constpool_value.h" 23 #include "ecmascript/jspandafile/method_literal.h" 24 #include "ecmascript/log_wrapper.h" 25 #include "ecmascript/mem/c_containers.h" 26 #include "ecmascript/platform/mutex.h" 27 28 #include "libpandafile/file-inl.h" 29 #include "libpandafile/file_items.h" 30 #include "libpandafile/literal_data_accessor.h" 31 32 namespace panda { 33 namespace ecmascript { 34 class EcmaVM; 35 using ReleaseSecureMemCallback = std::function<void(void* fileMapper)>; 36 37 enum class CreateMode : uint8_t { 38 RUNTIME = 0, 39 DFX, 40 }; 41 42 class JSPandaFile { 43 public: 44 struct JSRecordInfo { 45 uint32_t mainMethodIndex {0}; 46 bool isCjs {false}; 47 bool isJson {false}; 48 bool isSharedModule {false}; 49 int jsonStringId {-1}; 50 CUnorderedSet<const EcmaVM *> vmListOfParsedConstPool; 51 int moduleRecordIdx {-1}; 52 bool hasTopLevelAwait {false}; 53 CUnorderedMap<uint32_t, uint64_t> constpoolMap; 54 uint32_t lazyImportIdx {0}; 55 uint32_t classId {CLASSID_OFFSET_NOT_FOUND}; 56 CString npmPackageName; 57 SetParsedConstpoolVMJSRecordInfo58 void SetParsedConstpoolVM(const EcmaVM *vm) 59 { 60 vmListOfParsedConstPool.insert(vm); 61 } 62 IsParsedConstpoolOfCurrentVMJSRecordInfo63 bool IsParsedConstpoolOfCurrentVM(const EcmaVM *vm) const 64 { 65 auto iter = vmListOfParsedConstPool.find(vm); 66 if (iter != vmListOfParsedConstPool.end()) { 67 return true; 68 } 69 return false; 70 } 71 }; 72 static constexpr char ENTRY_FUNCTION_NAME[] = "func_main_0"; 73 static constexpr char ENTRY_MAIN_FUNCTION[] = "_GLOBAL::func_main_0"; 74 static constexpr char PATCH_MAIN_FUNCTION[] = "_GLOBAL::patch_main_0"; 75 static constexpr char PATCH_FUNCTION_NAME_0[] = "patch_main_0"; 76 static constexpr char PATCH_FUNCTION_NAME_1[] = "patch_main_1"; 77 78 static constexpr char MODULE_CLASS[] = "L_ESModuleRecord;"; 79 static constexpr char COMMONJS_CLASS[] = "L_CommonJsRecord;"; 80 static constexpr char HASTLA_CLASS[] = "L_HasTopLevelAwait;"; 81 82 static constexpr std::string_view IS_COMMON_JS = "isCommonjs"; 83 static constexpr std::string_view IS_JSON_CONTENT = "jsonFileContent"; 84 static constexpr std::string_view MODULE_RECORD_IDX = "moduleRecordIdx"; 85 static constexpr std::string_view IS_SHARED_MODULE = "isSharedModule"; 86 static constexpr std::string_view HAS_TOP_LEVEL_AWAIT = "hasTopLevelAwait"; 87 static constexpr std::string_view LAZY_IMPORT = "moduleRequestPhaseIdx"; 88 static constexpr std::string_view PACKAGE_NAME = "pkgName@"; 89 static constexpr char MERGE_ABC_NAME[] = "modules.abc"; 90 static constexpr char NPM_PATH_SEGMENT[] = "node_modules"; 91 static constexpr char PACKAGE_PATH_SEGMENT[] = "pkg_modules"; 92 static constexpr char BUNDLE_INSTALL_PATH[] = "/data/storage/el1/bundle/"; 93 94 static constexpr std::string_view HAP_SUFFIX = ".hap"; 95 static constexpr int PACKAGE_NAME_LEN = 8; 96 static constexpr int TYPE_SUMMARY_OFFSET_NOT_FOUND = 0; 97 static constexpr int CLASSID_OFFSET_NOT_FOUND = 0; 98 static constexpr int32_t PF_OFFSET = 0; 99 static constexpr uint32_t ASYN_TRANSLATE_CLSSS_COUNT = 128; 100 static constexpr uint32_t ASYN_TRANSLATE_CLSSS_MIN_COUNT = 2; 101 static constexpr uint32_t USING_TASKPOOL_MIN_CLASS_COUNT = 4; 102 static constexpr uint32_t DEFAULT_MAIN_METHOD_INDEX = 0; 103 104 JSPandaFile(const panda_file::File *pf, const CString &descriptor, CreateMode state = CreateMode::RUNTIME); 105 ~JSPandaFile(); 106 107 using ClassTranslateWork = std::vector<std::pair<uint32_t, uint32_t>>; 108 using CurClassTranslateWork = std::pair<uint32_t, ClassTranslateWork>; 109 using AllClassTranslateWork = std::vector<CurClassTranslateWork>; 110 111 class TranslateClassesTask : public common::Task { 112 public: TranslateClassesTask(int32_t id,JSThread * thread,JSPandaFile * jsPandaFile,const std::shared_ptr<CString> & methodNamePtr,CurClassTranslateWork & curTranslateWorks)113 TranslateClassesTask(int32_t id, JSThread *thread, JSPandaFile *jsPandaFile, 114 const std::shared_ptr<CString> &methodNamePtr, CurClassTranslateWork &curTranslateWorks) 115 : common::Task(id), thread_(thread), jsPandaFile_(jsPandaFile), methodNamePtr_(methodNamePtr), 116 curTranslateWorks_(curTranslateWorks){}; 117 ~TranslateClassesTask() override = default; 118 bool Run(uint32_t threadIndex) override; 119 120 NO_COPY_SEMANTIC(TranslateClassesTask); 121 NO_MOVE_SEMANTIC(TranslateClassesTask); 122 123 private: 124 JSThread *thread_ {nullptr}; 125 JSPandaFile *jsPandaFile_ {nullptr}; 126 std::shared_ptr<CString> methodNamePtr_; 127 CurClassTranslateWork &curTranslateWorks_; 128 }; 129 GetJSPandaFileDesc()130 inline const CString &GetJSPandaFileDesc() const 131 { 132 return desc_; 133 } 134 135 CString PUBLIC_API GetNormalizedFileDesc() const; 136 SetHapPath(const CString & hapPath)137 void SetHapPath(const CString &hapPath) 138 { 139 hapPath_ = hapPath; 140 } 141 GetJSPandaFileHapPath()142 const CString &GetJSPandaFileHapPath() const 143 { 144 return hapPath_; 145 } 146 147 static CString PUBLIC_API GetNormalizedFileDesc(const CString &desc); 148 GetChecksum()149 inline uint32_t GetChecksum() const 150 { 151 return checksum_; 152 } 153 GetPandaFile()154 inline const panda_file::File *GetPandaFile() const 155 { 156 return pf_; 157 } 158 159 std::pair<std::string_view, bool> GetMethodName(EntityId methodId); 160 std::pair<std::string_view, bool> GetCpuProfilerMethodName(EntityId methodId) const; 161 CString GetRecordName(EntityId methodId); 162 CString PUBLIC_API GetRecordNameWithBundlePack(EntityId methodIdx); 163 GetMethodLiterals()164 inline MethodLiteral* GetMethodLiterals() const 165 { 166 return methodLiterals_; 167 } 168 SetMethodLiteralToMap(MethodLiteral * methodLiteral)169 inline void SetMethodLiteralToMap(MethodLiteral *methodLiteral) 170 { 171 ASSERT(methodLiteral != nullptr); 172 methodLiteralMap_.try_emplace(methodLiteral->GetMethodId().GetOffset(), methodLiteral); 173 } 174 GetMethodLiteralMap()175 inline const std::unordered_map<uint32_t, MethodLiteral *> &GetMethodLiteralMap() const 176 { 177 return methodLiteralMap_; 178 } 179 GetNumMethods()180 uint32_t GetNumMethods() const 181 { 182 return numMethods_; 183 } 184 GetConstpoolIndex()185 uint32_t GetConstpoolIndex() const 186 { 187 return constpoolIndex_; 188 } 189 190 uint32_t GetMainMethodIndex(const CString &recordName = ENTRY_FUNCTION_NAME, bool isNewVersion = false) const 191 { 192 if (IsBundlePack()) { 193 return jsRecordInfo_.begin()->second->mainMethodIndex; 194 } 195 auto info = jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())); 196 if (info != jsRecordInfo_.end()) { 197 return info->second->mainMethodIndex; 198 } 199 200 if (isNewVersion) { 201 for (const auto &recordInfo : jsRecordInfo_) { 202 LOG_ECMA(ERROR) << "All current record info: " << recordInfo.first; 203 } 204 LOG_ECMA(FATAL) << "can not get main method index: " << recordName; 205 UNREACHABLE(); 206 } 207 208 LOG_ECMA(ERROR) << "can not get main method index: " << recordName; 209 return 0; 210 } 211 GetConstpoolMapByReocrd(const CString & recordName)212 const CUnorderedMap<uint32_t, uint64_t> *GetConstpoolMapByReocrd(const CString &recordName) const 213 { 214 auto info = jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())); 215 if (info != jsRecordInfo_.end()) { 216 return &info->second->constpoolMap; 217 } 218 LOG_FULL(FATAL) << "find entryPoint failed: " << recordName; 219 UNREACHABLE(); 220 } 221 GetConstpoolMap()222 const CUnorderedMap<uint32_t, uint64_t> &GetConstpoolMap() const 223 { 224 return constpoolMap_; 225 } 226 227 uint32_t PUBLIC_API GetOrInsertConstantPool(ConstPoolType type, uint32_t offset, 228 const CUnorderedMap<uint32_t, uint64_t> *constpoolMap = nullptr); 229 230 void UpdateMainMethodIndex(uint32_t mainMethodIndex, const CString &recordName = ENTRY_FUNCTION_NAME) 231 { 232 LockHolder lock(jsRecordInfoMutex_); 233 if (IsBundlePack()) { 234 jsRecordInfo_.begin()->second->mainMethodIndex = mainMethodIndex; 235 } else { 236 auto info = jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())); 237 if (info != jsRecordInfo_.end()) { 238 info->second->mainMethodIndex = mainMethodIndex; 239 } 240 } 241 } 242 FindMethodLiteral(uint32_t offset)243 inline PUBLIC_API MethodLiteral *FindMethodLiteral(uint32_t offset) const 244 { 245 auto iter = methodLiteralMap_.find(offset); 246 if (iter == methodLiteralMap_.end()) { 247 return nullptr; 248 } 249 return iter->second; 250 } 251 252 inline int GetModuleRecordIdx(const CString &recordName = ENTRY_FUNCTION_NAME) const 253 { 254 if (IsBundlePack()) { 255 return jsRecordInfo_.begin()->second->moduleRecordIdx; 256 } 257 auto info = jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())); 258 if (info != jsRecordInfo_.end()) { 259 return info->second->moduleRecordIdx; 260 } 261 // The array subscript will not have a negative number, and returning -1 means the search failed 262 return -1; 263 } 264 265 int GetHasTopLevelAwait(const CString &recordName = ENTRY_FUNCTION_NAME) const 266 { 267 if (IsBundlePack()) { 268 return jsRecordInfo_.begin()->second->hasTopLevelAwait; 269 } 270 auto info = jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())); 271 if (info != jsRecordInfo_.end()) { 272 return info->second->hasTopLevelAwait; 273 } 274 return false; 275 } 276 GetClasses()277 inline Span<const uint32_t> GetClasses() const 278 { 279 return pf_->GetClasses(); 280 } 281 IsExternal(panda_file::File::EntityId id)282 inline bool IsExternal(panda_file::File::EntityId id) const 283 { 284 return pf_->IsExternal(id); 285 } 286 Contain(uint8_t * data)287 inline bool Contain(uint8_t *data) const 288 { 289 uintptr_t header = ToUintPtr(GetHeader()); 290 uintptr_t dataPointer = ToUintPtr(data); 291 if (header < dataPointer && dataPointer < (header + GetFileSize())) { 292 return true; 293 } 294 return false; 295 } 296 GetStringData(panda_file::File::EntityId id)297 inline panda_file::File::StringData GetStringData(panda_file::File::EntityId id) const 298 { 299 return pf_->GetStringData(id); 300 } 301 ResolveMethodIndex(panda_file::File::EntityId id,uint16_t idx)302 panda_file::File::EntityId ResolveMethodIndex(panda_file::File::EntityId id, uint16_t idx) const 303 { 304 return pf_->ResolveMethodIndex(id, idx); 305 } 306 GetLiteralDataAccessor()307 panda_file::LiteralDataAccessor GetLiteralDataAccessor() const 308 { 309 EntityId literalArraysId = pf_->GetLiteralArraysId(); 310 panda_file::LiteralDataAccessor lda(*pf_, literalArraysId); 311 return lda; 312 } 313 GetConstpoolNum()314 uint32_t GetConstpoolNum() const 315 { 316 return pf_->GetHeader()->num_indexes; 317 } 318 GetMethodIndex(const panda_file::File::IndexHeader * indexHeader)319 Span<const panda_file::File::EntityId> GetMethodIndex(const panda_file::File::IndexHeader *indexHeader) const 320 { 321 return pf_->GetMethodIndex(indexHeader); 322 } 323 GetHeader()324 const void *GetHeader() const 325 { 326 return static_cast<const void *>(pf_->GetHeader()); 327 } 328 GetFileSize()329 uint32_t GetFileSize() const 330 { 331 return pf_->GetHeader()->file_size; 332 } 333 CheckAndGetRecordInfo(const CString & recordName)334 inline JSRecordInfo* CheckAndGetRecordInfo(const CString &recordName) const 335 { 336 if (UNLIKELY(IsBundlePack())) { 337 return jsRecordInfo_.begin()->second; 338 } 339 340 auto info = jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())); 341 if (info != jsRecordInfo_.end()) { 342 return info->second; 343 } 344 345 return nullptr; 346 } 347 348 CString GetJsonStringId(const JSRecordInfo &jsRecordInfo) const; 349 IsModule(const JSRecordInfo * jsRecordInfo)350 inline bool PUBLIC_API IsModule(const JSRecordInfo *jsRecordInfo) const 351 { 352 return jsRecordInfo->moduleRecordIdx != -1; 353 } 354 IsCjs(const JSRecordInfo * jsRecordInfo)355 inline bool IsCjs(const JSRecordInfo *jsRecordInfo) const 356 { 357 return jsRecordInfo->isCjs; 358 } 359 IsJson(const JSRecordInfo * jsRecordInfo)360 inline bool IsJson(const JSRecordInfo *jsRecordInfo) const 361 { 362 return jsRecordInfo->isJson; 363 } 364 IsSharedModule(const JSRecordInfo * jsRecordInfo)365 inline bool IsSharedModule(const JSRecordInfo *jsRecordInfo) const 366 { 367 return jsRecordInfo->isSharedModule; 368 } 369 IsBundlePack()370 inline bool IsBundlePack() const 371 { 372 return isBundlePack_; 373 } 374 IsLoadedAOT()375 bool IsLoadedAOT() const 376 { 377 return (GetAOTFileInfoIndex() != INVALID_INDEX); 378 } 379 GetFileUniqId()380 uint32_t GetFileUniqId() const 381 { 382 return static_cast<uint32_t>(GetPandaFile()->GetUniqId()); 383 } 384 IsNewVersion()385 inline bool IsNewVersion() const 386 { 387 return isNewVersion_; 388 } 389 HasRecord(const CString & recordName)390 inline bool HasRecord(const CString &recordName) const 391 { 392 return jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())) != jsRecordInfo_.end(); 393 } 394 FindRecordInfo(const CString & recordName)395 JSRecordInfo &FindRecordInfo(const CString &recordName) 396 { 397 auto info = jsRecordInfo_.find(std::string_view(recordName.c_str(), recordName.size())); 398 // check entry name, fix framework abc find recordName fail bug 399 if (recordName == "_GLOBAL") { 400 info = jsRecordInfo_.find(ENTRY_FUNCTION_NAME); 401 } 402 if (info == jsRecordInfo_.end()) { 403 LOG_FULL(FATAL) << "find recordName failed: " << recordName; 404 UNREACHABLE(); 405 } 406 return *(info->second); 407 } 408 409 // note : it only uses in TDD InsertJSRecordInfo(const CString & recordName)410 void InsertJSRecordInfo(const CString &recordName) 411 { 412 JSRecordInfo* info = new JSRecordInfo(); 413 jsRecordInfo_.insert({std::string_view(recordName.c_str(), recordName.size()), info}); 414 } 415 416 // note : it only uses in TDD InsertNpmEntries(const CString & recordName,const CString & fieldName)417 void InsertNpmEntries(const CString &recordName, const CString &fieldName) 418 { 419 npmEntries_.insert({std::string_view(recordName.c_str(), recordName.size()), 420 std::string_view(fieldName.c_str(), fieldName.size())}); 421 } 422 GetJSRecordInfo()423 const CUnorderedMap<std::string_view, JSRecordInfo*> &GetJSRecordInfo() const 424 { 425 return jsRecordInfo_; 426 } 427 ParseEntryPoint(const CString & desc)428 static CString ParseEntryPoint(const CString &desc) 429 { 430 return desc.substr(1, desc.size() - 2); // 2 : skip symbol "L" and ";" 431 } 432 ParseEntryPoint(const std::string_view & desc)433 static std::string_view ParseEntryPoint(const std::string_view &desc) 434 { 435 return desc.substr(1, desc.size() - 2); // 2 : skip symbol "L" and ";" 436 } 437 438 void CheckIsBundlePack(); 439 void CheckIsRecordWithBundleName(const CString &entry); IsRecordWithBundleName()440 bool IsRecordWithBundleName() const 441 { 442 return isRecordWithBundleName_; 443 } 444 CString GetEntryPoint(const CString &recordName) const; 445 CString GetRecordName(const CString &entryPoint) const; 446 bool FindOhmUrlInPF(const CString &recordName, CString &entryPoint) const; GetAOTFileInfoIndex()447 uint32_t GetAOTFileInfoIndex() const 448 { 449 return anFileInfoIndex_; 450 } 451 SetAOTFileInfoIndex(uint32_t index)452 void SetAOTFileInfoIndex(uint32_t index) 453 { 454 if (IsLoadedAOT()) { 455 LOG_ECMA(ERROR) << "Set Aot file info index failed. desc: " << GetJSPandaFileDesc() 456 << ", anFileIndex: " << anFileInfoIndex_ << " vs " << index; 457 return; 458 } 459 anFileInfoIndex_ = index; 460 } 461 IsEntryOrPatch(const CString & name)462 static bool IsEntryOrPatch(const CString &name) 463 { 464 return (name == PATCH_FUNCTION_NAME_0) || (name == ENTRY_FUNCTION_NAME); 465 } 466 DeleteParsedConstpoolVM(const EcmaVM * vm)467 inline void DeleteParsedConstpoolVM(const EcmaVM *vm) 468 { 469 for (auto &recordInfo : jsRecordInfo_) { 470 recordInfo.second->vmListOfParsedConstPool.erase(vm); 471 } 472 } 473 static FunctionKind PUBLIC_API GetFunctionKind(panda_file::FunctionKind funcKind); 474 static FunctionKind GetFunctionKind(ConstPoolType type); IsSendableFunctionKind(panda_file::FunctionKind funcKind)475 static bool PUBLIC_API IsSendableFunctionKind(panda_file::FunctionKind funcKind) 476 { 477 return (static_cast<uint32_t>(funcKind) & SENDABLE_FUNCTION_MASK) != 0; 478 } 479 GetBase()480 const void *GetBase() const 481 { 482 return static_cast<const void *>(pf_->GetBase()); 483 } 484 485 void ClearNameMap(); 486 487 void TranslateClasses(JSThread *thread, const CString &methodName); 488 IsHapPath()489 bool IsHapPath() const 490 { 491 return base::StringHelper::StringEndWith(hapPath_, HAP_SUFFIX); 492 } 493 SetFileMapper(void * fileMapper)494 void SetFileMapper(void *fileMapper) 495 { 496 fileMapper_ = fileMapper; 497 } 498 GetFileMapper()499 void *GetFileMapper() const 500 { 501 return fileMapper_; 502 } 503 504 static void CallReleaseSecureMemFunc(void* fileMapper); 505 506 private: 507 void InitializeUnMergedPF(); 508 void InitializeMergedPF(); 509 510 void WaitTranslateClassTaskFinished(); 511 512 void NotifyTranslateClassTaskCompleted(); 513 514 void IncreaseTaskCount(); 515 516 void TranslateClassInMainThread(JSThread *thread, const CString &methodName); 517 518 void TranslateClassInSubThread(JSThread *thread, const CString &methodName, 519 CurClassTranslateWork &curTranslateWorks); 520 521 void CheckOngoingClassTranslating(JSThread *thread, const CString &methodName, 522 const AllClassTranslateWork &remainingTranslateWorks); 523 524 void PostInitializeMethodTask(JSThread *thread, const std::shared_ptr<CString> &methodNamePtr, 525 CurClassTranslateWork &curTranslateWorks); 526 527 void ReduceTaskCount(); 528 529 void SetAllMethodLiteralToMap(); 530 531 void GetClassAndMethodIndexes(std::vector<std::pair<uint32_t, uint32_t>> &indexes); 532 533 static constexpr size_t VERSION_SIZE = 4; 534 static constexpr std::array<uint8_t, VERSION_SIZE> OLD_VERSION {0, 0, 0, 2}; 535 static constexpr uint32_t SENDABLE_FUNCTION_MASK = 1 << 3; 536 537 // please add member after *pf_. static constexpr int32_t PF_OFFSET = 0. 538 const panda_file::File *pf_ {nullptr}; 539 void *fileMapper_ {nullptr}; 540 CString hapPath_; 541 uint32_t constpoolIndex_ {0}; 542 uint32_t checksum_ {0}; 543 std::unordered_map<uint32_t, MethodLiteral *> methodLiteralMap_; 544 std::unordered_map<uint32_t, panda_file::File::StringData> methodNameMap_; 545 CUnorderedMap<uint32_t, CString> recordNameMap_; 546 Mutex methodNameMapMutex_; 547 Mutex recordNameMapMutex_; 548 Mutex waitTranslateClassFinishedMutex_; 549 Mutex classIndexMutex_; 550 Mutex jsRecordInfoMutex_; 551 ConditionVariable waitTranslateClassFinishedCV_; 552 uint32_t runningTaskCount_ {0}; 553 uint32_t classIndex_ {0}; 554 uint32_t methodIndex_ {0}; 555 556 CUnorderedMap<uint32_t, uint64_t> constpoolMap_; 557 uint32_t numMethods_ {0}; 558 uint32_t numClasses_ {0}; 559 MethodLiteral *methodLiterals_ {nullptr}; 560 CString desc_; 561 uint32_t anFileInfoIndex_ {INVALID_INDEX}; 562 bool isNewVersion_ {false}; 563 564 // marge abc 565 bool isBundlePack_ {true}; // isBundlePack means app compile mode is JSBundle 566 CUnorderedMap<std::string_view, JSRecordInfo*> jsRecordInfo_; 567 CUnorderedMap<std::string_view, std::string_view> npmEntries_; 568 bool isRecordWithBundleName_ {true}; 569 CreateMode mode_ {CreateMode::RUNTIME}; 570 friend class JSPandaFileSnapshot; 571 // This tag shows if main thread is waiting for the sub-threads to finish translate class tasks. 572 std::atomic<bool> waitingFinish_ {false}; 573 }; 574 } // namespace ecmascript 575 } // namespace panda 576 #endif // ECMASCRIPT_JSPANDAFILE_JS_PANDAFILE_H 577