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 <util/helpers.h>
19 #include <binder/scope.h>
20 #include <ir/expressions/identifier.h>
21 #include <ir/expressions/objectExpression.h>
22 #include <ir/expressions/arrayExpression.h>
23 #include <ir/expressions/literals/numberLiteral.h>
24 #include <ir/expressions/literals/stringLiteral.h>
25 #include <ir/base/spreadElement.h>
26 #include <ir/base/property.h>
27
28 namespace panda::es2panda::checker {
GetBestMatchingType(Type * indexType,const ir::Expression * sourceNode)29 Type *ElaborationContext::GetBestMatchingType(Type *indexType, const ir::Expression *sourceNode)
30 {
31 ArenaVector<Type *> bestMatchingType(checker_->Allocator()->Adapter());
32 Type *sourceType = sourceNode ? checker_->CheckTypeCached(sourceNode) : checker_->GlobalAnyType();
33
34 for (auto it = potentialTypes_.begin(); it != potentialTypes_.end();) {
35 Type *currentType = checker_->GetPropertyTypeForIndexType(*it, indexType);
36
37 if (!currentType) {
38 it = potentialTypes_.erase(it);
39 continue;
40 }
41
42 if (!checker_->IsTypeAssignableTo(sourceType, currentType)) {
43 it = potentialTypes_.erase(it);
44 } else {
45 it++;
46 }
47
48 bestMatchingType.push_back(currentType);
49 }
50
51 return checker_->CreateUnionType(std::move(bestMatchingType));
52 }
53
Start()54 void ArrayElaborationContext::Start()
55 {
56 ASSERT(sourceNode_->IsArrayExpression());
57 RemoveUnnecessaryTypes();
58
59 for (auto *it : sourceNode_->AsArrayExpression()->Elements()) {
60 if (it->IsOmittedExpression()) {
61 index_++;
62 continue;
63 }
64
65 util::StringView memberIndex = util::Helpers::ToStringView(checker_->Allocator(), index_);
66
67 Type *targetElementType = nullptr;
68
69 if (targetType_->IsUnionType()) {
70 targetElementType = GetBestMatchingType(checker_->CreateStringLiteralType(memberIndex), it);
71 } else {
72 targetElementType =
73 checker_->GetPropertyTypeForIndexType(targetType_, checker_->CreateStringLiteralType(memberIndex));
74 }
75
76 if (!targetElementType) {
77 return;
78 }
79
80 checker_->ElaborateElementwise(targetElementType, it, it->Start());
81 index_++;
82 }
83 }
84
RemoveUnnecessaryTypes()85 void ArrayElaborationContext::RemoveUnnecessaryTypes()
86 {
87 if (!targetType_->IsUnionType()) {
88 return;
89 }
90
91 for (auto *it : targetType_->AsUnionType()->ConstituentTypes()) {
92 if (it->IsArrayType() || it->IsObjectType()) {
93 potentialTypes_.push_back(it);
94 }
95 }
96 }
97
Start()98 void ObjectElaborationContext::Start()
99 {
100 ASSERT(sourceNode_->IsObjectExpression());
101 RemoveUnnecessaryTypes();
102
103 for (auto *it : sourceNode_->AsObjectExpression()->Properties()) {
104 if (it->IsSpreadElement()) {
105 continue;
106 }
107
108 const ir::Property *prop = it->AsProperty();
109
110 Type *propKeyType = nullptr;
111 if (prop->IsComputed()) {
112 propKeyType = checker_->CheckComputedPropertyName(prop->Key());
113 } else {
114 switch (prop->Key()->Type()) {
115 case ir::AstNodeType::IDENTIFIER: {
116 propKeyType = checker_->Allocator()->New<StringLiteralType>(prop->Key()->AsIdentifier()->Name());
117 break;
118 }
119 case ir::AstNodeType::NUMBER_LITERAL: {
120 propKeyType =
121 checker_->Allocator()->New<NumberLiteralType>(prop->Key()->AsNumberLiteral()->Number());
122 break;
123 }
124 case ir::AstNodeType::STRING_LITERAL: {
125 propKeyType = checker_->Allocator()->New<StringLiteralType>(prop->Key()->AsStringLiteral()->Str());
126 break;
127 }
128 default: {
129 UNREACHABLE();
130 break;
131 }
132 }
133 }
134
135 Type *targetElementType = nullptr;
136
137 if (targetType_->IsUnionType()) {
138 targetElementType = GetBestMatchingType(propKeyType, prop->IsShorthand() ? nullptr : prop->Value());
139 } else {
140 targetElementType = checker_->GetPropertyTypeForIndexType(targetType_, propKeyType);
141 }
142
143 if (!targetElementType) {
144 if (propKeyType->HasTypeFlag(TypeFlag::LITERAL)) {
145 checker_->ThrowTypeError({"Object literal may only specify known properties, and ", propKeyType,
146 " does not exist in type '", targetType_, "'."},
147 it->Start());
148 }
149
150 return;
151 }
152
153 if (prop->IsShorthand()) {
154 continue;
155 }
156
157 checker_->ElaborateElementwise(targetElementType, prop->Value(), it->Start());
158 }
159 }
160
RemoveUnnecessaryTypes()161 void ObjectElaborationContext::RemoveUnnecessaryTypes()
162 {
163 if (!targetType_->IsUnionType()) {
164 return;
165 }
166
167 for (auto *it : targetType_->AsUnionType()->ConstituentTypes()) {
168 if (it->IsObjectType()) {
169 potentialTypes_.push_back(it);
170 }
171 }
172 }
173 } // namespace panda::es2panda::checker
174