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