1 /* 2 * Copyright 2023 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "src/sksl/SkSLAnalysis.h" 9 #include "src/sksl/SkSLDefines.h" 10 #include "src/sksl/analysis/SkSLProgramVisitor.h" 11 #include "src/sksl/ir/SkSLBlock.h" 12 #include "src/sksl/ir/SkSLFunctionDefinition.h" 13 #include "src/sksl/ir/SkSLIRNode.h" 14 #include "src/sksl/ir/SkSLStatement.h" 15 16 #include <algorithm> 17 #include <memory> 18 19 namespace SkSL { 20 21 class Expression; 22 count_returns_at_end_of_control_flow(const FunctionDefinition & funcDef)23static int count_returns_at_end_of_control_flow(const FunctionDefinition& funcDef) { 24 class CountReturnsAtEndOfControlFlow : public ProgramVisitor { 25 public: 26 CountReturnsAtEndOfControlFlow(const FunctionDefinition& funcDef) { 27 this->visitProgramElement(funcDef); 28 } 29 30 bool visitExpression(const Expression& expr) override { 31 // Do not recurse into expressions. 32 return false; 33 } 34 35 bool visitStatement(const Statement& stmt) override { 36 switch (stmt.kind()) { 37 case Statement::Kind::kBlock: { 38 // Check only the last statement of a block. 39 const auto& block = stmt.as<Block>(); 40 return !block.children().empty() && 41 this->visitStatement(*block.children().back()); 42 } 43 case Statement::Kind::kSwitch: 44 case Statement::Kind::kDo: 45 case Statement::Kind::kFor: 46 // Don't introspect switches or loop structures at all. 47 return false; 48 49 case Statement::Kind::kReturn: 50 ++fNumReturns; 51 [[fallthrough]]; 52 53 default: 54 return INHERITED::visitStatement(stmt); 55 } 56 } 57 58 int fNumReturns = 0; 59 using INHERITED = ProgramVisitor; 60 }; 61 62 return CountReturnsAtEndOfControlFlow{funcDef}.fNumReturns; 63 } 64 65 class CountReturnsWithLimit : public ProgramVisitor { 66 public: CountReturnsWithLimit(const FunctionDefinition & funcDef,int limit)67 CountReturnsWithLimit(const FunctionDefinition& funcDef, int limit) : fLimit(limit) { 68 this->visitProgramElement(funcDef); 69 } 70 visitExpression(const Expression & expr)71 bool visitExpression(const Expression& expr) override { 72 // Do not recurse into expressions. 73 return false; 74 } 75 visitStatement(const Statement & stmt)76 bool visitStatement(const Statement& stmt) override { 77 switch (stmt.kind()) { 78 case Statement::Kind::kReturn: { 79 ++fNumReturns; 80 fDeepestReturn = std::max(fDeepestReturn, fScopedBlockDepth); 81 return (fNumReturns >= fLimit) || INHERITED::visitStatement(stmt); 82 } 83 case Statement::Kind::kVarDeclaration: { 84 if (fScopedBlockDepth > 1) { 85 fVariablesInBlocks = true; 86 } 87 return INHERITED::visitStatement(stmt); 88 } 89 case Statement::Kind::kBlock: { 90 int depthIncrement = stmt.as<Block>().isScope() ? 1 : 0; 91 fScopedBlockDepth += depthIncrement; 92 bool result = INHERITED::visitStatement(stmt); 93 fScopedBlockDepth -= depthIncrement; 94 if (fNumReturns == 0 && fScopedBlockDepth <= 1) { 95 // If closing this block puts us back at the top level, and we haven't 96 // encountered any return statements yet, any vardecls we may have encountered 97 // up until this point can be ignored. They are out of scope now, and they were 98 // never used in a return statement. 99 fVariablesInBlocks = false; 100 } 101 return result; 102 } 103 default: 104 return INHERITED::visitStatement(stmt); 105 } 106 } 107 108 int fNumReturns = 0; 109 int fDeepestReturn = 0; 110 int fLimit = 0; 111 int fScopedBlockDepth = 0; 112 bool fVariablesInBlocks = false; 113 using INHERITED = ProgramVisitor; 114 }; 115 GetReturnComplexity(const FunctionDefinition & funcDef)116Analysis::ReturnComplexity Analysis::GetReturnComplexity(const FunctionDefinition& funcDef) { 117 int returnsAtEndOfControlFlow = count_returns_at_end_of_control_flow(funcDef); 118 CountReturnsWithLimit counter{funcDef, returnsAtEndOfControlFlow + 1}; 119 if (counter.fNumReturns > returnsAtEndOfControlFlow) { 120 return ReturnComplexity::kEarlyReturns; 121 } 122 if (counter.fNumReturns > 1) { 123 return ReturnComplexity::kScopedReturns; 124 } 125 if (counter.fVariablesInBlocks && counter.fDeepestReturn > 1) { 126 return ReturnComplexity::kScopedReturns; 127 } 128 return ReturnComplexity::kSingleSafeReturn; 129 } 130 131 } // namespace SkSL 132