| // | |
| // Copyright 2019 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| #include "compiler/translator/ValidateAST.h" | |
| #include "compiler/translator/Diagnostics.h" | |
| #include "compiler/translator/tree_util/IntermTraverse.h" | |
| namespace sh | |
| { | |
| namespace | |
| { | |
| class ValidateAST : public TIntermTraverser | |
| { | |
| public: | |
| static bool validate(TIntermNode *root, | |
| TDiagnostics *diagnostics, | |
| const ValidateASTOptions &options); | |
| void visitSymbol(TIntermSymbol *node) override; | |
| void visitConstantUnion(TIntermConstantUnion *node) override; | |
| bool visitSwizzle(Visit visit, TIntermSwizzle *node) override; | |
| bool visitBinary(Visit visit, TIntermBinary *node) override; | |
| bool visitUnary(Visit visit, TIntermUnary *node) override; | |
| bool visitTernary(Visit visit, TIntermTernary *node) override; | |
| bool visitIfElse(Visit visit, TIntermIfElse *node) override; | |
| bool visitSwitch(Visit visit, TIntermSwitch *node) override; | |
| bool visitCase(Visit visit, TIntermCase *node) override; | |
| void visitFunctionPrototype(TIntermFunctionPrototype *node) override; | |
| bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; | |
| bool visitAggregate(Visit visit, TIntermAggregate *node) override; | |
| bool visitBlock(Visit visit, TIntermBlock *node) override; | |
| bool visitGlobalQualifierDeclaration(Visit visit, | |
| TIntermGlobalQualifierDeclaration *node) override; | |
| bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; | |
| bool visitLoop(Visit visit, TIntermLoop *node) override; | |
| bool visitBranch(Visit visit, TIntermBranch *node) override; | |
| void visitPreprocessorDirective(TIntermPreprocessorDirective *node) override; | |
| private: | |
| ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options); | |
| // Visit as a generic node | |
| void visitNode(Visit visit, TIntermNode *node); | |
| void expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count); | |
| bool validateInternal(); | |
| ValidateASTOptions mOptions; | |
| TDiagnostics *mDiagnostics; | |
| // For validateSingleParent: | |
| std::map<TIntermNode *, TIntermNode *> mParent; | |
| bool mSingleParentFailed = false; | |
| // For validateNullNodes | |
| bool mNullNodesFailed = false; | |
| }; | |
| bool ValidateAST::validate(TIntermNode *root, | |
| TDiagnostics *diagnostics, | |
| const ValidateASTOptions &options) | |
| { | |
| ValidateAST validate(root, diagnostics, options); | |
| root->traverse(&validate); | |
| return validate.validateInternal(); | |
| } | |
| ValidateAST::ValidateAST(TIntermNode *root, | |
| TDiagnostics *diagnostics, | |
| const ValidateASTOptions &options) | |
| : TIntermTraverser(true, false, true, nullptr), mOptions(options), mDiagnostics(diagnostics) | |
| { | |
| if (mOptions.validateSingleParent) | |
| { | |
| mParent[root] = nullptr; | |
| } | |
| } | |
| void ValidateAST::visitNode(Visit visit, TIntermNode *node) | |
| { | |
| if (visit == PreVisit && mOptions.validateSingleParent) | |
| { | |
| size_t childCount = node->getChildCount(); | |
| for (size_t i = 0; i < childCount; ++i) | |
| { | |
| TIntermNode *child = node->getChildNode(i); | |
| if (mParent.find(child) != mParent.end()) | |
| { | |
| // If child is visited twice but through the same parent, the problem is in one of | |
| // the ancestors. | |
| if (mParent[child] != node) | |
| { | |
| mDiagnostics->error(node->getLine(), "Found child with two parents", | |
| "<validateSingleParent>"); | |
| mSingleParentFailed = true; | |
| } | |
| } | |
| mParent[child] = node; | |
| } | |
| } | |
| } | |
| void ValidateAST::expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count) | |
| { | |
| if (visit == PreVisit && mOptions.validateNullNodes) | |
| { | |
| size_t childCount = node->getChildCount(); | |
| if (childCount < least_count) | |
| { | |
| mDiagnostics->error(node->getLine(), "Too few children", "<validateNullNodes>"); | |
| mNullNodesFailed = true; | |
| } | |
| for (size_t i = 0; i < childCount; ++i) | |
| { | |
| if (node->getChildNode(i) == nullptr) | |
| { | |
| mDiagnostics->error(node->getLine(), "Found nullptr child", "<validateNullNodes>"); | |
| mNullNodesFailed = true; | |
| } | |
| } | |
| } | |
| } | |
| void ValidateAST::visitSymbol(TIntermSymbol *node) | |
| { | |
| visitNode(PreVisit, node); | |
| } | |
| void ValidateAST::visitConstantUnion(TIntermConstantUnion *node) | |
| { | |
| visitNode(PreVisit, node); | |
| } | |
| bool ValidateAST::visitSwizzle(Visit visit, TIntermSwizzle *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitBinary(Visit visit, TIntermBinary *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitUnary(Visit visit, TIntermUnary *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitTernary(Visit visit, TIntermTernary *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitIfElse(Visit visit, TIntermIfElse *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitSwitch(Visit visit, TIntermSwitch *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitCase(Visit visit, TIntermCase *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| void ValidateAST::visitFunctionPrototype(TIntermFunctionPrototype *node) | |
| { | |
| visitNode(PreVisit, node); | |
| } | |
| bool ValidateAST::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitAggregate(Visit visit, TIntermAggregate *node) | |
| { | |
| visitNode(visit, node); | |
| expectNonNullChildren(visit, node, 0); | |
| return true; | |
| } | |
| bool ValidateAST::visitBlock(Visit visit, TIntermBlock *node) | |
| { | |
| visitNode(visit, node); | |
| expectNonNullChildren(visit, node, 0); | |
| return true; | |
| } | |
| bool ValidateAST::visitGlobalQualifierDeclaration(Visit visit, | |
| TIntermGlobalQualifierDeclaration *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node) | |
| { | |
| visitNode(visit, node); | |
| expectNonNullChildren(visit, node, 0); | |
| return true; | |
| } | |
| bool ValidateAST::visitLoop(Visit visit, TIntermLoop *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| bool ValidateAST::visitBranch(Visit visit, TIntermBranch *node) | |
| { | |
| visitNode(visit, node); | |
| return true; | |
| } | |
| void ValidateAST::visitPreprocessorDirective(TIntermPreprocessorDirective *node) | |
| { | |
| visitNode(PreVisit, node); | |
| } | |
| bool ValidateAST::validateInternal() | |
| { | |
| return !mSingleParentFailed && !mNullNodesFailed; | |
| } | |
| } // anonymous namespace | |
| bool ValidateAST(TIntermNode *root, TDiagnostics *diagnostics, const ValidateASTOptions &options) | |
| { | |
| return ValidateAST::validate(root, diagnostics, options); | |
| } | |
| } // namespace sh |