* 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 "setJumpTarget.h"
#include "checker/types/globalTypesHolder.h"
#include "compiler/lowering/util.h"
namespace ark::es2panda::compiler {
static void LogError(const public_lib::Context *ctx, const diagnostic::DiagnosticKind &diagnostic,
const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos)
{
ctx->diagnosticEngine->LogDiagnostic(diagnostic, diagnosticParams, pos);
}
static void SetTarget(ir::AstNode *const node, ir::AstNode *const target)
{
if (node->IsContinueStatement()) {
node->AsContinueStatement()->SetTarget(target);
} else {
ES2PANDA_ASSERT(node->IsBreakStatement());
node->AsBreakStatement()->SetTarget(target);
}
}
void SetJumpTargetPhase::FindJumpTarget(ir::AstNode *const node) const
{
bool isContinue = node->IsContinueStatement();
auto label = isContinue ? node->AsContinueStatement()->Ident() : node->AsBreakStatement()->Ident();
if (label != nullptr) {
if (auto var = label->Variable(); var == nullptr) {
varbinder::LetDecl *decl;
auto *varbinder = Context()->parserProgram->VarBinder()->AsETSBinder();
std::tie(decl, var) = varbinder->NewVarDecl<varbinder::LetDecl>(
label->Start(),
!label->IsErrorPlaceHolder() ? label->Name() : compiler::GenName(Context()->allocator).View());
var->SetScope(varbinder->GetScope());
label->SetVariable(var);
decl->BindNode(label);
label->SetTsType(var->SetTsType(Context()->GetChecker()->GetGlobalTypesHolder()->GlobalTypeError()));
} else if (var->Declaration()->IsLabelDecl()) {
SetTarget(node, var->Declaration()->Node());
return;
}
if (!label->IsErrorPlaceHolder()) {
LogError(Context(), diagnostic::UNRESOLVED_REF, {label->Name()}, label->Start());
}
SetTarget(node, nullptr);
return;
}
auto *target = node->Parent();
while (target != nullptr) {
switch (target->Type()) {
case ir::AstNodeType::SWITCH_STATEMENT: {
if (isContinue) {
break;
}
[[fallthrough]];
}
case ir::AstNodeType::DO_WHILE_STATEMENT:
case ir::AstNodeType::WHILE_STATEMENT:
case ir::AstNodeType::FOR_UPDATE_STATEMENT:
case ir::AstNodeType::FOR_OF_STATEMENT: {
SetTarget(node, target);
return;
}
default: {
break;
}
}
target = target->Parent();
}
LogError(Context(), diagnostic::FLOW_REDIRECTION_INVALID_CTX, {}, node->Start());
SetTarget(node, nullptr);
}
bool SetJumpTargetPhase::PerformForProgram(parser::Program *program)
{
program->Ast()->IterateRecursivelyPostorder([this](ir::AstNode *const node) -> void {
if (node->IsBreakStatement() || node->IsContinueStatement()) {
FindJumpTarget(node);
}
});
return true;
}
}