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
22 namespace ark::es2panda::compiler {
23 // Iterator
24
Iterator(PandaGen *pg, const ir::AstNode *node, IteratorType type)25 Iterator::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
GetMethod(util::StringView name) const41 void Iterator::GetMethod(util::StringView name) const
42 {
43 pg_->GetMethod(node_, iterator_, name);
44 pg_->StoreAccumulator(node_, method_);
45 }
46
CallMethodWithValue() const47 void Iterator::CallMethodWithValue() const
48 {
49 pg_->Call1This(node_, method_, iterator_, nextResult_);
50 }
51
CallMethod() const52 void Iterator::CallMethod() const
53 {
54 pg_->Call0This(node_, method_, iterator_);
55 }
56
Next() const57 void 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
Complete() const69 void Iterator::Complete() const
70 {
71 pg_->LoadObjByName(node_, "done");
72 pg_->ToBoolean(node_);
73 }
74
Value() const75 void Iterator::Value() const
76 {
77 pg_->LoadAccumulator(node_, nextResult_);
78 pg_->LoadObjByName(node_, "value");
79 }
80
CloseInnerResultNormal(bool abruptCompletion, Label *returnExits) const81 void 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
Close(bool abruptCompletion) const120 void 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
DestructuringIterator(PandaGen *pg, const ir::AstNode *node)175 DestructuringIterator::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
Step(Label *doneTarget) const182 void 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
OnIterDone([[maybe_unused]] Label *doneTarget) const215 void DestructuringIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const
216 {
217 pg_->LoadConst(node_, Constant::JS_UNDEFINED);
218 }
219
OnIterDone([[maybe_unused]] Label *doneTarget) const220 void DestructuringRestIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const
221 {
222 pg_->Branch(node_, doneTarget);
223 }
224 } // namespace ark::es2panda::compiler
225