xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLSymbolTable.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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