1 /**
2 * Copyright (c) 2023-2025 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 "util.h"
17
18 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
19 #include "ir/expressions/identifier.h"
20 #include "checker/checker.h"
21 #include "checker/ETSAnalyzer.h"
22
23 namespace ark::es2panda::compiler {
24
HasGlobalClassParent(const ir::AstNode * node)25 bool HasGlobalClassParent(const ir::AstNode *node)
26 {
27 auto parentClass = util::Helpers::FindAncestorGivenByType(node, ir::AstNodeType::CLASS_DEFINITION);
28 return parentClass != nullptr && parentClass->AsClassDefinition()->IsGlobal();
29 }
30
NearestScope(const ir::AstNode * ast)31 varbinder::Scope *NearestScope(const ir::AstNode *ast)
32 {
33 while (ast != nullptr && !ast->IsScopeBearer()) {
34 ast = ast->Parent();
35 }
36
37 return ast == nullptr ? nullptr : ast->Scope();
38 }
39
ContainingClass(const ir::AstNode * ast)40 checker::ETSObjectType const *ContainingClass(const ir::AstNode *ast)
41 {
42 ast = util::Helpers::FindAncestorGivenByType(ast, ir::AstNodeType::CLASS_DEFINITION);
43 return ast == nullptr ? nullptr : ast->AsClassDefinition()->TsType()->AsETSObjectType();
44 }
45
Gensym(ArenaAllocator * const allocator)46 ir::Identifier *Gensym(ArenaAllocator *const allocator)
47 {
48 util::UString const s = GenName(allocator);
49 return allocator->New<ir::Identifier>(s.View(), allocator);
50 }
51
GenName(ArenaAllocator * const allocator)52 util::UString GenName(ArenaAllocator *const allocator)
53 {
54 static std::size_t gensymCounter = 0U;
55 return util::UString {std::string(GENSYM_CORE) + std::to_string(++gensymCounter), allocator};
56 }
57
SetSourceRangesRecursively(ir::AstNode * node,const lexer::SourceRange & range)58 void SetSourceRangesRecursively(ir::AstNode *node, const lexer::SourceRange &range)
59 {
60 ES2PANDA_ASSERT(node != nullptr);
61 node->SetRange(range);
62 node->IterateRecursively([](ir::AstNode *n) { n->SetRange(n->Parent()->Range()); });
63 }
64
RefineSourceRanges(ir::AstNode * node)65 ir::AstNode *RefineSourceRanges(ir::AstNode *node)
66 {
67 auto const isDummyLoc = [](lexer::SourceRange const &range, ir::AstNode *ast) {
68 return (range.start.index == 0 && range.start.line == 0) || (range.end.index < range.start.index) ||
69 (range.start.index < ast->Parent()->Start().index) || (range.end.index > ast->Parent()->End().index);
70 };
71
72 auto const refine = [isDummyLoc](ir::AstNode *ast) {
73 if (isDummyLoc(ast->Range(), ast) && ast->Parent() != nullptr) {
74 ast->SetRange(ast->Parent()->Range());
75 }
76 };
77
78 refine(node);
79 node->IterateRecursively(refine);
80 return node;
81 }
82
83 // Function to clear expression node types and identifier node variables (for correct re-binding and re-checking)
ClearTypesVariablesAndScopes(ir::AstNode * node)84 void ClearTypesVariablesAndScopes(ir::AstNode *node) noexcept
85 {
86 std::function<void(ir::AstNode *)> doNode = [&](ir::AstNode *nn) {
87 if (nn->IsScopeBearer()) {
88 nn->ClearScope();
89 }
90 if (nn->IsTyped() && !(nn->IsExpression() && nn->AsExpression()->IsTypeNode())) {
91 nn->AsTyped()->SetTsType(nullptr);
92 }
93 if (nn->IsIdentifier()) {
94 nn->AsIdentifier()->SetVariable(nullptr);
95 }
96 if (!nn->IsETSTypeReference()) {
97 nn->Iterate([&](ir::AstNode *child) { doNode(child); });
98 }
99 };
100
101 doNode(node);
102 }
103
FindCaptured(ArenaAllocator * allocator,ir::AstNode * scopeBearer)104 ArenaSet<varbinder::Variable *> FindCaptured(ArenaAllocator *allocator, ir::AstNode *scopeBearer) noexcept
105 {
106 auto result = ArenaSet<varbinder::Variable *> {allocator->Adapter()};
107 auto scopes = ArenaSet<varbinder::Scope *> {allocator->Adapter()};
108 scopeBearer->IterateRecursivelyPreorder([&result, &scopes](ir::AstNode *ast) {
109 if (ast->IsScopeBearer() && ast->Scope() != nullptr) {
110 scopes.insert(ast->Scope());
111 if (ast->Scope()->IsFunctionScope()) {
112 scopes.insert(ast->Scope()->AsFunctionScope()->ParamScope());
113 } else if (ast->IsForUpdateStatement() || ast->IsForInStatement() || ast->IsForOfStatement() ||
114 ast->IsCatchClause()) {
115 // NOTE(gogabr) LoopScope _does not_ currently respond to IsLoopScope().
116 // For now, this is the way to reach LoopDeclarationScope.
117 scopes.insert(ast->Scope()->Parent());
118 }
119 }
120 if (ast->IsIdentifier() && !ast->Parent()->IsLabelledStatement()) {
121 auto *var = ast->AsIdentifier()->Variable();
122 if (var == nullptr || !var->HasFlag(varbinder::VariableFlags::LOCAL)) {
123 return;
124 }
125 auto *sc = var->GetScope();
126 if (sc != nullptr && !sc->IsClassScope() && !sc->IsGlobalScope() && scopes.count(var->GetScope()) == 0) {
127 result.insert(var);
128 }
129 }
130 });
131 return result;
132 }
133
ResetGlobalClass(parser::Program * prog)134 static void ResetGlobalClass(parser::Program *prog)
135 {
136 for (auto *statement : prog->Ast()->Statements()) {
137 if (statement->IsClassDeclaration() && statement->AsClassDeclaration()->Definition()->IsGlobal()) {
138 prog->SetGlobalClass(statement->AsClassDeclaration()->Definition());
139 break;
140 }
141 }
142 }
143
IsGeneratedForUtilityType(ir::AstNode const * ast)144 static bool IsGeneratedForUtilityType(ir::AstNode const *ast)
145 {
146 if (ast->IsClassDeclaration()) {
147 auto &name = ast->AsClassDeclaration()->Definition()->Ident()->Name();
148 return name.EndsWith(checker::PARTIAL_CLASS_SUFFIX);
149 }
150 if (ast->IsTSInterfaceDeclaration()) {
151 auto &name = ast->AsTSInterfaceDeclaration()->Id()->Name();
152 return name.EndsWith(checker::PARTIAL_CLASS_SUFFIX);
153 }
154 return false;
155 }
156
IsGeneratedDynamicClass(ir::AstNode const * ast)157 static bool IsGeneratedDynamicClass(ir::AstNode const *ast)
158 {
159 if (ast->IsClassDeclaration()) {
160 auto &name = ast->AsClassDeclaration()->Definition()->Ident()->Name();
161 return name.Is(Signatures::JSNEW_CLASS) || name.Is(Signatures::JSCALL_CLASS);
162 }
163
164 return false;
165 }
166
ClearHelper(parser::Program * prog)167 static void ClearHelper(parser::Program *prog)
168 {
169 ResetGlobalClass(prog);
170 prog->ClearASTCheckedStatus();
171 // #24256 Should be removed when code refactoring on checker is done and no ast node allocated in checker.
172 auto &stmts = prog->Ast()->Statements();
173 // clang-format off
174 stmts.erase(std::remove_if(stmts.begin(), stmts.end(),
175 [](ir::AstNode *ast) -> bool {
176 return !ast->HasAstNodeFlags(ir::AstNodeFlags::NOCLEANUP) ||
177 IsGeneratedForUtilityType(ast) || IsGeneratedDynamicClass(ast);
178 }),
179 stmts.end());
180 // clang-format on
181
182 prog->Ast()->IterateRecursively([](ir::AstNode *ast) -> void { ast->CleanUp(); });
183 prog->Ast()->ClearScope();
184 }
185
186 // Rerun varbinder on the node. (First clear typesVariables and scopes)
Rebind(PhaseManager * phaseManager,varbinder::ETSBinder * varBinder,ir::AstNode * node)187 varbinder::Scope *Rebind(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, ir::AstNode *node)
188 {
189 if (node->IsProgram()) {
190 auto program = node->AsETSModule()->Program();
191 if (program->IsPackage()) {
192 return nullptr;
193 }
194
195 for (auto [_, program_list] : program->ExternalSources()) {
196 for (auto prog : program_list) {
197 ClearHelper(prog);
198 }
199 }
200
201 ClearHelper(program);
202
203 varBinder->CleanUp();
204 for (auto *phase : phaseManager->RebindPhases()) {
205 phase->Apply(varBinder->GetContext(), program);
206 }
207
208 return varBinder->TopScope();
209 }
210
211 auto *scope = NearestScope(node->Parent());
212 auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, scope);
213
214 ClearTypesVariablesAndScopes(node);
215 InitScopesPhaseETS::RunExternalNode(node, varBinder);
216 varBinder->ResolveReferencesForScopeWithContext(node, scope);
217
218 return scope;
219 }
220
221 // Rerun varbinder and checker on the node.
Recheck(PhaseManager * phaseManager,varbinder::ETSBinder * varBinder,checker::ETSChecker * checker,ir::AstNode * node)222 void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checker::ETSChecker *checker,
223 ir::AstNode *node)
224 {
225 RefineSourceRanges(node);
226 if (node->IsProgram()) {
227 auto program = node->AsETSModule()->Program();
228 if (program->IsPackage()) {
229 return;
230 }
231
232 for (auto [_, program_list] : program->ExternalSources()) {
233 for (auto prog : program_list) {
234 ClearHelper(prog);
235 }
236 }
237
238 ClearHelper(program);
239
240 varBinder->CleanUp();
241 varBinder->GetContext()->checker->CleanUp();
242
243 for (auto *phase : phaseManager->RecheckPhases()) {
244 phase->Apply(varBinder->GetContext(), program);
245 }
246 return;
247 }
248
249 auto *scope = Rebind(phaseManager, varBinder, node);
250
251 // NOTE(gogabr: should determine checker status more finely.
252 auto *containingClass = ContainingClass(node);
253 checker::CheckerStatus newStatus =
254 (containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS;
255 if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) {
256 newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK;
257 }
258 auto checkerCtx = checker::SavedCheckerContext(checker, newStatus, containingClass);
259 auto scopeCtx = checker::ScopeContext(checker, scope);
260
261 node->Check(checker);
262 }
263
264 // NOTE: used to get the declaration from identifier in Plugin API and LSP
DeclarationFromIdentifier(const ir::Identifier * node)265 ir::AstNode *DeclarationFromIdentifier(const ir::Identifier *node)
266 {
267 if (node == nullptr) {
268 return nullptr;
269 }
270
271 auto idVar = node->Variable();
272 if (idVar == nullptr) {
273 return nullptr;
274 }
275 auto decl = idVar->Declaration();
276 if (decl == nullptr) {
277 return nullptr;
278 }
279 return decl->Node();
280 }
281
282 // Note: run varbinder and checker on the new node generated in lowering phases (without ClearTypesVariablesAndScopes)
CheckLoweredNode(varbinder::ETSBinder * varBinder,checker::ETSChecker * checker,ir::AstNode * node)283 void CheckLoweredNode(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node)
284 {
285 RefineSourceRanges(node);
286 InitScopesPhaseETS::RunExternalNode(node, varBinder);
287 auto *scope = NearestScope(node);
288 varBinder->ResolveReferencesForScopeWithContext(node, scope);
289
290 checker::CheckerStatus newStatus = checker::CheckerStatus::NO_OPTS;
291 auto *containingClass = util::Helpers::GetContainingClassDefinition(node);
292
293 if (containingClass != nullptr) {
294 if (containingClass->IsAbstract()) {
295 newStatus = checker::CheckerStatus::IN_ABSTRACT;
296 } else {
297 newStatus = checker::CheckerStatus::IN_CLASS;
298 }
299 }
300
301 if ((checker->Context().Status() & checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK) != 0) {
302 newStatus |= checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK;
303 }
304 auto checkerCtx = checker::SavedCheckerContext(
305 checker, newStatus, containingClass != nullptr ? containingClass->TsType()->AsETSObjectType() : nullptr);
306 auto scopeCtx = checker::ScopeContext(checker, scope);
307
308 node->Check(checker);
309 }
310
IsAnonymousClassType(const checker::Type * type)311 bool IsAnonymousClassType(const checker::Type *type)
312 {
313 if (type == nullptr || !type->IsETSObjectType()) {
314 return false;
315 }
316
317 auto declNode = type->AsETSObjectType()->GetDeclNode();
318 return declNode != nullptr && declNode->IsClassDefinition() && declNode->AsClassDefinition()->IsAnonymous();
319 }
320
ClassDefinitionIsEnumTransformed(const ir::AstNode * node)321 bool ClassDefinitionIsEnumTransformed(const ir::AstNode *node)
322 {
323 return node != nullptr && node->IsClassDefinition() && node->AsClassDefinition()->IsEnumTransformed();
324 }
325 } // namespace ark::es2panda::compiler
326