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 "util.h" 17 18#include "compiler/lowering/scopesInit/scopesInitPhase.h" 19#include "ir/expressions/identifier.h" 20 21namespace ark::es2panda::compiler { 22 23varbinder::Scope *NearestScope(const ir::AstNode *ast) 24{ 25 while (ast != nullptr && !ast->IsScopeBearer()) { 26 ast = ast->Parent(); 27 } 28 29 return ast == nullptr ? nullptr : ast->Scope(); 30} 31 32checker::ETSObjectType const *ContainingClass(const ir::AstNode *ast) 33{ 34 while (ast != nullptr && !ast->IsClassDefinition()) { 35 ast = ast->Parent(); 36 } 37 38 return ast == nullptr ? nullptr : ast->AsClassDefinition()->TsType()->AsETSObjectType(); 39} 40 41ir::Identifier *Gensym(ArenaAllocator *const allocator) 42{ 43 util::UString const s = GenName(allocator); 44 return allocator->New<ir::Identifier>(s.View(), allocator); 45} 46 47util::UString GenName(ArenaAllocator *const allocator) 48{ 49 static std::string const GENSYM_CORE = "gensym$_"; 50 static std::size_t gensymCounter = 0U; 51 52 return util::UString {GENSYM_CORE + std::to_string(++gensymCounter), allocator}; 53} 54 55// Function to clear expression node types and identifier node variables (for correct re-binding and re-checking) 56void ClearTypesVariablesAndScopes(ir::AstNode *node) noexcept 57{ 58 auto doNode = [](ir::AstNode *nn) { 59 if (nn->IsScopeBearer()) { 60 nn->ClearScope(); 61 } 62 if (nn->IsTyped() && !(nn->IsExpression() && nn->AsExpression()->IsTypeNode())) { 63 nn->AsTyped()->SetTsType(nullptr); 64 } 65 if (nn->IsIdentifier()) { 66 nn->AsIdentifier()->SetVariable(nullptr); 67 } 68 }; 69 70 doNode(node); 71 node->Iterate([doNode](ir::AstNode *child) -> void { 72 doNode(child); 73 ClearTypesVariablesAndScopes(child); 74 }); 75} 76 77ArenaSet<varbinder::Variable *> FindCaptured(ArenaAllocator *allocator, ir::AstNode *scopeBearer) noexcept 78{ 79 auto result = ArenaSet<varbinder::Variable *> {allocator->Adapter()}; 80 auto scopes = ArenaSet<varbinder::Scope *> {allocator->Adapter()}; 81 scopeBearer->IterateRecursivelyPreorder([&result, &scopes](ir::AstNode *ast) { 82 if (ast->IsScopeBearer() && ast->Scope() != nullptr) { 83 scopes.insert(ast->Scope()); 84 if (ast->Scope()->IsFunctionScope()) { 85 scopes.insert(ast->Scope()->AsFunctionScope()->ParamScope()); 86 } else if (ast->IsForUpdateStatement() || ast->IsForInStatement() || ast->IsForOfStatement() || 87 ast->IsCatchClause()) { 88 // NOTE(gogabr) LoopScope _does not_ currently respond to IsLoopScope(). 89 // For now, this is the way to reach LoopDeclarationScope. 90 scopes.insert(ast->Scope()->Parent()); 91 } 92 } 93 if (ast->IsIdentifier()) { 94 auto *var = ast->AsIdentifier()->Variable(); 95 if (var == nullptr || !var->HasFlag(varbinder::VariableFlags::LOCAL)) { 96 return; 97 } 98 auto *sc = var->GetScope(); 99 if (sc != nullptr && !sc->IsClassScope() && !sc->IsGlobalScope() && scopes.count(var->GetScope()) == 0) { 100 result.insert(var); 101 } 102 } 103 }); 104 return result; 105} 106 107// Rerun varbinder and checker on the node. 108void Recheck(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node) 109{ 110 auto *scope = NearestScope(node); 111 auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, scope); 112 113 ClearTypesVariablesAndScopes(node); 114 InitScopesPhaseETS::RunExternalNode(node, varBinder); 115 varBinder->ResolveReferencesForScopeWithContext(node, scope); 116 117 auto *containingClass = ContainingClass(node); 118 // NOTE(gogabr: should determine checker status more finely. 119 auto checkerCtx = checker::SavedCheckerContext( 120 checker, (containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS, 121 containingClass); 122 auto scopeCtx = checker::ScopeContext(checker, scope); 123 124 node->Check(checker); 125} 126 127} // namespace ark::es2panda::compiler 128