• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "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