blob: ffab3855b63eb1540b2e5e52ae630d50a7fd1204 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/sksl/SkSLAnalysis.h"
#include "include/private/SkSLStatement.h"
#include "src/sksl/analysis/SkSLProgramVisitor.h"
namespace SkSL {
namespace {
class SwitchCaseContainsExit : public ProgramVisitor {
public:
SwitchCaseContainsExit(bool conditionalExits) : fConditionalExits(conditionalExits) {}
bool visitStatement(const Statement& stmt) override {
switch (stmt.kind()) {
case Statement::Kind::kBlock:
case Statement::Kind::kSwitchCase:
return INHERITED::visitStatement(stmt);
case Statement::Kind::kReturn:
// Returns are an early exit regardless of the surrounding control structures.
return fConditionalExits ? fInConditional : !fInConditional;
case Statement::Kind::kContinue:
// Continues are an early exit from switches, but not loops.
return !fInLoop &&
(fConditionalExits ? fInConditional : !fInConditional);
case Statement::Kind::kBreak:
// Breaks cannot escape from switches or loops.
return !fInLoop && !fInSwitch &&
(fConditionalExits ? fInConditional : !fInConditional);
case Statement::Kind::kIf: {
++fInConditional;
bool result = INHERITED::visitStatement(stmt);
--fInConditional;
return result;
}
case Statement::Kind::kFor:
case Statement::Kind::kDo: {
// Loops are treated as conditionals because a loop could potentially execute zero
// times. We don't have a straightforward way to determine that a loop definitely
// executes at least once.
++fInConditional;
++fInLoop;
bool result = INHERITED::visitStatement(stmt);
--fInLoop;
--fInConditional;
return result;
}
case Statement::Kind::kSwitch: {
++fInSwitch;
bool result = INHERITED::visitStatement(stmt);
--fInSwitch;
return result;
}
default:
return false;
}
}
bool fConditionalExits = false;
int fInConditional = 0;
int fInLoop = 0;
int fInSwitch = 0;
using INHERITED = ProgramVisitor;
};
} // namespace
bool Analysis::SwitchCaseContainsUnconditionalExit(Statement& stmt) {
return SwitchCaseContainsExit{/*conditionalExits=*/false}.visitStatement(stmt);
}
bool Analysis::SwitchCaseContainsConditionalExit(Statement& stmt) {
return SwitchCaseContainsExit{/*conditionalExits=*/true}.visitStatement(stmt);
}
} // namespace SkSL