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