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 "tryStatement.h"
17
18#include <compiler/core/pandagen.h>
19#include <compiler/core/dynamicContext.h>
20#include <compiler/base/catchTable.h>
21#include <ir/astDump.h>
22#include <ir/base/catchClause.h>
23#include <ir/statements/blockStatement.h>
24
25namespace panda::es2panda::ir {
26
27void TryStatement::Iterate(const NodeTraverser &cb) const
28{
29    cb(block_);
30
31    if (catchClause_) {
32        cb(catchClause_);
33    }
34
35    if (finalizer_) {
36        cb(finalizer_);
37    }
38}
39
40void TryStatement::Dump(ir::AstDumper *dumper) const
41{
42    dumper->Add({{"type", "TryStatement"},
43                 {"block", block_},
44                 {"handler", AstDumper::Nullable(catchClause_)},
45                 {"finalizer", AstDumper::Nullable(finalizer_)}});
46}
47
48void TryStatement::CompileFinally(compiler::PandaGen *pg, compiler::TryContext *tryCtx,
49                                  const compiler::TryLabelSet &labelSet) const
50{
51    compiler::RegScope rs(pg);
52    compiler::VReg exception = pg->AllocReg();
53    pg->StoreConst(this, exception, compiler::Constant::JS_HOLE);
54    pg->Branch(this, labelSet.CatchEnd());
55
56    pg->SetLabel(this, labelSet.CatchBegin());
57    pg->StoreAccumulator(this, exception);
58
59    pg->SetLabel(this, labelSet.CatchEnd());
60
61    compiler::Label *label = pg->AllocLabel();
62    pg->LoadAccumulator(this, tryCtx->FinalizerRun());
63
64    pg->BranchIfNotUndefined(this, label);
65    pg->StoreAccumulator(this, tryCtx->FinalizerRun());
66    tryCtx->EmitFinalizer();
67    pg->SetLabel(this, label);
68
69    pg->LoadAccumulator(this, exception);
70    pg->EmitRethrow(this);
71}
72
73void TryStatement::CompileTryCatchFinally(compiler::PandaGen *pg) const
74{
75    ASSERT(catchClause_ && finalizer_);
76
77    compiler::TryContext tryCtx(pg, this);
78    const auto &labelSet = tryCtx.LabelSet();
79
80    pg->SetLabel(this, labelSet.TryBegin());
81    {
82        compiler::TryContext innerTryCtx(pg, this, false);
83        const auto &innerLabelSet = innerTryCtx.LabelSet();
84
85        pg->SetLabel(this, innerLabelSet.TryBegin());
86        block_->Compile(pg);
87        pg->SetLabel(this, innerLabelSet.TryEnd());
88
89        pg->Branch(this, innerLabelSet.CatchEnd());
90
91        pg->SetLabel(this, innerLabelSet.CatchBegin());
92        catchClause_->Compile(pg);
93        pg->SetLabel(this, innerLabelSet.CatchEnd());
94    }
95    pg->SetLabel(this, labelSet.TryEnd());
96
97    CompileFinally(pg, &tryCtx, labelSet);
98}
99
100void TryStatement::CompileTryFinally(compiler::PandaGen *pg) const
101{
102    ASSERT(!catchClause_ && finalizer_);
103
104    compiler::TryContext tryCtx(pg, this);
105    const auto &labelSet = tryCtx.LabelSet();
106
107    pg->SetLabel(this, labelSet.TryBegin());
108    {
109        compiler::TryContext innerTryCtx(pg, this, false);
110        const auto &innerLabelSet = innerTryCtx.LabelSet();
111
112        pg->SetLabel(this, innerLabelSet.TryBegin());
113        block_->Compile(pg);
114        pg->SetLabel(this, innerLabelSet.TryEnd());
115
116        pg->Branch(this, innerLabelSet.CatchEnd());
117
118        pg->SetLabel(this, innerLabelSet.CatchBegin());
119        pg->EmitThrow(this);
120        pg->SetLabel(this, innerLabelSet.CatchEnd());
121    }
122    pg->SetLabel(this, labelSet.TryEnd());
123
124    CompileFinally(pg, &tryCtx, labelSet);
125}
126
127void TryStatement::CompileTryCatch(compiler::PandaGen *pg) const
128{
129    ASSERT(catchClause_ && !finalizer_);
130
131    compiler::TryContext tryCtx(pg, this);
132    const auto &labelSet = tryCtx.LabelSet();
133
134    pg->SetLabel(this, labelSet.TryBegin());
135    block_->Compile(pg);
136    pg->SetLabel(this, labelSet.TryEnd());
137
138    pg->Branch(this, labelSet.CatchEnd());
139
140    pg->SetLabel(this, labelSet.CatchBegin());
141    catchClause_->Compile(pg);
142    pg->SetLabel(this, labelSet.CatchEnd());
143}
144
145void TryStatement::Compile(compiler::PandaGen *pg) const
146{
147    if (finalizer_) {
148        if (catchClause_) {
149            CompileTryCatchFinally(pg);
150        } else {
151            CompileTryFinally(pg);
152        }
153    } else {
154        CompileTryCatch(pg);
155    }
156}
157
158checker::Type *TryStatement::Check(checker::Checker *checker) const
159{
160    block_->Check(checker);
161
162    if (catchClause_) {
163        catchClause_->Check(checker);
164    }
165
166    if (finalizer_) {
167        finalizer_->Check(checker);
168    }
169
170    return nullptr;
171}
172
173void TryStatement::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
174{
175    block_ = std::get<ir::AstNode *>(cb(block_))->AsBlockStatement();
176
177    if (catchClause_) {
178        catchClause_ = std::get<ir::AstNode *>(cb(catchClause_))->AsCatchClause();
179    }
180
181    if (finalizer_) {
182        finalizer_ = std::get<ir::AstNode *>(cb(finalizer_))->AsBlockStatement();
183    }
184}
185
186}  // namespace panda::es2panda::ir
187