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