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 #ifndef SKSL_SYMBOLTABLE
9 #define SKSL_SYMBOLTABLE
10 
11 #include "include/private/SkSLString.h"
12 #include "include/private/SkSLSymbol.h"
13 #include "include/private/SkTArray.h"
14 #include "include/private/SkTHash.h"
15 #include "src/sksl/SkSLErrorReporter.h"
16 
17 #include <forward_list>
18 #include <memory>
19 #include <vector>
20 
21 namespace SkSL {
22 
23 class FunctionDeclaration;
24 
25 /**
26  * Maps identifiers to symbols. Functions, in particular, are mapped to either FunctionDeclaration
27  * or UnresolvedFunction depending on whether they are overloaded or not.
28  */
29 class SymbolTable {
30 public:
SymbolTable(ErrorReporter * errorReporter,bool builtin)31     SymbolTable(ErrorReporter* errorReporter, bool builtin)
32     : fBuiltin(builtin)
33     , fErrorReporter(*errorReporter) {}
34 
SymbolTable(std::shared_ptr<SymbolTable> parent,bool builtin)35     SymbolTable(std::shared_ptr<SymbolTable> parent, bool builtin)
36     : fParent(parent)
37     , fBuiltin(builtin)
38     , fErrorReporter(parent->fErrorReporter) {}
39 
40     /**
41      * If the input is a built-in symbol table, returns a new empty symbol table as a child of the
42      * input table. If the input is not a built-in symbol table, returns it as-is. Built-in symbol
43      * tables must not be mutated after creation, so they must be wrapped if mutation is necessary.
44      */
WrapIfBuiltin(std::shared_ptr<SymbolTable> symbolTable)45     static std::shared_ptr<SymbolTable> WrapIfBuiltin(std::shared_ptr<SymbolTable> symbolTable) {
46         if (!symbolTable) {
47             return nullptr;
48         }
49         if (!symbolTable->isBuiltin()) {
50             return symbolTable;
51         }
52         return std::make_shared<SymbolTable>(std::move(symbolTable), /*builtin=*/false);
53     }
54 
55     /**
56      * Looks up the requested symbol and returns it. If a function has overloads, an
57      * UnresolvedFunction symbol (pointing to all of the candidates) will be added to the symbol
58      * table and returned.
59      */
60     const Symbol* operator[](StringFragment name);
61 
62     /**
63      * Creates a new name for a symbol which already exists; does not take ownership of Symbol*.
64      */
65     void addAlias(StringFragment name, const Symbol* symbol);
66 
67     void addWithoutOwnership(const Symbol* symbol);
68 
69     template <typename T>
add(std::unique_ptr<T> symbol)70     const T* add(std::unique_ptr<T> symbol) {
71         const T* ptr = symbol.get();
72         this->addWithoutOwnership(ptr);
73         this->takeOwnershipOfSymbol(std::move(symbol));
74         return ptr;
75     }
76 
77     template <typename T>
takeOwnershipOfSymbol(std::unique_ptr<T> symbol)78     const T* takeOwnershipOfSymbol(std::unique_ptr<T> symbol) {
79         const T* ptr = symbol.get();
80         fOwnedSymbols.push_back(std::move(symbol));
81         return ptr;
82     }
83 
84     template <typename T>
takeOwnershipOfIRNode(std::unique_ptr<T> node)85     const T* takeOwnershipOfIRNode(std::unique_ptr<T> node) {
86         const T* ptr = node.get();
87         fOwnedNodes.push_back(std::move(node));
88         return ptr;
89     }
90 
91     /**
92      * Given type = `float` and arraySize = 5, creates the array type `float[5]` in the symbol
93      * table. The created array type is returned. `kUnsizedArray` can be passed as a `[]` dimension.
94      * If zero is passed, the base type is returned unchanged.
95      */
96     const Type* addArrayDimension(const Type* type, int arraySize);
97 
98     // Call fn for every symbol in the table.  You may not mutate anything.
99     template <typename Fn>
foreach(Fn && fn)100     void foreach(Fn&& fn) const {
101         fSymbols.foreach(
102                 [&fn](const SymbolKey& key, const Symbol* symbol) { fn(key.fName, symbol); });
103     }
104 
count()105     size_t count() {
106         return fSymbols.count();
107     }
108 
109     /** Returns true if this is a built-in SymbolTable. */
isBuiltin()110     bool isBuiltin() const {
111         return fBuiltin;
112     }
113 
114     const String* takeOwnershipOfString(String n);
115 
116     std::shared_ptr<SymbolTable> fParent;
117 
118     std::vector<std::unique_ptr<const Symbol>> fOwnedSymbols;
119 
120 private:
121     struct SymbolKey {
122         StringFragment fName;
123         uint32_t       fHash;
124 
125         bool operator==(const SymbolKey& that) const { return fName == that.fName; }
126         bool operator!=(const SymbolKey& that) const { return fName != that.fName; }
127         struct Hash {
operatorSymbolKey::Hash128             uint32_t operator()(const SymbolKey& key) const { return key.fHash; }
129         };
130     };
131 
MakeSymbolKey(StringFragment name)132     static SymbolKey MakeSymbolKey(StringFragment name) {
133         return SymbolKey{name, SkOpts::hash_fn(name.data(), name.size(), 0)};
134     }
135 
136     const Symbol* lookup(SymbolTable* writableSymbolTable, const SymbolKey& key);
137 
138     static std::vector<const FunctionDeclaration*> GetFunctions(const Symbol& s);
139 
140     bool fBuiltin = false;
141     std::vector<std::unique_ptr<IRNode>> fOwnedNodes;
142     std::forward_list<String> fOwnedStrings;
143     SkTHashMap<SymbolKey, const Symbol*, SymbolKey::Hash> fSymbols;
144     ErrorReporter& fErrorReporter;
145 
146     friend class Dehydrator;
147 };
148 
149 }  // namespace SkSL
150 
151 #endif
152