• 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/core/SkTypes.h"
9 #include "include/private/SkSLIRNode.h"
10 #include "include/private/SkSLModifiers.h"
11 #include "include/private/SkSLProgramElement.h"
12 #include "include/private/SkSLStatement.h"
13 #include "include/private/SkSLSymbol.h"
14 #include "src/base/SkStringView.h"
15 #include "src/sksl/SkSLAnalysis.h"
16 #include "src/sksl/SkSLCompiler.h"
17 #include "src/sksl/SkSLContext.h"
18 #include "src/sksl/SkSLModifiersPool.h"
19 #include "src/sksl/SkSLProgramSettings.h"
20 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
21 #include "src/sksl/ir/SkSLFunctionDefinition.h"
22 #include "src/sksl/ir/SkSLFunctionPrototype.h"
23 #include "src/sksl/ir/SkSLSymbolTable.h"
24 #include "src/sksl/ir/SkSLVarDeclarations.h"
25 #include "src/sksl/ir/SkSLVariable.h"
26 #include "src/sksl/transform/SkSLProgramWriter.h"
27 #include "src/sksl/transform/SkSLTransform.h"
28 
29 #include <cstdint>
30 #include <memory>
31 #include <string>
32 #include <string_view>
33 #include <utility>
34 #include <vector>
35 
36 namespace SkSL {
37 
38 class ProgramUsage;
39 enum class ProgramKind : int8_t;
40 
strip_export_flag(Context & context,const FunctionDeclaration * funcDecl,SymbolTable * symbols)41 static void strip_export_flag(Context& context,
42                               const FunctionDeclaration* funcDecl,
43                               SymbolTable* symbols) {
44     // Remove `$export` from every overload of this function.
45     Symbol* mutableSym = symbols->findMutable(funcDecl->name());
46     while (mutableSym) {
47         FunctionDeclaration* mutableDecl = &mutableSym->as<FunctionDeclaration>();
48 
49         Modifiers modifiers = mutableDecl->modifiers();
50         modifiers.fFlags &= ~Modifiers::kExport_Flag;
51         mutableDecl->setModifiers(context.fModifiersPool->add(modifiers));
52 
53         mutableSym = mutableDecl->mutableNextOverload();
54     }
55 }
56 
RenamePrivateSymbols(Context & context,Module & module,ProgramUsage * usage,ProgramKind kind)57 void Transform::RenamePrivateSymbols(Context& context,
58                                      Module& module,
59                                      ProgramUsage* usage,
60                                      ProgramKind kind) {
61     class SymbolRenamer : public ProgramWriter {
62     public:
63         SymbolRenamer(Context& context,
64                       ProgramUsage* usage,
65                       std::shared_ptr<SymbolTable> symbolBase,
66                       ProgramKind kind)
67                 : fContext(context)
68                 , fUsage(usage)
69                 , fSymbolTableStack({std::move(symbolBase)})
70                 , fKind(kind) {}
71 
72         static std::string FindShortNameForSymbol(const Symbol* sym,
73                                                   const SymbolTable* symbolTable,
74                                                   std::string namePrefix) {
75             static constexpr std::string_view kLetters[] = {
76                     "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
77                     "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
78                     "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
79                     "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
80 
81             // Try any single-letter option.
82             for (std::string_view letter : kLetters) {
83                 std::string name = namePrefix + std::string(letter);
84                 if (symbolTable->find(name) == nullptr) {
85                     return name;
86                 }
87             }
88 
89             // Try every two-letter option.
90             for (std::string_view letterA : kLetters) {
91                 for (std::string_view letterB : kLetters) {
92                     std::string name = namePrefix + std::string(letterA) + std::string(letterB);
93                     if (symbolTable->find(name) == nullptr) {
94                         return name;
95                     }
96                 }
97             }
98 
99             // We struck out. Somehow, all 2700 two-letter names have been claimed.
100             SkDEBUGFAILF("Unable to find unique name for '%s'", std::string(sym->name()).c_str());
101             return std::string(sym->name());
102         }
103 
104         void minifyVariableName(const Variable* var) {
105             // Some variables are associated with anonymous parameters--these don't have names and
106             // aren't present in the symbol table. Their names are already empty so there's no way
107             // to shrink them further.
108             if (var->name().empty()) {
109                 return;
110             }
111 
112             // Ensure that this variable is properly set up in the symbol table.
113             SymbolTable* symbols = fSymbolTableStack.back().get();
114             Symbol* mutableSym = symbols->findMutable(var->name());
115             SkASSERTF(mutableSym != nullptr,
116                       "symbol table missing '%.*s'", (int)var->name().size(), var->name().data());
117             SkASSERTF(mutableSym == var,
118                       "wrong symbol found for '%.*s'", (int)var->name().size(), var->name().data());
119 
120             // Look for a new name for this symbol.
121             // Note: we always rename _every_ variable, even ones with single-letter names. This is
122             // a safeguard: if we claimed a name like `i`, and then the program itself contained an
123             // `i` later on, in a nested SymbolTable, the two names would clash. By always renaming
124             // everything, we can ignore that problem.
125             std::string shortName = FindShortNameForSymbol(var, symbols, "");
126             SkASSERT(symbols->findMutable(shortName) == nullptr);
127 
128             // Update the symbol's name.
129             const std::string* ownedName = symbols->takeOwnershipOfString(std::move(shortName));
130             symbols->renameSymbol(mutableSym, *ownedName);
131         }
132 
133         void minifyFunctionName(const FunctionDeclaration* funcDecl) {
134             // Look for a new name for this function.
135             std::string namePrefix = ProgramConfig::IsRuntimeEffect(fKind) ? "" : "$";
136             SymbolTable* symbols = fSymbolTableStack.back().get();
137             std::string shortName = FindShortNameForSymbol(funcDecl, symbols,
138                                                            std::move(namePrefix));
139             SkASSERT(symbols->findMutable(shortName) == nullptr);
140 
141             if (shortName.size() < funcDecl->name().size()) {
142                 // Update the function's name. (If the function has overloads, this will rename all
143                 // of them at once.)
144                 Symbol* mutableSym = symbols->findMutable(funcDecl->name());
145                 const std::string* ownedName = symbols->takeOwnershipOfString(std::move(shortName));
146                 symbols->renameSymbol(mutableSym, *ownedName);
147             }
148         }
149 
150         bool functionNameCanBeMinifiedSafely(const FunctionDeclaration& funcDecl) const {
151             if (ProgramConfig::IsRuntimeEffect(fKind)) {
152                 // The only externally-accessible function in a runtime effect is main().
153                 return !funcDecl.isMain();
154             } else {
155                 // We will only minify $private_functions, and only ones not marked as $export.
156                 return skstd::starts_with(funcDecl.name(), '$') &&
157                        !(funcDecl.modifiers().fFlags & Modifiers::kExport_Flag);
158             }
159         }
160 
161         void minifyFunction(FunctionDefinition& def) {
162             // If the function is private, minify its name.
163             const FunctionDeclaration* funcDecl = &def.declaration();
164             if (this->functionNameCanBeMinifiedSafely(*funcDecl)) {
165                 this->minifyFunctionName(funcDecl);
166             }
167 
168             // Minify the names of each function parameter.
169             Analysis::SymbolTableStackBuilder symbolTableStackBuilder(def.body().get(),
170                                                                       &fSymbolTableStack);
171             for (Variable* param : funcDecl->parameters()) {
172                 this->minifyVariableName(param);
173             }
174         }
175 
176         void minifyPrototype(FunctionPrototype& proto) {
177             const FunctionDeclaration* funcDecl = &proto.declaration();
178             if (funcDecl->definition()) {
179                 // This function is defined somewhere; this isn't just a loose prototype.
180                 return;
181             }
182 
183             // Eliminate the names of each function parameter.
184             // The parameter names aren't in the symbol table's name lookup map at all.
185             // All we need to do is blank out their names.
186             for (Variable* param : funcDecl->parameters()) {
187                 param->setName("");
188             }
189         }
190 
191         bool visitProgramElement(ProgramElement& elem) override {
192             switch (elem.kind()) {
193                 case ProgramElement::Kind::kFunction:
194                     this->minifyFunction(elem.as<FunctionDefinition>());
195                     return INHERITED::visitProgramElement(elem);
196 
197                case ProgramElement::Kind::kFunctionPrototype:
198                    this->minifyPrototype(elem.as<FunctionPrototype>());
199                     return INHERITED::visitProgramElement(elem);
200 
201                 default:
202                     return false;
203             }
204         }
205 
206         bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
207             Analysis::SymbolTableStackBuilder symbolTableStackBuilder(stmt.get(),
208                                                                       &fSymbolTableStack);
209             if (stmt->is<VarDeclaration>()) {
210                 // Minify the variable's name.
211                 VarDeclaration& decl = stmt->as<VarDeclaration>();
212                 this->minifyVariableName(decl.var());
213             }
214 
215             return INHERITED::visitStatementPtr(stmt);
216         }
217 
218         Context& fContext;
219         ProgramUsage* fUsage;
220         std::vector<std::shared_ptr<SymbolTable>> fSymbolTableStack;
221         ProgramKind fKind;
222         using INHERITED = ProgramWriter;
223     };
224 
225     // Rename local variables and private functions.
226     SymbolRenamer renamer{context, usage, module.fSymbols, kind};
227     for (std::unique_ptr<ProgramElement>& pe : module.fElements) {
228         renamer.visitProgramElement(*pe);
229     }
230 
231     // Strip off modifier `$export` from every function. (Only the minifier checks this flag, so we
232     // can remove it without affecting the meaning of the code.)
233     for (std::unique_ptr<ProgramElement>& pe : module.fElements) {
234         if (pe->is<FunctionDefinition>()) {
235             const FunctionDeclaration* funcDecl = &pe->as<FunctionDefinition>().declaration();
236             if (funcDecl->modifiers().fFlags & Modifiers::kExport_Flag) {
237                 strip_export_flag(context, funcDecl, module.fSymbols.get());
238             }
239         }
240     }
241 }
242 
243 }  // namespace SkSL
244