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.h" 17 18#include "ecmascript/jspandafile/program_object.h" 19 20namespace panda::ecmascript { 21namespace { 22const CString OHOS_PKG_ABC_PATH_ROOT = "/ets/"; // abc file always under /ets/ dir in HAP/HSP 23} // namespace 24bool JSPandaFile::loadedFirstPandaFile = false; 25JSPandaFile::JSPandaFile(const panda_file::File *pf, const CString &descriptor) 26 : pf_(pf), desc_(descriptor) 27{ 28 ASSERT(pf_ != nullptr); 29 CheckIsBundlePack(); 30 if (isBundlePack_) { 31 InitializeUnMergedPF(); 32 } else { 33 InitializeMergedPF(); 34 } 35 checksum_ = pf->GetHeader()->checksum; 36 isNewVersion_ = pf_->GetHeader()->version > OLD_VERSION; 37 if (!loadedFirstPandaFile && !isBundlePack_) { 38 // Tag the first merged abc to use constant string. The lifetime of this first panda file is the same 39 // as the vm. And make sure the first pandafile is the same at the compile time and runtime. 40 isFirstPandafile_ = false; 41 loadedFirstPandaFile = true; 42 } 43} 44 45void JSPandaFile::CheckIsBundlePack() 46{ 47 Span<const uint32_t> classIndexes = pf_->GetClasses(); 48 for (const uint32_t index : classIndexes) { 49 panda_file::File::EntityId classId(index); 50 if (pf_->IsExternal(classId)) { 51 continue; 52 } 53 panda_file::ClassDataAccessor cda(*pf_, classId); 54 cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void { 55 panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId(); 56 panda_file::File::StringData sd = GetStringData(fieldNameId); 57 const char *fieldName = utf::Mutf8AsCString(sd.data); 58 if (std::strcmp(IS_COMMON_JS, fieldName) == 0 || std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) { 59 isBundlePack_ = false; 60 } 61 }); 62 if (!isBundlePack_) { 63 return; 64 } 65 } 66} 67 68void JSPandaFile::CheckIsRecordWithBundleName(const CString &entry) 69{ 70 size_t pos = entry.find('/'); 71 if (pos == CString::npos) { 72 LOG_FULL(FATAL) << "CheckIsRecordWithBundleName Invalid parameter entry"; 73 } 74 75 CString bundleName = entry.substr(0, pos); 76 size_t bundleNameLen = bundleName.length(); 77 for (auto info : jsRecordInfo_) { 78 if (info.first.find(PACKAGE_PATH_SEGMENT) != CString::npos || 79 info.first.find(NPM_PATH_SEGMENT) != CString::npos) { 80 continue; 81 } 82 CString recordName = info.first; 83 // Confirm whether the current record is new or old by judging whether the recordName has a bundleName 84 if (!(recordName.length() > bundleNameLen && (recordName.compare(0, bundleNameLen, bundleName) == 0))) { 85 isRecordWithBundleName_ = false; 86 } 87 return; 88 } 89} 90 91JSPandaFile::~JSPandaFile() 92{ 93 if (pf_ != nullptr) { 94 delete pf_; 95 pf_ = nullptr; 96 } 97 98 constpoolMap_.clear(); 99 for (auto& each : jsRecordInfo_) { 100 delete each.second; 101 } 102 jsRecordInfo_.clear(); 103 methodLiteralMap_.clear(); 104 ClearNameMap(); 105 if (methodLiterals_ != nullptr) { 106 JSPandaFileManager::FreeBuffer(methodLiterals_); 107 methodLiterals_ = nullptr; 108 } 109} 110 111uint32_t JSPandaFile::GetOrInsertConstantPool(ConstPoolType type, uint32_t offset, 112 const CUnorderedMap<uint32_t, uint64_t> *constpoolMap) 113{ 114 CUnorderedMap<uint32_t, uint64_t> *map = nullptr; 115 if (constpoolMap != nullptr && !IsBundlePack()) { 116 map = const_cast<CUnorderedMap<uint32_t, uint64_t> *>(constpoolMap); 117 } else { 118 map = &constpoolMap_; 119 } 120 auto it = map->find(offset); 121 if (it != map->cend()) { 122 ConstPoolValue value(it->second); 123 return value.GetConstpoolIndex(); 124 } 125 ASSERT(constpoolIndex_ != UINT32_MAX); 126 uint32_t index = constpoolIndex_++; 127 ConstPoolValue value(type, index); 128 map->emplace(offset, value.GetValue()); 129 return index; 130} 131 132void JSPandaFile::InitializeUnMergedPF() 133{ 134 Span<const uint32_t> classIndexes = pf_->GetClasses(); 135 numClasses_ = classIndexes.size(); 136 JSRecordInfo* info = new JSRecordInfo(); 137 for (const uint32_t index : classIndexes) { 138 panda_file::File::EntityId classId(index); 139 if (pf_->IsExternal(classId)) { 140 continue; 141 } 142 panda_file::ClassDataAccessor cda(*pf_, classId); 143 numMethods_ += cda.GetMethodsNumber(); 144 const char *desc = utf::Mutf8AsCString(cda.GetDescriptor()); 145 if (info->moduleRecordIdx == -1 && std::strcmp(MODULE_CLASS, desc) == 0) { 146 cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void { 147 panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId(); 148 panda_file::File::StringData sd = GetStringData(fieldNameId); 149 CString fieldName = utf::Mutf8AsCString(sd.data); 150 if (fieldName != desc_) { 151 info->moduleRecordIdx = fieldAccessor.GetValue<int32_t>().value(); 152 info->classId = index; 153 return; 154 } 155 }); 156 } 157 if (!info->isCjs && std::strcmp(COMMONJS_CLASS, desc) == 0) { 158 info->classId = index; 159 info->isCjs = true; 160 } 161 if (!info->isSharedModule && std::strcmp(IS_SHARED_MODULE, desc) == 0) { 162 info->isSharedModule = true; 163 } 164 if (!info->hasTopLevelAwait && std::strcmp(HASTLA_CLASS, desc) == 0) { 165 info->hasTopLevelAwait = true; 166 } 167 } 168 jsRecordInfo_.insert({JSPandaFile::ENTRY_FUNCTION_NAME, info}); 169 methodLiterals_ = 170 static_cast<MethodLiteral *>(JSPandaFileManager::AllocateBuffer(sizeof(MethodLiteral) * numMethods_)); 171 methodLiteralMap_.reserve(numMethods_); 172} 173 174void JSPandaFile::InitializeMergedPF() 175{ 176 Span<const uint32_t> classIndexes = pf_->GetClasses(); 177 numClasses_ = classIndexes.size(); 178 for (const uint32_t index : classIndexes) { 179 panda_file::File::EntityId classId(index); 180 if (pf_->IsExternal(classId)) { 181 continue; 182 } 183 panda_file::ClassDataAccessor cda(*pf_, classId); 184 numMethods_ += cda.GetMethodsNumber(); 185 JSRecordInfo* info = new JSRecordInfo(); 186 info->classId = index; 187 bool hasCjsFiled = false; 188 bool hasJsonFiled = false; 189 CString desc = utf::Mutf8AsCString(cda.GetDescriptor()); 190 CString recordName = ParseEntryPoint(desc); 191 cda.EnumerateFields([&](panda_file::FieldDataAccessor &fieldAccessor) -> void { 192 panda_file::File::EntityId fieldNameId = fieldAccessor.GetNameId(); 193 panda_file::File::StringData sd = GetStringData(fieldNameId); 194 const char *fieldName = utf::Mutf8AsCString(sd.data); 195 if (std::strcmp(IS_COMMON_JS, fieldName) == 0) { 196 hasCjsFiled = true; 197 info->isCjs = fieldAccessor.GetValue<bool>().value(); 198 } else if (std::strcmp(IS_JSON_CONTENT, fieldName) == 0) { 199 hasJsonFiled = true; 200 info->isJson = true; 201 info->jsonStringId = fieldAccessor.GetValue<uint32_t>().value(); 202 } else if (std::strcmp(MODULE_RECORD_IDX, fieldName) == 0) { 203 info->moduleRecordIdx = fieldAccessor.GetValue<int32_t>().value(); 204 } else if (std::strcmp(IS_SHARED_MODULE, fieldName) == 0) { 205 info->isSharedModule = fieldAccessor.GetValue<bool>().value(); 206 } else if (std::strcmp(HAS_TOP_LEVEL_AWAIT, fieldName) == 0) { 207 info->hasTopLevelAwait = fieldAccessor.GetValue<bool>().value(); 208 } else if (std::strcmp(LAZY_IMPORT, fieldName) == 0) { 209 info->lazyImportIdx = fieldAccessor.GetValue<uint32_t>().value(); 210 } else if (std::strlen(fieldName) > PACKAGE_NAME_LEN && 211 std::strncmp(fieldName, PACKAGE_NAME, PACKAGE_NAME_LEN) == 0) { 212 info->npmPackageName = fieldName + PACKAGE_NAME_LEN; 213 } else { 214 npmEntries_.emplace(recordName, fieldName); 215 } 216 }); 217 if (hasCjsFiled || hasJsonFiled) { 218 jsRecordInfo_.emplace(recordName, info); 219 } else { 220 delete info; 221 } 222 } 223 methodLiterals_ = 224 static_cast<MethodLiteral *>(JSPandaFileManager::AllocateBuffer(sizeof(MethodLiteral) * numMethods_)); 225 methodLiteralMap_.reserve(numMethods_); 226} 227 228MethodLiteral *JSPandaFile::FindMethodLiteral(uint32_t offset) const 229{ 230 auto iter = methodLiteralMap_.find(offset); 231 if (iter == methodLiteralMap_.end()) { 232 return nullptr; 233 } 234 return iter->second; 235} 236 237bool JSPandaFile::IsFirstMergedAbc() const 238{ 239 if (isFirstPandafile_ && !IsBundlePack()) { 240 return true; 241 } 242 return false; 243} 244 245bool JSPandaFile::CheckAndGetRecordInfo(const CString &recordName, [[maybe_unused]] JSRecordInfo **recordInfo) const 246{ 247 if (IsBundlePack()) { 248 *recordInfo = jsRecordInfo_.begin()->second; 249 return true; 250 } 251 auto info = jsRecordInfo_.find(recordName); 252 if (info != jsRecordInfo_.end()) { 253 *recordInfo = info->second; 254 return true; 255 } 256 return false; 257} 258 259const JSRecordInfo* JSPandaFile::GetRecordInfo(const CString &recordName) 260{ 261 if (IsBundlePack()) { 262 return jsRecordInfo_.begin()->second; 263 } 264 auto info = jsRecordInfo_.find(recordName); 265 if (info != jsRecordInfo_.end()) { 266 return info->second; 267 } 268 LOG_FULL(FATAL) << "Get record info failed"; 269 UNREACHABLE(); 270} 271 272CString JSPandaFile::GetEntryPoint(const CString &recordName) const 273{ 274 CString entryPoint; 275 if (FindOhmUrlInPF(recordName, entryPoint) && HasRecord(entryPoint)) { 276 return entryPoint; 277 } 278 return CString(); 279} 280 281CString JSPandaFile::GetRecordName(const CString &entryPoint) const 282{ 283 if (entryPoint.empty() || entryPoint == JSPandaFile::ENTRY_FUNCTION_NAME) { 284 return GetJSPandaFileDesc(); 285 } 286 return entryPoint; 287} 288 289bool JSPandaFile::FindOhmUrlInPF(const CString &recordName, CString &entryPoint) const 290{ 291 auto info = npmEntries_.find(recordName); 292 if (info != npmEntries_.end()) { 293 entryPoint = info->second; 294 return true; 295 } 296 return false; 297} 298 299FunctionKind JSPandaFile::GetFunctionKind(panda_file::FunctionKind funcKind) 300{ 301 FunctionKind kind; 302 if ((static_cast<uint32_t>(funcKind) & SENDABLE_FUNCTION_MASK) != 0) { 303 funcKind = static_cast<panda_file::FunctionKind>(static_cast<uint32_t>(funcKind) & (~SENDABLE_FUNCTION_MASK)); 304 } 305 switch (funcKind) { 306 case panda_file::FunctionKind::NONE: 307 kind = FunctionKind::NONE_FUNCTION; 308 break; 309 case panda_file::FunctionKind::FUNCTION: 310 kind = FunctionKind::BASE_CONSTRUCTOR; 311 break; 312 case panda_file::FunctionKind::NC_FUNCTION: 313 kind = FunctionKind::ARROW_FUNCTION; 314 break; 315 case panda_file::FunctionKind::GENERATOR_FUNCTION: 316 kind = FunctionKind::GENERATOR_FUNCTION; 317 break; 318 case panda_file::FunctionKind::ASYNC_FUNCTION: 319 kind = FunctionKind::ASYNC_FUNCTION; 320 break; 321 case panda_file::FunctionKind::ASYNC_GENERATOR_FUNCTION: 322 kind = FunctionKind::ASYNC_GENERATOR_FUNCTION; 323 break; 324 case panda_file::FunctionKind::ASYNC_NC_FUNCTION: 325 kind = FunctionKind::ASYNC_ARROW_FUNCTION; 326 break; 327 case panda_file::FunctionKind::CONCURRENT_FUNCTION: 328 kind = FunctionKind::CONCURRENT_FUNCTION; 329 break; 330 default: 331 LOG_ECMA(FATAL) << "this branch is unreachable"; 332 UNREACHABLE(); 333 } 334 return kind; 335} 336 337FunctionKind JSPandaFile::GetFunctionKind(ConstPoolType type) 338{ 339 FunctionKind kind; 340 switch (type) { 341 case ConstPoolType::BASE_FUNCTION: 342 kind = FunctionKind::BASE_CONSTRUCTOR; 343 break; 344 case ConstPoolType::NC_FUNCTION: 345 kind = FunctionKind::ARROW_FUNCTION; 346 break; 347 case ConstPoolType::GENERATOR_FUNCTION: 348 kind = FunctionKind::GENERATOR_FUNCTION; 349 break; 350 case ConstPoolType::ASYNC_FUNCTION: 351 kind = FunctionKind::ASYNC_FUNCTION; 352 break; 353 case ConstPoolType::CLASS_FUNCTION: 354 kind = FunctionKind::CLASS_CONSTRUCTOR; 355 break; 356 case ConstPoolType::METHOD: 357 kind = FunctionKind::NORMAL_FUNCTION; 358 break; 359 case ConstPoolType::ASYNC_GENERATOR_FUNCTION: 360 kind = FunctionKind::ASYNC_GENERATOR_FUNCTION; 361 break; 362 default: 363 LOG_ECMA(FATAL) << "this branch is unreachable"; 364 UNREACHABLE(); 365 } 366 return kind; 367} 368 369/* 370 handle desc like: case1: /data/storage/el1/bundle/entry/ets/modules.abc -> entry/ets/modules.abc 371 case2: /data/storage/el1/bundle/entry/ets/widgets.abc -> entry/ets/widgets.abc 372 case3: /data/app/el1/bundle/public/com.xx.xx/entry/ets/modules.abc -> entry/ets/modules.abc 373 case4: /data/app/el1/bundle/public/com.xx.xx/entry/ets/widgets.abc -> entry/ets/widgets.abc 374*/ 375CString JSPandaFile::GetNormalizedFileDesc(const CString &desc) 376{ 377 auto etsTokenPos = desc.rfind(OHOS_PKG_ABC_PATH_ROOT); 378 if (etsTokenPos == std::string::npos) { 379 // file not in OHOS package. 380 return desc; 381 } 382 auto ohosModulePos = desc.rfind('/', etsTokenPos - 1); 383 if (ohosModulePos == std::string::npos) { 384 LOG_ECMA(ERROR) << "Get abcPath from desc failed. desc: " << desc; 385 return desc; 386 } 387 // substring likes {ohosModuleName}/ets/modules.abc or {ohosModuleName}/ets/widgets.abc 388 return desc.substr(ohosModulePos + 1); 389} 390 391CString JSPandaFile::GetNormalizedFileDesc() const 392{ 393 return GetNormalizedFileDesc(desc_); 394} 395 396std::pair<std::string_view, bool> JSPandaFile::GetMethodName(EntityId methodId) 397{ 398 LockHolder lock(methodNameMapMutex_); 399 uint32_t id = methodId.GetOffset(); 400 auto iter = methodNameMap_.find(id); 401 if (iter != methodNameMap_.end()) { 402 panda_file::StringData sd = iter->second; 403 return {std::string_view(utf::Mutf8AsCString(sd.data), sd.utf16_length), sd.is_ascii}; 404 } 405 406 panda_file::MethodDataAccessor mda(*pf_, methodId); 407 auto sd = GetStringData(mda.GetNameId()); 408 methodNameMap_.emplace(id, sd); 409 return {std::string_view(utf::Mutf8AsCString(sd.data), sd.utf16_length), sd.is_ascii}; 410} 411 412std::pair<std::string_view, bool> JSPandaFile::GetCpuProfilerMethodName(EntityId methodId) const 413{ 414 panda_file::MethodDataAccessor mda(*pf_, methodId); 415 auto sd = GetStringData(mda.GetNameId()); 416 std::string_view strView(utf::Mutf8AsCString(sd.data), sd.utf16_length); 417 return {strView, sd.is_ascii}; 418} 419 420CString JSPandaFile::GetRecordName(EntityId methodId) 421{ 422 LockHolder lock(recordNameMapMutex_); 423 uint32_t id = methodId.GetOffset(); 424 auto iter = recordNameMap_.find(id); 425 if (iter != recordNameMap_.end()) { 426 return iter->second; 427 } 428 429 panda_file::MethodDataAccessor mda(*pf_, methodId); 430 panda_file::ClassDataAccessor cda(*pf_, mda.GetClassId()); 431 CString desc = utf::Mutf8AsCString(cda.GetDescriptor()); 432 auto name = JSPandaFile::ParseEntryPoint(desc); 433 recordNameMap_.emplace(id, name); 434 return name; 435} 436 437CString JSPandaFile::GetRecordNameWithBundlePack(EntityId methodIdx) 438{ 439 CString recordName = IsBundlePack() ? ENTRY_FUNCTION_NAME : GetRecordName(methodIdx); 440 ASSERT(HasRecord(recordName)); 441 return recordName; 442} 443 444 445void JSPandaFile::ClearNameMap() 446{ 447 { 448 LockHolder lock(methodNameMapMutex_); 449 methodNameMap_.clear(); 450 } 451 { 452 LockHolder lock(recordNameMapMutex_); 453 recordNameMap_.clear(); 454 } 455} 456 457size_t JSPandaFile::GetClassAndMethodIndex(size_t *methodIdx) 458{ 459 LockHolder lock(classIndexMutex_); 460 size_t result = 0; 461 Span<const uint32_t> classIndexes = GetClasses(); 462 uint32_t index = 0; 463 do { 464 result = classIndex_++; 465 if (result >= numClasses_) { 466 return result; 467 } 468 index = classIndexes[result]; 469 } while (IsExternal(panda_file::File::EntityId(index))); 470 471 *methodIdx = methodIndex_; 472 panda_file::File::EntityId classId(classIndexes[result]); 473 panda_file::ClassDataAccessor cda(*pf_, classId); 474 methodIndex_ += cda.GetMethodsNumber(); 475 return result; 476} 477 478bool JSPandaFile::TranslateClassesTask::Run([[maybe_unused]] uint32_t threadIndex) 479{ 480 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "TranslateClassesTask::Run"); 481 jsPandaFile_->TranslateClass(thread_, *methodNamePtr_); 482 jsPandaFile_->ReduceTaskCount(); 483 return true; 484} 485 486void JSPandaFile::TranslateClass(JSThread *thread, const CString &methodName) 487{ 488 size_t methodIdx = 0; 489 size_t classIdx = GetClassAndMethodIndex(&methodIdx); 490 while (classIdx < numClasses_) { 491 PandaFileTranslator::TranslateClass(thread, this, methodName, methodIdx, classIdx); 492 classIdx = GetClassAndMethodIndex(&methodIdx); 493 } 494} 495 496void JSPandaFile::PostInitializeMethodTask(JSThread *thread, const std::shared_ptr<CString> &methodNamePtr) 497{ 498 IncreaseTaskCount(); 499 Taskpool::GetCurrentTaskpool()->PostTask( 500 std::make_unique<TranslateClassesTask>(thread->GetThreadId(), thread, this, methodNamePtr)); 501} 502 503void JSPandaFile::IncreaseTaskCount() 504{ 505 LockHolder holder(waitTranslateClassFinishedMutex_); 506 runningTaskCount_++; 507} 508 509void JSPandaFile::WaitTranslateClassTaskFinished() 510{ 511 LockHolder holder(waitTranslateClassFinishedMutex_); 512 while (runningTaskCount_ > 0) { 513 waitTranslateClassFinishedCV_.Wait(&waitTranslateClassFinishedMutex_); 514 } 515} 516 517void JSPandaFile::ReduceTaskCount() 518{ 519 LockHolder holder(waitTranslateClassFinishedMutex_); 520 runningTaskCount_--; 521 if (runningTaskCount_ == 0) { 522 waitTranslateClassFinishedCV_.SignalAll(); 523 } 524} 525 526void JSPandaFile::SetAllMethodLiteralToMap() 527{ 528 // async to optimize SetAllMethodLiteralToMap later 529 MethodLiteral *methodLiterals = GetMethodLiterals(); 530 size_t methodIdx = 0; 531 while (methodIdx < numMethods_) { 532 MethodLiteral *methodLiteral = methodLiterals + (methodIdx++); 533 SetMethodLiteralToMap(methodLiteral); 534 } 535} 536 537void JSPandaFile::TranslateClasses(JSThread *thread, const CString &methodName) 538{ 539 const std::shared_ptr<CString> methodNamePtr = std::make_shared<CString>(methodName); 540 for (uint32_t i = 0; i < Taskpool::GetCurrentTaskpool()->GetTotalThreadNum(); i++) { 541 PostInitializeMethodTask(thread, methodNamePtr); 542 } 543 TranslateClass(thread, methodName); 544 WaitTranslateClassTaskFinished(); 545 SetAllMethodLiteralToMap(); 546} 547} // namespace panda::ecmascript 548