* 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 "optionalLowering.h"
#include "checker/ETSchecker.h"
#include "compiler/lowering/util.h"
#include "compiler/lowering/scopesInit/scopesInitPhase.h"
#include "ir/statements/blockStatement.h"
#include "ir/expressions/memberExpression.h"
#include "parser/ETSparser.h"
#include "varbinder/ETSBinder.h"
namespace ark::es2panda::compiler {
std::string_view OptionalLowering::Name() const
{
return "OptionalLowering";
}
static ir::AstNode *LowerOptionalExpr(public_lib::Context *ctx, ir::Expression *const expr,
ir::ChainExpression *const chain)
{
ES2PANDA_ASSERT(expr->IsMemberExpression() || expr->IsCallExpression());
auto *const allocator = ctx->allocator;
auto *const parser = ctx->parser->AsETSParser();
auto *const varbinder = ctx->parserProgram->VarBinder()->AsETSBinder();
auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, NearestScope(expr));
auto *ident = Gensym(allocator);
ir::Expression *source =
expr->IsMemberExpression() ? expr->AsMemberExpression()->Object() : expr->AsCallExpression()->Callee();
auto *sequenceExpr = parser->CreateFormattedExpression("let @@I1 = @@E2; @@I3 == null ? undefined : 0", ident,
source, ident->Clone(allocator, nullptr));
sequenceExpr->SetParent(chain->Parent());
SetSourceRangesRecursively(sequenceExpr, chain->Range());
InitScopesPhaseETS::RunExternalNode(sequenceExpr, varbinder);
auto const &stmts = sequenceExpr->AsBlockExpression()->Statements();
stmts[1]->AsExpressionStatement()->GetExpression()->AsConditionalExpression()->SetAlternate(chain->GetExpression());
auto *identClone = ident->Clone(allocator, nullptr);
expr->IsMemberExpression() ? expr->AsMemberExpression()->SetObject(identClone)
: expr->AsCallExpression()->SetCallee(identClone);
return sequenceExpr;
}
static ir::Expression *FindOptionalInChain(ir::Expression *expr)
{
if (expr->IsMemberExpression()) {
auto typed = expr->AsMemberExpression();
return typed->IsOptional() ? typed : FindOptionalInChain(typed->Object());
}
if (expr->IsCallExpression()) {
auto typed = expr->AsCallExpression();
return typed->IsOptional() ? typed : FindOptionalInChain(typed->Callee());
}
if (expr->IsTSNonNullExpression()) {
return FindOptionalInChain(expr->AsTSNonNullExpression()->Expr());
}
ES2PANDA_UNREACHABLE();
}
static ir::AstNode *LowerChain(public_lib::Context *ctx, ir::ChainExpression *const chain)
{
auto *optional = FindOptionalInChain(chain->GetExpression());
if (optional->IsMemberExpression()) {
auto *expr = optional->AsMemberExpression();
ES2PANDA_ASSERT(expr->IsOptional());
expr->ClearOptional();
return LowerOptionalExpr(ctx, expr, chain);
}
if (optional->IsCallExpression()) {
auto *expr = optional->AsCallExpression();
ES2PANDA_ASSERT(expr->IsOptional());
expr->ClearOptional();
return LowerOptionalExpr(ctx, expr, chain);
}
ES2PANDA_UNREACHABLE();
}
bool OptionalLowering::PerformForProgram(parser::Program *program)
{
program->Ast()->TransformChildrenRecursively(
[ctx = Context()](ir::AstNode *const node) -> ir::AstNode * {
if (node->IsChainExpression()) {
return RefineSourceRanges(LowerChain(ctx, node->AsChainExpression()));
}
return node;
},
Name());
return true;
}
bool OptionalLowering::PostconditionForProgram(const parser::Program *program)
{
return !program->Ast()->IsAnyChild([](const ir::AstNode *node) {
return node->IsChainExpression() || (node->IsMemberExpression() && node->AsMemberExpression()->IsOptional()) ||
(node->IsCallExpression() && node->AsCallExpression()->IsOptional());
});
}
}