* Copyright (c) 2023-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 "util.h"
#include "checker/checkerContext.h"
#include "checker/types/globalTypesHolder.h"
#include "compiler/lowering/scopesInit/scopesInitPhase.h"
#include "ir/expressions/identifier.h"
#include "checker/checker.h"
#include "checker/ETSAnalyzer.h"
#include "parser/JsdocHelper.h"
#include "parser/program/program.h"
#include "util/ustring.h"
#include "varbinder/varbinder.h"
namespace ark::es2panda::compiler {
bool HasGlobalClassParent(const ir::AstNode *node)
{
auto parentClass = util::Helpers::FindAncestorGivenByType(node, ir::AstNodeType::CLASS_DEFINITION);
return parentClass != nullptr && parentClass->AsClassDefinition()->IsGlobal();
}
varbinder::Scope *NearestScope(const ir::AstNode *ast)
{
while (ast != nullptr && !ast->IsScopeBearer()) {
ast = ast->Parent();
}
return ast == nullptr ? nullptr : ast->Scope();
}
std::vector<varbinder::ClassScope *> DiffClassScopes(varbinder::Scope *base, varbinder::Scope *findFrom)
{
ES2PANDA_ASSERT(base != nullptr && findFrom != nullptr);
auto result = std::vector<varbinder::ClassScope *> {};
auto baseScopes = std::set<varbinder::Scope *> {};
for (varbinder::Scope *currentScope = base; currentScope != nullptr && !currentScope->IsGlobalScope();
currentScope = currentScope->Parent()) {
baseScopes.insert(currentScope);
}
for (varbinder::Scope *currentScope = findFrom;
baseScopes.find(currentScope) == baseScopes.end() && !currentScope->IsGlobalScope();
currentScope = currentScope->Parent()) {
if (currentScope->IsClassScope() && currentScope->AsClassScope()->Node() != nullptr &&
currentScope->AsClassScope()->Node()->IsClassDefinition() &&
!currentScope->AsClassScope()->Node()->AsClassDefinition()->IsGlobal()) {
result.push_back(currentScope->AsClassScope());
}
}
return result;
}
checker::ETSObjectType const *ContainingClass(const ir::AstNode *ast)
{
ast = util::Helpers::FindAncestorGivenByType(ast, ir::AstNodeType::CLASS_DEFINITION);
return ast == nullptr || ast->AsClassDefinition()->TsType() == nullptr
? nullptr
: ast->AsClassDefinition()->TsType()->AsETSObjectType();
}
ir::Identifier *Gensym(ArenaAllocator *const allocator)
{
util::UString const s = GenName(allocator);
return allocator->New<ir::Identifier>(s.View(), allocator);
}
static std::size_t g_gensymCounter = 0U;
static std::mutex g_gensymCounterMutex {};
void ResetGenSymCounter()
{
std::lock_guard lock(g_gensymCounterMutex);
g_gensymCounter = 0U;
}
std::string GenName()
{
std::size_t individualGensym = 0;
{
std::lock_guard lock(g_gensymCounterMutex);
individualGensym = ++g_gensymCounter;
}
return std::string(GENSYM_CORE) + std::to_string(individualGensym);
}
util::UString GenName(ArenaAllocator *const allocator)
{
return util::UString {GenName(), allocator};
}
void SetSourceRangesRecursively(ir::AstNode *node, const lexer::SourceRange &range)
{
ES2PANDA_ASSERT(node != nullptr);
node->SetRange(range);
node->IterateRecursively([](ir::AstNode *n) { n->SetRange(n->Parent()->Range()); });
}
bool IsSyntheticIdentifier(const ir::AstNode *node) noexcept
{
return node->IsIdentifier() && node->AsIdentifier()->Name().StartsWith(compiler::GENSYM_CORE);
}
ir::AstNode *RefineSourceRanges(ir::AstNode *node)
{
auto const isInvalidRange = [](lexer::SourceRange const &range) {
return (range.start.index == 0 && range.start.line == 0 && range.end.index == 0 && range.end.line == 0) ||
(range.end.index < range.start.index);
};
auto const isDummyLoc = [isInvalidRange](lexer::SourceRange const &range, ir::AstNode *ast) {
return isInvalidRange(range) || (range.start.index < ast->Parent()->Start().index) ||
(range.end.index > ast->Parent()->End().index) ||
(ast->IsMethodDefinition() && !ast->AsMethodDefinition()->Overloads().empty());
};
auto const refine = [isDummyLoc, isInvalidRange](ir::AstNode *ast) {
if (ast->Parent() != nullptr && isDummyLoc(ast->Range(), ast) && !isInvalidRange(ast->Parent()->Range())) {
ast->SetRange(ast->Parent()->Range());
}
};
refine(node);
node->IterateRecursively(refine);
return node;
}
void ClearTypesVariablesAndScopes(ir::AstNode *node) noexcept
{
std::function<void(ir::AstNode *)> doNode = [&](ir::AstNode *nn) {
if (nn->IsOpaqueTypeNode()) {
return;
}
if (nn->IsScopeBearer()) {
nn->ClearScope();
}
if (nn->IsTyped() && !(nn->IsExpression() && nn->AsExpression()->IsTypeNode())) {
nn->AsTyped()->SetTsType(nullptr);
}
if (nn->IsIdentifier()) {
nn->AsIdentifier()->SetVariable(nullptr);
}
if (!nn->IsETSTypeReference() && !nn->IsLabelledStatement()) {
nn->Iterate([&](ir::AstNode *child) { doNode(child); });
}
};
doNode(node);
}
ArenaSet<varbinder::Variable *> FindCaptured(ArenaAllocator *allocator, ir::AstNode *scopeBearer) noexcept
{
auto result = ArenaSet<varbinder::Variable *> {allocator->Adapter()};
auto scopes = ArenaSet<varbinder::Scope *> {allocator->Adapter()};
scopeBearer->IterateRecursivelyPreorder([&result, &scopes](ir::AstNode *ast) {
if (ast->IsScopeBearer() && ast->Scope() != nullptr) {
scopes.insert(ast->Scope());
if (ast->Scope()->IsFunctionScope()) {
scopes.insert(ast->Scope()->AsFunctionScope()->ParamScope());
} else if (ast->IsForUpdateStatement() || ast->IsForInStatement() || ast->IsForOfStatement() ||
ast->IsCatchClause()) {
scopes.insert(ast->Scope()->Parent());
}
}
if (ast->IsIdentifier() && !ast->Parent()->IsLabelledStatement()) {
auto *var = ast->AsIdentifier()->Variable();
if (var == nullptr || !var->HasFlag(varbinder::VariableFlags::LOCAL)) {
return;
}
auto *sc = var->GetScope();
if (sc != nullptr && !sc->IsClassScope() && !sc->IsGlobalScope() && scopes.count(var->GetScope()) == 0) {
result.insert(var);
}
}
});
return result;
}
static void ResetGlobalClass(parser::Program *prog)
{
for (auto *statement : prog->Ast()->Statements()) {
if (statement->IsClassDeclaration() && statement->AsClassDeclaration()->Definition()->IsGlobal()) {
prog->SetGlobalClass(statement->AsClassDeclaration()->Definition());
break;
}
}
}
static bool IsGeneratedForUtilityType(ir::AstNode const *ast)
{
if (ast->IsClassDeclaration()) {
auto &name = ast->AsClassDeclaration()->Definition()->Ident()->Name();
return name.StartsWith(checker::PARTIAL_CLASS_PREFIX);
}
if (ast->IsTSInterfaceDeclaration()) {
auto &name = ast->AsTSInterfaceDeclaration()->Id()->Name();
return name.StartsWith(checker::PARTIAL_CLASS_PREFIX);
}
return false;
}
static void ClearHelper(parser::Program *prog)
{
prog->RemoveAstChecked();
ResetGlobalClass(prog);
auto &stmts = prog->Ast()->StatementsForUpdates();
stmts.erase(std::remove_if(stmts.begin(), stmts.end(),
[](ir::AstNode *ast) -> bool {
return !ast->HasAstNodeFlags(ir::AstNodeFlags::NOCLEANUP) ||
IsGeneratedForUtilityType(ast);
}),
stmts.end());
prog->Ast()->IterateRecursively([](ir::AstNode *ast) -> void { ast->CleanUp(); });
prog->Ast()->ClearScope();
}
varbinder::Scope *Rebind(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, ir::AstNode *node)
{
if (node->IsProgram()) {
auto program = node->AsETSModule()->Program();
ES2PANDA_ASSERT(program == phaseManager->Context()->parserProgram);
if (program->Is<util::ModuleKind::PACKAGE>()) {
return nullptr;
}
program->GetExternalDecls()->Visit([](auto *extProg) { ClearHelper(extProg); });
ClearHelper(program);
varBinder->CleanUp();
for (auto *phase : phaseManager->RebindPhases()) {
phase->Apply(phaseManager->Context());
}
return varBinder->TopScope();
}
auto *scope = NearestScope(node->Parent());
auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, scope);
ClearTypesVariablesAndScopes(node);
InitScopesPhaseETS::RunExternalNode(node, varBinder);
varBinder->ResolveReferencesForScopeWithContext(node, scope);
return scope;
}
static std::vector<parser::Program *> CollectDirectExtSources(public_lib::Context *ctx, parser::Program *prog)
{
const auto &fileDeps = ctx->parser->GetImportPathManager()->GetFileDependencies();
auto prgPath = ArenaString {prog->AbsoluteName().Utf8()};
if (fileDeps.find(prgPath) == fileDeps.end()) {
return {};
}
std::map<std::string_view, parser::Program *> path2prog;
path2prog.emplace(prog->AbsoluteName().Utf8(), prog);
ctx->parserProgram->GetExternalDecls()->Visit(
[&path2prog](auto *prg) { path2prog.emplace(prg->AbsoluteName().Utf8(), prg); });
std::vector<parser::Program *> res;
const auto &directDepsSet = fileDeps.at(prgPath);
res.reserve(directDepsSet.size());
for (const auto &extSrcPath : directDepsSet) {
if (path2prog.find(extSrcPath) == path2prog.end()) {
continue;
}
res.emplace_back(path2prog.at(extSrcPath));
}
return res;
}
class RecheckGraph {
public:
class Node {
public:
explicit Node(parser::Program *prog) : prog_(prog) {}
std::set<Node *> &ImportedNodes()
{
return importedNodes_;
}
std::set<Node *> &NodesImportedBy()
{
return nodesImportedBy_;
}
parser::Program *Prog() const
{
return prog_;
}
private:
std::set<Node *> importedNodes_;
std::set<Node *> nodesImportedBy_;
parser::Program *prog_;
};
std::map<parser::Program *, Node> &Programs()
{
return programs_;
}
std::set<Node *> &FoundModifiedProgs()
{
return foundModifiedProgs_;
}
private:
std::map<parser::Program *, Node> programs_ {};
std::set<Node *> foundModifiedProgs_ {};
};
static RecheckGraph::Node *RecheckGraphCreatorHelper(public_lib::Context *ctx, parser::Program *program,
RecheckGraph *graph);
static void RecheckDependencies(public_lib::Context *ctx, parser::Program *prg, RecheckGraph *graph)
{
auto *node = &graph->Programs().at(prg);
auto recheckImpl = [&ctx, &node, &graph](auto *prog) {
RecheckGraph::Node *importedNode = nullptr;
importedNode = RecheckGraphCreatorHelper(ctx, prog, graph);
node->ImportedNodes().emplace(importedNode);
importedNode->NodesImportedBy().emplace(node);
};
std::vector<parser::Program *> directExtSources;
if (prg->Is<util::ModuleKind::SIMULT_MAIN>()) {
directExtSources.reserve(prg->GetExternalDecls()->Direct().size());
for (auto &[_, directExtSrc] : prg->GetExternalDecls()->Direct()) {
directExtSources.emplace_back(directExtSrc);
}
} else {
directExtSources = CollectDirectExtSources(ctx, prg);
}
for (auto directExtPrg : directExtSources) {
recheckImpl(directExtPrg);
}
}
static RecheckGraph::Node *RecheckGraphCreatorHelper(public_lib::Context *ctx, parser::Program *program,
RecheckGraph *graph)
{
if (graph->Programs().find(program) != graph->Programs().end()) {
return &graph->Programs().at(program);
}
graph->Programs().emplace(program, RecheckGraph::Node(program));
RecheckDependencies(ctx, program, graph);
auto *node = &graph->Programs().at(program);
if (program->IsProgramModified()) {
graph->FoundModifiedProgs().emplace(node);
}
return node;
}
static void MarkModifiedRecursively(RecheckGraph::Node *node)
{
if (node->Prog()->IsProgramModified()) {
return;
}
node->Prog()->SetProgramModified(true);
for (auto importedBy : node->NodesImportedBy()) {
MarkModifiedRecursively(importedBy);
}
}
static void ExtendModifiedFlagOnPackagePrograms(parser::Program *globalProg)
{
std::unordered_set<parser::ProgramAdapter<util::ModuleKind::PACKAGE> *> modifiedPackagePrograms {};
for (auto *packageProg : globalProg->GetExternalDecls()->Get<util::ModuleKind::PACKAGE>()) {
if (packageProg->GetUnmergedPackagePrograms().empty()) {
continue;
}
packageProg->SetProgramModified(false);
for (auto fraction : packageProg->GetUnmergedPackagePrograms()) {
if (fraction->IsProgramModified()) {
modifiedPackagePrograms.insert(packageProg);
break;
}
}
}
for (auto *packageProg : modifiedPackagePrograms) {
packageProg->SetProgramModified(true);
for (auto fraction : packageProg->GetUnmergedPackagePrograms()) {
fraction->SetProgramModified(true);
}
}
}
static bool ExtendModifiedFlagOnDependentPrograms(public_lib::Context *ctx, parser::Program *program)
{
auto *globalProg = ctx->parserProgram;
ExtendModifiedFlagOnPackagePrograms(globalProg);
RecheckGraph graph;
RecheckGraphCreatorHelper(ctx, program, &graph);
for (auto node : graph.FoundModifiedProgs()) {
if (node->Prog()->IsProgramModified()) {
node->Prog()->SetProgramModified(false);
MarkModifiedRecursively(node);
}
}
return program->IsProgramModified();
}
template <typename CB>
static void IterateExternalProgramsForBinderAndCheckerPushing(parser::Program *program, const CB &cb)
{
program->GetExternalDecls()->Visit(cb);
for (auto *packageProg : program->GetExternalDecls()->Get<util::ModuleKind::PACKAGE>()) {
cb(packageProg);
}
}
static void RestoreGlobalTypesHolder(checker::ETSChecker *newChecker, parser::Program *program)
{
checker::GlobalTypesHolder *globalTypesHolder = nullptr;
program->GetExternalDecls()->Visit([&globalTypesHolder](auto *extProg) {
if ((globalTypesHolder == nullptr) && (!extProg->IsProgramModified() || extProg->IsASTLowered())) {
globalTypesHolder = extProg->Checker()->GetGlobalTypesHolder();
}
});
if (globalTypesHolder != nullptr) {
newChecker->SetGlobalTypesHolder(globalTypesHolder);
}
}
using SavedVarbindersAndCheckers = std::map<parser::Program *, std::pair<varbinder::VarBinder *, checker::Checker *>>;
static varbinder::ETSBinder *SetupNewVarBinderHierarchy(public_lib::Context *ctx, parser::Program *program,
SavedVarbindersAndCheckers varbindersCheckers,
varbinder::ETSBinder *varBinder)
{
auto newVarbinder = new varbinder::ETSBinder(ctx);
newVarbinder->SetProgram(program);
program->PushVarBinder(newVarbinder);
varBinder->CopyTo(newVarbinder);
auto visitor = [newVarbinder, &varbindersCheckers](parser::Program *prog) {
if (!prog->IsASTLowered() && prog->IsProgramModified()) {
ClearHelper(prog);
prog->PushVarBinder(newVarbinder);
return;
}
prog->PushVarBinder(varbindersCheckers.at(prog).first);
if (prog->Is<util::ModuleKind::PACKAGE>()) {
return;
}
prog->PushChecker(varbindersCheckers.at(prog).second);
};
IterateExternalProgramsForBinderAndCheckerPushing(program, visitor);
return newVarbinder;
}
static void RecheckProgram(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, parser::Program *program)
{
auto ctx = phaseManager->Context();
if (!ExtendModifiedFlagOnDependentPrograms(ctx, program)) {
return;
}
auto newChecker = ctx->allocator->New<checker::ETSChecker>(ctx->allocator, *ctx->diagnosticEngine);
auto analyzer = ctx->allocator->New<checker::ETSAnalyzer>(newChecker);
RestoreGlobalTypesHolder(newChecker, program);
std::set<varbinder::VarBinder *> savedVarBinders {};
SavedVarbindersAndCheckers varbindersCheckers {};
IterateExternalProgramsForBinderAndCheckerPushing(
program, [&savedVarBinders, &varbindersCheckers](parser::Program *prog) {
if (!prog->IsASTLowered() && prog->IsProgramModified()) {
return;
}
savedVarBinders.insert(prog->VarBinder());
varbindersCheckers[prog].first = prog->VarBinder();
varbindersCheckers[prog].second = prog->Is<util::ModuleKind::PACKAGE>() ? nullptr : prog->Checker();
});
phaseManager->SetCurrentPhaseId(0);
auto newVarbinder = SetupNewVarBinderHierarchy(ctx, program, varbindersCheckers, varBinder);
ClearHelper(program);
ctx->PushAnalyzer(analyzer);
newChecker->SetAnalyzer(analyzer);
newChecker->Initialize(newVarbinder);
ctx->PushChecker(newChecker);
for (auto *savedVarBinder : savedVarBinders) {
for (auto func : savedVarBinder->FunctionScopes()) {
if (func->Node()->Program() != nullptr && !func->Node()->Program()->IsProgramModified()) {
newVarbinder->FunctionScopes().push_back(func);
}
}
}
for (auto *phase : phaseManager->RecheckPhases()) {
phase->Apply(ctx);
}
phaseManager->SetCurrentPhaseIdToAfterCheck();
IterateExternalProgramsForBinderAndCheckerPushing(program, [newVarbinder, newChecker](parser::Program *prog) {
prog->PushVarBinder(newVarbinder);
if (prog->Is<util::ModuleKind::PACKAGE>()) {
return;
}
prog->PushChecker(newChecker);
});
}
void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checker::ETSChecker *checker,
ir::AstNode *node)
{
RefineSourceRanges(node);
if (node->IsProgram()) {
return RecheckProgram(phaseManager, varBinder, node->AsETSModule()->Program());
}
auto *scope = Rebind(phaseManager, varBinder, node);
auto *containingClass = ContainingClass(node);
checker::CheckerStatus newStatus =
(containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS;
if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) {
newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK;
}
auto checkerCtx = checker::SavedCheckerContext(checker, newStatus, containingClass);
auto scopeCtx = checker::ScopeContext(checker, scope);
node->Check(checker);
}
std::optional<std::string> GetNameOfDeclaration(const ir::AstNode *node)
{
if (node == nullptr) {
return std::nullopt;
}
switch (node->Type()) {
case ir::AstNodeType::IDENTIFIER:
return std::string(node->AsIdentifier()->Name().Utf8());
case ir::AstNodeType::METHOD_DEFINITION:
return std::string(node->AsMethodDefinition()->Id()->Name().Utf8());
case ir::AstNodeType::FUNCTION_DECLARATION:
return std::string(node->AsFunctionDeclaration()->Function()->Id()->Name().Utf8());
case ir::AstNodeType::FUNCTION_EXPRESSION:
return std::string(node->AsFunctionExpression()->Function()->Id()->Name().Utf8());
case ir::AstNodeType::CLASS_DEFINITION:
return std::string(node->AsClassDefinition()->Ident()->Name().Utf8());
case ir::AstNodeType::CLASS_PROPERTY:
return std::string(node->AsClassProperty()->Id()->Name().Utf8());
case ir::AstNodeType::TS_INTERFACE_DECLARATION:
return std::string(node->AsTSInterfaceDeclaration()->Id()->Name().Utf8());
case ir::AstNodeType::VARIABLE_DECLARATION:
if (node->AsVariableDeclaration()->Declarators()[0]->Id()->IsIdentifier()) {
ir::Identifier *ident = node->AsVariableDeclaration()->Declarators()[0]->Id()->AsIdentifier();
return std::string(ident->Name().Utf8());
} else {
return std::nullopt;
}
default:
return std::nullopt;
}
}
ir::AstNode *DeclarationFromIdentifier(const ir::Identifier *node)
{
if (node == nullptr) {
return nullptr;
}
auto idVar = node->Variable();
if (idVar == nullptr) {
return nullptr;
}
auto decl = idVar->Declaration();
if (decl == nullptr) {
return nullptr;
}
return decl->Node();
}
util::StringView GetLicenseFromRootNode(const ir::AstNode *node)
{
std::unique_ptr<parser::JsdocHelper> jsdocGetter = std::make_unique<parser::JsdocHelper>(node);
return jsdocGetter->GetLicenseStringFromStart();
}
util::StringView JsdocStringFromDeclaration(const ir::AstNode *node)
{
std::unique_ptr<parser::JsdocHelper> jsdocGetter = std::make_unique<parser::JsdocHelper>(node);
return jsdocGetter->GetJsdocBackward();
}
void BindLoweredNode(varbinder::ETSBinder *varBinder, ir::AstNode *node)
{
RefineSourceRanges(node);
InitScopesPhaseETS::RunExternalNode(node, varBinder);
auto *scope = NearestScope(node);
varBinder->ResolveReferencesForScopeWithContext(node, scope);
}
void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node)
{
RefineSourceRanges(node);
InitScopesPhaseETS::RunExternalNode(node, varBinder);
auto *scope = NearestScope(node);
varBinder->ResolveReferencesForScopeWithContext(node, scope);
checker::CheckerStatus newStatus = checker::CheckerStatus::NO_OPTS;
auto *containingClass = util::Helpers::GetContainingClassDefinition(node);
if (containingClass != nullptr) {
if (containingClass->IsAbstract()) {
newStatus = checker::CheckerStatus::IN_ABSTRACT;
} else {
newStatus = checker::CheckerStatus::IN_CLASS;
}
}
if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) {
newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK;
}
auto checkerCtx =
checker::SavedCheckerContext(checker, newStatus,
containingClass == nullptr ? checker->Context().ContainingClass()
: containingClass->TsType()->AsETSObjectType());
auto scopeCtx = checker::ScopeContext(checker, scope);
node->Check(checker);
}
bool IsAnonymousClassType(const checker::Type *type)
{
if (type == nullptr || !type->IsETSObjectType()) {
return false;
}
auto declNode = type->AsETSObjectType()->GetDeclNode();
return declNode != nullptr && declNode->IsClassDefinition() && declNode->AsClassDefinition()->IsAnonymous();
}
bool ClassDefinitionIsEnumTransformed(const ir::AstNode *node)
{
return node != nullptr && node->IsClassDefinition() && node->AsClassDefinition()->IsEnumTransformed();
}
ir::Expression *CreateUninitializedFixedArray(public_lib::Context *ctx, ir::Expression *arraySize,
checker::Type *arrayType)
{
auto *allocator = ctx->allocator;
auto *checker = ctx->GetChecker()->AsETSChecker();
ArenaVector<ir::Expression *> params(allocator->Adapter());
params.emplace_back(arraySize);
params.emplace_back(checker->AllocNode<ir::OpaqueTypeNode>(arrayType, allocator));
auto resultExpr =
util::NodeAllocator::ForceSetParent<ir::ETSIntrinsicNode>(allocator, "createrawfixedarray", std::move(params));
return resultExpr;
}
ir::Expression *CreateUninitializedResizableArray(public_lib::Context *ctx, ir::Expression *arraySize,
checker::Type *arrayType)
{
auto *allocator = ctx->allocator;
auto *checker = ctx->GetChecker()->AsETSChecker();
ArenaVector<ir::Expression *> params(allocator->Adapter());
params.emplace_back(arraySize);
params.emplace_back(allocator->New<ir::UndefinedLiteral>());
params.emplace_back(checker->AllocNode<ir::OpaqueTypeNode>(arrayType, allocator));
auto resultExpr = util::NodeAllocator::ForceSetParent<ir::ETSIntrinsicNode>(allocator, "createrawresizablearray",
std::move(params));
return resultExpr;
}
}