/** * Copyright (c) 2023-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "util.h" #include "compiler/lowering/scopesInit/scopesInitPhase.h" #include "ir/expressions/identifier.h" namespace ark::es2panda::compiler { varbinder::Scope *NearestScope(const ir::AstNode *ast) { while (ast != nullptr && !ast->IsScopeBearer()) { ast = ast->Parent(); } return ast == nullptr ? nullptr : ast->Scope(); } checker::ETSObjectType const *ContainingClass(const ir::AstNode *ast) { while (ast != nullptr && !ast->IsClassDefinition()) { ast = ast->Parent(); } return ast == nullptr ? nullptr : ast->AsClassDefinition()->TsType()->AsETSObjectType(); } ir::Identifier *Gensym(ArenaAllocator *const allocator) { util::UString const s = GenName(allocator); return allocator->New(s.View(), allocator); } util::UString GenName(ArenaAllocator *const allocator) { static std::string const GENSYM_CORE = "gensym$_"; static std::size_t gensymCounter = 0U; return util::UString {GENSYM_CORE + std::to_string(++gensymCounter), allocator}; } // Function to clear expression node types and identifier node variables (for correct re-binding and re-checking) void ClearTypesVariablesAndScopes(ir::AstNode *node) noexcept { auto doNode = [](ir::AstNode *nn) { if (nn->IsScopeBearer()) { nn->ClearScope(); } if (nn->IsTyped() && !(nn->IsExpression() && nn->AsExpression()->IsTypeNode())) { nn->AsTyped()->SetTsType(nullptr); } if (nn->IsIdentifier()) { nn->AsIdentifier()->SetVariable(nullptr); } }; doNode(node); node->Iterate([doNode](ir::AstNode *child) -> void { doNode(child); ClearTypesVariablesAndScopes(child); }); } ArenaSet FindCaptured(ArenaAllocator *allocator, ir::AstNode *scopeBearer) noexcept { auto result = ArenaSet {allocator->Adapter()}; auto scopes = ArenaSet {allocator->Adapter()}; scopeBearer->IterateRecursivelyPreorder([&result, &scopes](ir::AstNode *ast) { if (ast->IsScopeBearer() && ast->Scope() != nullptr) { scopes.insert(ast->Scope()); if (ast->Scope()->IsFunctionScope()) { scopes.insert(ast->Scope()->AsFunctionScope()->ParamScope()); } else if (ast->IsForUpdateStatement() || ast->IsForInStatement() || ast->IsForOfStatement() || ast->IsCatchClause()) { // NOTE(gogabr) LoopScope _does not_ currently respond to IsLoopScope(). // For now, this is the way to reach LoopDeclarationScope. scopes.insert(ast->Scope()->Parent()); } } if (ast->IsIdentifier()) { auto *var = ast->AsIdentifier()->Variable(); if (var == nullptr || !var->HasFlag(varbinder::VariableFlags::LOCAL)) { return; } auto *sc = var->GetScope(); if (sc != nullptr && !sc->IsClassScope() && !sc->IsGlobalScope() && scopes.count(var->GetScope()) == 0) { result.insert(var); } } }); return result; } // Rerun varbinder and checker on the node. void Recheck(varbinder::ETSBinder *varBinder, checker::ETSChecker *checker, ir::AstNode *node) { auto *scope = NearestScope(node); auto bscope = varbinder::LexicalScope::Enter(varBinder, scope); ClearTypesVariablesAndScopes(node); InitScopesPhaseETS::RunExternalNode(node, varBinder); varBinder->ResolveReferencesForScopeWithContext(node, scope); auto *containingClass = ContainingClass(node); // NOTE(gogabr: should determine checker status more finely. auto checkerCtx = checker::SavedCheckerContext( checker, (containingClass == nullptr) ? checker::CheckerStatus::NO_OPTS : checker::CheckerStatus::IN_CLASS, containingClass); auto scopeCtx = checker::ScopeContext(checker, scope); node->Check(checker); } } // namespace ark::es2panda::compiler