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