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