• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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