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