1/**
2 * Copyright (c) 2021 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 "arrayExpression.h"
17
18#include <util/helpers.h>
19#include <typescript/checker.h>
20#include <typescript/core/destructuringContext.h>
21#include <compiler/base/literals.h>
22#include <compiler/core/pandagen.h>
23#include <ir/astDump.h>
24#include <ir/base/spreadElement.h>
25#include <ir/expressions/assignmentExpression.h>
26#include <ir/expressions/objectExpression.h>
27#include <ir/expressions/identifier.h>
28
29namespace panda::es2panda::ir {
30
31bool ArrayExpression::ConvertibleToArrayPattern()
32{
33    bool restFound = false;
34    bool convResult = true;
35    for (auto *it : elements_) {
36        switch (it->Type()) {
37            case AstNodeType::ARRAY_EXPRESSION: {
38                convResult = it->AsArrayExpression()->ConvertibleToArrayPattern();
39                break;
40            }
41            case AstNodeType::SPREAD_ELEMENT: {
42                if (!restFound && it == elements_.back() && !trailingComma_) {
43                    convResult = it->AsSpreadElement()->ConvertibleToRest(isDeclaration_);
44                } else {
45                    convResult = false;
46                }
47                restFound = true;
48                break;
49            }
50            case AstNodeType::OBJECT_EXPRESSION: {
51                convResult = it->AsObjectExpression()->ConvertibleToObjectPattern();
52                break;
53            }
54            case AstNodeType::ASSIGNMENT_EXPRESSION: {
55                convResult = it->AsAssignmentExpression()->ConvertibleToAssignmentPattern();
56                break;
57            }
58            case AstNodeType::META_PROPERTY_EXPRESSION:
59            case AstNodeType::CHAIN_EXPRESSION:
60            case AstNodeType::SEQUENCE_EXPRESSION: {
61                convResult = false;
62                break;
63            }
64            default: {
65                break;
66            }
67        }
68
69        if (!convResult) {
70            break;
71        }
72    }
73
74    SetType(AstNodeType::ARRAY_PATTERN);
75    return convResult;
76}
77
78ValidationInfo ArrayExpression::ValidateExpression()
79{
80    ValidationInfo info;
81
82    for (auto *it : elements_) {
83        switch (it->Type()) {
84            case AstNodeType::OBJECT_EXPRESSION: {
85                info = it->AsObjectExpression()->ValidateExpression();
86                break;
87            }
88            case AstNodeType::ARRAY_EXPRESSION: {
89                info = it->AsArrayExpression()->ValidateExpression();
90                break;
91            }
92            case AstNodeType::ASSIGNMENT_EXPRESSION: {
93                auto *assignmentExpr = it->AsAssignmentExpression();
94
95                if (assignmentExpr->Left()->IsArrayExpression()) {
96                    info = assignmentExpr->Left()->AsArrayExpression()->ValidateExpression();
97                } else if (assignmentExpr->Left()->IsObjectExpression()) {
98                    info = assignmentExpr->Left()->AsObjectExpression()->ValidateExpression();
99                }
100
101                break;
102            }
103            case AstNodeType::SPREAD_ELEMENT: {
104                info = it->AsSpreadElement()->ValidateExpression();
105                break;
106            }
107            default: {
108                break;
109            }
110        }
111
112        if (info.Fail()) {
113            break;
114        }
115    }
116
117    return info;
118}
119
120void ArrayExpression::Iterate(const NodeTraverser &cb) const
121{
122    for (auto *it : elements_) {
123        cb(it);
124    }
125
126    if (typeAnnotation_) {
127        cb(typeAnnotation_);
128    }
129}
130
131void ArrayExpression::Dump(ir::AstDumper *dumper) const
132{
133    dumper->Add({{"type", type_ == AstNodeType::ARRAY_EXPRESSION ? "ArrayExpression" : "ArrayPattern"},
134                 {"elements", elements_},
135                 {"typeAnnotation", AstDumper::Optional(typeAnnotation_)},
136                 {"optional", AstDumper::Optional(optional_)}});
137}
138
139void ArrayExpression::Compile(compiler::PandaGen *pg) const
140{
141    compiler::RegScope rs(pg);
142    compiler::VReg arrayObj = pg->AllocReg();
143
144    pg->CreateArray(this, elements_, arrayObj);
145}
146
147void GetSpreadElementType(checker::Checker *checker, checker::Type *spreadType,
148                          ArenaVector<checker::Type *> &elementTypes, const lexer::SourcePosition &loc)
149{
150    bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
151
152    if (spreadType->IsObjectType() && spreadType->AsObjectType()->IsTupleType()) {
153        ArenaVector<checker::Type *> tupleElementTypes(checker->Allocator()->Adapter());
154        checker::TupleType *spreadTuple = spreadType->AsObjectType()->AsTupleType();
155
156        for (auto *it : spreadTuple->Properties()) {
157            if (inConstContext) {
158                elementTypes.push_back(it->TsType());
159                continue;
160            }
161
162            tupleElementTypes.push_back(it->TsType());
163        }
164
165        if (inConstContext) {
166            return;
167        }
168
169        elementTypes.push_back(checker->CreateUnionType(std::move(tupleElementTypes)));
170        return;
171    }
172
173    // TODO(aszilagyi) handle const context cases in case of union spread type
174    if (spreadType->IsUnionType()) {
175        ArenaVector<checker::Type *> spreadTypes(checker->Allocator()->Adapter());
176        bool throwError = false;
177
178        for (auto *type : spreadType->AsUnionType()->ConstituentTypes()) {
179            if (type->IsArrayType()) {
180                spreadTypes.push_back(type->AsArrayType()->ElementType());
181                continue;
182            }
183
184            if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
185                checker::TupleType *tuple = type->AsObjectType()->AsTupleType();
186
187                for (auto *it : tuple->Properties()) {
188                    spreadTypes.push_back(it->TsType());
189                }
190
191                continue;
192            }
193
194            throwError = true;
195            break;
196        }
197
198        if (!throwError) {
199            elementTypes.push_back(checker->CreateUnionType(std::move(spreadTypes)));
200            return;
201        }
202    }
203
204    checker->ThrowTypeError(
205        {"Type '", spreadType, "' must have a '[Symbol.iterator]()' method that returns an iterator."}, loc);
206}
207
208checker::Type *ArrayExpression::Check(checker::Checker *checker) const
209{
210    ArenaVector<checker::Type *> elementTypes(checker->Allocator()->Adapter());
211    ArenaVector<checker::ElementFlags> elementFlags(checker->Allocator()->Adapter());
212    bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
213    bool createTuple = checker->HasStatus(checker::CheckerStatus::FORCE_TUPLE);
214
215    for (auto *it : elements_) {
216        if (it->IsSpreadElement()) {
217            checker::Type *spreadType = it->AsSpreadElement()->Argument()->Check(checker);
218
219            if (spreadType->IsArrayType()) {
220                elementTypes.push_back(inConstContext ? spreadType : spreadType->AsArrayType()->ElementType());
221                elementFlags.push_back(checker::ElementFlags::VARIADIC);
222                continue;
223            }
224
225            GetSpreadElementType(checker, spreadType, elementTypes, it->Start());
226            elementFlags.push_back(checker::ElementFlags::REST);
227            continue;
228        }
229
230        checker::Type *elementType = it->Check(checker);
231
232        if (!inConstContext) {
233            elementType = checker->GetBaseTypeOfLiteralType(elementType);
234        }
235
236        elementFlags.push_back(checker::ElementFlags::REQUIRED);
237        elementTypes.push_back(elementType);
238    }
239
240    if (inConstContext || createTuple) {
241        checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
242        CHECK_NOT_NULL(desc);
243        uint32_t index = 0;
244
245        for (auto it = elementTypes.begin(); it != elementTypes.end(); it++, index++) {
246            util::StringView memberIndex = util::Helpers::ToStringView(checker->Allocator(), index);
247            binder::LocalVariable *tupleMember =
248                binder::Scope::CreateVar(checker->Allocator(), memberIndex, binder::VariableFlags::PROPERTY, nullptr);
249            CHECK_NOT_NULL(tupleMember);
250            if (inConstContext) {
251                tupleMember->AddFlag(binder::VariableFlags::READONLY);
252            }
253
254            tupleMember->SetTsType(*it);
255            desc->properties.push_back(tupleMember);
256        }
257
258        return checker->CreateTupleType(desc, std::move(elementFlags), checker::ElementFlags::REQUIRED, index, index,
259                                        inConstContext);
260    }
261
262    checker::Type *arrayElementType = nullptr;
263    if (elementTypes.empty()) {
264        arrayElementType = checker->GlobalAnyType();
265    } else {
266        arrayElementType = checker->CreateUnionType(std::move(elementTypes));
267    }
268
269    return checker->Allocator()->New<checker::ArrayType>(arrayElementType);
270}
271
272checker::Type *ArrayExpression::CheckPattern(checker::Checker *checker) const
273{
274    checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
275    CHECK_NOT_NULL(desc);
276    ArenaVector<checker::ElementFlags> elementFlags(checker->Allocator()->Adapter());
277    checker::ElementFlags combinedFlags = checker::ElementFlags::NO_OPTS;
278    uint32_t minLength = 0;
279    uint32_t index = elements_.size();
280    bool addOptional = true;
281
282    for (auto it = elements_.rbegin(); it != elements_.rend(); it++) {
283        checker::Type *elementType = nullptr;
284        checker::ElementFlags memberFlag = checker::ElementFlags::NO_OPTS;
285
286        switch ((*it)->Type()) {
287            case ir::AstNodeType::REST_ELEMENT: {
288                elementType = checker->Allocator()->New<checker::ArrayType>(checker->GlobalAnyType());
289                memberFlag = checker::ElementFlags::REST;
290                addOptional = false;
291                break;
292            }
293            case ir::AstNodeType::OBJECT_PATTERN: {
294                elementType = (*it)->AsObjectPattern()->CheckPattern(checker);
295                memberFlag = checker::ElementFlags::REQUIRED;
296                addOptional = false;
297                break;
298            }
299            case ir::AstNodeType::ARRAY_PATTERN: {
300                elementType = (*it)->AsArrayPattern()->CheckPattern(checker);
301                memberFlag = checker::ElementFlags::REQUIRED;
302                addOptional = false;
303                break;
304            }
305            case ir::AstNodeType::ASSIGNMENT_PATTERN: {
306                const ir::AssignmentExpression *assignmentPattern = (*it)->AsAssignmentPattern();
307
308                if (assignmentPattern->Left()->IsIdentifier()) {
309                    const ir::Identifier *ident = assignmentPattern->Left()->AsIdentifier();
310                    ASSERT(ident->Variable());
311                    binder::Variable *bindingVar = ident->Variable();
312                    checker::Type *initializerType =
313                        checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
314                    bindingVar->SetTsType(initializerType);
315                    elementType = initializerType;
316                } else if (assignmentPattern->Left()->IsArrayPattern()) {
317                    auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
318                    auto destructuringContext =
319                        checker::ArrayDestructuringContext(checker, assignmentPattern->Left()->AsArrayPattern(), false,
320                                                           true, nullptr, assignmentPattern->Right());
321                    destructuringContext.Start();
322                    elementType = destructuringContext.InferedType();
323                } else {
324                    ASSERT(assignmentPattern->Left()->IsObjectPattern());
325                    auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
326                    auto destructuringContext =
327                        checker::ObjectDestructuringContext(checker, assignmentPattern->Left()->AsObjectPattern(),
328                                                            false, true, nullptr, assignmentPattern->Right());
329                    destructuringContext.Start();
330                    elementType = destructuringContext.InferedType();
331                }
332
333                if (addOptional) {
334                    memberFlag = checker::ElementFlags::OPTIONAL;
335                } else {
336                    memberFlag = checker::ElementFlags::REQUIRED;
337                }
338
339                break;
340            }
341            case ir::AstNodeType::OMITTED_EXPRESSION: {
342                elementType = checker->GlobalAnyType();
343                memberFlag = checker::ElementFlags::REQUIRED;
344                addOptional = false;
345                break;
346            }
347            case ir::AstNodeType::IDENTIFIER: {
348                const ir::Identifier *ident = (*it)->AsIdentifier();
349                ASSERT(ident->Variable());
350                elementType = checker->GlobalAnyType();
351                ident->Variable()->SetTsType(elementType);
352                memberFlag = checker::ElementFlags::REQUIRED;
353                addOptional = false;
354                break;
355            }
356            default: {
357                UNREACHABLE();
358            }
359        }
360
361        util::StringView memberIndex = util::Helpers::ToStringView(checker->Allocator(), index - 1);
362
363        auto *memberVar =
364            binder::Scope::CreateVar(checker->Allocator(), memberIndex, binder::VariableFlags::PROPERTY, *it);
365
366        if (memberFlag == checker::ElementFlags::OPTIONAL) {
367            memberVar->AddFlag(binder::VariableFlags::OPTIONAL);
368        } else {
369            minLength++;
370        }
371
372        memberVar->SetTsType(elementType);
373        elementFlags.push_back(memberFlag);
374        desc->properties.insert(desc->properties.begin(), memberVar);
375
376        combinedFlags |= memberFlag;
377        index--;
378    }
379
380    return checker->CreateTupleType(desc, std::move(elementFlags), combinedFlags, minLength, desc->properties.size(),
381                                    false);
382}
383
384void ArrayExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
385{
386    for (auto iter = elements_.begin(); iter != elements_.end(); iter++) {
387        *iter = std::get<ir::AstNode *>(cb(*iter))->AsExpression();
388    }
389}
390
391}  // namespace panda::es2panda::ir
392