• 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 "varbinder/ETSBinder.h"
21 #include "varbinder/variable.h"
22 
23 namespace ark::es2panda::compiler {
24 
25 namespace {
26 
MakeFunctionParam(checker::ETSChecker * const checker,varbinder::ETSBinder * const varbinder,varbinder::FunctionParamScope * const scope,const util::StringView & name,ir::TypeNode * const typeAnnotation)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 
MakeParamRefIdent(checker::ETSChecker * const checker,ir::ETSParameterExpression * paramExpr)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 
MakeTypeReference(checker::ETSChecker * const checker,const util::StringView & name)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 
MakeMethodDef(checker::ETSChecker * const checker,ir::ClassDefinition * enumClass,varbinder::ETSBinder * const varbinder,ir::Identifier * const ident,ir::ScriptFunction * const function)57 ir::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 
MakeFunction(FunctionInfo && functionInfo)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 
GetEnumClassName(checker::ETSChecker * checker,const ir::TSEnumDeclaration * const enumDecl)120 util::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 
128 template <typename ElementMaker>
MakeArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,const util::StringView & name,ir::TypeNode * const typeAnnotation,ElementMaker && 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 
CreateEnumNamesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)160 ir::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 
CreateClass(ir::TSEnumDeclaration * const enumDecl)176 ir::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 
CreateCCtorForEnumClass(ir::ClassDefinition * const enumClass)204 void 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 
CreateOrdinalField(ir::ClassDefinition * const enumClass)251 ir::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 
CreateCtorForEnumClass(ir::ClassDefinition * const enumClass)272 void 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 
CreateEnumIntClassFromEnumDeclaration(ir::TSEnumDeclaration * const enumDecl)333 void 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 
CreateEnumStringClassFromEnumDeclaration(ir::TSEnumDeclaration * const enumDecl)369 void 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 
Perform(public_lib::Context * ctx,parser::Program * program)401 bool EnumLoweringPhase::Perform(public_lib::Context *ctx, parser::Program *program)
402 {
403     if (program->Extension() != ScriptExtension::ETS) {
404         return true;
405     }
406 
407     for (auto &[_, extPrograms] : program->ExternalSources()) {
408         (void)_;
409         for (auto *extProg : extPrograms) {
410             Perform(ctx, extProg);
411         }
412     }
413 
414     checker_ = ctx->checker->AsETSChecker();
415     varbinder_ = ctx->parserProgram->VarBinder()->AsETSBinder();
416     program_ = program;
417     program->Ast()->IterateRecursively([this](ir::AstNode *ast) -> void {
418         if (ast->IsTSEnumDeclaration()) {
419             auto *enumDecl = ast->AsTSEnumDeclaration();
420 
421             if (auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init();
422                 itemInit->IsNumberLiteral()) {
423                 CreateEnumIntClassFromEnumDeclaration(enumDecl);
424             } else if (itemInit->IsStringLiteral()) {
425                 CreateEnumStringClassFromEnumDeclaration(enumDecl);
426             } else {
427                 checker_->ThrowTypeError("Invalid enumeration value type.", enumDecl->Start());
428             }
429         }
430     });
431     return true;
432 }
433 
CreateEnumValuesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)434 ir::Identifier *EnumLoweringPhase::CreateEnumValuesArray(const ir::TSEnumDeclaration *const enumDecl,
435                                                          ir::ClassDefinition *const enumClass)
436 {
437     auto *const intType = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
438     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(intType);
439     // clang-format off
440     return MakeArray(enumDecl, enumClass, "ValuesArray", arrayTypeAnnotation,
441                      [this](const ir::TSEnumMember *const member) {
442                         auto *const enumValueLiteral = checker_->AllocNode<ir::NumberLiteral>(
443                             lexer::Number(member->AsTSEnumMember()
444                                                 ->Init()
445                                                 ->AsNumberLiteral()
446                                                 ->Number()
447                                                 .GetValue<checker::ETSIntEnumType::ValueType>()));
448                         return enumValueLiteral;
449                     });
450     // clang-format on
451 }
452 
CreateEnumStringValuesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)453 ir::Identifier *EnumLoweringPhase::CreateEnumStringValuesArray(const ir::TSEnumDeclaration *const enumDecl,
454                                                                ir::ClassDefinition *const enumClass)
455 {
456     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
457     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(stringTypeAnnotation);
458 
459     // clang-format off
460     return MakeArray(enumDecl, enumClass, "StringValuesArray", arrayTypeAnnotation,
461                      [this](const ir::TSEnumMember *const member) {
462                         auto *const init = member->AsTSEnumMember()->Init();
463                         util::StringView stringValue;
464 
465                         if (init->IsStringLiteral()) {
466                             stringValue = init->AsStringLiteral()->Str();
467                         } else {
468                             auto str = std::to_string(
469                                 init->AsNumberLiteral()->Number().GetValue<checker::ETSIntEnumType::ValueType>());
470                             stringValue = util::UString(str, Allocator()).View();
471                         }
472 
473                         auto *const enumValueStringLiteral = checker_->AllocNode<ir::StringLiteral>(stringValue);
474                         return enumValueStringLiteral;
475                     });
476     // clang-format on
477 }
478 
CreateEnumItemsArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)479 ir::Identifier *EnumLoweringPhase::CreateEnumItemsArray(const ir::TSEnumDeclaration *const enumDecl,
480                                                         ir::ClassDefinition *const enumClass)
481 {
482     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
483     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(enumTypeAnnotation);
484     // clang-format off
485     return MakeArray(enumDecl, enumClass, "ItemsArray", arrayTypeAnnotation,
486                      [this, enumDecl](const ir::TSEnumMember *const member) {
487                         auto *const enumTypeIdent =
488                             checker_->AllocNode<ir::Identifier>(enumDecl->Key()->Name(), Allocator());
489                         enumTypeIdent->SetReference();
490 
491                         auto *const enumMemberIdent = checker_->AllocNode<ir::Identifier>(
492                             member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), Allocator());
493                         enumMemberIdent->SetReference();
494                         auto *const enumMemberExpr = checker_->AllocNode<ir::MemberExpression>(
495                             enumTypeIdent, enumMemberIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
496                         return enumMemberExpr;
497                     });
498     // clang-format on
499 }
500 
CreateBoxedEnumItemsArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)501 ir::Identifier *EnumLoweringPhase::CreateBoxedEnumItemsArray(const ir::TSEnumDeclaration *const enumDecl,
502                                                              ir::ClassDefinition *const enumClass)
503 {
504     auto boxedClassName = GetEnumClassName(checker_, enumDecl).View();
505     auto *const enumTypeAnnotation = MakeTypeReference(checker_, boxedClassName);
506     auto *const arrayTypeAnnotation = checker_->AllocNode<ir::TSArrayType>(enumTypeAnnotation);
507     // clang-format off
508     return MakeArray(enumDecl, enumClass, "BoxedItemsArray", arrayTypeAnnotation,
509                      [this, enumDecl, &boxedClassName](const ir::TSEnumMember *const member) {
510                         auto *const enumTypeIdent =
511                             checker_->AllocNode<ir::Identifier>(enumDecl->Key()->Name(), Allocator());
512                         enumTypeIdent->SetReference();
513 
514                         auto *const enumMemberIdent = checker_->AllocNode<ir::Identifier>(
515                             member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), Allocator());
516                         enumMemberIdent->SetReference();
517                         auto *const enumMemberExpr = checker_->AllocNode<ir::MemberExpression>(
518                             enumTypeIdent, enumMemberIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
519 
520                         auto intType = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
521                         auto asExpression = checker_->AllocNode<ir::TSAsExpression>(enumMemberExpr, intType, false);
522 
523                         ArenaVector<ir::Expression *> newExprArgs(Allocator()->Adapter());
524                         newExprArgs.push_back(asExpression);
525 
526                         auto boxedTypeRef = MakeTypeReference(checker_, boxedClassName);
527 
528                         auto *const newExpression = checker_->AllocNode<ir::ETSNewClassInstanceExpression>(
529                             boxedTypeRef, std::move(newExprArgs), nullptr);
530                         return newExpression;
531                     });
532     // clang-format on
533 }
534 
535 namespace {
536 
CreateIfTest(EnumLoweringPhase * const elp,ir::Identifier * const itemsArrayIdentifier,ir::ETSParameterExpression * const parameter)537 ir::BinaryExpression *CreateIfTest(EnumLoweringPhase *const elp, ir::Identifier *const itemsArrayIdentifier,
538                                    ir::ETSParameterExpression *const parameter)
539 {
540     auto *const checker = elp->Checker();
541     auto *const lengthIdent = checker->AllocNode<ir::Identifier>("length", checker->Allocator());
542     lengthIdent->SetReference();
543     auto *const valuesArrayLengthExpr = checker->AllocNode<ir::MemberExpression>(
544         itemsArrayIdentifier, lengthIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
545     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
546     auto *const expr = checker->AllocNode<ir::BinaryExpression>(paramRefIdent, valuesArrayLengthExpr,
547                                                                 lexer::TokenType::PUNCTUATOR_LESS_THAN);
548     paramRefIdent->SetParent(expr);
549     return expr;
550 }
CreateReturnEnumStatement(EnumLoweringPhase * const elp,ir::Identifier * const itemsArrayIdentifier,ir::ETSParameterExpression * const parameter)551 ir::ReturnStatement *CreateReturnEnumStatement(EnumLoweringPhase *const elp, ir::Identifier *const itemsArrayIdentifier,
552                                                ir::ETSParameterExpression *const parameter)
553 {
554     auto *const checker = elp->Checker();
555     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
556     auto itemsArrayIdentClone = itemsArrayIdentifier->Clone(checker->Allocator(), nullptr);
557     auto *const arrayAccessExpr = checker->AllocNode<ir::MemberExpression>(
558         itemsArrayIdentClone, paramRefIdent, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
559     auto *const returnStatement = checker->AllocNode<ir::ReturnStatement>(arrayAccessExpr);
560     return returnStatement;
561 }
562 
CreateThrowStatement(EnumLoweringPhase * const elp,ir::ETSParameterExpression * const parameter,const util::UString & messageString)563 ir::ThrowStatement *CreateThrowStatement(EnumLoweringPhase *const elp, ir::ETSParameterExpression *const parameter,
564                                          const util::UString &messageString)
565 {
566     auto *const checker = elp->Checker();
567 
568     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
569     auto *const message = checker->AllocNode<ir::StringLiteral>(messageString.View());
570     auto *const newExprArg =
571         checker->AllocNode<ir::BinaryExpression>(message, paramRefIdent, lexer::TokenType::PUNCTUATOR_PLUS);
572 
573     paramRefIdent->SetParent(newExprArg);
574     ArenaVector<ir::Expression *> newExprArgs(checker->Allocator()->Adapter());
575     newExprArgs.push_back(newExprArg);
576 
577     auto *const exceptionReference = MakeTypeReference(checker, "Exception");
578     auto *const newExpr =
579         checker->AllocNode<ir::ETSNewClassInstanceExpression>(exceptionReference, std::move(newExprArgs), nullptr);
580     return checker->AllocNode<ir::ThrowStatement>(newExpr);
581 }
582 
CreateReturnWitAsStatement(EnumLoweringPhase * const elp,ir::Identifier * const arrayIdentifier,ir::ETSParameterExpression * const parameter)583 ir::ReturnStatement *CreateReturnWitAsStatement(EnumLoweringPhase *const elp, ir::Identifier *const arrayIdentifier,
584                                                 ir::ETSParameterExpression *const parameter)
585 {
586     auto *const checker = elp->Checker();
587     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
588     auto intType = checker->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
589     auto asExpression = checker->AllocNode<ir::TSAsExpression>(paramRefIdent, intType, false);
590     paramRefIdent->SetParent(asExpression);
591 
592     auto *const arrayAccessExpr = checker->AllocNode<ir::MemberExpression>(
593         arrayIdentifier, asExpression, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
594 
595     return checker->AllocNode<ir::ReturnStatement>(arrayAccessExpr);
596 }
597 
598 }  // namespace
599 
CreateEnumFromIntMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const arrayIdent,const util::StringView & methodName,const util::StringView & returnTypeName)600 void EnumLoweringPhase::CreateEnumFromIntMethod(const ir::TSEnumDeclaration *const enumDecl,
601                                                 ir::ClassDefinition *const enumClass, ir::Identifier *const arrayIdent,
602                                                 const util::StringView &methodName,
603                                                 const util::StringView &returnTypeName)
604 {
605     auto *const paramScope =
606         varbinder_->Allocator()->New<varbinder::FunctionParamScope>(Allocator(), enumClass->Scope());
607 
608     auto *const intTypeAnnotation = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
609     auto *const inputOrdinalParameter =
610         MakeFunctionParam(checker_, varbinder_, paramScope, "ordinal", intTypeAnnotation);
611     auto *const inArraySizeExpr = CreateIfTest(this, arrayIdent, inputOrdinalParameter);
612     auto *const returnEnumStmt = CreateReturnEnumStatement(this, arrayIdent, inputOrdinalParameter);
613     auto *const ifOrdinalExistsStmt = checker_->AllocNode<ir::IfStatement>(inArraySizeExpr, returnEnumStmt, nullptr);
614 
615     util::UString messageString(util::StringView("No enum constant in "), Allocator());
616     messageString.Append(enumDecl->Key()->Name());
617     messageString.Append(" with ordinal value ");
618 
619     auto *const throwNoEnumStmt = CreateThrowStatement(this, inputOrdinalParameter, messageString);
620 
621     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
622     params.push_back(inputOrdinalParameter);
623 
624     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
625     body.push_back(ifOrdinalExistsStmt);
626     body.push_back(throwNoEnumStmt);
627     auto *const returnTypeAnnotation = MakeTypeReference(checker_, returnTypeName);
628 
629     auto *const function = MakeFunction({paramScope, std::move(params), std::move(body), returnTypeAnnotation, enumDecl,
630                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
631     function->AddFlag(ir::ScriptFunctionFlags::THROWS);
632     auto *const ident = checker_->AllocNode<ir::Identifier>(methodName, Allocator());
633 
634     function->SetIdent(ident);
635     function->Scope()->BindInternalName(ident->Name());
636 
637     MakeMethodDef(checker_, enumClass, varbinder_, ident, function);
638     ident->SetReference();
639 }
640 
CreateEnumToStringMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const stringValuesArrayIdent)641 void EnumLoweringPhase::CreateEnumToStringMethod(const ir::TSEnumDeclaration *const enumDecl,
642                                                  ir::ClassDefinition *const enumClass,
643                                                  ir::Identifier *const stringValuesArrayIdent)
644 {
645     auto *const paramScope =
646         varbinder_->Allocator()->New<varbinder::FunctionParamScope>(Allocator(), enumClass->Scope());
647     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
648     auto *const inputEnumIdent = MakeFunctionParam(checker_, varbinder_, paramScope, "ordinal", enumTypeAnnotation);
649     auto *const returnStmt = CreateReturnWitAsStatement(this, stringValuesArrayIdent, inputEnumIdent);
650 
651     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
652     body.push_back(returnStmt);
653 
654     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
655     params.push_back(inputEnumIdent);
656     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
657     auto *const function = MakeFunction({paramScope, std::move(params), std::move(body), stringTypeAnnotation, enumDecl,
658                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
659 
660     auto *const functionIdent =
661         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::TO_STRING_METHOD_NAME, Allocator());
662 
663     function->SetIdent(functionIdent);
664     function->Scope()->BindInternalName(functionIdent->Name());
665     MakeMethodDef(checker_, enumClass, varbinder_, functionIdent, function);
666     functionIdent->SetReference();
667 }
668 
CreateEnumValueOfMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const valuesArrayIdent)669 void EnumLoweringPhase::CreateEnumValueOfMethod(const ir::TSEnumDeclaration *const enumDecl,
670                                                 ir::ClassDefinition *const enumClass,
671                                                 ir::Identifier *const valuesArrayIdent)
672 {
673     auto *const paramScope =
674         varbinder_->Allocator()->New<varbinder::FunctionParamScope>(Allocator(), enumClass->Scope());
675 
676     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
677     auto *const inputEnumIdent = MakeFunctionParam(checker_, varbinder_, paramScope, "e", enumTypeAnnotation);
678     auto *const returnStmt = CreateReturnWitAsStatement(this, valuesArrayIdent, inputEnumIdent);
679 
680     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
681     body.push_back(returnStmt);
682 
683     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
684     params.push_back(inputEnumIdent);
685     auto *const intTypeAnnotation = checker_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
686     auto *const function = MakeFunction({paramScope, std::move(params), std::move(body), intTypeAnnotation, enumDecl,
687                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
688     auto *const functionIdent =
689         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::VALUE_OF_METHOD_NAME, Allocator());
690     function->SetIdent(functionIdent);
691     function->Scope()->BindInternalName(functionIdent->Name());
692 
693     MakeMethodDef(checker_, enumClass, varbinder_, functionIdent, function);
694 
695     functionIdent->SetReference();
696 }
697 
CreateEnumGetNameMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const namesArrayIdent)698 void EnumLoweringPhase::CreateEnumGetNameMethod(const ir::TSEnumDeclaration *const enumDecl,
699                                                 ir::ClassDefinition *const enumClass,
700                                                 ir::Identifier *const namesArrayIdent)
701 {
702     auto *const paramScope =
703         varbinder_->Allocator()->New<varbinder::FunctionParamScope>(Allocator(), enumClass->Scope());
704 
705     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
706     auto *const inputEnumIdent = MakeFunctionParam(checker_, varbinder_, paramScope, "ordinal", enumTypeAnnotation);
707     auto *const returnStmt = CreateReturnWitAsStatement(this, namesArrayIdent, inputEnumIdent);
708 
709     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
710     body.push_back(returnStmt);
711 
712     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
713     params.push_back(inputEnumIdent);
714     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
715 
716     auto *const function = MakeFunction({paramScope, std::move(params), std::move(body), stringTypeAnnotation, enumDecl,
717                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
718     auto *const functionIdent =
719         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::GET_NAME_METHOD_NAME, Allocator());
720 
721     function->SetIdent(functionIdent);
722     function->Scope()->BindInternalName(functionIdent->Name());
723 
724     MakeMethodDef(checker_, enumClass, varbinder_, functionIdent, function);
725     functionIdent->SetReference();
726 }
727 
728 namespace {
729 
CreateForLoopIdent(EnumLoweringPhase * const elp)730 ir::Identifier *CreateForLoopIdent(EnumLoweringPhase *const elp)
731 {
732     auto *const ident = elp->Checker()->AllocNode<ir::Identifier>("i", elp->Checker()->Allocator());
733     auto [decl, var] = elp->Varbinder()->NewVarDecl<varbinder::LetDecl>(ident->Start(), ident->Name());
734     ident->SetVariable(var);
735     var->SetScope(elp->Varbinder()->GetScope());
736     var->AddFlag(varbinder::VariableFlags::LOCAL);
737     decl->BindNode(ident);
738     return ident;
739 }
740 
CreateForLoopInitVariableDeclaration(EnumLoweringPhase * const elp,ir::Identifier * const loopIdentifier)741 ir::VariableDeclaration *CreateForLoopInitVariableDeclaration(EnumLoweringPhase *const elp,
742                                                               ir::Identifier *const loopIdentifier)
743 {
744     auto *const checker = elp->Checker();
745     auto *const init = checker->AllocNode<ir::NumberLiteral>("0");
746     auto *const decl =
747         checker->AllocNode<ir::VariableDeclarator>(ir::VariableDeclaratorFlag::LET, loopIdentifier, init);
748     loopIdentifier->SetParent(decl);
749     ArenaVector<ir::VariableDeclarator *> decls(checker->Allocator()->Adapter());
750     decls.push_back(decl);
751     auto *const declaration = checker->AllocNode<ir::VariableDeclaration>(
752         ir::VariableDeclaration::VariableDeclarationKind::LET, checker->Allocator(), std::move(decls), false);
753     decl->SetParent(declaration);
754     return declaration;
755 }
756 
CreateForLoopTest(EnumLoweringPhase * const elp,ir::Identifier * const namesArrayIdentifier,ir::Identifier * const loopIdentifier)757 ir::BinaryExpression *CreateForLoopTest(EnumLoweringPhase *const elp, ir::Identifier *const namesArrayIdentifier,
758                                         ir::Identifier *const loopIdentifier)
759 {
760     auto *const checker = elp->Checker();
761     auto *const lengthIdent = checker->AllocNode<ir::Identifier>("length", checker->Allocator());
762     lengthIdent->SetReference();
763     auto *const arrayLengthExpr = checker->AllocNode<ir::MemberExpression>(
764         namesArrayIdentifier, lengthIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
765     auto *const forLoopIdentClone = loopIdentifier->Clone(checker->Allocator(), nullptr);
766     auto *const binaryExpr = checker->AllocNode<ir::BinaryExpression>(forLoopIdentClone, arrayLengthExpr,
767                                                                       lexer::TokenType::PUNCTUATOR_LESS_THAN);
768     return binaryExpr;
769 }
770 
CreateForLoopUpdate(EnumLoweringPhase * const elp,ir::Identifier * const loopIdentifier)771 ir::UpdateExpression *CreateForLoopUpdate(EnumLoweringPhase *const elp, ir::Identifier *const loopIdentifier)
772 {
773     auto *const checker = elp->Checker();
774     auto *const forLoopIdentClone = loopIdentifier->Clone(checker->Allocator(), nullptr);
775     auto *const incrementExpr =
776         checker->AllocNode<ir::UpdateExpression>(forLoopIdentClone, lexer::TokenType::PUNCTUATOR_PLUS_PLUS, true);
777     return incrementExpr;
778 }
779 
CreateIf(EnumLoweringPhase * const elp,const ir::TSEnumDeclaration * const enumDecl,ir::Identifier * const namesArrayIdentifier,ir::Identifier * const loopIdentifier,ir::ETSParameterExpression * const parameter)780 ir::IfStatement *CreateIf(EnumLoweringPhase *const elp, const ir::TSEnumDeclaration *const enumDecl,
781                           ir::Identifier *const namesArrayIdentifier, ir::Identifier *const loopIdentifier,
782                           ir::ETSParameterExpression *const parameter)
783 {
784     auto *const checker = elp->Checker();
785     auto *const identClone = namesArrayIdentifier->Clone(checker->Allocator(), nullptr);
786     auto *const forLoopIdentClone1 = loopIdentifier->Clone(checker->Allocator(), nullptr);
787     auto *const namesArrayElementExpr = checker->AllocNode<ir::MemberExpression>(
788         identClone, forLoopIdentClone1, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
789 
790     auto *const paramRefIdent = MakeParamRefIdent(checker, parameter);
791     auto *const namesEqualExpr = checker->AllocNode<ir::BinaryExpression>(paramRefIdent, namesArrayElementExpr,
792                                                                           lexer::TokenType::PUNCTUATOR_EQUAL);
793     paramRefIdent->SetParent(namesEqualExpr);
794     auto *const forLoopIdentClone2 = loopIdentifier->Clone(checker->Allocator(), nullptr);
795     auto *const enumTypeAnnotation = MakeTypeReference(checker, enumDecl->Key()->Name());
796     auto asExpression = checker->AllocNode<ir::TSAsExpression>(forLoopIdentClone2, enumTypeAnnotation, false);
797 
798     auto *const returnStmt = checker->AllocNode<ir::ReturnStatement>(asExpression);
799     return checker->AllocNode<ir::IfStatement>(namesEqualExpr, returnStmt, nullptr);
800 }
801 
802 }  // namespace
803 
CreateEnumGetValueOfMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const namesArrayIdent)804 void EnumLoweringPhase::CreateEnumGetValueOfMethod(const ir::TSEnumDeclaration *const enumDecl,
805                                                    ir::ClassDefinition *const enumClass,
806                                                    ir::Identifier *const namesArrayIdent)
807 {
808     auto *const paramScope =
809         varbinder_->Allocator()->New<varbinder::FunctionParamScope>(Allocator(), enumClass->Scope());
810 
811     varbinder::LexicalScope<varbinder::LoopDeclarationScope> loopDeclScope(varbinder_);
812 
813     auto *const forLoopIIdent = CreateForLoopIdent(this);
814     auto *const forLoopInitVarDecl = CreateForLoopInitVariableDeclaration(this, forLoopIIdent);
815     auto *const forLoopTest = CreateForLoopTest(this, namesArrayIdent, forLoopIIdent);
816     auto *const forLoopUpdate = CreateForLoopUpdate(this, forLoopIIdent);
817     auto *const stringTypeAnnotation = MakeTypeReference(checker_, "String");  // NOTE String -> Builtin?
818     auto *const inputNameIdent = MakeFunctionParam(checker_, varbinder_, paramScope, "name", stringTypeAnnotation);
819     auto *const ifStmt = CreateIf(this, enumDecl, namesArrayIdent, forLoopIIdent, inputNameIdent);
820 
821     varbinder::LexicalScope<varbinder::LoopScope> loopScope(varbinder_);
822     loopScope.GetScope()->BindDecls(loopDeclScope.GetScope());
823     auto *const forLoop =
824         checker_->AllocNode<ir::ForUpdateStatement>(forLoopInitVarDecl, forLoopTest, forLoopUpdate, ifStmt);
825     loopScope.GetScope()->BindNode(forLoop);
826     forLoop->SetScope(loopScope.GetScope());
827     loopScope.GetScope()->DeclScope()->BindNode(forLoop);
828 
829     util::UString messageString(util::StringView("No enum constant "), Allocator());
830     messageString.Append(enumDecl->Key()->Name());
831     messageString.Append('.');
832 
833     auto *const throwStmt = CreateThrowStatement(this, inputNameIdent, messageString);
834 
835     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
836     body.push_back(forLoop);
837     body.push_back(throwStmt);
838 
839     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
840     params.push_back(inputNameIdent);
841     auto *const enumTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
842 
843     auto *const function = MakeFunction({paramScope, std::move(params), std::move(body), enumTypeAnnotation, enumDecl,
844                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
845     function->AddFlag(ir::ScriptFunctionFlags::THROWS);
846     auto *const functionIdent =
847         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::GET_VALUE_OF_METHOD_NAME, Allocator());
848 
849     function->SetIdent(functionIdent);
850     function->Scope()->BindInternalName(functionIdent->Name());
851     MakeMethodDef(checker_, enumClass, varbinder_, functionIdent, function);
852     functionIdent->SetReference();
853 }
854 
CreateEnumValuesMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const itemsArrayIdent)855 void EnumLoweringPhase::CreateEnumValuesMethod(const ir::TSEnumDeclaration *const enumDecl,
856                                                ir::ClassDefinition *const enumClass,
857                                                ir::Identifier *const itemsArrayIdent)
858 {
859     auto *const paramScope =
860         varbinder_->Allocator()->New<varbinder::FunctionParamScope>(Allocator(), enumClass->Scope());
861     auto *const returnStmt = checker_->AllocNode<ir::ReturnStatement>(itemsArrayIdent);
862     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
863     body.push_back(returnStmt);
864 
865     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
866     auto *const enumArrayTypeAnnotation =
867         checker_->AllocNode<ir::TSArrayType>(MakeTypeReference(checker_, enumDecl->Key()->Name()));
868 
869     auto *const function = MakeFunction({paramScope, std::move(params), std::move(body), enumArrayTypeAnnotation,
870                                          enumDecl, ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
871     auto *const functionIdent =
872         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::VALUES_METHOD_NAME, Allocator());
873     function->SetIdent(functionIdent);
874     function->Scope()->BindInternalName(functionIdent->Name());
875 
876     MakeMethodDef(checker_, enumClass, varbinder_, functionIdent, function);
877     functionIdent->SetReference();
878 }
879 
CreateUnboxingMethod(ir::TSEnumDeclaration const * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const itemsArrayIdent)880 void EnumLoweringPhase::CreateUnboxingMethod(ir::TSEnumDeclaration const *const enumDecl,
881                                              ir::ClassDefinition *const enumClass,
882                                              ir::Identifier *const itemsArrayIdent)
883 
884 {
885     auto *const paramScope =
886         varbinder_->Allocator()->New<varbinder::FunctionParamScope>(Allocator(), enumClass->Scope());
887 
888     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
889 
890     auto *thisExpr = Allocator()->New<ir::ThisExpression>();
891     auto *fieldIdentifier = Allocator()->New<ir::Identifier>("ordinal", Allocator());
892     fieldIdentifier->SetReference();
893     auto *arrayIndexExpr = checker_->AllocNode<ir::MemberExpression>(
894         thisExpr, fieldIdentifier, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
895 
896     auto itemsArrayIdentClone = itemsArrayIdent->Clone(checker_->Allocator(), nullptr);
897     auto *const arrayAccessExpr = checker_->AllocNode<ir::MemberExpression>(
898         itemsArrayIdentClone, arrayIndexExpr, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
899 
900     auto *const returnStmt = checker_->AllocNode<ir::ReturnStatement>(arrayAccessExpr);
901     body.push_back(returnStmt);
902 
903     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
904 
905     auto *const returnTypeAnnotation = MakeTypeReference(checker_, enumDecl->Key()->Name());
906 
907     auto *const function = MakeFunction(
908         {paramScope, std::move(params), std::move(body), returnTypeAnnotation, enumDecl, ir::ModifierFlags::PUBLIC});
909 
910     varbinder_->AddFunctionThisParam(function);
911     auto *const functionIdent =
912         checker_->AllocNode<ir::Identifier>(checker::ETSEnumType::UNBOX_METHOD_NAME, Allocator());
913     function->SetIdent(functionIdent);
914     function->Scope()->BindInternalName(functionIdent->Name());
915 
916     MakeMethodDef(checker_, enumClass, varbinder_, functionIdent, function);
917     functionIdent->SetReference();
918 }
919 
Allocator()920 ArenaAllocator *EnumLoweringPhase::Allocator()
921 {
922     return checker_->Allocator();
923 }
924 
925 }  // namespace ark::es2panda::compiler