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)90bool Analysis::SwitchCaseContainsUnconditionalExit(const Statement& stmt) { 91 return SwitchCaseContainsExit{/*conditionalExits=*/false}.visitStatement(stmt); 92 } 93 SwitchCaseContainsConditionalExit(const Statement & stmt)94bool Analysis::SwitchCaseContainsConditionalExit(const Statement& stmt) { 95 return SwitchCaseContainsExit{/*conditionalExits=*/true}.visitStatement(stmt); 96 } 97 98 } // namespace SkSL 99