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 "iterators.h"
17
18#include "compiler/core/pandagen.h"
19#include "compiler/base/catchTable.h"
20#include "compiler/function/functionBuilder.h"
21
22namespace ark::es2panda::compiler {
23// Iterator
24
25Iterator::Iterator(PandaGen *pg, const ir::AstNode *node, IteratorType type)
26    : pg_(pg), node_(node), method_(pg->AllocReg()), iterator_(pg->AllocReg()), nextResult_(pg->AllocReg()), type_(type)
27{
28    if (type_ == IteratorType::ASYNC) {
29        pg_->GetAsyncIterator(node);
30    } else {
31        pg_->GetIterator(node);
32    }
33
34    pg_->StoreAccumulator(node, iterator_);
35    pg_->LoadObjByName(node_, "next");
36    pg_->StoreAccumulator(node_, method_);
37
38    pg_->ThrowIfNotObject(node_);
39}
40
41void Iterator::GetMethod(util::StringView name) const
42{
43    pg_->GetMethod(node_, iterator_, name);
44    pg_->StoreAccumulator(node_, method_);
45}
46
47void Iterator::CallMethodWithValue() const
48{
49    pg_->Call1This(node_, method_, iterator_, nextResult_);
50}
51
52void Iterator::CallMethod() const
53{
54    pg_->Call0This(node_, method_, iterator_);
55}
56
57void Iterator::Next() const
58{
59    CallMethod();
60
61    if (type_ == IteratorType::ASYNC) {
62        pg_->FuncBuilder()->Await(node_);
63    }
64
65    pg_->ThrowIfNotObject(node_);
66    pg_->StoreAccumulator(node_, nextResult_);
67}
68
69void Iterator::Complete() const
70{
71    pg_->LoadObjByName(node_, "done");
72    pg_->ToBoolean(node_);
73}
74
75void Iterator::Value() const
76{
77    pg_->LoadAccumulator(node_, nextResult_);
78    pg_->LoadObjByName(node_, "value");
79}
80
81void Iterator::CloseInnerResultNormal(bool abruptCompletion, Label *returnExits) const
82{
83    VReg completion = pg_->AllocReg();
84    VReg innerResult = pg_->AllocReg();
85    VReg innerResultType = pg_->AllocReg();
86
87    // b. If return is undefined, return Completion(completion).
88    pg_->BranchIfNotUndefined(node_, returnExits);
89    // a. Let return be innerResult.[[Value]].
90    pg_->LoadAccumulator(node_, completion);
91
92    if (abruptCompletion) {
93        pg_->EmitThrow(node_);
94    } else {
95        pg_->DirectReturn(node_);
96    }
97
98    pg_->SetLabel(node_, returnExits);
99
100    {
101        TryContext innerTryCtx(pg_);
102        const auto &innerLabelSet = innerTryCtx.LabelSet();
103
104        pg_->SetLabel(node_, innerLabelSet.TryBegin());
105        // c. Set innerResult to Call(return, iterator).
106        CallMethod();
107        // d. If innerResult.[[Type]] is normal, set innerResult to Await(innerResult.[[Value]]).
108        pg_->FuncBuilder()->Await(node_);
109        pg_->StoreAccumulator(node_, innerResult);
110        pg_->SetLabel(node_, innerLabelSet.TryEnd());
111        pg_->Branch(node_, innerLabelSet.CatchEnd());
112
113        pg_->SetLabel(node_, innerLabelSet.CatchBegin());
114        pg_->StoreAccumulator(node_, innerResult);
115        pg_->StoreAccumulator(node_, innerResultType);
116        pg_->SetLabel(node_, innerLabelSet.CatchEnd());
117    }
118}
119
120void Iterator::Close(bool abruptCompletion) const
121{
122    if (type_ == IteratorType::SYNC) {
123        if (!abruptCompletion) {
124            pg_->LoadConst(node_, Constant::JS_HOLE);
125        }
126        pg_->CloseIterator(node_, iterator_);
127        return;
128    }
129
130    RegScope rs(pg_);
131    VReg completion = pg_->AllocReg();
132    VReg innerResult = pg_->AllocReg();
133    VReg innerResultType = pg_->AllocReg();
134
135    pg_->StoreAccumulator(node_, completion);
136    pg_->StoreConst(node_, innerResultType, Constant::JS_HOLE);
137
138    TryContext tryCtx(pg_);
139    const auto &labelSet = tryCtx.LabelSet();
140    Label *returnExits = pg_->AllocLabel();
141
142    pg_->SetLabel(node_, labelSet.TryBegin());
143
144    // 4. Let innerResult be GetMethod(iterator, "return").
145    GetMethod("return");
146
147    // 5. If innerResult.[[Type]] is normal, then
148    {
149        CloseInnerResultNormal(abruptCompletion, returnExits);
150    }
151
152    pg_->SetLabel(node_, labelSet.TryEnd());
153    pg_->Branch(node_, labelSet.CatchEnd());
154
155    pg_->SetLabel(node_, labelSet.CatchBegin());
156    pg_->StoreAccumulator(node_, innerResult);
157    pg_->StoreAccumulator(node_, innerResultType);
158    pg_->SetLabel(node_, labelSet.CatchEnd());
159
160    // 6. If completion.[[Type]] is throw, return Completion(completion).
161    if (abruptCompletion) {
162        pg_->LoadAccumulator(node_, completion);
163        pg_->EmitThrow(node_);
164    } else {
165        // 7. If innerResult.[[Type]] is throw, return Completion(innerResult).
166        pg_->LoadAccumulator(node_, innerResultType);
167        pg_->EmitRethrow(node_);
168    }
169
170    // 8. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
171    pg_->LoadAccumulator(node_, innerResult);
172    pg_->ThrowIfNotObject(node_);
173}
174
175DestructuringIterator::DestructuringIterator(PandaGen *pg, const ir::AstNode *node)
176    : Iterator(pg, node, IteratorType::SYNC), done_(pg->AllocReg()), result_(pg->AllocReg())
177{
178    pg_->StoreConst(node, done_, Constant::JS_FALSE);
179    pg_->StoreConst(node, result_, Constant::JS_UNDEFINED);
180}
181
182void DestructuringIterator::Step(Label *doneTarget) const
183{
184    TryContext tryCtx(pg_);
185    const auto &labelSet = tryCtx.LabelSet();
186    Label *normalClose = pg_->AllocLabel();
187    Label *noClose = pg_->AllocLabel();
188
189    pg_->SetLabel(node_, labelSet.TryBegin());
190    Next();
191    Complete();
192    pg_->StoreAccumulator(node_, done_);
193    pg_->BranchIfFalse(node_, normalClose);
194    pg_->StoreConst(node_, done_, Constant::JS_TRUE);
195    pg_->LoadConst(node_, Constant::JS_UNDEFINED);
196    OnIterDone(doneTarget);
197    pg_->Branch(node_, noClose);
198
199    pg_->SetLabel(node_, normalClose);
200    Value();
201    pg_->StoreAccumulator(node_, result_);
202    pg_->SetLabel(node_, noClose);
203
204    pg_->SetLabel(node_, labelSet.TryEnd());
205    pg_->Branch(node_, labelSet.CatchEnd());
206
207    pg_->SetLabel(node_, labelSet.CatchBegin());
208    pg_->StoreAccumulator(node_, result_);
209    pg_->StoreConst(node_, done_, Constant::JS_TRUE);
210    pg_->LoadAccumulator(node_, result_);
211    pg_->EmitThrow(node_);
212    pg_->SetLabel(node_, labelSet.CatchEnd());
213}
214
215void DestructuringIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const
216{
217    pg_->LoadConst(node_, Constant::JS_UNDEFINED);
218}
219
220void DestructuringRestIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const
221{
222    pg_->Branch(node_, doneTarget);
223}
224}  // namespace ark::es2panda::compiler
225