1/**
2 * Copyright (c) 2021-2022 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 panda::es2panda::compiler {
31
32static void GenRestElement(PandaGen *pg, const ir::SpreadElement *restElement,
33                           const DestructuringIterator &destIterator, bool isDeclaration)
34{
35    VReg array = pg->AllocReg();
36    VReg index = pg->AllocReg();
37
38    auto *next = pg->AllocLabel();
39    auto *done = pg->AllocLabel();
40
41    DestructuringRestIterator iterator(destIterator);
42
43    // create left reference for rest element
44    LReference lref = LReference::CreateLRef(pg, restElement, isDeclaration);
45
46    // create an empty array first
47    pg->CreateEmptyArray(restElement);
48    pg->StoreAccumulator(restElement, array);
49
50    // index = 0
51    pg->LoadAccumulatorInt(restElement, 0);
52    pg->StoreAccumulator(restElement, index);
53
54    pg->SetLabel(restElement, next);
55
56    iterator.Step(done);
57    pg->StoreObjByValue(restElement, array, index);
58
59    // index++
60    pg->LoadAccumulatorInt(restElement, 1);
61    pg->Binary(restElement, lexer::TokenType::PUNCTUATOR_PLUS, index);
62    pg->StoreAccumulator(restElement, index);
63
64    pg->Branch(restElement, next);
65
66    pg->SetLabel(restElement, done);
67    pg->LoadAccumulator(restElement, array);
68
69    lref.SetValue();
70}
71
72static void GenArray(PandaGen *pg, const ir::ArrayExpression *array)
73{
74    DestructuringIterator iterator(pg, array);
75
76    if (array->Elements().empty()) {
77        iterator.Close(false);
78        return;
79    }
80
81    DestructuringIteratorContext dstrCtx(pg, iterator);
82
83    for (const auto *element : array->Elements()) {
84        RegScope ers(pg);
85
86        if (element->IsRestElement()) {
87            GenRestElement(pg, element->AsRestElement(), iterator, array->IsDeclaration());
88            break;
89        }
90
91        // if a hole exist, just let the iterator step ahead
92        if (element->IsOmittedExpression()) {
93            iterator.Step();
94            continue;
95        }
96
97        const ir::Expression *init = nullptr;
98        const ir::Expression *target = element;
99
100        if (element->IsAssignmentPattern() || element->IsAssignmentExpression()) {
101            auto *assignment = element->IsAssignmentPattern() ? element->AsAssignmentPattern() :
102                                                                element->AsAssignmentExpression();
103            target = assignment->Left();
104            init = assignment->Right();
105        }
106
107        LReference lref = LReference::CreateLRef(pg, target, array->IsDeclaration());
108        iterator.Step();
109
110        if (init) {
111            auto *assingValue = pg->AllocLabel();
112            auto *defaultInit = pg->AllocLabel();
113            pg->BranchIfStrictUndefined(element, defaultInit);
114            pg->LoadAccumulator(element, iterator.Result());
115            pg->Branch(element, assingValue);
116
117            pg->SetLabel(element, defaultInit);
118            init->Compile(pg);
119            pg->SetLabel(element, assingValue);
120        }
121
122        lref.SetValue();
123    }
124}
125
126static void GenObjectProperty(PandaGen *pg, const ir::ObjectExpression *object,
127                              const ir::Expression *element, VReg value)
128{
129    RegScope propScope(pg);
130
131    const ir::Property *propExpr = element->AsProperty();
132
133    const ir::Expression *init = nullptr;
134    const ir::Expression *key = propExpr->Key();
135    const ir::Expression *target = propExpr->Value();
136
137    if (target->IsAssignmentPattern() || target->IsAssignmentExpression()) {
138        auto *assignment = target->IsAssignmentPattern() ? target->AsAssignmentPattern() :
139                                                           target->AsAssignmentExpression();
140        init = assignment->Right();
141        target = assignment->Left();
142    }
143
144    LReference lref = LReference::CreateLRef(pg, target, object->IsDeclaration());
145
146    // load obj property from rhs, return undefined if no corresponding property exists
147    if (key->IsIdentifier() && !propExpr->IsComputed()) {
148        pg->LoadObjByName(element, value, key->AsIdentifier()->Name());
149    } else {
150        key->Compile(pg);
151        pg->LoadObjByValue(element, value);
152    }
153
154    if (init != nullptr) {
155        VReg loadedValue = pg->AllocReg();
156        pg->StoreAccumulator(element, loadedValue);
157        auto *getDefault = pg->AllocLabel();
158        auto *store = pg->AllocLabel();
159
160        pg->BranchIfStrictUndefined(element, getDefault);
161        pg->LoadAccumulator(element, loadedValue);
162        pg->Branch(element, store);
163
164        // load default value
165        pg->SetLabel(element, getDefault);
166        init->Compile(pg);
167
168        pg->SetLabel(element, store);
169    }
170
171    lref.SetValue();
172}
173
174static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs)
175{
176    const auto &properties = object->Properties();
177
178    RegScope rs(pg);
179
180    if (properties.size() == 1) {
181        auto *element = properties[0];
182        ASSERT(element->IsRestElement());
183        VReg defaultProp = pg->AllocReg();
184        LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration());
185        pg->CreateObjectWithExcludedKeys(element, rhs, defaultProp, 0);
186        lref.SetValue();
187        return;
188    }
189
190    VReg propStart = pg->NextReg();
191
192    for (const auto *element : properties) {
193        if (element->IsRestElement()) {
194            RegScope restScope(pg);
195            LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration());
196            pg->CreateObjectWithExcludedKeys(element, rhs, propStart, properties.size() - 1);
197            lref.SetValue();
198            break;
199        }
200
201        VReg propName = pg->AllocReg();
202        const ir::Expression *key = element->AsProperty()->Key();
203        if (key->IsIdentifier()) {
204            pg->LoadAccumulatorString(key, key->AsIdentifier()->Name());
205        } else {
206            key->Compile(pg);
207        }
208        pg->StoreAccumulator(element, propName);
209
210        GenObjectProperty(pg, object, element, rhs);
211    }
212}
213
214static void GenObject(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs)
215{
216    const auto &properties = object->Properties();
217
218    if (properties.empty() || properties.back()->IsRestElement()) {
219        auto *notNullish = pg->AllocLabel();
220        auto *nullish = pg->AllocLabel();
221
222        pg->LoadConst(object, Constant::JS_NULL);
223        pg->Condition(object, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, rhs, nullish);
224        pg->LoadConst(object, Constant::JS_UNDEFINED);
225        pg->Condition(object, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, rhs, nullish);
226        pg->Branch(object, notNullish);
227
228        pg->SetLabel(object, nullish);
229        pg->ThrowObjectNonCoercible(object);
230
231        pg->SetLabel(object, notNullish);
232
233        if (!properties.empty()) {
234            return GenObjectWithRest(pg, object, rhs);
235        }
236    }
237
238    for (const auto *element : properties) {
239        GenObjectProperty(pg, object, element, rhs);
240    }
241}
242
243void Destructuring::Compile(PandaGen *pg, const ir::Expression *pattern)
244{
245    RegScope rs(pg);
246
247    VReg rhs = pg->AllocReg();
248    pg->StoreAccumulator(pattern, rhs);
249
250    if (pattern->IsArrayPattern() || pattern->IsArrayExpression()) {
251        auto *arrExpr = pattern->IsArrayPattern() ? pattern->AsArrayPattern() :
252                        pattern->AsArrayExpression();
253        GenArray(pg, arrExpr);
254    } else {
255        auto *objExpr = pattern->IsObjectPattern() ? pattern->AsObjectPattern() :
256                        pattern->AsObjectExpression();
257        GenObject(pg, objExpr, rhs);
258    }
259
260    pg->LoadAccumulator(pattern, rhs);
261}
262
263}  // namespace panda::es2panda::compiler
264