1 /*
2 * Copyright 2016 Google Inc.
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 "src/sksl/ir/SkSLSymbolTable.h"
9
10 #include "src/sksl/SkSLContext.h"
11 #include "src/sksl/SkSLErrorReporter.h"
12 #include "src/sksl/SkSLPosition.h"
13 #include "src/sksl/SkSLProgramSettings.h"
14 #include "src/sksl/ir/SkSLExpression.h"
15 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
16 #include "src/sksl/ir/SkSLType.h"
17
18 namespace SkSL {
19
insertNewParent()20 std::unique_ptr<SymbolTable> SymbolTable::insertNewParent() {
21 auto newTable = std::make_unique<SymbolTable>(fParent, fBuiltin);
22 fParent = newTable.get();
23 return newTable;
24 }
25
isType(std::string_view name) const26 bool SymbolTable::isType(std::string_view name) const {
27 const Symbol* symbol = this->find(name);
28 return symbol && symbol->is<Type>();
29 }
30
isBuiltinType(std::string_view name) const31 bool SymbolTable::isBuiltinType(std::string_view name) const {
32 if (!this->isBuiltin()) {
33 return fParent && fParent->isBuiltinType(name);
34 }
35 return this->isType(name);
36 }
37
findBuiltinSymbol(std::string_view name) const38 const Symbol* SymbolTable::findBuiltinSymbol(std::string_view name) const {
39 if (!this->isBuiltin()) {
40 return fParent ? fParent->findBuiltinSymbol(name) : nullptr;
41 }
42 return this->find(name);
43 }
44
wouldShadowSymbolsFrom(const SymbolTable * other) const45 bool SymbolTable::wouldShadowSymbolsFrom(const SymbolTable* other) const {
46 // We are checking two hash maps for overlap; we always iterate over the smaller one to minimize
47 // the total number of checks.
48 const SymbolTable* self = this;
49 if (self->count() > other->count()) {
50 std::swap(self, other);
51 }
52
53 bool foundShadow = false;
54
55 self->fSymbols.foreach([&](const SymbolKey& key, const Symbol* symbol) {
56 if (foundShadow) {
57 // We've already found a shadowed symbol; stop searching.
58 return;
59 }
60 if (other->fSymbols.find(key) != nullptr) {
61 foundShadow = true;
62 }
63 });
64
65 return foundShadow;
66 }
67
lookup(const SymbolKey & key) const68 Symbol* SymbolTable::lookup(const SymbolKey& key) const {
69 Symbol** symbolPPtr = fSymbols.find(key);
70 if (symbolPPtr) {
71 return *symbolPPtr;
72 }
73
74 // The symbol wasn't found; recurse into the parent symbol table.
75 return fParent ? fParent->lookup(key) : nullptr;
76 }
77
renameSymbol(const Context & context,Symbol * symbol,std::string_view newName)78 void SymbolTable::renameSymbol(const Context& context, Symbol* symbol, std::string_view newName) {
79 if (symbol->is<FunctionDeclaration>()) {
80 // This is a function declaration, so we need to rename the entire overload set.
81 for (FunctionDeclaration* fn = &symbol->as<FunctionDeclaration>(); fn != nullptr;
82 fn = fn->mutableNextOverload()) {
83 fn->setName(newName);
84 }
85 } else {
86 // Other types of symbols don't allow multiple symbols with the same name.
87 symbol->setName(newName);
88 }
89
90 this->addWithoutOwnership(context, symbol);
91 }
92
removeSymbol(const Symbol * symbol)93 std::unique_ptr<Symbol> SymbolTable::removeSymbol(const Symbol* symbol) {
94 // Remove the symbol from our symbol lookup table.
95 if (fSymbols.removeIfExists(MakeSymbolKey(symbol->name()))) {
96 // Transfer ownership of the symbol if we own it. (This will leave a nullptr behind in the
97 // `fOwnedSymbols` list, which should be harmless.)
98 for (std::unique_ptr<Symbol>& owned : fOwnedSymbols) {
99 if (symbol == owned.get()) {
100 return std::move(owned);
101 }
102 }
103 }
104
105 // We don't own the symbol after all.
106 return nullptr;
107 }
108
moveSymbolTo(SymbolTable * otherTable,Symbol * sym,const Context & context)109 void SymbolTable::moveSymbolTo(SymbolTable* otherTable, Symbol* sym, const Context& context) {
110 if (std::unique_ptr<Symbol> ownedSymbol = this->removeSymbol(sym)) {
111 otherTable->add(context, std::move(ownedSymbol));
112 } else {
113 otherTable->addWithoutOwnership(context, sym);
114 }
115 }
116
takeOwnershipOfString(std::string str)117 const std::string* SymbolTable::takeOwnershipOfString(std::string str) {
118 fOwnedStrings.push_front(std::move(str));
119 // Because fOwnedStrings is a linked list, pointers to elements are stable.
120 return &fOwnedStrings.front();
121 }
122
addWithoutOwnership(const Context & context,Symbol * symbol)123 void SymbolTable::addWithoutOwnership(const Context& context, Symbol* symbol) {
124 if (!this->addWithoutOwnership(symbol)) {
125 context.fErrors->error(symbol->position(),
126 "symbol '" + std::string(symbol->name()) + "' was already defined");
127 }
128 }
129
addWithoutOwnershipOrDie(Symbol * symbol)130 void SymbolTable::addWithoutOwnershipOrDie(Symbol* symbol) {
131 if (!this->addWithoutOwnership(symbol)) {
132 SK_ABORT("symbol '%.*s' was already defined",
133 (int)symbol->name().size(), symbol->name().data());
134 }
135 }
136
addWithoutOwnership(Symbol * symbol)137 bool SymbolTable::addWithoutOwnership(Symbol* symbol) {
138 if (symbol->name().empty()) {
139 // We have legitimate use cases of nameless symbols, such as anonymous function parameters.
140 // If we find one here, we don't need to add its name to the symbol table.
141 return true;
142 }
143 auto key = MakeSymbolKey(symbol->name());
144
145 // If this is a function declaration, we need to keep the overload chain in sync.
146 if (symbol->is<FunctionDeclaration>()) {
147 // If we have a function with the same name...
148 Symbol* existingSymbol = this->lookup(key);
149 if (existingSymbol && existingSymbol->is<FunctionDeclaration>()) {
150 // ... add the existing function as the next overload in the chain.
151 FunctionDeclaration* existingDecl = &existingSymbol->as<FunctionDeclaration>();
152 symbol->as<FunctionDeclaration>().setNextOverload(existingDecl);
153 fSymbols[key] = symbol;
154 return true;
155 }
156 }
157
158 if (fAtModuleBoundary && fParent && fParent->lookup(key)) {
159 // We are attempting to declare a symbol at global scope that already exists in a parent
160 // module. This is a duplicate symbol and should be rejected.
161 return false;
162 }
163
164 std::swap(symbol, fSymbols[key]);
165 return symbol == nullptr;
166 }
167
injectWithoutOwnership(Symbol * symbol)168 void SymbolTable::injectWithoutOwnership(Symbol* symbol) {
169 auto key = MakeSymbolKey(symbol->name());
170 fSymbols[key] = symbol;
171 }
172
addArrayDimension(const Context & context,const Type * type,int arraySize)173 const Type* SymbolTable::addArrayDimension(const Context& context,
174 const Type* type,
175 int arraySize) {
176 if (arraySize == 0) {
177 return type;
178 }
179 // If we are making an array of a builtin type, we add it as high as possible in the symbol
180 // table tree (at the module boundary), to enable additional reuse of the array-type.
181 if (fParent && !fAtModuleBoundary && !context.fConfig->isBuiltinCode() && type->isBuiltin()) {
182 return fParent->addArrayDimension(context, type, arraySize);
183 }
184 // Reuse an existing array type with this name if one already exists in our symbol table.
185 std::string arrayName = type->getArrayName(arraySize);
186 if (const Symbol* existingSymbol = this->find(arrayName)) {
187 // We would expect an existing symbol named `Type[123]` to match our `Type[123]`. However,
188 // we might be compiling invalid code that contains duplicate symbols, and so we need to
189 // verify that these two types actually match before reusing the existing type.
190 const Type* existingType = &existingSymbol->as<Type>();
191 if (existingType->isArray() && type->matches(existingType->componentType())) {
192 return existingType;
193 }
194 }
195 // Add a new array type to the symbol table.
196 const std::string* arrayNamePtr = this->takeOwnershipOfString(std::move(arrayName));
197 return this->add(context, Type::MakeArrayType(context, *arrayNamePtr, *type, arraySize));
198 }
199
instantiateSymbolRef(const Context & context,std::string_view name,Position pos)200 std::unique_ptr<Expression> SymbolTable::instantiateSymbolRef(const Context& context,
201 std::string_view name,
202 Position pos) {
203 if (const Symbol* symbol = this->find(name)) {
204 return symbol->instantiate(context, pos);
205 }
206 context.fErrors->error(pos, "unknown identifier '" + std::string(name) + "'");
207 return nullptr;
208 }
209
210 } // namespace SkSL
211