xref: /aosp_15_r20/external/skia/src/sksl/analysis/SkSLCheckSymbolTableCorrectness.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2024 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/core/SkTypes.h"
9 #include "src/core/SkTHash.h"
10 #include "src/sksl/SkSLAnalysis.h"
11 #include "src/sksl/SkSLContext.h"
12 #include "src/sksl/SkSLErrorReporter.h"
13 #include "src/sksl/analysis/SkSLProgramVisitor.h"
14 #include "src/sksl/ir/SkSLProgram.h"
15 #include "src/sksl/ir/SkSLStatement.h"
16 #include "src/sksl/ir/SkSLSymbolTable.h"
17 #include "src/sksl/ir/SkSLVarDeclarations.h"
18 #include "src/sksl/ir/SkSLVariable.h"
19 
20 #include <memory>
21 #include <string>
22 #include <string_view>
23 #include <vector>
24 
25 using namespace skia_private;
26 
27 namespace SkSL {
28 
29 class Expression;
30 class ProgramElement;
31 class Symbol;
32 
CheckSymbolTableCorrectness(const Program & program)33 void Analysis::CheckSymbolTableCorrectness(const Program& program) {
34     const Context& context = *program.fContext;
35 
36     class SymbolTableCorrectnessVisitor : public ProgramVisitor {
37     public:
38         SymbolTableCorrectnessVisitor(const Context& c, SymbolTable* sym)
39                 : fContext(c)
40                 , fSymbolTableStack({sym}) {}
41 
42         using ProgramVisitor::visitProgramElement;
43 
44         bool visitStatement(const Statement& stmt) override {
45             Analysis::SymbolTableStackBuilder symbolTableStackBuilder(&stmt, &fSymbolTableStack);
46             if (stmt.is<VarDeclaration>()) {
47                 // Check the top of the symbol table stack to see if it contains this exact symbol.
48                 const VarDeclaration& vardecl = stmt.as<VarDeclaration>();
49                 bool containsSymbol = false;
50 
51                 // We want to do an exact Symbol* comparison in just one symbol table; we don't want
52                 // to look up by name, and we don't want to walk the symbol table tree. This makes
53                 // SymbolTable::find() an inappropriate tool for the job. Instead, we can iterate
54                 // the symbol table's contents directly and check for a pointer match.
55                 fSymbolTableStack.back()->foreach([&](std::string_view, const Symbol* symbol) {
56                     if (symbol == vardecl.var()) {
57                         containsSymbol = true;
58                     }
59                 });
60                 if (!containsSymbol) {
61                     fContext.fErrors->error(vardecl.position(), "internal error (variable '" +
62                                                                 std::string(vardecl.var()->name()) +
63                                                                 "' is incorrectly scoped)");
64                 }
65             }
66             return INHERITED::visitStatement(stmt);
67         }
68 
69         bool visitExpression(const Expression&) override {
70             return false;
71         }
72 
73     private:
74         using INHERITED = ProgramVisitor;
75 
76         const Context& fContext;
77         std::vector<SymbolTable*> fSymbolTableStack;
78     };
79 
80     SymbolTableCorrectnessVisitor visitor{context, program.fSymbols.get()};
81     for (const std::unique_ptr<ProgramElement>& pe : program.fOwnedElements) {
82         visitor.visitProgramElement(*pe);
83     }
84 }
85 
86 }  // namespace SkSL
87