1/*
2 * Copyright (c) 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#ifndef ES2PANDA_EVALUATE_VARBINDER_SCOPES_H
17#define ES2PANDA_EVALUATE_VARBINDER_SCOPES_H
18
19#include "parser/program/program.h"
20#include "varbinder/ETSBinder.h"
21#include "varbinder/recordTable.h"
22
23namespace ark::es2panda::evaluate {
24
25// This scope must be used before running VarBinder or Checker on nodes from another Program.
26class ProgramScope final {
27public:
28    explicit ProgramScope(varbinder::ETSBinder *varBinder, parser::Program *program)
29        : varBinder_(varBinder),
30          prevProgram_(varBinder->Program()),
31          prevRecordTable_(varBinder->GetRecordTable()),
32          prevTopScope_(varBinder->TopScope()),
33          prevVarScope_(varBinder->VarScope()),
34          prevScope_(varBinder->GetScope())
35    {
36        ASSERT(varBinder);
37        ASSERT(program);
38
39        varBinder_->SetProgram(program);
40
41        auto &extTables = varBinder_->GetExternalRecordTable();
42        auto iter = extTables.find(program);
43        ASSERT(iter != extTables.end());
44        varBinder_->SetRecordTable(iter->second);
45
46        varBinder_->ResetAllScopes(program->GlobalScope(), program->GlobalScope(), program->GlobalScope());
47    }
48
49    ~ProgramScope() noexcept
50    {
51        varBinder_->SetProgram(prevProgram_);
52        varBinder_->SetRecordTable(prevRecordTable_);
53        varBinder_->ResetAllScopes(prevTopScope_, prevVarScope_, prevScope_);
54    }
55
56    NO_COPY_SEMANTIC(ProgramScope);
57    NO_MOVE_SEMANTIC(ProgramScope);
58
59    void *operator new(size_t) = delete;
60    void *operator new[](size_t) = delete;
61
62private:
63    varbinder::ETSBinder *varBinder_ {nullptr};
64    parser::Program *prevProgram_ {nullptr};
65    varbinder::RecordTable *prevRecordTable_ {nullptr};
66    varbinder::GlobalScope *prevTopScope_ {nullptr};
67    varbinder::VariableScope *prevVarScope_ {nullptr};
68    varbinder::Scope *prevScope_ {nullptr};
69};
70
71// The scope is required for running VarBinder or Checker on nodes from another class,
72// so that entities will be registered with correct names in record table.
73class RecordTableClassScope final {
74public:
75    explicit RecordTableClassScope(varbinder::ETSBinder *varBinder, ir::AstNode *recordClass) : varBinder_(varBinder)
76    {
77        ASSERT(varBinder_);
78
79        auto *recordTable = varBinder_->GetRecordTable();
80        ASSERT(recordTable);
81
82        prevRecordClass_ = recordTable->ClassDefinition();
83        if (prevRecordClass_ == nullptr) {
84            prevRecordClass_ = recordTable->InterfaceDeclaration();
85        }
86
87        if (recordClass != nullptr) {
88            if (recordClass->IsClassDefinition()) {
89                recordTable->SetClassDefinition(recordClass->AsClassDefinition());
90            } else {
91                recordTable->SetInterfaceDeclaration(recordClass->AsTSInterfaceDeclaration());
92            }
93        } else {
94            ir::ClassDefinition *nullDef = nullptr;
95            recordTable->SetClassDefinition(nullDef);
96        }
97    }
98
99    // NOLINTNEXTLINE(bugprone-exception-escape)
100    ~RecordTableClassScope() noexcept
101    {
102        auto *recordTable = varBinder_->GetRecordTable();
103        ASSERT(recordTable != nullptr);
104
105        if (prevRecordClass_ != nullptr) {
106            if (prevRecordClass_->IsClassDefinition()) {
107                recordTable->SetClassDefinition(prevRecordClass_->AsClassDefinition());
108            } else {
109                recordTable->SetInterfaceDeclaration(prevRecordClass_->AsTSInterfaceDeclaration());
110            }
111        } else {
112            ir::ClassDefinition *nullDef = nullptr;
113            recordTable->SetClassDefinition(nullDef);
114        }
115    }
116
117    NO_COPY_SEMANTIC(RecordTableClassScope);
118    NO_MOVE_SEMANTIC(RecordTableClassScope);
119
120    void *operator new(size_t) = delete;
121    void *operator new[](size_t) = delete;
122
123private:
124    varbinder::ETSBinder *varBinder_ {nullptr};
125    ir::AstNode *prevRecordClass_ {nullptr};
126};
127
128}  // namespace ark::es2panda::evaluate
129
130#endif  // ES2PANDA_EVALUATE_VARBINDER_SCOPES_H
131