1 /*
2 * Copyright (c) 2021-2024 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 "etsEnumType.h"
17
18 #include "checker/ETSchecker.h"
19 #include "checker/ets/conversion.h"
20 #include "ir/expressions/identifier.h"
21 #include "ir/expressions/literals/numberLiteral.h"
22 #include "ir/expressions/memberExpression.h"
23 #include "ir/ts/tsEnumMember.h"
24
25 namespace panda::es2panda::checker {
ETSEnumInterface(const ir::TSEnumDeclaration * const enumDecl,UType ordinal,const ir::TSEnumMember * const member,TypeFlag const typeFlag)26 ETSEnumInterface::ETSEnumInterface(const ir::TSEnumDeclaration *const enumDecl, UType ordinal,
27 const ir::TSEnumMember *const member, TypeFlag const typeFlag)
28 : Type(typeFlag), decl_(enumDecl), ordinal_ {ordinal}, member_(member)
29 {
30 }
31
AssignmentSource(TypeRelation * const relation,Type * const target)32 bool ETSEnumInterface::AssignmentSource(TypeRelation *const relation, Type *const target)
33 {
34 auto const result = target->IsETSEnumType()
35 ? IsSameEnumType(target->AsETSEnumType())
36 : (target->IsETSStringEnumType() ? IsSameEnumType(target->AsETSStringEnumType()) : false);
37 relation->Result(result);
38 return relation->IsTrue();
39 }
40
AssignmentTarget(TypeRelation * const relation,Type * const source)41 void ETSEnumInterface::AssignmentTarget(TypeRelation *const relation, Type *const source)
42 {
43 auto const result = source->IsETSEnumType()
44 ? IsSameEnumType(source->AsETSEnumType())
45 : (source->IsETSStringEnumType() ? IsSameEnumType(source->AsETSStringEnumType()) : false);
46 relation->Result(result);
47 }
48
Cast(TypeRelation * relation,Type * target)49 void ETSEnumInterface::Cast(TypeRelation *relation, Type *target)
50 {
51 if (target->IsIntType()) {
52 relation->Result(true);
53 return;
54 }
55
56 conversion::Forbidden(relation);
57 }
58
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)59 Type *ETSEnumInterface::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation,
60 [[maybe_unused]] GlobalTypesHolder *globalTypes)
61 {
62 return this;
63 }
64
Identical(TypeRelation * const relation,Type * const other)65 void ETSEnumInterface::Identical(TypeRelation *const relation, Type *const other)
66 {
67 ETSEnumInterface const *const otherEnumType = [other]() -> ETSEnumInterface const * {
68 if (other->IsETSEnumType()) {
69 return other->AsETSEnumType();
70 }
71 if (other->IsETSStringEnumType()) {
72 return other->AsETSStringEnumType();
73 }
74 return nullptr;
75 }();
76
77 relation->Result(otherEnumType != nullptr && IsSameEnumType(otherEnumType) && member_ == otherEnumType->member_);
78 }
79
ToAssemblerType(std::stringstream & ss) const80 void ETSEnumInterface::ToAssemblerType(std::stringstream &ss) const
81 {
82 ToAssemblerTypeImpl<UType>(ss);
83 }
84
ToDebugInfoType(std::stringstream & ss) const85 void ETSEnumInterface::ToDebugInfoType(std::stringstream &ss) const
86 {
87 ToDebugInfoTypeImpl<UType>(ss);
88 }
89
ToString(std::stringstream & ss) const90 void ETSEnumInterface::ToString(std::stringstream &ss) const
91 {
92 ss << decl_->Key()->Name();
93 }
94
GetDecl() const95 const ir::TSEnumDeclaration *ETSEnumInterface::GetDecl() const noexcept
96 {
97 return decl_;
98 }
99
GetMembers() const100 const ArenaVector<ir::AstNode *> &ETSEnumInterface::GetMembers() const noexcept
101 {
102 return decl_->Members();
103 }
104
GetMemberVar() const105 varbinder::LocalVariable *ETSEnumInterface::GetMemberVar() const noexcept
106 {
107 ASSERT(IsLiteralType());
108 return member_->Key()->AsIdentifier()->Variable()->AsLocalVariable();
109 }
110
GetName() const111 util::StringView ETSEnumInterface::GetName() const noexcept
112 {
113 return decl_->Key()->Name();
114 }
115
GetOrdinal() const116 ETSEnumInterface::UType ETSEnumInterface::GetOrdinal() const noexcept
117 {
118 ASSERT(IsLiteralType());
119 return ordinal_;
120 }
121
LookupConstant(ETSChecker * const checker,const ir::Expression * const expression,const ir::Identifier * const prop) const122 ETSEnumInterface *ETSEnumInterface::LookupConstant(ETSChecker *const checker, const ir::Expression *const expression,
123 const ir::Identifier *const prop) const
124 {
125 if (!IsEnumTypeExpression(expression)) {
126 checker->ThrowTypeError({"Enum constant do not have property '", prop->Name(), "'"}, prop->Start());
127 }
128
129 auto *const member = FindMember(prop->Name());
130 if (member == nullptr) {
131 checker->ThrowTypeError({"No enum constant named '", prop->Name(), "' in enum '", this, "'"}, prop->Start());
132 }
133
134 auto *const enumInterface =
135 [enumType = member->Key()->AsIdentifier()->Variable()->TsType()]() -> checker::ETSEnumInterface * {
136 if (enumType->IsETSEnumType()) {
137 return enumType->AsETSEnumType();
138 }
139 return enumType->AsETSStringEnumType();
140 }();
141
142 ASSERT(enumInterface->IsLiteralType());
143 return enumInterface;
144 }
145
LookupMethod(ETSChecker * checker,const ir::Expression * const expression,const ir::Identifier * const prop) const146 ETSFunctionType *ETSEnumInterface::LookupMethod(ETSChecker *checker, const ir::Expression *const expression,
147 const ir::Identifier *const prop) const
148 {
149 if (IsEnumTypeExpression(expression)) {
150 return LookupTypeMethod(checker, prop);
151 }
152
153 ASSERT(IsEnumInstanceExpression(expression));
154 return LookupConstantMethod(checker, prop);
155 }
156
IsSameEnumType(const ETSEnumInterface * const other) const157 bool ETSEnumInterface::IsSameEnumType(const ETSEnumInterface *const other) const noexcept
158 {
159 return other->decl_ == decl_;
160 }
161
IsSameEnumLiteralType(const ETSEnumInterface * const other) const162 bool ETSEnumInterface::IsSameEnumLiteralType(const ETSEnumInterface *const other) const noexcept
163 {
164 ASSERT(IsLiteralType() && IsSameEnumType(other));
165 return member_ == other->member_;
166 }
167
IsEnumInstanceExpression(const ir::Expression * const expression) const168 bool ETSEnumInterface::IsEnumInstanceExpression(const ir::Expression *const expression) const noexcept
169 {
170 [[maybe_unused]] ETSEnumInterface const *const enumInterface =
171 [enumType = expression->TsType()]() -> ETSEnumInterface const * {
172 if (enumType->IsETSEnumType()) {
173 return enumType->AsETSEnumType();
174 }
175 if (enumType->IsETSStringEnumType()) {
176 return enumType->AsETSStringEnumType();
177 }
178 return nullptr;
179 }();
180
181 ASSERT(IsSameEnumType(enumInterface));
182
183 return IsEnumLiteralExpression(expression) || !IsEnumTypeExpression(expression);
184 }
185
IsEnumLiteralExpression(const ir::Expression * const expression) const186 bool ETSEnumInterface::IsEnumLiteralExpression(const ir::Expression *const expression) const noexcept
187 {
188 [[maybe_unused]] ETSEnumInterface const *const enumInterface =
189 [enumType = expression->TsType()]() -> ETSEnumInterface const * {
190 if (enumType->IsETSEnumType()) {
191 return enumType->AsETSEnumType();
192 }
193 if (enumType->IsETSStringEnumType()) {
194 return enumType->AsETSStringEnumType();
195 }
196 return nullptr;
197 }();
198
199 ASSERT(IsSameEnumType(enumInterface));
200
201 if (expression->IsMemberExpression()) {
202 const auto *const memberExpr = expression->AsMemberExpression();
203 return memberExpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS &&
204 IsEnumTypeExpression(memberExpr->Object());
205 }
206
207 return false;
208 }
209
IsEnumTypeExpression(const ir::Expression * const expression) const210 bool ETSEnumInterface::IsEnumTypeExpression(const ir::Expression *const expression) const noexcept
211 {
212 [[maybe_unused]] ETSEnumInterface const *const enumInterface =
213 [enumType = expression->TsType()]() -> ETSEnumInterface const * {
214 if (enumType->IsETSEnumType()) {
215 return enumType->AsETSEnumType();
216 }
217 if (enumType->IsETSStringEnumType()) {
218 return enumType->AsETSStringEnumType();
219 }
220 return nullptr;
221 }();
222
223 ASSERT(IsSameEnumType(enumInterface));
224
225 if (expression->IsCallExpression()) {
226 return false;
227 }
228
229 const auto *const localVar = [expression]() -> const varbinder::LocalVariable * {
230 if (expression->IsMemberExpression()) {
231 const auto *const memberExpr = expression->AsMemberExpression();
232 return memberExpr->PropVar() != nullptr
233 ? memberExpr->PropVar()
234 : memberExpr->Object()->AsIdentifier()->Variable()->AsLocalVariable();
235 }
236 return expression->AsIdentifier()->Variable()->AsLocalVariable();
237 }();
238
239 ASSERT(localVar->Declaration() == decl_->Key()->AsIdentifier()->Variable()->Declaration() ||
240 !localVar->HasFlag(varbinder::VariableFlags::ENUM_LITERAL));
241 return localVar->HasFlag(varbinder::VariableFlags::ENUM_LITERAL);
242 }
243
FromIntMethod() const244 ETSEnumInterface::Method ETSEnumInterface::FromIntMethod() const noexcept
245 {
246 ASSERT(fromIntMethod_.globalSignature != nullptr && fromIntMethod_.memberProxyType == nullptr);
247 return fromIntMethod_;
248 }
249
GetValueMethod() const250 ETSEnumInterface::Method ETSEnumInterface::GetValueMethod() const noexcept
251 {
252 ASSERT(getValueMethod_.globalSignature != nullptr && getValueMethod_.memberProxyType != nullptr);
253 return getValueMethod_;
254 }
255
GetNameMethod() const256 ETSEnumInterface::Method ETSEnumInterface::GetNameMethod() const noexcept
257 {
258 ASSERT(getNameMethod_.globalSignature != nullptr && getNameMethod_.memberProxyType != nullptr);
259 return getNameMethod_;
260 }
261
ToStringMethod() const262 ETSEnumInterface::Method ETSEnumInterface::ToStringMethod() const noexcept
263 {
264 ASSERT(toStringMethod_.globalSignature != nullptr && toStringMethod_.memberProxyType != nullptr);
265 return toStringMethod_;
266 }
267
ValueOfMethod() const268 ETSEnumInterface::Method ETSEnumInterface::ValueOfMethod() const noexcept
269 {
270 ASSERT(valueOfMethod_.globalSignature != nullptr && valueOfMethod_.memberProxyType != nullptr);
271 return valueOfMethod_;
272 }
273
ValuesMethod() const274 ETSEnumInterface::Method ETSEnumInterface::ValuesMethod() const noexcept
275 {
276 ASSERT(valuesMethod_.globalSignature != nullptr && valuesMethod_.memberProxyType != nullptr);
277 return valuesMethod_;
278 }
279
IsLiteralType() const280 bool ETSEnumInterface::IsLiteralType() const noexcept
281 {
282 return member_ != nullptr;
283 }
284
FindMember(const util::StringView & name) const285 ir::TSEnumMember *ETSEnumInterface::FindMember(const util::StringView &name) const noexcept
286 {
287 ASSERT(!IsLiteralType());
288 const auto &members = GetMembers();
289 auto memberIt = std::find_if(members.begin(), members.end(), [name](const ir::AstNode *const node) {
290 return node->AsTSEnumMember()->Key()->AsIdentifier()->Name() == name;
291 });
292 if (memberIt != members.end()) {
293 return (*memberIt)->AsTSEnumMember();
294 }
295
296 return nullptr;
297 }
298
LookupConstantMethod(ETSChecker * const checker,const ir::Identifier * const prop) const299 ETSFunctionType *ETSEnumInterface::LookupConstantMethod(ETSChecker *const checker,
300 const ir::Identifier *const prop) const
301 {
302 if (prop->Name() == TO_STRING_METHOD_NAME) {
303 ASSERT(toStringMethod_.memberProxyType != nullptr);
304 return toStringMethod_.memberProxyType;
305 }
306
307 if (prop->Name() == GET_VALUE_METHOD_NAME) {
308 ASSERT(getValueMethod_.memberProxyType != nullptr);
309 return getValueMethod_.memberProxyType;
310 }
311
312 if (prop->Name() == GET_NAME_METHOD_NAME) {
313 ASSERT(getNameMethod_.memberProxyType != nullptr);
314 return getNameMethod_.memberProxyType;
315 }
316
317 checker->ThrowTypeError({"No enum item method called '", prop->Name(), "'"}, prop->Start());
318 }
319
LookupTypeMethod(ETSChecker * const checker,const ir::Identifier * const prop) const320 ETSFunctionType *ETSEnumInterface::LookupTypeMethod(ETSChecker *const checker, const ir::Identifier *const prop) const
321 {
322 if (prop->Name() == VALUES_METHOD_NAME) {
323 ASSERT(valuesMethod_.memberProxyType != nullptr);
324 return valuesMethod_.memberProxyType;
325 }
326
327 if (prop->Name() == VALUE_OF_METHOD_NAME) {
328 ASSERT(valueOfMethod_.memberProxyType != nullptr);
329 return valueOfMethod_.memberProxyType;
330 }
331
332 checker->ThrowTypeError({"No enum type method called '", prop->Name(), "'"}, prop->Start());
333 }
334
335 } // namespace panda::es2panda::checker
336