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 "destructuring.h"
17
18 #include "util/helpers.h"
19 #include "compiler/base/iterators.h"
20 #include "compiler/base/lreference.h"
21 #include "compiler/base/catchTable.h"
22 #include "compiler/core/pandagen.h"
23 #include "ir/base/property.h"
24 #include "ir/base/spreadElement.h"
25 #include "ir/expressions/arrayExpression.h"
26 #include "ir/expressions/assignmentExpression.h"
27 #include "ir/expressions/identifier.h"
28 #include "ir/expressions/objectExpression.h"
29
30 namespace ark::es2panda::compiler {
GenRestElement(PandaGen * pg,const ir::SpreadElement * restElement,const DestructuringIterator & destIterator,bool isDeclaration)31 static void GenRestElement(PandaGen *pg, const ir::SpreadElement *restElement,
32 const DestructuringIterator &destIterator, bool isDeclaration)
33 {
34 VReg array = pg->AllocReg();
35 VReg index = pg->AllocReg();
36
37 auto *next = pg->AllocLabel();
38 auto *done = pg->AllocLabel();
39
40 DestructuringRestIterator iterator(destIterator);
41
42 // create left reference for rest element
43 auto lref = JSLReference::Create(pg, restElement, isDeclaration);
44
45 // create an empty array first
46 pg->CreateEmptyArray(restElement);
47 pg->StoreAccumulator(restElement, array);
48
49 // index = 0
50 pg->LoadAccumulatorInt(restElement, 0);
51 pg->StoreAccumulator(restElement, index);
52
53 pg->SetLabel(restElement, next);
54
55 iterator.Step(done);
56 pg->StoreObjByValue(restElement, array, index);
57
58 // index++
59 pg->LoadAccumulatorInt(restElement, 1);
60 pg->Binary(restElement, lexer::TokenType::PUNCTUATOR_PLUS, index);
61 pg->StoreAccumulator(restElement, index);
62
63 pg->Branch(restElement, next);
64
65 pg->SetLabel(restElement, done);
66 pg->LoadAccumulator(restElement, array);
67
68 lref.SetValue();
69 }
70
GenElement(const ir::ArrayExpression * array,DestructuringIterator & iterator,PandaGen * pg)71 static void GenElement(const ir::ArrayExpression *array, DestructuringIterator &iterator, PandaGen *pg)
72 {
73 for (const auto *element : array->Elements()) {
74 RegScope ers(pg);
75
76 if (element->IsRestElement()) {
77 GenRestElement(pg, element->AsRestElement(), iterator, array->IsDeclaration());
78 break;
79 }
80
81 // if a hole exist, just let the iterator step ahead
82 if (element->IsOmittedExpression()) {
83 iterator.Step();
84 continue;
85 }
86
87 const ir::Expression *init = nullptr;
88 const ir::Expression *target = element;
89
90 if (element->IsAssignmentPattern()) {
91 target = element->AsAssignmentPattern()->Left();
92 init = element->AsAssignmentPattern()->Right();
93 }
94
95 auto lref = JSLReference::Create(pg, target, array->IsDeclaration());
96 iterator.Step();
97
98 if (init != nullptr) {
99 auto *assignValue = pg->AllocLabel();
100 auto *defaultInit = pg->AllocLabel();
101 pg->BranchIfUndefined(element, defaultInit);
102 pg->LoadAccumulator(element, iterator.Result());
103 pg->Branch(element, assignValue);
104
105 pg->SetLabel(element, defaultInit);
106 init->Compile(pg);
107 pg->SetLabel(element, assignValue);
108 }
109
110 lref.SetValue();
111 }
112 }
113
GenArray(PandaGen * pg,const ir::ArrayExpression * array)114 static void GenArray(PandaGen *pg, const ir::ArrayExpression *array)
115 {
116 DestructuringIterator iterator(pg, array);
117
118 if (array->Elements().empty()) {
119 iterator.Close(false);
120 return;
121 }
122
123 TryContext tryCtx(pg);
124 const auto &labelSet = tryCtx.LabelSet();
125 pg->SetLabel(array, labelSet.TryBegin());
126
127 GenElement(array, iterator, pg);
128
129 pg->SetLabel(array, labelSet.TryEnd());
130
131 // Normal completion
132 pg->LoadAccumulator(array, iterator.Done());
133 pg->BranchIfTrue(array, labelSet.CatchEnd());
134 iterator.Close(false);
135
136 pg->Branch(array, labelSet.CatchEnd());
137
138 Label *end = pg->AllocLabel();
139 pg->SetLabel(array, labelSet.CatchBegin());
140 pg->StoreAccumulator(array, iterator.Result());
141 pg->LoadAccumulator(array, iterator.Done());
142
143 pg->BranchIfTrue(array, end);
144 pg->LoadAccumulator(array, iterator.Result());
145 iterator.Close(true);
146 pg->SetLabel(array, end);
147 pg->LoadAccumulator(array, iterator.Result());
148 pg->EmitThrow(array);
149 pg->SetLabel(array, labelSet.CatchEnd());
150 }
151
GetAssignmentTarget(const ir::Property * propExpr)152 static std::tuple<const ir::Expression *, const ir::Expression *> GetAssignmentTarget(const ir::Property *propExpr)
153 {
154 const ir::Expression *init = nullptr;
155 const ir::Expression *target = propExpr->Value();
156
157 if (target->IsAssignmentPattern()) {
158 init = target->AsAssignmentPattern()->Right();
159 target = target->AsAssignmentPattern()->Left();
160 }
161
162 return {init, target};
163 }
164
GenDefaultInitializer(PandaGen * pg,const ir::Expression * element,const ir::Expression * init)165 static void GenDefaultInitializer(PandaGen *pg, const ir::Expression *element, const ir::Expression *init)
166 {
167 if (init == nullptr) {
168 return;
169 }
170
171 RegScope rs(pg);
172 VReg loadedValue = pg->AllocReg();
173 pg->StoreAccumulator(element, loadedValue);
174
175 auto *getDefault = pg->AllocLabel();
176 auto *store = pg->AllocLabel();
177
178 pg->BranchIfUndefined(element, getDefault);
179 pg->LoadAccumulator(element, loadedValue);
180 pg->Branch(element, store);
181
182 // load default value
183 pg->SetLabel(element, getDefault);
184 init->Compile(pg);
185
186 pg->SetLabel(element, store);
187 }
188
GenObjectWithRest(PandaGen * pg,const ir::ObjectExpression * object,VReg rhs)189 static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs)
190 {
191 const auto &properties = object->Properties();
192
193 RegScope rs(pg);
194 VReg propStart = pg->NextReg();
195
196 for (const auto *element : properties) {
197 if (element->IsRestElement()) {
198 RegScope restScope(pg);
199 auto lref = JSLReference::Create(pg, element, object->IsDeclaration());
200 pg->CreateObjectWithExcludedKeys(element, rhs, propStart, properties.size() - 1);
201 lref.SetValue();
202 break;
203 }
204
205 VReg propReg = pg->AllocReg();
206
207 RegScope propScope(pg);
208
209 const ir::Property *propExpr = element->AsProperty();
210 const ir::Expression *key = propExpr->Key();
211 const auto [init, target] = GetAssignmentTarget(propExpr);
212
213 if (key->IsIdentifier()) {
214 pg->LoadAccumulatorString(key, key->AsIdentifier()->Name());
215 } else {
216 key->Compile(pg);
217 }
218
219 pg->StoreAccumulator(key, propReg);
220
221 auto lref = JSLReference::Create(pg, target, object->IsDeclaration());
222
223 pg->LoadAccumulator(element, propReg);
224 pg->LoadObjByValue(element, rhs);
225
226 GenDefaultInitializer(pg, element, init);
227
228 lref.SetValue();
229 }
230 }
231
GenObject(PandaGen * pg,const ir::ObjectExpression * object,VReg rhs)232 static void GenObject(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs)
233 {
234 const auto &properties = object->Properties();
235
236 if (properties.empty() || properties.back()->IsRestElement()) {
237 auto *notNullish = pg->AllocLabel();
238
239 pg->LoadAccumulator(object, rhs);
240 pg->BranchIfCoercible(object, notNullish);
241 pg->ThrowObjectNonCoercible(object);
242
243 pg->SetLabel(object, notNullish);
244
245 if (!properties.empty()) {
246 return GenObjectWithRest(pg, object, rhs);
247 }
248 }
249
250 for (const auto *element : properties) {
251 RegScope propScope(pg);
252
253 const ir::Property *propExpr = element->AsProperty();
254 const ir::Expression *key = propExpr->Key();
255 const auto [init, target] = GetAssignmentTarget(propExpr);
256
257 Operand propOperand = pg->ToOwnPropertyKey(key, propExpr->IsComputed());
258
259 auto lref = JSLReference::Create(pg, target, object->IsDeclaration());
260
261 if (std::holds_alternative<VReg>(propOperand)) {
262 pg->LoadAccumulator(element, std::get<VReg>(propOperand));
263 pg->LoadObjByValue(element, rhs);
264 } else {
265 pg->LoadAccumulator(element, rhs);
266 pg->LoadObjProperty(element, propOperand);
267 }
268
269 GenDefaultInitializer(pg, element, init);
270
271 lref.SetValue();
272 }
273 }
274
Compile(PandaGen * pg,const ir::Expression * pattern)275 void Destructuring::Compile(PandaGen *pg, const ir::Expression *pattern)
276 {
277 RegScope rs(pg);
278
279 VReg rhs = pg->AllocReg();
280 pg->StoreAccumulator(pattern, rhs);
281
282 if (pattern->IsArrayPattern()) {
283 GenArray(pg, pattern->AsArrayPattern());
284 } else {
285 GenObject(pg, pattern->AsObjectPattern(), rhs);
286 }
287
288 pg->LoadAccumulator(pattern, rhs);
289 }
290 } // namespace ark::es2panda::compiler
291