1 | // |
2 | // Copyright 2019 The ANGLE Project Authors. All rights reserved. |
3 | // Use of this source code is governed by a BSD-style license that can be |
4 | // found in the LICENSE file. |
5 | // |
6 | |
7 | #include "compiler/translator/ValidateAST.h" |
8 | |
9 | #include "compiler/translator/Diagnostics.h" |
10 | #include "compiler/translator/tree_util/IntermTraverse.h" |
11 | |
12 | namespace sh |
13 | { |
14 | |
15 | namespace |
16 | { |
17 | |
18 | class ValidateAST : public TIntermTraverser |
19 | { |
20 | public: |
21 | static bool validate(TIntermNode *root, |
22 | TDiagnostics *diagnostics, |
23 | const ValidateASTOptions &options); |
24 | |
25 | void visitSymbol(TIntermSymbol *node) override; |
26 | void visitConstantUnion(TIntermConstantUnion *node) override; |
27 | bool visitSwizzle(Visit visit, TIntermSwizzle *node) override; |
28 | bool visitBinary(Visit visit, TIntermBinary *node) override; |
29 | bool visitUnary(Visit visit, TIntermUnary *node) override; |
30 | bool visitTernary(Visit visit, TIntermTernary *node) override; |
31 | bool visitIfElse(Visit visit, TIntermIfElse *node) override; |
32 | bool visitSwitch(Visit visit, TIntermSwitch *node) override; |
33 | bool visitCase(Visit visit, TIntermCase *node) override; |
34 | void visitFunctionPrototype(TIntermFunctionPrototype *node) override; |
35 | bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; |
36 | bool visitAggregate(Visit visit, TIntermAggregate *node) override; |
37 | bool visitBlock(Visit visit, TIntermBlock *node) override; |
38 | bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override; |
39 | bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; |
40 | bool visitLoop(Visit visit, TIntermLoop *node) override; |
41 | bool visitBranch(Visit visit, TIntermBranch *node) override; |
42 | void visitPreprocessorDirective(TIntermPreprocessorDirective *node) override; |
43 | |
44 | private: |
45 | ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options); |
46 | |
47 | // Visit as a generic node |
48 | void visitNode(Visit visit, TIntermNode *node); |
49 | |
50 | void expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count); |
51 | |
52 | bool validateInternal(); |
53 | |
54 | ValidateASTOptions mOptions; |
55 | TDiagnostics *mDiagnostics; |
56 | |
57 | // For validateSingleParent: |
58 | std::map<TIntermNode *, TIntermNode *> mParent; |
59 | bool mSingleParentFailed = false; |
60 | |
61 | // For validateNullNodes |
62 | bool mNullNodesFailed = false; |
63 | }; |
64 | |
65 | bool ValidateAST::validate(TIntermNode *root, |
66 | TDiagnostics *diagnostics, |
67 | const ValidateASTOptions &options) |
68 | { |
69 | ValidateAST validate(root, diagnostics, options); |
70 | root->traverse(&validate); |
71 | return validate.validateInternal(); |
72 | } |
73 | |
74 | ValidateAST::ValidateAST(TIntermNode *root, |
75 | TDiagnostics *diagnostics, |
76 | const ValidateASTOptions &options) |
77 | : TIntermTraverser(true, false, true, nullptr), mOptions(options), mDiagnostics(diagnostics) |
78 | { |
79 | if (mOptions.validateSingleParent) |
80 | { |
81 | mParent[root] = nullptr; |
82 | } |
83 | } |
84 | |
85 | void ValidateAST::visitNode(Visit visit, TIntermNode *node) |
86 | { |
87 | if (visit == PreVisit && mOptions.validateSingleParent) |
88 | { |
89 | size_t childCount = node->getChildCount(); |
90 | for (size_t i = 0; i < childCount; ++i) |
91 | { |
92 | TIntermNode *child = node->getChildNode(i); |
93 | if (mParent.find(child) != mParent.end()) |
94 | { |
95 | // If child is visited twice but through the same parent, the problem is in one of |
96 | // the ancestors. |
97 | if (mParent[child] != node) |
98 | { |
99 | mDiagnostics->error(node->getLine(), "Found child with two parents" , |
100 | "<validateSingleParent>" ); |
101 | mSingleParentFailed = true; |
102 | } |
103 | } |
104 | |
105 | mParent[child] = node; |
106 | } |
107 | } |
108 | } |
109 | |
110 | void ValidateAST::expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count) |
111 | { |
112 | if (visit == PreVisit && mOptions.validateNullNodes) |
113 | { |
114 | size_t childCount = node->getChildCount(); |
115 | if (childCount < least_count) |
116 | { |
117 | mDiagnostics->error(node->getLine(), "Too few children" , "<validateNullNodes>" ); |
118 | mNullNodesFailed = true; |
119 | } |
120 | |
121 | for (size_t i = 0; i < childCount; ++i) |
122 | { |
123 | if (node->getChildNode(i) == nullptr) |
124 | { |
125 | mDiagnostics->error(node->getLine(), "Found nullptr child" , "<validateNullNodes>" ); |
126 | mNullNodesFailed = true; |
127 | } |
128 | } |
129 | } |
130 | } |
131 | |
132 | void ValidateAST::visitSymbol(TIntermSymbol *node) |
133 | { |
134 | visitNode(PreVisit, node); |
135 | } |
136 | |
137 | void ValidateAST::visitConstantUnion(TIntermConstantUnion *node) |
138 | { |
139 | visitNode(PreVisit, node); |
140 | } |
141 | |
142 | bool ValidateAST::visitSwizzle(Visit visit, TIntermSwizzle *node) |
143 | { |
144 | visitNode(visit, node); |
145 | return true; |
146 | } |
147 | |
148 | bool ValidateAST::visitBinary(Visit visit, TIntermBinary *node) |
149 | { |
150 | visitNode(visit, node); |
151 | return true; |
152 | } |
153 | |
154 | bool ValidateAST::visitUnary(Visit visit, TIntermUnary *node) |
155 | { |
156 | visitNode(visit, node); |
157 | return true; |
158 | } |
159 | |
160 | bool ValidateAST::visitTernary(Visit visit, TIntermTernary *node) |
161 | { |
162 | visitNode(visit, node); |
163 | return true; |
164 | } |
165 | |
166 | bool ValidateAST::visitIfElse(Visit visit, TIntermIfElse *node) |
167 | { |
168 | visitNode(visit, node); |
169 | return true; |
170 | } |
171 | |
172 | bool ValidateAST::visitSwitch(Visit visit, TIntermSwitch *node) |
173 | { |
174 | visitNode(visit, node); |
175 | return true; |
176 | } |
177 | |
178 | bool ValidateAST::visitCase(Visit visit, TIntermCase *node) |
179 | { |
180 | visitNode(visit, node); |
181 | return true; |
182 | } |
183 | |
184 | void ValidateAST::visitFunctionPrototype(TIntermFunctionPrototype *node) |
185 | { |
186 | visitNode(PreVisit, node); |
187 | } |
188 | |
189 | bool ValidateAST::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) |
190 | { |
191 | visitNode(visit, node); |
192 | return true; |
193 | } |
194 | |
195 | bool ValidateAST::visitAggregate(Visit visit, TIntermAggregate *node) |
196 | { |
197 | visitNode(visit, node); |
198 | expectNonNullChildren(visit, node, 0); |
199 | return true; |
200 | } |
201 | |
202 | bool ValidateAST::visitBlock(Visit visit, TIntermBlock *node) |
203 | { |
204 | visitNode(visit, node); |
205 | expectNonNullChildren(visit, node, 0); |
206 | return true; |
207 | } |
208 | |
209 | bool ValidateAST::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) |
210 | { |
211 | visitNode(visit, node); |
212 | return true; |
213 | } |
214 | |
215 | bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node) |
216 | { |
217 | visitNode(visit, node); |
218 | expectNonNullChildren(visit, node, 0); |
219 | return true; |
220 | } |
221 | |
222 | bool ValidateAST::visitLoop(Visit visit, TIntermLoop *node) |
223 | { |
224 | visitNode(visit, node); |
225 | return true; |
226 | } |
227 | |
228 | bool ValidateAST::visitBranch(Visit visit, TIntermBranch *node) |
229 | { |
230 | visitNode(visit, node); |
231 | return true; |
232 | } |
233 | |
234 | void ValidateAST::visitPreprocessorDirective(TIntermPreprocessorDirective *node) |
235 | { |
236 | visitNode(PreVisit, node); |
237 | } |
238 | |
239 | bool ValidateAST::validateInternal() |
240 | { |
241 | return !mSingleParentFailed && !mNullNodesFailed; |
242 | } |
243 | |
244 | } // anonymous namespace |
245 | |
246 | bool ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options) |
247 | { |
248 | return ValidateAST::validate(root, diagnostics, options); |
249 | } |
250 | |
251 | } // namespace sh |
252 | |