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 
23 namespace ark::es2panda::evaluate {
24 
25 // This scope must be used before running VarBinder or Checker on nodes from another Program.
26 class ProgramScope final {
27 public:
ProgramScope(varbinder::ETSBinder *varBinder, parser::Program *program)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 
62 private:
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.
73 class RecordTableClassScope final {
74 public:
RecordTableClassScope(varbinder::ETSBinder *varBinder, ir::AstNode *recordClass)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 
123 private:
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