* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "checker/ETSchecker.h"
#include "evaluate/helpers.h"
#include "evaluate/debugInfoDeserialization/debugInfoDeserializer.h"
#include "evaluate/scopedDebugInfoPlugin.h"
#include "evaluate/debugInfoStorage.h"
#include "evaluate/proxyProgramsCache.h"
#include "evaluate/entityDeclarator-inl.h"
#include "libarkfile/class_data_accessor.h"
#include "libarkfile/file-inl.h"
namespace ark::es2panda::evaluate {
namespace {
std::string GetFullSuperClassName(panda_file::ClassDataAccessor &cda)
{
return panda_file::ClassDataAccessor::DemangledName(cda.GetPandaFile().GetStringData(cda.GetSuperClassId()));
}
std::optional<std::pair<util::StringView, FileDebugInfo *>> GetSuperClassModuleAndClassName(
panda_file::ClassDataAccessor &cda, DebugInfoStorage *debugInfoStorage)
{
ES2PANDA_ASSERT(debugInfoStorage);
auto fullSuperClassName = GetFullSuperClassName(cda);
if (fullSuperClassName == compiler::Signatures::BUILTIN_OBJECT) {
return {};
}
auto [moduleName, recordName] = helpers::SplitRecordName(fullSuperClassName);
auto *debugInfo = debugInfoStorage->GetDebugInfoByModuleName(moduleName);
if (UNLIKELY(debugInfo == nullptr)) {
LOG(FATAL, ES2PANDA) << "Failed to find debug info for module '" << moduleName << "'";
}
return std::make_pair(util::UString(recordName, debugInfoStorage->Allocator()).View(), debugInfo);
}
}
struct ChainEntryInfo final {
explicit ChainEntryInfo(std::string_view filePath, std::string_view declName,
panda_file::ClassDataAccessor *accessor, parser::Program *prog)
: sourceFilePath(filePath), entityDeclarationName(declName), cda(accessor), program(prog)
{
ES2PANDA_ASSERT(cda != nullptr);
}
DEFAULT_MOVE_SEMANTIC(ChainEntryInfo);
DEFAULT_COPY_SEMANTIC(ChainEntryInfo);
~ChainEntryInfo() = default;
std::string_view sourceFilePath;
std::string_view entityDeclarationName;
panda_file::ClassDataAccessor *cda {nullptr};
parser::Program *program {nullptr};
};
ir::ETSTypeReference *DebugInfoDeserializer::GetSuperClass(panda_file::ClassDataAccessor &cda)
{
auto optClassInfo = GetSuperClassModuleAndClassName(cda, debugInfoPlugin_.GetDebugInfoStorage());
if (!optClassInfo) {
return nullptr;
}
auto [superClassName, debugInfo] = *optClassInfo;
return ResolveInheritanceChain(superClassName, debugInfo);
}
ir::ETSTypeReference *DebugInfoDeserializer::ResolveInheritanceChain(util::StringView abcSuperName,
FileDebugInfo *debugInfo)
{
ES2PANDA_ASSERT(debugInfo);
auto *program = debugInfoPlugin_.GetProxyProgramsCache()->GetProgram(debugInfo->sourceFilePath);
if (debugInfoPlugin_.GetEntityDeclarator()->IsEntityDeclared(program, abcSuperName)) {
return helpers::CreateETSTypeReference(debugInfoPlugin_.GetIrCheckHelper()->GetChecker(), abcSuperName);
}
return ResolveInheritanceChainImpl(abcSuperName, debugInfo);
}
ir::ETSTypeReference *DebugInfoDeserializer::ResolveInheritanceChainImpl(util::StringView abcSuperName,
FileDebugInfo *debugInfo)
{
auto *checker = debugInfoPlugin_.GetIrCheckHelper()->GetChecker();
std::vector<ChainEntryInfo> chainEntryList {};
auto alreadyCreatedSuperClassName = CollectChainInfo(chainEntryList, abcSuperName, debugInfo);
ir::ETSTypeReference *superClass = nullptr;
if (!alreadyCreatedSuperClassName.Empty()) {
superClass = helpers::CreateETSTypeReference(checker, util::StringView(alreadyCreatedSuperClassName));
}
auto *entityDeclarator = debugInfoPlugin_.GetEntityDeclarator();
for (auto it = chainEntryList.rbegin(); it != chainEntryList.rend(); ++it) {
std::string_view declarationName = it->entityDeclarationName;
auto *cda = it->cda;
ES2PANDA_ASSERT(cda != nullptr);
entityDeclarator->ImportGlobalEntity(it->sourceFilePath, declarationName, it->program, declarationName,
[this, superClass, cda](auto, auto *program, auto, auto name) {
auto *classDecl = CreateClassDeclaration(name, *cda, superClass, program);
return classDecl->Definition()->Ident()->Variable();
});
superClass = helpers::CreateETSTypeReference(checker, declarationName);
}
return superClass;
}
util::StringView DebugInfoDeserializer::CollectChainInfo(std::vector<ChainEntryInfo> &chainEntryList,
util::StringView abcSuperName, FileDebugInfo *debugInfo)
{
ES2PANDA_ASSERT(debugInfo != nullptr);
auto *proxyProgramsCache = debugInfoPlugin_.GetProxyProgramsCache();
auto *debugInfoStorage = debugInfoPlugin_.GetDebugInfoStorage();
auto *entityDeclarator = debugInfoPlugin_.GetEntityDeclarator();
auto *allocator = debugInfoPlugin_.GetIrCheckHelper()->GetChecker()->Allocator();
while (true) {
auto *program = proxyProgramsCache->GetProgram(debugInfo->sourceFilePath);
ES2PANDA_ASSERT(program != nullptr);
if (entityDeclarator->IsEntityDeclared(program, abcSuperName)) {
return abcSuperName;
}
auto classId = debugInfoStorage->FindClass(debugInfo->sourceFilePath, abcSuperName.Utf8());
if (!classId.IsValid()) {
LOG(FATAL, ES2PANDA) << "Can't find classId for " << abcSuperName;
}
auto *cda = allocator->New<panda_file::ClassDataAccessor>(*debugInfo->pf, classId);
chainEntryList.emplace_back(debugInfo->sourceFilePath, abcSuperName.Utf8(), cda, program);
auto optClassInfo = GetSuperClassModuleAndClassName(*cda, debugInfoStorage);
if (!optClassInfo) {
return "";
}
std::tie(abcSuperName, debugInfo) = *optClassInfo;
}
}
}