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 #include "compiler/lowering/ets/topLevelStmts/globalDeclTransformer.h"
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
35 namespace ark::es2panda::compiler {
36
37 using util::NodeAllocator;
38
FunctionExists(const ArenaVector<ir::Statement * > & statements,const std::string_view name)39 static bool FunctionExists(const ArenaVector<ir::Statement *> &statements, const std::string_view name)
40 {
41 for (auto stmt : statements) {
42 if (stmt->IsFunctionDeclaration() && stmt->AsFunctionDeclaration()->Function()->Id()->Name().Is(name)) {
43 return true;
44 }
45 }
46 return false;
47 }
48
SetupGlobalClass(const ArenaVector<parser::Program * > & programs,const ModuleDependencies * moduleDependencies)49 void GlobalClassHandler::SetupGlobalClass(const ArenaVector<parser::Program *> &programs,
50 const ModuleDependencies *moduleDependencies)
51 {
52 if (programs.empty()) {
53 return;
54 }
55 ir::ClassDeclaration *const globalDecl = CreateGlobalClass();
56 ir::ClassDefinition *const globalClass = globalDecl->Definition();
57
58 auto addStaticBlock = [this](ir::AstNode *node) {
59 if (node->IsClassDefinition() && !node->AsClassDefinition()->IsDeclare()) {
60 auto classDef = node->AsClassDefinition();
61 if (auto staticBlock = CreateStaticBlock(classDef); staticBlock != nullptr) {
62 classDef->Body().emplace_back(staticBlock); // NOTE(vpukhov): inserted to end for some reason
63 staticBlock->SetParent(classDef);
64 }
65 }
66 };
67
68 ArenaVector<GlobalStmts> statements(allocator_->Adapter());
69 bool mainExists = false;
70 bool topLevelStatementsExist = false;
71 parser::Program *const globalProgram = programs.front();
72
73 bool isEntrypoint = programs.size() == 1 ? globalProgram->IsEntryPoint() : false;
74 for (auto program : programs) {
75 program->Ast()->IterateRecursively(addStaticBlock);
76 if (program->IsEntryPoint() && !mainExists &&
77 FunctionExists(program->Ast()->Statements(), compiler::Signatures::MAIN)) {
78 mainExists = true;
79 }
80 auto stmts = CollectProgramGlobalStatements(program, globalClass, isEntrypoint);
81 if (!topLevelStatementsExist && !stmts.empty()) {
82 topLevelStatementsExist = true;
83 }
84 statements.emplace_back(GlobalStmts {program, std::move(stmts)});
85 program->SetGlobalClass(globalClass);
86 }
87
88 globalProgram->Ast()->Statements().emplace_back(globalDecl);
89 globalDecl->SetParent(globalProgram->Ast());
90 globalClass->SetGlobalInitialized();
91
92 // NOTE(vpukhov): stdlib checks are to be removed - do not extend the existing logic
93 if (globalProgram->Kind() != parser::ScriptKind::STDLIB) {
94 addStaticBlock(globalClass);
95 if (!util::Helpers::IsStdLib(globalProgram)) {
96 auto initStatements = FormInitMethodStatements(globalProgram, moduleDependencies, std::move(statements));
97 SetupGlobalMethods(globalProgram, std::move(initStatements), mainExists, topLevelStatementsExist);
98 }
99 }
100 }
101
CreateGlobalMethod(const std::string_view name,ArenaVector<ir::Statement * > && statements)102 ir::MethodDefinition *GlobalClassHandler::CreateGlobalMethod(const std::string_view name,
103 ArenaVector<ir::Statement *> &&statements)
104 {
105 const auto functionFlags = ir::ScriptFunctionFlags::NONE;
106 auto functionModifiers = ir::ModifierFlags::STATIC | ir::ModifierFlags::PUBLIC;
107 auto ident = NodeAllocator::Alloc<ir::Identifier>(allocator_, name, allocator_);
108 auto body = NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator_, allocator_, std::move(statements));
109 auto funcSignature = ir::FunctionSignature(nullptr, ArenaVector<ir::Expression *>(allocator_->Adapter()), nullptr);
110
111 auto *func = NodeAllocator::Alloc<ir::ScriptFunction>(
112 allocator_, allocator_,
113 ir::ScriptFunction::ScriptFunctionData {
114 body, std::move(funcSignature), functionFlags, {}, Language(Language::Id::ETS)});
115
116 func->SetIdent(ident);
117 func->AddModifier(functionModifiers);
118
119 auto *funcExpr = NodeAllocator::Alloc<ir::FunctionExpression>(allocator_, func);
120 return NodeAllocator::Alloc<ir::MethodDefinition>(allocator_, ir::MethodDefinitionKind::METHOD,
121 ident->Clone(allocator_, nullptr)->AsExpression(), funcExpr,
122 functionModifiers, allocator_, false);
123 }
124
AddInitCallFromStaticBlock(ir::ClassDefinition * globalClass,ir::MethodDefinition * initMethod)125 void GlobalClassHandler::AddInitCallFromStaticBlock(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 *node) { return node->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 return callee;
150 }
151
ReplaceSpecialCharacters(util::UString * word) const152 util::UString GlobalClassHandler::ReplaceSpecialCharacters(util::UString *word) const
153 /*
154 * This function replaces special characters that might occur in a a filename but should not be in a method name.
155 *
156 * `$` is an exception: it is replaced so that it would not crash with the naming in `FormTriggeringCCtorMethodName`.
157 */
158 {
159 std::unordered_map<char, std::string> replacements = {
160 {'.', "$DOT$"},
161 {':', "$COLON$"},
162 {';', "$SEMICOLON$"},
163 {',', "$COMMA$"},
164 {'/', "$SLASH$"},
165 {'\\', "$BACKSLASH$"},
166 {'|', "$PIPE$"},
167 {'!', "$EXCL_MARK$"},
168 {'?', "$QUESTION_MARK$"},
169 {'~', "$TILDE$"},
170 {'@', "$AT_SIGN$"},
171 {'&', "$AND_SIGN$"},
172 {'#', "$HASHMARK$"},
173 {'$', "$DOLLAR_SIGN$"},
174 {'^', "$CARET$"},
175 {'*', "$ASTERISK$"},
176 {'=', "$EQUAL_SIGN$"},
177 {'(', "$OPEN_PARENTHESIS$"},
178 {')', "$CLOSE_PARENTHESIS$"},
179 {'{', "$OPEN_CURLY_BRACE$"},
180 {'}', "$CLOSE_CURLY_BRACE$"},
181 {'[', "$OPEN_BRACKET$"},
182 {']', "$CLOSE_BRACKET$"},
183 {'<', "$OPEN_ANGULAR_BRACKET$"},
184 {'>', "$CLOSE_ANGULAR_BRACKET$"},
185 {'\'', "$APOSTROPHE$"},
186 {'"', "$DOUBLE_QUOTATION_MARK$"},
187 {' ', "$SPACE$"},
188 };
189
190 size_t pos = 0;
191
192 auto text = word->View().Mutf8();
193 while (pos < text.size()) {
194 char currentChar = text[pos];
195
196 if (replacements.find(currentChar) != replacements.end()) {
197 const auto replacement = replacements.at(currentChar);
198 text.replace(pos, 1, replacement);
199
200 pos += replacement.size();
201 } else {
202 ++pos;
203 }
204 }
205
206 return util::UString(text, allocator_);
207 }
208
FormInitMethodStatements(parser::Program * program,const ModuleDependencies * moduleDependencies,ArenaVector<GlobalStmts> && initStatements)209 ArenaVector<ir::Statement *> GlobalClassHandler::FormInitMethodStatements(parser::Program *program,
210 const ModuleDependencies *moduleDependencies,
211 ArenaVector<GlobalStmts> &&initStatements)
212 {
213 ArenaVector<ir::Statement *> statements(allocator_->Adapter());
214 if (!util::Helpers::IsStdLib(program) && moduleDependencies != nullptr) {
215 FormDependentInitTriggers(statements, moduleDependencies);
216 }
217 for (const auto &[p, ps] : initStatements) {
218 statements.insert(statements.end(), ps.begin(), ps.end());
219 }
220 for (auto st : statements) {
221 st->SetParent(nullptr);
222 }
223 return statements;
224 }
225
FormDependentInitTriggers(ArenaVector<ir::Statement * > & statements,const ModuleDependencies * moduleDependencies)226 void GlobalClassHandler::FormDependentInitTriggers(ArenaVector<ir::Statement *> &statements,
227 const ModuleDependencies *moduleDependencies)
228 {
229 auto const sequence = [&statements](ir::Statement *stmt) { statements.push_back(stmt); };
230
231 auto triggerInitOf = [this, sequence, initialized = false](parser::Program *prog) mutable {
232 if (!initialized) {
233 initialized = true;
234 sequence(parser_->CreateFormattedStatement("const __linker = Class.ofCaller().getLinker();"));
235 }
236 std::string name = (prog->OmitModuleName() ? "" : std::string(prog->ModuleName()) + ".") + "ETSGLOBAL";
237 sequence(parser_->CreateFormattedStatement("__linker.loadClass(\"" + name + "\", true);"));
238 };
239
240 for (auto depProg : *moduleDependencies) {
241 if (util::Helpers::IsStdLib(depProg)) {
242 continue;
243 }
244 triggerInitOf(depProg);
245 }
246 }
247
CreateStaticBlock(ir::ClassDefinition * classDef)248 ir::ClassStaticBlock *GlobalClassHandler::CreateStaticBlock(ir::ClassDefinition *classDef)
249 {
250 bool hasStaticField = false;
251 for (const auto *prop : classDef->Body()) {
252 if (prop->IsClassStaticBlock()) {
253 return nullptr;
254 }
255 if (prop->IsClassProperty() && prop->AsClassProperty()->IsStatic()) {
256 hasStaticField = true;
257 }
258 }
259
260 if (!hasStaticField && !classDef->IsGlobal()) {
261 return nullptr;
262 }
263
264 ArenaVector<ir::Expression *> params(allocator_->Adapter());
265
266 auto *id = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::CCTOR, allocator_);
267
268 ArenaVector<ir::Statement *> statements(allocator_->Adapter());
269
270 auto *body = NodeAllocator::Alloc<ir::BlockStatement>(allocator_, allocator_, std::move(statements));
271 auto *func = NodeAllocator::Alloc<ir::ScriptFunction>(
272 allocator_, allocator_,
273 ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
274 ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::HIDDEN,
275 ir::ModifierFlags::STATIC, Language(Language::Id::ETS)});
276
277 func->SetIdent(id);
278
279 auto *funcExpr = NodeAllocator::Alloc<ir::FunctionExpression>(allocator_, func);
280 auto *staticBlock = NodeAllocator::Alloc<ir::ClassStaticBlock>(allocator_, funcExpr, allocator_);
281 staticBlock->AddModifier(ir::ModifierFlags::STATIC);
282 staticBlock->SetRange({classDef->Start(), classDef->Start()});
283 return staticBlock;
284 }
285
CollectProgramGlobalStatements(parser::Program * program,ir::ClassDefinition * classDef,bool addInitializer)286 ArenaVector<ir::Statement *> GlobalClassHandler::CollectProgramGlobalStatements(parser::Program *program,
287 ir::ClassDefinition *classDef,
288 bool addInitializer)
289 {
290 auto ast = program->Ast();
291 auto globalDecl = GlobalDeclTransformer(allocator_);
292 auto statements = globalDecl.TransformStatements(ast->Statements(), addInitializer);
293 classDef->AddProperties(util::Helpers::ConvertVector<ir::AstNode>(statements.classProperties));
294 globalDecl.FilterDeclarations(ast->Statements());
295 return std::move(statements.initStatements);
296 }
297
CreateGlobalClass()298 ir::ClassDeclaration *GlobalClassHandler::CreateGlobalClass()
299 {
300 auto *ident = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::ETS_GLOBAL, allocator_);
301
302 auto *classDef =
303 NodeAllocator::Alloc<ir::ClassDefinition>(allocator_, allocator_, ident, ir::ClassDefinitionModifiers::GLOBAL,
304 ir::ModifierFlags::ABSTRACT, Language(Language::Id::ETS));
305 auto *classDecl = NodeAllocator::Alloc<ir::ClassDeclaration>(allocator_, classDef, allocator_);
306 return classDecl;
307 }
308
SetupGlobalMethods(parser::Program * program,ArenaVector<ir::Statement * > && initStatements,bool mainExists,bool topLevelStatementsExist)309 void GlobalClassHandler::SetupGlobalMethods(parser::Program *program, ArenaVector<ir::Statement *> &&initStatements,
310 bool mainExists, bool topLevelStatementsExist)
311 {
312 ir::ClassDefinition *const globalClass = program->GlobalClass();
313
314 auto const insertInGlobal = [globalClass](ir::AstNode *node) {
315 // NOTE(vpukhov): inserted to begin for some reason
316 globalClass->Body().insert(globalClass->Body().begin(), node);
317 node->SetParent(globalClass);
318 };
319
320 if (!program->IsDeclarationModule()) {
321 ir::MethodDefinition *initMethod =
322 CreateGlobalMethod(compiler::Signatures::INIT_METHOD, std::move(initStatements));
323 insertInGlobal(initMethod);
324 if (!initMethod->Function()->Body()->AsBlockStatement()->Statements().empty()) {
325 AddInitCallFromStaticBlock(globalClass, initMethod);
326 }
327 }
328
329 // NOTE(rsipka): unclear call, OmitModuleName() used to determine the entry points without --ets-module option
330 if (program->OmitModuleName()) {
331 if (!mainExists && topLevelStatementsExist) {
332 ir::MethodDefinition *mainMethod =
333 CreateGlobalMethod(compiler::Signatures::MAIN, ArenaVector<ir::Statement *>(allocator_->Adapter()));
334 insertInGlobal(mainMethod);
335 }
336 }
337 }
338
339 } // namespace ark::es2panda::compiler
340