* 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 "include/core/SkTypes.h"
#include "src/core/SkTHash.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLErrorReporter.h"
#include "src/sksl/SkSLOperator.h"
#include "src/sksl/analysis/SkSLProgramVisitor.h"
#include "src/sksl/ir/SkSLBinaryExpression.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLForStatement.h"
#include "src/sksl/ir/SkSLIRNode.h"
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLModifierFlags.h"
#include "src/sksl/ir/SkSLStatement.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include <memory>
using namespace skia_private;
namespace SkSL {
class ProgramElement;
namespace {
class ConstantExpressionVisitor : public ProgramVisitor {
public:
ConstantExpressionVisitor(const THashSet<const Variable*>* loopIndices)
: fLoopIndices(loopIndices) {}
bool visitExpression(const Expression& e) override {
switch (e.kind()) {
case Expression::Kind::kLiteral:
return false;
case Expression::Kind::kSetting:
return false;
case Expression::Kind::kVariableReference: {
const Variable* v = e.as<VariableReference>().variable();
if (v->modifierFlags().isConst() && (v->storage() == Variable::Storage::kGlobal ||
v->storage() == Variable::Storage::kLocal)) {
return false;
}
return !fLoopIndices || !fLoopIndices->contains(v);
}
case Expression::Kind::kBinary:
if (e.as<BinaryExpression>().getOperator().kind() == Operator::Kind::COMMA) {
return true;
}
[[fallthrough]];
case Expression::Kind::kConstructorArray:
case Expression::Kind::kConstructorArrayCast:
case Expression::Kind::kConstructorCompound:
case Expression::Kind::kConstructorCompoundCast:
case Expression::Kind::kConstructorDiagonalMatrix:
case Expression::Kind::kConstructorMatrixResize:
case Expression::Kind::kConstructorScalarCast:
case Expression::Kind::kConstructorSplat:
case Expression::Kind::kConstructorStruct:
case Expression::Kind::kFieldAccess:
case Expression::Kind::kIndex:
case Expression::Kind::kPrefix:
case Expression::Kind::kPostfix:
case Expression::Kind::kSwizzle:
case Expression::Kind::kTernary:
return INHERITED::visitExpression(e);
case Expression::Kind::kFunctionCall:
case Expression::Kind::kChildCall:
case Expression::Kind::kPoison:
case Expression::Kind::kFunctionReference:
case Expression::Kind::kMethodReference:
case Expression::Kind::kTypeReference:
case Expression::Kind::kEmpty:
return true;
default:
SkDEBUGFAIL("Unexpected expression type");
return true;
}
}
private:
const THashSet<const Variable*>* fLoopIndices;
using INHERITED = ProgramVisitor;
};
class ES2IndexingVisitor : public ProgramVisitor {
public:
ES2IndexingVisitor(ErrorReporter& errors) : fErrors(errors) {}
bool visitStatement(const Statement& s) override {
if (s.is<ForStatement>()) {
const ForStatement& f = s.as<ForStatement>();
SkASSERT(f.initializer() && f.initializer()->is<VarDeclaration>());
const Variable* var = f.initializer()->as<VarDeclaration>().var();
SkASSERT(!fLoopIndices.contains(var));
fLoopIndices.add(var);
bool result = this->visitStatement(*f.statement());
fLoopIndices.remove(var);
return result;
}
return INHERITED::visitStatement(s);
}
bool visitExpression(const Expression& e) override {
if (e.is<IndexExpression>()) {
const IndexExpression& i = e.as<IndexExpression>();
if (ConstantExpressionVisitor{&fLoopIndices}.visitExpression(*i.index())) {
fErrors.error(i.fPosition, "index expression must be constant");
return true;
}
}
return INHERITED::visitExpression(e);
}
using ProgramVisitor::visitProgramElement;
private:
ErrorReporter& fErrors;
THashSet<const Variable*> fLoopIndices;
using INHERITED = ProgramVisitor;
};
}
bool Analysis::IsConstantExpression(const Expression& expr) {
return !ConstantExpressionVisitor{nullptr}.visitExpression(expr);
}
void Analysis::ValidateIndexingForES2(const ProgramElement& pe, ErrorReporter& errors) {
ES2IndexingVisitor visitor(errors);
visitor.visitProgramElement(pe);
}
}