blob: 65c9e5e424006e42e3299bafa47866d50cb5fd6b [file] [log] [blame]
/*
* Copyright 2023 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/SkSLIRNode.h"
#include "include/private/SkSLStatement.h"
#include "src/sksl/analysis/SkSLProgramVisitor.h"
namespace SkSL {
class Expression;
namespace Analysis {
namespace {
class LoopControlFlowVisitor : public ProgramVisitor {
public:
LoopControlFlowVisitor() {}
bool visitExpression(const Expression& expr) override {
// We can avoid processing expressions entirely.
return false;
}
bool visitStatement(const Statement& stmt) override {
switch (stmt.kind()) {
case Statement::Kind::kContinue:
// A continue only affects the control flow of the loop if it's not nested inside
// another looping structure. (Inside a switch, SkSL disallows continue entirely.)
fResult.fHasContinue |= (fDepth == 0);
break;
case Statement::Kind::kBreak:
// A break only affects the control flow of the loop if it's not nested inside
// another loop/switch structure.
fResult.fHasBreak |= (fDepth == 0);
break;
case Statement::Kind::kReturn:
// A return will abort the loop's control flow no matter how deeply it is nested.
fResult.fHasReturn = true;
break;
case Statement::Kind::kFor:
case Statement::Kind::kDo:
case Statement::Kind::kSwitch: {
++fDepth;
bool done = ProgramVisitor::visitStatement(stmt);
--fDepth;
return done;
}
default:
return ProgramVisitor::visitStatement(stmt);
}
// If we've already found everything we're hunting for, we can stop searching early.
return fResult.fHasContinue && fResult.fHasBreak && fResult.fHasReturn;
}
LoopControlFlowInfo fResult;
int fDepth = 0;
};
} // namespace
LoopControlFlowInfo GetLoopControlFlowInfo(const Statement& stmt) {
LoopControlFlowVisitor visitor;
visitor.visitStatement(stmt);
return visitor.fResult;
}
} // namespace Analysis
} // namespace SkSL