// // 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 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", ""); 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", ""); mNullNodesFailed = true; } for (size_t i = 0; i < childCount; ++i) { if (node->getChildNode(i) == nullptr) { mDiagnostics->error(node->getLine(), "Found nullptr child", ""); 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