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