• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "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 panda::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 
HandleAssignmentPattern(ir::AssignmentExpression * assignmentPattern,Type * inferredType,bool validateDefault)127 void DestructuringContext::HandleAssignmentPattern(ir::AssignmentExpression *assignmentPattern, Type *inferredType,
128                                                    bool validateDefault)
129 {
130     if (!assignmentPattern->Left()->IsArrayPattern()) {
131         checker_->RemoveStatus(CheckerStatus::FORCE_TUPLE);
132     }
133 
134     Type *defaultType = assignmentPattern->Right()->Check(checker_);
135 
136     if (!checker_->HasStatus(CheckerStatus::IN_CONST_CONTEXT)) {
137         defaultType = checker_->GetBaseTypeOfLiteralType(defaultType);
138     }
139 
140     if (validateDefault && assignmentPattern->Right()->IsObjectExpression() &&
141         assignmentPattern->Left()->IsObjectPattern()) {
142         ValidateObjectLiteralType(defaultType->AsObjectType(), assignmentPattern->Left()->AsObjectPattern());
143     }
144 
145     Type *initType = inferredType;
146     checker_->AddStatus(CheckerStatus::FORCE_TUPLE);
147 
148     if (validateTypeAnnotation_) {
149         if (inferredType == nullptr) {
150             inferredType = checker_->GlobalUndefinedType();
151         }
152     } else {
153         if (inferredType == nullptr) {
154             inferredType = defaultType;
155         } else if (inferredType->IsUnionType()) {
156             inferredType->AsUnionType()->AddConstituentType(defaultType, checker_->Relation());
157         } else {
158             inferredType = checker_->CreateUnionType({inferredType, defaultType});
159         }
160     }
161 
162     if (assignmentPattern->Left()->IsIdentifier()) {
163         if (inAssignment_) {
164             HandleDestructuringAssignment(assignmentPattern->Left()->AsIdentifier(), initType, defaultType);
165             return;
166         }
167 
168         if (validateTypeAnnotation_ && !checker_->IsTypeAssignableTo(defaultType, inferredType)) {
169             checker_->ThrowAssignmentError(defaultType, inferredType, assignmentPattern->Left()->Start());
170         }
171 
172         SetInferredTypeForVariable(assignmentPattern->Left()->AsIdentifier()->Variable(), inferredType,
173                                    assignmentPattern->Start());
174         return;
175     }
176 
177     if (assignmentPattern->Left()->IsArrayPattern()) {
178         ArrayDestructuringContext nextContext = ArrayDestructuringContext(
179             checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr);
180         nextContext.SetInferredType(inferredType);
181         nextContext.Start();
182         return;
183     }
184 
185     ASSERT(assignmentPattern->Left()->IsObjectPattern());
186     ObjectDestructuringContext nextContext = ObjectDestructuringContext(
187         checker_, assignmentPattern->Left(), inAssignment_, convertTupleToArray_, nullptr, nullptr);
188     nextContext.SetInferredType(inferredType);
189     nextContext.Start();
190 }
191 
ValidateInferredType()192 void ArrayDestructuringContext::ValidateInferredType()
193 {
194     if (!inferredType_->IsArrayType() && !inferredType_->IsUnionType() &&
195         (!inferredType_->IsObjectType() || !inferredType_->AsObjectType()->IsTupleType())) {
196         checker_->ThrowTypeError(
197             {"Type ", inferredType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
198             id_->Start());
199     }
200 
201     if (inferredType_->IsUnionType()) {
202         for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
203             if (!it->IsArrayType() && (!it->IsObjectType() || !it->AsObjectType()->IsTupleType())) {
204                 checker_->ThrowTypeError(
205                     {"Type ", inferredType_, " must have a '[Symbol.iterator]()' method that returns an iterator."},
206                     id_->Start());
207             }
208         }
209     }
210 }
211 
GetTypeFromTupleByIndex(TupleType * tuple)212 Type *ArrayDestructuringContext::GetTypeFromTupleByIndex(TupleType *tuple)
213 {
214     util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), index_);
215     varbinder::Variable *memberVar = tuple->GetProperty(memberIndex, false);
216 
217     if (memberVar == nullptr) {
218         return nullptr;
219     }
220 
221     return memberVar->TsType();
222 }
223 
NextInferredType(const util::StringView & searchName,bool throwError)224 Type *ArrayDestructuringContext::NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)
225 {
226     if (inferredType_->IsArrayType()) {
227         return inferredType_->AsArrayType()->ElementType();
228     }
229 
230     if (inferredType_->IsObjectType()) {
231         ASSERT(inferredType_->AsObjectType()->IsTupleType());
232         Type *returnType = GetTypeFromTupleByIndex(inferredType_->AsObjectType()->AsTupleType());
233 
234         if (returnType == nullptr && throwError) {
235             if (!validateTypeAnnotation_ && checker_->HasStatus(CheckerStatus::IN_PARAMETER)) {
236                 return returnType;
237             }
238 
239             checker_->ThrowTypeError({"Tuple type ", inferredType_, " of length ",
240                                       inferredType_->AsObjectType()->AsTupleType()->FixedLength(),
241                                       " has no element at index ", index_, "."},
242                                      id_->Start());
243         }
244 
245         return returnType;
246     }
247 
248     ASSERT(inferredType_->IsUnionType());
249 
250     ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
251 
252     for (auto *type : inferredType_->AsUnionType()->ConstituentTypes()) {
253         if (type->IsArrayType()) {
254             unionTypes.push_back(type->AsArrayType()->ElementType());
255             continue;
256         }
257 
258         ASSERT(type->IsObjectType() && type->AsObjectType()->IsTupleType());
259         Type *elementType = GetTypeFromTupleByIndex(type->AsObjectType()->AsTupleType());
260 
261         if (elementType == nullptr) {
262             continue;
263         }
264 
265         unionTypes.push_back(elementType);
266     }
267 
268     if (unionTypes.empty()) {
269         if (throwError) {
270             checker_->ThrowTypeError({"Property ", index_, " does not exist on type ", inferredType_, "."},
271                                      id_->Start());
272         }
273 
274         return nullptr;
275     }
276 
277     return checker_->CreateUnionType(std::move(unionTypes));
278 }
279 
CreateArrayTypeForRest(UnionType * inferredType)280 Type *ArrayDestructuringContext::CreateArrayTypeForRest(UnionType *inferredType)
281 {
282     ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
283     uint32_t savedIdx = index_;
284 
285     for (auto *it : inferredType->ConstituentTypes()) {
286         if (it->IsArrayType()) {
287             unionTypes.push_back(it->AsArrayType()->ElementType());
288             continue;
289         }
290 
291         ASSERT(it->IsObjectType() && it->AsObjectType()->IsTupleType());
292         Type *tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
293 
294         while (tupleElementType != nullptr) {
295             unionTypes.push_back(tupleElementType);
296             index_++;
297             tupleElementType = GetTypeFromTupleByIndex(it->AsObjectType()->AsTupleType());
298         }
299 
300         index_ = savedIdx;
301     }
302 
303     Type *restArrayElementType = checker_->CreateUnionType(std::move(unionTypes));
304     return checker_->Allocator()->New<ArrayType>(restArrayElementType);
305 }
306 
CreateTupleTypeForRest(TupleType * tuple)307 Type *ArrayDestructuringContext::CreateTupleTypeForRest(TupleType *tuple)
308 {
309     ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
310     ArenaVector<ElementFlags> elementFlags(checker_->Allocator()->Adapter());
311     uint32_t savedIdx = index_;
312     uint32_t iterIndex = 0;
313 
314     Type *tupleElementType = GetTypeFromTupleByIndex(tuple);
315 
316     while (tupleElementType != nullptr) {
317         ElementFlags memberFlag = ElementFlags::REQUIRED;
318         util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), iterIndex);
319         auto *memberVar = varbinder::Scope::CreateVar(checker_->Allocator(), memberIndex,
320                                                       varbinder::VariableFlags::PROPERTY, nullptr);
321         memberVar->SetTsType(tupleElementType);
322         elementFlags.push_back(memberFlag);
323         desc->properties.push_back(memberVar);
324 
325         index_++;
326         iterIndex++;
327 
328         tupleElementType = GetTypeFromTupleByIndex(tuple);
329     }
330 
331     index_ = savedIdx;
332     return checker_->CreateTupleType(desc, std::move(elementFlags), ElementFlags::REQUIRED, iterIndex, iterIndex,
333                                      false);
334 }
335 
GetRestType(const lexer::SourcePosition & loc)336 Type *ArrayDestructuringContext::GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)
337 {
338     if (inferredType_->IsArrayType()) {
339         return inferredType_;
340     }
341 
342     if (inferredType_->IsObjectType() && inferredType_->AsObjectType()->IsTupleType()) {
343         return CreateTupleTypeForRest(inferredType_->AsObjectType()->AsTupleType());
344     }
345 
346     ASSERT(inferredType_->IsUnionType());
347     bool createArrayType = false;
348 
349     for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
350         if (it->IsArrayType()) {
351             createArrayType = true;
352             break;
353         }
354     }
355 
356     if (createArrayType) {
357         return CreateArrayTypeForRest(inferredType_->AsUnionType());
358     }
359 
360     ArenaVector<Type *> tupleUnion(checker_->Allocator()->Adapter());
361 
362     for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
363         ASSERT(it->IsObjectType() && it->AsObjectType()->IsTupleType());
364         Type *newTuple = CreateTupleTypeForRest(it->AsObjectType()->AsTupleType());
365         tupleUnion.push_back(newTuple);
366     }
367 
368     return checker_->CreateUnionType(std::move(tupleUnion));
369 }
370 
HandleRest(ir::SpreadElement * rest)371 void ArrayDestructuringContext::HandleRest(ir::SpreadElement *rest)
372 {
373     Type *inferredRestType = GetRestType(rest->Start());
374 
375     if (rest->Argument()->IsIdentifier()) {
376         if (inAssignment_) {
377             HandleDestructuringAssignment(rest->Argument()->AsIdentifier(), inferredRestType, nullptr);
378             return;
379         }
380 
381         SetInferredTypeForVariable(rest->Argument()->AsIdentifier()->Variable(), inferredRestType, rest->Start());
382         return;
383     }
384 
385     if (rest->Argument()->IsArrayPattern()) {
386         ArrayDestructuringContext nextContext = ArrayDestructuringContext(checker_, rest->Argument(), inAssignment_,
387                                                                           convertTupleToArray_, nullptr, nullptr);
388         nextContext.SetInferredType(inferredRestType);
389         nextContext.Start();
390         return;
391     }
392 
393     ASSERT(rest->Argument()->IsObjectPattern());
394     ObjectDestructuringContext nextContext =
395         ObjectDestructuringContext(checker_, rest->Argument(), inAssignment_, convertTupleToArray_, nullptr, nullptr);
396     nextContext.SetInferredType(inferredRestType);
397     nextContext.Start();
398 }
399 
ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode * node,Type * type)400 Type *ArrayDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type)
401 {
402     if (!convertTupleToArray_) {
403         return type;
404     }
405 
406     if (type == nullptr) {
407         return type;
408     }
409 
410     if (node->IsArrayPattern() ||
411         (node->IsAssignmentPattern() && node->AsAssignmentPattern()->Left()->IsArrayPattern())) {
412         return type;
413     }
414 
415     if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
416         return type->AsObjectType()->AsTupleType()->ConvertToArrayType(checker_);
417     }
418 
419     return type;
420 }
421 
SetParameterType(ir::AstNode * parent,Type * type)422 static void SetParameterType(ir::AstNode *parent, Type *type)
423 {
424     parent->Iterate([type](ir::AstNode *childNode) -> void {
425         if (childNode->IsIdentifier() && childNode->AsIdentifier()->Variable() != nullptr) {
426             childNode->AsIdentifier()->Variable()->SetTsType(type);
427             return;
428         }
429 
430         SetParameterType(childNode, type);
431     });
432 }
433 
SetRemainingParameterTypes()434 void ArrayDestructuringContext::SetRemainingParameterTypes()
435 {
436     do {
437         auto *it = id_->AsArrayPattern()->Elements()[index_];
438         ASSERT(it);
439         SetParameterType(it, checker_->GlobalAnyType());
440     } while (++index_ != id_->AsArrayPattern()->Elements().size());
441 }
442 
Start()443 void ArrayDestructuringContext::Start()
444 {
445     ASSERT(id_->IsArrayPattern());
446 
447     ValidateInferredType();
448 
449     util::StringView name = util::Helpers::ToStringView(checker_->Allocator(), 0);
450 
451     for (auto *it : id_->AsArrayPattern()->Elements()) {
452         if (it->IsRestElement()) {
453             HandleRest(it->AsRestElement());
454             break;
455         }
456 
457         Type *nextInferredType =
458             ConvertTupleTypeToArrayTypeIfNecessary(it, NextInferredType(name, !it->IsAssignmentPattern()));
459 
460         if (nextInferredType == nullptr && checker_->HasStatus(CheckerStatus::IN_PARAMETER)) {
461             SetRemainingParameterTypes();
462             return;
463         }
464 
465         if (convertTupleToArray_ && nextInferredType != nullptr && inferredType_->IsObjectType()) {
466             ASSERT(inferredType_->AsObjectType()->IsTupleType());
467 
468             varbinder::Variable *currentTupleElement = inferredType_->AsObjectType()->Properties()[index_];
469 
470             if (currentTupleElement != nullptr) {
471                 currentTupleElement->SetTsType(nextInferredType);
472             }
473         }
474 
475         switch (it->Type()) {
476             case ir::AstNodeType::IDENTIFIER: {
477                 if (inAssignment_) {
478                     HandleDestructuringAssignment(it->AsIdentifier(), nextInferredType, nullptr);
479                     break;
480                 }
481 
482                 SetInferredTypeForVariable(it->AsIdentifier()->Variable(), nextInferredType, it->Start());
483                 break;
484             }
485             case ir::AstNodeType::ARRAY_PATTERN: {
486                 ArrayDestructuringContext nextContext =
487                     ArrayDestructuringContext(checker_, it, inAssignment_, convertTupleToArray_, nullptr, nullptr);
488                 nextContext.SetInferredType(nextInferredType);
489                 nextContext.Start();
490                 break;
491             }
492             case ir::AstNodeType::OBJECT_PATTERN: {
493                 ObjectDestructuringContext nextContext =
494                     ObjectDestructuringContext(checker_, it, inAssignment_, convertTupleToArray_, nullptr, nullptr);
495                 nextContext.SetInferredType(nextInferredType);
496                 nextContext.Start();
497                 break;
498             }
499             case ir::AstNodeType::ASSIGNMENT_PATTERN: {
500                 HandleAssignmentPattern(it->AsAssignmentPattern(), nextInferredType, false);
501                 break;
502             }
503             case ir::AstNodeType::OMITTED_EXPRESSION: {
504                 break;
505             }
506             default: {
507                 UNREACHABLE();
508             }
509         }
510 
511         index_++;
512     }
513 }
514 
ValidateInferredType()515 void ObjectDestructuringContext::ValidateInferredType()
516 {
517     if (!inferredType_->IsObjectType()) {
518         return;
519     }
520 
521     ValidateObjectLiteralType(inferredType_->AsObjectType(), id_->AsObjectPattern());
522 }
523 
HandleRest(ir::SpreadElement * rest)524 void ObjectDestructuringContext::HandleRest(ir::SpreadElement *rest)
525 {
526     Type *inferredRestType = GetRestType(rest->Start());
527     ASSERT(rest->Argument()->IsIdentifier());
528 
529     if (inAssignment_) {
530         HandleDestructuringAssignment(rest->Argument()->AsIdentifier(), inferredRestType, nullptr);
531         return;
532     }
533 
534     SetInferredTypeForVariable(rest->Argument()->AsIdentifier()->Variable(), inferredRestType, rest->Start());
535 }
536 
CreateObjectTypeForRest(ObjectType * objType)537 Type *ObjectDestructuringContext::CreateObjectTypeForRest(ObjectType *objType)
538 {
539     ObjectDescriptor *desc = checker_->Allocator()->New<ObjectDescriptor>(checker_->Allocator());
540 
541     for (auto *it : objType->AsObjectType()->Properties()) {
542         if (!it->HasFlag(varbinder::VariableFlags::INFERRED_IN_PATTERN)) {
543             auto *memberVar =
544                 varbinder::Scope::CreateVar(checker_->Allocator(), it->Name(), varbinder::VariableFlags::NONE, nullptr);
545             memberVar->SetTsType(it->TsType());
546             memberVar->AddFlag(it->Flags());
547             desc->properties.push_back(memberVar);
548         }
549     }
550 
551     Type *returnType = checker_->Allocator()->New<ObjectLiteralType>(desc);
552     returnType->AsObjectType()->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
553     return returnType;
554 }
555 
GetRestType(const lexer::SourcePosition & loc)556 Type *ObjectDestructuringContext::GetRestType([[maybe_unused]] const lexer::SourcePosition &loc)
557 {
558     if (inferredType_->IsUnionType()) {
559         ArenaVector<Type *> unionTypes(checker_->Allocator()->Adapter());
560 
561         for (auto *it : inferredType_->AsUnionType()->ConstituentTypes()) {
562             if (it->IsObjectType()) {
563                 unionTypes.push_back(CreateObjectTypeForRest(it->AsObjectType()));
564                 continue;
565             }
566 
567             checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
568         }
569 
570         return checker_->CreateUnionType(std::move(unionTypes));
571     }
572 
573     if (inferredType_->IsObjectType()) {
574         return CreateObjectTypeForRest(inferredType_->AsObjectType());
575     }
576 
577     checker_->ThrowTypeError("Rest types may only be created from object types.", loc);
578 }
579 
ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode * node,Type * type)580 Type *ObjectDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type)
581 {
582     if (!convertTupleToArray_) {
583         return type;
584     }
585 
586     if (type == nullptr) {
587         return type;
588     }
589 
590     ASSERT(node->IsProperty());
591 
592     ir::Property *property = node->AsProperty();
593 
594     if (property->Value()->IsArrayPattern()) {
595         return type;
596     }
597 
598     if (property->Value()->IsAssignmentPattern() &&
599         property->Value()->AsAssignmentPattern()->Left()->IsArrayPattern()) {
600         return type;
601     }
602 
603     if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
604         return type->AsObjectType()->AsTupleType()->ConvertToArrayType(checker_);
605     }
606 
607     return type;
608 }
609 
NextInferredType(const util::StringView & searchName,bool throwError)610 Type *ObjectDestructuringContext::NextInferredType([[maybe_unused]] const util::StringView &searchName, bool throwError)
611 {
612     varbinder::Variable *prop = checker_->GetPropertyOfType(inferredType_, searchName, !throwError,
613                                                             varbinder::VariableFlags::INFERRED_IN_PATTERN);
614 
615     if (prop != nullptr) {
616         prop->AddFlag(varbinder::VariableFlags::INFERRED_IN_PATTERN);
617         return prop->TsType();
618     }
619 
620     if (inferredType_->IsObjectType()) {
621         checker::ObjectType *objType = inferredType_->AsObjectType();
622 
623         if (objType->StringIndexInfo() != nullptr) {
624             return objType->StringIndexInfo()->GetType();
625         }
626     }
627 
628     if (throwError) {
629         checker_->ThrowTypeError({"Property ", searchName, " does not exist on type ", inferredType_, "."},
630                                  id_->Start());
631     }
632 
633     return nullptr;
634 }
635 
Start()636 void ObjectDestructuringContext::Start()
637 {
638     ASSERT(id_->IsObjectPattern());
639 
640     if (!id_->AsObjectPattern()->Properties().back()->IsRestElement() && validateObjectPatternInitializer_) {
641         ValidateInferredType();
642     }
643 
644     for (auto *it : id_->AsObjectPattern()->Properties()) {
645         switch (it->Type()) {
646             case ir::AstNodeType::PROPERTY: {
647                 ir::Property *property = it->AsProperty();
648 
649                 if (property->IsComputed()) {
650                     // NOTE: aszilagyi.
651                     return;
652                 }
653 
654                 Type *nextInferredType = ConvertTupleTypeToArrayTypeIfNecessary(
655                     it->AsProperty(),
656                     NextInferredType(property->Key()->AsIdentifier()->Name(),
657                                      (!property->Value()->IsAssignmentPattern() || validateTypeAnnotation_)));
658 
659                 if (property->Value()->IsIdentifier()) {
660                     if (inAssignment_) {
661                         HandleDestructuringAssignment(property->Value()->AsIdentifier(), nextInferredType, nullptr);
662                         break;
663                     }
664 
665                     SetInferredTypeForVariable(property->Value()->AsIdentifier()->Variable(), nextInferredType,
666                                                it->Start());
667                     break;
668                 }
669 
670                 if (property->Value()->IsArrayPattern()) {
671                     ArrayDestructuringContext nextContext =
672                         ArrayDestructuringContext(checker_, property->Value()->AsArrayPattern(), inAssignment_,
673                                                   convertTupleToArray_, nullptr, nullptr);
674                     nextContext.SetInferredType(nextInferredType);
675                     nextContext.Start();
676                     break;
677                 }
678 
679                 if (property->Value()->IsObjectPattern()) {
680                     ObjectDestructuringContext nextContext =
681                         ObjectDestructuringContext(checker_, property->Value()->AsObjectPattern(), inAssignment_,
682                                                    convertTupleToArray_, nullptr, nullptr);
683                     nextContext.SetInferredType(nextInferredType);
684                     nextContext.Start();
685                     break;
686                 }
687 
688                 ASSERT(property->Value()->IsAssignmentPattern());
689                 HandleAssignmentPattern(property->Value()->AsAssignmentPattern(), nextInferredType, true);
690                 break;
691             }
692             case ir::AstNodeType::REST_ELEMENT: {
693                 HandleRest(it->AsRestElement());
694                 break;
695             }
696             default: {
697                 UNREACHABLE();
698             }
699         }
700     }
701 }
702 }  // namespace panda::es2panda::checker
703