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