blob: 95c9904f86f8d7e4beade32f1ce5f27154b72d51 [file] [log] [blame]
/*
* Copyright 2020 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_REHYDRATOR
#define SKSL_REHYDRATOR
#include "include/core/SkTypes.h"
#include "include/private/SkSLDefines.h"
#include "include/private/SkSLLayout.h"
#include "include/private/SkSLModifiers.h"
#include "src/sksl/SkSLContext.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include <memory>
#include <string_view>
#include <vector>
namespace SkSL {
class Compiler;
class ErrorReporter;
class Expression;
class ModifiersPool;
class ProgramElement;
class Statement;
class Symbol;
class Type;
struct Program;
/**
* Interprets a simple bytecode format that encodes the structure of an SkSL IR tree. This is used
* to process the .sksl files representing SkSL's core include files, so that they can be quickly
* reconstituted at runtime.
*/
class Rehydrator {
public:
static constexpr uint16_t kVersion = 10;
// see binary_format.md for a description of the command data
enum Command {
kArrayType_Command,
kBinary_Command,
kBlock_Command,
kBoolLiteral_Command,
kBreak_Command,
kBuiltinLayout_Command,
kConstructorArray_Command,
kConstructorArrayCast_Command,
kConstructorCompound_Command,
kConstructorCompoundCast_Command,
kConstructorDiagonalMatrix_Command,
kConstructorMatrixResize_Command,
kConstructorScalarCast_Command,
kConstructorSplat_Command,
kConstructorStruct_Command,
kContinue_Command,
kDefaultLayout_Command,
kDefaultModifiers_Command,
kDiscard_Command,
kDo_Command,
kElements_Command,
kElementsComplete_Command,
kExpressionStatement_Command,
kField_Command,
kFieldAccess_Command,
kFloatLiteral_Command,
kFor_Command,
kFunctionCall_Command,
kFunctionDeclaration_Command,
kFunctionDefinition_Command,
kFunctionPrototype_Command,
kGlobalVar_Command,
kIf_Command,
kIndex_Command,
kUnused_Command, // was kInlineMarker_Command
kInterfaceBlock_Command,
kIntLiteral_Command,
kLayout_Command,
kModifiers8Bit_Command,
kModifiers_Command,
kNop_Command,
kPostfix_Command,
kPrefix_Command,
kProgram_Command,
kReturn_Command,
kSetting_Command,
kSharedFunction_Command,
kStructDefinition_Command,
kStructType_Command,
kSwitch_Command,
kSwizzle_Command,
kSymbolRef_Command,
kSymbolTable_Command,
kTernary_Command,
kUnresolvedFunction_Command,
kVariable_Command,
kVarDeclaration_Command,
kVariableReference_Command,
kVoid_Command,
};
// src must remain in memory as long as the objects created from it do
Rehydrator(Compiler& compiler, const uint8_t* src, size_t length,
std::shared_ptr<SymbolTable> base = nullptr);
#ifdef SK_DEBUG
~Rehydrator();
#endif
// Reads a symbol table and makes it current (inheriting from the previous current table)
std::shared_ptr<SymbolTable> symbolTable();
// Reads a collection of program elements and returns it
std::vector<std::unique_ptr<ProgramElement>> elements();
// Reads an entire program.
//
// NOTE: The program is initialized using a new ProgramConfig that may differ from the one that
// was assigned to the context of the Compiler this Rehydrator was constructed with.
std::unique_ptr<Program> program();
private:
// If this ID appears in place of a symbol ID, it means the corresponding symbol isn't actually
// present in the file as it's a builtin. The string name of the symbol follows.
static constexpr uint16_t kBuiltin_Symbol = 65535;
int8_t readS8() {
SkASSERT(fIP < fEnd);
return *(fIP++);
}
uint8_t readU8() {
return this->readS8();
}
int16_t readS16() {
uint8_t b1 = this->readU8();
uint8_t b2 = this->readU8();
return (b2 << 8) + b1;
}
uint16_t readU16() {
return this->readS16();
}
int32_t readS32() {
uint8_t b1 = this->readU8();
uint8_t b2 = this->readU8();
uint8_t b3 = this->readU8();
uint8_t b4 = this->readU8();
return (b4 << 24) + (b3 << 16) + (b2 << 8) + b1;
}
uint32_t readU32() {
return this->readS32();
}
std::string_view readString() {
uint16_t offset = this->readU16();
uint8_t length = *(uint8_t*) (fStringStart + offset);
const char* chars = (const char*) fStringStart + offset + 1;
return std::string_view(chars, length);
}
void addSymbol(int id, const Symbol* symbol) {
while ((size_t) id >= fSymbols.size()) {
fSymbols.push_back(nullptr);
}
fSymbols[id] = symbol;
}
template<typename T>
T* symbolRef() {
uint16_t result = this->readU16();
SkASSERTF(result != kBuiltin_Symbol, "use possiblyBuiltinSymbolRef() instead");
SkASSERT(fSymbols.size() > result);
return (T*) fSymbols[result];
}
/**
* Reads either a symbol belonging to this program, or a named reference to a builtin symbol.
* This has to be a separate method from symbolRef() because builtin symbols can be const, and
* thus this method must have a const return, but there is at least one case in which we
* specifically require a non-const return value.
*/
const Symbol* possiblyBuiltinSymbolRef() {
uint16_t id = this->readU16();
if (id == kBuiltin_Symbol) {
std::string_view name = this->readString();
const Symbol* result = (*fSymbolTable)[name];
SkASSERTF(result, "symbol '%s' not found", std::string(name).c_str());
return result;
}
SkASSERT(fSymbols.size() > id);
return fSymbols[id];
}
Layout layout();
Modifiers modifiers();
const Symbol* symbol();
std::unique_ptr<ProgramElement> element();
std::unique_ptr<Statement> statement();
std::unique_ptr<Expression> expression();
ExpressionArray expressionArray();
const Type* type();
Context& context() const;
ErrorReporter* errorReporter() const { return this->context().fErrors; }
ModifiersPool& modifiersPool() const { return *this->context().fModifiersPool; }
Compiler& fCompiler;
std::shared_ptr<SymbolTable> fSymbolTable;
std::vector<const Symbol*> fSymbols;
const uint8_t* fStringStart;
const uint8_t* fIP;
SkDEBUGCODE(const uint8_t* fEnd;)
friend class AutoRehydratorSymbolTable;
friend class Dehydrator;
};
} // namespace SkSL
#endif