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