* Copyright (c) 2024-2026 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 "evaluate/helpers.h"
#include "checker/types/globalTypesHolder.h"
#include "ir/ets/etsPrimitiveType.h"
#include "ir/ets/etsTypeReference.h"
#include "ir/ets/etsTypeReferencePart.h"
#include "ir/expressions/identifier.h"
#include "ir/statements/blockStatement.h"
#include "ir/ts/tsArrayType.h"
#include "ir/typeNode.h"
#include "assembler/assembly-type.h"
#include "libarkfile/field_data_accessor-inl.h"
#include "libarkfile/file-inl.h"
#include <algorithm>
#include <unordered_map>
namespace ark::es2panda::evaluate::helpers {
namespace {
ir::TypeNode *PrimitiveToTypeNode(panda_file::Type::TypeId typeId, checker::ETSChecker *checker)
{
ir::PrimitiveType irType;
switch (typeId) {
case panda_file::Type::TypeId::VOID:
irType = ir::PrimitiveType::VOID;
break;
case panda_file::Type::TypeId::U1:
irType = ir::PrimitiveType::BOOLEAN;
break;
case panda_file::Type::TypeId::I8:
irType = ir::PrimitiveType::BYTE;
break;
case panda_file::Type::TypeId::U16:
irType = ir::PrimitiveType::CHAR;
break;
case panda_file::Type::TypeId::I16:
irType = ir::PrimitiveType::SHORT;
break;
case panda_file::Type::TypeId::I32:
irType = ir::PrimitiveType::INT;
break;
case panda_file::Type::TypeId::I64:
irType = ir::PrimitiveType::LONG;
break;
case panda_file::Type::TypeId::F32:
irType = ir::PrimitiveType::FLOAT;
break;
case panda_file::Type::TypeId::F64:
irType = ir::PrimitiveType::DOUBLE;
break;
default:
ES2PANDA_UNREACHABLE();
}
return checker->AllocNode<ir::ETSPrimitiveType>(irType, checker->Allocator());
}
ir::TypeNode *ClassReferenceToTypeNode(std::string_view name, checker::ETSChecker *checker)
{
util::UString typeName(name, checker->Allocator());
return CreateETSTypeReference(checker, typeName.View());
}
ir::TypeNode *ReferenceToTypeNode(std::string_view typeSignature, checker::ETSChecker *checker)
{
ES2PANDA_ASSERT(checker);
ES2PANDA_ASSERT(!typeSignature.empty());
switch (typeSignature[0]) {
case 'L': {
ES2PANDA_ASSERT(typeSignature.back() == ';');
auto startPos = typeSignature.find_last_of('/');
if (startPos == std::string_view::npos) {
startPos = 1;
} else {
startPos += 1;
}
return ClassReferenceToTypeNode(typeSignature.substr(startPos, typeSignature.size() - 1 - startPos),
checker);
}
case '[': {
size_t rank = std::count(typeSignature.begin(), typeSignature.end(), '[');
auto *elementType = ToTypeNode(typeSignature.substr(rank), checker);
if (elementType != nullptr) {
for (size_t i = 0; i < rank; ++i) {
elementType = checker->AllocNode<ir::TSArrayType>(elementType, checker->Allocator());
}
return elementType;
}
return nullptr;
}
default:
return nullptr;
}
return nullptr;
}
}
ir::TypeNode *ToTypeNode(std::string_view typeSignature, checker::ETSChecker *checker)
{
ES2PANDA_ASSERT(checker);
ES2PANDA_ASSERT(!typeSignature.empty());
if (typeSignature[0] == 'L' || typeSignature[0] == '[') {
return ReferenceToTypeNode(typeSignature, checker);
}
pandasm::Type type = pandasm::Type::FromDescriptor(typeSignature);
return PrimitiveToTypeNode(type.GetId(), checker);
}
ir::TypeNode *PandaTypeToTypeNode(const panda_file::File &pf, panda_file::FieldDataAccessor &fda,
checker::ETSChecker *checker)
{
auto pandaType = panda_file::Type::GetTypeFromFieldEncoding(fda.GetType());
if (pandaType.IsReference()) {
auto typeId = panda_file::FieldDataAccessor::GetTypeId(pf, fda.GetFieldId());
std::string_view refSignature = utf::Mutf8AsCString(pf.GetStringData(typeId).data);
return ReferenceToTypeNode(refSignature, checker);
}
return PrimitiveToTypeNode(pandaType.GetId(), checker);
}
ir::TypeNode *PandaTypeToTypeNode(const panda_file::File &pf, panda_file::Type pandaType,
panda_file::File::EntityId classId, checker::ETSChecker *checker)
{
if (pandaType.IsReference()) {
ES2PANDA_ASSERT(classId.IsValid());
std::string_view refSignature = utf::Mutf8AsCString(pf.GetStringData(classId).data);
return ReferenceToTypeNode(refSignature, checker);
}
return PrimitiveToTypeNode(pandaType.GetId(), checker);
}
static checker::Type *PrimitiveToCheckerType(panda_file::Type::TypeId typeId, checker::GlobalTypesHolder *globalTypes)
{
ES2PANDA_ASSERT(globalTypes);
switch (typeId) {
case panda_file::Type::TypeId::VOID:
return globalTypes->GlobalETSVoidType();
case panda_file::Type::TypeId::U1:
return globalTypes->GlobalBooleanType();
case panda_file::Type::TypeId::I8:
return globalTypes->GlobalCharType();
case panda_file::Type::TypeId::U8:
return globalTypes->GlobalByteType();
case panda_file::Type::TypeId::I16:
[[fallthrough]];
case panda_file::Type::TypeId::U16:
return globalTypes->GlobalShortType();
case panda_file::Type::TypeId::I32:
[[fallthrough]];
case panda_file::Type::TypeId::U32:
return globalTypes->GlobalIntType();
case panda_file::Type::TypeId::F32:
return globalTypes->GlobalFloatType();
case panda_file::Type::TypeId::F64:
return globalTypes->GlobalDoubleType();
case panda_file::Type::TypeId::I64:
[[fallthrough]];
case panda_file::Type::TypeId::U64:
return globalTypes->GlobalLongType();
default:
return nullptr;
}
return nullptr;
}
static std::optional<std::string> ReferenceToName(std::string_view typeSignature,
checker::GlobalTypesHolder *globalTypes)
{
static constexpr const size_t ARRAY_RANK_SYMBOLS = 2;
ES2PANDA_ASSERT(globalTypes);
ES2PANDA_ASSERT(!typeSignature.empty());
switch (typeSignature[0]) {
case 'L': {
ES2PANDA_ASSERT(typeSignature.back() == ';');
auto startPos = typeSignature.find_last_of('/');
if (startPos == std::string_view::npos) {
startPos = 1;
} else {
startPos += 1;
}
return std::string(typeSignature.substr(startPos, typeSignature.size() - 1 - startPos));
}
case '[': {
auto rank = std::count(typeSignature.begin(), typeSignature.end(), '[');
auto elementType = ToTypeName(typeSignature.substr(rank), globalTypes);
if (!elementType) {
return elementType;
}
auto &arrayType = *elementType;
auto subtypeSize = arrayType.size();
arrayType.resize(subtypeSize + rank * ARRAY_RANK_SYMBOLS);
for (size_t i = subtypeSize, end = arrayType.size(); i < end; i += ARRAY_RANK_SYMBOLS) {
arrayType[i] = '[';
arrayType[i + 1] = ']';
}
return arrayType;
}
default:
ES2PANDA_UNREACHABLE();
}
return {};
}
std::optional<std::string> ToTypeName(std::string_view typeSignature, checker::GlobalTypesHolder *globalTypes)
{
ES2PANDA_ASSERT(globalTypes);
ES2PANDA_ASSERT(!typeSignature.empty());
if (typeSignature[0] == 'L' || typeSignature[0] == '[') {
return ReferenceToName(typeSignature, globalTypes);
}
pandasm::Type type = pandasm::Type::FromDescriptor(typeSignature);
auto *checkerType = PrimitiveToCheckerType(type.GetId(), globalTypes);
ES2PANDA_ASSERT(checkerType != nullptr);
return checkerType->ToString();
}
panda_file::Type::TypeId GetTypeId(std::string_view typeSignature)
{
if (typeSignature.empty()) {
return panda_file::Type::TypeId::INVALID;
}
if (typeSignature[0] == 'L' || typeSignature[0] == '[') {
return panda_file::Type::TypeId::REFERENCE;
}
pandasm::Type type = pandasm::Type::FromDescriptor(typeSignature);
return type.GetId();
}
ir::BlockStatement *GetEnclosingBlock(ir::Identifier *ident)
{
ES2PANDA_ASSERT(ident);
ir::AstNode *iter = ident;
while (iter->Parent() != nullptr && !iter->IsBlockStatement()) {
iter = iter->Parent();
}
ES2PANDA_ASSERT(iter);
return iter->AsBlockStatement();
}
SafeStateScope::SafeStateScope(checker::ETSChecker *checker, varbinder::ETSBinder *varBinder)
: checker_(checker),
varBinder_(varBinder),
checkerScope_(checker->Scope()),
binderTopScope_(varBinder->TopScope()),
binderVarScope_(varBinder->VarScope()),
binderScope_(varBinder->GetScope()),
binderProgram_(varBinder->Program()),
recordTable_(varBinder->GetRecordTable())
{
}
SafeStateScope::~SafeStateScope()
{
(void)checker_;
(void)varBinder_;
(void)checkerScope_;
(void)binderTopScope_;
(void)binderVarScope_;
(void)binderScope_;
(void)binderProgram_;
(void)recordTable_;
ES2PANDA_ASSERT(checkerScope_ == checker_->Scope());
ES2PANDA_ASSERT(binderTopScope_ == varBinder_->TopScope());
ES2PANDA_ASSERT(binderVarScope_ == varBinder_->VarScope());
ES2PANDA_ASSERT(binderScope_ == varBinder_->GetScope());
ES2PANDA_ASSERT(binderProgram_ == varBinder_->Program());
ES2PANDA_ASSERT(recordTable_ == varBinder_->GetRecordTable());
}
ir::ETSTypeReference *CreateETSTypeReference(checker::ETSChecker *checker, util::StringView name)
{
auto *identRef = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
auto *typeRefPart = checker->AllocNode<ir::ETSTypeReferencePart>(identRef, checker->Allocator());
return checker->AllocNode<ir::ETSTypeReference>(typeRefPart, checker->Allocator());
}
std::pair<std::string_view, std::string_view> SplitRecordName(std::string_view recordName)
{
std::string_view moduleName;
std::string_view newRecordName;
if (auto pos = recordName.find_last_of('.'); pos != std::string_view::npos) {
moduleName = recordName.substr(0, pos);
newRecordName = recordName.substr(pos + 1, recordName.size());
} else {
newRecordName = recordName;
}
return std::make_pair(moduleName, newRecordName);
}
ir::ClassProperty *CreateClassProperty(checker::ETSChecker *checker, std::string_view name, ir::TypeNode *type,
ir::ModifierFlags modifiers)
{
ES2PANDA_ASSERT(type);
auto *fieldIdent = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
auto *field =
checker->AllocNode<ir::ClassProperty>(fieldIdent, nullptr, type, modifiers, checker->Allocator(), false);
return field;
}
ir::ModifierFlags GetModifierFlags(panda_file::ClassDataAccessor &da)
{
auto modifierFlags = ir::ModifierFlags::NONE;
auto accFlags = da.GetAccessFlags();
if ((accFlags & ACC_ABSTRACT) != 0) {
modifierFlags |= ir::ModifierFlags::ABSTRACT;
}
if ((accFlags & ACC_FINAL) != 0) {
modifierFlags |= ir::ModifierFlags::FINAL;
}
return modifierFlags;
}
}