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