xref: /aosp_15_r20/external/skia/src/sksl/analysis/SkSLSwitchCaseContainsExit.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "src/sksl/SkSLAnalysis.h"
9 
10 #include "src/sksl/analysis/SkSLProgramVisitor.h"
11 #include "src/sksl/ir/SkSLIRNode.h"
12 #include "src/sksl/ir/SkSLStatement.h"
13 
14 namespace SkSL {
15 
16 class Expression;
17 
18 namespace {
19 
20 class SwitchCaseContainsExit : public ProgramVisitor {
21 public:
SwitchCaseContainsExit(bool conditionalExits)22     SwitchCaseContainsExit(bool conditionalExits) : fConditionalExits(conditionalExits) {}
23 
visitExpression(const Expression & expr)24     bool visitExpression(const Expression& expr) override {
25         // We can avoid processing expressions entirely.
26         return false;
27     }
28 
visitStatement(const Statement & stmt)29     bool visitStatement(const Statement& stmt) override {
30         switch (stmt.kind()) {
31             case Statement::Kind::kBlock:
32             case Statement::Kind::kSwitchCase:
33                 return INHERITED::visitStatement(stmt);
34 
35             case Statement::Kind::kReturn:
36                 // Returns are an early exit regardless of the surrounding control structures.
37                 return fConditionalExits ? fInConditional : !fInConditional;
38 
39             case Statement::Kind::kContinue:
40                 // Continues are an early exit from switches, but not loops.
41                 return !fInLoop &&
42                        (fConditionalExits ? fInConditional : !fInConditional);
43 
44             case Statement::Kind::kBreak:
45                 // Breaks cannot escape from switches or loops.
46                 return !fInLoop && !fInSwitch &&
47                        (fConditionalExits ? fInConditional : !fInConditional);
48 
49             case Statement::Kind::kIf: {
50                 ++fInConditional;
51                 bool result = INHERITED::visitStatement(stmt);
52                 --fInConditional;
53                 return result;
54             }
55 
56             case Statement::Kind::kFor:
57             case Statement::Kind::kDo: {
58                 // Loops are treated as conditionals because a loop could potentially execute zero
59                 // times. We don't have a straightforward way to determine that a loop definitely
60                 // executes at least once.
61                 ++fInConditional;
62                 ++fInLoop;
63                 bool result = INHERITED::visitStatement(stmt);
64                 --fInLoop;
65                 --fInConditional;
66                 return result;
67             }
68 
69             case Statement::Kind::kSwitch: {
70                 ++fInSwitch;
71                 bool result = INHERITED::visitStatement(stmt);
72                 --fInSwitch;
73                 return result;
74             }
75 
76             default:
77                 return false;
78         }
79     }
80 
81     bool fConditionalExits = false;
82     int fInConditional = 0;
83     int fInLoop = 0;
84     int fInSwitch = 0;
85     using INHERITED = ProgramVisitor;
86 };
87 
88 }  // namespace
89 
SwitchCaseContainsUnconditionalExit(const Statement & stmt)90 bool Analysis::SwitchCaseContainsUnconditionalExit(const Statement& stmt) {
91     return SwitchCaseContainsExit{/*conditionalExits=*/false}.visitStatement(stmt);
92 }
93 
SwitchCaseContainsConditionalExit(const Statement & stmt)94 bool Analysis::SwitchCaseContainsConditionalExit(const Statement& stmt) {
95     return SwitchCaseContainsExit{/*conditionalExits=*/true}.visitStatement(stmt);
96 }
97 
98 }  // namespace SkSL
99