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