* Copyright (c) 2021-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "enumLowering.h"
#include "checker/ETSchecker.h"
#include "checker/types/ets/etsEnumType.h"
#include "checker/types/type.h"
#include "compiler/lowering/ets/binaryExpressionLowering.h"
#include "compiler/lowering/scopesInit/scopesInitPhase.h"
#include "compiler/lowering/util.h"
#include "lexer/token/number.h"
#include "util/es2pandaMacros.h"
#include "varbinder/ETSBinder.h"
namespace ark::es2panda::compiler {
namespace {
[[nodiscard]] ir::ETSParameterExpression *MakeFunctionParam(public_lib::Context *ctx, const util::StringView &name,
ir::TypeNode *const typeAnnotation)
{
auto *const paramIdent = ctx->AllocNode<ir::Identifier>(name, typeAnnotation, ctx->Allocator());
paramIdent->SetRange(typeAnnotation->Range());
auto *const param = ctx->AllocNode<ir::ETSParameterExpression>(paramIdent, false, ctx->Allocator());
return param;
}
[[nodiscard]] ir::Identifier *MakeParamRefIdent(public_lib::Context *ctx, ir::ETSParameterExpression *paramExpr)
{
auto *const refIdent = ctx->AllocNode<ir::Identifier>(paramExpr->Ident()->Name(), ctx->Allocator());
ES2PANDA_ASSERT(refIdent);
refIdent->SetRange(paramExpr->Ident()->Range());
refIdent->SetVariable(paramExpr->Ident()->Variable());
return refIdent;
}
[[nodiscard]] ir::ETSTypeReference *MakeTypeReference(public_lib::Context *ctx, const util::StringView &name)
{
auto *const ident = ctx->AllocNode<ir::Identifier>(name, ctx->Allocator());
auto *const referencePart = ctx->AllocNode<ir::ETSTypeReferencePart>(ident, ctx->Allocator());
return ctx->AllocNode<ir::ETSTypeReference>(referencePart, ctx->Allocator());
}
ir::MethodDefinition *MakeMethodDef(public_lib::Context *ctx, ir::ClassDefinition *enumClass,
ir::Identifier *const ident, ir::ScriptFunction *const function)
{
auto *const functionExpr = ctx->AllocNode<ir::FunctionExpression>(function);
auto *const identClone = ident->Clone(ctx->Allocator(), nullptr);
auto *const methodDef = ctx->AllocNode<ir::MethodDefinition>(
ir::MethodDefinitionKind::METHOD, identClone, functionExpr, function->Modifiers(), ctx->Allocator(), false);
methodDef->SetParent(enumClass);
enumClass->EmplaceBody(methodDef);
return methodDef;
}
}
void EnumLoweringPhase::LogError(const diagnostic::DiagnosticKind &diagnostic,
const util::DiagnosticMessageParams &diagnosticParams,
const lexer::SourcePosition &pos)
{
context_->diagnosticEngine->LogDiagnostic(diagnostic, diagnosticParams, pos);
}
template <EnumLoweringPhase::EnumType TYPE_NODE>
void EnumLoweringPhase::HandleIntEnumLongLiteralError(ir::TSEnumMember *member, const lexer::Number &asNumber,
bool &hasLoggedError, bool *hasLongLiteral)
{
if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::INT) {
*hasLongLiteral = true;
LogError(diagnostic::ERROR_NO_ENUM_MIXED_TYPES, {}, member->Init()->Start());
if (member->IsGenerated() && asNumber.GetLong() == std::numeric_limits<int64_t>::min()) {
LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, member->Init()->Start());
hasLoggedError = true;
}
}
}
template <EnumLoweringPhase::EnumType TYPE_NODE>
bool EnumLoweringPhase::CheckEnumMemberType(const ArenaVector<ir::AstNode *> &enumMembers, bool &hasLoggedError,
bool isAnnoted, bool *hasLongLiteral)
{
for (auto *member : enumMembers) {
auto *init = member->AsTSEnumMember()->Init();
auto mixedTypesError = [&](bool typeCondition, bool checkGenerated) -> bool {
if (checkGenerated && member->AsTSEnumMember()->IsGenerated()) {
LogError(diagnostic::ENUM_NON_INT_TYPE_ALL_ITEMS_INIT, {}, init->Start());
hasLoggedError = true;
} else if (!typeCondition) {
LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, init->Start());
hasLoggedError = true;
}
return typeCondition;
};
if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::STRING) {
mixedTypesError(init->IsStringLiteral(), true);
continue;
}
if (!mixedTypesError(init->IsNumberLiteral(), false)) {
continue;
}
auto &asNumber = init->AsNumberLiteral()->Number();
if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::INT) {
if (isAnnoted) {
mixedTypesError(asNumber.CanGetValue<int32_t>(), false);
continue;
}
if (!mixedTypesError(asNumber.IsInteger(), false) || !asNumber.IsLong()) {
continue;
}
HandleIntEnumLongLiteralError<TYPE_NODE>(member->AsTSEnumMember(), asNumber, hasLoggedError,
hasLongLiteral);
} else if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::LONG) {
mixedTypesError(asNumber.CanGetValue<int64_t>(), false);
} else if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::FLOAT) {
mixedTypesError(asNumber.IsFloat(), true);
} else if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::DOUBLE) {
mixedTypesError(init->AsNumberLiteral(), true);
} else if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::BYTE) {
mixedTypesError(asNumber.CanGetValue<int8_t>(), false);
} else if constexpr (TYPE_NODE == EnumLoweringPhase::EnumType::SHORT) {
mixedTypesError(asNumber.CanGetValue<int16_t>(), false);
} else {
ES2PANDA_UNREACHABLE();
}
}
return !hasLoggedError;
}
[[nodiscard]] ir::ScriptFunction *EnumLoweringPhase::MakeFunction(FunctionInfo &&functionInfo)
{
ir::BlockStatement *bodyBlock = nullptr;
if (functionInfo.enumDecl->IsDeclare()) {
functionInfo.flags |= ir::ModifierFlags::DECLARE;
} else {
bodyBlock = AllocNode<ir::BlockStatement>(Allocator(), std::move(functionInfo.body));
}
auto *const function = AllocNode<ir::ScriptFunction>(
Allocator(), ir::ScriptFunction::ScriptFunctionData {
bodyBlock,
ir::FunctionSignature(nullptr, std::move(functionInfo.params), functionInfo.returnTypeAnnotation),
ir::ScriptFunctionFlags::METHOD, functionInfo.flags});
return function;
}
template <typename ElementMaker>
[[nodiscard]] ir::Identifier *EnumLoweringPhase::MakeArray(const ir::TSEnumDeclaration *const enumDecl,
ir::ClassDefinition *const enumClass,
const util::StringView &name,
ir::TypeNode *const typeAnnotation,
ElementMaker &&elementMaker)
{
ArenaVector<ir::Expression *> elements(Allocator()->Adapter());
elements.reserve(enumDecl->Members().size());
for (const auto *const member : enumDecl->Members()) {
elements.push_back(elementMaker(member->AsTSEnumMember()));
}
auto *const arrayExpr = AllocNode<ir::ArrayExpression>(std::move(elements), Allocator());
auto *const arrayIdent = AllocNode<ir::Identifier>(name, Allocator());
auto *const arrayClassProp = AllocNode<ir::ClassProperty>(
arrayIdent, arrayExpr, typeAnnotation,
ir::ModifierFlags::STATIC | ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY, Allocator(), false);
ES2PANDA_ASSERT(arrayClassProp != nullptr);
arrayClassProp->SetParent(enumClass);
enumClass->EmplaceBody(arrayClassProp);
return arrayIdent;
}
ir::Expression *EnumLoweringPhase::CheckEnumTypeForItemFields(EnumType enumType, ir::TSEnumMember *const member)
{
ir::Expression *valueArgument = nullptr;
switch (enumType) {
case EnumType::INT: {
auto enumFieldValue =
member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<std::int32_t>();
valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
break;
}
case EnumType::LONG: {
auto enumFieldValue =
member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<std::int64_t>();
valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
break;
}
case EnumType::DOUBLE: {
auto enumFieldValue = member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<double>();
valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
break;
}
case EnumType::FLOAT: {
auto enumFieldValue = member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<float>();
valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
break;
}
case EnumType::BYTE: {
auto enumFieldValue = member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<int8_t>();
valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
break;
}
case EnumType::SHORT: {
auto enumFieldValue = member->AsTSEnumMember()->Init()->AsNumberLiteral()->Number().GetValue<int16_t>();
valueArgument = AllocNode<ir::NumberLiteral>(lexer::Number(enumFieldValue));
break;
}
case EnumType::STRING: {
auto enumFieldValue = member->AsTSEnumMember()->Init()->AsStringLiteral()->Str();
valueArgument = AllocNode<ir::StringLiteral>(enumFieldValue);
break;
}
case EnumType::NOT_SPECIFIED: {
LogError(diagnostic::ENUM_INVALID_INIT, {}, member->AsTSEnumMember()->Init()->Start());
break;
}
}
return valueArgument;
}
void EnumLoweringPhase::CreateEnumItemFields(const ir::TSEnumDeclaration *const enumDecl,
ir::ClassDefinition *const enumClass, EnumType enumType)
{
static_assert(ORDINAL_TYPE == ir::PrimitiveType::INT);
int32_t ordinal = 0;
auto createEnumItemField = [this, enumClass, enumType, &ordinal](ir::TSEnumMember *const member) {
auto *const enumMemberIdent =
AllocNode<ir::Identifier>(member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), Allocator());
auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
auto *const ordinalLiteral = AllocNode<ir::NumberLiteral>(lexer::Number(ordinal));
ordinal++;
ArenaVector<ir::Expression *> newExprArgs(Allocator()->Adapter());
newExprArgs.push_back(ordinalLiteral);
ir::Expression *valueArgument = CheckEnumTypeForItemFields(enumType, member);
newExprArgs.push_back(valueArgument);
auto *const nameIdent = member->AsTSEnumMember()->Key()->AsIdentifier();
auto *const nameLiteral = AllocNode<ir::StringLiteral>(nameIdent->Name());
newExprArgs.push_back(nameLiteral);
auto enumTypeAnnotation1 = enumTypeAnnotation->Clone(Allocator(), nullptr);
auto *const newExpression =
AllocNode<ir::ETSNewClassInstanceExpression>(enumTypeAnnotation1, std::move(newExprArgs));
auto *field = AllocNode<ir::ClassProperty>(
enumMemberIdent, newExpression, enumTypeAnnotation,
ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC | ir::ModifierFlags::READONLY, Allocator(), false);
enumMemberIdent->SetRange(member->Key()->Range());
newExpression->SetRange(member->Init()->Range());
field->SetRange(member->Range());
field->SetOrigEnumMember(member->AsTSEnumMember());
field->SetParent(enumClass);
return field;
};
for (auto *const member : enumDecl->Members()) {
enumClass->EmplaceBody(createEnumItemField(member->AsTSEnumMember()));
}
}
ir::TypeNode *EnumLoweringPhase::CreateType(EnumLoweringPhase::EnumType enumType, ir::TSEnumDeclaration *enumDecl,
ir::ClassDefinition *parent)
{
if (enumDecl->TypeAnnotation() != nullptr) {
if (parent == nullptr) {
return enumDecl->TypeAnnotation();
}
if (parent != nullptr) {
return enumDecl->TypeAnnotation()->Clone(Allocator(), parent);
}
} else {
switch (enumType) {
case EnumLoweringPhase::EnumType::INT: {
return context_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT, context_->Allocator());
}
case EnumLoweringPhase::EnumType::LONG: {
return context_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::LONG, context_->Allocator());
}
case EnumLoweringPhase::EnumType::DOUBLE: {
return context_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::DOUBLE, context_->Allocator());
}
case EnumLoweringPhase::EnumType::FLOAT: {
return context_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::FLOAT, context_->Allocator());
}
case EnumLoweringPhase::EnumType::BYTE: {
return context_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::BYTE, context_->Allocator());
}
case EnumLoweringPhase::EnumType::SHORT: {
return context_->AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::SHORT, context_->Allocator());
}
case EnumLoweringPhase::EnumType::STRING: {
return MakeTypeReference(context_, EnumLoweringPhase::STRING_REFERENCE_TYPE);
}
default: {
ES2PANDA_UNREACHABLE();
}
}
}
return nullptr;
}
ir::ClassDeclaration *EnumLoweringPhase::CreateClass(ir::TSEnumDeclaration *const enumDecl,
const DeclarationFlags flags, EnumType enumType)
{
auto *ident = Allocator()->New<ir::Identifier>(enumDecl->Key()->Name(), Allocator());
ident->SetRange(enumDecl->Key()->Range());
auto enumFlag = !(enumType == EnumType::STRING) ? ir::ClassDefinitionModifiers::NUMERIC_ENUM_TRANSFORMED
: ir::ClassDefinitionModifiers::STRING_ENUM_TRANSFORMED;
auto baseClassDefinitionFlag = ir::ClassDefinitionModifiers::CLASS_DECL | enumFlag;
auto typeParamsVector = ArenaVector<ir::TypeNode *>(Allocator()->Adapter());
typeParamsVector.push_back(CreateType(enumType, enumDecl, nullptr));
auto *typeParam = AllocNode<ir::TSTypeParameterInstantiation>(std::move(typeParamsVector));
auto *identRef = AllocNode<ir::Identifier>(util::StringView(BASE_CLASS_NAME), Allocator());
auto *typeRefPart = AllocNode<ir::ETSTypeReferencePart>(identRef, typeParam, nullptr, Allocator());
auto *superClass = AllocNode<ir::ETSTypeReference>(typeRefPart, Allocator());
auto *classDef = AllocNode<ir::ClassDefinition>(
Allocator(), ident,
flags.isLocal ? baseClassDefinitionFlag | ir::ClassDefinitionModifiers::LOCAL : baseClassDefinitionFlag,
enumDecl->IsDeclare() ? ir::ModifierFlags::FINAL | ir::ModifierFlags::DECLARE : ir::ModifierFlags::FINAL,
enumDecl->Language());
classDef->SetAnnotations(std::move(enumDecl->Annotations()));
classDef->SetSuper(superClass);
auto *classDecl = AllocNode<ir::ClassDeclaration>(classDef, Allocator());
if (enumDecl->IsExported()) {
classDecl->AddModifier(ir::ModifierFlags::EXPORT);
classDef->AddModifier(ir::ModifierFlags::EXPORT);
program_->GlobalClass()->AddToExportedClasses(classDecl);
} else if (enumDecl->IsDefaultExported()) {
classDecl->AddModifier(ir::ModifierFlags::DEFAULT_EXPORT);
classDef->AddModifier(ir::ModifierFlags::DEFAULT_EXPORT);
} else if (enumDecl->HasExportAlias()) {
classDecl->AddModifier(ir::ModifierFlags::EXPORT_WITH_ALIAS);
classDef->AddModifier(ir::ModifierFlags::EXPORT_WITH_ALIAS);
}
classDef->SetOrigEnumDecl(enumDecl);
CreateOrdinalField(classDef);
if (!enumDecl->IsDeclare()) {
CreateCCtorForEnumClass(classDef);
}
ir::TypeNode *enumTypeAnnotation = CreateType(enumType, enumDecl, classDef);
CreateCtorForEnumClass(classDef, enumTypeAnnotation);
classDecl->SetRange(enumDecl->Range());
return classDecl;
}
void EnumLoweringPhase::CreateCCtorForEnumClass(ir::ClassDefinition *const enumClass)
{
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
auto *id = AllocNode<ir::Identifier>(compiler::Signatures::CCTOR, Allocator());
ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
auto *func = AllocNode<ir::ScriptFunction>(
Allocator(),
ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::HIDDEN,
ir::ModifierFlags::STATIC, Language(Language::Id::ETS)});
ES2PANDA_ASSERT(func != nullptr);
func->SetIdent(id);
auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
auto *const identClone = id->Clone(Allocator(), nullptr);
auto *const methodDef =
AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, identClone, funcExpr,
ir::ModifierFlags::PRIVATE | ir::ModifierFlags::STATIC, Allocator(), false);
ES2PANDA_ASSERT(methodDef != nullptr);
methodDef->SetParent(enumClass);
enumClass->EmplaceBody(methodDef);
}
ir::ClassProperty *EnumLoweringPhase::CreateOrdinalField(ir::ClassDefinition *const enumClass)
{
auto *const fieldIdent = Allocator()->New<ir::Identifier>(ORDINAL_NAME, Allocator());
auto *const intTypeAnnotation = Allocator()->New<ir::ETSPrimitiveType>(ORDINAL_TYPE, Allocator());
auto *field =
AllocNode<ir::ClassProperty>(fieldIdent, nullptr, intTypeAnnotation,
ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY, Allocator(), false);
enumClass->EmplaceBody(field);
field->SetParent(enumClass);
return field;
}
ir::ScriptFunction *EnumLoweringPhase::CreateFunctionForCtorOfEnumClass(ir::ClassDefinition *const enumClass,
ir::TypeNode *enumType)
{
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
auto *const intTypeAnnotation = AllocNode<ir::ETSPrimitiveType>(ORDINAL_TYPE, Allocator());
auto *const inputOrdinalParam = MakeFunctionParam(context_, PARAM_ORDINAL, intTypeAnnotation);
params.push_back(inputOrdinalParam);
auto *const inputValueParam = MakeFunctionParam(context_, PARAM_VALUE, enumType);
params.push_back(inputValueParam);
auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);
auto *const inputNameParam = MakeFunctionParam(context_, PARAM_NAME, stringTypeAnnotation);
params.push_back(inputNameParam);
ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
auto scriptFlags = ir::ScriptFunctionFlags::CONSTRUCTOR;
scriptFlags |= enumClass->IsDeclare() ? ir::ScriptFunctionFlags::EXTERNAL : ir::ScriptFunctionFlags::NONE;
auto *func = AllocNode<ir::ScriptFunction>(
Allocator(),
ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
scriptFlags,
ir::ModifierFlags::CONSTRUCTOR |
ir::ModifierFlags::PRIVATE,
Language(Language::Id::ETS)});
func->SetIdent(AllocNode<ir::Identifier>("constructor", Allocator()));
if (enumClass->IsDeclare()) {
return func;
}
auto *valueIdentifier = AllocNode<ir::Identifier>(PARAM_VALUE, Allocator());
valueIdentifier->SetVariable(inputValueParam->Ident()->Variable());
auto *nameIdentifier = AllocNode<ir::Identifier>(PARAM_NAME, Allocator());
nameIdentifier->SetVariable(inputNameParam->Ident()->Variable());
ArenaVector<ir::Expression *> callArguments(Allocator()->Adapter());
auto *callee = AllocNode<ir::SuperExpression>();
callArguments.push_back(valueIdentifier);
callArguments.push_back(nameIdentifier);
auto *superConstructorCall = AllocNode<ir::CallExpression>(callee, std::move(callArguments), nullptr, false);
auto *superCallStatement = AllocNode<ir::ExpressionStatement>(superConstructorCall);
superCallStatement->SetParent(body);
body->AddStatement(superCallStatement);
auto *leftHandSide = AllocNode<ir::MemberExpression>(Allocator()->New<ir::ThisExpression>(),
Allocator()->New<ir::Identifier>(ORDINAL_NAME, Allocator()),
ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
auto *rightHandSide = AllocNode<ir::Identifier>(PARAM_ORDINAL, Allocator());
rightHandSide->SetVariable(inputOrdinalParam->Ident()->Variable());
auto *initializer =
AllocNode<ir::AssignmentExpression>(leftHandSide, rightHandSide, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto initStatement = AllocNode<ir::ExpressionStatement>(initializer);
initStatement->SetParent(body);
body->AddStatement(initStatement);
return func;
}
void EnumLoweringPhase::CreateCtorForEnumClass(ir::ClassDefinition *const enumClass, ir::TypeNode *enumType)
{
auto *func = CreateFunctionForCtorOfEnumClass(enumClass, enumType);
auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
auto *const identClone = func->Id()->Clone(Allocator(), nullptr);
auto *const methodDef = AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::CONSTRUCTOR, identClone, funcExpr,
ir::ModifierFlags::PRIVATE, Allocator(), false);
methodDef->SetParent(enumClass);
enumClass->EmplaceBody(methodDef);
}
void EnumLoweringPhase::ProcessEnumClassDeclaration(ir::TSEnumDeclaration *const enumDecl,
const DeclarationFlags &flags, ir::ClassDeclaration *enumClassDecl)
{
varbinder::Variable *var = nullptr;
auto *ident = enumClassDecl->Definition()->Ident();
if (flags.isLocal) {
auto *scope = NearestScope(enumDecl->Parent());
ES2PANDA_ASSERT(scope);
auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
ES2PANDA_ASSERT(scope);
scope->EraseBinding(ident->Name());
InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::ALL);
} else if (flags.isTopLevel) {
auto *scope = program_->GlobalClassScope();
ES2PANDA_ASSERT(scope);
auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
ES2PANDA_ASSERT(scope);
scope->StaticDeclScope()->EraseBinding(ident->Name());
InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::ALL);
if (var != nullptr) {
program_->GlobalScope()->InsertBinding(ident->Name(), var);
}
} else if (flags.isNamespace) {
auto *scope = enumDecl->Parent()->Scope();
auto localCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder_, scope);
scope->AsClassScope()->StaticDeclScope()->EraseBinding(ident->Name());
InitScopesPhaseETS::RunExternalNode(enumClassDecl, varbinder_);
var = varbinder_->GetScope()->FindLocal(ident->Name(), varbinder::ResolveBindingOptions::DECLARATION);
} else {
ES2PANDA_UNREACHABLE();
}
if (var != nullptr) {
var->RemoveFlag(varbinder::VariableFlags::CLASS);
var->AddFlag(varbinder::VariableFlags::ENUM_LITERAL);
}
}
constexpr ir::PrimitiveType MapEnum(EnumLoweringPhase::EnumType type)
{
switch (type) {
case EnumLoweringPhase::EnumType::INT:
return ir::PrimitiveType::INT;
case EnumLoweringPhase::EnumType::LONG:
return ir::PrimitiveType::LONG;
case EnumLoweringPhase::EnumType::FLOAT:
return ir::PrimitiveType::FLOAT;
case EnumLoweringPhase::EnumType::DOUBLE:
return ir::PrimitiveType::DOUBLE;
case EnumLoweringPhase::EnumType::SHORT:
return ir::PrimitiveType::SHORT;
case EnumLoweringPhase::EnumType::BYTE:
return ir::PrimitiveType::BYTE;
default:
return ir::PrimitiveType::VOID;
}
}
template <EnumLoweringPhase::EnumType TYPE>
ir::ClassDeclaration *EnumLoweringPhase::CreateEnumNumericClassFromEnumDeclaration(
ir::TSEnumDeclaration *const enumDecl, const DeclarationFlags flags)
{
constexpr ir::PrimitiveType ENUM_TYPE = MapEnum(TYPE);
auto *const enumClassDecl = CreateClass(enumDecl, flags, TYPE);
auto *const enumClass = enumClassDecl->Definition();
CreateEnumItemFields(enumDecl, enumClass, TYPE);
auto *const itemsArrayIdent = CreateEnumItemsArray(enumDecl, enumClass);
CreateEnumGetValueOfMethod(enumDecl, enumClass, itemsArrayIdent);
if (TYPE != EnumType::STRING) {
CreateEnumFromValueMethod(enumDecl, enumClass, itemsArrayIdent, ENUM_TYPE);
} else {
CreateEnumFromValueMethod(enumDecl, enumClass, itemsArrayIdent, std::nullopt);
}
CreateEnumValuesMethod(enumDecl, enumClass, itemsArrayIdent);
CreateEnumGetOrdinalMethod(enumDecl, enumClass);
CreateEnumDollarGetMethod(enumDecl, enumClass);
SetDefaultPositionInUnfilledClassNodes(enumClassDecl, enumDecl);
enumClassDecl->SetParent(enumDecl->Parent());
ProcessEnumClassDeclaration(enumDecl, flags, enumClassDecl);
return enumClassDecl;
}
static EnumLoweringPhase::DeclarationFlags GetDeclFlags(ir::TSEnumDeclaration *const enumDecl)
{
return {enumDecl->Parent() != nullptr && enumDecl->Parent()->IsETSModule() &&
enumDecl->Parent()->AsETSModule()->IsETSScript(),
enumDecl->Parent() != nullptr && enumDecl->Parent()->IsBlockStatement(),
enumDecl->Parent() != nullptr && enumDecl->Parent()->IsClassDefinition() &&
enumDecl->Parent()->AsClassDefinition()->IsNamespaceTransformed()};
}
ir::ClassDeclaration *EnumLoweringPhase::CreateEnumClassByPrimitiveType(ir::TSEnumDeclaration *const enumDecl,
const DeclarationFlags &flags,
bool &hasLoggedError,
ir::TypeNode *typeAnnotation)
{
ir::PrimitiveType primitiveType = typeAnnotation->AsETSPrimitiveType()->GetPrimitiveType();
if ((primitiveType == ir::PrimitiveType::BYTE) &&
CheckEnumMemberType<EnumType::BYTE>(enumDecl->Members(), hasLoggedError, true)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::BYTE>(enumDecl, flags);
}
if ((primitiveType == ir::PrimitiveType::SHORT) &&
CheckEnumMemberType<EnumType::SHORT>(enumDecl->Members(), hasLoggedError, true)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::SHORT>(enumDecl, flags);
}
if ((primitiveType == ir::PrimitiveType::FLOAT) &&
CheckEnumMemberType<EnumType::FLOAT>(enumDecl->Members(), hasLoggedError, true)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::FLOAT>(enumDecl, flags);
}
if ((primitiveType == ir::PrimitiveType::LONG) &&
CheckEnumMemberType<EnumType::LONG>(enumDecl->Members(), hasLoggedError, true)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::LONG>(enumDecl, flags);
}
if ((primitiveType == ir::PrimitiveType::INT) &&
CheckEnumMemberType<EnumType::INT>(enumDecl->Members(), hasLoggedError, true)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::INT>(enumDecl, flags);
}
if ((primitiveType == ir::PrimitiveType::DOUBLE) &&
CheckEnumMemberType<EnumType::DOUBLE>(enumDecl->Members(), hasLoggedError, true)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::DOUBLE>(enumDecl, flags);
}
return nullptr;
}
void EnumLoweringPhase::CheckEnumInitializerConstraints(ir::TSEnumDeclaration *enumDecl)
{
bool hasNonConstExplicitInitializer = false;
for (auto *member : enumDecl->Members()) {
auto *enumMember = member->AsTSEnumMember();
auto *init = enumMember->Init();
if (hasNonConstExplicitInitializer && enumMember->IsGenerated()) {
LogError(diagnostic::ENUM_IMPLICIT_AFTER_NON_CONST_INIT, {}, enumMember->Start());
continue;
}
if (!enumMember->IsGenerated() && init != nullptr && !init->IsLiteral()) {
hasNonConstExplicitInitializer = true;
}
}
}
checker::AstNodePtr EnumLoweringPhase::TransformAnnotedEnumChildrenRecursively(checker::AstNodePtr &ast)
{
auto *enumDecl = ast->AsTSEnumDeclaration();
CheckEnumInitializerConstraints(enumDecl);
auto const flags = GetDeclFlags(enumDecl);
if (!flags.IsValid() || enumDecl->Members().empty()) {
return ast;
}
bool hasLoggedError = false;
auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init();
ir::TypeNode *typeAnnotation = enumDecl->TypeAnnotation();
auto isStringEnum = typeAnnotation->IsETSTypeReference() &&
(typeAnnotation->AsETSTypeReference()->BaseName()->Name() == STRING_REFERENCE_TYPE ||
typeAnnotation->AsETSTypeReference()->BaseName()->Name() == STRING_TYPE);
auto isNumberEnum =
typeAnnotation->IsETSTypeReference() && typeAnnotation->AsETSTypeReference()->BaseName()->Name() == NUMBER_TYPE;
if (!typeAnnotation->IsETSPrimitiveType() && !isStringEnum && !isNumberEnum) {
LogError(diagnostic::UNSUPPORTED_ENUM_TYPE, {}, itemInit->Start());
return ast;
}
if (isStringEnum) {
if (!CheckEnumMemberType<EnumLoweringPhase::EnumType::STRING>(enumDecl->Members(), hasLoggedError, true)) {
return ast;
}
return CreateEnumNumericClassFromEnumDeclaration<EnumType::STRING>(enumDecl, flags);
}
if (isNumberEnum &&
CheckEnumMemberType<EnumLoweringPhase::EnumType::DOUBLE>(enumDecl->Members(), hasLoggedError, true)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::DOUBLE>(enumDecl, flags);
}
auto *const enumClassDecl = CreateEnumClassByPrimitiveType(enumDecl, flags, hasLoggedError, typeAnnotation);
if (enumClassDecl != nullptr) {
return enumClassDecl;
}
if (!hasLoggedError) {
LogError(diagnostic::UNSUPPORTED_ENUM_TYPE, {}, itemInit->Start());
}
return ast;
}
checker::AstNodePtr EnumLoweringPhase::TransformEnumChildrenRecursively(checker::AstNodePtr &ast)
{
auto *enumDecl = ast->AsTSEnumDeclaration();
CheckEnumInitializerConstraints(enumDecl);
auto const flags = GetDeclFlags(enumDecl);
if (!flags.IsValid()) {
return ast;
}
if (enumDecl->Members().empty()) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::INT>(enumDecl, flags);
}
bool hasLoggedError = false;
bool hasLongLiteral = false;
auto *const itemInit = enumDecl->Members().front()->AsTSEnumMember()->Init();
if (itemInit->IsNumberLiteral()) {
if (((itemInit->AsNumberLiteral()->Number().IsInteger() || itemInit->AsNumberLiteral()->Number().IsLong()) &&
CheckEnumMemberType<EnumType::INT>(enumDecl->Members(), hasLoggedError, false, &hasLongLiteral))) {
auto res = hasLongLiteral ? CreateEnumNumericClassFromEnumDeclaration<EnumType::LONG>(enumDecl, flags)
: CreateEnumNumericClassFromEnumDeclaration<EnumType::INT>(enumDecl, flags);
return res;
}
} else if (itemInit->IsStringLiteral() &&
CheckEnumMemberType<EnumType::STRING>(enumDecl->Members(), hasLoggedError, false)) {
return CreateEnumNumericClassFromEnumDeclaration<EnumType::STRING>(enumDecl, flags);
}
if (!hasLoggedError) {
LogError(diagnostic::ERROR_ARKTS_NO_ENUM_MIXED_TYPES, {}, itemInit->Start());
}
return ast;
}
bool EnumLoweringPhase::PerformForProgram(parser::Program *program)
{
if (program->Extension() != ScriptExtension::ETS) {
return true;
}
program_ = program;
program->Ast()->TransformChildrenRecursively(
[this](checker::AstNodePtr ast) -> checker::AstNodePtr {
if (ast->IsTSEnumDeclaration()) {
ir::TSEnumDeclaration *enumDecl = ast->AsTSEnumDeclaration();
if (enumDecl->TypeAnnotation() != nullptr) {
enumDecl->TypeAnnotation()->SetParent(ast->AsTSEnumDeclaration());
return TransformAnnotedEnumChildrenRecursively(ast);
}
return TransformEnumChildrenRecursively(ast);
}
return ast;
},
Name());
return true;
}
ir::Identifier *EnumLoweringPhase::CreateEnumItemsArray(const ir::TSEnumDeclaration *const enumDecl,
ir::ClassDefinition *const enumClass)
{
auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
auto *const arrayTypeAnnotation = AllocNode<ir::TSArrayType>(enumTypeAnnotation, Allocator());
return MakeArray(enumDecl, enumClass, ITEMS_ARRAY_NAME, arrayTypeAnnotation,
[this, enumClass, enumDecl](const ir::TSEnumMember *const member) {
auto *const enumTypeIdent =
AllocNode<ir::Identifier>(enumClass->Ident()->Name(),
Allocator());
enumTypeIdent->SetRange(enumDecl->Key()->Range());
auto *const enumMemberIdent = AllocNode<ir::Identifier>(
member->AsTSEnumMember()->Key()->AsIdentifier()->Name(), context_->Allocator());
enumMemberIdent->SetRange(member->AsTSEnumMember()->Key()->Range());
auto *const enumMemberExpr = AllocNode<ir::MemberExpression>(
enumTypeIdent, enumMemberIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
return enumMemberExpr;
});
}
ir::MemberExpression *EnumLoweringPhase::CreateOrdinalAccessExpression()
{
auto *thisExpr = AllocNode<ir::ThisExpression>();
auto *fieldIdentifier = AllocNode<ir::Identifier>(ORDINAL_NAME, Allocator());
auto *ordinalAccessExpr = AllocNode<ir::MemberExpression>(thisExpr, fieldIdentifier,
ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
return ordinalAccessExpr;
}
namespace {
ir::MemberExpression *CreateStaticAccessMemberExpression(public_lib::Context *ctx,
ir::Identifier *const enumClassIdentifier,
ir::Identifier *const arrayIdentifier)
{
auto *const enumClassIdentifierClone = enumClassIdentifier->Clone(ctx->Allocator(), nullptr);
auto *const arrayIdentifierClone = arrayIdentifier->Clone(ctx->Allocator(), nullptr);
return ctx->AllocNode<ir::MemberExpression>(enumClassIdentifierClone, arrayIdentifierClone,
ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
}
ir::ThrowStatement *CreateThrowStatement(public_lib::Context *ctx, ir::ETSParameterExpression *const parameter,
const util::UString &messageString)
{
auto *const paramRefIdent = MakeParamRefIdent(ctx, parameter);
auto *const message = ctx->AllocNode<ir::StringLiteral>(messageString.View());
auto *const newExprArg =
ctx->AllocNode<ir::BinaryExpression>(message, paramRefIdent, lexer::TokenType::PUNCTUATOR_PLUS);
paramRefIdent->SetParent(newExprArg);
ArenaVector<ir::Expression *> newExprArgs(ctx->Allocator()->Adapter());
newExprArgs.push_back(newExprArg);
auto *const exceptionReference = MakeTypeReference(ctx, "Error");
auto *const newExpr = ctx->AllocNode<ir::ETSNewClassInstanceExpression>(exceptionReference, std::move(newExprArgs));
return ctx->AllocNode<ir::ThrowStatement>(newExpr);
}
ir::CallExpression *CreateCallInstanceMethod(public_lib::Context *ctx, std::string_view methodName,
ir::Expression *thisArg)
{
auto methodId = ctx->AllocNode<ir::Identifier>(methodName, ctx->Allocator());
auto callee = ctx->AllocNode<ir::MemberExpression>(thisArg, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS,
false, false);
ArenaVector<ir::Expression *> callArguments({}, ctx->Allocator()->Adapter());
return ctx->AllocNode<ir::CallExpression>(callee, std::move(callArguments), nullptr, false);
}
}
namespace {
ir::VariableDeclaration *CreateForLoopInitVariableDeclaration(public_lib::Context *ctx,
ir::Identifier *const enumClassIdentifier,
ir::Identifier *const namesArrayIdentifier,
ir::Identifier *const loopIdentifier)
{
static_assert(EnumLoweringPhase::ORDINAL_TYPE == ir::PrimitiveType::INT);
auto *const lengthIdent = ctx->AllocNode<ir::Identifier>("length", ctx->Allocator());
auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(ctx, enumClassIdentifier, namesArrayIdentifier);
auto *const arrayLengthExpr = ctx->AllocNode<ir::MemberExpression>(
propertyAccessExpr, lengthIdent, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
auto *const oneLiteral = ctx->AllocNode<ir::NumberLiteral>(lexer::Number((int32_t)1));
auto *const init =
ctx->AllocNode<ir::BinaryExpression>(arrayLengthExpr, oneLiteral, lexer::TokenType::PUNCTUATOR_MINUS);
auto *const decl = ctx->AllocNode<ir::VariableDeclarator>(ir::VariableDeclaratorFlag::LET, loopIdentifier, init);
ES2PANDA_ASSERT(loopIdentifier);
loopIdentifier->SetParent(decl);
ArenaVector<ir::VariableDeclarator *> decls(ctx->Allocator()->Adapter());
decls.push_back(decl);
auto *const declaration = ctx->AllocNode<ir::VariableDeclaration>(
ir::VariableDeclaration::VariableDeclarationKind::LET, ctx->Allocator(), std::move(decls));
ES2PANDA_ASSERT(decl);
decl->SetParent(declaration);
return declaration;
}
ir::BinaryExpression *CreateForLoopTest(public_lib::Context *ctx, ir::Identifier *const loopIdentifier)
{
auto *const zeroLiteral = ctx->AllocNode<ir::NumberLiteral>(lexer::Number((int32_t)0));
auto *const forLoopIdentClone = loopIdentifier->Clone(ctx->Allocator(), nullptr);
auto *const binaryExpr = ctx->AllocNode<ir::BinaryExpression>(forLoopIdentClone, zeroLiteral,
lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL);
return binaryExpr;
}
ir::UpdateExpression *CreateForLoopUpdate(public_lib::Context *ctx, ir::Identifier *const loopIdentifier)
{
auto *const forLoopIdentClone = loopIdentifier->Clone(ctx->Allocator(), nullptr);
auto *const incrementExpr =
ctx->AllocNode<ir::UpdateExpression>(forLoopIdentClone, lexer::TokenType::PUNCTUATOR_MINUS_MINUS, true);
return incrementExpr;
}
ir::ForUpdateStatement *CreateForLoop(public_lib::Context *ctx, ir::ClassDefinition *const enumClass,
ir::Identifier *const itemsArrayIdent, std::string_view methodName,
ir::ETSParameterExpression *inputIdent)
{
auto *const forLoopIIdent = ctx->AllocNode<ir::Identifier>(EnumLoweringPhase::IDENTIFIER_I, ctx->Allocator());
auto *const forLoopInitVarDecl =
CreateForLoopInitVariableDeclaration(ctx, enumClass->Ident(), itemsArrayIdent, forLoopIIdent);
auto *const forLoopTest = CreateForLoopTest(ctx, forLoopIIdent);
auto *const forLoopUpdate = CreateForLoopUpdate(ctx, forLoopIIdent);
auto *const forLoopIdentCloneForCond = forLoopIIdent->Clone(ctx->Allocator(), nullptr);
auto *const itemsArrayStaticAccessForCond =
CreateStaticAccessMemberExpression(ctx, enumClass->Ident(), itemsArrayIdent);
auto *const itemsArrayElementForCond = ctx->AllocNode<ir::MemberExpression>(
itemsArrayStaticAccessForCond, forLoopIdentCloneForCond, ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
ir::BinaryExpression *condExpr = nullptr;
auto *const paramRefIdent = MakeParamRefIdent(ctx, inputIdent);
if (methodName == checker::ETSEnumType::GET_VALUE_OF_METHOD_NAME) {
auto *const getNameCall =
CreateCallInstanceMethod(ctx, checker::ETSEnumType::GET_NAME_METHOD_NAME, itemsArrayElementForCond);
condExpr = ctx->AllocNode<ir::BinaryExpression>(paramRefIdent, getNameCall, lexer::TokenType::PUNCTUATOR_EQUAL);
} else if (methodName == checker::ETSEnumType::FROM_VALUE_METHOD_NAME) {
auto *const valueOfCall =
CreateCallInstanceMethod(ctx, checker::ETSEnumType::VALUE_OF_METHOD_NAME, itemsArrayElementForCond);
condExpr = ctx->AllocNode<ir::BinaryExpression>(valueOfCall, paramRefIdent, lexer::TokenType::PUNCTUATOR_EQUAL);
}
ES2PANDA_ASSERT(condExpr != nullptr);
paramRefIdent->SetParent(condExpr);
auto *const forLoopIdentCloneForReturn = forLoopIIdent->Clone(ctx->Allocator(), nullptr);
auto *const itemsArrayStaticAccessForReturn =
CreateStaticAccessMemberExpression(ctx, enumClass->Ident(), itemsArrayIdent);
auto *const itemsArrayElementForReturn =
ctx->AllocNode<ir::MemberExpression>(itemsArrayStaticAccessForReturn, forLoopIdentCloneForReturn,
ir::MemberExpressionKind::ELEMENT_ACCESS, true, false);
auto *const returnStmt = ctx->AllocNode<ir::ReturnStatement>(itemsArrayElementForReturn);
auto *const ifStmt = ctx->AllocNode<ir::IfStatement>(condExpr, returnStmt, nullptr);
auto *const forLoop =
ctx->AllocNode<ir::ForUpdateStatement>(forLoopInitVarDecl, forLoopTest, forLoopUpdate, ifStmt);
return forLoop;
}
}
void EnumLoweringPhase::CreateEnumGetValueOfMethod(const ir::TSEnumDeclaration *const enumDecl,
ir::ClassDefinition *const enumClass,
ir::Identifier *const itemsArrayIdent)
{
auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);
auto *const inputNameIdent = MakeFunctionParam(context_, PARAM_NAME, stringTypeAnnotation);
auto *const forLoop = CreateForLoop(context_, enumClass, itemsArrayIdent,
checker::ETSEnumType::GET_VALUE_OF_METHOD_NAME, inputNameIdent);
util::UString messageString(util::StringView("No enum constant "), Allocator());
messageString.Append(enumClass->Ident()->Name());
messageString.Append('.');
auto *const throwStmt = CreateThrowStatement(context_, inputNameIdent, messageString);
ArenaVector<ir::Statement *> body(Allocator()->Adapter());
body.push_back(forLoop);
body.push_back(throwStmt);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
params.push_back(inputNameIdent);
auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
auto *const function = MakeFunction({std::move(params), std::move(body), enumTypeAnnotation, enumDecl,
ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::GET_VALUE_OF_METHOD_NAME, Allocator());
function->SetIdent(functionIdent);
MakeMethodDef(context_, enumClass, functionIdent, function);
}
void EnumLoweringPhase::CreateEnumFromValueMethod(ir::TSEnumDeclaration *const enumDecl,
ir::ClassDefinition *const enumClass,
ir::Identifier *const itemsArrayIdent,
std::optional<ir::PrimitiveType> primitiveType)
{
ir::TypeNode *typeAnnotation;
if (primitiveType.has_value()) {
if (enumDecl->TypeAnnotation() != nullptr) {
typeAnnotation = enumDecl->TypeAnnotation()->Clone(Allocator(), enumClass);
} else {
typeAnnotation = AllocNode<ir::ETSPrimitiveType>(primitiveType.value(), Allocator())->AsTypeNode();
}
} else {
typeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE)->AsTypeNode();
}
auto *const inputValueIdent = MakeFunctionParam(context_, PARAM_VALUE, typeAnnotation);
auto *const forLoop = CreateForLoop(context_, enumClass, itemsArrayIdent,
checker::ETSEnumType::FROM_VALUE_METHOD_NAME, inputValueIdent);
util::UString messageString(util::StringView("No enum "), Allocator());
messageString.Append(enumClass->Ident()->Name());
messageString.Append(" with value ");
auto *const throwStmt = CreateThrowStatement(context_, inputValueIdent, messageString);
ArenaVector<ir::Statement *> body(Allocator()->Adapter());
body.push_back(forLoop);
body.push_back(throwStmt);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
params.push_back(inputValueIdent);
auto *const enumTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
auto *const function = MakeFunction({std::move(params), std::move(body), enumTypeAnnotation, enumDecl,
ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::FROM_VALUE_METHOD_NAME, Allocator());
function->SetIdent(functionIdent);
MakeMethodDef(context_, enumClass, functionIdent, function);
}
void EnumLoweringPhase::CreateEnumValuesMethod(const ir::TSEnumDeclaration *const enumDecl,
ir::ClassDefinition *const enumClass,
ir::Identifier *const itemsArrayIdent)
{
auto *const propertyAccessExpr = CreateStaticAccessMemberExpression(context_, enumClass->Ident(), itemsArrayIdent);
auto *const returnStmt = AllocNode<ir::ReturnStatement>(propertyAccessExpr);
ArenaVector<ir::Statement *> body(Allocator()->Adapter());
body.push_back(returnStmt);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
auto *const enumArrayTypeAnnotation =
AllocNode<ir::TSArrayType>(MakeTypeReference(context_, enumClass->Ident()->Name()), Allocator());
auto *const function = MakeFunction({std::move(params), std::move(body), enumArrayTypeAnnotation, enumDecl,
ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::VALUES_METHOD_NAME, Allocator());
function->SetIdent(functionIdent);
MakeMethodDef(context_, enumClass, functionIdent, function);
}
void EnumLoweringPhase::CreateEnumGetOrdinalMethod(const ir::TSEnumDeclaration *const enumDecl,
ir::ClassDefinition *const enumClass)
{
auto ordinalAccessExpr = CreateOrdinalAccessExpression();
auto *const returnStmt = AllocNode<ir::ReturnStatement>(ordinalAccessExpr);
ArenaVector<ir::Statement *> body(Allocator()->Adapter());
body.push_back(returnStmt);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
auto *const intTypeAnnotation = Allocator()->New<ir::ETSPrimitiveType>(ORDINAL_TYPE, Allocator());
auto *const function =
MakeFunction({std::move(params), std::move(body), intTypeAnnotation, enumDecl, ir::ModifierFlags::PUBLIC});
auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::GET_ORDINAL_METHOD_NAME, Allocator());
function->SetIdent(functionIdent);
MakeMethodDef(context_, enumClass, functionIdent, function);
}
void EnumLoweringPhase::CreateEnumDollarGetMethod(ir::TSEnumDeclaration const *const enumDecl,
ir::ClassDefinition *const enumClass)
{
auto *const inputTypeAnnotation = MakeTypeReference(context_, enumClass->Ident()->Name());
auto *const inputNameIdent = MakeFunctionParam(context_, "e", inputTypeAnnotation);
auto *const paramRefIdent = MakeParamRefIdent(context_, inputNameIdent);
auto *const callExpr =
CreateCallInstanceMethod(context_, checker::ETSEnumType::GET_NAME_METHOD_NAME, paramRefIdent);
auto *const returnStmt = AllocNode<ir::ReturnStatement>(callExpr);
ArenaVector<ir::Statement *> body(Allocator()->Adapter());
body.push_back(returnStmt);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
params.push_back(inputNameIdent);
auto *const stringTypeAnnotation = MakeTypeReference(context_, STRING_REFERENCE_TYPE);
auto *const function = MakeFunction({std::move(params), std::move(body), stringTypeAnnotation, enumDecl,
ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC});
auto *const functionIdent = AllocNode<ir::Identifier>(checker::ETSEnumType::DOLLAR_GET_METHOD_NAME, Allocator());
function->SetIdent(functionIdent);
MakeMethodDef(context_, enumClass, functionIdent, function);
}
void EnumLoweringPhase::SetDefaultPositionInUnfilledClassNodes(const ir::ClassDeclaration *enumClassDecl,
ir::TSEnumDeclaration const *const enumDecl)
{
const auto &defautlRange = lexer::SourceRange(enumDecl->Range().start, enumDecl->Range().start);
enumClassDecl->IterateRecursively([&defautlRange, enumDecl](ir::AstNode *ast) -> void {
if (ast->IsClassDefinition()) {
ast->SetRange(enumDecl->Range());
return;
}
if (ast->Range().start.Program() == nullptr) {
ast->SetRange(defautlRange);
}
});
}
ArenaAllocator *EnumLoweringPhase::Allocator()
{
return context_->Allocator();
}
template <typename T, typename... Args>
T *EnumLoweringPhase::AllocNode(Args &&...args)
{
return context_->AllocNode<T>(std::forward<Args>(args)...);
}
}