• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "typeElaborationContext.h"
17 
18 #include <ir/expressions/identifier.h>
19 #include <ir/expressions/objectExpression.h>
20 #include <ir/expressions/arrayExpression.h>
21 #include <ir/expressions/literals/numberLiteral.h>
22 #include <ir/expressions/literals/stringLiteral.h>
23 #include <ir/base/property.h>
24 
25 namespace panda::es2panda::checker {
GetBestMatchingType(Type * indexType,const ir::Expression * sourceNode)26 Type *ElaborationContext::GetBestMatchingType(Type *indexType, const ir::Expression *sourceNode)
27 {
28     ArenaVector<Type *> bestMatchingType(checker_->Allocator()->Adapter());
29     Type *sourceType = sourceNode ? checker_->CheckTypeCached(sourceNode) : checker_->GlobalAnyType();
30 
31     for (auto it = potentialTypes_.begin(); it != potentialTypes_.end();) {
32         Type *currentType = checker_->GetPropertyTypeForIndexType(*it, indexType);
33 
34         if (!currentType) {
35             it = potentialTypes_.erase(it);
36             continue;
37         }
38 
39         if (!checker_->IsTypeAssignableTo(sourceType, currentType)) {
40             it = potentialTypes_.erase(it);
41         } else {
42             it++;
43         }
44 
45         bestMatchingType.push_back(currentType);
46     }
47 
48     return checker_->CreateUnionType(std::move(bestMatchingType));
49 }
50 
Start()51 void ArrayElaborationContext::Start()
52 {
53     ASSERT(sourceNode_->IsArrayExpression());
54     RemoveUnnecessaryTypes();
55 
56     for (auto *it : sourceNode_->AsArrayExpression()->Elements()) {
57         if (it->IsOmittedExpression()) {
58             index_++;
59             continue;
60         }
61 
62         util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), index_);
63 
64         Type *targetElementType = nullptr;
65 
66         if (targetType_->IsUnionType()) {
67             targetElementType = GetBestMatchingType(checker_->CreateStringLiteralType(memberIndex), it);
68         } else {
69             targetElementType =
70                 checker_->GetPropertyTypeForIndexType(targetType_, checker_->CreateStringLiteralType(memberIndex));
71         }
72 
73         if (!targetElementType) {
74             return;
75         }
76 
77         checker_->ElaborateElementwise(targetElementType, it, it->Start());
78         index_++;
79     }
80 }
81 
RemoveUnnecessaryTypes()82 void ArrayElaborationContext::RemoveUnnecessaryTypes()
83 {
84     if (!targetType_->IsUnionType()) {
85         return;
86     }
87 
88     for (auto *it : targetType_->AsUnionType()->ConstituentTypes()) {
89         if (it->IsArrayType() || it->IsObjectType()) {
90             potentialTypes_.push_back(it);
91         }
92     }
93 }
94 
Start()95 void ObjectElaborationContext::Start()
96 {
97     ASSERT(sourceNode_->IsObjectExpression());
98     RemoveUnnecessaryTypes();
99 
100     for (auto *it : sourceNode_->AsObjectExpression()->Properties()) {
101         if (it->IsSpreadElement()) {
102             continue;
103         }
104 
105         const ir::Property *prop = it->AsProperty();
106 
107         Type *propKeyType = nullptr;
108         if (prop->IsComputed()) {
109             propKeyType = checker_->CheckComputedPropertyName(prop->Key());
110         } else {
111             switch (prop->Key()->Type()) {
112                 case ir::AstNodeType::IDENTIFIER: {
113                     propKeyType = checker_->Allocator()->New<StringLiteralType>(prop->Key()->AsIdentifier()->Name());
114                     break;
115                 }
116                 case ir::AstNodeType::NUMBER_LITERAL: {
117                     propKeyType =
118                         checker_->Allocator()->New<NumberLiteralType>(prop->Key()->AsNumberLiteral()->Number());
119                     break;
120                 }
121                 case ir::AstNodeType::STRING_LITERAL: {
122                     propKeyType = checker_->Allocator()->New<StringLiteralType>(prop->Key()->AsStringLiteral()->Str());
123                     break;
124                 }
125                 default: {
126                     UNREACHABLE();
127                     break;
128                 }
129             }
130         }
131 
132         Type *targetElementType = nullptr;
133 
134         if (targetType_->IsUnionType()) {
135             targetElementType = GetBestMatchingType(propKeyType, prop->IsShorthand() ? nullptr : prop->Value());
136         } else {
137             targetElementType = checker_->GetPropertyTypeForIndexType(targetType_, propKeyType);
138         }
139 
140         if (!targetElementType) {
141             CHECK_NOT_NULL(propKeyType);
142             if (propKeyType->HasTypeFlag(TypeFlag::LITERAL)) {
143                 checker_->ThrowTypeError({"Object literal may only specify known properties, and ", propKeyType,
144                                          " does not exist in type '", targetType_, "'."}, it->Start());
145             }
146 
147             return;
148         }
149 
150         if (prop->IsShorthand()) {
151             continue;
152         }
153 
154         checker_->ElaborateElementwise(targetElementType, prop->Value(), it->Start());
155     }
156 }
157 
RemoveUnnecessaryTypes()158 void ObjectElaborationContext::RemoveUnnecessaryTypes()
159 {
160     if (!targetType_->IsUnionType()) {
161         return;
162     }
163 
164     for (auto *it : targetType_->AsUnionType()->ConstituentTypes()) {
165         if (it->IsObjectType()) {
166             potentialTypes_.push_back(it);
167         }
168     }
169 }
170 }  // namespace panda::es2panda::checker
171