• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 
18 #include "checker/ETSchecker.h"
19 #include "checker/types/ets/etsEnumType.h"
20 #include "checker/types/type.h"
21 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
22 #include "compiler/lowering/util.h"
23 #include "varbinder/ETSBinder.h"
24 
25 namespace ark::es2panda::compiler {
26 
27 namespace {
28 
MakeFunctionParam(public_lib::Context * ctx,const util::StringView & name,ir::TypeNode * const typeAnnotation)29 [[nodiscard]] ir::ETSParameterExpression *MakeFunctionParam(public_lib::Context *ctx, const util::StringView &name,
30                                                             ir::TypeNode *const typeAnnotation)
31 {
32     auto *const paramIdent = ctx->AllocNode<ir::Identifier>(name, typeAnnotation, ctx->Allocator());
33     paramIdent->SetRange(typeAnnotation->Range());
34     auto *const param = ctx->AllocNode<ir::ETSParameterExpression>(paramIdent, false, ctx->Allocator());
35 
36     return param;
37 }
38 
MakeParamRefIdent(public_lib::Context * ctx,ir::ETSParameterExpression * paramExpr)39 [[nodiscard]] ir::Identifier *MakeParamRefIdent(public_lib::Context *ctx, ir::ETSParameterExpression *paramExpr)
40 {
41     auto *const refIdent = ctx->AllocNode<ir::Identifier>(paramExpr->Ident()->Name(), ctx->Allocator());
42     ES2PANDA_ASSERT(refIdent);
43     refIdent->SetRange(paramExpr->Ident()->Range());
44     refIdent->SetVariable(paramExpr->Ident()->Variable());
45     return refIdent;
46 }
47 
MakeTypeReference(public_lib::Context * ctx,const util::StringView & name)48 [[nodiscard]] ir::ETSTypeReference *MakeTypeReference(public_lib::Context *ctx, const util::StringView &name)
49 {
50     auto *const ident = ctx->AllocNode<ir::Identifier>(name, ctx->Allocator());
51     auto *const referencePart = ctx->AllocNode<ir::ETSTypeReferencePart>(ident, ctx->Allocator());
52     return ctx->AllocNode<ir::ETSTypeReference>(referencePart, ctx->Allocator());
53 }
54 
MakeMethodDef(public_lib::Context * ctx,ir::ClassDefinition * enumClass,ir::Identifier * const ident,ir::ScriptFunction * const function)55 ir::MethodDefinition *MakeMethodDef(public_lib::Context *ctx, ir::ClassDefinition *enumClass,
56                                     ir::Identifier *const ident, ir::ScriptFunction *const function)
57 {
58     auto *const functionExpr = ctx->AllocNode<ir::FunctionExpression>(function);
59     auto *const identClone = ident->Clone(ctx->Allocator(), nullptr);
60 
61     auto *const methodDef = ctx->AllocNode<ir::MethodDefinition>(
62         ir::MethodDefinitionKind::METHOD, identClone, functionExpr, function->Modifiers(), ctx->Allocator(), false);
63     methodDef->SetParent(enumClass);
64     enumClass->Body().push_back(methodDef);
65 
66     return methodDef;
67 }
68 
69 }  // namespace
70 
LogError(const diagnostic::DiagnosticKind & diagnostic,const util::DiagnosticMessageParams & diagnosticParams,const lexer::SourcePosition & pos)71 void EnumLoweringPhase::LogError(const diagnostic::DiagnosticKind &diagnostic,
72                                  const util::DiagnosticMessageParams &diagnosticParams,
73                                  const lexer::SourcePosition &pos)
74 {
75     context_->diagnosticEngine->LogDiagnostic(diagnostic, diagnosticParams, pos);
76 }
77 
78 template <typename TypeNode>
CheckEnumMemberType(const ArenaVector<ir::AstNode * > & enumMembers,bool & hasLoggedError,bool & hasLongLiteral)79 bool EnumLoweringPhase::CheckEnumMemberType(const ArenaVector<ir::AstNode *> &enumMembers, bool &hasLoggedError,
80                                             bool &hasLongLiteral)
81 {
82     for (auto *member : enumMembers) {
83         auto *init = member->AsTSEnumMember()->Init();
84         if constexpr (std::is_same_v<TypeNode, ir::NumberLiteral>) {
85             if (!init->IsNumberLiteral() || !init->AsNumberLiteral()->Number().IsInteger()) {
86                 LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start());
87                 hasLoggedError = true;
88                 continue;
89             }
90 
91             if (!init->AsNumberLiteral()->Number().IsLong()) {
92                 continue;
93             }
94 
95             hasLongLiteral = true;
96             // Invalid generated value.
97             if (member->AsTSEnumMember()->IsGenerated() &&
98                 init->AsNumberLiteral()->Number().GetLong() == std::numeric_limits<int64_t>::min()) {
99                 LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start());
100                 hasLoggedError = true;
101             }
102         } else if constexpr (std::is_same_v<TypeNode, ir::StringLiteral>) {
103             if (!init->IsStringLiteral()) {
104                 LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start());
105                 hasLoggedError = true;
106             }
107             if (member->AsTSEnumMember()->IsGenerated()) {
108                 LogError(diagnostic::ENUM_STRING_TYPE_ALL_ITEMS_INIT, {}, init->Start());
109                 hasLoggedError = true;
110             }
111         } else {
112             static_assert(std::is_same_v<TypeNode, void>, "Unsupported TypeNode in CheckEnumMemberType.");
113         }
114     }
115     return !hasLoggedError;
116 }
117 
MakeFunction(FunctionInfo && functionInfo)118 [[nodiscard]] ir::ScriptFunction *EnumLoweringPhase::MakeFunction(FunctionInfo &&functionInfo)
119 {
120     ir::BlockStatement *bodyBlock = nullptr;
121 
122     if (functionInfo.enumDecl->IsDeclare()) {
123         functionInfo.flags |= ir::ModifierFlags::DECLARE;
124     } else {
125         bodyBlock = AllocNode<ir::BlockStatement>(Allocator(), std::move(functionInfo.body));
126     }
127     // clang-format off
128     auto *const function = AllocNode<ir::ScriptFunction>(
129         Allocator(), ir::ScriptFunction::ScriptFunctionData {
130             bodyBlock,
131             ir::FunctionSignature(nullptr, std::move(functionInfo.params), functionInfo.returnTypeAnnotation),
132             ir::ScriptFunctionFlags::METHOD, functionInfo.flags});
133     // clang-format on
134 
135     return function;
136 }
137 
138 template <typename ElementMaker>
MakeArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,const util::StringView & name,ir::TypeNode * const typeAnnotation,ElementMaker && elementMaker)139 [[nodiscard]] ir::Identifier *EnumLoweringPhase::MakeArray(const ir::TSEnumDeclaration *const enumDecl,
140                                                            ir::ClassDefinition *const enumClass,
141                                                            const util::StringView &name,
142                                                            ir::TypeNode *const typeAnnotation,
143                                                            ElementMaker &&elementMaker)
144 {
145     ArenaVector<ir::Expression *> elements(Allocator()->Adapter());
146     elements.reserve(enumDecl->Members().size());
147     for (const auto *const member : enumDecl->Members()) {
148         elements.push_back(elementMaker(member->AsTSEnumMember()));
149     }
150     auto *const arrayExpr = AllocNode<ir::ArrayExpression>(std::move(elements), Allocator());
151     auto *const arrayIdent = AllocNode<ir::Identifier>(name, Allocator());
152     auto *const arrayClassProp = AllocNode<ir::ClassProperty>(
153         arrayIdent, arrayExpr, typeAnnotation,
154         ir::ModifierFlags::STATIC | ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY, Allocator(), false);
155     ES2PANDA_ASSERT(arrayClassProp != nullptr);
156     arrayClassProp->SetParent(enumClass);
157     enumClass->Body().push_back(arrayClassProp);
158 
159     return arrayIdent;
160 }
161 
CreateEnumItemFields(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,EnumType enumType)162 void EnumLoweringPhase::CreateEnumItemFields(const ir::TSEnumDeclaration *const enumDecl,
163                                              ir::ClassDefinition *const enumClass, EnumType enumType)
164 {
165     int32_t ordinal = 0;
166     auto createEnumItemField = [this, enumClass, enumType, &ordinal](ir::TSEnumMember *const member) {
167         auto *const enumMemberIdent =
168             AllocNode<ir::Identifier>(member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), Allocator());
169         auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
170 
171         auto *const ordinalLiteral = AllocNode<ir::NumberLiteral>(lexer::Number(ordinal));
172         ordinal++;
173         ArenaVector<ir::Expression *> newExprArgs(Allocator()->Adapter());
174         newExprArgs.push_back(ordinalLiteral);
175 
176         ir::Expression *valueArgument = nullptr;
177         switch (enumType) {
178             case EnumType::INT: {
179                 auto enumFieldValue =
180                     member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<std::int32_t>();
181                 valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
182                 break;
183             }
184             case EnumType::LONG: {
185                 auto enumFieldValue =
186                     member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<std::int64_t>();
187                 valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
188                 break;
189             }
190             case EnumType::STRING: {
191                 auto enumFieldValue = member->AsTSEnumMember()->Init()->AsStringLiteral()->Str();
192                 valueArgument = AllocNode<ir::StringLiteral>(enumFieldValue);
193                 break;
194             }
195         }
196         newExprArgs.push_back(valueArgument);
197 
198         auto enumTypeAnnotation1 = enumTypeAnnotation->Clone(Allocator(), nullptr);
199         auto *const newExpression =
200             AllocNode<ir::ETSNewClassInstanceExpression>(enumTypeAnnotation1, std::move(newExprArgs));
201 
202         auto *field = AllocNode<ir::ClassProperty>(
203             enumMemberIdent, newExpression, enumTypeAnnotation,
204             ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC | ir::ModifierFlags::READONLY, Allocator(), false);
205         enumMemberIdent->SetRange(member->Key()->Range());
206         newExpression->SetRange(member->Init()->Range());
207         field->SetRange(member->Range());
208         field->SetOrigEnumMember(member->AsTSEnumMember());
209         field->SetParent(enumClass);
210         return field;
211     };
212     for (auto *const member : enumDecl->Members()) {
213         enumClass->Body().push_back(createEnumItemField(member->AsTSEnumMember()));
214     }
215 }
216 
CreateEnumNamesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)217 ir::Identifier *EnumLoweringPhase::CreateEnumNamesArray(const ir::TSEnumDeclaration *const enumDecl,
218                                                         ir::ClassDefinition *const enumClass)
219 {
220     auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);  // NOTE String -> Builtin?
221     auto *const arrayTypeAnnotation = AllocNode<ir::TSArrayType>(stringTypeAnnotation, Allocator());
222 
223     // clang-format off
224     return MakeArray(enumDecl, enumClass, NAMES_ARRAY_NAME, arrayTypeAnnotation,
225                      [this](const ir::TSEnumMember *const member) {
226                         auto *const enumNameStringLiteral =
227                             AllocNode<ir::StringLiteral>(member->Key()->AsIdentifier()->Name());
228                         return enumNameStringLiteral;
229                     });
230     // clang-format on
231 }
232 
CreateType(public_lib::Context * ctx,EnumLoweringPhase::EnumType enumType)233 static ir::TypeNode *CreateType(public_lib::Context *ctx, EnumLoweringPhase::EnumType enumType)
234 {
235     switch (enumType) {
236         case EnumLoweringPhase::EnumType::INT: {
237             return ctx->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT, ctx->Allocator());
238         }
239         case EnumLoweringPhase::EnumType::LONG: {
240             return ctx->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::LONG, ctx->Allocator());
241         }
242         case EnumLoweringPhase::EnumType::STRING: {
243             return MakeTypeReference(ctx, EnumLoweringPhase::STRING_REFERENCE_TYPE);
244         }
245         default: {
246             ES2PANDA_UNREACHABLE();
247         }
248     }
249 
250     return nullptr;
251 }
252 
CreateClass(ir::TSEnumDeclaration * const enumDecl,const DeclarationFlags flags,EnumType enumType)253 ir::ClassDeclaration *EnumLoweringPhase::CreateClass(ir::TSEnumDeclaration *const enumDecl,
254                                                      const DeclarationFlags flags, EnumType enumType)
255 {
256     auto *ident = Allocator()->New<ir::Identifier>(enumDecl->Key()->Name(), Allocator());
257     ident->SetRange(enumDecl->Key()->Range());
258     auto enumFlag = enumType == EnumType::INT || enumType == EnumType::LONG
259                         ? ir::ClassDefinitionModifiers::INT_ENUM_TRANSFORMED
260                         : ir::ClassDefinitionModifiers::STRING_ENUM_TRANSFORMED;
261     auto baseClassDefinitionFlag = ir::ClassDefinitionModifiers::CLASS_DECL | enumFlag;
262 
263     auto typeParamsVector = ArenaVector<ir::TypeNode *>(Allocator()->Adapter());
264     typeParamsVector.push_back(CreateType(context_, enumType));
265     auto *typeParam = AllocNode<ir::TSTypeParameterInstantiation>(std::move(typeParamsVector));
266 
267     auto *identRef = AllocNode<ir::Identifier>(util::StringView(BASE_CLASS_NAME), Allocator());
268     auto *typeRefPart = AllocNode<ir::ETSTypeReferencePart>(identRef, typeParam, nullptr, Allocator());
269     auto *superClass = AllocNode<ir::ETSTypeReference>(typeRefPart, Allocator());
270 
271     auto *classDef = AllocNode<ir::ClassDefinition>(
272         Allocator(), ident,
273         flags.isLocal ? baseClassDefinitionFlag | ir::ClassDefinitionModifiers::LOCAL : baseClassDefinitionFlag,
274         enumDecl->IsDeclare() ? ir::ModifierFlags::FINAL | ir::ModifierFlags::DECLARE : ir::ModifierFlags::FINAL,
275         Language(Language::Id::ETS));
276 
277     classDef->SetSuper(superClass);
278     auto *classDecl = AllocNode<ir::ClassDeclaration>(classDef, Allocator());
279 
280     if (enumDecl->IsExported()) {
281         classDecl->AddModifier(ir::ModifierFlags::EXPORT);
282         program_->GlobalClass()->AddToExportedClasses(classDecl);
283     } else if (enumDecl->IsDefaultExported()) {
284         classDecl->AddModifier(ir::ModifierFlags::DEFAULT_EXPORT);
285     }
286 
287     classDef->SetOrigEnumDecl(enumDecl);
288 
289     CreateOrdinalField(classDef);
290     if (!enumDecl->IsDeclare()) {
291         CreateCCtorForEnumClass(classDef);
292     }
293     CreateCtorForEnumClass(classDef, enumType);
294 
295     return classDecl;
296 }
297 
CreateCCtorForEnumClass(ir::ClassDefinition * const enumClass)298 void EnumLoweringPhase::CreateCCtorForEnumClass(ir::ClassDefinition *const enumClass)
299 {
300     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
301     auto *id = AllocNode<ir::Identifier>(compiler::Signatures::CCTOR, Allocator());
302 
303     ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
304 
305     auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
306     auto *func = AllocNode<ir::ScriptFunction>(
307         Allocator(),
308         ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
309                                                 ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::HIDDEN,
310                                                 ir::ModifierFlags::STATIC, Language(Language::Id::ETS)});
311 
312     func->SetIdent(id);
313     id->SetParent(func);
314 
315     auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
316 
317     auto *const identClone = id->Clone(Allocator(), nullptr);
318     auto *const methodDef =
319         AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, identClone, funcExpr,
320                                         ir::ModifierFlags::PRIVATE | ir::ModifierFlags::STATIC, Allocator(), false);
321     ES2PANDA_ASSERT(methodDef != nullptr);
322     methodDef->SetParent(enumClass);
323     enumClass->Body().push_back(methodDef);
324 }
325 
CreateOrdinalField(ir::ClassDefinition * const enumClass)326 ir::ClassProperty *EnumLoweringPhase::CreateOrdinalField(ir::ClassDefinition *const enumClass)
327 {
328     auto *const fieldIdent = Allocator()->New<ir::Identifier>(ORDINAL_NAME, Allocator());
329     auto *const intTypeAnnotation = Allocator()->New<ir::ETSPrimitiveType>(ir::PrimitiveType::INT, Allocator());
330     auto *field =
331         AllocNode<ir::ClassProperty>(fieldIdent, nullptr, intTypeAnnotation,
332                                      ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY, Allocator(), false);
333 
334     enumClass->Body().push_back(field);
335     field->SetParent(enumClass);
336     return field;
337 }
338 
CreateFunctionForCtorOfEnumClass(ir::ClassDefinition * const enumClass,EnumType enumType)339 ir::ScriptFunction *EnumLoweringPhase::CreateFunctionForCtorOfEnumClass(ir::ClassDefinition *const enumClass,
340                                                                         EnumType enumType)
341 {
342     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
343 
344     auto *const intTypeAnnotation = AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT, Allocator());
345     auto *const inputOrdinalParam = MakeFunctionParam(context_, PARAM_ORDINAL, intTypeAnnotation);
346     params.push_back(inputOrdinalParam);
347 
348     ir::TypeNode *typeAnnotation = CreateType(context_, enumType);
349     auto *const inputValueParam = MakeFunctionParam(context_, PARAM_VALUE, typeAnnotation);
350     params.push_back(inputValueParam);
351 
352     auto *id = AllocNode<ir::Identifier>("constructor", Allocator());
353     ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
354 
355     auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
356 
357     auto scriptFlags = ir::ScriptFunctionFlags::CONSTRUCTOR;
358     scriptFlags |= enumClass->IsDeclare() ? ir::ScriptFunctionFlags::EXTERNAL : ir::ScriptFunctionFlags::NONE;
359 
360     auto *func = AllocNode<ir::ScriptFunction>(
361         Allocator(),
362         ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
363                                                 scriptFlags,  // CC-OFF(G.FMT.02) project code style
364                                                 ir::ModifierFlags::CONSTRUCTOR |
365                                                     ir::ModifierFlags::PRIVATE,  // CC-OFF(G.FMT.02) project code style
366                                                 Language(Language::Id::ETS)});   // CC-OFF(G.FMT.02) project code style
367 
368     func->SetIdent(id);
369 
370     if (enumClass->IsDeclare()) {
371         // NOTE: In aliveAnalyzer call to super is processed and leads to error. Need to invetigate it
372         return func;
373     }
374 
375     auto *valueIdentifier = AllocNode<ir::Identifier>(PARAM_VALUE, Allocator());
376     valueIdentifier->SetVariable(inputValueParam->Ident()->Variable());
377 
378     ArenaVector<ir::Expression *> callArguments(Allocator()->Adapter());
379     auto *callee = AllocNode<ir::SuperExpression>();
380     callArguments.push_back(valueIdentifier);
381     auto *superConstructorCall = AllocNode<ir::CallExpression>(callee, std::move(callArguments), nullptr, false);
382     auto *superCallStatement = AllocNode<ir::ExpressionStatement>(superConstructorCall);
383     superCallStatement->SetParent(body);
384     body->Statements().push_back(superCallStatement);
385 
386     auto *thisExpr = Allocator()->New<ir::ThisExpression>();
387     auto *fieldIdentifier = Allocator()->New<ir::Identifier>(ORDINAL_NAME, Allocator());
388     auto *leftHandSide = AllocNode<ir::MemberExpression>(thisExpr, fieldIdentifier,
389                                                          ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
390     auto *rightHandSide = AllocNode<ir::Identifier>(PARAM_ORDINAL, Allocator());
391     rightHandSide->SetVariable(inputOrdinalParam->Ident()->Variable());
392     auto *initializer =
393         AllocNode<ir::AssignmentExpression>(leftHandSide, rightHandSide, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
394     auto initStatement = AllocNode<ir::ExpressionStatement>(initializer);
395     initStatement->SetParent(body);
396     body->Statements().push_back(initStatement);
397 
398     return func;
399 }
400 
CreateCtorForEnumClass(ir::ClassDefinition * const enumClass,EnumType enumType)401 void EnumLoweringPhase::CreateCtorForEnumClass(ir::ClassDefinition *const enumClass, EnumType enumType)
402 {
403     auto *func = CreateFunctionForCtorOfEnumClass(enumClass, enumType);
404     auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
405 
406     auto *const identClone = func->Id()->Clone(Allocator(), nullptr);
407     auto *const methodDef = AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::CONSTRUCTOR, identClone, funcExpr,
408                                                             ir::ModifierFlags::PUBLIC, Allocator(), false);
409     methodDef->SetParent(enumClass);
410     enumClass->Body().push_back(methodDef);
411 }
412 
ProcessEnumClassDeclaration(ir::TSEnumDeclaration * const enumDecl,const DeclarationFlags & flags,ir::ClassDeclaration * enumClassDecl)413 void EnumLoweringPhase::ProcessEnumClassDeclaration(ir::TSEnumDeclaration *const enumDecl,
414                                                     const DeclarationFlags &flags, ir::ClassDeclaration *enumClassDecl)
415 {
416     varbinder::Variable *var = nullptr;
417     auto *ident = enumClassDecl->Definition()->Ident();
418     if (flags.isLocal) {
419         auto *scope = NearestScope(enumDecl->Parent());
420         ES2PANDA_ASSERT(scope);
421         auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
422         ES2PANDA_ASSERT(scope);
423         scope->EraseBinding(ident->Name());
424         InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
425         var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::ALL);
426     } else if (flags.isTopLevel) {
427         auto *scope = program_->GlobalClassScope();
428         ES2PANDA_ASSERT(scope);
429         auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
430         ES2PANDA_ASSERT(scope);
431         scope->StaticDeclScope()->EraseBinding(ident->Name());
432         InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
433         var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::ALL);
434         if (var != nullptr) {
435             program_->GlobalScope()->InsertBinding(ident->Name(), var);
436         }
437     } else if (flags.isNamespace) {
438         auto *scope = enumDecl->Parent()->Scope();
439         auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
440         scope->AsClassScope()->StaticDeclScope()->EraseBinding(ident->Name());
441         InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
442         var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::DECLARATION);
443     } else {
444         ES2PANDA_UNREACHABLE();
445     }
446     if (var != nullptr) {
447         // Although it enum was transformed to class, it should still be regarded as enum.
448         var->RemoveFlag(varbinder::VariableFlags::CLASS);
449         var->AddFlag(varbinder::VariableFlags::ENUM_LITERAL);
450     }
451 }
452 
453 template <ir::PrimitiveType TYPE>
CreateEnumIntClassFromEnumDeclaration(ir::TSEnumDeclaration * const enumDecl,const DeclarationFlags flags)454 ir::ClassDeclaration *EnumLoweringPhase::CreateEnumIntClassFromEnumDeclaration(ir::TSEnumDeclaration *const enumDecl,
455                                                                                const DeclarationFlags flags)
456 {
457     EnumType enumType = EnumType::INT;
458     if constexpr (TYPE == ir::PrimitiveType::LONG) {
459         enumType = EnumType::LONG;
460     }
461 
462     auto *const enumClassDecl = CreateClass(enumDecl, flags, enumType);
463     auto *const enumClass = enumClassDecl->Definition();
464 
465     CreateEnumItemFields(enumDecl, enumClass, enumType);
466     auto *const namesArrayIdent = CreateEnumNamesArray(enumDecl, enumClass);
467     auto *const valuesArrayIdent = CreateEnumValuesArray<TYPE>(enumDecl, enumClass);
468     auto *const stringValuesArrayIdent = CreateEnumStringValuesArray(enumDecl, enumClass);
469     auto *const itemsArrayIdent = CreateEnumItemsArray(enumDecl, enumClass);
470 
471     CreateEnumGetNameMethod(enumDecl, enumClass, namesArrayIdent);
472 
473     CreateEnumGetValueOfMethod(enumDecl, enumClass, namesArrayIdent, itemsArrayIdent);
474 
475     CreateEnumFromValueMethod(enumDecl, enumClass, valuesArrayIdent, itemsArrayIdent, TYPE);
476 
477     CreateEnumValueOfMethod(enumDecl, enumClass, valuesArrayIdent, TYPE);
478 
479     CreateEnumToStringMethod(enumDecl, enumClass, stringValuesArrayIdent);
480 
481     CreateEnumValuesMethod(enumDecl, enumClass, itemsArrayIdent);
482 
483     CreateEnumGetOrdinalMethod(enumDecl, enumClass);
484 
485     CreateEnumDollarGetMethod(enumDecl, enumClass);
486 
487     SetDefaultPositionInUnfilledClassNodes(enumClassDecl, enumDecl);
488 
489     enumClassDecl->SetParent(enumDecl->Parent());
490     ProcessEnumClassDeclaration(enumDecl, flags, enumClassDecl);
491 
492     return enumClassDecl;
493 }
494 
CreateEnumStringClassFromEnumDeclaration(ir::TSEnumDeclaration * const enumDecl,const DeclarationFlags flags)495 ir::ClassDeclaration *EnumLoweringPhase::CreateEnumStringClassFromEnumDeclaration(ir::TSEnumDeclaration *const enumDecl,
496                                                                                   const DeclarationFlags flags)
497 {
498     auto *const enumClassDecl = CreateClass(enumDecl, flags, EnumType::STRING);
499     auto *const enumClass = enumClassDecl->Definition();
500 
501     CreateEnumItemFields(enumDecl, enumClass, EnumType::STRING);
502     auto *const namesArrayIdent = CreateEnumNamesArray(enumDecl, enumClass);
503     auto *const stringValuesArrayIdent = CreateEnumStringValuesArray(enumDecl, enumClass);
504     auto *const itemsArrayIdent = CreateEnumItemsArray(enumDecl, enumClass);
505 
506     CreateEnumGetNameMethod(enumDecl, enumClass, namesArrayIdent);
507 
508     CreateEnumGetValueOfMethod(enumDecl, enumClass, namesArrayIdent, itemsArrayIdent);
509 
510     CreateEnumFromValueMethod(enumDecl, enumClass, stringValuesArrayIdent, itemsArrayIdent, std::nullopt);
511 
512     CreateEnumValueOfMethod(enumDecl, enumClass, stringValuesArrayIdent, std::nullopt);
513 
514     CreateEnumToStringMethod(enumDecl, enumClass, stringValuesArrayIdent);
515 
516     CreateEnumValuesMethod(enumDecl, enumClass, itemsArrayIdent);
517 
518     CreateEnumGetOrdinalMethod(enumDecl, enumClass);
519 
520     CreateEnumDollarGetMethod(enumDecl, enumClass);
521 
522     SetDefaultPositionInUnfilledClassNodes(enumClassDecl, enumDecl);
523 
524     enumClassDecl->SetParent(enumDecl->Parent());
525     ProcessEnumClassDeclaration(enumDecl, flags, enumClassDecl);
526 
527     return enumClassDecl;
528 }
529 
GetDeclFlags(ir::TSEnumDeclaration * const enumDecl)530 static EnumLoweringPhase::DeclarationFlags GetDeclFlags(ir::TSEnumDeclaration *const enumDecl)
531 {
532     return {enumDecl->Parent() != nullptr && enumDecl->Parent()->IsETSModule() &&
533                 enumDecl->Parent()->AsETSModule()->IsETSScript(),
534             enumDecl->Parent() != nullptr && enumDecl->Parent()->IsBlockStatement(),
535             enumDecl->Parent() != nullptr && enumDecl->Parent()->IsClassDefinition() &&
536                 enumDecl->Parent()->AsClassDefinition()->IsNamespaceTransformed()};
537 }
538 
PerformForModule(public_lib::Context * ctx,parser::Program * program)539 bool EnumLoweringPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
540 {
541     if (program->Extension() != ScriptExtension::ETS) {
542         return true;
543     }
544 
545     if (program->GetFlag(parser::ProgramFlags::AST_ENUM_LOWERED)) {
546         return true;
547     }
548 
549     context_ = ctx;
550     checker_ = ctx->checker->AsETSChecker();
551     varbinder_ = ctx->parserProgram->VarBinder()->AsETSBinder();
552     program_ = program;
553 
554     program->Ast()->TransformChildrenRecursively(
555         [this](checker::AstNodePtr ast) -> checker::AstNodePtr {
556             if (ast->IsTSEnumDeclaration()) {
557                 auto *enumDecl = ast->AsTSEnumDeclaration();
558                 auto const flags = GetDeclFlags(enumDecl);
559                 if (!flags.IsValid()) {
560                     return ast;
561                 }
562                 if (enumDecl->Members().empty()) {
563                     return CreateEnumIntClassFromEnumDeclaration<ir::PrimitiveType::INT>(enumDecl, flags);
564                 }
565 
566                 bool hasLoggedError = false;
567                 bool hasLongLiteral = false;
568                 auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init();
569 
570                 if (itemInit->IsNumberLiteral() &&
571                     CheckEnumMemberType<ir::NumberLiteral>(enumDecl->Members(), hasLoggedError, hasLongLiteral)) {
572                     auto res = hasLongLiteral
573                                    ? CreateEnumIntClassFromEnumDeclaration<ir::PrimitiveType::LONG>(enumDecl, flags)
574                                    : CreateEnumIntClassFromEnumDeclaration<ir::PrimitiveType::INT>(enumDecl, flags);
575                     return res;
576                 }
577                 if (itemInit->IsStringLiteral() &&
578                     CheckEnumMemberType<ir::StringLiteral>(enumDecl->Members(), hasLoggedError, hasLongLiteral)) {
579                     return CreateEnumStringClassFromEnumDeclaration(enumDecl, flags);
580                 }
581 
582                 if (!hasLoggedError) {
583                     LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, itemInit->Start());
584                 }
585 
586                 return ast;
587             }
588             return ast;
589         },
590         Name());
591 
592     program->SetFlag(parser::ProgramFlags::AST_ENUM_LOWERED);
593 
594     return true;
595 }
596 
597 template <ir::PrimitiveType TYPE>
CreateEnumValuesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)598 ir::Identifier *EnumLoweringPhase::CreateEnumValuesArray(const ir::TSEnumDeclaration *const enumDecl,
599                                                          ir::ClassDefinition *const enumClass)
600 {
601     auto *const type = AllocNode<ir::ETSPrimitiveType>(TYPE, Allocator());
602     auto *const arrayTypeAnnotation = AllocNode<ir::TSArrayType>(type, Allocator());
603     // clang-format off
604     return MakeArray(enumDecl, enumClass, VALUES_ARRAY_NAME, arrayTypeAnnotation,
605                      [this](const ir::TSEnumMember *const member) {
606                         auto *const enumValueLiteral = AllocNode<ir::NumberLiteral>(
607                             lexer::Number(member->AsTSEnumMember()
608                                                 ->Init()
609                                                 ->AsNumberLiteral()
610                                                 ->Number()
611                                                 .GetValue<std::int64_t>()));
612                         return enumValueLiteral;
613                     });
614     // clang-format on
615 }
616 
CreateEnumStringValuesArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)617 ir::Identifier *EnumLoweringPhase::CreateEnumStringValuesArray(const ir::TSEnumDeclaration *const enumDecl,
618                                                                ir::ClassDefinition *const enumClass)
619 {
620     auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);  // NOTE String -> Builtin?
621     auto *const arrayTypeAnnotation = AllocNode<ir::TSArrayType>(stringTypeAnnotation, Allocator());
622 
623     // clang-format off
624     return MakeArray(enumDecl, enumClass, STRING_VALUES_ARRAY_NAME, arrayTypeAnnotation,
625                      [this](const ir::TSEnumMember *const member) {
626                         auto *const init = member->AsTSEnumMember()->Init();
627                         util::StringView stringValue;
628 
629                         if (init->IsStringLiteral()) {
630                             stringValue = init->AsStringLiteral()->Str();
631                         } else {
632                             auto str = std::to_string(
633                                 init->AsNumberLiteral()->Number().GetValue<std::int64_t>());
634                             stringValue = util::UString(str, Allocator()).View();
635                         }
636 
637                         auto *const enumValueStringLiteral = AllocNode<ir::StringLiteral>(stringValue);
638                         return enumValueStringLiteral;
639                     });
640     // clang-format on
641 }
642 
CreateEnumItemsArray(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)643 ir::Identifier *EnumLoweringPhase::CreateEnumItemsArray(const ir::TSEnumDeclaration *const enumDecl,
644                                                         ir::ClassDefinition *const enumClass)
645 {
646     auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
647     auto *const arrayTypeAnnotation = AllocNode<ir::TSArrayType>(enumTypeAnnotation, Allocator());
648     // clang-format off
649     return MakeArray(enumDecl, enumClass, ITEMS_ARRAY_NAME, arrayTypeAnnotation,
650                      [this, enumClass, enumDecl](const ir::TSEnumMember *const member) {
651                         auto *const enumTypeIdent =
652                             AllocNode<ir::Identifier>(enumClass->Ident()->Name(),
653 					                               Allocator());
654                         enumTypeIdent->SetRange(enumDecl->Key()->Range());
655                         auto *const enumMemberIdent = AllocNode<ir::Identifier>(
656                             member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), context_->Allocator());
657                         enumMemberIdent->SetRange(member->AsTSEnumMember()->Key()->Range());
658                         auto *const enumMemberExpr = AllocNode<ir::MemberExpression>(
659                             enumTypeIdent, enumMemberIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
660                         return enumMemberExpr;
661                     });
662     // clang-format on
663 }
664 
CreateOrdinalAccessExpression()665 ir::MemberExpression *EnumLoweringPhase::CreateOrdinalAccessExpression()
666 {
667     auto *thisExpr = AllocNode<ir::ThisExpression>();
668     auto *fieldIdentifier = AllocNode<ir::Identifier>(ORDINAL_NAME, Allocator());
669     auto *ordinalAccessExpr = AllocNode<ir::MemberExpression>(thisExpr, fieldIdentifier,
670                                                               ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
671     return ordinalAccessExpr;
672 }
673 
674 namespace {
675 
CreateStaticAccessMemberExpression(public_lib::Context * ctx,ir::Identifier * const enumClassIdentifier,ir::Identifier * const arrayIdentifier)676 ir::MemberExpression *CreateStaticAccessMemberExpression(public_lib::Context *ctx,
677                                                          ir::Identifier *const enumClassIdentifier,
678                                                          ir::Identifier *const arrayIdentifier)
679 {
680     auto *const enumClassIdentifierClone = enumClassIdentifier->Clone(ctx->Allocator(), nullptr);
681     auto *const arrayIdentifierClone = arrayIdentifier->Clone(ctx->Allocator(), nullptr);
682 
683     return ctx->AllocNode<ir::MemberExpression>(enumClassIdentifierClone, arrayIdentifierClone,
684                                                 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
685 }
686 
CreateThrowStatement(public_lib::Context * ctx,ir::ETSParameterExpression * const parameter,const util::UString & messageString)687 ir::ThrowStatement *CreateThrowStatement(public_lib::Context *ctx, ir::ETSParameterExpression *const parameter,
688                                          const util::UString &messageString)
689 {
690     auto *const paramRefIdent = MakeParamRefIdent(ctx, parameter);
691     auto *const message = ctx->AllocNode<ir::StringLiteral>(messageString.View());
692     auto *const newExprArg =
693         ctx->AllocNode<ir::BinaryExpression>(message, paramRefIdent, lexer::TokenType::PUNCTUATOR_PLUS);
694 
695     paramRefIdent->SetParent(newExprArg);
696     ArenaVector<ir::Expression *> newExprArgs(ctx->Allocator()->Adapter());
697     newExprArgs.push_back(newExprArg);
698 
699     auto *const exceptionReference = MakeTypeReference(ctx, "Error");
700     auto *const newExpr = ctx->AllocNode<ir::ETSNewClassInstanceExpression>(exceptionReference, std::move(newExprArgs));
701     return ctx->AllocNode<ir::ThrowStatement>(newExpr);
702 }
703 
CreateReturnStatement(public_lib::Context * ctx,ir::Identifier * const enumClassIdentifier,ir::Identifier * const arrayIdentifier,ir::Expression * const parameter)704 ir::ReturnStatement *CreateReturnStatement(public_lib::Context *ctx, ir::Identifier *const enumClassIdentifier,
705                                            ir::Identifier *const arrayIdentifier, ir::Expression *const parameter)
706 {
707     auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(ctx, enumClassIdentifier, arrayIdentifier);
708 
709     auto *const arrayAccessExpr = ctx->AllocNode<ir::MemberExpression>(
710         propertyAccessExpr, parameter, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
711 
712     return ctx->AllocNode<ir::ReturnStatement>(arrayAccessExpr);
713 }
714 
CreateCallInstanceMethod(public_lib::Context * ctx,std::string_view methodName,ir::Expression * thisArg)715 ir::CallExpression *CreateCallInstanceMethod(public_lib::Context *ctx, std::string_view methodName,
716                                              ir::Expression *thisArg)
717 {
718     auto methodId = ctx->AllocNode<ir::Identifier>(methodName, ctx->Allocator());
719     auto callee = ctx->AllocNode<ir::MemberExpression>(thisArg, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS,
720                                                        false, false);
721 
722     ArenaVector<ir::Expression *> callArguments({}, ctx->Allocator()->Adapter());
723     return ctx->AllocNode<ir::CallExpression>(callee, std::move(callArguments), nullptr, false);
724 }
725 
726 }  // namespace
727 
CreateEnumToStringMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const stringValuesArrayIdent)728 void EnumLoweringPhase::CreateEnumToStringMethod(const ir::TSEnumDeclaration *const enumDecl,
729                                                  ir::ClassDefinition *const enumClass,
730                                                  ir::Identifier *const stringValuesArrayIdent)
731 {
732     auto *ordinalAccessExpr = CreateOrdinalAccessExpression();
733     auto *const returnStmt =
734         CreateReturnStatement(context_, enumClass->Ident(), stringValuesArrayIdent, ordinalAccessExpr);
735 
736     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
737     body.push_back(returnStmt);
738 
739     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
740     auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);  // NOTE String -> Builtin?
741     auto *const function =
742         MakeFunction({std::move(params), std::move(body), stringTypeAnnotation, enumDecl, ir::ModifierFlags::PUBLIC});
743 
744     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::TO_STRING_METHOD_NAME, Allocator());
745 
746     function->SetIdent(functionIdent);
747     MakeMethodDef(context_, enumClass, functionIdent, function);
748 }
749 
CreateEnumValueOfMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const valuesArrayIdent,std::optional<ir::PrimitiveType> primitiveType)750 void EnumLoweringPhase::CreateEnumValueOfMethod(const ir::TSEnumDeclaration *const enumDecl,
751                                                 ir::ClassDefinition *const enumClass,
752                                                 ir::Identifier *const valuesArrayIdent,
753                                                 std::optional<ir::PrimitiveType> primitiveType)
754 {
755     auto *ordinalAccessExpr = CreateOrdinalAccessExpression();
756     auto *const returnStmt = CreateReturnStatement(context_, enumClass->Ident(), valuesArrayIdent, ordinalAccessExpr);
757 
758     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
759     body.push_back(returnStmt);
760 
761     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
762     auto *const typeAnnotation = primitiveType.has_value()
763                                      ? AllocNode<ir::ETSPrimitiveType>(primitiveType.value(), Allocator())->AsTypeNode()
764                                      : MakeTypeReference(context_, STRING_REFERENCE_TYPE)->AsTypeNode();
765     auto *const function =
766         MakeFunction({std::move(params), std::move(body), typeAnnotation, enumDecl, ir::ModifierFlags::PUBLIC});
767     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::VALUE_OF_METHOD_NAME, Allocator());
768     function->SetIdent(functionIdent);
769 
770     MakeMethodDef(context_, enumClass, functionIdent, function);
771 }
772 
CreateEnumGetNameMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const namesArrayIdent)773 void EnumLoweringPhase::CreateEnumGetNameMethod(const ir::TSEnumDeclaration *const enumDecl,
774                                                 ir::ClassDefinition *const enumClass,
775                                                 ir::Identifier *const namesArrayIdent)
776 {
777     auto ordinalAccessExpr = CreateOrdinalAccessExpression();
778     auto *const returnStmt = CreateReturnStatement(context_, enumClass->Ident(), namesArrayIdent, ordinalAccessExpr);
779 
780     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
781     body.push_back(returnStmt);
782 
783     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
784     auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);  // NOTE String -> Builtin?
785 
786     auto *const function =
787         MakeFunction({std::move(params), std::move(body), stringTypeAnnotation, enumDecl, ir::ModifierFlags::PUBLIC});
788     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::GET_NAME_METHOD_NAME, Allocator());
789 
790     function->SetIdent(functionIdent);
791 
792     MakeMethodDef(context_, enumClass, functionIdent, function);
793 }
794 
795 namespace {
796 
CreateForLoopInitVariableDeclaration(public_lib::Context * ctx,ir::Identifier * const loopIdentifier)797 ir::VariableDeclaration *CreateForLoopInitVariableDeclaration(public_lib::Context *ctx,
798                                                               ir::Identifier *const loopIdentifier)
799 {
800     auto *const init = ctx->AllocNode<ir::NumberLiteral>("0");
801     auto *const decl = ctx->AllocNode<ir::VariableDeclarator>(ir::VariableDeclaratorFlag::LET, loopIdentifier, init);
802     ES2PANDA_ASSERT(loopIdentifier);
803     loopIdentifier->SetParent(decl);
804     ArenaVector<ir::VariableDeclarator *> decls(ctx->Allocator()->Adapter());
805     decls.push_back(decl);
806     auto *const declaration = ctx->AllocNode<ir::VariableDeclaration>(
807         ir::VariableDeclaration::VariableDeclarationKind::LET, ctx->Allocator(), std::move(decls));
808     ES2PANDA_ASSERT(decl);
809     decl->SetParent(declaration);
810     return declaration;
811 }
812 
CreateForLoopTest(public_lib::Context * ctx,ir::Identifier * const enumClassIdentifier,ir::Identifier * const namesArrayIdentifier,ir::Identifier * const loopIdentifier)813 ir::BinaryExpression *CreateForLoopTest(public_lib::Context *ctx, ir::Identifier *const enumClassIdentifier,
814                                         ir::Identifier *const namesArrayIdentifier,
815                                         ir::Identifier *const loopIdentifier)
816 {
817     auto *const lengthIdent = ctx->AllocNode<ir::Identifier>("length", ctx->Allocator());
818     auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(ctx, enumClassIdentifier, namesArrayIdentifier);
819     auto *const arrayLengthExpr = ctx->AllocNode<ir::MemberExpression>(
820         propertyAccessExpr, lengthIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
821     auto *const forLoopIdentClone = loopIdentifier->Clone(ctx->Allocator(), nullptr);
822     auto *const binaryExpr = ctx->AllocNode<ir::BinaryExpression>(forLoopIdentClone, arrayLengthExpr,
823                                                                   lexer::TokenType::PUNCTUATOR_LESS_THAN);
824     return binaryExpr;
825 }
826 
CreateForLoopUpdate(public_lib::Context * ctx,ir::Identifier * const loopIdentifier)827 ir::UpdateExpression *CreateForLoopUpdate(public_lib::Context *ctx, ir::Identifier *const loopIdentifier)
828 {
829     auto *const forLoopIdentClone = loopIdentifier->Clone(ctx->Allocator(), nullptr);
830     auto *const incrementExpr =
831         ctx->AllocNode<ir::UpdateExpression>(forLoopIdentClone, lexer::TokenType::PUNCTUATOR_PLUS_PLUS, true);
832     return incrementExpr;
833 }
834 
CreateIf(public_lib::Context * ctx,ir::MemberExpression * propertyAccessExpr,ir::MemberExpression * itemAccessExpr,ir::Identifier * const loopIdentifier,ir::ETSParameterExpression * const parameter)835 ir::IfStatement *CreateIf(public_lib::Context *ctx, ir::MemberExpression *propertyAccessExpr,
836                           ir::MemberExpression *itemAccessExpr, ir::Identifier *const loopIdentifier,
837                           ir::ETSParameterExpression *const parameter)
838 {
839     auto *const forLoopIdentClone1 = loopIdentifier->Clone(ctx->Allocator(), nullptr);
840     auto *const namesArrayElementExpr = ctx->AllocNode<ir::MemberExpression>(
841         propertyAccessExpr, forLoopIdentClone1, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
842 
843     auto *const paramRefIdent = MakeParamRefIdent(ctx, parameter);
844     auto *const namesEqualExpr =
845         ctx->AllocNode<ir::BinaryExpression>(paramRefIdent, namesArrayElementExpr, lexer::TokenType::PUNCTUATOR_EQUAL);
846     paramRefIdent->SetParent(namesEqualExpr);
847 
848     auto *const forLoopIdentClone2 = loopIdentifier->Clone(ctx->Allocator(), nullptr);
849 
850     auto *const itemsArrayElementExpr = ctx->AllocNode<ir::MemberExpression>(
851         itemAccessExpr, forLoopIdentClone2, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
852 
853     auto *const returnStmt = ctx->AllocNode<ir::ReturnStatement>(itemsArrayElementExpr);
854     return ctx->AllocNode<ir::IfStatement>(namesEqualExpr, returnStmt, nullptr);
855 }
856 
857 }  // namespace
858 
CreateEnumGetValueOfMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const namesArrayIdent,ir::Identifier * const itemsArrayIdent)859 void EnumLoweringPhase::CreateEnumGetValueOfMethod(const ir::TSEnumDeclaration *const enumDecl,
860                                                    ir::ClassDefinition *const enumClass,
861                                                    ir::Identifier *const namesArrayIdent,
862                                                    ir::Identifier *const itemsArrayIdent)
863 {
864     auto *const forLoopIIdent = AllocNode<ir::Identifier>(IDENTIFIER_I, Allocator());
865     auto *const forLoopInitVarDecl = CreateForLoopInitVariableDeclaration(context_, forLoopIIdent);
866     auto *const forLoopTest = CreateForLoopTest(context_, enumClass->Ident(), namesArrayIdent, forLoopIIdent);
867     auto *const forLoopUpdate = CreateForLoopUpdate(context_, forLoopIIdent);
868     auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);  // NOTE String -> Builtin?
869     auto *const inputNameIdent = MakeFunctionParam(context_, PARAM_NAME, stringTypeAnnotation);
870     auto *const ifStmt =
871         CreateIf(context_, CreateStaticAccessMemberExpression(context_, enumClass->Ident(), namesArrayIdent),
872                  CreateStaticAccessMemberExpression(context_, enumClass->Ident(), itemsArrayIdent), forLoopIIdent,
873                  inputNameIdent);
874 
875     auto *const forLoop = AllocNode<ir::ForUpdateStatement>(forLoopInitVarDecl, forLoopTest, forLoopUpdate, ifStmt);
876 
877     util::UString messageString(util::StringView("No enum constant "), Allocator());
878     messageString.Append(enumClass->Ident()->Name());
879     messageString.Append('.');
880 
881     auto *const throwStmt = CreateThrowStatement(context_, inputNameIdent, messageString);
882 
883     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
884     body.push_back(forLoop);
885     body.push_back(throwStmt);
886 
887     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
888     params.push_back(inputNameIdent);
889     auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
890 
891     auto *const function = MakeFunction({std::move(params), std::move(body), enumTypeAnnotation, enumDecl,
892                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
893     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::GET_VALUE_OF_METHOD_NAME, Allocator());
894 
895     function->SetIdent(functionIdent);
896     MakeMethodDef(context_, enumClass, functionIdent, function);
897 }
898 
CreateEnumFromValueMethod(ir::TSEnumDeclaration const * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const valuesArrayIdent,ir::Identifier * const itemsArrayIdent,std::optional<ir::PrimitiveType> primitiveType)899 void EnumLoweringPhase::CreateEnumFromValueMethod(ir::TSEnumDeclaration const *const enumDecl,
900                                                   ir::ClassDefinition *const enumClass,
901                                                   ir::Identifier *const valuesArrayIdent,
902                                                   ir::Identifier *const itemsArrayIdent,
903                                                   std::optional<ir::PrimitiveType> primitiveType)
904 {
905     auto *const forLoopIIdent = AllocNode<ir::Identifier>(IDENTIFIER_I, Allocator());
906     auto *const forLoopInitVarDecl = CreateForLoopInitVariableDeclaration(context_, forLoopIIdent);
907     auto *const forLoopTest = CreateForLoopTest(context_, enumClass->Ident(), valuesArrayIdent, forLoopIIdent);
908     auto *const forLoopUpdate = CreateForLoopUpdate(context_, forLoopIIdent);
909     auto *const typeAnnotation = primitiveType.has_value()
910                                      ? AllocNode<ir::ETSPrimitiveType>(primitiveType.value(), Allocator())->AsTypeNode()
911                                      : MakeTypeReference(context_, STRING_REFERENCE_TYPE)->AsTypeNode();
912     auto *const inputValueIdent = MakeFunctionParam(context_, PARAM_VALUE, typeAnnotation);
913     auto *const ifStmt =
914         CreateIf(context_, CreateStaticAccessMemberExpression(context_, enumClass->Ident(), valuesArrayIdent),
915                  CreateStaticAccessMemberExpression(context_, enumClass->Ident(), itemsArrayIdent), forLoopIIdent,
916                  inputValueIdent);
917 
918     auto *const forLoop = AllocNode<ir::ForUpdateStatement>(forLoopInitVarDecl, forLoopTest, forLoopUpdate, ifStmt);
919 
920     util::UString messageString(util::StringView("No enum "), Allocator());
921     messageString.Append(enumClass->Ident()->Name());
922     messageString.Append(" with value ");
923 
924     auto *const throwStmt = CreateThrowStatement(context_, inputValueIdent, messageString);
925 
926     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
927     body.push_back(forLoop);
928     body.push_back(throwStmt);
929 
930     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
931     params.push_back(inputValueIdent);
932     auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
933 
934     auto *const function = MakeFunction({std::move(params), std::move(body), enumTypeAnnotation, enumDecl,
935                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
936     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::FROM_VALUE_METHOD_NAME, Allocator());
937 
938     function->SetIdent(functionIdent);
939     MakeMethodDef(context_, enumClass, functionIdent, function);
940 }
941 
CreateEnumValuesMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass,ir::Identifier * const itemsArrayIdent)942 void EnumLoweringPhase::CreateEnumValuesMethod(const ir::TSEnumDeclaration *const enumDecl,
943                                                ir::ClassDefinition *const enumClass,
944                                                ir::Identifier *const itemsArrayIdent)
945 {
946     auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(context_, enumClass->Ident(), itemsArrayIdent);
947     auto *const returnStmt = AllocNode<ir::ReturnStatement>(propertyAccessExpr);
948     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
949     body.push_back(returnStmt);
950 
951     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
952     auto *const enumArrayTypeAnnotation =
953         AllocNode<ir::TSArrayType>(MakeTypeReference(context_, enumClass->Ident()->Name()), Allocator());
954 
955     auto *const function = MakeFunction({std::move(params), std::move(body), enumArrayTypeAnnotation, enumDecl,
956                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
957     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::VALUES_METHOD_NAME, Allocator());
958     function->SetIdent(functionIdent);
959 
960     MakeMethodDef(context_, enumClass, functionIdent, function);
961 }
962 
CreateEnumGetOrdinalMethod(const ir::TSEnumDeclaration * const enumDecl,ir::ClassDefinition * const enumClass)963 void EnumLoweringPhase::CreateEnumGetOrdinalMethod(const ir::TSEnumDeclaration *const enumDecl,
964                                                    ir::ClassDefinition *const enumClass)
965 {
966     auto ordinalAccessExpr = CreateOrdinalAccessExpression();
967     auto *const returnStmt = AllocNode<ir::ReturnStatement>(ordinalAccessExpr);
968     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
969     body.push_back(returnStmt);
970 
971     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
972     auto *const intTypeAnnotation = Allocator()->New<ir::ETSPrimitiveType>(ir::PrimitiveType::INT, Allocator());
973 
974     auto *const function =
975         MakeFunction({std::move(params), std::move(body), intTypeAnnotation, enumDecl, ir::ModifierFlags::PUBLIC});
976 
977     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::GET_ORDINAL_METHOD_NAME, Allocator());
978     function->SetIdent(functionIdent);
979 
980     MakeMethodDef(context_, enumClass, functionIdent, function);
981 }
982 
CreateEnumDollarGetMethod(ir::TSEnumDeclaration const * const enumDecl,ir::ClassDefinition * const enumClass)983 void EnumLoweringPhase::CreateEnumDollarGetMethod(ir::TSEnumDeclaration const *const enumDecl,
984                                                   ir::ClassDefinition *const enumClass)
985 {
986     auto *const inputTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
987     auto *const inputNameIdent = MakeFunctionParam(context_, "e", inputTypeAnnotation);
988     auto *const paramRefIdent = MakeParamRefIdent(context_, inputNameIdent);
989     auto *const callExpr =
990         CreateCallInstanceMethod(context_, checker::ETSEnumType::GET_NAME_METHOD_NAME, paramRefIdent);
991     auto *const returnStmt = AllocNode<ir::ReturnStatement>(callExpr);
992     ArenaVector<ir::Statement *> body(Allocator()->Adapter());
993     body.push_back(returnStmt);
994 
995     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
996     params.push_back(inputNameIdent);
997     auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);
998     auto *const function = MakeFunction({std::move(params), std::move(body), stringTypeAnnotation, enumDecl,
999                                          ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
1000     auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::DOLLAR_GET_METHOD_NAME, Allocator());
1001     function->SetIdent(functionIdent);
1002 
1003     MakeMethodDef(context_, enumClass, functionIdent, function);
1004 }
1005 
SetDefaultPositionInUnfilledClassNodes(const ir::ClassDeclaration * enumClassDecl,ir::TSEnumDeclaration const * const enumDecl)1006 void EnumLoweringPhase::SetDefaultPositionInUnfilledClassNodes(const ir::ClassDeclaration *enumClassDecl,
1007                                                                ir::TSEnumDeclaration const *const enumDecl)
1008 {
1009     // Range of "default" value is one point, because that code is not exist in initial source code
1010     // but createad by enum at this point
1011     const auto &defautlRange = lexer::SourceRange(enumDecl->Range().start, enumDecl->Range().start);
1012     enumClassDecl->IterateRecursively([&defautlRange](ir::AstNode *ast) -> void {
1013         // If SourcePostion is not set before, we set "default" which point to start of enum
1014         if (ast->Range().start.Program() == nullptr) {
1015             ast->SetRange(defautlRange);
1016         }
1017     });
1018 }
1019 
Allocator()1020 ArenaAllocator *EnumLoweringPhase::Allocator()
1021 {
1022     return context_->Allocator();
1023 }
1024 
1025 template <typename T, typename... Args>
AllocNode(Args &&...args)1026 T *EnumLoweringPhase::AllocNode(Args &&...args)
1027 {
1028     return context_->AllocNode<T>(std::forward<Args>(args)...);
1029 }
1030 
1031 }  // namespace ark::es2panda::compiler
1032