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 35namespace ark::es2panda::compiler { 36 37using util::NodeAllocator; 38 39static 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 49void 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()) { 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 102ir::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, {}, false, 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 125void 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 146ir::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 153util::UString GlobalClassHandler::ReplaceSpecialCharacters(util::UString *word) const 154/* 155 * This function replaces special characters that might occur in a a filename but should not be in a method name. 156 * 157 * `$` is an exception: it is replaced so that it would not crash with the naming in `FormTriggeringCCtorMethodName`. 158 */ 159{ 160 std::unordered_map<char, std::string> replacements = { 161 {'.', "$DOT$"}, 162 {':', "$COLON$"}, 163 {';', "$SEMICOLON$"}, 164 {',', "$COMMA$"}, 165 {'/', "$SLASH$"}, 166 {'\\', "$BACKSLASH$"}, 167 {'|', "$PIPE$"}, 168 {'!', "$EXCL_MARK$"}, 169 {'?', "$QUESTION_MARK$"}, 170 {'~', "$TILDE$"}, 171 {'@', "$AT_SIGN$"}, 172 {'&', "$AND_SIGN$"}, 173 {'#', "$HASHMARK$"}, 174 {'$', "$DOLLAR_SIGN$"}, 175 {'^', "$CARET$"}, 176 {'*', "$ASTERISK$"}, 177 {'=', "$EQUAL_SIGN$"}, 178 {'(', "$OPEN_PARENTHESIS$"}, 179 {')', "$CLOSE_PARENTHESIS$"}, 180 {'{', "$OPEN_CURLY_BRACE$"}, 181 {'}', "$CLOSE_CURLY_BRACE$"}, 182 {'[', "$OPEN_BRACKET$"}, 183 {']', "$CLOSE_BRACKET$"}, 184 {'<', "$OPEN_ANGULAR_BRACKET$"}, 185 {'>', "$CLOSE_ANGULAR_BRACKET$"}, 186 {'\'', "$APOSTROPHE$"}, 187 {'"', "$DOUBLE_QUOTATION_MARK$"}, 188 {' ', "$SPACE$"}, 189 }; 190 191 size_t pos = 0; 192 193 auto text = word->View().Mutf8(); 194 while (pos < text.size()) { 195 char currentChar = text[pos]; 196 197 if (replacements.find(currentChar) != replacements.end()) { 198 const auto replacement = replacements.at(currentChar); 199 text.replace(pos, 1, replacement); 200 201 pos += replacement.size(); 202 } else { 203 ++pos; 204 } 205 } 206 207 return util::UString(text, allocator_); 208} 209 210ArenaVector<ir::Statement *> GlobalClassHandler::FormInitMethodStatements(parser::Program *program, 211 const ModuleDependencies *moduleDependencies, 212 ArenaVector<GlobalStmts> &&initStatements) 213{ 214 ArenaVector<ir::Statement *> statements(allocator_->Adapter()); 215 if (!util::Helpers::IsStdLib(program) && moduleDependencies != nullptr) { 216 FormDependentInitTriggers(statements, moduleDependencies); 217 } 218 for (const auto &[p, ps] : initStatements) { 219 statements.insert(statements.end(), ps.begin(), ps.end()); 220 } 221 for (auto st : statements) { 222 st->SetParent(nullptr); 223 } 224 return statements; 225} 226 227void GlobalClassHandler::FormDependentInitTriggers(ArenaVector<ir::Statement *> &statements, 228 const ModuleDependencies *moduleDependencies) 229{ 230 auto const sequence = [&statements](ir::Statement *stmt) { statements.push_back(stmt); }; 231 232 auto triggerInitOf = [this, sequence, initialized = false](parser::Program *prog) mutable { 233 if (!initialized) { 234 initialized = true; 235 sequence(parser_->CreateFormattedStatement("const __linker = Class.ofCaller().getLinker();")); 236 } 237 std::string name = (prog->OmitModuleName() ? "" : std::string(prog->ModuleName()) + ".") + "ETSGLOBAL"; 238 sequence(parser_->CreateFormattedStatement("__linker.loadClass(\"" + name + "\", true);")); 239 }; 240 241 for (auto depProg : *moduleDependencies) { 242 if (util::Helpers::IsStdLib(depProg)) { 243 continue; 244 } 245 triggerInitOf(depProg); 246 } 247} 248 249ir::ClassStaticBlock *GlobalClassHandler::CreateStaticBlock(ir::ClassDefinition *classDef) 250{ 251 bool hasStaticField = false; 252 for (const auto *prop : classDef->Body()) { 253 if (prop->IsClassStaticBlock()) { 254 return nullptr; 255 } 256 if (prop->IsClassProperty() && prop->AsClassProperty()->IsStatic()) { 257 hasStaticField = true; 258 } 259 } 260 261 if (!hasStaticField && !classDef->IsGlobal()) { 262 return nullptr; 263 } 264 265 ArenaVector<ir::Expression *> params(allocator_->Adapter()); 266 267 auto *id = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::CCTOR, allocator_); 268 269 ArenaVector<ir::Statement *> statements(allocator_->Adapter()); 270 271 auto *body = NodeAllocator::Alloc<ir::BlockStatement>(allocator_, allocator_, std::move(statements)); 272 auto *func = NodeAllocator::Alloc<ir::ScriptFunction>( 273 allocator_, allocator_, 274 ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr), 275 ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::HIDDEN, 276 ir::ModifierFlags::STATIC, false, Language(Language::Id::ETS)}); 277 278 func->SetIdent(id); 279 280 auto *funcExpr = NodeAllocator::Alloc<ir::FunctionExpression>(allocator_, func); 281 auto *staticBlock = NodeAllocator::Alloc<ir::ClassStaticBlock>(allocator_, funcExpr, allocator_); 282 staticBlock->AddModifier(ir::ModifierFlags::STATIC); 283 staticBlock->SetRange({classDef->Start(), classDef->Start()}); 284 return staticBlock; 285} 286 287ArenaVector<ir::Statement *> GlobalClassHandler::CollectProgramGlobalStatements(parser::Program *program, 288 ir::ClassDefinition *classDef, 289 bool addInitializer) 290{ 291 auto ast = program->Ast(); 292 auto globalDecl = GlobalDeclTransformer(allocator_); 293 auto statements = globalDecl.TransformStatements(ast->Statements(), addInitializer); 294 classDef->AddProperties(util::Helpers::ConvertVector<ir::AstNode>(statements.classProperties)); 295 globalDecl.FilterDeclarations(ast->Statements()); 296 return std::move(statements.initStatements); 297} 298 299ir::ClassDeclaration *GlobalClassHandler::CreateGlobalClass() 300{ 301 auto *ident = NodeAllocator::Alloc<ir::Identifier>(allocator_, compiler::Signatures::ETS_GLOBAL, allocator_); 302 303 auto *classDef = 304 NodeAllocator::Alloc<ir::ClassDefinition>(allocator_, allocator_, ident, ir::ClassDefinitionModifiers::GLOBAL, 305 ir::ModifierFlags::ABSTRACT, Language(Language::Id::ETS)); 306 auto *classDecl = NodeAllocator::Alloc<ir::ClassDeclaration>(allocator_, classDef, allocator_); 307 return classDecl; 308} 309 310void GlobalClassHandler::SetupGlobalMethods(parser::Program *program, ArenaVector<ir::Statement *> &&initStatements, 311 bool mainExists, bool topLevelStatementsExist) 312{ 313 ir::ClassDefinition *const globalClass = program->GlobalClass(); 314 315 auto const insertInGlobal = [globalClass](ir::AstNode *node) { 316 // NOTE(vpukhov): inserted to begin for some reason 317 globalClass->Body().insert(globalClass->Body().begin(), node); 318 node->SetParent(globalClass); 319 }; 320 321 if (!program->IsDeclarationModule()) { 322 ir::MethodDefinition *initMethod = 323 CreateGlobalMethod(compiler::Signatures::INIT_METHOD, std::move(initStatements)); 324 insertInGlobal(initMethod); 325 if (!initMethod->Function()->Body()->AsBlockStatement()->Statements().empty()) { 326 AddInitCallFromStaticBlock(globalClass, initMethod); 327 } 328 } 329 330 // NOTE(rsipka): unclear call, OmitModuleName() used to determine the entry points without --ets-module option 331 if (program->OmitModuleName()) { 332 if (!mainExists && topLevelStatementsExist) { 333 ir::MethodDefinition *mainMethod = 334 CreateGlobalMethod(compiler::Signatures::MAIN, ArenaVector<ir::Statement *>(allocator_->Adapter())); 335 insertInGlobal(mainMethod); 336 } 337 } 338} 339 340} // namespace ark::es2panda::compiler 341