xref: /aosp_15_r20/external/skia/src/sksl/analysis/SkSLGetReturnComplexity.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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)23 static 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)116 Analysis::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