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 #include "evaluate/helpers.h"
17 #include "evaluate/irCheckHelper.h"
18 #include "compiler/lowering/util.h"
19 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
20 #include "checker/ETSchecker.h"
21 
22 namespace ark::es2panda::evaluate {
23 
IrCheckHelper(checker::ETSChecker *checker, varbinder::ETSBinder *varBinder)24 IrCheckHelper::IrCheckHelper(checker::ETSChecker *checker, varbinder::ETSBinder *varBinder)
25     : checker_(checker), varBinder_(varBinder), recursiveDecls_(checker->Allocator()->Adapter())
26 {
27     ASSERT(checker_);
28     ASSERT(varBinder_);
29 }
30 
CheckNewNode(ir::AstNode *node, varbinder::Scope *scope, ir::AstNode *parentClass, parser::Program *program)31 bool IrCheckHelper::CheckNewNode(ir::AstNode *node, varbinder::Scope *scope, ir::AstNode *parentClass,
32                                  parser::Program *program)
33 {
34     ASSERT(node);
35 
36     if (program == nullptr) {
37         program = varBinder_->Program();
38     }
39     if (scope == nullptr) {
40         scope = checker_->Scope();
41     }
42 
43     recursiveDecls_.emplace_back(program, scope, parentClass, node);
44 
45     if (isRecursive_) {
46         return false;
47     }
48     isRecursive_ = true;
49 
50     if (isPrecheckPassed_) {
51         HandleCustomNodes();
52         CheckDecls();
53     }
54 
55     isRecursive_ = false;
56 
57     return true;
58 }
59 
PreCheck()60 void IrCheckHelper::PreCheck()
61 {
62     HandleCustomNodes();
63     CheckDecls();
64 
65     isPrecheckPassed_ = true;
66 }
67 
CheckDecls()68 void IrCheckHelper::CheckDecls()
69 {
70     // All dependent user-classes must be created at this point, so we can run checker.
71     while (!recursiveDecls_.empty()) {
72         auto [program, scope, parent, node] = recursiveDecls_.front();
73         recursiveDecls_.pop_front();
74         helpers::DoScopedAction(checker_, varBinder_, program, scope, parent, [this, node = node, scope = scope]() {
75             varBinder_->ResolveReferencesForScope(node, scope);
76             node->Check(checker_);
77         });
78     }
79 }
80 
HandleCustomNodes()81 void IrCheckHelper::HandleCustomNodes()
82 {
83     auto iter = recursiveDecls_.begin();
84     while (iter != recursiveDecls_.end()) {
85         // Can trigger `ETSBinder::BuildClassDefinition`,
86         // which can eventually call debug-info plugin to create another class.
87         // Hence we delay `ETSChecker::Check` until all required classes are built and initialized in varbinder.
88         auto [program, scope, parent, node] = *iter;
89         helpers::DoScopedAction(checker_, varBinder_, program, scope, parent,
90                                 [varBinder = varBinder_, node = node]() { varBinder->HandleCustomNodes(node); });
91         ++iter;
92     }
93 }
94 
CheckGlobalEntity(parser::Program *program, ir::AstNode *node, bool mustCheck)95 void IrCheckHelper::CheckGlobalEntity(parser::Program *program, ir::AstNode *node, bool mustCheck)
96 {
97     ASSERT(program);
98 
99     auto *globalClass = program->GlobalClass();
100     auto *globalClassScope = program->GlobalClassScope();
101 
102     helpers::DoScopedAction(checker_, varBinder_, program, globalClassScope, globalClass,
103                             [this, globalClassScope, node]() {
104                                 compiler::InitScopesPhaseETS::RunExternalNode(node, varBinder_);
105                                 varBinder_->ResolveReferencesForScope(node, globalClassScope);
106                             });
107     if (mustCheck) {
108         CheckNewNode(node, globalClassScope, globalClass, program);
109     }
110 }
111 
CheckLocalEntity(ir::AstNode *node)112 void IrCheckHelper::CheckLocalEntity(ir::AstNode *node)
113 {
114     compiler::InitScopesPhaseETS::RunExternalNode(node, varBinder_);
115     CheckNewNode(node, nullptr, nullptr, nullptr);
116 }
117 
118 }  // namespace ark::es2panda::evaluate
119