1/**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "dynamicContext.h"
17
18#include "checker/types/ets/etsObjectType.h"
19#include "checker/types/type.h"
20#include "compiler/core/envScope.h"
21#include "compiler/core/ETSGen.h"
22#include "compiler/core/pandagen.h"
23#include "compiler/base/catchTable.h"
24#include "ir/expressions/identifier.h"
25#include "ir/base/catchClause.h"
26#include "ir/statements/blockStatement.h"
27#include "ir/statements/breakStatement.h"
28#include "ir/statements/continueStatement.h"
29#include "ir/statements/returnStatement.h"
30#include "ir/statements/tryStatement.h"
31#include "ir/statements/labelledStatement.h"
32
33namespace ark::es2panda::compiler {
34DynamicContext::DynamicContext(CodeGen *cg, LabelTarget target) : cg_(cg), target_(target), prev_(Cg()->dynamicContext_)
35{
36    Cg()->dynamicContext_ = this;
37}
38
39DynamicContext::~DynamicContext()
40{
41    Cg()->dynamicContext_ = prev_;
42}
43
44LabelContext::LabelContext(CodeGen *cg, const ir::LabelledStatement *labelledStmt)
45    : DynamicContext(cg, LabelTarget(labelledStmt->Ident()->Name())), labelledStmt_(labelledStmt)
46{
47    if (!labelledStmt->Body()->IsBlockStatement()) {
48        return;
49    }
50
51    label_ = cg->AllocLabel();
52    Target().SetBreakTarget(label_);
53}
54
55LabelContext::~LabelContext()
56{
57    if (label_ == nullptr) {
58        return;
59    }
60
61    Cg()->SetLabel(labelledStmt_, label_);
62}
63
64LexEnvContext::LexEnvContext(LoopEnvScope *envScope, PandaGen *pg, LabelTarget target)
65    : DynamicContext(pg, target), envScope_(envScope)
66{
67    if (!envScope_->HasEnv()) {
68        return;
69    }
70
71    catchTable_ = Cg()->CreateCatchTable();
72    const auto &labelSet = catchTable_->LabelSet();
73    const auto *node = envScope_->Scope()->Node();
74
75    Cg()->SetLabel(node, labelSet.TryBegin());
76}
77
78LexEnvContext::~LexEnvContext()
79{
80    if (!envScope_->HasEnv()) {
81        return;
82    }
83
84    const auto &labelSet = catchTable_->LabelSet();
85    const auto *node = envScope_->Scope()->Node();
86
87    Cg()->SetLabel(node, labelSet.TryEnd());
88    Cg()->Branch(node, labelSet.CatchEnd());
89
90    Cg()->SetLabel(node, labelSet.CatchBegin());
91    AsPandaGen()->PopLexEnv(node);
92    AsPandaGen()->EmitThrow(node);
93    Cg()->SetLabel(node, labelSet.CatchEnd());
94    AsPandaGen()->PopLexEnv(node);
95}
96
97PandaGen *LexEnvContext::AsPandaGen() const
98{
99    return static_cast<PandaGen *>(Cg());
100}
101
102bool LexEnvContext::HasTryCatch() const
103{
104    return envScope_->HasEnv();
105}
106
107void LexEnvContext::AbortContext([[maybe_unused]] ControlFlowChange cfc,
108                                 [[maybe_unused]] const util::StringView &targetLabel)
109{
110    if (cfc == ControlFlowChange::CONTINUE || !envScope_->HasEnv()) {
111        return;
112    }
113
114    const auto *node = envScope_->Scope()->Node();
115    AsPandaGen()->PopLexEnv(node);
116}
117
118IteratorContext::IteratorContext(PandaGen *pg, const Iterator &iterator, LabelTarget target)
119    : DynamicContext(pg, target), iterator_(iterator), catchTable_(pg->CreateCatchTable())
120{
121    const auto &labelSet = catchTable_->LabelSet();
122    pg->SetLabel(iterator_.Node(), labelSet.TryBegin());
123}
124
125IteratorContext::~IteratorContext()
126{
127    const auto &labelSet = catchTable_->LabelSet();
128    const auto *node = iterator_.Node();
129
130    Cg()->SetLabel(node, labelSet.TryEnd());
131    Cg()->Branch(node, labelSet.CatchEnd());
132
133    Cg()->SetLabel(node, labelSet.CatchBegin());
134    iterator_.Close(true);
135    Cg()->SetLabel(node, labelSet.CatchEnd());
136}
137
138void IteratorContext::AbortContext([[maybe_unused]] ControlFlowChange cfc,
139                                   [[maybe_unused]] const util::StringView &targetLabel)
140{
141    if (cfc == ControlFlowChange::CONTINUE && Target().ContinueLabel() == targetLabel) {
142        return;
143    }
144
145    iterator_.Close(false);
146}
147
148void TryContext::InitFinalizer()
149{
150    ASSERT(tryStmt_);
151
152    if (!hasFinalizer_ || (tryStmt_->FinallyBlock() == nullptr)) {
153        return;
154    }
155
156    auto *pg = static_cast<PandaGen *>(Cg());
157
158    finalizerRun_ = pg->AllocReg();
159    pg->StoreConst(tryStmt_, finalizerRun_, Constant::JS_UNDEFINED);
160}
161
162void CatchContext::InitCatchTable()
163{
164    auto *pg = static_cast<PandaGen *>(Cg());
165    catchTable_ = pg->CreateCatchTable();
166}
167
168const TryLabelSet &CatchContext::LabelSet() const
169{
170    return catchTable_->LabelSet();
171}
172
173bool TryContext::HasFinalizer() const
174{
175    return hasFinalizer_;
176}
177
178void TryContext::EmitFinalizer()
179{
180    if (!hasFinalizer_ || inFinalizer_ || (tryStmt_->FinallyBlock() == nullptr)) {
181        return;
182    }
183
184    auto *pg = static_cast<PandaGen *>(Cg());
185    inFinalizer_ = true;
186    tryStmt_->FinallyBlock()->Compile(pg);
187    inFinalizer_ = false;
188}
189
190CatchTable *ETSCatchContext::AddNewCathTable(const util::StringView assemblerType)
191{
192    auto *cg = Cg();
193
194    CatchTable *catchTable = cg->CreateCatchTable(assemblerType);
195    catchTables_.push_back(catchTable);
196
197    return catchTable;
198}
199
200CatchTable *ETSCatchContext::AddNewCathTable(const util::StringView assemblerType, const LabelPair tryLabelPair)
201{
202    auto *cg = Cg();
203
204    CatchTable *catchTable = cg->CreateCatchTable(tryLabelPair, assemblerType);
205    catchTables_.push_back(catchTable);
206
207    return catchTable;
208}
209
210void ETSTryContext::EmitFinalizer(
211    LabelPair trycatchLabelPair,
212    const ArenaVector<std::pair<compiler::LabelPair, const ir::Statement *>> &finalizerInsertions)
213{
214    ASSERT(tryStmt_);
215
216    if (!hasFinalizer_ || (tryStmt_->FinallyBlock() == nullptr)) {
217        return;
218    }
219    auto *etsg = static_cast<ETSGen *>(Cg());
220
221    CatchTable *finalizerTable = AddNewCathTable("", trycatchLabelPair);
222    // First compile of the finaly clause, executed if the statement executed normally
223    tryStmt_->FinallyBlock()->Compile(etsg);
224
225    etsg->Branch(tryStmt_, finalizerTable->LabelSet().CatchEnd());
226
227    for (std::pair<compiler::LabelPair, const ir::Statement *> insertion : finalizerInsertions) {
228        EmitFinalizerInsertion(etsg, insertion.first, insertion.second);
229    }
230
231    etsg->SetLabel(tryStmt_, finalizerTable->LabelSet().CatchBegin());
232
233    compiler::VReg exception = etsg->StoreException(tryStmt_);
234    // Third compile of the finaly clause, executed if the statement executed abruptly
235    tryStmt_->FinallyBlock()->Compile(etsg);
236
237    etsg->LoadAccumulator(tryStmt_, exception);
238    etsg->EmitThrow(tryStmt_, exception);
239
240    etsg->SetLabel(tryStmt_, finalizerTable->LabelSet().CatchEnd());
241}
242
243void ETSTryContext::EmitFinalizerInsertion(ETSGen *etsg, compiler::LabelPair labelPair, const ir::Statement *statement)
244{
245    etsg->SetLabel(tryStmt_, labelPair.Begin());
246
247    ASSERT(statement != nullptr);
248    bool isReturn = statement->IsReturnStatement();
249
250    compiler::RegScope rs(etsg);
251    compiler::VReg res = etsg->AllocReg();
252
253    if (isReturn) {
254        etsg->SetAccumulatorType(statement->AsReturnStatement()->ReturnType());
255        etsg->StoreAccumulator(tryStmt_, res);
256        etsg->SetVRegType(res, statement->AsReturnStatement()->ReturnType());
257    }
258
259    // Second compile of the finaly clause, executed if the statement executed normally, but abrupted by
260    // return, break, or continue statements.
261    tryStmt_->FinallyBlock()->Compile(etsg);
262
263    if (isReturn) {
264        etsg->SetAccumulatorType(statement->AsReturnStatement()->ReturnType());
265        etsg->LoadAccumulator(tryStmt_, res);
266    }
267
268    if (labelPair.End() != nullptr) {
269        etsg->Branch(tryStmt_, labelPair.End());
270    } else if (isReturn) {
271        if (etsg->CheckControlFlowChange()) {
272            etsg->StoreAccumulator(tryStmt_, res);
273            etsg->ControlFlowChangeBreak();
274            etsg->LoadAccumulator(tryStmt_, res);
275        }
276
277        etsg->ReturnAcc(tryStmt_);
278    } else if (statement->IsBreakStatement()) {
279        compiler::Label *target = etsg->ControlFlowChangeBreak(statement->AsBreakStatement()->Ident());
280        etsg->Branch(tryStmt_, target);
281    } else if (statement->IsContinueStatement()) {
282        compiler::Label *target = etsg->ControlFlowChangeContinue(statement->AsContinueStatement()->Ident());
283        etsg->Branch(tryStmt_, target);
284    } else {
285        UNREACHABLE();
286    }
287}
288
289}  // namespace ark::es2panda::compiler
290