* Copyright (c) 2021-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 "objectIndexAccess.h"
#include "checker/types/ets/etsTupleType.h"
#include "compiler/lowering/util.h"
namespace ark::es2panda::compiler {
static ir::AstNode *ProcessIndexSetAccess(public_lib::Context *ctx, ir::AssignmentExpression *assignmentExpression)
{
auto *const parser = ctx->parser->AsETSParser();
auto *const checker = ctx->GetChecker()->AsETSChecker();
auto *const memberExpression = assignmentExpression->Left()->AsMemberExpression();
static std::string const CALL_EXPRESSION =
std::string {"@@E1."} + std::string {compiler::Signatures::SET_INDEX_METHOD} + "(@@E2, @@E3)";
ir::Expression *loweringResult = parser->CreateFormattedExpression(
CALL_EXPRESSION, memberExpression->Object(), memberExpression->Property(), assignmentExpression->Right());
ES2PANDA_ASSERT(loweringResult != nullptr);
loweringResult->SetParent(assignmentExpression->Parent());
loweringResult->SetRange(assignmentExpression->Range());
auto scope = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(),
NearestScope(assignmentExpression->Parent()));
CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, loweringResult);
loweringResult->AddModifier(ir::ModifierFlags::SETTER);
return loweringResult;
}
static ir::AstNode *ProcessIndexGetAccess(public_lib::Context *ctx, ir::MemberExpression *memberExpression)
{
auto *const parser = ctx->parser->AsETSParser();
auto *const checker = ctx->GetChecker()->AsETSChecker();
static std::string const CALL_EXPRESSION =
std::string {"@@E1."} + std::string {Signatures::GET_INDEX_METHOD} + "(@@E2)";
auto *const loweringResult =
parser->CreateFormattedExpression(CALL_EXPRESSION, memberExpression->Object(), memberExpression->Property());
loweringResult->AddModifier(ir::ModifierFlags::GETTER);
loweringResult->SetParent(memberExpression->Parent());
loweringResult->SetRange(memberExpression->Range());
CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, loweringResult);
return loweringResult;
}
static ir::AstNode *ProcessTupleSetAccess(public_lib::Context *ctx, ir::AssignmentExpression *assignmentExpression)
{
auto *const memberExpression = assignmentExpression->Left()->AsMemberExpression();
ES2PANDA_ASSERT_POS(memberExpression->Property()->IsNumberLiteral(), memberExpression->Property()->Start());
auto *const checker = ctx->GetChecker()->AsETSChecker();
static std::size_t const TUPLE_THRESHOLD = checker->GetGlobalTypesHolder()->VariadicTupleTypeThreshold();
std::size_t value = memberExpression->Property()->AsNumberLiteral()->Number().GetInt();
if (value < TUPLE_THRESHOLD) {
return assignmentExpression;
}
value -= TUPLE_THRESHOLD;
auto const code = "@@E1." + std::string {compiler::Signatures::TUPLE_ARRAY} + '.' +
std::string {compiler::Signatures::SET_INDEX_METHOD} + '(' + std::to_string(value) + ", @@E2)";
auto *loweringResult = ctx->parser->AsETSParser()->CreateFormattedExpression(code, memberExpression->Object(),
assignmentExpression->Right());
loweringResult->SetParent(assignmentExpression->Parent());
SetSourceRangesRecursively(loweringResult, assignmentExpression->Range());
auto *const varbinder = checker->VarBinder()->AsETSBinder();
auto *scope = NearestScope(assignmentExpression->Parent());
auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, scope);
CheckLoweredNode(varbinder, checker, loweringResult);
return loweringResult;
}
static ir::AstNode *ProcessTupleGetAccess(public_lib::Context *ctx, ir::MemberExpression *memberExpression,
checker::ETSTupleType const *tupleType)
{
ES2PANDA_ASSERT_POS(memberExpression->Property()->IsNumberLiteral(), memberExpression->Property()->Start());
auto *const checker = ctx->GetChecker()->AsETSChecker();
static std::size_t const TUPLE_THRESHOLD = checker->GetGlobalTypesHolder()->VariadicTupleTypeThreshold();
static std::size_t const CODE_STRING_RESERVE = 64U;
std::string code {};
code.reserve(CODE_STRING_RESERVE);
std::vector<ir::AstNode *> nodes {};
nodes.emplace_back(memberExpression->Object());
ir::Expression *loweringResult = nullptr;
if (std::size_t value = memberExpression->Property()->AsNumberLiteral()->Number().GetInt();
value < TUPLE_THRESHOLD) {
code = "@@E1.$" + std::to_string(value);
} else {
auto *memberType =
checker->AllocNode<ir::OpaqueTypeNode>(tupleType->GetTupleTypesList()[value], checker->Allocator());
value -= TUPLE_THRESHOLD;
code = "@@E1." + std::string {compiler::Signatures::TUPLE_ARRAY} + '.' +
std::string {compiler::Signatures::GET_INDEX_METHOD} + '(' + std::to_string(value) + ") as @@T2";
nodes.emplace_back(memberType);
}
loweringResult = ctx->parser->AsETSParser()->CreateFormattedExpression(code, nodes);
;
loweringResult->SetParent(memberExpression->Parent());
loweringResult->SetRange(memberExpression->Property()->Range());
auto *const varbinder = checker->VarBinder()->AsETSBinder();
auto *scope = NearestScope(memberExpression->Parent());
auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, scope);
CheckLoweredNode(varbinder, checker, loweringResult);
return loweringResult;
}
bool ObjectIndexLowering::PerformForProgram(parser::Program *program)
{
const auto isGetSetExpression = [](const ir::MemberExpression *const memberExpr) {
return memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS && memberExpr->ObjType() != nullptr;
};
const auto isTupleAccess =
[](const ir::MemberExpression *const memberExpr) -> std::optional<checker::ETSTupleType const *> {
if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
auto *type = memberExpr->Object()->TsType();
while (type->IsETSTypeAliasType()) {
type = type->AsETSTypeAliasType()->GetTargetType();
}
if (type->IsETSTupleType()) {
return std::make_optional(type->AsETSTupleType());
}
}
return std::nullopt;
};
program->Ast()->TransformChildrenRecursively(
[ctx = Context(), &isGetSetExpression, &isTupleAccess](ir::AstNode *const ast) {
if (ast->IsAssignmentExpression() && ast->AsAssignmentExpression()->Left()->IsMemberExpression()) {
const auto *const memberExpr = ast->AsAssignmentExpression()->Left()->AsMemberExpression();
if (isGetSetExpression(memberExpr)) {
return ProcessIndexSetAccess(ctx, ast->AsAssignmentExpression());
}
if (isTupleAccess(memberExpr)) {
return ProcessTupleSetAccess(ctx, ast->AsAssignmentExpression());
}
}
return ast;
},
Name());
program->Ast()->TransformChildrenRecursively(
[ctx = Context(), &isGetSetExpression, &isTupleAccess](ir::AstNode *const ast) {
if (ast->IsMemberExpression()) {
auto *const memberExpr = ast->AsMemberExpression();
if (isGetSetExpression(memberExpr)) {
return ProcessIndexGetAccess(ctx, memberExpr);
}
if (auto type = isTupleAccess(memberExpr); type.has_value()) {
return ProcessTupleGetAccess(ctx, memberExpr, *type);
}
}
return ast;
},
Name());
return true;
}
bool ObjectIndexLowering::PostconditionForProgram(const parser::Program *program)
{
return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) {
if (ast->IsMemberExpression() &&
ast->AsMemberExpression()->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
if (auto const *const objectType = ast->AsMemberExpression()->ObjType(); objectType != nullptr) {
return true;
}
}
return false;
});
}
}