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