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