1 /*
2 * Copyright (c) 2023 - 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "compiler/lowering/ets/topLevelStmts/globalClassHandler.h"
17
18 #include "ir/statements/classDeclaration.h"
19 #include "ir/base/classDefinition.h"
20 #include "ir/base/classProperty.h"
21 #include "ir/base/classStaticBlock.h"
22 #include "ir/base/scriptFunction.h"
23 #include "ir/base/methodDefinition.h"
24 #include "ir/expressions/identifier.h"
25 #include "ir/expressions/classExpression.h"
26 #include "ir/expressions/functionExpression.h"
27 #include "ir/expressions/callExpression.h"
28 #include "ir/statements/expressionStatement.h"
29 #include "ir/statements/blockStatement.h"
30 #include "compiler/lowering/ets/topLevelStmts/globalDeclTransformer.h"
31 #include "util/helpers.h"
32
33 namespace ark::es2panda::compiler {
34
35 using util::NodeAllocator;
36
MainFunctionExists(const ArenaVector<ir::Statement * > & statements)37 static bool MainFunctionExists(const ArenaVector<ir::Statement *> &statements)
38 {
39 for (auto stmt : statements) {
40 if (stmt->IsFunctionDeclaration() &&
41 stmt->AsFunctionDeclaration()->Function()->Id()->Name().Is(compiler::Signatures::MAIN)) {
42 return true;
43 }
44 }
45 return false;
46 }
47
InitGlobalClass(const ArenaVector<parser::Program * > & programs)48 void GlobalClassHandler::InitGlobalClass(const ArenaVector<parser::Program *> &programs)
49 {
50 if (programs.empty()) {
51 return;
52 }
53 auto globalDecl = CreateGlobalClass();
54 auto globalClass = globalDecl->Definition();
55
56 auto addCCtor = [this](ir::AstNode *node) {
57 if (node->IsClassDefinition()) {
58 auto classDef = node->AsClassDefinition();
59 bool allowEmpty = false;
60 auto staticBlock = CreateCCtor(classDef->Body(), classDef->Start(), allowEmpty);
61 if (staticBlock != nullptr) {
62 classDef->Body().emplace_back(staticBlock);
63 staticBlock->SetParent(classDef);
64 }
65 }
66 };
67
68 ArenaVector<GlobalStmts> statements(allocator_->Adapter());
69 bool mainExists = false;
70 bool topLevelStatementsExist = false;
71 for (auto program : programs) {
72 program->Ast()->IterateRecursively(addCCtor);
73 if (program->IsEntryPoint() && !mainExists && MainFunctionExists(program->Ast()->Statements())) {
74 mainExists = true;
75 }
76
77 // NOTE(rsipka): unclear naming, OmitModuleName() used to determine the entry point without --ets-module option
78 auto stmts = MakeGlobalStatements(program->Ast(), globalClass, program->OmitModuleName());
79 if (!topLevelStatementsExist && !stmts.empty()) {
80 topLevelStatementsExist = true;
81 }
82 statements.emplace_back(GlobalStmts {program, std::move(stmts)});
83 program->SetGlobalClass(globalClass);
84 }
85 InitCallToCCTOR(programs.front(), statements, mainExists, topLevelStatementsExist);
86 }
87
CreateAndFillTopLevelMethod(const ArenaVector<GlobalClassHandler::GlobalStmts> & initStatements,ArenaAllocator * allocator,const std::string_view name)88 static ir::MethodDefinition *CreateAndFillTopLevelMethod(
89 const ArenaVector<GlobalClassHandler::GlobalStmts> &initStatements, ArenaAllocator *allocator,
90 const std::string_view name)
91 {
92 const auto functionFlags = ir::ScriptFunctionFlags::NONE;
93 const auto functionModifiers = ir::ModifierFlags::STATIC | ir::ModifierFlags::PUBLIC;
94 auto *ident = NodeAllocator::Alloc<ir::Identifier>(allocator, name, allocator);
95
96 ArenaVector<ir::Expression *> params(allocator->Adapter());
97
98 ArenaVector<ir::Statement *> statements(allocator->Adapter());
99 auto *body = NodeAllocator::Alloc<ir::BlockStatement>(allocator, allocator, std::move(statements));
100
101 auto funcSignature = ir::FunctionSignature(nullptr, std::move(params), nullptr);
102
103 auto *func = NodeAllocator::Alloc<ir::ScriptFunction>(
104 allocator, allocator,
105 ir::ScriptFunction::ScriptFunctionData {
106 body, std::move(funcSignature), functionFlags, {}, false, Language(Language::Id::ETS)});
107
108 func->SetIdent(ident);
109 func->AddModifier(functionModifiers);
110
111 auto *funcExpr = NodeAllocator::Alloc<ir::FunctionExpression>(allocator, func);
112 auto methodDef = NodeAllocator::Alloc<ir::MethodDefinition>(allocator, ir::MethodDefinitionKind::METHOD,
113 ident->Clone(allocator, nullptr)->AsExpression(),
114 funcExpr, functionModifiers, allocator, false);
115
116 for (const auto &stmts : initStatements) {
117 for (auto stmt : stmts.statements) {
118 methodDef->Function()->Body()->AsBlockStatement()->Statements().emplace_back(stmt);
119 stmt->SetParent(methodDef->Function()->Body());
120 }
121 }
122 return methodDef;
123 }
124
AddInitCall(ir::ClassDefinition * globalClass,ir::MethodDefinition * initMethod)125 void GlobalClassHandler::AddInitCall(ir::ClassDefinition *globalClass, ir::MethodDefinition *initMethod)
126 {
127 ASSERT(initMethod != nullptr);
128
129 auto &globalBody = globalClass->Body();
130 auto maybeStaticBlock = std::find_if(globalBody.begin(), globalBody.end(),
131 [](ir::AstNode *cctor) { return cctor->IsClassStaticBlock(); });
132 ASSERT(maybeStaticBlock != globalBody.end());
133
134 auto *staticBlock = (*maybeStaticBlock)->AsClassStaticBlock();
135 auto *callee = RefIdent(initMethod->Id()->Name());
136
137 auto *const callExpr = NodeAllocator::Alloc<ir::CallExpression>(
138 allocator_, callee, ArenaVector<ir::Expression *>(allocator_->Adapter()), nullptr, false, false);
139
140 auto *blockBody = staticBlock->Function()->Body()->AsBlockStatement();
141 auto exprStmt = NodeAllocator::Alloc<ir::ExpressionStatement>(allocator_, callExpr);
142 exprStmt->SetParent(blockBody);
143 blockBody->Statements().emplace_back(exprStmt);
144 }
145
RefIdent(const util::StringView & name)146 ir::Identifier *GlobalClassHandler::RefIdent(const util::StringView &name)
147 {
148 auto *const callee = NodeAllocator::Alloc<ir::Identifier>(allocator_, name, allocator_);
149 callee->SetReference();
150 return callee;
151 }
152
CreateCCtor(const ArenaVector<ir::AstNode * > & properties,const lexer::SourcePosition & loc,bool allowEmptyCctor)153 ir::ClassStaticBlock *GlobalClassHandler::CreateCCtor(const ArenaVector<ir::AstNode *> &properties,
154 const lexer::SourcePosition &loc, bool allowEmptyCctor)
155 {
156 bool hasStaticField = false;
157 for (const auto *prop : properties) {
158 if (prop->IsClassStaticBlock()) {
159 return nullptr;
160 }
161
162 if (!prop->IsClassProperty()) {
163 continue;
164 }
165
166 const auto *field = prop->AsClassProperty();
167
168 if (field->IsStatic()) {
169 hasStaticField = true;
170 }
171 }
172
173 if (!hasStaticField && !allowEmptyCctor) {
174 return nullptr;
175 }
176
177 ArenaVector<ir::Expression *> params(allocator_->Adapter());
178
179 auto *id = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::CCTOR, allocator_);
180
181 ArenaVector<ir::Statement *> statements(allocator_->Adapter());
182
183 auto *body = NodeAllocator::Alloc<ir::BlockStatement>(allocator_, allocator_, std::move(statements));
184 auto *func = NodeAllocator::Alloc<ir::ScriptFunction>(
185 allocator_, allocator_,
186 ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
187 ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::HIDDEN,
188 ir::ModifierFlags::STATIC, false, Language(Language::Id::ETS)});
189
190 func->SetIdent(id);
191
192 auto *funcExpr = NodeAllocator::Alloc<ir::FunctionExpression>(allocator_, func);
193 auto *staticBlock = NodeAllocator::Alloc<ir::ClassStaticBlock>(allocator_, funcExpr, allocator_);
194 staticBlock->AddModifier(ir::ModifierFlags::STATIC);
195 staticBlock->SetRange({loc, loc});
196 return staticBlock;
197 }
198
MakeGlobalStatements(ir::BlockStatement * globalStmts,ir::ClassDefinition * classDef,bool addInitializer)199 ArenaVector<ir::Statement *> GlobalClassHandler::MakeGlobalStatements(ir::BlockStatement *globalStmts,
200 ir::ClassDefinition *classDef,
201 bool addInitializer)
202 {
203 auto globalDecl = GlobalDeclTransformer(allocator_);
204 auto statements = globalDecl.TransformStatements(globalStmts->Statements(), addInitializer);
205 classDef->AddProperties(util::Helpers::ConvertVector<ir::AstNode>(statements.classProperties));
206 globalDecl.FilterDeclarations(globalStmts->Statements());
207 return std::move(statements.initStatements);
208 }
209
InitGlobalClass(ir::ClassDefinition * classDef,parser::ScriptKind scriptKind)210 void GlobalClassHandler::InitGlobalClass(ir::ClassDefinition *classDef, parser::ScriptKind scriptKind)
211 {
212 auto &globalProperties = classDef->Body();
213 auto staticBlock = CreateCCtor(globalProperties, classDef->Start(), scriptKind != parser::ScriptKind::STDLIB);
214 if (staticBlock != nullptr) {
215 staticBlock->SetParent(classDef);
216 globalProperties.emplace_back(staticBlock);
217 }
218 classDef->SetGlobalInitialized();
219 }
220
CreateGlobalClass()221 ir::ClassDeclaration *GlobalClassHandler::CreateGlobalClass()
222 {
223 auto *ident = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::ETS_GLOBAL, allocator_);
224
225 auto *classDef =
226 NodeAllocator::Alloc<ir::ClassDefinition>(allocator_, allocator_, ident, ir::ClassDefinitionModifiers::GLOBAL,
227 ir::ModifierFlags::ABSTRACT, Language(Language::Id::ETS));
228 auto *classDecl = NodeAllocator::Alloc<ir::ClassDeclaration>(allocator_, classDef, allocator_);
229 return classDecl;
230 }
231
InitCallToCCTOR(parser::Program * program,const ArenaVector<GlobalStmts> & initStatements,bool mainExists,bool topLevelStatementsExist)232 void GlobalClassHandler::InitCallToCCTOR(parser::Program *program, const ArenaVector<GlobalStmts> &initStatements,
233 bool mainExists, bool topLevelStatementsExist)
234 {
235 auto globalClass = program->GlobalClass();
236 auto globalDecl = globalClass->Parent()->AsClassDeclaration();
237 program->Ast()->Statements().emplace_back(globalDecl);
238 globalDecl->SetParent(program->Ast());
239 InitGlobalClass(globalClass, program->Kind());
240 auto &globalBody = globalClass->Body();
241 // NOTE(rsipka): unclear call, OmitModuleName() used to determine the entry points without --ets-module option
242 if (program->OmitModuleName() && program->Kind() != parser::ScriptKind::STDLIB) {
243 ir::MethodDefinition *initMethod = CreateAndFillTopLevelMethod(initStatements, allocator_, INIT_NAME);
244 ir::MethodDefinition *mainMethod = nullptr;
245 if (!mainExists && topLevelStatementsExist) {
246 const ArenaVector<GlobalStmts> emptyStatements(allocator_->Adapter());
247 mainMethod = CreateAndFillTopLevelMethod(emptyStatements, allocator_, compiler::Signatures::MAIN);
248 }
249 if (initMethod != nullptr) {
250 initMethod->SetParent(program->GlobalClass());
251 globalBody.insert(globalBody.begin(), initMethod);
252 if (!initMethod->Function()->Body()->AsBlockStatement()->Statements().empty()) {
253 AddInitCall(program->GlobalClass(), initMethod);
254 }
255 }
256 if (mainMethod != nullptr) {
257 mainMethod->SetParent(program->GlobalClass());
258 globalBody.insert(globalBody.begin(), mainMethod);
259 }
260 }
261 }
262
263 } // namespace ark::es2panda::compiler
264