1 /**
2 * Copyright (c) 2021-2025 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 "varbinder/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 ark::es2panda::checker {
GetBestMatchingType(Type * indexType,ir::Expression * sourceNode)29 Type *ElaborationContext::GetBestMatchingType(Type *indexType, ir::Expression *sourceNode)
30 {
31 ArenaVector<Type *> bestMatchingType(checker_->Allocator()->Adapter());
32 Type *sourceType = sourceNode != nullptr ? 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 == nullptr) {
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 ES2PANDA_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 == nullptr) {
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
NonComputedPropKeyType(ir::Property * prop)98 Type *ObjectElaborationContext::NonComputedPropKeyType(ir::Property *prop)
99 {
100 switch (prop->Key()->Type()) {
101 case ir::AstNodeType::IDENTIFIER: {
102 return checker_->Allocator()->New<StringLiteralType>(prop->Key()->AsIdentifier()->Name());
103 }
104 case ir::AstNodeType::NUMBER_LITERAL: {
105 return checker_->Allocator()->New<NumberLiteralType>(prop->Key()->AsNumberLiteral()->Number().GetDouble());
106 }
107 case ir::AstNodeType::STRING_LITERAL: {
108 return checker_->Allocator()->New<StringLiteralType>(prop->Key()->AsStringLiteral()->Str());
109 }
110 default: {
111 ES2PANDA_UNREACHABLE();
112 break;
113 }
114 }
115 return nullptr;
116 }
117
Start()118 void ObjectElaborationContext::Start()
119 {
120 ES2PANDA_ASSERT(sourceNode_->IsObjectExpression());
121 RemoveUnnecessaryTypes();
122
123 for (auto *it : sourceNode_->AsObjectExpression()->Properties()) {
124 if (it->IsSpreadElement()) {
125 continue;
126 }
127
128 ir::Property *prop = it->AsProperty();
129
130 Type *propKeyType = nullptr;
131 if (prop->IsComputed()) {
132 propKeyType = checker_->CheckComputedPropertyName(prop->Key());
133 } else {
134 propKeyType = NonComputedPropKeyType(prop);
135 }
136
137 Type *targetElementType = nullptr;
138
139 if (targetType_->IsUnionType()) {
140 targetElementType = GetBestMatchingType(propKeyType, prop->IsShorthand() ? nullptr : prop->Value());
141 } else {
142 targetElementType = checker_->GetPropertyTypeForIndexType(targetType_, propKeyType);
143 }
144
145 if (targetElementType == nullptr) {
146 if (propKeyType->HasTypeFlag(TypeFlag::LITERAL)) {
147 checker_->ThrowTypeError({"Object literal may only specify known properties, and ", propKeyType,
148 " does not exist in type '", targetType_, "'."},
149 it->Start());
150 }
151
152 return;
153 }
154
155 if (prop->IsShorthand()) {
156 continue;
157 }
158
159 checker_->ElaborateElementwise(targetElementType, prop->Value(), it->Start());
160 }
161 }
162
RemoveUnnecessaryTypes()163 void ObjectElaborationContext::RemoveUnnecessaryTypes()
164 {
165 if (!targetType_->IsUnionType()) {
166 return;
167 }
168
169 for (auto *it : targetType_->AsUnionType()->ConstituentTypes()) {
170 if (it->IsObjectType()) {
171 potentialTypes_.push_back(it);
172 }
173 }
174 }
175 } // namespace ark::es2panda::checker
176