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 "destructuringContext.h"
17
18 #include "util/helpers.h"
19 #include "varbinder/scope.h"
20 #include "ir/typeNode.h"
21 #include "ir/expressions/identifier.h"
22 #include "ir/expressions/objectExpression.h"
23 #include "ir/expressions/assignmentExpression.h"
24 #include "ir/expressions/arrayExpression.h"
25 #include "ir/base/spreadElement.h"
26 #include "ir/base/property.h"
27 #include "ir/expression.h"
28
29 namespace ark::es2panda::checker {
Prepare(ir::TypeNode * typeAnnotation,ir::Expression * initializer,const lexer::SourcePosition & loc)30 void DestructuringContext::Prepare(ir::TypeNode *typeAnnotation, ir::Expression *initializer,
31 const lexer::SourcePosition &loc)
32 {
33 if (typeAnnotation != nullptr) {
34 typeAnnotation->Check(checker_);
35 Type *annotationType = typeAnnotation->GetType(checker_);
36
37 if (initializer != nullptr) {
38 checker_->ElaborateElementwise(annotationType, initializer, loc);
39 }
40
41 validateTypeAnnotation_ = true;
42 inferredType_ = annotationType;
43 return;
44 }
45
46 if (initializer != nullptr) {
47 if (!initializer->IsObjectExpression()) {
48 validateObjectPatternInitializer_ = false;
49 }
50
51 inferredType_ = initializer->Check(checker_);
52 }
53 }
54
HandleDestructuringAssignment(ir::Identifier * ident,Type * inferredType,Type * defaultType)55 void DestructuringContext::HandleDestructuringAssignment(ir::Identifier *ident, Type *inferredType, Type *defaultType)
56 {
57 if (ident->Variable() == nullptr) {
58 checker_->ThrowTypeError({"Cannot find name '", ident->Name(), "'."}, ident->Start());
59 }
60
61 varbinder::Variable *variable = ident->Variable();
62 ES2PANDA_ASSERT(variable->TsType());
63
64 if (defaultType != nullptr && !checker_->IsTypeAssignableTo(defaultType, variable->TsType())) {
65 checker_->ThrowAssignmentError(defaultType, variable->TsType(), ident->Start());
66 }
67
68 if (inferredType != nullptr && !checker_->IsTypeAssignableTo(inferredType, variable->TsType())) {
69 checker_->ThrowAssignmentError(inferredType, variable->TsType(), ident->Start());
70 }
71 }
72
SetInferredTypeForVariable(varbinder::Variable * var,Type * inferredType,const lexer::SourcePosition & loc)73 void DestructuringContext::SetInferredTypeForVariable(varbinder::Variable *var, Type *inferredType,
74 const lexer::SourcePosition &loc)
75 {
76 ES2PANDA_ASSERT(var);
77
78 if (!checker_->HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
79 inferredType = checker_->GetBaseTypeOfLiteralType(inferredType);
80 }
81
82 if (var->TsType() != nullptr) {
83 checker_->IsTypeIdenticalTo(var->TsType(), inferredType, diagnostic::DIFFERENT_SUBSEQ_DECL,
84 {var->Name(), var->TsType(), inferredType}, loc);
85 return;
86 }
87
88 if (signatureInfo_ != nullptr) {
89 signatureInfo_->params.push_back(var->AsLocalVariable());
90 signatureInfo_->minArgCount++;
91 }
92
93 var->SetTsType(inferredType);
94 }
95
ValidateObjectLiteralType(ObjectType * objType,ir::ObjectExpression * objPattern)96 void DestructuringContext::ValidateObjectLiteralType(ObjectType *objType, ir::ObjectExpression *objPattern)
97 {
98 for (const auto *sourceProp : objType->Properties()) {
99 const util::StringView &sourceName = sourceProp->Name();
100 bool found = false;
101
102 for (const auto *targetProp : objPattern->Properties()) {
103 if (targetProp->IsRestElement()) {
104 continue;
105 }
106
107 ES2PANDA_ASSERT(targetProp->IsProperty());
108 const util::StringView &targetName = targetProp->AsProperty()->Key()->AsIdentifier()->Name();
109
110 if (sourceName == targetName) {
111 found = true;
112 break;
113 }
114 }
115
116 if (!found) {
117 checker_->ThrowTypeError({"Object literal may only specify known properties, and property '", sourceName,
118 "' does not exist in the pattern."},
119 objPattern->Start());
120 }
121 }
122 }
123
124 // Helper function to reduce HandleAssignmentPattern and pass code checker
HandleAssignmentPatternArrayPattern(ir::AssignmentExpression * assignmentPattern,Type * inferredType)125 void DestructuringContext::HandleAssignmentPatternArrayPattern(ir::AssignmentExpression *assignmentPattern,
126 Type *inferredType)
127 {
128 ArrayDestructuringContext nextContext = ArrayDestructuringContext(
129 {checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
130 nextContext.SetInferredType(inferredType);
131 nextContext.Start();
132 }
133
134 // Helper function to reduce HandleAssignmentPattern and pass code checker
HandleAssignmentPatternIdentifier(ir::AssignmentExpression * assignmentPattern,Type * defaultType,Type * inferredType)135 void DestructuringContext::HandleAssignmentPatternIdentifier(ir::AssignmentExpression *assignmentPattern,
136 Type *defaultType, Type *inferredType)
137 {
138 if (validateTypeAnnotation_ && !checker_->IsTypeAssignableTo(defaultType, inferredType)) {
139 checker_->ThrowAssignmentError(defaultType, inferredType, assignmentPattern->Left()->Start());
140 }
141
142 SetInferredTypeForVariable(assignmentPattern->Left()->AsIdentifier()->Variable(), inferredType,
143 assignmentPattern->Start());
144 }
145
HandleAssignmentPattern(ir::AssignmentExpression * assignmentPattern,Type * inferredType,bool validateDefault)146 void DestructuringContext::HandleAssignmentPattern(ir::AssignmentExpression *assignmentPattern, Type *inferredType,
147 bool validateDefault)
148 {
149 if (!assignmentPattern->Left()->IsArrayPattern()) {
150 checker_->RemoveStatus(CheckerStatus::FORCE_TUPLE);
151 }
152
153 Type *defaultType = assignmentPattern->Right()->Check(checker_);
154 if (!checker_->HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
155 defaultType = checker_->GetBaseTypeOfLiteralType(defaultType);
156 }
157 ES2PANDA_ASSERT(defaultType != nullptr);
158 if (validateDefault && assignmentPattern->Right()->IsObjectExpression() &&
159 assignmentPattern->Left()->IsObjectPattern()) {
160 ES2PANDA_ASSERT(defaultType != nullptr);
161 ValidateObjectLiteralType(defaultType->AsObjectType(), assignmentPattern->Left()->AsObjectPattern());
162 }
163
164 Type *initType = inferredType;
165 checker_->AddStatus(CheckerStatus::FORCE_TUPLE);
166
167 if (validateTypeAnnotation_) {
168 if (inferredType == nullptr) {
169 inferredType = checker_->GlobalUndefinedType();
170 }
171 } else {
172 if (inferredType == nullptr) {
173 inferredType = defaultType;
174 } else if (inferredType->IsUnionType()) {
175 inferredType->AsUnionType()->AddConstituentType(defaultType, checker_->Relation());
176 } else {
177 inferredType = checker_->CreateUnionType({inferredType, defaultType});
178 }
179 }
180
181 if (assignmentPattern->Left()->IsIdentifier()) {
182 if (inAssignment_) {
183 HandleDestructuringAssignment(assignmentPattern->Left()->AsIdentifier(), initType, defaultType);
184 return;
185 }
186
187 HandleAssignmentPatternIdentifier(assignmentPattern, defaultType, inferredType);
188 return;
189 }
190
191 if (assignmentPattern->Left()->IsArrayPattern()) {
192 HandleAssignmentPatternArrayPattern(assignmentPattern, inferredType);
193 return;
194 }
195
196 ES2PANDA_ASSERT(assignmentPattern->Left()->IsObjectPattern());
197 ObjectDestructuringContext nextContext = ObjectDestructuringContext(
198 {checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
199 nextContext.SetInferredType(inferredType);
200 nextContext.Start();
201 }
202
ValidateInferredType()203 void ArrayDestructuringContext::ValidateInferredType()
204 {
205 ES2PANDA_ASSERT(inferredType_ != nullptr);
206 if (!inferredType_->IsArrayType() && !inferredType_->IsUnionType() &&
207 (!inferredType_->IsObjectType() || !inferredType_->AsObjectType()->IsTupleType())) {
208 checker_->ThrowTypeError(
209 {"Type ", inferredType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
210 id_->Start());
211 }
212
213 if (inferredType_->IsUnionType()) {
214 for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
215 if (!it->IsArrayType() && (!it->IsObjectType() || !it->AsObjectType()->IsTupleType())) {
216 checker_->ThrowTypeError(
217 {"Type ", inferredType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
218 id_->Start());
219 }
220 }
221 }
222 }
223
GetTypeFromTupleByIndex(TupleType * tuple)224 Type *ArrayDestructuringContext::GetTypeFromTupleByIndex(TupleType *tuple)
225 {
226 util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), index_);
227 varbinder::Variable *memberVar = tuple->GetProperty(memberIndex, false);
228
229 if (memberVar == nullptr) {
230 return nullptr;
231 }
232
233 return memberVar->TsType();
234 }
235
NextInferredType(const util::StringView & searchName,bool throwError)236 Type *ArrayDestructuringContext::NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)
237 {
238 if (inferredType_->IsArrayType()) {
239 return inferredType_->AsArrayType()->ElementType();
240 }
241
242 if (inferredType_->IsObjectType()) {
243 ES2PANDA_ASSERT(inferredType_->AsObjectType()->IsTupleType());
244 Type *returnType = GetTypeFromTupleByIndex(inferredType_->AsObjectType()->AsTupleType());
245
246 if (returnType == nullptr && throwError) {
247 if (!validateTypeAnnotation_ && checker_->HasStatus(CheckerStatus::IN_PARAMETER)) {
248 return returnType;
249 }
250
251 checker_->ThrowTypeError({"Tuple type ", inferredType_, " of length ",
252 inferredType_->AsObjectType()->AsTupleType()->FixedLength(),
253 " has no element at index ", index_, "."},
254 id_->Start());
255 }
256
257 return returnType;
258 }
259
260 ES2PANDA_ASSERT(inferredType_->IsUnionType());
261
262 ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
263
264 for (auto *type : inferredType_->AsUnionType()->ConstituentTypes()) {
265 if (type->IsArrayType()) {
266 unionTypes.push_back(type->AsArrayType()->ElementType());
267 continue;
268 }
269
270 ES2PANDA_ASSERT(type->IsObjectType() && type->AsObjectType()->IsTupleType());
271 Type *elementType = GetTypeFromTupleByIndex(type->AsObjectType()->AsTupleType());
272
273 if (elementType == nullptr) {
274 continue;
275 }
276
277 unionTypes.push_back(elementType);
278 }
279
280 if (unionTypes.empty()) {
281 if (throwError) {
282 checker_->ThrowTypeError({"Property ", index_, " does not exist on type ", inferredType_, "."},
283 id_->Start());
284 }
285
286 return nullptr;
287 }
288
289 return checker_->CreateUnionType(std::move(unionTypes));
290 }
291
CreateArrayTypeForRest(UnionType * inferredType)292 Type *ArrayDestructuringContext::CreateArrayTypeForRest(UnionType *inferredType)
293 {
294 ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
295 uint32_t savedIdx = index_;
296
297 for (auto *it : inferredType->ConstituentTypes()) {
298 if (it->IsArrayType()) {
299 unionTypes.push_back(it->AsArrayType()->ElementType());
300 continue;
301 }
302
303 ES2PANDA_ASSERT(it->IsObjectType() && it->AsObjectType()->IsTupleType());
304 Type *tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
305
306 while (tupleElementType != nullptr) {
307 unionTypes.push_back(tupleElementType);
308 index_++;
309 tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
310 }
311
312 index_ = savedIdx;
313 }
314
315 Type *restArrayElementType = checker_->CreateUnionType(std::move(unionTypes));
316 return checker_->Allocator()->New<ArrayType>(restArrayElementType);
317 }
318
CreateTupleTypeForRest(TupleType * tuple)319 Type *ArrayDestructuringContext::CreateTupleTypeForRest(TupleType *tuple)
320 {
321 ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
322 ArenaVector<ElementFlags> elementFlags(checker_->Allocator()->Adapter());
323 uint32_t savedIdx = index_;
324 uint32_t iterIndex = 0;
325
326 Type *tupleElementType = GetTypeFromTupleByIndex(tuple);
327 ES2PANDA_ASSERT(desc != nullptr);
328 while (tupleElementType != nullptr) {
329 ElementFlags memberFlag = ElementFlags::REQUIRED;
330 util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), iterIndex);
331 auto *memberVar = varbinder::Scope::CreateVar(checker_->Allocator(), memberIndex,
332 varbinder::VariableFlags::PROPERTY, nullptr);
333 ES2PANDA_ASSERT(memberVar != nullptr);
334 memberVar->SetTsType(tupleElementType);
335 elementFlags.push_back(memberFlag);
336 desc->properties.push_back(memberVar);
337
338 index_++;
339 iterIndex++;
340
341 tupleElementType = GetTypeFromTupleByIndex(tuple);
342 }
343
344 index_ = savedIdx;
345 const checker::TupleTypeInfo tupleTypeInfo = {ElementFlags::REQUIRED, iterIndex, iterIndex, false};
346 return checker_->CreateTupleType(desc, std::move(elementFlags), tupleTypeInfo);
347 }
348
GetRestType(const lexer::SourcePosition & loc)349 Type *ArrayDestructuringContext::GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)
350 {
351 if (inferredType_->IsArrayType()) {
352 return inferredType_;
353 }
354
355 if (inferredType_->IsObjectType() && inferredType_->AsObjectType()->IsTupleType()) {
356 return CreateTupleTypeForRest(inferredType_->AsObjectType()->AsTupleType());
357 }
358
359 ES2PANDA_ASSERT(inferredType_->IsUnionType());
360 bool createArrayType = false;
361
362 for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
363 if (it->IsArrayType()) {
364 createArrayType = true;
365 break;
366 }
367 }
368
369 if (createArrayType) {
370 return CreateArrayTypeForRest(inferredType_->AsUnionType());
371 }
372
373 ArenaVector<Type *> tupleUnion(checker_->Allocator()->Adapter());
374
375 for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
376 ES2PANDA_ASSERT(it->IsObjectType() && it->AsObjectType()->IsTupleType());
377 Type *newTuple = CreateTupleTypeForRest(it->AsObjectType()->AsTupleType());
378 tupleUnion.push_back(newTuple);
379 }
380
381 return checker_->CreateUnionType(std::move(tupleUnion));
382 }
383
HandleRest(ir::SpreadElement * rest)384 void ArrayDestructuringContext::HandleRest(ir::SpreadElement *rest)
385 {
386 Type *inferredRestType = GetRestType(rest->Start());
387
388 if (rest->Argument()->IsIdentifier()) {
389 if (inAssignment_) {
390 HandleDestructuringAssignment(rest->Argument()->AsIdentifier(), inferredRestType, nullptr);
391 return;
392 }
393
394 SetInferredTypeForVariable(rest->Argument()->AsIdentifier()->Variable(), inferredRestType, rest->Start());
395 return;
396 }
397
398 if (rest->Argument()->IsArrayPattern()) {
399 ArrayDestructuringContext nextContext = ArrayDestructuringContext(
400 {checker_, rest->Argument(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
401 nextContext.SetInferredType(inferredRestType);
402 nextContext.Start();
403 return;
404 }
405
406 ES2PANDA_ASSERT(rest->Argument()->IsObjectPattern());
407 ObjectDestructuringContext nextContext =
408 ObjectDestructuringContext({checker_, rest->Argument(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
409 nextContext.SetInferredType(inferredRestType);
410 nextContext.Start();
411 }
412
ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode * node,Type * type)413 Type *ArrayDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type)
414 {
415 if (!convertTupleToArray_) {
416 return type;
417 }
418
419 if (type == nullptr) {
420 return type;
421 }
422
423 if (node->IsArrayPattern() ||
424 (node->IsAssignmentPattern() && node->AsAssignmentPattern()->Left()->IsArrayPattern())) {
425 return type;
426 }
427
428 if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
429 return type->AsObjectType()->AsTupleType()->ConvertToArrayType(checker_);
430 }
431
432 return type;
433 }
434
SetParameterType(ir::AstNode * parent,Type * type)435 static void SetParameterType(ir::AstNode *parent, Type *type)
436 {
437 parent->Iterate([type](ir::AstNode *childNode) -> void {
438 if (childNode->IsIdentifier() && childNode->AsIdentifier()->Variable() != nullptr) {
439 childNode->AsIdentifier()->Variable()->SetTsType(type);
440 return;
441 }
442
443 SetParameterType(childNode, type);
444 });
445 }
446
SetRemainingParameterTypes()447 void ArrayDestructuringContext::SetRemainingParameterTypes()
448 {
449 do {
450 auto *it = id_->AsArrayPattern()->Elements()[index_];
451 ES2PANDA_ASSERT(it);
452 SetParameterType(it, checker_->GlobalAnyType());
453 } while (++index_ != id_->AsArrayPattern()->Elements().size());
454 }
455
HandleElement(ir::Expression * element,Type * nextInferredType)456 void ArrayDestructuringContext::HandleElement(ir::Expression *element, Type *nextInferredType)
457 {
458 switch (element->Type()) {
459 case ir::AstNodeType::IDENTIFIER: {
460 if (inAssignment_) {
461 HandleDestructuringAssignment(element->AsIdentifier(), nextInferredType, nullptr);
462 break;
463 }
464
465 SetInferredTypeForVariable(element->AsIdentifier()->Variable(), nextInferredType, element->Start());
466 break;
467 }
468 case ir::AstNodeType::ARRAY_PATTERN: {
469 ArrayDestructuringContext nextContext =
470 ArrayDestructuringContext({checker_, element, inAssignment_, convertTupleToArray_, nullptr, nullptr});
471 nextContext.SetInferredType(nextInferredType);
472 nextContext.Start();
473 break;
474 }
475 case ir::AstNodeType::OBJECT_PATTERN: {
476 ObjectDestructuringContext nextContext =
477 ObjectDestructuringContext({checker_, element, inAssignment_, convertTupleToArray_, nullptr, nullptr});
478 nextContext.SetInferredType(nextInferredType);
479 nextContext.Start();
480 break;
481 }
482 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
483 HandleAssignmentPattern(element->AsAssignmentPattern(), nextInferredType, false);
484 break;
485 }
486 case ir::AstNodeType::OMITTED_EXPRESSION: {
487 break;
488 }
489 default: {
490 ES2PANDA_UNREACHABLE();
491 }
492 }
493 }
494
Start()495 void ArrayDestructuringContext::Start()
496 {
497 ES2PANDA_ASSERT(id_->IsArrayPattern());
498
499 ValidateInferredType();
500
501 util::StringView name = util::Helpers::ToStringView(checker_->Allocator(), 0);
502
503 for (auto *it : id_->AsArrayPattern()->Elements()) {
504 if (it->IsRestElement()) {
505 HandleRest(it->AsRestElement());
506 break;
507 }
508
509 Type *nextInferredType =
510 ConvertTupleTypeToArrayTypeIfNecessary(it, NextInferredType(name, !it->IsAssignmentPattern()));
511
512 if (nextInferredType == nullptr && checker_->HasStatus(CheckerStatus::IN_PARAMETER)) {
513 SetRemainingParameterTypes();
514 return;
515 }
516
517 if (convertTupleToArray_ && nextInferredType != nullptr && inferredType_->IsObjectType()) {
518 ES2PANDA_ASSERT(inferredType_->AsObjectType()->IsTupleType());
519
520 varbinder::Variable *currentTupleElement = inferredType_->AsObjectType()->Properties()[index_];
521
522 if (currentTupleElement != nullptr) {
523 currentTupleElement->SetTsType(nextInferredType);
524 }
525 }
526
527 HandleElement(it, nextInferredType);
528 index_++;
529 }
530 }
531
ValidateInferredType()532 void ObjectDestructuringContext::ValidateInferredType()
533 {
534 ES2PANDA_ASSERT(inferredType_ != nullptr);
535 // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
536 if (!inferredType_->IsObjectType()) {
537 return;
538 }
539
540 ValidateObjectLiteralType(inferredType_->AsObjectType(), id_->AsObjectPattern());
541 }
542
HandleRest(ir::SpreadElement * rest)543 void ObjectDestructuringContext::HandleRest(ir::SpreadElement *rest)
544 {
545 Type *inferredRestType = GetRestType(rest->Start());
546 ES2PANDA_ASSERT(rest->Argument()->IsIdentifier());
547
548 if (inAssignment_) {
549 HandleDestructuringAssignment(rest->Argument()->AsIdentifier(), inferredRestType, nullptr);
550 return;
551 }
552
553 SetInferredTypeForVariable(rest->Argument()->AsIdentifier()->Variable(), inferredRestType, rest->Start());
554 }
555
CreateObjectTypeForRest(ObjectType * objType)556 Type *ObjectDestructuringContext::CreateObjectTypeForRest(ObjectType *objType)
557 {
558 ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
559
560 for (auto *it : objType->AsObjectType()->Properties()) {
561 if (!it->HasFlag(varbinder::VariableFlags::INFERRED_IN_PATTERN)) {
562 auto *memberVar =
563 varbinder::Scope::CreateVar(checker_->Allocator(), it->Name(), varbinder::VariableFlags::NONE, nullptr);
564 ES2PANDA_ASSERT(memberVar != nullptr);
565 memberVar->SetTsType(it->TsType());
566 memberVar->AddFlag(it->Flags());
567 ES2PANDA_ASSERT(desc != nullptr);
568 desc->properties.push_back(memberVar);
569 }
570 }
571
572 Type *returnType = checker_->Allocator()->New<ObjectLiteralType>(desc);
573 ES2PANDA_ASSERT(returnType != nullptr);
574 returnType->AsObjectType()->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
575 return returnType;
576 }
577
GetRestType(const lexer::SourcePosition & loc)578 Type *ObjectDestructuringContext::GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)
579 {
580 if (inferredType_->IsUnionType()) {
581 ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
582
583 for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
584 if (it->IsObjectType()) {
585 unionTypes.push_back(CreateObjectTypeForRest(it->AsObjectType()));
586 continue;
587 }
588
589 checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
590 }
591
592 return checker_->CreateUnionType(std::move(unionTypes));
593 }
594
595 if (inferredType_->IsObjectType()) {
596 return CreateObjectTypeForRest(inferredType_->AsObjectType());
597 }
598
599 checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
600 }
601
ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode * node,Type * type)602 Type *ObjectDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type)
603 {
604 if (!convertTupleToArray_) {
605 return type;
606 }
607
608 if (type == nullptr) {
609 return type;
610 }
611
612 ES2PANDA_ASSERT(node->IsProperty());
613
614 ir::Property *property = node->AsProperty();
615
616 if (property->Value()->IsArrayPattern()) {
617 return type;
618 }
619
620 if (property->Value()->IsAssignmentPattern() &&
621 property->Value()->AsAssignmentPattern()->Left()->IsArrayPattern()) {
622 return type;
623 }
624
625 if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
626 return type->AsObjectType()->AsTupleType()->ConvertToArrayType(checker_);
627 }
628
629 return type;
630 }
631
NextInferredType(const util::StringView & searchName,bool throwError)632 Type *ObjectDestructuringContext::NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)
633 {
634 varbinder::Variable *prop = checker_->GetPropertyOfType(inferredType_, searchName, !throwError,
635 varbinder::VariableFlags::INFERRED_IN_PATTERN);
636
637 if (prop != nullptr) {
638 prop->AddFlag(varbinder::VariableFlags::INFERRED_IN_PATTERN);
639 return prop->TsType();
640 }
641
642 if (inferredType_->IsObjectType()) {
643 checker::ObjectType *objType = inferredType_->AsObjectType();
644
645 if (objType->StringIndexInfo() != nullptr) {
646 return objType->StringIndexInfo()->GetType();
647 }
648 }
649
650 if (throwError) {
651 checker_->ThrowTypeError({"Property ", searchName, " does not exist on type ", inferredType_, "."},
652 id_->Start());
653 }
654
655 return nullptr;
656 }
657
StartPropertyHelper(ir::Expression * it)658 void ObjectDestructuringContext::StartPropertyHelper(ir::Expression *it)
659 {
660 ir::Property *property = it->AsProperty();
661
662 Type *nextInferredType = ConvertTupleTypeToArrayTypeIfNecessary(
663 it->AsProperty(), NextInferredType(property->Key()->AsIdentifier()->Name(),
664 (!property->Value()->IsAssignmentPattern() || validateTypeAnnotation_)));
665
666 if (property->Value()->IsIdentifier()) {
667 if (inAssignment_) {
668 HandleDestructuringAssignment(property->Value()->AsIdentifier(), nextInferredType, nullptr);
669 return;
670 }
671
672 SetInferredTypeForVariable(property->Value()->AsIdentifier()->Variable(), nextInferredType, it->Start());
673 return;
674 }
675
676 if (property->Value()->IsArrayPattern()) {
677 ArrayDestructuringContext nextContext = ArrayDestructuringContext(
678 {checker_, property->Value()->AsArrayPattern(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
679 nextContext.SetInferredType(nextInferredType);
680 nextContext.Start();
681 return;
682 }
683
684 if (property->Value()->IsObjectPattern()) {
685 ObjectDestructuringContext nextContext = ObjectDestructuringContext(
686 {checker_, property->Value()->AsObjectPattern(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
687 nextContext.SetInferredType(nextInferredType);
688 nextContext.Start();
689 return;
690 }
691
692 ES2PANDA_ASSERT(property->Value()->IsAssignmentPattern());
693 HandleAssignmentPattern(property->Value()->AsAssignmentPattern(), nextInferredType, true);
694 }
695
Start()696 void ObjectDestructuringContext::Start()
697 {
698 ES2PANDA_ASSERT(id_->IsObjectPattern());
699
700 if (!id_->AsObjectPattern()->Properties().back()->IsRestElement() && validateObjectPatternInitializer_) {
701 ValidateInferredType();
702 }
703
704 for (auto *it : id_->AsObjectPattern()->Properties()) {
705 const auto itType = it->Type();
706 if (itType == ir::AstNodeType::PROPERTY) {
707 ir::Property *property = it->AsProperty();
708 if (property->IsComputed()) {
709 // NOTE: aszilagyi.
710 return;
711 }
712 StartPropertyHelper(it);
713 } else if (itType == ir::AstNodeType::REST_ELEMENT) {
714 HandleRest(it->AsRestElement());
715 } else {
716 ES2PANDA_UNREACHABLE();
717 }
718 }
719 }
720 } // namespace ark::es2panda::checker
721