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 #ifndef ES2PANDA_COMPILER_CORE_ASTVERIFIER_H
17 #define ES2PANDA_COMPILER_CORE_ASTVERIFIER_H
18 
19 #include <algorithm>
20 #include <iterator>
21 #include <regex>
22 #include <string>
23 #include <unordered_set>
24 
25 #include "ast_verifier/checkContext.h"
26 
27 #include "ir/astNode.h"
28 #include "ir/statements/blockStatement.h"
29 #include "lexer/token/sourceLocation.h"
30 #include "parser/program/program.h"
31 #include "util/ustring.h"
32 #include "utils/arena_containers.h"
33 #include "varbinder/variable.h"
34 
35 namespace ark::es2panda::compiler::ast_verifier {
36 
37 using InvariantCheck = std::function<CheckResult(CheckContext &ctx, const ir::AstNode *)>;
38 using Invariants = std::map<std::string, InvariantCheck>;
39 
40 using InvariantNameSet = std::set<std::string>;
41 
42 class VerificationContext final {
43 public:
IntroduceNewInvariants(util::StringView phaseName)44     void IntroduceNewInvariants(util::StringView phaseName)
45     {
46         if (phaseName == "ScopesInitPhase") {
47             accumulatedChecks_.insert("NodeHasParentForAll");
48             accumulatedChecks_.insert("NodeHasSourceRangeForAll");
49             accumulatedChecks_.insert("EveryChildHasValidParentForAll");
50             accumulatedChecks_.insert("EveryChildInParentRangeForAll");
51             accumulatedChecks_.insert("VariableHasScopeForAll");
52         }
53         if (phaseName == "CheckerPhase") {
54             accumulatedChecks_.insert("NodeHasTypeForAll");
55             accumulatedChecks_.insert("IdentifierHasVariableForAll");
56             accumulatedChecks_.insert("ReferenceTypeAnnotationIsNullForAll");
57             accumulatedChecks_.insert("ArithmeticOperationValidForAll");
58             accumulatedChecks_.insert("SequenceExpressionHasLastTypeForAll");
59             accumulatedChecks_.insert("CheckInfiniteLoopForAll");
60             accumulatedChecks_.insert("ForLoopCorrectlyInitializedForAll");
61             accumulatedChecks_.insert("VariableHasEnclosingScopeForAll");
62             accumulatedChecks_.insert("ModifierAccessValidForAll");
63             accumulatedChecks_.insert("ImportExportAccessValid");
64             accumulatedChecks_.insert("VariableNameIdentifierNameSameForAll");
65             accumulatedChecks_.insert("CheckAbstractMethodForAll");
66             accumulatedChecks_.insert("GetterSetterValidationForAll");
67             accumulatedChecks_.insert("CheckScopeDeclarationForAll");
68         }
69     }
70 
AccumulatedChecks() const71     const InvariantNameSet &AccumulatedChecks() const
72     {
73         return accumulatedChecks_;
74     }
75 
76 private:
77     InvariantNameSet accumulatedChecks_ {};
78 };
79 
80 /*
81  * ASTVerifier used for checking various invariants that should hold during AST transformation in lowerings
82  * For all available checks lookup the constructor
83  */
84 class ASTVerifier final {
85 public:
86     NO_COPY_SEMANTIC(ASTVerifier);
87     NO_MOVE_SEMANTIC(ASTVerifier);
88 
89     explicit ASTVerifier(ArenaAllocator *allocator);
90     ~ASTVerifier() = default;
91 
92     /**
93      * @brief Run all existing invariants on some ast node (and consequently it's children)
94      * @param ast AstNode which will be analyzed
95      * @return Messages report of analysis
96      */
97     Messages VerifyFull(const ir::AstNode *ast);
98 
99     /**
100      * @brief Run some particular invariants on some ast node
101      * @note invariants must be supplied as strings to invariant_set, additionally invariant
102      * name can be suffixed by `ForAll` string to include recursive analysis of provided node
103      * I.e. 'HasParent' invariant can be named 'HasParentRecursive' to traverse all child nodes as well
104      * @param ast AstNode which will be analyzed
105      * @param invariantSet Set of invariants to check
106      * @return Messages report of analysis
107      */
108     Messages Verify(const ir::AstNode *ast, const InvariantNameSet &invariantSet);
109 
110 private:
111     static constexpr const char *RECURSIVE_SUFFIX = "ForAll";
112 
RecursiveInvariant(const InvariantCheck &func)113     static InvariantCheck RecursiveInvariant(const InvariantCheck &func)
114     {
115         return [func](CheckContext &ctx, const ir::AstNode *ast) -> CheckResult {
116             std::function<void(const ir::AstNode *)> aux;
117             auto finalDecision = CheckDecision::CORRECT;
118             aux = [&ctx, func, &aux, &finalDecision](const ir::AstNode *child) -> void {
119                 const auto [decision, action] = func(ctx, child);
120                 if (decision == CheckDecision::INCORRECT) {
121                     finalDecision = CheckDecision::INCORRECT;
122                 }
123                 if (action == CheckAction::SKIP_SUBTREE) {
124                     return;
125                 }
126                 child->Iterate(aux);
127             };
128             aux(ast);
129             return {finalDecision, CheckAction::CONTINUE};
130         };
131     }
132 
133     template <typename T>
AddInvariant(ArenaAllocator *allocator, const std::string &name)134     void AddInvariant(ArenaAllocator *allocator, const std::string &name)
135     {
136         auto check = *allocator->New<T>(*allocator);
137         invariantsChecks_[name] = check;
138         invariantsNames_.insert(name);
139         invariantsChecks_[name + RECURSIVE_SUFFIX] = RecursiveInvariant(check);
140         invariantsNames_.insert(name + RECURSIVE_SUFFIX);
141     }
142 
143     Invariants invariantsChecks_;
144     InvariantNameSet invariantsNames_;
145 };
146 
147 }  // namespace ark::es2panda::compiler::ast_verifier
148 
149 #endif  // ES2PANDA_COMPILER_CORE_ASTVERIFIER_H
150