1/**
2 * Copyright (c) 2021 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 <compiler/core/pandagen.h>
19#include <compiler/base/catchTable.h>
20#include <ir/expressions/identifier.h>
21#include <ir/statements/tryStatement.h>
22#include <ir/statements/blockStatement.h>
23#include <ir/statements/labelledStatement.h>
24
25namespace panda::es2panda::compiler {
26DynamicContext::DynamicContext(PandaGen *pg, LabelTarget target) : pg_(pg), target_(target), prev_(pg_->dynamicContext_)
27{
28    pg_->dynamicContext_ = this;
29}
30
31DynamicContext::~DynamicContext()
32{
33    pg_->dynamicContext_ = prev_;
34}
35
36LabelContext::LabelContext(PandaGen *pg, const ir::LabelledStatement *labelledStmt)
37    : DynamicContext(pg, LabelTarget(labelledStmt->Ident()->Name())), labelledStmt_(labelledStmt)
38{
39    if (!labelledStmt->Body()->IsBlockStatement() && !labelledStmt->Body()->IsIfStatement() &&
40        !labelledStmt->Body()->IsBreakStatement()) {
41        return;
42    }
43
44    label_ = pg->AllocLabel();
45    target_.SetBreakTarget(label_);
46}
47
48LabelContext::~LabelContext()
49{
50    if (!label_) {
51        return;
52    }
53
54    pg_->SetLabel(labelledStmt_, label_);
55}
56
57LexEnvContext::LexEnvContext(VariableEnvScope *envScope, PandaGen *pg, LabelTarget target)
58    : DynamicContext(pg, target), envScope_(envScope)
59{
60    if (!envScope_->HasEnv()) {
61        return;
62    }
63
64    catchTable_ = pg_->CreateCatchTable();
65    const auto &labelSet = catchTable_->LabelSet();
66    const auto *node = envScope_->Scope()->Node();
67
68    pg_->SetLabel(node, labelSet.TryBegin());
69}
70
71LexEnvContext::~LexEnvContext()
72{
73    if (!envScope_->HasEnv()) {
74        return;
75    }
76
77    const auto &labelSet = catchTable_->LabelSet();
78    const auto *node = envScope_->Scope()->Node();
79
80    pg_->SetLabel(node, labelSet.TryEnd());
81    pg_->Branch(node, labelSet.CatchEnd());
82
83    pg_->SetLabel(node, labelSet.CatchBegin());
84    pg_->PopLexEnv(node);
85    pg_->EmitThrow(node);
86    pg_->SetLabel(node, labelSet.CatchEnd());
87    pg_->PopLexEnv(node);
88}
89
90bool LexEnvContext::HasTryCatch() const
91{
92    return envScope_->HasEnv();
93}
94
95void LexEnvContext::AbortContext([[maybe_unused]] ControlFlowChange cfc,
96                                 [[maybe_unused]] const util::StringView &targetLabel)
97{
98    if (!envScope_->HasEnv()) {
99        return;
100    }
101
102    const auto *node = envScope_->Scope()->Node();
103    if (node->IsForUpdateStatement()) {
104        return;
105    }
106
107    pg_->PopLexEnv(node);
108}
109
110IteratorContext::IteratorContext(PandaGen *pg, const Iterator &iterator, LabelTarget target)
111    : DynamicContext(pg, target), iterator_(iterator), catchTable_(pg->CreateCatchTable())
112{
113    const auto &labelSet = catchTable_->LabelSet();
114    pg_->SetLabel(iterator_.Node(), labelSet.TryBegin());
115}
116
117IteratorContext::~IteratorContext()
118{
119    const auto &labelSet = catchTable_->LabelSet();
120    const auto *node = iterator_.Node();
121
122    pg_->SetLabel(node, labelSet.TryEnd());
123    pg_->Branch(node, labelSet.CatchEnd());
124
125    pg_->SetLabel(node, labelSet.CatchBegin());
126    iterator_.Close(true);
127    pg_->SetLabel(node, labelSet.CatchEnd());
128}
129
130void IteratorContext::AbortContext([[maybe_unused]] ControlFlowChange cfc,
131                                   [[maybe_unused]] const util::StringView &targetLabel)
132{
133    if (cfc == ControlFlowChange::CONTINUE && target_.ContinueLabel() == targetLabel) {
134        return;
135    }
136
137    iterator_.Close(false);
138}
139
140DestructuringIteratorContext::DestructuringIteratorContext(PandaGen *pg, const DestructuringIterator &iterator)
141    : DynamicContext(pg, {}), iterator_(iterator), catchTable_(pg->CreateCatchTable())
142{
143    const auto &labelSet = catchTable_->LabelSet();
144    pg_->SetLabel(iterator_.Node(), labelSet.TryBegin());
145}
146
147DestructuringIteratorContext::~DestructuringIteratorContext()
148{
149    const auto &labelSet = catchTable_->LabelSet();
150    const auto *node = iterator_.Node();
151
152    pg_->SetLabel(node, labelSet.TryEnd());
153
154    // Normal completion
155    pg_->LoadAccumulator(node, iterator_.Done());
156    pg_->BranchIfTrue(node, labelSet.CatchEnd());
157    iterator_.Close(false);
158
159    pg_->Branch(node, labelSet.CatchEnd());
160
161    Label *end = pg_->AllocLabel();
162    pg_->SetLabel(node, labelSet.CatchBegin());
163    pg_->StoreAccumulator(node, iterator_.Result());
164    pg_->LoadAccumulator(node, iterator_.Done());
165
166    pg_->BranchIfTrue(node, end);
167    pg_->LoadAccumulator(node, iterator_.Result());
168    iterator_.Close(true);
169    pg_->SetLabel(node, end);
170    pg_->LoadAccumulator(node, iterator_.Result());
171    pg_->EmitThrow(node);
172    pg_->SetLabel(node, labelSet.CatchEnd());
173}
174
175void DestructuringIteratorContext::AbortContext(ControlFlowChange cfc, const util::StringView &targetLabel)
176{
177    if (cfc == ControlFlowChange::CONTINUE && target_.ContinueLabel() == targetLabel) {
178        return;
179    }
180
181    iterator_.Close(false);
182}
183
184void TryContext::InitFinalizer()
185{
186    ASSERT(tryStmt_);
187
188    if (!hasFinalizer_ || !tryStmt_->FinallyBlock()) {
189        return;
190    }
191
192    finalizerRun_ = pg_->AllocReg();
193    pg_->StoreConst(tryStmt_, finalizerRun_, Constant::JS_UNDEFINED);
194}
195
196void TryContext::InitCatchTable()
197{
198    catchTable_ = pg_->CreateCatchTable();
199}
200
201const TryLabelSet &TryContext::LabelSet() const
202{
203    return catchTable_->LabelSet();
204}
205
206bool TryContext::HasFinalizer() const
207{
208    return hasFinalizer_;
209}
210
211void TryContext::EmitFinalizer()
212{
213    if (!hasFinalizer_ || inFinalizer_ || !tryStmt_->FinallyBlock()) {
214        return;
215    }
216
217    inFinalizer_ = true;
218    tryStmt_->FinallyBlock()->Compile(pg_);
219    inFinalizer_ = false;
220}
221
222void TryContext::AbortContext([[maybe_unused]] ControlFlowChange cfc,
223                              [[maybe_unused]] const util::StringView &targetLabel)
224{
225    EmitFinalizer();
226}
227}  // namespace panda::es2panda::compiler
228