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