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