1 /**
2  * Copyright (c) 2021-2024 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     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     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,
84                                     {"Subsequent variable declaration must have the same type. Variable '", var->Name(),
85                                      "' must be of type '", var->TsType(), "', but here has type '", inferredType,
86                                      "'."},
87                                     loc);
88         return;
89     }
90 
91     if (signatureInfo_ != nullptr) {
92         signatureInfo_->params.push_back(var->AsLocalVariable());
93         signatureInfo_->minArgCount++;
94     }
95 
96     var->SetTsType(inferredType);
97 }
98 
ValidateObjectLiteralType(ObjectType *objType, ir::ObjectExpression *objPattern)99 void DestructuringContext::ValidateObjectLiteralType(ObjectType *objType, ir::ObjectExpression *objPattern)
100 {
101     for (const auto *sourceProp : objType->Properties()) {
102         const util::StringView &sourceName = sourceProp->Name();
103         bool found = false;
104 
105         for (const auto *targetProp : objPattern->Properties()) {
106             if (targetProp->IsRestElement()) {
107                 continue;
108             }
109 
110             ASSERT(targetProp->IsProperty());
111             const util::StringView &targetName = targetProp->AsProperty()->Key()->AsIdentifier()->Name();
112 
113             if (sourceName == targetName) {
114                 found = true;
115                 break;
116             }
117         }
118 
119         if (!found) {
120             checker_->ThrowTypeError({"Object literal may only specify known properties, and property '", sourceName,
121                                       "' does not exist in the pattern."},
122                                      objPattern->Start());
123         }
124     }
125 }
126 
127 // Helper function to reduce HandleAssignmentPattern and pass code checker
HandleAssignmentPatternArrayPattern(ir::AssignmentExpression *assignmentPattern, Type *inferredType)128 void DestructuringContext::HandleAssignmentPatternArrayPattern(ir::AssignmentExpression *assignmentPattern,
129                                                                Type *inferredType)
130 {
131     ArrayDestructuringContext nextContext = ArrayDestructuringContext(
132         {checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
133     nextContext.SetInferredType(inferredType);
134     nextContext.Start();
135 }
136 
137 // Helper function to reduce HandleAssignmentPattern and pass code checker
HandleAssignmentPatternIdentifier(ir::AssignmentExpression *assignmentPattern, Type *defaultType, Type *inferredType)138 void DestructuringContext::HandleAssignmentPatternIdentifier(ir::AssignmentExpression *assignmentPattern,
139                                                              Type *defaultType, Type *inferredType)
140 {
141     if (validateTypeAnnotation_ && !checker_->IsTypeAssignableTo(defaultType, inferredType)) {
142         checker_->ThrowAssignmentError(defaultType, inferredType, assignmentPattern->Left()->Start());
143     }
144 
145     SetInferredTypeForVariable(assignmentPattern->Left()->AsIdentifier()->Variable(), inferredType,
146                                assignmentPattern->Start());
147 }
148 
HandleAssignmentPattern(ir::AssignmentExpression *assignmentPattern, Type *inferredType, bool validateDefault)149 void DestructuringContext::HandleAssignmentPattern(ir::AssignmentExpression *assignmentPattern, Type *inferredType,
150                                                    bool validateDefault)
151 {
152     if (!assignmentPattern->Left()->IsArrayPattern()) {
153         checker_->RemoveStatus(CheckerStatus::FORCE_TUPLE);
154     }
155 
156     Type *defaultType = assignmentPattern->Right()->Check(checker_);
157     if (!checker_->HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
158         defaultType = checker_->GetBaseTypeOfLiteralType(defaultType);
159     }
160 
161     if (validateDefault && assignmentPattern->Right()->IsObjectExpression() &&
162         assignmentPattern->Left()->IsObjectPattern()) {
163         ValidateObjectLiteralType(defaultType->AsObjectType(), assignmentPattern->Left()->AsObjectPattern());
164     }
165 
166     Type *initType = inferredType;
167     checker_->AddStatus(CheckerStatus::FORCE_TUPLE);
168 
169     if (validateTypeAnnotation_) {
170         if (inferredType == nullptr) {
171             inferredType = checker_->GlobalUndefinedType();
172         }
173     } else {
174         if (inferredType == nullptr) {
175             inferredType = defaultType;
176         } else if (inferredType->IsUnionType()) {
177             inferredType->AsUnionType()->AddConstituentType(defaultType, checker_->Relation());
178         } else {
179             inferredType = checker_->CreateUnionType({inferredType, defaultType});
180         }
181     }
182 
183     if (assignmentPattern->Left()->IsIdentifier()) {
184         if (inAssignment_) {
185             HandleDestructuringAssignment(assignmentPattern->Left()->AsIdentifier(), initType, defaultType);
186             return;
187         }
188 
189         HandleAssignmentPatternIdentifier(assignmentPattern, defaultType, inferredType);
190         return;
191     }
192 
193     if (assignmentPattern->Left()->IsArrayPattern()) {
194         HandleAssignmentPatternArrayPattern(assignmentPattern, inferredType);
195         return;
196     }
197 
198     ASSERT(assignmentPattern->Left()->IsObjectPattern());
199     ObjectDestructuringContext nextContext = ObjectDestructuringContext(
200         {checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr});
201     nextContext.SetInferredType(inferredType);
202     nextContext.Start();
203 }
204 
ValidateInferredType()205 void ArrayDestructuringContext::ValidateInferredType()
206 {
207     if (!inferredType_->IsArrayType() && !inferredType_->IsUnionType() &&
208         (!inferredType_->IsObjectType() || !inferredType_->AsObjectType()->IsTupleType())) {
209         checker_->ThrowTypeError(
210             {"Type ", inferredType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
211             id_->Start());
212     }
213 
214     if (inferredType_->IsUnionType()) {
215         for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
216             if (!it->IsArrayType() && (!it->IsObjectType() || !it->AsObjectType()->IsTupleType())) {
217                 checker_->ThrowTypeError(
218                     {"Type ", inferredType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
219                     id_->Start());
220             }
221         }
222     }
223 }
224 
GetTypeFromTupleByIndex(TupleType *tuple)225 Type *ArrayDestructuringContext::GetTypeFromTupleByIndex(TupleType *tuple)
226 {
227     util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), index_);
228     varbinder::Variable *memberVar = tuple->GetProperty(memberIndex, false);
229 
230     if (memberVar == nullptr) {
231         return nullptr;
232     }
233 
234     return memberVar->TsType();
235 }
236 
NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)237 Type *ArrayDestructuringContext::NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)
238 {
239     if (inferredType_->IsArrayType()) {
240         return inferredType_->AsArrayType()->ElementType();
241     }
242 
243     if (inferredType_->IsObjectType()) {
244         ASSERT(inferredType_->AsObjectType()->IsTupleType());
245         Type *returnType = GetTypeFromTupleByIndex(inferredType_->AsObjectType()->AsTupleType());
246 
247         if (returnType == nullptr && throwError) {
248             if (!validateTypeAnnotation_ && checker_->HasStatus(CheckerStatus::IN_PARAMETER)) {
249                 return returnType;
250             }
251 
252             checker_->ThrowTypeError({"Tuple type ", inferredType_, " of length ",
253                                       inferredType_->AsObjectType()->AsTupleType()->FixedLength(),
254                                       " has no element at index ", index_, "."},
255                                      id_->Start());
256         }
257 
258         return returnType;
259     }
260 
261     ASSERT(inferredType_->IsUnionType());
262 
263     ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
264 
265     for (auto *type : inferredType_->AsUnionType()->ConstituentTypes()) {
266         if (type->IsArrayType()) {
267             unionTypes.push_back(type->AsArrayType()->ElementType());
268             continue;
269         }
270 
271         ASSERT(type->IsObjectType() && type->AsObjectType()->IsTupleType());
272         Type *elementType = GetTypeFromTupleByIndex(type->AsObjectType()->AsTupleType());
273 
274         if (elementType == nullptr) {
275             continue;
276         }
277 
278         unionTypes.push_back(elementType);
279     }
280 
281     if (unionTypes.empty()) {
282         if (throwError) {
283             checker_->ThrowTypeError({"Property ", index_, " does not exist on type ", inferredType_, "."},
284                                      id_->Start());
285         }
286 
287         return nullptr;
288     }
289 
290     return checker_->CreateUnionType(std::move(unionTypes));
291 }
292 
CreateArrayTypeForRest(UnionType *inferredType)293 Type *ArrayDestructuringContext::CreateArrayTypeForRest(UnionType *inferredType)
294 {
295     ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
296     uint32_t savedIdx = index_;
297 
298     for (auto *it : inferredType->ConstituentTypes()) {
299         if (it->IsArrayType()) {
300             unionTypes.push_back(it->AsArrayType()->ElementType());
301             continue;
302         }
303 
304         ASSERT(it->IsObjectType() && it->AsObjectType()->IsTupleType());
305         Type *tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
306 
307         while (tupleElementType != nullptr) {
308             unionTypes.push_back(tupleElementType);
309             index_++;
310             tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
311         }
312 
313         index_ = savedIdx;
314     }
315 
316     Type *restArrayElementType = checker_->CreateUnionType(std::move(unionTypes));
317     return checker_->Allocator()->New<ArrayType>(restArrayElementType);
318 }
319 
CreateTupleTypeForRest(TupleType *tuple)320 Type *ArrayDestructuringContext::CreateTupleTypeForRest(TupleType *tuple)
321 {
322     ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
323     ArenaVector<ElementFlags> elementFlags(checker_->Allocator()->Adapter());
324     uint32_t savedIdx = index_;
325     uint32_t iterIndex = 0;
326 
327     Type *tupleElementType = GetTypeFromTupleByIndex(tuple);
328 
329     while (tupleElementType != nullptr) {
330         ElementFlags memberFlag = ElementFlags::REQUIRED;
331         util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), iterIndex);
332         auto *memberVar = varbinder::Scope::CreateVar(checker_->Allocator(), memberIndex,
333                                                       varbinder::VariableFlags::PROPERTY, 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([[maybe_unused]] 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     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         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     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         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             UNREACHABLE();
491         }
492     }
493 }
494 
Start()495 void ArrayDestructuringContext::Start()
496 {
497     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             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     if (!inferredType_->IsObjectType()) {
535         return;
536     }
537 
538     ValidateObjectLiteralType(inferredType_->AsObjectType(), id_->AsObjectPattern());
539 }
540 
HandleRest(ir::SpreadElement *rest)541 void ObjectDestructuringContext::HandleRest(ir::SpreadElement *rest)
542 {
543     Type *inferredRestType = GetRestType(rest->Start());
544     ASSERT(rest->Argument()->IsIdentifier());
545 
546     if (inAssignment_) {
547         HandleDestructuringAssignment(rest->Argument()->AsIdentifier(), inferredRestType, nullptr);
548         return;
549     }
550 
551     SetInferredTypeForVariable(rest->Argument()->AsIdentifier()->Variable(), inferredRestType, rest->Start());
552 }
553 
CreateObjectTypeForRest(ObjectType *objType)554 Type *ObjectDestructuringContext::CreateObjectTypeForRest(ObjectType *objType)
555 {
556     ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
557 
558     for (auto *it : objType->AsObjectType()->Properties()) {
559         if (!it->HasFlag(varbinder::VariableFlags::INFERRED_IN_PATTERN)) {
560             auto *memberVar =
561                 varbinder::Scope::CreateVar(checker_->Allocator(), it->Name(), varbinder::VariableFlags::NONE, nullptr);
562             memberVar->SetTsType(it->TsType());
563             memberVar->AddFlag(it->Flags());
564             desc->properties.push_back(memberVar);
565         }
566     }
567 
568     Type *returnType = checker_->Allocator()->New<ObjectLiteralType>(desc);
569     returnType->AsObjectType()->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
570     return returnType;
571 }
572 
GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)573 Type *ObjectDestructuringContext::GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)
574 {
575     if (inferredType_->IsUnionType()) {
576         ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
577 
578         for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
579             if (it->IsObjectType()) {
580                 unionTypes.push_back(CreateObjectTypeForRest(it->AsObjectType()));
581                 continue;
582             }
583 
584             checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
585         }
586 
587         return checker_->CreateUnionType(std::move(unionTypes));
588     }
589 
590     if (inferredType_->IsObjectType()) {
591         return CreateObjectTypeForRest(inferredType_->AsObjectType());
592     }
593 
594     checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
595 }
596 
ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type)597 Type *ObjectDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type)
598 {
599     if (!convertTupleToArray_) {
600         return type;
601     }
602 
603     if (type == nullptr) {
604         return type;
605     }
606 
607     ASSERT(node->IsProperty());
608 
609     ir::Property *property = node->AsProperty();
610 
611     if (property->Value()->IsArrayPattern()) {
612         return type;
613     }
614 
615     if (property->Value()->IsAssignmentPattern() &&
616         property->Value()->AsAssignmentPattern()->Left()->IsArrayPattern()) {
617         return type;
618     }
619 
620     if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
621         return type->AsObjectType()->AsTupleType()->ConvertToArrayType(checker_);
622     }
623 
624     return type;
625 }
626 
NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)627 Type *ObjectDestructuringContext::NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)
628 {
629     varbinder::Variable *prop = checker_->GetPropertyOfType(inferredType_, searchName, !throwError,
630                                                             varbinder::VariableFlags::INFERRED_IN_PATTERN);
631 
632     if (prop != nullptr) {
633         prop->AddFlag(varbinder::VariableFlags::INFERRED_IN_PATTERN);
634         return prop->TsType();
635     }
636 
637     if (inferredType_->IsObjectType()) {
638         checker::ObjectType *objType = inferredType_->AsObjectType();
639 
640         if (objType->StringIndexInfo() != nullptr) {
641             return objType->StringIndexInfo()->GetType();
642         }
643     }
644 
645     if (throwError) {
646         checker_->ThrowTypeError({"Property ", searchName, " does not exist on type ", inferredType_, "."},
647                                  id_->Start());
648     }
649 
650     return nullptr;
651 }
652 
Start()653 void ObjectDestructuringContext::Start()
654 {
655     ASSERT(id_->IsObjectPattern());
656 
657     if (!id_->AsObjectPattern()->Properties().back()->IsRestElement() && validateObjectPatternInitializer_) {
658         ValidateInferredType();
659     }
660 
661     for (auto *it : id_->AsObjectPattern()->Properties()) {
662         switch (it->Type()) {
663             case ir::AstNodeType::PROPERTY: {
664                 ir::Property *property = it->AsProperty();
665 
666                 if (property->IsComputed()) {
667                     // NOTE: aszilagyi.
668                     return;
669                 }
670 
671                 Type *nextInferredType = ConvertTupleTypeToArrayTypeIfNecessary(
672                     it->AsProperty(),
673                     NextInferredType(property->Key()->AsIdentifier()->Name(),
674                                      (!property->Value()->IsAssignmentPattern() || validateTypeAnnotation_)));
675 
676                 if (property->Value()->IsIdentifier()) {
677                     if (inAssignment_) {
678                         HandleDestructuringAssignment(property->Value()->AsIdentifier(), nextInferredType, nullptr);
679                         break;
680                     }
681 
682                     SetInferredTypeForVariable(property->Value()->AsIdentifier()->Variable(), nextInferredType,
683                                                it->Start());
684                     break;
685                 }
686 
687                 if (property->Value()->IsArrayPattern()) {
688                     ArrayDestructuringContext nextContext =
689                         ArrayDestructuringContext({checker_, property->Value()->AsArrayPattern(), inAssignment_,
690                                                    convertTupleToArray_, nullptr, nullptr});
691                     nextContext.SetInferredType(nextInferredType);
692                     nextContext.Start();
693                     break;
694                 }
695 
696                 if (property->Value()->IsObjectPattern()) {
697                     ObjectDestructuringContext nextContext =
698                         ObjectDestructuringContext({checker_, property->Value()->AsObjectPattern(), inAssignment_,
699                                                     convertTupleToArray_, nullptr, nullptr});
700                     nextContext.SetInferredType(nextInferredType);
701                     nextContext.Start();
702                     break;
703                 }
704 
705                 ASSERT(property->Value()->IsAssignmentPattern());
706                 HandleAssignmentPattern(property->Value()->AsAssignmentPattern(), nextInferredType, true);
707                 break;
708             }
709             case ir::AstNodeType::REST_ELEMENT: {
710                 HandleRest(it->AsRestElement());
711                 break;
712             }
713             default: {
714                 UNREACHABLE();
715             }
716         }
717     }
718 }
719 }  // namespace ark::es2panda::checker
720