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