• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 - 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "compiler/lowering/ets/topLevelStmts/globalClassHandler.h"
17 #include <algorithm>
18 #include "compiler/lowering/util.h"
19 
20 #include "ir/statements/classDeclaration.h"
21 #include "ir/base/classDefinition.h"
22 #include "ir/base/classProperty.h"
23 #include "ir/base/classStaticBlock.h"
24 #include "ir/base/scriptFunction.h"
25 #include "ir/base/methodDefinition.h"
26 #include "ir/expressions/identifier.h"
27 #include "ir/expressions/classExpression.h"
28 #include "ir/expressions/functionExpression.h"
29 #include "ir/expressions/callExpression.h"
30 #include "ir/statements/expressionStatement.h"
31 #include "ir/statements/blockStatement.h"
32 #include "util/helpers.h"
33 #include "util/ustring.h"
34 #include "utils/arena_containers.h"
35 #include "generated/diagnostic.h"
36 
37 namespace ark::es2panda::compiler {
38 
39 using util::NodeAllocator;
40 
AddStaticBlockToClass(ir::AstNode * node)41 void GlobalClassHandler::AddStaticBlockToClass(ir::AstNode *node)
42 {
43     if (node->IsClassDefinition() && !node->AsClassDefinition()->IsDeclare()) {
44         auto classDef = node->AsClassDefinition();
45         if (auto staticBlock = CreateStaticBlock(classDef); staticBlock != nullptr) {
46             classDef->Body().emplace_back(staticBlock);  // NOTE(vpukhov): inserted to end for some reason
47             staticBlock->SetParent(classDef);
48         }
49     }
50 }
51 
AddToNamespaceChain(std::string chain,std::string name)52 std::string AddToNamespaceChain(std::string chain, std::string name)
53 {
54     if (chain.empty()) {
55         return name;
56     }
57     if (name.empty()) {
58         return chain;
59     }
60     return chain + "." + name;
61 }
62 
CollectNamespaceExportedClasses(parser::Program * program,ir::ClassDefinition * classDef)63 void GlobalClassHandler::CollectNamespaceExportedClasses(parser::Program *program, ir::ClassDefinition *classDef)
64 {
65     CollectExportedClasses(program, classDef, classDef->Body());
66 }
67 
CollectReExportedClasses(parser::Program * program,ir::ClassDefinition * classDef,const ir::ETSReExportDeclaration * reExport)68 void GlobalClassHandler::CollectReExportedClasses(parser::Program *program, ir::ClassDefinition *classDef,
69                                                   const ir::ETSReExportDeclaration *reExport)
70 {
71     auto importDecl = reExport->GetETSImportDeclarations();
72     const auto importPath = reExport->GetETSImportDeclarations()->ImportMetadata().resolvedSource;
73     parser::Program *extProg = nullptr;
74     // Search Correct external program by comparing importPath and absolutePath
75     for (auto &[_, progs] : program->DirectExternalSources()) {
76         auto it = std::find_if(progs.begin(), progs.end(),
77                                [&](const auto *prog) { return prog->AbsoluteName() == importPath; });
78         if (it != progs.end()) {
79             extProg = *it;
80             break;
81         }
82     }
83     if (extProg == nullptr) {
84         return;
85     }
86     auto &externalExportedClasses = extProg->GlobalClass()->ExportedClasses();
87     const auto &specifiers = importDecl->Specifiers();
88     bool needAddETSGlobal = false;
89     for (const auto *specifier : specifiers) {
90         if (specifier->IsImportNamespaceSpecifier()) {
91             classDef->BatchAddToExportedClasses(externalExportedClasses);
92             break;
93         }
94         auto found = std::find_if(externalExportedClasses.begin(), externalExportedClasses.end(),
95                                   [&specifier](const ir::ClassDeclaration *classDecl) {
96                                       return specifier->IsImportSpecifier() &&
97                                              specifier->AsImportSpecifier()->Imported()->Name() ==
98                                                  // CC-OFFNXT(G.FMT.02-CPP) solid logic
99                                                  classDecl->Definition()->Ident()->Name();
100                                       // CC-OFFNXT(G.FMT.02-CPP) solid logic
101                                   });
102         if (found == externalExportedClasses.end()) {
103             needAddETSGlobal = true;
104             continue;
105         }
106         classDef->AddToExportedClasses(*found);
107     }
108 
109     /*
110      *        a.ets:                                 b.ets:
111      * export let ident = 10             export {ident, A, B} from './a'
112      * export class A {}
113      * export class B {}
114      *              Note: (`a.ets` exported classes: A, B and ETSGLOBAL)
115      *
116      * In this re-export declaration, we need manually add ETSGLOBAL to exportedClasses.
117      */
118     if (needAddETSGlobal) {
119         classDef->AddToExportedClasses(extProg->GlobalClass()->Parent()->AsClassDeclaration());
120     }
121 }
122 
123 template <class Node>
CollectExportedClasses(parser::Program * program,ir::ClassDefinition * classDef,const ArenaVector<Node * > & statements)124 void GlobalClassHandler::CollectExportedClasses(parser::Program *program, ir::ClassDefinition *classDef,
125                                                 const ArenaVector<Node *> &statements)
126 {
127     for (const auto *statement : statements) {
128         if (!statement->IsExported()) {
129             continue;
130         }
131         if (statement->IsClassDeclaration()) {
132             classDef->AddToExportedClasses(statement->AsClassDeclaration());
133             continue;
134         }
135         if (statement->IsETSReExportDeclaration()) {
136             CollectReExportedClasses(program, classDef, statement->AsETSReExportDeclaration());
137         }
138     }
139     auto globalClass = program->GlobalClass();
140     bool foundExport = false;
141     // Add ETSGLOBAL to Module in case of export let a = 10
142     std::function<void(ir::AstNode *)> findExportInGlobal = [&findExportInGlobal, &foundExport](ir::AstNode *node) {
143         if (node->IsExported()) {
144             foundExport = true;
145             return;
146         }
147         node->Iterate(findExportInGlobal);
148     };
149     globalClass->Iterate(findExportInGlobal);
150     if (foundExport) {
151         auto globalClassDecl = globalClass->Parent()->AsClassDeclaration();
152         classDef->AddToExportedClasses(globalClassDecl);
153     }
154 }
155 
CreateTransformedClass(ir::ETSModule * ns)156 ir::ClassDeclaration *GlobalClassHandler::CreateTransformedClass(ir::ETSModule *ns)
157 {
158     auto className = ns->Ident()->Name();
159     auto *ident = NodeAllocator::Alloc<ir::Identifier>(allocator_, className, allocator_);
160     ident->SetRange(ns->Ident()->Range());
161 
162     auto *classDef = NodeAllocator::Alloc<ir::ClassDefinition>(
163         allocator_, allocator_, ident, ir::ClassDefinitionModifiers::CLASS_DECL, ir::ModifierFlags::ABSTRACT,
164         Language(Language::Id::ETS));
165     ES2PANDA_ASSERT(classDef != nullptr);
166     classDef->SetRange(ns->Range());
167     classDef->AddModifier(ns->Modifiers());
168     auto *classDecl = NodeAllocator::Alloc<ir::ClassDeclaration>(allocator_, classDef, allocator_);
169     classDecl->AddModifier(ns->Modifiers());
170     classDef->SetNamespaceTransformed();
171     ArenaVector<ir::AnnotationUsage *> annotations {allocator_->Adapter()};
172     for (auto *anno : ns->Annotations()) {
173         auto clone = anno->Clone(allocator_, classDef);
174         annotations.push_back(clone);
175     }
176 
177     classDef->SetAnnotations(std::move(annotations));
178     return classDecl;
179 }
180 
InsertInGlobal(ir::ClassDefinition * globalClass,ir::AstNode * node)181 static void InsertInGlobal(ir::ClassDefinition *globalClass, ir::AstNode *node)
182 {
183     ES2PANDA_ASSERT(node != nullptr);
184     globalClass->Body().insert(globalClass->Body().begin(), node);
185     node->SetParent(globalClass);
186 }
187 
SetupInitializerBlock(parser::Program * program,ArenaVector<ArenaVector<ir::Statement * >> && initializerBlock,ir::ClassDefinition * globalClass)188 void GlobalClassHandler::SetupInitializerBlock(parser::Program *program,
189                                                ArenaVector<ArenaVector<ir::Statement *>> &&initializerBlock,
190                                                ir::ClassDefinition *globalClass)
191 {
192     if (program->IsDeclarationModule() || initializerBlock.empty()) {
193         return;
194     }
195 
196     ArenaVector<ir::Statement *> blockStmts(allocator_->Adapter());
197     for (auto iBlock : initializerBlock) {
198         if (iBlock.empty()) {
199             continue;
200         }
201         blockStmts.emplace_back(
202             NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator_, allocator_, std::move(iBlock)));
203     }
204 
205     // Note: cannot use the all same name for every stdlib package.
206     std::string moduleName = std::string(program->ModuleName());
207     std::replace(moduleName.begin(), moduleName.end(), '.', '_');
208     util::UString initializerBlockName =
209         util::UString {std::string(compiler::Signatures::INITIALIZER_BLOCK_INIT) + moduleName, allocator_};
210     ir::MethodDefinition *initializerBlockInit =
211         CreateGlobalMethod(initializerBlockName.View().Utf8(), std::move(blockStmts), program);
212     InsertInGlobal(globalClass, initializerBlockInit);
213     AddInitCallToStaticBlock(globalClass, initializerBlockInit);
214 }
215 
SetupGlobalMethods(parser::Program * program,ArenaVector<ir::Statement * > && initStatements,ir::ClassDefinition * globalClass,bool isDeclare)216 void GlobalClassHandler::SetupGlobalMethods(parser::Program *program, ArenaVector<ir::Statement *> &&initStatements,
217                                             ir::ClassDefinition *globalClass, bool isDeclare)
218 {
219     if (isDeclare) {
220         return;
221     }
222 
223     ir::MethodDefinition *initMethod =
224         CreateGlobalMethod(compiler::Signatures::INIT_METHOD, std::move(initStatements), program);
225     InsertInGlobal(globalClass, initMethod);
226     ES2PANDA_ASSERT(initMethod->Function());
227     if (!initMethod->Function()->Body()->AsBlockStatement()->Statements().empty()) {
228         AddInitCallToStaticBlock(globalClass, initMethod);
229     }
230 }
231 
MergeNamespace(ArenaVector<ir::ETSModule * > & namespaces,parser::Program * program)232 void GlobalClassHandler::MergeNamespace(ArenaVector<ir::ETSModule *> &namespaces, parser::Program *program)
233 {
234     auto *parser = program->VarBinder()->GetContext()->parser->AsETSParser();
235     ArenaUnorderedMap<util::StringView, ir::ETSModule *> nsMap {program->Allocator()->Adapter()};
236     for (auto it = namespaces.begin(); it != namespaces.end();) {
237         auto *ns = *it;
238         auto res = nsMap.find(ns->Ident()->Name());
239         if (res != nsMap.end()) {
240             if (res->second->Modifiers() != ns->Modifiers()) {
241                 parser->LogError(diagnostic::NAMESPACE_MERGE_ERROR, {ns->Ident()->Name().Mutf8()}, ns->Start());
242             }
243             if (!res->second->Annotations().empty() && !ns->Annotations().empty()) {
244                 parser->LogError(diagnostic::NAMESPACE_ANNOTATION_CONFLICT, {ns->Ident()->Name().Mutf8()}, ns->Start());
245             } else if (!ns->Annotations().empty()) {
246                 ES2PANDA_ASSERT(res->second->Annotations().empty());
247                 res->second->SetAnnotations(std::move(ns->Annotations()));
248             }
249             for (auto *statement : ns->Statements()) {
250                 res->second->Statements().emplace_back(statement);
251             }
252             namespaces.erase(it);
253         } else {
254             nsMap.insert({ns->Ident()->Name(), ns});
255             ++it;
256         }
257     }
258 }
259 
TransformNamespaces(ArenaVector<ir::ETSModule * > & namespaces,parser::Program * program)260 ArenaVector<ir::ClassDeclaration *> GlobalClassHandler::TransformNamespaces(ArenaVector<ir::ETSModule *> &namespaces,
261                                                                             parser::Program *program)
262 {
263     ArenaVector<ir::ClassDeclaration *> classDecls {allocator_->Adapter()};
264     MergeNamespace(namespaces, program);
265     for (auto ns : namespaces) {
266         classDecls.emplace_back(TransformNamespace(ns, program));
267     }
268     return classDecls;
269 }
270 
TransformBrokenNamespace(ir::AstNode * node,parser::Program * program)271 void GlobalClassHandler::TransformBrokenNamespace(ir::AstNode *node, parser::Program *program)
272 {
273     node->TransformChildrenRecursively(
274         // clang-format off
275         // CC-OFFNXT(G.FMT.14-CPP) project code style
276         [this, &program](ir::AstNode *child) -> ir::AstNode* {
277             if (child->IsETSModule() && child->AsETSModule()->IsNamespace()) {
278                 auto res = TransformNamespace(child->AsETSModule(), program);
279                 res->SetParent(child->Parent());
280                 return res;
281             }
282             return child;
283         },
284         // clang-format on
285         "TransformBrokenNamespace");
286 }
287 
TransformNamespace(ir::ETSModule * ns,parser::Program * program)288 ir::ClassDeclaration *GlobalClassHandler::TransformNamespace(ir::ETSModule *ns, parser::Program *program)
289 {
290     ir::ClassDeclaration *const globalDecl = CreateTransformedClass(ns);
291     ES2PANDA_ASSERT(globalDecl != nullptr);
292     ir::ClassDefinition *const globalClass = globalDecl->Definition();
293 
294     ArenaVector<GlobalStmts> immediateInitializers(allocator_->Adapter());
295     ArenaVector<GlobalStmts> initializerBlock(allocator_->Adapter());
296     ArenaVector<ir::ETSModule *> namespaces(allocator_->Adapter());
297     auto &body = ns->Statements();
298     for (auto *statement : body) {
299         statement->Iterate([this](ir::AstNode *node) { AddStaticBlockToClass(node); });
300     }
301     auto stmts = CollectProgramGlobalStatements(body, globalClass, ns);
302     immediateInitializers.emplace_back(GlobalStmts {program, std::move(stmts.immediateInit)});
303     for (auto &initBlock : stmts.initializerBlocks) {
304         initializerBlock.emplace_back(GlobalStmts {program, std::move(initBlock)});
305     }
306     AddStaticBlockToClass(globalClass);
307     const ModuleDependencies md {allocator_->Adapter()};
308     auto immediateInitStatements = FormInitMethodStatements(program, &md, std::move(immediateInitializers));
309     auto initializerBlockStatements = FormInitStaticBlockMethodStatements(program, &md, std::move(initializerBlock));
310     SetupGlobalMethods(program, std::move(immediateInitStatements), globalClass, ns->IsDeclare());
311     SetupInitializerBlock(program, std::move(initializerBlockStatements), globalClass);
312 
313     // remove namespaceDecl from orginal node
314     auto end = std::remove_if(body.begin(), body.end(), [&namespaces](ir::AstNode *node) {
315         if (node->IsETSModule()) {
316             namespaces.emplace_back(node->AsETSModule());
317             return true;
318         }
319         return false;
320     });
321     body.erase(end, body.end());
322     auto globalClasses = TransformNamespaces(namespaces, program);
323     for (auto *cls : globalClasses) {
324         globalClass->Body().emplace_back(cls);
325         cls->SetParent(globalClass);
326         CollectNamespaceExportedClasses(program, cls->AsClassDeclaration()->Definition());
327     }
328 
329     // Add rest statement, such as type declaration
330     for (auto *statement : body) {
331         globalClass->Body().emplace_back(statement);
332         statement->SetParent(globalClass);
333     }
334     body.clear();
335     return globalDecl;
336 }
337 
CollectProgramGlobalClasses(parser::Program * program,ArenaVector<ir::ETSModule * > namespaces)338 void GlobalClassHandler::CollectProgramGlobalClasses(parser::Program *program, ArenaVector<ir::ETSModule *> namespaces)
339 {
340     auto classDecls = TransformNamespaces(namespaces, program);
341     for (auto cls : classDecls) {
342         program->Ast()->Statements().push_back(cls);
343         cls->SetParent(program->Ast());
344         CollectNamespaceExportedClasses(program, cls->Definition());
345     }
346 }
347 
CheckPackageMultiInitializerBlock(util::StringView packageName,const ArenaVector<ArenaVector<ir::Statement * >> & initializerBlocks)348 void GlobalClassHandler::CheckPackageMultiInitializerBlock(
349     util::StringView packageName, const ArenaVector<ArenaVector<ir::Statement *>> &initializerBlocks)
350 {
351     if (initializerBlocks.empty()) {
352         return;
353     }
354 
355     if (packageInitializerBlockCount_.count(packageName) != 0) {
356         parser_->LogError(diagnostic::PACKAGE_MULTIPLE_STATIC_BLOCK, {}, initializerBlocks[0][0]->Start());
357     } else {
358         packageInitializerBlockCount_.insert(packageName);
359     }
360 }
361 
362 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
SetupGlobalClass(const ArenaVector<parser::Program * > & programs,const ModuleDependencies * moduleDependencies)363 void GlobalClassHandler::SetupGlobalClass(const ArenaVector<parser::Program *> &programs,
364                                           const ModuleDependencies *moduleDependencies)
365 {
366     if (programs.empty()) {
367         return;
368     }
369 
370     parser::Program *const globalProgram = programs.front();
371     if (globalProgram->GlobalClass() != nullptr) {
372         return;
373     }
374 
375     ArenaUnorderedSet<util::StringView> packageInitializerBlockCount(allocator_->Adapter());
376     ir::ClassDeclaration *const globalDecl = CreateGlobalClass(globalProgram);
377     ES2PANDA_ASSERT(globalDecl != nullptr);
378     ir::ClassDefinition *const globalClass = globalDecl->Definition();
379 
380     // NOTE(vpukhov): a clash inside program list is possible
381     ES2PANDA_ASSERT(globalProgram->IsPackage() || programs.size() == 1);
382 
383     ArenaVector<GlobalStmts> immediateInitializers(allocator_->Adapter());
384     ArenaVector<GlobalStmts> initializerBlock(allocator_->Adapter());
385     ArenaVector<ir::ETSModule *> namespaces(allocator_->Adapter());
386 
387     for (auto program : programs) {
388         program->Ast()->IterateRecursively([this](ir::AstNode *node) { AddStaticBlockToClass(node); });
389         auto &body = program->Ast()->Statements();
390         auto stmts = CollectProgramGlobalStatements(body, globalClass, program->Ast());
391         auto end = std::remove_if(body.begin(), body.end(), [&namespaces](ir::AstNode *node) {
392             if (node->IsETSModule() && node->AsETSModule()->IsNamespace()) {
393                 namespaces.emplace_back(node->AsETSModule());
394                 return true;
395             }
396             return false;
397         });
398         body.erase(end, body.end());
399         CheckPackageMultiInitializerBlock(program->ModuleName(), stmts.initializerBlocks);
400         immediateInitializers.emplace_back(GlobalStmts {program, std::move(stmts.immediateInit)});
401         for (auto &initBlock : stmts.initializerBlocks) {
402             initializerBlock.emplace_back(GlobalStmts {program, std::move(initBlock)});
403         }
404         program->SetGlobalClass(globalClass);
405     }
406 
407     globalProgram->Ast()->Statements().emplace_back(globalDecl);
408     globalDecl->SetParent(globalProgram->Ast());
409     globalClass->SetGlobalInitialized();
410     CollectProgramGlobalClasses(globalProgram, namespaces);
411     TransformBrokenNamespace(globalProgram->Ast(), globalProgram);
412     auto initializerBlockStmts =
413         FormInitStaticBlockMethodStatements(globalProgram, moduleDependencies, std::move(initializerBlock));
414     CollectExportedClasses(globalProgram, globalClass, globalProgram->Ast()->Statements());
415 
416     // NOTE(vpukhov): stdlib checks are to be removed - do not extend the existing logic
417     if (globalProgram->Kind() != parser::ScriptKind::STDLIB) {
418         AddStaticBlockToClass(globalClass);
419         if (!util::Helpers::IsStdLib(globalProgram)) {
420             auto immInitStmts =
421                 FormInitMethodStatements(globalProgram, moduleDependencies, std::move(immediateInitializers));
422             SetupGlobalMethods(globalProgram, std::move(immInitStmts));
423         }
424     }
425     SetupInitializerBlock(globalProgram, std::move(initializerBlockStmts), globalClass);
426 }
427 
CreateGlobalMethod(std::string_view name,ArenaVector<ir::Statement * > && statements,const parser::Program * program)428 ir::MethodDefinition *GlobalClassHandler::CreateGlobalMethod(std::string_view name,
429                                                              ArenaVector<ir::Statement *> &&statements,
430                                                              const parser::Program *program)
431 {
432     const auto functionFlags = ir::ScriptFunctionFlags::NONE;
433     auto functionModifiers = ir::ModifierFlags::STATIC | ir::ModifierFlags::PUBLIC;
434     auto ident = NodeAllocator::Alloc<ir::Identifier>(allocator_, name, allocator_);
435     auto body = NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator_, allocator_, std::move(statements));
436     auto funcSignature = ir::FunctionSignature(nullptr, ArenaVector<ir::Expression *>(allocator_->Adapter()), nullptr);
437 
438     auto *func = NodeAllocator::Alloc<ir::ScriptFunction>(
439         allocator_, allocator_,
440         ir::ScriptFunction::ScriptFunctionData {
441             body, std::move(funcSignature), functionFlags, {}, Language(Language::Id::ETS)});
442     ES2PANDA_ASSERT(func != nullptr);
443     func->SetIdent(ident);
444     func->AddModifier(functionModifiers);
445 
446     auto *funcExpr = NodeAllocator::Alloc<ir::FunctionExpression>(allocator_, func);
447     auto *methodDef = NodeAllocator::Alloc<ir::MethodDefinition>(allocator_, ir::MethodDefinitionKind::METHOD,
448                                                                  ident->Clone(allocator_, nullptr)->AsExpression(),
449                                                                  funcExpr, functionModifiers, allocator_, false);
450     methodDef->SetRange({lexer::SourcePosition(program), lexer::SourcePosition(program)});
451     return methodDef;
452 }
453 
AddInitializerBlockToStaticBlock(ir::ClassDefinition * globalClass,ArenaVector<ir::Statement * > && initializerBlocks)454 void GlobalClassHandler::AddInitializerBlockToStaticBlock(ir::ClassDefinition *globalClass,
455                                                           ArenaVector<ir::Statement *> &&initializerBlocks)
456 {
457     auto &globalBody = globalClass->Body();
458     auto maybeStaticBlock = std::find_if(globalBody.begin(), globalBody.end(),
459                                          [](ir::AstNode *node) { return node->IsClassStaticBlock(); });
460     ES2PANDA_ASSERT(maybeStaticBlock != globalBody.end());
461 
462     auto *staticBlock = (*maybeStaticBlock)->AsClassStaticBlock();
463     auto *initializerStmts =
464         NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator_, allocator_, std::move(initializerBlocks));
465     ES2PANDA_ASSERT(initializerStmts != nullptr);
466     auto *blockBody = staticBlock->Function()->Body()->AsBlockStatement();
467     initializerStmts->SetParent(blockBody);
468     blockBody->Statements().emplace_back(initializerStmts);
469 }
470 
AddInitCallToStaticBlock(ir::ClassDefinition * globalClass,ir::MethodDefinition * initMethod)471 void GlobalClassHandler::AddInitCallToStaticBlock(ir::ClassDefinition *globalClass, ir::MethodDefinition *initMethod)
472 {
473     ES2PANDA_ASSERT(initMethod != nullptr);
474 
475     auto &globalBody = globalClass->Body();
476     auto maybeStaticBlock = std::find_if(globalBody.begin(), globalBody.end(),
477                                          [](ir::AstNode *node) { return node->IsClassStaticBlock(); });
478     ES2PANDA_ASSERT(maybeStaticBlock != globalBody.end());
479 
480     auto *staticBlock = (*maybeStaticBlock)->AsClassStaticBlock();
481     ES2PANDA_ASSERT(initMethod->Id() != nullptr);
482     auto *callee = RefIdent(initMethod->Id()->Name());
483 
484     auto *const callExpr = NodeAllocator::Alloc<ir::CallExpression>(
485         allocator_, callee, ArenaVector<ir::Expression *>(allocator_->Adapter()), nullptr, false, false);
486 
487     auto *blockBody = staticBlock->Function()->Body()->AsBlockStatement();
488     auto exprStmt = NodeAllocator::Alloc<ir::ExpressionStatement>(allocator_, callExpr);
489     exprStmt->SetParent(blockBody);
490     blockBody->Statements().emplace_back(exprStmt);
491 }
492 
RefIdent(const util::StringView & name)493 ir::Identifier *GlobalClassHandler::RefIdent(const util::StringView &name)
494 {
495     auto *const callee = NodeAllocator::Alloc<ir::Identifier>(allocator_, name, allocator_);
496     return callee;
497 }
498 
FormInitStaticBlockMethodStatements(parser::Program * program,const ModuleDependencies * moduleDependencies,ArenaVector<GlobalStmts> && initStatements)499 ArenaVector<ArenaVector<ir::Statement *>> GlobalClassHandler::FormInitStaticBlockMethodStatements(
500     parser::Program *program, const ModuleDependencies *moduleDependencies, ArenaVector<GlobalStmts> &&initStatements)
501 {
502     // Note: will create method body for initializer block one by one, don't merge them.
503     ArenaVector<ArenaVector<ir::Statement *>> staticBlocks(allocator_->Adapter());
504     for (const auto &[p, ps] : initStatements) {
505         ArenaVector<ir::Statement *> statements(allocator_->Adapter());
506         if (!util::Helpers::IsStdLib(program) && moduleDependencies != nullptr) {
507             FormDependentInitTriggers(statements, moduleDependencies);
508         }
509         statements.insert(statements.end(), ps.begin(), ps.end());
510         std::for_each(statements.begin(), statements.end(), [](auto stmt) { stmt->SetParent(nullptr); });
511         staticBlocks.emplace_back(std::move(statements));
512     }
513     return staticBlocks;
514 }
515 
FormInitMethodStatements(parser::Program * program,const ModuleDependencies * moduleDependencies,ArenaVector<GlobalStmts> && initStatements)516 ArenaVector<ir::Statement *> GlobalClassHandler::FormInitMethodStatements(parser::Program *program,
517                                                                           const ModuleDependencies *moduleDependencies,
518                                                                           ArenaVector<GlobalStmts> &&initStatements)
519 {
520     ArenaVector<ir::Statement *> statements(allocator_->Adapter());
521     if (!util::Helpers::IsStdLib(program) && moduleDependencies != nullptr) {
522         FormDependentInitTriggers(statements, moduleDependencies);
523     }
524     for (const auto &[p, ps] : initStatements) {
525         for (auto st : ps) {
526             TransformBrokenNamespace(st, p);
527             st->SetParent(nullptr);
528         }
529         statements.insert(statements.end(), ps.begin(), ps.end());
530     }
531     return statements;
532 }
533 
FormDependentInitTriggers(ArenaVector<ir::Statement * > & statements,const ModuleDependencies * moduleDependencies)534 void GlobalClassHandler::FormDependentInitTriggers([[maybe_unused]] ArenaVector<ir::Statement *> &statements,
535                                                    [[maybe_unused]] const ModuleDependencies *moduleDependencies)
536 {
537     // NOTE(dslynko, #26183): leaving this function for later reuse in `import "path"` feature,
538     // which would initialize the dependency.
539 }
540 
CreateStaticBlock(ir::ClassDefinition * classDef)541 ir::ClassStaticBlock *GlobalClassHandler::CreateStaticBlock(ir::ClassDefinition *classDef)
542 {
543     bool hasStaticField = false;
544     for (const auto *prop : classDef->Body()) {
545         if (prop->IsClassStaticBlock()) {
546             return nullptr;
547         }
548         if (prop->IsClassProperty() && prop->AsClassProperty()->IsStatic()) {
549             hasStaticField = true;
550         }
551     }
552 
553     if (!hasStaticField && !classDef->IsModule()) {
554         return nullptr;
555     }
556 
557     ArenaVector<ir::Expression *> params(allocator_->Adapter());
558 
559     auto *id = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::CCTOR, allocator_);
560 
561     ArenaVector<ir::Statement *> statements(allocator_->Adapter());
562 
563     auto *body = NodeAllocator::Alloc<ir::BlockStatement>(allocator_, allocator_, std::move(statements));
564     auto *func = NodeAllocator::Alloc<ir::ScriptFunction>(
565         allocator_, allocator_,
566         ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
567                                                 ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::HIDDEN,
568                                                 ir::ModifierFlags::STATIC, Language(Language::Id::ETS)});
569     ES2PANDA_ASSERT(func != nullptr);
570     func->SetIdent(id);
571 
572     auto *funcExpr = NodeAllocator::Alloc<ir::FunctionExpression>(allocator_, func);
573     auto *staticBlock = NodeAllocator::Alloc<ir::ClassStaticBlock>(allocator_, funcExpr, allocator_);
574     staticBlock->AddModifier(ir::ModifierFlags::STATIC);
575     staticBlock->SetRange({classDef->Start(), classDef->Start()});
576     return staticBlock;
577 }
578 
CollectProgramGlobalStatements(ArenaVector<ir::Statement * > & stmts,ir::ClassDefinition * classDef,ir::Statement const * stmt)579 GlobalDeclTransformer::ResultT GlobalClassHandler::CollectProgramGlobalStatements(ArenaVector<ir::Statement *> &stmts,
580                                                                                   ir::ClassDefinition *classDef,
581                                                                                   ir::Statement const *stmt)
582 {
583     auto globalDecl = GlobalDeclTransformer(allocator_, stmt, parser_);
584     auto statements = globalDecl.TransformStatements(stmts);
585     if (globalDecl.IsMultiInitializer() && stmt->IsETSModule() && stmt->AsETSModule()->IsNamespace()) {
586         parser_->LogError(diagnostic::MULTIPLE_STATIC_BLOCK, {}, statements.initializerBlocks[0][0]->Start());
587     }
588 
589     if (stmt->IsETSModule() && !stmt->AsETSModule()->IsNamespace() && stmt->AsETSModule()->Program()->IsPackage()) {
590         const auto &immInitsOfPackage = statements.immediateInit;
591         std::for_each(immInitsOfPackage.begin(), immInitsOfPackage.end(), [this](auto immInit) {
592             if (immInit->IsExpressionStatement() &&
593                 !immInit->AsExpressionStatement()->GetExpression()->IsAssignmentExpression()) {
594                 this->parser_->LogError(diagnostic::INVALID_PACKAGE_TOP_LEVEL_STMT, {}, immInit->Start());
595             }
596         });
597     }
598     classDef->AddProperties(util::Helpers::ConvertVector<ir::AstNode>(statements.classProperties));
599     /*
600     initializers consists of two parts:
601     immediate initializers and initializer blocks, the former should be executed firstly.
602 
603     Example code:
604         namespace NS {
605             let a: number;
606             let b: number = 2;
607             static {
608                 a = 1;
609                 b = 0;
610             }
611         }
612 
613     In the example code, execute order will be: b = 2, a = 1, b = 0;
614     */
615     globalDecl.FilterDeclarations(stmts);
616     return statements;
617 }
618 
CreateGlobalClass(const parser::Program * const globalProgram)619 ir::ClassDeclaration *GlobalClassHandler::CreateGlobalClass(const parser::Program *const globalProgram)
620 {
621     const auto rangeToStartOfFile =
622         lexer::SourceRange(lexer::SourcePosition(globalProgram), lexer::SourcePosition(globalProgram));
623     auto *ident = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::ETS_GLOBAL, allocator_);
624     ident->SetRange(rangeToStartOfFile);
625     auto *classDef =
626         NodeAllocator::Alloc<ir::ClassDefinition>(allocator_, allocator_, ident, ir::ClassDefinitionModifiers::GLOBAL,
627                                                   ir::ModifierFlags::ABSTRACT, Language(Language::Id::ETS));
628     ES2PANDA_ASSERT(classDef != nullptr);
629     classDef->SetRange(rangeToStartOfFile);
630     auto *classDecl = NodeAllocator::Alloc<ir::ClassDeclaration>(allocator_, classDef, allocator_);
631     classDecl->SetRange(rangeToStartOfFile);
632 
633     return classDecl;
634 }
635 
HasMethod(ir::ClassDefinition const * cls,const std::string_view name)636 static bool HasMethod(ir::ClassDefinition const *cls, const std::string_view name)
637 {
638     return std::any_of(cls->Body().begin(), cls->Body().end(), [name](ir::AstNode const *node) {
639         return node->IsMethodDefinition() && node->AsMethodDefinition()->Key()->AsIdentifier()->Name().Is(name);
640     });
641 }
642 
SetupGlobalMethods(parser::Program * program,ArenaVector<ir::Statement * > && initStatements)643 void GlobalClassHandler::SetupGlobalMethods(parser::Program *program, ArenaVector<ir::Statement *> &&initStatements)
644 {
645     ir::ClassDefinition *const globalClass = program->GlobalClass();
646     SetupGlobalMethods(program, std::move(initStatements), globalClass, program->IsDeclarationModule());
647 
648     if (program->IsSeparateModule() && !HasMethod(globalClass, compiler::Signatures::MAIN)) {
649         ir::MethodDefinition *mainMethod = CreateGlobalMethod(
650             compiler::Signatures::MAIN, ArenaVector<ir::Statement *>(allocator_->Adapter()), program);
651         InsertInGlobal(globalClass, mainMethod);
652     }
653 }
654 
655 }  // namespace ark::es2panda::compiler
656