blob: 473052cd5e895d3571b7ec90c77b1cd43315ac51 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_SYMBOLTABLE
#define SKSL_SYMBOLTABLE
#include "include/core/SkTypes.h"
#include "include/private/SkOpts_spi.h"
#include "include/private/SkSLSymbol.h"
#include "include/private/SkTHash.h"
#include <cstddef>
#include <cstdint>
#include <forward_list>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
namespace SkSL {
class Type;
/**
* Maps identifiers to symbols.
*/
class SymbolTable {
public:
SymbolTable(bool builtin)
: fBuiltin(builtin) {}
SymbolTable(std::shared_ptr<SymbolTable> parent, bool builtin)
: fParent(parent)
, fBuiltin(builtin) {}
/** Replaces the passed-in SymbolTable with a newly-created child symbol table. */
static void Push(std::shared_ptr<SymbolTable>* table) {
Push(table, (*table)->isBuiltin());
}
static void Push(std::shared_ptr<SymbolTable>* table, bool isBuiltin) {
*table = std::make_shared<SymbolTable>(*table, isBuiltin);
}
/**
* Replaces the passed-in SymbolTable with its parent. If the child symbol table is otherwise
* unreferenced, it will be deleted.
*/
static void Pop(std::shared_ptr<SymbolTable>* table) {
*table = (*table)->fParent;
}
/**
* If the input is a built-in symbol table, returns a new empty symbol table as a child of the
* input table. If the input is not a built-in symbol table, returns it as-is. Built-in symbol
* tables must not be mutated after creation, so they must be wrapped if mutation is necessary.
*/
static std::shared_ptr<SymbolTable> WrapIfBuiltin(std::shared_ptr<SymbolTable> symbolTable) {
if (!symbolTable) {
return nullptr;
}
if (!symbolTable->isBuiltin()) {
return symbolTable;
}
return std::make_shared<SymbolTable>(std::move(symbolTable), /*builtin=*/false);
}
/**
* Looks up the requested symbol and returns it.
*/
const Symbol* operator[](std::string_view name) const;
/**
* Returns true if the name refers to a type (user or built-in) in the current symbol table.
*/
bool isType(std::string_view name) const;
/**
* Returns true if the name refers to a builtin type.
*/
bool isBuiltinType(std::string_view name) const;
/**
* Adds a symbol to this symbol table, without conferring ownership. The caller is responsible
* for keeping the Symbol alive throughout the lifetime of the program/module.
*/
void addWithoutOwnership(Symbol* symbol);
void addWithoutOwnership(const Symbol* symbol) {
// If the symbol is a FunctionDeclaration, we need to use the non-const
// `addWithoutOwnership` call to ensure that overload chains are kept up-to-date.
SkASSERT(symbol->kind() != Symbol::Kind::kFunctionDeclaration);
return this->addWithoutOwnership(symbol, MakeSymbolKey(symbol->name()));
}
/**
* Adds a symbol to this symbol table, conferring ownership.
*/
template <typename T>
const T* add(std::unique_ptr<T> symbol) {
T* ptr = symbol.get();
this->addWithoutOwnership(ptr);
this->takeOwnershipOfSymbol(std::move(symbol));
return ptr;
}
/**
* Confers ownership of a symbol without adding its name to the lookup table.
*/
template <typename T>
T* takeOwnershipOfSymbol(std::unique_ptr<T> symbol) {
T* ptr = symbol.get();
fOwnedSymbols.push_back(std::move(symbol));
return ptr;
}
/**
* Given type = `float` and arraySize = 5, creates the array type `float[5]` in the symbol
* table. The created array type is returned. If zero is passed, the base type is returned
* unchanged.
*/
const Type* addArrayDimension(const Type* type, int arraySize);
// Call fn for every symbol in the table. You may not mutate anything.
template <typename Fn>
void foreach(Fn&& fn) const {
fSymbols.foreach(
[&fn](const SymbolKey& key, const Symbol* symbol) { fn(key.fName, symbol); });
}
size_t count() {
return fSymbols.count();
}
/** Returns true if this is a built-in SymbolTable. */
bool isBuiltin() const {
return fBuiltin;
}
const std::string* takeOwnershipOfString(std::string n);
/**
* Indicates that this symbol table's parent is in a different module than this one.
*/
void markModuleBoundary() {
fAtModuleBoundary = true;
}
std::shared_ptr<SymbolTable> fParent;
std::vector<std::unique_ptr<const Symbol>> fOwnedSymbols;
private:
struct SymbolKey {
std::string_view fName;
uint32_t fHash;
bool operator==(const SymbolKey& that) const { return fName == that.fName; }
bool operator!=(const SymbolKey& that) const { return fName != that.fName; }
struct Hash {
uint32_t operator()(const SymbolKey& key) const { return key.fHash; }
};
};
static SymbolKey MakeSymbolKey(std::string_view name) {
return SymbolKey{name, SkOpts::hash_fn(name.data(), name.size(), 0)};
}
const Symbol* lookup(const SymbolKey& key) const;
void addWithoutOwnership(const Symbol* symbol, const SymbolKey& key);
bool fBuiltin = false;
bool fAtModuleBoundary = false;
std::forward_list<std::string> fOwnedStrings;
SkTHashMap<SymbolKey, const Symbol*, SymbolKey::Hash> fSymbols;
friend class Dehydrator;
};
} // namespace SkSL
#endif