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