diff options
Diffstat (limited to 'gfx/skia/skia/src/sksl/analysis/SkSLGetReturnComplexity.cpp')
-rw-r--r-- | gfx/skia/skia/src/sksl/analysis/SkSLGetReturnComplexity.cpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/sksl/analysis/SkSLGetReturnComplexity.cpp b/gfx/skia/skia/src/sksl/analysis/SkSLGetReturnComplexity.cpp new file mode 100644 index 0000000000..a5b6e1132f --- /dev/null +++ b/gfx/skia/skia/src/sksl/analysis/SkSLGetReturnComplexity.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2023 Google LLC + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/private/SkSLDefines.h" +#include "include/private/SkSLIRNode.h" +#include "include/private/SkSLStatement.h" +#include "src/sksl/SkSLAnalysis.h" +#include "src/sksl/analysis/SkSLProgramVisitor.h" +#include "src/sksl/ir/SkSLBlock.h" +#include "src/sksl/ir/SkSLFunctionDefinition.h" + +#include <algorithm> +#include <memory> + +namespace SkSL { + +class Expression; + +static int count_returns_at_end_of_control_flow(const FunctionDefinition& funcDef) { + class CountReturnsAtEndOfControlFlow : public ProgramVisitor { + public: + CountReturnsAtEndOfControlFlow(const FunctionDefinition& funcDef) { + this->visitProgramElement(funcDef); + } + + bool visitExpression(const Expression& expr) override { + // Do not recurse into expressions. + return false; + } + + bool visitStatement(const Statement& stmt) override { + switch (stmt.kind()) { + case Statement::Kind::kBlock: { + // Check only the last statement of a block. + const auto& block = stmt.as<Block>(); + return block.children().size() && + this->visitStatement(*block.children().back()); + } + case Statement::Kind::kSwitch: + case Statement::Kind::kDo: + case Statement::Kind::kFor: + // Don't introspect switches or loop structures at all. + return false; + + case Statement::Kind::kReturn: + ++fNumReturns; + [[fallthrough]]; + + default: + return INHERITED::visitStatement(stmt); + } + } + + int fNumReturns = 0; + using INHERITED = ProgramVisitor; + }; + + return CountReturnsAtEndOfControlFlow{funcDef}.fNumReturns; +} + +class CountReturnsWithLimit : public ProgramVisitor { +public: + CountReturnsWithLimit(const FunctionDefinition& funcDef, int limit) : fLimit(limit) { + this->visitProgramElement(funcDef); + } + + bool visitExpression(const Expression& expr) override { + // Do not recurse into expressions. + return false; + } + + bool visitStatement(const Statement& stmt) override { + switch (stmt.kind()) { + case Statement::Kind::kReturn: { + ++fNumReturns; + fDeepestReturn = std::max(fDeepestReturn, fScopedBlockDepth); + return (fNumReturns >= fLimit) || INHERITED::visitStatement(stmt); + } + case Statement::Kind::kVarDeclaration: { + if (fScopedBlockDepth > 1) { + fVariablesInBlocks = true; + } + return INHERITED::visitStatement(stmt); + } + case Statement::Kind::kBlock: { + int depthIncrement = stmt.as<Block>().isScope() ? 1 : 0; + fScopedBlockDepth += depthIncrement; + bool result = INHERITED::visitStatement(stmt); + fScopedBlockDepth -= depthIncrement; + if (fNumReturns == 0 && fScopedBlockDepth <= 1) { + // If closing this block puts us back at the top level, and we haven't + // encountered any return statements yet, any vardecls we may have encountered + // up until this point can be ignored. They are out of scope now, and they were + // never used in a return statement. + fVariablesInBlocks = false; + } + return result; + } + default: + return INHERITED::visitStatement(stmt); + } + } + + int fNumReturns = 0; + int fDeepestReturn = 0; + int fLimit = 0; + int fScopedBlockDepth = 0; + bool fVariablesInBlocks = false; + using INHERITED = ProgramVisitor; +}; + +Analysis::ReturnComplexity Analysis::GetReturnComplexity(const FunctionDefinition& funcDef) { + int returnsAtEndOfControlFlow = count_returns_at_end_of_control_flow(funcDef); + CountReturnsWithLimit counter{funcDef, returnsAtEndOfControlFlow + 1}; + if (counter.fNumReturns > returnsAtEndOfControlFlow) { + return ReturnComplexity::kEarlyReturns; + } + if (counter.fNumReturns > 1) { + return ReturnComplexity::kScopedReturns; + } + if (counter.fVariablesInBlocks && counter.fDeepestReturn > 1) { + return ReturnComplexity::kScopedReturns; + } + return ReturnComplexity::kSingleSafeReturn; +} + +} // namespace SkSL |