• 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 "checker/ets/unboxingConverter.h"
21 #include "ir/expressions/identifier.h"
22 #include "ir/expressions/literals/numberLiteral.h"
23 #include "ir/expressions/memberExpression.h"
24 #include "ir/ts/tsEnumMember.h"
25 
26 namespace ark::es2panda::checker {
ETSEnumType(const ir::TSEnumDeclaration * const enumDecl,UType ordinal,const ir::TSEnumMember * const member,TypeFlag const typeFlag)27 ETSEnumType::ETSEnumType(const ir::TSEnumDeclaration *const enumDecl, UType ordinal,
28                          const ir::TSEnumMember *const member, TypeFlag const typeFlag)
29     : Type(typeFlag), decl_(enumDecl), ordinal_ {ordinal}, member_(member)
30 {
31 }
32 
AssignmentSource(TypeRelation * const relation,Type * const target)33 bool ETSEnumType::AssignmentSource(TypeRelation *const relation, Type *const target)
34 {
35     bool result = false;
36 
37     if (target->IsETSEnumType()) {
38         result = IsSameEnumType(target->AsETSEnumType());
39     } else if (target->IsETSStringType()) {
40         result = IsETSStringEnumType();
41         if (result) {
42             relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
43         }
44     } else if (target->IsETSObjectType()) {
45         if (target->AsETSObjectType()->IsGlobalETSObjectType()) {
46             result = true;
47             relation->GetNode()->AddBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
48         } else if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_NUMERIC) && IsETSIntEnumType()) {
49             result = true;
50             relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
51         }
52     } else if (target->HasTypeFlag(TypeFlag::ETS_NUMERIC)) {
53         result = IsETSIntEnumType();
54         if (result) {
55             relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
56         }
57     } else if (target->IsETSBooleanType()) {
58         result = true;
59         relation->GetNode()->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
60     } else if (target->IsETSUnionType()) {
61         auto &unionConstituentTypes = target->AsETSUnionType()->ConstituentTypes();
62         for (auto *constituentType : unionConstituentTypes) {
63             if (constituentType == GetDecl()->BoxedClass()->TsType()) {
64                 result = true;
65                 relation->GetNode()->AddBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM);
66                 break;
67             }
68         }
69     }
70     relation->Result(result);
71     return relation->IsTrue();
72 }
73 
AssignmentTarget(TypeRelation * const relation,Type * const source)74 void ETSEnumType::AssignmentTarget(TypeRelation *const relation, Type *const source)
75 {
76     auto checker = relation->GetChecker()->AsETSChecker();
77 
78     Type *const unboxedSource = checker->MaybeUnboxType(source);
79     if (unboxedSource != source && !relation->OnlyCheckBoxingUnboxing()) {
80         relation->GetNode()->AddBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOX_TO_ENUM);
81     }
82 
83     relation->Result(false);
84     if (unboxedSource->IsETSEnumType()) {
85         relation->Result(IsSameEnumType(unboxedSource->AsETSEnumType()));
86     } else if (unboxedSource->IsETSStringType()) {
87         relation->Result(IsSameEnumType(unboxedSource->AsETSStringEnumType()));
88     }
89 }
90 
Cast(TypeRelation * relation,Type * target)91 void ETSEnumType::Cast(TypeRelation *relation, Type *target)
92 {
93     if (target->IsETSEnumType()) {
94         conversion::Identity(relation, this, target);
95         return;
96     }
97 
98     if (target->HasTypeFlag(TypeFlag::ETS_NUMERIC)) {
99         relation->Result(true);
100         return;
101     }
102 
103     // NOTE(vpukhov): #20510 conversion rules
104     if (target->IsETSObjectType()) {
105         if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BOXED_ENUM)) {
106             conversion::Boxing(relation, this);
107             return;
108         }
109 
110         if (target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) {
111             auto unboxedTarget = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target);
112             if (unboxedTarget == nullptr) {
113                 conversion::Forbidden(relation);
114                 return;
115             }
116             Cast(relation, unboxedTarget);
117             if (relation->IsTrue()) {
118                 conversion::Boxing(relation, unboxedTarget);
119                 return;
120             }
121             conversion::Forbidden(relation);
122             return;
123         }
124 
125         conversion::BoxingWideningReference(relation, this, target->AsETSObjectType());
126         return;
127     }
128     conversion::Forbidden(relation);
129 }
130 
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)131 Type *ETSEnumType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation,
132                                [[maybe_unused]] GlobalTypesHolder *globalTypes)
133 {
134     return this;
135 }
136 
Identical(TypeRelation * const relation,Type * const other)137 void ETSEnumType::Identical(TypeRelation *const relation, Type *const other)
138 {
139     relation->Result(other->IsETSEnumType() && IsSameEnumType(other->AsETSEnumType()));
140 }
141 
ToAssemblerType(std::stringstream & ss) const142 void ETSEnumType::ToAssemblerType(std::stringstream &ss) const
143 {
144     ToAssemblerTypeImpl<UType>(ss);
145 }
146 
ToDebugInfoType(std::stringstream & ss) const147 void ETSEnumType::ToDebugInfoType(std::stringstream &ss) const
148 {
149     ToDebugInfoTypeImpl<UType>(ss);
150 }
151 
ToString(std::stringstream & ss,bool precise) const152 void ETSEnumType::ToString(std::stringstream &ss, [[maybe_unused]] bool precise) const
153 {
154     ss << decl_->Key()->Name();
155 }
156 
GetDecl() const157 const ir::TSEnumDeclaration *ETSEnumType::GetDecl() const noexcept
158 {
159     return decl_;
160 }
161 
BoxedType() const162 Type *ETSEnumType::BoxedType() const noexcept
163 {
164     return GetDecl()->BoxedClass()->TsType();
165 }
166 
GetMembers() const167 const ArenaVector<ir::AstNode *> &ETSEnumType::GetMembers() const noexcept
168 {
169     return decl_->Members();
170 }
171 
GetMemberVar() const172 varbinder::LocalVariable *ETSEnumType::GetMemberVar() const noexcept
173 {
174     ASSERT(IsLiteralType());
175     return member_->Key()->AsIdentifier()->Variable()->AsLocalVariable();
176 }
177 
GetName() const178 util::StringView ETSEnumType::GetName() const noexcept
179 {
180     return decl_->Key()->Name();
181 }
182 
GetOrdinal() const183 ETSEnumType::UType ETSEnumType::GetOrdinal() const noexcept
184 {
185     ASSERT(IsLiteralType());
186     return ordinal_;
187 }
188 
LookupConstant(ETSChecker * const checker,const ir::Expression * const expression,const ir::Identifier * const prop) const189 ETSEnumType *ETSEnumType::LookupConstant(ETSChecker *const checker, const ir::Expression *const expression,
190                                          const ir::Identifier *const prop) const
191 {
192     if (!IsEnumTypeExpression(expression)) {
193         if (expression->IsIdentifier() &&
194             expression->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::TYPE_ALIAS)) {
195             checker->LogTypeError({"Cannot refer to enum members through type alias."}, prop->Start());
196         } else if (IsLiteralType()) {
197             checker->LogTypeError({"Cannot refer to enum members through variable."}, prop->Start());
198         } else {
199             checker->LogTypeError({"Enum constant does not have property '", prop->Name(), "'."}, prop->Start());
200         }
201         return nullptr;
202     }
203 
204     auto *const member = FindMember(prop->Name());
205     if (member == nullptr) {
206         checker->LogTypeError({"No enum constant named '", prop->Name(), "' in enum '", this, "'"}, prop->Start());
207         return nullptr;
208     }
209 
210     auto *const enumInterface = member->Key()->AsIdentifier()->Variable()->TsType()->AsETSEnumType();
211     ASSERT(enumInterface->IsLiteralType());
212     return enumInterface;
213 }
214 
LookupMethod(ETSChecker * checker,const ir::Expression * const expression,const ir::Identifier * const prop) const215 ETSFunctionType *ETSEnumType::LookupMethod(ETSChecker *checker, const ir::Expression *const expression,
216                                            const ir::Identifier *const prop) const
217 {
218     if (IsEnumTypeExpression(expression)) {
219         return LookupTypeMethod(checker, prop);
220     }
221 
222     ASSERT(IsEnumInstanceExpression(expression));
223     return LookupConstantMethod(checker, prop);
224 }
225 
IsSameEnumType(const ETSEnumType * const other) const226 bool ETSEnumType::IsSameEnumType(const ETSEnumType *const other) const noexcept
227 {
228     return other->decl_ == decl_;
229 }
230 
IsSameEnumLiteralType(const ETSEnumType * const other) const231 bool ETSEnumType::IsSameEnumLiteralType(const ETSEnumType *const other) const noexcept
232 {
233     ASSERT(IsLiteralType() && IsSameEnumType(other));
234     return member_ == other->member_;
235 }
236 
IsEnumInstanceExpression(const ir::Expression * const expression) const237 bool ETSEnumType::IsEnumInstanceExpression(const ir::Expression *const expression) const noexcept
238 {
239     ASSERT(IsSameEnumType(expression->TsType()->AsETSEnumType()));
240 
241     return IsEnumLiteralExpression(expression) || !IsEnumTypeExpression(expression);
242 }
243 
IsEnumLiteralExpression(const ir::Expression * const expression) const244 bool ETSEnumType::IsEnumLiteralExpression(const ir::Expression *const expression) const noexcept
245 {
246     ASSERT(IsSameEnumType(expression->TsType()->AsETSEnumType()));
247 
248     if (expression->IsMemberExpression()) {
249         const auto *const memberExpr = expression->AsMemberExpression();
250         return memberExpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS &&
251                IsEnumTypeExpression(memberExpr->Object());
252     }
253 
254     return false;
255 }
256 
IsEnumTypeExpression(const ir::Expression * const expression) const257 bool ETSEnumType::IsEnumTypeExpression(const ir::Expression *const expression) const noexcept
258 {
259     if (expression->TsType()->IsETSEnumType()) {
260         ASSERT(IsSameEnumType(expression->TsType()->AsETSEnumType()));
261     } else {
262         return false;
263     }
264 
265     if (expression->IsCallExpression()) {
266         return false;
267     }
268 
269     // clang-format off
270     const auto *const localVar = [expression]() -> const varbinder::LocalVariable* {
271         if (expression->IsMemberExpression()) {
272             const auto *const memberExpr = expression->AsMemberExpression();
273             return memberExpr->PropVar() != nullptr
274                        ? memberExpr->PropVar()
275                        : memberExpr->Object()->AsIdentifier()->Variable()->AsLocalVariable();
276         }
277         return expression->AsIdentifier()->Variable()->AsLocalVariable();
278     }();
279     // clang-format on
280     ASSERT(localVar->Declaration() == decl_->Key()->AsIdentifier()->Variable()->Declaration() ||
281            !localVar->HasFlag(varbinder::VariableFlags::ENUM_LITERAL));
282     return localVar->HasFlag(varbinder::VariableFlags::ENUM_LITERAL);
283 }
284 
FromIntMethod() const285 ETSEnumType::Method ETSEnumType::FromIntMethod() const noexcept
286 {
287     ASSERT(fromIntMethod_.globalSignature != nullptr && fromIntMethod_.memberProxyType == nullptr);
288     return fromIntMethod_;
289 }
290 
BoxedFromIntMethod() const291 ETSEnumType::Method ETSEnumType::BoxedFromIntMethod() const noexcept
292 {
293     ASSERT(boxedFromIntMethod_.globalSignature != nullptr && boxedFromIntMethod_.memberProxyType == nullptr);
294     return boxedFromIntMethod_;
295 }
296 
UnboxMethod() const297 ETSEnumType::Method ETSEnumType::UnboxMethod() const noexcept
298 {
299     ASSERT(unboxMethod_.globalSignature != nullptr && unboxMethod_.memberProxyType != nullptr);
300     return unboxMethod_;
301 }
302 
ValueOfMethod() const303 ETSEnumType::Method ETSEnumType::ValueOfMethod() const noexcept
304 {
305     ASSERT(valueOfMethod_.globalSignature != nullptr && valueOfMethod_.memberProxyType != nullptr);
306     return valueOfMethod_;
307 }
308 
GetNameMethod() const309 ETSEnumType::Method ETSEnumType::GetNameMethod() const noexcept
310 {
311     ASSERT(getNameMethod_.globalSignature != nullptr && getNameMethod_.memberProxyType != nullptr);
312     return getNameMethod_;
313 }
314 
ToStringMethod() const315 ETSEnumType::Method ETSEnumType::ToStringMethod() const noexcept
316 {
317     ASSERT(toStringMethod_.globalSignature != nullptr && toStringMethod_.memberProxyType != nullptr);
318     return toStringMethod_;
319 }
320 
GetValueOfMethod() const321 ETSEnumType::Method ETSEnumType::GetValueOfMethod() const noexcept
322 {
323     ASSERT(getValueOfMethod_.globalSignature != nullptr && getValueOfMethod_.memberProxyType != nullptr);
324     return getValueOfMethod_;
325 }
326 
ValuesMethod() const327 ETSEnumType::Method ETSEnumType::ValuesMethod() const noexcept
328 {
329     ASSERT(valuesMethod_.globalSignature != nullptr && valuesMethod_.memberProxyType != nullptr);
330     return valuesMethod_;
331 }
332 
IsLiteralType() const333 bool ETSEnumType::IsLiteralType() const noexcept
334 {
335     return member_ != nullptr;
336 }
337 
FindMember(const util::StringView & name) const338 ir::TSEnumMember *ETSEnumType::FindMember(const util::StringView &name) const noexcept
339 {
340     ASSERT(!IsLiteralType());
341     const auto &members = GetMembers();
342     auto memberIt = std::find_if(members.begin(), members.end(), [name](const ir::AstNode *const node) {
343         return node->AsTSEnumMember()->Key()->AsIdentifier()->Name() == name;
344     });
345     if (memberIt != members.end()) {
346         return (*memberIt)->AsTSEnumMember();
347     }
348 
349     return nullptr;
350 }
351 
LookupConstantMethod(ETSChecker * const checker,const ir::Identifier * const prop) const352 ETSFunctionType *ETSEnumType::LookupConstantMethod(ETSChecker *const checker, const ir::Identifier *const prop) const
353 {
354     if (prop->Name() == TO_STRING_METHOD_NAME) {
355         ASSERT(toStringMethod_.memberProxyType != nullptr);
356         return toStringMethod_.memberProxyType;
357     }
358 
359     if (prop->Name() == VALUE_OF_METHOD_NAME) {
360         ASSERT(valueOfMethod_.memberProxyType != nullptr);
361         return valueOfMethod_.memberProxyType;
362     }
363 
364     if (prop->Name() == GET_NAME_METHOD_NAME) {
365         ASSERT(getNameMethod_.memberProxyType != nullptr);
366         return getNameMethod_.memberProxyType;
367     }
368 
369     checker->LogTypeError({"No enum item method called '", prop->Name(), "'"}, prop->Start());
370     return nullptr;
371 }
372 
LookupTypeMethod(ETSChecker * const checker,const ir::Identifier * const prop) const373 ETSFunctionType *ETSEnumType::LookupTypeMethod(ETSChecker *const checker, const ir::Identifier *const prop) const
374 {
375     if (prop->Name() == VALUES_METHOD_NAME) {
376         ASSERT(valuesMethod_.memberProxyType != nullptr);
377         return valuesMethod_.memberProxyType;
378     }
379 
380     if (prop->Name() == GET_VALUE_OF_METHOD_NAME) {
381         ASSERT(getValueOfMethod_.memberProxyType != nullptr);
382         return getValueOfMethod_.memberProxyType;
383     }
384 
385     checker->LogTypeError({"No enum type method called '", prop->Name(), "'"}, prop->Start());
386     return nullptr;
387 }
388 
389 }  // namespace ark::es2panda::checker
390