* Copyright (c) 2025-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 "setterLowering.h"
#include "checker/ETSchecker.h"
#include "compiler/lowering/util.h"
#include "compiler/lowering/scopesInit/scopesInitPhase.h"
namespace ark::es2panda::compiler {
static bool IsSetterCallOrSetExpression(const ir::Expression *const expr)
{
if (!expr->IsMemberExpression() && !expr->IsIdentifier()) {
return false;
}
auto *const variable = expr->IsMemberExpression() ? expr->AsMemberExpression()->Property()->Variable()
: expr->AsIdentifier()->Variable();
if (checker::ETSChecker::IsVariableGetterSetter(variable)) {
ES2PANDA_ASSERT(variable != nullptr && variable->TsType() != nullptr);
return variable->TsType()->HasTypeFlag(checker::TypeFlag::SETTER);
}
if (expr->IsIdentifier()) {
return false;
}
const auto *memberExpr = expr->AsMemberExpression();
const auto isSetExpression = [](const ir::MemberExpression *const possibleSetExpr) {
return possibleSetExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS &&
possibleSetExpr->ObjType() != nullptr;
};
return isSetExpression(memberExpr);
}
static ir::Expression *TransformSetterCall(public_lib::Context *ctx,
ir::AssignmentExpression *const assignmentExpression)
{
auto *const allocator = ctx->Allocator();
auto *const parser = ctx->parser->AsETSParser();
ir::Identifier *gensymValue = Gensym(allocator);
auto *gensymValueType = ctx->AllocNode<ir::OpaqueTypeNode>(assignmentExpression->Right()->TsType(), allocator);
auto *setGensymAndEvalValue = parser->CreateFormattedExpression(
"@@I1 = @@E2", gensymValue->Clone(allocator, nullptr), assignmentExpression->Right());
std::stringstream ss;
ss << "let @@I1: @@T2;";
ss << "@@E3 = @@E4;";
ss << "(@@I5);";
return parser->CreateFormattedExpression(ss.str(), gensymValue, gensymValueType, assignmentExpression->Left(),
setGensymAndEvalValue, gensymValue->Clone(allocator, nullptr));
}
bool SetterLowering::PerformForProgram(parser::Program *program)
{
program->Ast()->TransformChildrenRecursively(
[ctx = Context()](ir::AstNode *const node) {
if (!node->IsAssignmentExpression() ||
!IsSetterCallOrSetExpression(node->AsAssignmentExpression()->Left())) {
return node;
}
auto *checker = ctx->GetChecker()->AsETSChecker();
auto *assignmentExpr = node->AsAssignmentExpression();
ir::AstNode *loweringResult = TransformSetterCall(ctx, assignmentExpr);
loweringResult->SetParent(assignmentExpr->Parent());
loweringResult->SetRange(assignmentExpr->Range());
RefineSourceRanges(loweringResult);
auto *const scope = NearestScope(assignmentExpr);
auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
InitScopesPhaseETS::RunExternalNode(loweringResult, checker->VarBinder());
checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(loweringResult, scope);
checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY};
loweringResult->Check(checker);
return loweringResult;
},
Name());
return true;
}
}