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