• 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 
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