• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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 "include/private/SkSLStatement.h"
9 #include "src/sksl/SkSLAnalysis.h"
10 #include "src/sksl/analysis/SkSLProgramVisitor.h"
11 #include "src/sksl/ir/SkSLDoStatement.h"
12 #include "src/sksl/ir/SkSLForStatement.h"
13 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
14 #include "src/sksl/ir/SkSLIfStatement.h"
15 #include "src/sksl/ir/SkSLSwitchStatement.h"
16 
17 namespace SkSL {
18 namespace {
19 
20 class ReturnsOnAllPathsVisitor : public ProgramVisitor {
21 public:
visitExpression(const Expression & expr)22     bool visitExpression(const Expression& expr) override {
23         // We can avoid processing expressions entirely.
24         return false;
25     }
26 
visitStatement(const Statement & stmt)27     bool visitStatement(const Statement& stmt) override {
28         switch (stmt.kind()) {
29             // Returns, breaks, or continues will stop the scan, so only one of these should ever be
30             // true.
31             case Statement::Kind::kReturn:
32                 fFoundReturn = true;
33                 return true;
34 
35             case Statement::Kind::kBreak:
36                 fFoundBreak = true;
37                 return true;
38 
39             case Statement::Kind::kContinue:
40                 fFoundContinue = true;
41                 return true;
42 
43             case Statement::Kind::kIf: {
44                 const IfStatement& i = stmt.as<IfStatement>();
45                 ReturnsOnAllPathsVisitor trueVisitor;
46                 ReturnsOnAllPathsVisitor falseVisitor;
47                 trueVisitor.visitStatement(*i.ifTrue());
48                 if (i.ifFalse()) {
49                     falseVisitor.visitStatement(*i.ifFalse());
50                 }
51                 // If either branch leads to a break or continue, we report the entire if as
52                 // containing a break or continue, since we don't know which side will be reached.
53                 fFoundBreak    = (trueVisitor.fFoundBreak    || falseVisitor.fFoundBreak);
54                 fFoundContinue = (trueVisitor.fFoundContinue || falseVisitor.fFoundContinue);
55                 // On the other hand, we only want to report returns that definitely happen, so we
56                 // require those to be found on both sides.
57                 fFoundReturn   = (trueVisitor.fFoundReturn   && falseVisitor.fFoundReturn);
58                 return fFoundBreak || fFoundContinue || fFoundReturn;
59             }
60             case Statement::Kind::kFor: {
61                 const ForStatement& f = stmt.as<ForStatement>();
62                 // We assume a for/while loop runs for at least one iteration; this isn't strictly
63                 // guaranteed, but it's better to be slightly over-permissive here than to fail on
64                 // reasonable code.
65                 ReturnsOnAllPathsVisitor forVisitor;
66                 forVisitor.visitStatement(*f.statement());
67                 // A for loop that contains a break or continue is safe; it won't exit the entire
68                 // function, just the loop. So we disregard those signals.
69                 fFoundReturn = forVisitor.fFoundReturn;
70                 return fFoundReturn;
71             }
72             case Statement::Kind::kDo: {
73                 const DoStatement& d = stmt.as<DoStatement>();
74                 // Do-while blocks are always entered at least once.
75                 ReturnsOnAllPathsVisitor doVisitor;
76                 doVisitor.visitStatement(*d.statement());
77                 // A do-while loop that contains a break or continue is safe; it won't exit the
78                 // entire function, just the loop. So we disregard those signals.
79                 fFoundReturn = doVisitor.fFoundReturn;
80                 return fFoundReturn;
81             }
82             case Statement::Kind::kBlock:
83                 // Blocks are definitely entered and don't imply any additional control flow.
84                 // If the block contains a break, continue or return, we want to keep that.
85                 return INHERITED::visitStatement(stmt);
86 
87             case Statement::Kind::kSwitch: {
88                 // Switches are the most complex control flow we need to deal with; fortunately we
89                 // already have good primitives for dissecting them. We need to verify that:
90                 // - a default case exists, so that every possible input value is covered
91                 // - every switch-case either (a) returns unconditionally, or
92                 //                            (b) falls through to another case that does
93                 const SwitchStatement& s = stmt.as<SwitchStatement>();
94                 bool foundDefault = false;
95                 bool fellThrough = false;
96                 for (const std::unique_ptr<Statement>& switchStmt : s.cases()) {
97                     // The default case is indicated by a null value. A switch without a default
98                     // case cannot definitively return, as its value might not be in the cases list.
99                     const SwitchCase& sc = switchStmt->as<SwitchCase>();
100                     if (!sc.value()) {
101                         foundDefault = true;
102                     }
103                     // Scan this switch-case for any exit (break, continue or return).
104                     ReturnsOnAllPathsVisitor caseVisitor;
105                     caseVisitor.visitStatement(sc);
106 
107                     // If we found a break or continue, whether conditional or not, this switch case
108                     // can't be called an unconditional return. Switches absorb breaks but not
109                     // continues.
110                     if (caseVisitor.fFoundContinue) {
111                         fFoundContinue = true;
112                         return false;
113                     }
114                     if (caseVisitor.fFoundBreak) {
115                         return false;
116                     }
117                     // We just confirmed that there weren't any breaks or continues. If we didn't
118                     // find an unconditional return either, the switch is considered fallen-through.
119                     // (There might be a conditional return, but that doesn't count.)
120                     fellThrough = !caseVisitor.fFoundReturn;
121                 }
122 
123                 // If we didn't find a default case, or the very last case fell through, this switch
124                 // doesn't meet our criteria.
125                 if (fellThrough || !foundDefault) {
126                     return false;
127                 }
128 
129                 // We scanned the entire switch, found a default case, and every section either fell
130                 // through or contained an unconditional return.
131                 fFoundReturn = true;
132                 return true;
133             }
134 
135             case Statement::Kind::kSwitchCase:
136                 // Recurse into the switch-case.
137                 return INHERITED::visitStatement(stmt);
138 
139             case Statement::Kind::kDiscard:
140             case Statement::Kind::kExpression:
141             case Statement::Kind::kInlineMarker:
142             case Statement::Kind::kNop:
143             case Statement::Kind::kVarDeclaration:
144                 // None of these statements could contain a return.
145                 break;
146         }
147 
148         return false;
149     }
150 
151     bool fFoundReturn = false;
152     bool fFoundBreak = false;
153     bool fFoundContinue = false;
154 
155     using INHERITED = ProgramVisitor;
156 };
157 
158 }  // namespace
159 
CanExitWithoutReturningValue(const FunctionDeclaration & funcDecl,const Statement & body)160 bool Analysis::CanExitWithoutReturningValue(const FunctionDeclaration& funcDecl,
161                                             const Statement& body) {
162     if (funcDecl.returnType().isVoid()) {
163         return false;
164     }
165     ReturnsOnAllPathsVisitor visitor;
166     visitor.visitStatement(body);
167     return !visitor.fFoundReturn;
168 }
169 
170 }  // namespace SkSL
171