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