• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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