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
30namespace ark::es2panda::compiler {
31static 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
71static 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
114static 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
152static 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
165static 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
189static 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
232static 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
275void 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