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