• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 #include <cstddef>
18 
19 #include "checker/ETSchecker.h"
20 #include "checker/TSchecker.h"
21 #include "checker/ets/typeRelationContext.h"
22 #include "checker/ts/destructuringContext.h"
23 #include "checker/types/ets/etsTupleType.h"
24 #include "compiler/core/ETSGen.h"
25 #include "compiler/core/pandagen.h"
26 
27 namespace ark::es2panda::ir {
ArrayExpression(Tag const tag,ArrayExpression const & other,ArenaAllocator * const allocator)28 ArrayExpression::ArrayExpression([[maybe_unused]] Tag const tag, ArrayExpression const &other,
29                                  ArenaAllocator *const allocator)
30     : AnnotatedExpression(static_cast<AnnotatedExpression const &>(other), allocator),
31       decorators_(allocator->Adapter()),
32       elements_(allocator->Adapter())
33 {
34     preferredType_ = other.preferredType_;
35     isDeclaration_ = other.isDeclaration_;
36     trailingComma_ = other.trailingComma_;
37     optional_ = other.optional_;
38 
39     for (auto *element : other.elements_) {
40         elements_.emplace_back(element->Clone(allocator, this)->AsExpression());
41     }
42 
43     for (auto *decorator : other.decorators_) {
44         decorators_.emplace_back(decorator->Clone(allocator, this));
45     }
46 }
47 
Clone(ArenaAllocator * const allocator,AstNode * const parent)48 ArrayExpression *ArrayExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
49 {
50     auto *const clone = allocator->New<ArrayExpression>(Tag {}, *this, allocator);
51     ES2PANDA_ASSERT(clone);
52     if (parent != nullptr) {
53         clone->SetParent(parent);
54     }
55     clone->SetRange(Range());
56     return clone;
57 }
58 
ConvertibleToArrayPattern()59 bool ArrayExpression::ConvertibleToArrayPattern()
60 {
61     bool restFound = false;
62     bool convResult = true;
63     for (auto *it : elements_) {
64         switch (it->Type()) {
65             case AstNodeType::ARRAY_EXPRESSION: {
66                 convResult = it->AsArrayExpression()->ConvertibleToArrayPattern();
67                 break;
68             }
69             case AstNodeType::SPREAD_ELEMENT: {
70                 if (!restFound && it == elements_.back() && !trailingComma_) {
71                     convResult = it->AsSpreadElement()->ConvertibleToRest(isDeclaration_);
72                 } else {
73                     convResult = false;
74                 }
75                 restFound = true;
76                 break;
77             }
78             case AstNodeType::OBJECT_EXPRESSION: {
79                 convResult = it->AsObjectExpression()->ConvertibleToObjectPattern();
80                 break;
81             }
82             case AstNodeType::ASSIGNMENT_EXPRESSION: {
83                 convResult = it->AsAssignmentExpression()->ConvertibleToAssignmentPattern();
84                 break;
85             }
86             case AstNodeType::MEMBER_EXPRESSION:
87             case AstNodeType::OMITTED_EXPRESSION:
88             case AstNodeType::IDENTIFIER:
89             case AstNodeType::ARRAY_PATTERN:
90             case AstNodeType::OBJECT_PATTERN:
91             case AstNodeType::ASSIGNMENT_PATTERN:
92             case AstNodeType::REST_ELEMENT: {
93                 break;
94             }
95             default: {
96                 convResult = false;
97                 break;
98             }
99         }
100 
101         if (!convResult) {
102             break;
103         }
104     }
105 
106     SetType(AstNodeType::ARRAY_PATTERN);
107     return convResult;
108 }
109 
ValidateExpression()110 ValidationInfo ArrayExpression::ValidateExpression()
111 {
112     if (optional_) {
113         return {"Unexpected token '?'.", Start()};
114     }
115 
116     if (TypeAnnotation() != nullptr) {
117         return {"Unexpected token.", TypeAnnotation()->Start()};
118     }
119 
120     ValidationInfo info;
121 
122     for (auto *it : elements_) {
123         switch (it->Type()) {
124             case AstNodeType::OBJECT_EXPRESSION: {
125                 info = it->AsObjectExpression()->ValidateExpression();
126                 break;
127             }
128             case AstNodeType::ARRAY_EXPRESSION: {
129                 info = it->AsArrayExpression()->ValidateExpression();
130                 break;
131             }
132             case AstNodeType::ASSIGNMENT_EXPRESSION: {
133                 auto *assignmentExpr = it->AsAssignmentExpression();
134 
135                 if (assignmentExpr->Left()->IsArrayExpression()) {
136                     info = assignmentExpr->Left()->AsArrayExpression()->ValidateExpression();
137                 } else if (assignmentExpr->Left()->IsObjectExpression()) {
138                     info = assignmentExpr->Left()->AsObjectExpression()->ValidateExpression();
139                 }
140 
141                 break;
142             }
143             case AstNodeType::SPREAD_ELEMENT: {
144                 info = it->AsSpreadElement()->ValidateExpression();
145                 break;
146             }
147             default: {
148                 break;
149             }
150         }
151 
152         if (info.Fail()) {
153             break;
154         }
155     }
156 
157     return info;
158 }
159 
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)160 void ArrayExpression::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
161 {
162     for (auto *&it : VectorIterationGuard(decorators_)) {
163         if (auto *transformedNode = cb(it); it != transformedNode) {
164             it->SetTransformedNode(transformationName, transformedNode);
165             it = transformedNode->AsDecorator();
166         }
167     }
168 
169     for (auto *&it : VectorIterationGuard(elements_)) {
170         if (auto *transformedNode = cb(it); it != transformedNode) {
171             it->SetTransformedNode(transformationName, transformedNode);
172             it = transformedNode->AsExpression();
173         }
174     }
175 
176     if (auto *typeAnnotation = TypeAnnotation(); typeAnnotation != nullptr) {
177         if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) {
178             typeAnnotation->SetTransformedNode(transformationName, transformedNode);
179             SetTsTypeAnnotation(static_cast<TypeNode *>(transformedNode));
180         }
181     }
182 }
183 
Iterate(const NodeTraverser & cb) const184 void ArrayExpression::Iterate(const NodeTraverser &cb) const
185 {
186     for (auto *it : VectorIterationGuard(decorators_)) {
187         cb(it);
188     }
189 
190     for (auto *it : VectorIterationGuard(elements_)) {
191         cb(it);
192     }
193 
194     if (TypeAnnotation() != nullptr) {
195         cb(TypeAnnotation());
196     }
197 }
198 
Dump(ir::AstDumper * dumper) const199 void ArrayExpression::Dump(ir::AstDumper *dumper) const
200 {
201     dumper->Add({{"type", type_ == AstNodeType::ARRAY_EXPRESSION ? "ArrayExpression" : "ArrayPattern"},
202                  {"decorators", AstDumper::Optional(decorators_)},
203                  {"elements", elements_},
204                  {"typeAnnotation", AstDumper::Optional(TypeAnnotation())},
205                  {"optional", AstDumper::Optional(optional_)}});
206 }
207 
Dump(ir::SrcDumper * dumper) const208 void ArrayExpression::Dump(ir::SrcDumper *dumper) const
209 {
210     dumper->Add("[");
211     for (auto element : elements_) {
212         element->Dump(dumper);
213         if (element != elements_.back()) {
214             dumper->Add(", ");
215         }
216     }
217     dumper->Add("]");
218 }
219 
Compile(compiler::PandaGen * pg) const220 void ArrayExpression::Compile(compiler::PandaGen *pg) const
221 {
222     pg->GetAstCompiler()->Compile(this);
223 }
224 
Compile(compiler::ETSGen * const etsg) const225 void ArrayExpression::Compile(compiler::ETSGen *const etsg) const
226 {
227     etsg->GetAstCompiler()->Compile(this);
228 }
229 
Check(checker::TSChecker * checker)230 checker::Type *ArrayExpression::Check(checker::TSChecker *checker)
231 {
232     return checker->GetAnalyzer()->Check(this);
233 }
234 
CheckAssignmentPattern(Expression * it,checker::TSChecker * checker,checker::Type * elementType,bool & addOptional,checker::ElementFlags & memberFlag)235 checker::Type *CheckAssignmentPattern(Expression *it, checker::TSChecker *checker, checker::Type *elementType,
236                                       bool &addOptional, checker::ElementFlags &memberFlag)
237 {
238     auto *assignmentPattern = it->AsAssignmentPattern();
239     if (assignmentPattern->Left()->IsIdentifier()) {
240         const ir::Identifier *ident = assignmentPattern->Left()->AsIdentifier();
241         ES2PANDA_ASSERT(ident->Variable());
242         varbinder::Variable *bindingVar = ident->Variable();
243         checker::Type *initializerType = checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
244         bindingVar->SetTsType(initializerType);
245         elementType = initializerType;
246     } else if (assignmentPattern->Left()->IsArrayPattern()) {
247         auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
248         auto destructuringContext = checker::ArrayDestructuringContext(
249             {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()});
250         destructuringContext.Start();
251         elementType = destructuringContext.InferredType();
252     } else {
253         ES2PANDA_ASSERT(assignmentPattern->Left()->IsObjectPattern());
254         auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
255         auto destructuringContext = checker::ObjectDestructuringContext(
256             {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()});
257         destructuringContext.Start();
258         elementType = destructuringContext.InferredType();
259     }
260 
261     if (addOptional) {
262         memberFlag = checker::ElementFlags::OPTIONAL;
263     } else {
264         memberFlag = checker::ElementFlags::REQUIRED;
265     }
266     return elementType;
267 }
268 
CheckElementPattern(Expression * it,checker::Type * elementType,checker::TSChecker * checker,bool & addOptional,checker::ElementFlags & memberFlag)269 checker::Type *CheckElementPattern(Expression *it, checker::Type *elementType, checker::TSChecker *checker,
270                                    bool &addOptional, checker::ElementFlags &memberFlag)
271 {
272     switch (it->Type()) {
273         case ir::AstNodeType::REST_ELEMENT: {
274             elementType = checker->Allocator()->New<checker::ArrayType>(checker->GlobalAnyType());
275             memberFlag = checker::ElementFlags::REST;
276             addOptional = false;
277             return elementType;
278         }
279         case ir::AstNodeType::OBJECT_PATTERN: {
280             elementType = it->AsObjectPattern()->CheckPattern(checker);
281             memberFlag = checker::ElementFlags::REQUIRED;
282             addOptional = false;
283             return elementType;
284         }
285         case ir::AstNodeType::ARRAY_PATTERN: {
286             elementType = it->AsArrayPattern()->CheckPattern(checker);
287             memberFlag = checker::ElementFlags::REQUIRED;
288             addOptional = false;
289             return elementType;
290         }
291         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
292             return CheckAssignmentPattern(it, checker, elementType, addOptional, memberFlag);
293         }
294         case ir::AstNodeType::OMITTED_EXPRESSION: {
295             elementType = checker->GlobalAnyType();
296             memberFlag = checker::ElementFlags::REQUIRED;
297             addOptional = false;
298             return elementType;
299         }
300         case ir::AstNodeType::IDENTIFIER: {
301             const ir::Identifier *ident = it->AsIdentifier();
302             ES2PANDA_ASSERT(ident->Variable());
303             elementType = checker->GlobalAnyType();
304             ident->Variable()->SetTsType(elementType);
305             memberFlag = checker::ElementFlags::REQUIRED;
306             addOptional = false;
307             return elementType;
308         }
309         default: {
310             ES2PANDA_UNREACHABLE();
311         }
312     }
313 }
314 
CheckPattern(checker::TSChecker * checker)315 checker::Type *ArrayExpression::CheckPattern(checker::TSChecker *checker)
316 {
317     checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
318     ArenaVector<checker::ElementFlags> elementFlags(checker->Allocator()->Adapter());
319     checker::ElementFlags combinedFlags = checker::ElementFlags::NO_OPTS;
320     uint32_t minLength = 0;
321     uint32_t index = elements_.size();
322     bool addOptional = true;
323 
324     for (auto it = elements_.rbegin(); it != elements_.rend(); it++) {
325         checker::Type *elementType = nullptr;
326         checker::ElementFlags memberFlag = checker::ElementFlags::NO_OPTS;
327 
328         elementType = CheckElementPattern(*it, elementType, checker, addOptional, memberFlag);
329 
330         util::StringView memberIndex = util::Helpers::ToStringView(checker->Allocator(), index - 1);
331 
332         auto *memberVar =
333             varbinder::Scope::CreateVar(checker->Allocator(), memberIndex, varbinder::VariableFlags::PROPERTY, *it);
334 
335         if (memberFlag == checker::ElementFlags::OPTIONAL) {
336             memberVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
337         } else {
338             minLength++;
339         }
340 
341         memberVar->SetTsType(elementType);
342         elementFlags.push_back(memberFlag);
343         desc->properties.insert(desc->properties.begin(), memberVar);
344 
345         combinedFlags |= memberFlag;
346         index--;
347     }
348 
349     ES2PANDA_ASSERT(desc);
350     const checker::TupleTypeInfo tupleTypeInfo = {combinedFlags, minLength,
351                                                   static_cast<uint32_t>(desc->properties.size()), false};
352     return checker->CreateTupleType(desc, std::move(elementFlags), tupleTypeInfo);
353 }
354 
ClearPreferredType()355 void ArrayExpression::ClearPreferredType()
356 {
357     SetPreferredType(nullptr);
358     SetTsType(nullptr);
359     for (auto element : Elements()) {
360         element->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
361         element->SetTsType(nullptr);
362         element->SetAstNodeFlags(ir::AstNodeFlags::NO_OPTS);
363         if (element->IsArrayExpression()) {
364             element->AsArrayExpression()->ClearPreferredType();
365         }
366     }
367 }
368 
TrySetPreferredTypeForNestedArrayExpr(checker::ETSChecker * const checker,ArrayExpression * const nestedArrayExpr,const std::size_t idx) const369 bool ArrayExpression::TrySetPreferredTypeForNestedArrayExpr(checker::ETSChecker *const checker,
370                                                             ArrayExpression *const nestedArrayExpr,
371                                                             const std::size_t idx) const
372 {
373     auto doesArrayExprFitInTuple = [&checker, &nestedArrayExpr](const checker::Type *const possibleTupleType) {
374         return !possibleTupleType->IsETSTupleType() ||
375                checker->IsArrayExprSizeValidForTuple(nestedArrayExpr, possibleTupleType->AsETSTupleType());
376     };
377 
378     if (GetPreferredType()->IsETSTupleType()) {
379         if (idx >= preferredType_->AsETSTupleType()->GetTupleSize()) {
380             return false;
381         }
382         auto *const typeInTupleAtIdx = preferredType_->AsETSTupleType()->GetTypeAtIndex(idx);
383         nestedArrayExpr->SetPreferredType(typeInTupleAtIdx);
384 
385         return doesArrayExprFitInTuple(typeInTupleAtIdx);
386     }
387 
388     if (GetPreferredType()->IsETSArrayType()) {
389         auto *const arrayElementType = preferredType_->AsETSArrayType()->ElementType();
390         nestedArrayExpr->SetPreferredType(arrayElementType);
391 
392         return doesArrayExprFitInTuple(arrayElementType);
393     }
394 
395     if (preferredType_->IsETSResizableArrayType()) {
396         auto *const arrayElementType = preferredType_->AsETSObjectType()->TypeArguments()[0];
397         if (!doesArrayExprFitInTuple(arrayElementType)) {
398             return false;
399         }
400         nestedArrayExpr->SetPreferredType(arrayElementType);
401         return true;
402     }
403 
404     if (nestedArrayExpr->GetPreferredType() == nullptr) {
405         nestedArrayExpr->SetPreferredType(preferredType_);
406     }
407 
408     return true;
409 }
410 
Check(checker::ETSChecker * checker)411 checker::VerifiedType ArrayExpression::Check(checker::ETSChecker *checker)
412 {
413     return {this, checker->GetAnalyzer()->Check(this)};
414 }
415 
ExtractPossiblePreferredType(checker::Type * type)416 std::optional<checker::Type *> ArrayExpression::ExtractPossiblePreferredType(checker::Type *type)
417 {
418     ES2PANDA_ASSERT(type);
419     if (type->IsETSArrayType() || type->IsETSTupleType() || type->IsETSResizableArrayType()) {
420         return std::make_optional(type);
421     }
422 
423     if (type->IsETSUnionType()) {
424         for (checker::Type *const typeOfUnion : type->AsETSUnionType()->ConstituentTypes()) {
425             auto possiblePreferredType = ExtractPossiblePreferredType(typeOfUnion);
426             if (possiblePreferredType.has_value()) {
427                 return std::make_optional(possiblePreferredType.value());
428             }
429         }
430     }
431 
432     return std::nullopt;
433 }
434 
SetPreferredTypeBasedOnFuncParam(checker::ETSChecker * checker,checker::Type * param,checker::TypeRelationFlag flags)435 void ArrayExpression::SetPreferredTypeBasedOnFuncParam(checker::ETSChecker *checker, checker::Type *param,
436                                                        checker::TypeRelationFlag flags)
437 {
438     // NOTE (mmartin): This needs a complete solution
439     if (preferredType_ != nullptr) {
440         return;
441     }
442 
443     auto possiblePreferredType = ExtractPossiblePreferredType(param);
444     if (!possiblePreferredType.has_value()) {
445         return;
446     }
447 
448     param = possiblePreferredType.value();
449     if (param->IsETSTupleType()) {
450         preferredType_ = param;
451         return;
452     }
453 
454     checker::Type *elementType = nullptr;
455     if (param->IsETSArrayType()) {
456         elementType = param->AsETSArrayType()->ElementType();
457     } else {
458         elementType = param->AsETSResizableArrayType()->ElementType();
459     }
460 
461     bool isAssignable = true;
462 
463     for (auto *const elem : elements_) {
464         checker->SetPreferredTypeIfPossible(elem, elementType);
465         checker::AssignmentContext assignCtx(checker->Relation(), elem, elem->Check(checker), elementType,
466                                              elem->Start(), std::nullopt, checker::TypeRelationFlag::NO_THROW | flags);
467         isAssignable &= assignCtx.IsAssignable();
468     }
469 
470     if (isAssignable) {
471         preferredType_ = param;
472     }
473 }
474 
475 }  // namespace ark::es2panda::ir
476