• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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 "include/private/SkSLStatement.h"
9 #include "src/core/SkSafeMath.h"
10 #include "src/sksl/SkSLAnalysis.h"
11 #include "src/sksl/SkSLContext.h"
12 #include "src/sksl/SkSLProgramSettings.h"
13 #include "src/sksl/analysis/SkSLProgramVisitor.h"
14 #include "src/sksl/ir/SkSLFunctionCall.h"
15 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
16 #include "src/sksl/ir/SkSLFunctionDefinition.h"
17 #include "src/sksl/ir/SkSLIfStatement.h"
18 #include "src/sksl/ir/SkSLProgram.h"
19 #include "src/sksl/ir/SkSLSwitchStatement.h"
20 #include "src/sksl/ir/SkSLVarDeclarations.h"
21 #include "src/sksl/ir/SkSLVariable.h"
22 
23 namespace SkSL {
24 namespace {
25 
26 class FinalizationVisitor : public ProgramVisitor {
27 public:
FinalizationVisitor(const Context & c,const ProgramUsage & u)28     FinalizationVisitor(const Context& c, const ProgramUsage& u) : fContext(c), fUsage(u) {}
29 
visitProgramElement(const ProgramElement & pe)30     bool visitProgramElement(const ProgramElement& pe) override {
31         switch (pe.kind()) {
32             case ProgramElement::Kind::kGlobalVar: {
33                 this->checkGlobalVariableSizeLimit(pe.as<GlobalVarDeclaration>());
34                 break;
35             }
36             case ProgramElement::Kind::kFunction: {
37                 this->checkOutParamsAreAssigned(pe.as<FunctionDefinition>());
38                 break;
39             }
40             default:
41                 break;
42         }
43         return INHERITED::visitProgramElement(pe);
44     }
45 
checkGlobalVariableSizeLimit(const GlobalVarDeclaration & globalDecl)46     void checkGlobalVariableSizeLimit(const GlobalVarDeclaration& globalDecl) {
47         const VarDeclaration& decl = globalDecl.declaration()->as<VarDeclaration>();
48 
49         size_t prevSlotsUsed = fGlobalSlotsUsed;
50         fGlobalSlotsUsed = SkSafeMath::Add(fGlobalSlotsUsed, decl.var().type().slotCount());
51         // To avoid overzealous error reporting, only trigger the error at the first place where the
52         // global limit is exceeded.
53         if (prevSlotsUsed < kVariableSlotLimit && fGlobalSlotsUsed >= kVariableSlotLimit) {
54             fContext.fErrors->error(decl.fLine,
55                                     "global variable '" + std::string(decl.var().name()) +
56                                     "' exceeds the size limit");
57         }
58     }
59 
checkOutParamsAreAssigned(const FunctionDefinition & funcDef)60     void checkOutParamsAreAssigned(const FunctionDefinition& funcDef) {
61         const FunctionDeclaration& funcDecl = funcDef.declaration();
62 
63         // Searches for `out` parameters that are not written to. According to the GLSL spec,
64         // the value of an out-param that's never assigned to is unspecified, so report it.
65         // Structs are currently exempt from the rule because custom mesh specifications require an
66         // `out` parameter for a Varyings struct, even if your mesh program doesn't need Varyings.
67         for (const Variable* param : funcDecl.parameters()) {
68             const int paramInout = param->modifiers().fFlags & (Modifiers::Flag::kIn_Flag |
69                                                                 Modifiers::Flag::kOut_Flag);
70             if (!param->type().isStruct() && paramInout == Modifiers::Flag::kOut_Flag) {
71                 ProgramUsage::VariableCounts counts = fUsage.get(*param);
72                 if (counts.fWrite <= 0) {
73                     fContext.fErrors->error(funcDecl.fLine,
74                                             "function '" + std::string(funcDecl.name()) +
75                                             "' never assigns a value to out parameter '" +
76                                             std::string(param->name()) + "'");
77                 }
78             }
79         }
80     }
81 
visitStatement(const Statement & stmt)82     bool visitStatement(const Statement& stmt) override {
83         if (!fContext.fConfig->fSettings.fPermitInvalidStaticTests) {
84             switch (stmt.kind()) {
85                 case Statement::Kind::kIf:
86                     if (stmt.as<IfStatement>().isStatic()) {
87                         fContext.fErrors->error(stmt.fLine, "static if has non-static test");
88                     }
89                     break;
90 
91                 case Statement::Kind::kSwitch:
92                     if (stmt.as<SwitchStatement>().isStatic()) {
93                         fContext.fErrors->error(stmt.fLine, "static switch has non-static test");
94                     }
95                     break;
96 
97                 default:
98                     break;
99             }
100         }
101         return INHERITED::visitStatement(stmt);
102     }
103 
visitExpression(const Expression & expr)104     bool visitExpression(const Expression& expr) override {
105         switch (expr.kind()) {
106             case Expression::Kind::kFunctionCall: {
107                 const FunctionDeclaration& decl = expr.as<FunctionCall>().function();
108                 if (!decl.isBuiltin() && !decl.definition()) {
109                     fContext.fErrors->error(expr.fLine, "function '" + decl.description() +
110                                                         "' is not defined");
111                 }
112                 break;
113             }
114             case Expression::Kind::kExternalFunctionReference:
115             case Expression::Kind::kFunctionReference:
116             case Expression::Kind::kMethodReference:
117             case Expression::Kind::kTypeReference:
118                 SkDEBUGFAIL("invalid reference-expr, should have been reported by coerce()");
119                 fContext.fErrors->error(expr.fLine, "invalid expression");
120                 break;
121             default:
122                 if (expr.type().matches(*fContext.fTypes.fInvalid)) {
123                     fContext.fErrors->error(expr.fLine, "invalid expression");
124                 }
125                 break;
126         }
127         return INHERITED::visitExpression(expr);
128     }
129 
130 private:
131     using INHERITED = ProgramVisitor;
132     size_t fGlobalSlotsUsed = 0;
133     const Context& fContext;
134     const ProgramUsage& fUsage;
135 };
136 
137 }  // namespace
138 
DoFinalizationChecks(const Program & program)139 void Analysis::DoFinalizationChecks(const Program& program) {
140     // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
141     FinalizationVisitor visitor{*program.fContext, *program.usage()};
142     for (const std::unique_ptr<ProgramElement>& element : program.fOwnedElements) {
143         visitor.visitProgramElement(*element);
144     }
145 }
146 
147 }  // namespace SkSL
148