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 ark::es2panda::checker {
ETSEnumType(const ir::TSEnumDeclaration * const enumDecl,UType ordinal,const ir::TSEnumMember * const member,TypeFlag const typeFlag)26 ETSEnumType::ETSEnumType(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 ETSEnumType::AssignmentSource(TypeRelation *const relation, Type *const target)
33 {
34 bool result = false;
35
36 if (target->IsETSEnumType()) {
37 result = IsSameEnumType(target->AsETSEnumType());
38 } else if (target->IsETSStringType()) {
39 result = IsETSStringEnumType();
40 if (result) {
41 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
42 }
43 } else if (target->IsETSObjectType()) {
44 if (target->AsETSObjectType()->IsGlobalETSObjectType()) {
45 result = true;
46 relation->GetNode()->AddBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
47 } else if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC) && IsETSIntEnumType()) {
48 result = true;
49 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
50 }
51 } else if (target->HasTypeFlag(TypeFlag::ETS_NUMERIC)) {
52 result = IsETSIntEnumType();
53 if (result) {
54 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
55 }
56 } else if (target->IsETSBooleanType()) {
57 result = true;
58 relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
59 } else if (target->IsETSUnionType()) {
60 auto &unionConstituentTypes = target->AsETSUnionType()->ConstituentTypes();
61 for (auto *constituentType : unionConstituentTypes) {
62 if (constituentType == GetDecl()->BoxedClass()->TsType()) {
63 result = true;
64 relation->GetNode()->AddBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
65 break;
66 }
67 }
68 }
69 relation->Result(result);
70 return relation->IsTrue();
71 }
72
AssignmentTarget(TypeRelation * const relation,Type * const source)73 void ETSEnumType::AssignmentTarget(TypeRelation *const relation, Type *const source)
74 {
75 auto const result = source->IsETSIntEnumType()
76 ? IsSameEnumType(source->AsETSIntEnumType())
77 : (source->IsETSStringEnumType() ? IsSameEnumType(source->AsETSStringEnumType()) : false);
78 relation->Result(result);
79 }
80
Cast(TypeRelation * relation,Type * target)81 void ETSEnumType::Cast(TypeRelation *relation, Type *target)
82 {
83 if (target->HasTypeFlag(TypeFlag::ENUM | TypeFlag::ETS_INT_ENUM | TypeFlag::ETS_STRING_ENUM)) {
84 conversion::Identity(relation, this, target);
85 return;
86 }
87
88 if (target->IsIntType()) {
89 relation->Result(true);
90 return;
91 }
92 if (target->IsETSObjectType()) {
93 if (target->AsETSObjectType()->IsGlobalETSObjectType()) {
94 relation->Result(true);
95 relation->GetNode()->AddBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
96 return;
97 }
98 }
99 if (target->IsETSUnionType()) {
100 auto &unionConstituentTypes = target->AsETSUnionType()->ConstituentTypes();
101 for (auto *constituentType : unionConstituentTypes) {
102 if (constituentType == GetDecl()->BoxedClass()->TsType() ||
103 (constituentType->IsETSObjectType() && constituentType->AsETSObjectType()->IsGlobalETSObjectType())) {
104 relation->Result(true);
105 relation->GetNode()->AddBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
106 return;
107 }
108 }
109 }
110 conversion::Forbidden(relation);
111 }
112
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)113 Type *ETSEnumType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation,
114 [[maybe_unused]] GlobalTypesHolder *globalTypes)
115 {
116 return this;
117 }
118
Identical(TypeRelation * const relation,Type * const other)119 void ETSEnumType::Identical(TypeRelation *const relation, Type *const other)
120 {
121 ETSEnumType const *const otherEnumType = [other]() -> ETSEnumType const * {
122 if (other->IsETSIntEnumType()) {
123 return other->AsETSIntEnumType();
124 }
125 if (other->IsETSStringEnumType()) {
126 return other->AsETSStringEnumType();
127 }
128 return nullptr;
129 }();
130
131 relation->Result(otherEnumType != nullptr && IsSameEnumType(otherEnumType) && member_ == otherEnumType->member_);
132 }
133
ToAssemblerType(std::stringstream & ss) const134 void ETSEnumType::ToAssemblerType(std::stringstream &ss) const
135 {
136 ToAssemblerTypeImpl<UType>(ss);
137 }
138
ToDebugInfoType(std::stringstream & ss) const139 void ETSEnumType::ToDebugInfoType(std::stringstream &ss) const
140 {
141 ToDebugInfoTypeImpl<UType>(ss);
142 }
143
ToString(std::stringstream & ss,bool precise) const144 void ETSEnumType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const
145 {
146 ss << decl_->Key()->Name();
147 }
148
GetDecl() const149 const ir::TSEnumDeclaration *ETSEnumType::GetDecl() const noexcept
150 {
151 return decl_;
152 }
153
GetMembers() const154 const ArenaVector<ir::AstNode *> &ETSEnumType::GetMembers() const noexcept
155 {
156 return decl_->Members();
157 }
158
GetMemberVar() const159 varbinder::LocalVariable *ETSEnumType::GetMemberVar() const noexcept
160 {
161 ASSERT(IsLiteralType());
162 return member_->Key()->AsIdentifier()->Variable()->AsLocalVariable();
163 }
164
GetName() const165 util::StringView ETSEnumType::GetName() const noexcept
166 {
167 return decl_->Key()->Name();
168 }
169
GetOrdinal() const170 ETSEnumType::UType ETSEnumType::GetOrdinal() const noexcept
171 {
172 ASSERT(IsLiteralType());
173 return ordinal_;
174 }
175
LookupConstant(ETSChecker * const checker,const ir::Expression * const expression,const ir::Identifier * const prop) const176 ETSEnumType *ETSEnumType::LookupConstant(ETSChecker *const checker, const ir::Expression *const expression,
177 const ir::Identifier *const prop) const
178 {
179 if (!IsEnumTypeExpression(expression)) {
180 if (expression->IsIdentifier() &&
181 expression->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::TYPE_ALIAS)) {
182 checker->ThrowTypeError({"Cannot refer to enum members through type alias."}, prop->Start());
183 } else if (IsLiteralType()) {
184 checker->ThrowTypeError({"Cannot refer to enum members through variable."}, prop->Start());
185 } else {
186 checker->ThrowTypeError({"Enum constant does not have property '", prop->Name(), "'."}, prop->Start());
187 }
188 }
189
190 auto *const member = FindMember(prop->Name());
191 if (member == nullptr) {
192 checker->ThrowTypeError({"No enum constant named '", prop->Name(), "' in enum '", this, "'"}, prop->Start());
193 }
194 // clang-format off
195 auto *const enumInterface = [enumType =
196 member->Key()->AsIdentifier()->Variable()->TsType()]() -> checker::ETSEnumType* {
197 if (enumType->IsETSIntEnumType()) {
198 return enumType->AsETSIntEnumType();
199 }
200 return enumType->AsETSStringEnumType();
201 }();
202 // clang-format on
203 ASSERT(enumInterface->IsLiteralType());
204 return enumInterface;
205 }
206
LookupMethod(ETSChecker * checker,const ir::Expression * const expression,const ir::Identifier * const prop) const207 ETSFunctionType *ETSEnumType::LookupMethod(ETSChecker *checker, const ir::Expression *const expression,
208 const ir::Identifier *const prop) const
209 {
210 if (IsEnumTypeExpression(expression)) {
211 return LookupTypeMethod(checker, prop);
212 }
213
214 ASSERT(IsEnumInstanceExpression(expression));
215 return LookupConstantMethod(checker, prop);
216 }
217
IsSameEnumType(const ETSEnumType * const other) const218 bool ETSEnumType::IsSameEnumType(const ETSEnumType *const other) const noexcept
219 {
220 return other->decl_ == decl_;
221 }
222
IsSameEnumLiteralType(const ETSEnumType * const other) const223 bool ETSEnumType::IsSameEnumLiteralType(const ETSEnumType *const other) const noexcept
224 {
225 ASSERT(IsLiteralType() && IsSameEnumType(other));
226 return member_ == other->member_;
227 }
228
SpecifyEnumInterface(const checker::Type * enumType)229 [[maybe_unused]] static const ETSEnumType *SpecifyEnumInterface(const checker::Type *enumType)
230 {
231 if (enumType->IsETSIntEnumType()) {
232 return enumType->AsETSIntEnumType();
233 }
234 if (enumType->IsETSStringEnumType()) {
235 return enumType->AsETSStringEnumType();
236 }
237 return nullptr;
238 }
239
IsEnumInstanceExpression(const ir::Expression * const expression) const240 bool ETSEnumType::IsEnumInstanceExpression(const ir::Expression *const expression) const noexcept
241 {
242 ASSERT(IsSameEnumType(SpecifyEnumInterface(expression->TsType())));
243
244 return IsEnumLiteralExpression(expression) || !IsEnumTypeExpression(expression);
245 }
246
IsEnumLiteralExpression(const ir::Expression * const expression) const247 bool ETSEnumType::IsEnumLiteralExpression(const ir::Expression *const expression) const noexcept
248 {
249 ASSERT(IsSameEnumType(SpecifyEnumInterface(expression->TsType())));
250
251 if (expression->IsMemberExpression()) {
252 const auto *const memberExpr = expression->AsMemberExpression();
253 return memberExpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS &&
254 IsEnumTypeExpression(memberExpr->Object());
255 }
256
257 return false;
258 }
259
IsEnumTypeExpression(const ir::Expression * const expression) const260 bool ETSEnumType::IsEnumTypeExpression(const ir::Expression *const expression) const noexcept
261 {
262 auto specifiedEnumInterface = SpecifyEnumInterface(expression->TsType());
263 if (specifiedEnumInterface != nullptr) {
264 ASSERT(IsSameEnumType(specifiedEnumInterface));
265 } else {
266 return false;
267 }
268
269 if (expression->IsCallExpression()) {
270 return false;
271 }
272
273 // clang-format off
274 const auto *const localVar = [expression]() -> const varbinder::LocalVariable* {
275 if (expression->IsMemberExpression()) {
276 const auto *const memberExpr = expression->AsMemberExpression();
277 return memberExpr->PropVar() != nullptr
278 ? memberExpr->PropVar()
279 : memberExpr->Object()->AsIdentifier()->Variable()->AsLocalVariable();
280 }
281 return expression->AsIdentifier()->Variable()->AsLocalVariable();
282 }();
283 // clang-format on
284 ASSERT(localVar->Declaration() == decl_->Key()->AsIdentifier()->Variable()->Declaration() ||
285 !localVar->HasFlag(varbinder::VariableFlags::ENUM_LITERAL));
286 return localVar->HasFlag(varbinder::VariableFlags::ENUM_LITERAL);
287 }
288
FromIntMethod() const289 ETSEnumType::Method ETSEnumType::FromIntMethod() const noexcept
290 {
291 ASSERT(fromIntMethod_.globalSignature != nullptr && fromIntMethod_.memberProxyType == nullptr);
292 return fromIntMethod_;
293 }
294
BoxedFromIntMethod() const295 ETSEnumType::Method ETSEnumType::BoxedFromIntMethod() const noexcept
296 {
297 ASSERT(boxedFromIntMethod_.globalSignature != nullptr && boxedFromIntMethod_.memberProxyType == nullptr);
298 return boxedFromIntMethod_;
299 }
300
UnboxMethod() const301 ETSEnumType::Method ETSEnumType::UnboxMethod() const noexcept
302 {
303 ASSERT(unboxMethod_.globalSignature != nullptr && unboxMethod_.memberProxyType == nullptr);
304 return unboxMethod_;
305 }
306
ValueOfMethod() const307 ETSEnumType::Method ETSEnumType::ValueOfMethod() const noexcept
308 {
309 ASSERT(valueOfMethod_.globalSignature != nullptr && valueOfMethod_.memberProxyType != nullptr);
310 return valueOfMethod_;
311 }
312
GetNameMethod() const313 ETSEnumType::Method ETSEnumType::GetNameMethod() const noexcept
314 {
315 ASSERT(getNameMethod_.globalSignature != nullptr && getNameMethod_.memberProxyType != nullptr);
316 return getNameMethod_;
317 }
318
ToStringMethod() const319 ETSEnumType::Method ETSEnumType::ToStringMethod() const noexcept
320 {
321 ASSERT(toStringMethod_.globalSignature != nullptr && toStringMethod_.memberProxyType != nullptr);
322 return toStringMethod_;
323 }
324
GetValueOfMethod() const325 ETSEnumType::Method ETSEnumType::GetValueOfMethod() const noexcept
326 {
327 ASSERT(getValueOfMethod_.globalSignature != nullptr && getValueOfMethod_.memberProxyType != nullptr);
328 return getValueOfMethod_;
329 }
330
ValuesMethod() const331 ETSEnumType::Method ETSEnumType::ValuesMethod() const noexcept
332 {
333 ASSERT(valuesMethod_.globalSignature != nullptr && valuesMethod_.memberProxyType != nullptr);
334 return valuesMethod_;
335 }
336
IsLiteralType() const337 bool ETSEnumType::IsLiteralType() const noexcept
338 {
339 return member_ != nullptr;
340 }
341
FindMember(const util::StringView & name) const342 ir::TSEnumMember *ETSEnumType::FindMember(const util::StringView &name) const noexcept
343 {
344 ASSERT(!IsLiteralType());
345 const auto &members = GetMembers();
346 auto memberIt = std::find_if(members.begin(), members.end(), [name](const ir::AstNode *const node) {
347 return node->AsTSEnumMember()->Key()->AsIdentifier()->Name() == name;
348 });
349 if (memberIt != members.end()) {
350 return (*memberIt)->AsTSEnumMember();
351 }
352
353 return nullptr;
354 }
355
LookupConstantMethod(ETSChecker * const checker,const ir::Identifier * const prop) const356 ETSFunctionType *ETSEnumType::LookupConstantMethod(ETSChecker *const checker, const ir::Identifier *const prop) const
357 {
358 if (prop->Name() == TO_STRING_METHOD_NAME) {
359 ASSERT(toStringMethod_.memberProxyType != nullptr);
360 return toStringMethod_.memberProxyType;
361 }
362
363 if (prop->Name() == VALUE_OF_METHOD_NAME) {
364 ASSERT(valueOfMethod_.memberProxyType != nullptr);
365 return valueOfMethod_.memberProxyType;
366 }
367
368 if (prop->Name() == GET_NAME_METHOD_NAME) {
369 ASSERT(getNameMethod_.memberProxyType != nullptr);
370 return getNameMethod_.memberProxyType;
371 }
372
373 checker->ThrowTypeError({"No enum item method called '", prop->Name(), "'"}, prop->Start());
374 }
375
LookupTypeMethod(ETSChecker * const checker,const ir::Identifier * const prop) const376 ETSFunctionType *ETSEnumType::LookupTypeMethod(ETSChecker *const checker, const ir::Identifier *const prop) const
377 {
378 if (prop->Name() == VALUES_METHOD_NAME) {
379 ASSERT(valuesMethod_.memberProxyType != nullptr);
380 return valuesMethod_.memberProxyType;
381 }
382
383 if (prop->Name() == GET_VALUE_OF_METHOD_NAME) {
384 ASSERT(getValueOfMethod_.memberProxyType != nullptr);
385 return getValueOfMethod_.memberProxyType;
386 }
387
388 checker->ThrowTypeError({"No enum type method called '", prop->Name(), "'"}, prop->Start());
389 }
390
391 } // namespace ark::es2panda::checker
392