• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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