• 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 "enumLowering.h"
17 #include "checker/types/ets/etsEnumType.h"
18 #include "checker/ETSchecker.h"
19 #include "checker/types/type.h"
20 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
21 #include "compiler/lowering/util.h"
22 #include "varbinder/ETSBinder.h"
23 #include "varbinder/variable.h"
24 
25 namespace ark::es2panda::compiler {
26 
27 namespace {
28 
MakeFunctionParam(checker::ETSChecker * const checker,const util::StringView & name,ir::TypeNode * const typeAnnotation)29 [[nodiscard]] ir::ETSParameterExpression *MakeFunctionParam(checker::ETSChecker *const checker,
30                                                             const util::StringView &name,
31                                                             ir::TypeNode *const typeAnnotation)
32 {
33     auto *const paramIdent = checker->AllocNode<ir::Identifier>(name, typeAnnotation, checker->Allocator());
34     auto *const param = checker->AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr);
35 
36     return param;
37 }
38 
MakeParamRefIdent(checker::ETSChecker * const checker,ir::ETSParameterExpression * paramExpr)39 [[nodiscard]] ir::Identifier *MakeParamRefIdent(checker::ETSChecker *const checker,
40                                                 ir::ETSParameterExpression *paramExpr)
41 {
42     auto *const refIdent = checker->AllocNode<ir::Identifier>(paramExpr->Ident()->Name(), checker->Allocator());
43     refIdent->SetVariable(paramExpr->Ident()->Variable());
44     return refIdent;
45 }
46 
MakeTypeReference(checker::ETSChecker * const checker,const util::StringView & name)47 [[nodiscard]] ir::ETSTypeReference *MakeTypeReference(checker::ETSChecker *const checker, const util::StringView &name)
48 {
49     auto *const ident = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
50     auto *const referencePart = checker->AllocNode<ir::ETSTypeReferencePart>(ident);
51     return checker->AllocNode<ir::ETSTypeReference>(referencePart);
52 }
53 
MakeMethodDef(checker::ETSChecker * const checker,ir::ClassDefinition * enumClass,ir::Identifier * const ident,ir::ScriptFunction * const function)54 ir::MethodDefinition *MakeMethodDef(checker::ETSChecker *const checker, ir::ClassDefinition *enumClass,
55                                     ir::Identifier *const ident, ir::ScriptFunction *const function)
56 {
57     auto *const functionExpr = checker->AllocNode<ir::FunctionExpression>(function);
58     auto *const identClone = ident->Clone(checker->Allocator(), nullptr);
59 
60     auto *const methodDef = checker->AllocNode<ir::MethodDefinition>(
61         ir::MethodDefinitionKind::METHOD, identClone, functionExpr, function->Modifiers(), checker->Allocator(), false);
62     methodDef->SetParent(enumClass);
63     enumClass->Body().push_back(methodDef);
64 
65     return methodDef;
66 }
67 
68 }  // namespace
69 
MakeFunction(FunctionInfo && functionInfo)70 [[nodiscard]] ir::ScriptFunction *EnumLoweringPhase::MakeFunction(FunctionInfo &&functionInfo)
71 {
72     ir::BlockStatement *bodyBlock = nullptr;
73 
74     if (functionInfo.enumDecl->IsDeclare()) {
75         functionInfo.flags |= ir::ModifierFlags::DECLARE;
76     } else {
77         bodyBlock = checker_->AllocNode<ir::BlockStatement>(Allocator(), std::move(functionInfo.body));
78     }
79     // clang-format off
80     auto *const function = checker_->AllocNode<ir::ScriptFunction>(
81         Allocator(), ir::ScriptFunction::ScriptFunctionData {
82             bodyBlock,
83             ir::FunctionSignature(nullptr, std::move(functionInfo.params), functionInfo.returnTypeAnnotation),
84             ir::ScriptFunctionFlags::METHOD, functionInfo.flags});
85     // clang-format on
86 
87     return function;
88 }
89 
GetEnumClassName(checker::ETSChecker * checker,const ir::TSEnumDeclaration * const enumDecl)90 util::UString EnumLoweringPhase::GetEnumClassName(checker::ETSChecker *checker,
91                                                   const ir::TSEnumDeclaration *const enumDecl)
92 {
93     util::UString className(util::StringView("#"), checker->Allocator());
94     className.Append(enumDecl->Key()->Name());
95     return className;
96 }
97 
98 template <typename ElementMaker>
MakeArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,const util::StringView & name,ir::TypeNode * const typeAnnotation,ElementMaker && elementMaker)99 [[nodiscard]] ir::Identifier *EnumLoweringPhase::MakeArray(const ir::TSEnumDeclaration *const enumDecl,
100                                                            ir::ClassDefinition *const enumClass,
101                                                            const util::StringView &name,
102                                                            ir::TypeNode *const typeAnnotation,
103                                                            ElementMaker &&elementMaker)
104 {
105     ArenaVector<ir::Expression *> elements(Allocator()->Adapter());
106     elements.reserve(enumDecl->Members().size());
107     for (const auto *const member : enumDecl->Members()) {
108         elements.push_back(elementMaker(member->AsTSEnumMember()));
109     }
110     auto *const arrayExpr = checker_->AllocNode<ir::ArrayExpression>(std::move(elements), Allocator());
111     auto *const arrayIdent = checker_->AllocNode<ir::Identifier>(name, Allocator());
112     auto *const arrayClassProp = checker_->AllocNode<ir::ClassProperty>(
113         arrayIdent, arrayExpr, typeAnnotation,
114         ir::ModifierFlags::STATIC | ir::ModifierFlags::PROTECTED | ir::ModifierFlags::READONLY, Allocator(), false);
115     arrayClassProp->SetParent(enumClass);
116     enumClass->Body().push_back(arrayClassProp);
117 
118     return arrayIdent;
119 }
120 
CreateEnumNamesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)121 ir::Identifier *EnumLoweringPhase::CreateEnumNamesArray(const ir::TSEnumDeclaration *const enumDecl,
122                                                         ir::ClassDefinition *const enumClass)
123 {
124     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
125     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(stringTypeAnnotation);
126 
127     // clang-format off
128     return MakeArray(enumDecl, enumClass, "NamesArray", arrayTypeAnnotation,
129                      [this](const ir::TSEnumMember *const member) {
130                         auto *const enumNameStringLiteral =
131                             checker_->AllocNode<ir::StringLiteral>(member->Key()->AsIdentifier()->Name());
132                         return enumNameStringLiteral;
133                     });
134     // clang-format on
135 }
136 
CreateClass(ir::TSEnumDeclaration * const enumDecl)137 ir::ClassDeclaration *EnumLoweringPhase::CreateClass(ir::TSEnumDeclaration *const enumDecl)
138 {
139     auto *ident = Allocator()->New<ir::Identifier>(GetEnumClassName(checker_, enumDecl).View(), Allocator());
140 
141     bool localDeclaration = (enumDecl->Parent() != nullptr && enumDecl->Parent()->IsBlockStatement());
142     auto *classDef = checker_->AllocNode<ir::ClassDefinition>(
143         Allocator(), ident,
144         localDeclaration ? ir::ClassDefinitionModifiers::CLASS_DECL | ir::ClassDefinitionModifiers::LOCAL
145                          : ir::ClassDefinitionModifiers::CLASS_DECL,
146         enumDecl->IsDeclare() ? ir::ModifierFlags::DECLARE : ir::ModifierFlags::NONE, Language(Language::Id::ETS));
147 
148     auto *classDecl = checker_->AllocNode<ir::ClassDeclaration>(classDef, Allocator());
149 
150     if (localDeclaration) {
151         enumDecl->Parent()->AsBlockStatement()->Statements().push_back(classDecl);
152         classDecl->SetParent(enumDecl->Parent());
153     } else {
154         program_->Ast()->Statements().push_back(classDecl);
155         classDecl->SetParent(program_->Ast());
156     }
157 
158     classDef->SetOrigEnumDecl(enumDecl);
159     enumDecl->SetBoxedClass(classDef);
160 
161     CreateOrdinalField(classDef);
162     if (!enumDecl->IsDeclare()) {
163         CreateCCtorForEnumClass(classDef);
164     }
165     CreateCtorForEnumClass(classDef);
166 
167     return classDecl;
168 }
169 
CreateCCtorForEnumClass(ir::ClassDefinition * const enumClass)170 void EnumLoweringPhase::CreateCCtorForEnumClass(ir::ClassDefinition *const enumClass)
171 {
172     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
173     auto *id = checker_->AllocNode<ir::Identifier>(compiler::Signatures::CCTOR, Allocator());
174 
175     ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
176 
177     auto *body = checker_->AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
178     auto *func = checker_->AllocNode<ir::ScriptFunction>(
179         Allocator(),
180         ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
181                                                 ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::HIDDEN,
182                                                 ir::ModifierFlags::STATIC, Language(Language::Id::ETS)});
183 
184     func->SetIdent(id);
185     id->SetParent(func);
186 
187     auto *funcExpr = checker_->AllocNode<ir::FunctionExpression>(func);
188 
189     auto *const identClone = id->Clone(Allocator(), nullptr);
190     auto *const methodDef = checker_->AllocNode<ir::MethodDefinition>(
191         ir::MethodDefinitionKind::METHOD, identClone, funcExpr, ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC,
192         Allocator(), false);
193     methodDef->SetParent(enumClass);
194     enumClass->Body().push_back(methodDef);
195 }
196 
CreateOrdinalField(ir::ClassDefinition * const enumClass)197 ir::ClassProperty *EnumLoweringPhase::CreateOrdinalField(ir::ClassDefinition *const enumClass)
198 {
199     auto *const fieldIdent = Allocator()->New<ir::Identifier>("ordinal", Allocator());
200     auto *const intTypeAnnotation = Allocator()->New<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
201     auto *field = checker_->AllocNode<ir::ClassProperty>(fieldIdent, nullptr, intTypeAnnotation,
202                                                          ir::ModifierFlags::PROTECTED, Allocator(), false);
203 
204     enumClass->Body().push_back(field);
205     field->SetParent(enumClass);
206     return field;
207 }
208 
CreateFunctionForCtorOfEnumClass(ir::ClassDefinition * const enumClass)209 ir::ScriptFunction *EnumLoweringPhase::CreateFunctionForCtorOfEnumClass(ir::ClassDefinition *const enumClass)
210 {
211     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
212 
213     auto *const intTypeAnnotation = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
214     auto *const inputOrdinalParam = MakeFunctionParam(checker_, "ordinal", intTypeAnnotation);
215     params.push_back(inputOrdinalParam);
216 
217     auto *id = checker_->AllocNode<ir::Identifier>("constructor", Allocator());
218     ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
219 
220     auto *body = checker_->AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
221 
222     auto scriptFlags = ir::ScriptFunctionFlags::CONSTRUCTOR;
223     scriptFlags |= enumClass->IsDeclare() ? ir::ScriptFunctionFlags::EXTERNAL : ir::ScriptFunctionFlags::NONE;
224 
225     auto *func = checker_->AllocNode<ir::ScriptFunction>(
226         Allocator(),
227         ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
228                                                 scriptFlags,                     // CC-OFF(G.FMT.02) project code style
229                                                 ir::ModifierFlags::CONSTRUCTOR,  // CC-OFF(G.FMT.02) project code style
230                                                 Language(Language::Id::ETS)});   // CC-OFF(G.FMT.02) project code style
231 
232     func->SetIdent(id);
233 
234     auto *thisExpr = Allocator()->New<ir::ThisExpression>();
235     auto *fieldIdentifier = Allocator()->New<ir::Identifier>("ordinal", Allocator());
236     auto *leftHandSide = checker_->AllocNode<ir::MemberExpression>(
237         thisExpr, fieldIdentifier, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
238     auto *rightHandSide = checker_->AllocNode<ir::Identifier>("ordinal", Allocator());
239     rightHandSide->SetVariable(inputOrdinalParam->Ident()->Variable());
240     auto *initializer = checker_->AllocNode<ir::AssignmentExpression>(leftHandSide, rightHandSide,
241                                                                       lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
242     auto initStatement = checker_->AllocNode<ir::ExpressionStatement>(initializer);
243     initStatement->SetParent(body);
244     body->Statements().push_back(initStatement);
245 
246     return func;
247 }
248 
CreateCtorForEnumClass(ir::ClassDefinition * const enumClass)249 void EnumLoweringPhase::CreateCtorForEnumClass(ir::ClassDefinition *const enumClass)
250 {
251     auto *func = CreateFunctionForCtorOfEnumClass(enumClass);
252     auto *funcExpr = checker_->AllocNode<ir::FunctionExpression>(func);
253 
254     auto *const identClone = func->Id()->Clone(Allocator(), nullptr);
255     auto *const methodDef = checker_->AllocNode<ir::MethodDefinition>(
256         ir::MethodDefinitionKind::CONSTRUCTOR, identClone, funcExpr, ir::ModifierFlags::PUBLIC, Allocator(), false);
257     methodDef->SetParent(enumClass);
258     enumClass->Body().push_back(methodDef);
259 }
260 
CreateEnumIntClassFromEnumDeclaration(ir::TSEnumDeclaration * const enumDecl)261 void EnumLoweringPhase::CreateEnumIntClassFromEnumDeclaration(ir::TSEnumDeclaration *const enumDecl)
262 {
263     auto *const enumClassDecl = CreateClass(enumDecl);
264     auto *const enumClass = enumClassDecl->Definition();
265 
266     auto *const namesArrayIdent = CreateEnumNamesArray(enumDecl, enumClass);
267     auto *const valuesArrayIdent = CreateEnumValuesArray(enumDecl, enumClass);
268     auto *const stringValuesArrayIdent = CreateEnumStringValuesArray(enumDecl, enumClass);
269     auto *const itemsArrayIdent = CreateEnumItemsArray(enumDecl, enumClass);
270     auto *const boxedItemsArrayIdent = CreateBoxedEnumItemsArray(enumDecl, enumClass);
271 
272     CreateEnumGetNameMethod(enumDecl, enumClass, namesArrayIdent);
273 
274     CreateEnumGetValueOfMethod(enumDecl, enumClass, namesArrayIdent);
275 
276     CreateEnumValueOfMethod(enumDecl, enumClass, valuesArrayIdent);
277 
278     CreateEnumToStringMethod(enumDecl, enumClass, stringValuesArrayIdent);
279 
280     CreateEnumValuesMethod(enumDecl, enumClass, itemsArrayIdent);
281 
282     CreateEnumFromIntMethod(enumDecl, enumClass, itemsArrayIdent, checker::ETSEnumType::FROM_INT_METHOD_NAME,
283                             enumDecl->Key()->Name());
284 
285     CreateUnboxingMethod(enumDecl, enumClass, itemsArrayIdent);
286 
287     CreateEnumFromIntMethod(enumDecl, enumClass, boxedItemsArrayIdent, checker::ETSEnumType::BOXED_FROM_INT_METHOD_NAME,
288                             GetEnumClassName(checker_, enumDecl).View());
289 
290     if (enumDecl->Parent() != nullptr && enumDecl->Parent()->IsBlockStatement()) {
291         auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, NearestScope(enumDecl->Parent()));
292         InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
293     } else {
294         auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, program_->GlobalScope());
295         InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
296         auto *ident = enumClass->Ident();
297         auto *var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::BINDINGS);
298         ident->SetVariable(var);
299     }
300 }
301 
CreateEnumStringClassFromEnumDeclaration(ir::TSEnumDeclaration * const enumDecl)302 void EnumLoweringPhase::CreateEnumStringClassFromEnumDeclaration(ir::TSEnumDeclaration *const enumDecl)
303 {
304     auto *const enumClassDecl = CreateClass(enumDecl);
305     auto *const enumClass = enumClassDecl->Definition();
306 
307     auto *const namesArrayIdent = CreateEnumNamesArray(enumDecl, enumClass);
308     auto *const stringValuesArrayIdent = CreateEnumStringValuesArray(enumDecl, enumClass);
309     auto *const itemsArrayIdent = CreateEnumItemsArray(enumDecl, enumClass);
310     auto *const boxedItemsArrayIdent = CreateBoxedEnumItemsArray(enumDecl, enumClass);
311 
312     CreateEnumGetNameMethod(enumDecl, enumClass, namesArrayIdent);
313 
314     CreateEnumGetValueOfMethod(enumDecl, enumClass, namesArrayIdent);
315 
316     CreateEnumToStringMethod(enumDecl, enumClass, stringValuesArrayIdent);
317 
318     CreateEnumValuesMethod(enumDecl, enumClass, itemsArrayIdent);
319 
320     CreateEnumFromIntMethod(enumDecl, enumClass, itemsArrayIdent, checker::ETSEnumType::FROM_INT_METHOD_NAME,
321                             enumDecl->Key()->Name());
322 
323     CreateUnboxingMethod(enumDecl, enumClass, itemsArrayIdent);
324 
325     CreateEnumFromIntMethod(enumDecl, enumClass, boxedItemsArrayIdent, checker::ETSEnumType::BOXED_FROM_INT_METHOD_NAME,
326                             GetEnumClassName(checker_, enumDecl).View());
327 
328     if (enumDecl->Parent() != nullptr && enumDecl->Parent()->IsBlockStatement()) {
329         auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, NearestScope(enumDecl->Parent()));
330         InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
331     } else {
332         auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, program_->GlobalScope());
333         InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
334         auto *ident = enumClass->Ident();
335         auto *var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::BINDINGS);
336         ident->SetVariable(var);
337     }
338 }
339 
Perform(public_lib::Context * ctx,parser::Program * program)340 bool EnumLoweringPhase::Perform(public_lib::Context *ctx, parser::Program *program)
341 {
342     bool isPerformedSuccess = true;
343     if (program->Extension() != ScriptExtension::ETS) {
344         return isPerformedSuccess;
345     }
346 
347     for (auto &[_, extPrograms] : program->ExternalSources()) {
348         (void)_;
349         for (auto *extProg : extPrograms) {
350             isPerformedSuccess &= Perform(ctx, extProg);
351         }
352     }
353 
354     checker_ = ctx->checker->AsETSChecker();
355     varbinder_ = ctx->parserProgram->VarBinder()->AsETSBinder();
356     program_ = program;
357     program->Ast()->IterateRecursively([this, &isPerformedSuccess](ir::AstNode *ast) -> void {
358         if (ast->IsTSEnumDeclaration()) {
359             auto *enumDecl = ast->AsTSEnumDeclaration();
360 
361             if (auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init();
362                 itemInit->IsNumberLiteral()) {
363                 CreateEnumIntClassFromEnumDeclaration(enumDecl);
364             } else if (itemInit->IsStringLiteral()) {
365                 CreateEnumStringClassFromEnumDeclaration(enumDecl);
366             } else {
367                 checker_->LogTypeError("Invalid enumeration value type.", enumDecl->Start());
368                 isPerformedSuccess = false;
369             }
370         }
371     });
372     return isPerformedSuccess;
373 }
374 
CreateEnumValuesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)375 ir::Identifier *EnumLoweringPhase::CreateEnumValuesArray(const ir::TSEnumDeclaration *const enumDecl,
376                                                          ir::ClassDefinition *const enumClass)
377 {
378     auto *const intType = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
379     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(intType);
380     // clang-format off
381     return MakeArray(enumDecl, enumClass, "ValuesArray", arrayTypeAnnotation,
382                      [this](const ir::TSEnumMember *const member) {
383                         auto *const enumValueLiteral = checker_->AllocNode<ir::NumberLiteral>(
384                             lexer::Number(member->AsTSEnumMember()
385                                                 ->Init()
386                                                 ->AsNumberLiteral()
387                                                 ->Number()
388                                                 .GetValue<checker::ETSIntEnumType::ValueType>()));
389                         return enumValueLiteral;
390                     });
391     // clang-format on
392 }
393 
CreateEnumStringValuesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)394 ir::Identifier *EnumLoweringPhase::CreateEnumStringValuesArray(const ir::TSEnumDeclaration *const enumDecl,
395                                                                ir::ClassDefinition *const enumClass)
396 {
397     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
398     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(stringTypeAnnotation);
399 
400     // clang-format off
401     return MakeArray(enumDecl, enumClass, "StringValuesArray", arrayTypeAnnotation,
402                      [this](const ir::TSEnumMember *const member) {
403                         auto *const init = member->AsTSEnumMember()->Init();
404                         util::StringView stringValue;
405 
406                         if (init->IsStringLiteral()) {
407                             stringValue = init->AsStringLiteral()->Str();
408                         } else {
409                             auto str = std::to_string(
410                                 init->AsNumberLiteral()->Number().GetValue<checker::ETSIntEnumType::ValueType>());
411                             stringValue = util::UString(str, Allocator()).View();
412                         }
413 
414                         auto *const enumValueStringLiteral = checker_->AllocNode<ir::StringLiteral>(stringValue);
415                         return enumValueStringLiteral;
416                     });
417     // clang-format on
418 }
419 
CreateEnumItemsArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)420 ir::Identifier *EnumLoweringPhase::CreateEnumItemsArray(const ir::TSEnumDeclaration *const enumDecl,
421                                                         ir::ClassDefinition *const enumClass)
422 {
423     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
424     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(enumTypeAnnotation);
425     // clang-format off
426     return MakeArray(enumDecl, enumClass, "ItemsArray", arrayTypeAnnotation,
427                      [this, enumDecl](const ir::TSEnumMember *const member) {
428                         auto *const enumTypeIdent =
429                             checker_->AllocNode<ir::Identifier>(enumDecl->Key()->Name(), Allocator());
430 
431                         auto *const enumMemberIdent = checker_->AllocNode<ir::Identifier>(
432                             member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), Allocator());
433                         auto *const enumMemberExpr = checker_->AllocNode<ir::MemberExpression>(
434                             enumTypeIdent, enumMemberIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
435                         return enumMemberExpr;
436                     });
437     // clang-format on
438 }
439 
CreateBoxedEnumItemsArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)440 ir::Identifier *EnumLoweringPhase::CreateBoxedEnumItemsArray(const ir::TSEnumDeclaration *const enumDecl,
441                                                              ir::ClassDefinition *const enumClass)
442 {
443     auto boxedClassName = GetEnumClassName(checker_, enumDecl).View();
444     auto *const enumTypeAnnotation = MakeTypeReference(checker_, boxedClassName);
445     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(enumTypeAnnotation);
446     // clang-format off
447     return MakeArray(enumDecl, enumClass, "BoxedItemsArray", arrayTypeAnnotation,
448                      [this, enumDecl, &boxedClassName](const ir::TSEnumMember *const member) {
449                         auto *const enumTypeIdent =
450                             checker_->AllocNode<ir::Identifier>(enumDecl->Key()->Name(), Allocator());
451 
452                         auto *const enumMemberIdent = checker_->AllocNode<ir::Identifier>(
453                             member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), Allocator());
454                         auto *const enumMemberExpr = checker_->AllocNode<ir::MemberExpression>(
455                             enumTypeIdent, enumMemberIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
456 
457                         auto intType = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
458                         auto asExpression = checker_->AllocNode<ir::TSAsExpression>(enumMemberExpr, intType, false);
459 
460                         ArenaVector<ir::Expression *> newExprArgs(Allocator()->Adapter());
461                         newExprArgs.push_back(asExpression);
462 
463                         auto boxedTypeRef = MakeTypeReference(checker_, boxedClassName);
464 
465                         auto *const newExpression = checker_->AllocNode<ir::ETSNewClassInstanceExpression>(
466                             boxedTypeRef, std::move(newExprArgs), nullptr);
467                         return newExpression;
468                     });
469     // clang-format on
470 }
471 
472 namespace {
473 
CreateStaticAccessMemberExpression(checker::ETSChecker * checker,ir::Identifier * const enumClassIdentifier,ir::Identifier * const arrayIdentifier)474 ir::MemberExpression *CreateStaticAccessMemberExpression(checker::ETSChecker *checker,
475                                                          ir::Identifier *const enumClassIdentifier,
476                                                          ir::Identifier *const arrayIdentifier)
477 {
478     auto *const enumClassIdentifierClone = enumClassIdentifier->Clone(checker->Allocator(), nullptr);
479     auto *const arrayIdentifierClone = arrayIdentifier->Clone(checker->Allocator(), nullptr);
480 
481     return checker->AllocNode<ir::MemberExpression>(enumClassIdentifierClone, arrayIdentifierClone,
482                                                     ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
483 }
484 
CreateIfTest(checker::ETSChecker * checker,ir::Identifier * const enumClassIdentifier,ir::Identifier * const itemsArrayIdentifier,ir::ETSParameterExpression * const parameter)485 ir::BinaryExpression *CreateIfTest(checker::ETSChecker *checker, ir::Identifier *const enumClassIdentifier,
486                                    ir::Identifier *const itemsArrayIdentifier,
487                                    ir::ETSParameterExpression *const parameter)
488 {
489     auto *const lengthIdent = checker->AllocNode<ir::Identifier>("length", checker->Allocator());
490     auto *const propertyAccessExpr =
491         CreateStaticAccessMemberExpression(checker, enumClassIdentifier, itemsArrayIdentifier);
492     auto *const valuesArrayLengthExpr = checker->AllocNode<ir::MemberExpression>(
493         propertyAccessExpr, lengthIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
494     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
495     auto *const expr = checker->AllocNode<ir::BinaryExpression>(paramRefIdent, valuesArrayLengthExpr,
496                                                                 lexer::TokenType::PUNCTUATOR_LESS_THAN);
497     paramRefIdent->SetParent(expr);
498     return expr;
499 }
CreateReturnEnumStatement(checker::ETSChecker * checker,ir::Identifier * const enumClassIdentifier,ir::Identifier * const itemsArrayIdentifier,ir::ETSParameterExpression * const parameter)500 ir::ReturnStatement *CreateReturnEnumStatement(checker::ETSChecker *checker, ir::Identifier *const enumClassIdentifier,
501                                                ir::Identifier *const itemsArrayIdentifier,
502                                                ir::ETSParameterExpression *const parameter)
503 {
504     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
505     auto *const propertyAccessExpr =
506         CreateStaticAccessMemberExpression(checker, enumClassIdentifier, itemsArrayIdentifier);
507     auto *const arrayAccessExpr = checker->AllocNode<ir::MemberExpression>(
508         propertyAccessExpr, paramRefIdent, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
509     auto *const returnStatement = checker->AllocNode<ir::ReturnStatement>(arrayAccessExpr);
510     return returnStatement;
511 }
512 
CreateThrowStatement(checker::ETSChecker * checker,ir::ETSParameterExpression * const parameter,const util::UString & messageString)513 ir::ThrowStatement *CreateThrowStatement(checker::ETSChecker *checker, ir::ETSParameterExpression *const parameter,
514                                          const util::UString &messageString)
515 {
516     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
517     auto *const message = checker->AllocNode<ir::StringLiteral>(messageString.View());
518     auto *const newExprArg =
519         checker->AllocNode<ir::BinaryExpression>(message, paramRefIdent, lexer::TokenType::PUNCTUATOR_PLUS);
520 
521     paramRefIdent->SetParent(newExprArg);
522     ArenaVector<ir::Expression *> newExprArgs(checker->Allocator()->Adapter());
523     newExprArgs.push_back(newExprArg);
524 
525     auto *const exceptionReference = MakeTypeReference(checker, "Exception");
526     auto *const newExpr =
527         checker->AllocNode<ir::ETSNewClassInstanceExpression>(exceptionReference, std::move(newExprArgs), nullptr);
528     return checker->AllocNode<ir::ThrowStatement>(newExpr);
529 }
530 
CreateReturnWitAsStatement(checker::ETSChecker * checker,ir::Identifier * const enumClassIdentifier,ir::Identifier * const arrayIdentifier,ir::ETSParameterExpression * const parameter)531 ir::ReturnStatement *CreateReturnWitAsStatement(checker::ETSChecker *checker, ir::Identifier *const enumClassIdentifier,
532                                                 ir::Identifier *const arrayIdentifier,
533                                                 ir::ETSParameterExpression *const parameter)
534 {
535     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
536     auto intType = checker->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
537     auto asExpression = checker->AllocNode<ir::TSAsExpression>(paramRefIdent, intType, false);
538     paramRefIdent->SetParent(asExpression);
539 
540     auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(checker, enumClassIdentifier, arrayIdentifier);
541 
542     auto *const arrayAccessExpr = checker->AllocNode<ir::MemberExpression>(
543         propertyAccessExpr, asExpression, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
544 
545     return checker->AllocNode<ir::ReturnStatement>(arrayAccessExpr);
546 }
547 
548 }  // namespace
549 
CreateEnumFromIntMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const arrayIdent,const util::StringView & methodName,const util::StringView & returnTypeName)550 void EnumLoweringPhase::CreateEnumFromIntMethod(const ir::TSEnumDeclaration *const enumDecl,
551                                                 ir::ClassDefinition *const enumClass, ir::Identifier *const arrayIdent,
552                                                 const util::StringView &methodName,
553                                                 const util::StringView &returnTypeName)
554 {
555     auto *const intTypeAnnotation = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
556     auto *const inputOrdinalParameter = MakeFunctionParam(checker_, "ordinal", intTypeAnnotation);
557     auto *const inArraySizeExpr = CreateIfTest(checker_, enumClass->Ident(), arrayIdent, inputOrdinalParameter);
558     auto *const returnEnumStmt =
559         CreateReturnEnumStatement(checker_, enumClass->Ident(), arrayIdent, inputOrdinalParameter);
560     auto *const ifOrdinalExistsStmt = checker_->AllocNode<ir::IfStatement>(inArraySizeExpr, returnEnumStmt, nullptr);
561 
562     auto const messageString = util::UString(std::string_view("No enum constant in "), Allocator())
563                                    .Append(enumDecl->Key()->Name())
564                                    .Append(" with ordinal value ");
565 
566     auto *const throwNoEnumStmt = CreateThrowStatement(checker_, inputOrdinalParameter, messageString);
567 
568     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
569     params.push_back(inputOrdinalParameter);
570 
571     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
572     body.push_back(ifOrdinalExistsStmt);
573     body.push_back(throwNoEnumStmt);
574     auto *const returnTypeAnnotation = MakeTypeReference(checker_, returnTypeName);
575 
576     auto *const function = MakeFunction({std::move(params), std::move(body), returnTypeAnnotation, enumDecl,
577                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
578     function->AddFlag(ir::ScriptFunctionFlags::THROWS);
579     auto *const ident = checker_->AllocNode<ir::Identifier>(methodName, Allocator());
580 
581     function->SetIdent(ident);
582 
583     MakeMethodDef(checker_, enumClass, ident, function);
584 }
585 
CreateEnumToStringMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const stringValuesArrayIdent)586 void EnumLoweringPhase::CreateEnumToStringMethod(const ir::TSEnumDeclaration *const enumDecl,
587                                                  ir::ClassDefinition *const enumClass,
588                                                  ir::Identifier *const stringValuesArrayIdent)
589 {
590     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
591     auto *const inputEnumIdent = MakeFunctionParam(checker_, "ordinal", enumTypeAnnotation);
592     auto *const returnStmt =
593         CreateReturnWitAsStatement(checker_, enumClass->Ident(), stringValuesArrayIdent, inputEnumIdent);
594 
595     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
596     body.push_back(returnStmt);
597 
598     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
599     params.push_back(inputEnumIdent);
600     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
601     auto *const function = MakeFunction({std::move(params), std::move(body), stringTypeAnnotation, enumDecl,
602                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
603 
604     auto *const functionIdent =
605         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::TO_STRING_METHOD_NAME, Allocator());
606 
607     function->SetIdent(functionIdent);
608     MakeMethodDef(checker_, enumClass, functionIdent, function);
609 }
610 
CreateEnumValueOfMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const valuesArrayIdent)611 void EnumLoweringPhase::CreateEnumValueOfMethod(const ir::TSEnumDeclaration *const enumDecl,
612                                                 ir::ClassDefinition *const enumClass,
613                                                 ir::Identifier *const valuesArrayIdent)
614 {
615     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
616     auto *const inputEnumIdent = MakeFunctionParam(checker_, "e", enumTypeAnnotation);
617     auto *const returnStmt = CreateReturnWitAsStatement(checker_, enumClass->Ident(), valuesArrayIdent, inputEnumIdent);
618 
619     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
620     body.push_back(returnStmt);
621 
622     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
623     params.push_back(inputEnumIdent);
624     auto *const intTypeAnnotation = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
625     auto *const function = MakeFunction({std::move(params), std::move(body), intTypeAnnotation, enumDecl,
626                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
627     auto *const functionIdent =
628         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::VALUE_OF_METHOD_NAME, Allocator());
629     function->SetIdent(functionIdent);
630 
631     MakeMethodDef(checker_, enumClass, functionIdent, function);
632 }
633 
CreateEnumGetNameMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const namesArrayIdent)634 void EnumLoweringPhase::CreateEnumGetNameMethod(const ir::TSEnumDeclaration *const enumDecl,
635                                                 ir::ClassDefinition *const enumClass,
636                                                 ir::Identifier *const namesArrayIdent)
637 {
638     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
639     auto *const inputEnumIdent = MakeFunctionParam(checker_, "ordinal", enumTypeAnnotation);
640     auto *const returnStmt = CreateReturnWitAsStatement(checker_, enumClass->Ident(), namesArrayIdent, inputEnumIdent);
641 
642     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
643     body.push_back(returnStmt);
644 
645     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
646     params.push_back(inputEnumIdent);
647     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
648 
649     auto *const function = MakeFunction({std::move(params), std::move(body), stringTypeAnnotation, enumDecl,
650                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
651     auto *const functionIdent =
652         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::GET_NAME_METHOD_NAME, Allocator());
653 
654     function->SetIdent(functionIdent);
655 
656     MakeMethodDef(checker_, enumClass, functionIdent, function);
657 }
658 
659 namespace {
660 
CreateForLoopInitVariableDeclaration(checker::ETSChecker * checker,ir::Identifier * const loopIdentifier)661 ir::VariableDeclaration *CreateForLoopInitVariableDeclaration(checker::ETSChecker *checker,
662                                                               ir::Identifier *const loopIdentifier)
663 {
664     auto *const init = checker->AllocNode<ir::NumberLiteral>("0");
665     auto *const decl =
666         checker->AllocNode<ir::VariableDeclarator>(ir::VariableDeclaratorFlag::LET, loopIdentifier, init);
667     loopIdentifier->SetParent(decl);
668     ArenaVector<ir::VariableDeclarator *> decls(checker->Allocator()->Adapter());
669     decls.push_back(decl);
670     auto *const declaration = checker->AllocNode<ir::VariableDeclaration>(
671         ir::VariableDeclaration::VariableDeclarationKind::LET, checker->Allocator(), std::move(decls));
672     decl->SetParent(declaration);
673     return declaration;
674 }
675 
CreateForLoopTest(checker::ETSChecker * checker,ir::Identifier * const enumClassIdentifier,ir::Identifier * const namesArrayIdentifier,ir::Identifier * const loopIdentifier)676 ir::BinaryExpression *CreateForLoopTest(checker::ETSChecker *checker, ir::Identifier *const enumClassIdentifier,
677                                         ir::Identifier *const namesArrayIdentifier,
678                                         ir::Identifier *const loopIdentifier)
679 {
680     auto *const lengthIdent = checker->AllocNode<ir::Identifier>("length", checker->Allocator());
681     auto *const propertyAccessExpr =
682         CreateStaticAccessMemberExpression(checker, enumClassIdentifier, namesArrayIdentifier);
683     auto *const arrayLengthExpr = checker->AllocNode<ir::MemberExpression>(
684         propertyAccessExpr, lengthIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
685     auto *const forLoopIdentClone = loopIdentifier->Clone(checker->Allocator(), nullptr);
686     auto *const binaryExpr = checker->AllocNode<ir::BinaryExpression>(forLoopIdentClone, arrayLengthExpr,
687                                                                       lexer::TokenType::PUNCTUATOR_LESS_THAN);
688     return binaryExpr;
689 }
690 
CreateForLoopUpdate(checker::ETSChecker * checker,ir::Identifier * const loopIdentifier)691 ir::UpdateExpression *CreateForLoopUpdate(checker::ETSChecker *checker, ir::Identifier *const loopIdentifier)
692 {
693     auto *const forLoopIdentClone = loopIdentifier->Clone(checker->Allocator(), nullptr);
694     auto *const incrementExpr =
695         checker->AllocNode<ir::UpdateExpression>(forLoopIdentClone, lexer::TokenType::PUNCTUATOR_PLUS_PLUS, true);
696     return incrementExpr;
697 }
698 
CreateIf(checker::ETSChecker * checker,const ir::TSEnumDeclaration * const enumDecl,ir::MemberExpression * propertyAccessExpr,ir::Identifier * const loopIdentifier,ir::ETSParameterExpression * const parameter)699 ir::IfStatement *CreateIf(checker::ETSChecker *checker, const ir::TSEnumDeclaration *const enumDecl,
700                           ir::MemberExpression *propertyAccessExpr, ir::Identifier *const loopIdentifier,
701                           ir::ETSParameterExpression *const parameter)
702 {
703     auto *const forLoopIdentClone1 = loopIdentifier->Clone(checker->Allocator(), nullptr);
704     auto *const namesArrayElementExpr = checker->AllocNode<ir::MemberExpression>(
705         propertyAccessExpr, forLoopIdentClone1, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
706 
707     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
708     auto *const namesEqualExpr = checker->AllocNode<ir::BinaryExpression>(paramRefIdent, namesArrayElementExpr,
709                                                                           lexer::TokenType::PUNCTUATOR_EQUAL);
710     paramRefIdent->SetParent(namesEqualExpr);
711     auto *const forLoopIdentClone2 = loopIdentifier->Clone(checker->Allocator(), nullptr);
712     auto *const enumTypeAnnotation = MakeTypeReference(checker, enumDecl->Key()->Name());
713     auto asExpression = checker->AllocNode<ir::TSAsExpression>(forLoopIdentClone2, enumTypeAnnotation, false);
714 
715     auto *const returnStmt = checker->AllocNode<ir::ReturnStatement>(asExpression);
716     return checker->AllocNode<ir::IfStatement>(namesEqualExpr, returnStmt, nullptr);
717 }
718 
719 }  // namespace
720 
CreateEnumGetValueOfMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const namesArrayIdent)721 void EnumLoweringPhase::CreateEnumGetValueOfMethod(const ir::TSEnumDeclaration *const enumDecl,
722                                                    ir::ClassDefinition *const enumClass,
723                                                    ir::Identifier *const namesArrayIdent)
724 {
725     auto *const forLoopIIdent = checker_->AllocNode<ir::Identifier>("i", checker_->Allocator());
726     auto *const forLoopInitVarDecl = CreateForLoopInitVariableDeclaration(checker_, forLoopIIdent);
727     auto *const forLoopTest = CreateForLoopTest(checker_, enumClass->Ident(), namesArrayIdent, forLoopIIdent);
728     auto *const forLoopUpdate = CreateForLoopUpdate(checker_, forLoopIIdent);
729     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
730     auto *const inputNameIdent = MakeFunctionParam(checker_, "name", stringTypeAnnotation);
731     auto *const ifStmt =
732         CreateIf(checker_, enumDecl, CreateStaticAccessMemberExpression(checker_, enumClass->Ident(), namesArrayIdent),
733                  forLoopIIdent, inputNameIdent);
734 
735     auto *const forLoop =
736         checker_->AllocNode<ir::ForUpdateStatement>(forLoopInitVarDecl, forLoopTest, forLoopUpdate, ifStmt);
737 
738     util::UString messageString(util::StringView("No enum constant "), Allocator());
739     messageString.Append(enumDecl->Key()->Name());
740     messageString.Append('.');
741 
742     [[maybe_unused]] auto *const throwStmt = CreateThrowStatement(checker_, inputNameIdent, messageString);
743 
744     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
745     body.push_back(forLoop);
746     body.push_back(throwStmt);
747 
748     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
749     params.push_back(inputNameIdent);
750     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
751 
752     auto *const function = MakeFunction({std::move(params), std::move(body), enumTypeAnnotation, enumDecl,
753                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
754     function->AddFlag(ir::ScriptFunctionFlags::THROWS);
755     auto *const functionIdent =
756         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::GET_VALUE_OF_METHOD_NAME, Allocator());
757 
758     function->SetIdent(functionIdent);
759     MakeMethodDef(checker_, enumClass, functionIdent, function);
760 }
761 
CreateEnumValuesMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const itemsArrayIdent)762 void EnumLoweringPhase::CreateEnumValuesMethod(const ir::TSEnumDeclaration *const enumDecl,
763                                                ir::ClassDefinition *const enumClass,
764                                                ir::Identifier *const itemsArrayIdent)
765 {
766     auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(checker_, enumClass->Ident(), itemsArrayIdent);
767     auto *const returnStmt = checker_->AllocNode<ir::ReturnStatement>(propertyAccessExpr);
768     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
769     body.push_back(returnStmt);
770 
771     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
772     auto *const enumArrayTypeAnnotation =
773         checker_->AllocNode<ir::TSArrayType>(MakeTypeReference(checker_, enumDecl->Key()->Name()));
774 
775     auto *const function = MakeFunction({std::move(params), std::move(body), enumArrayTypeAnnotation, enumDecl,
776                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
777     auto *const functionIdent =
778         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::VALUES_METHOD_NAME, Allocator());
779     function->SetIdent(functionIdent);
780 
781     MakeMethodDef(checker_, enumClass, functionIdent, function);
782 }
783 
CreateUnboxingMethod(ir::TSEnumDeclaration const * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const itemsArrayIdent)784 void EnumLoweringPhase::CreateUnboxingMethod(ir::TSEnumDeclaration const *const enumDecl,
785                                              ir::ClassDefinition *const enumClass,
786                                              ir::Identifier *const itemsArrayIdent)
787 
788 {
789     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
790 
791     auto *thisExpr = Allocator()->New<ir::ThisExpression>();
792     auto *fieldIdentifier = Allocator()->New<ir::Identifier>("ordinal", Allocator());
793     auto *arrayIndexExpr = checker_->AllocNode<ir::MemberExpression>(
794         thisExpr, fieldIdentifier, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
795 
796     auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(checker_, enumClass->Ident(), itemsArrayIdent);
797     auto *const arrayAccessExpr = checker_->AllocNode<ir::MemberExpression>(
798         propertyAccessExpr, arrayIndexExpr, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
799 
800     auto *const returnStmt = checker_->AllocNode<ir::ReturnStatement>(arrayAccessExpr);
801     body.push_back(returnStmt);
802 
803     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
804 
805     auto *const returnTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
806 
807     auto *const function =
808         MakeFunction({std::move(params), std::move(body), returnTypeAnnotation, enumDecl, ir::ModifierFlags::PUBLIC});
809 
810     auto *const functionIdent =
811         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::UNBOX_METHOD_NAME, Allocator());
812     function->SetIdent(functionIdent);
813 
814     MakeMethodDef(checker_, enumClass, functionIdent, function);
815 }
816 
Allocator()817 ArenaAllocator *EnumLoweringPhase::Allocator()
818 {
819     return checker_->Allocator();
820 }
821 
822 }  // namespace ark::es2panda::compiler
823