blob: 771481cbd495b0fc92cc6f72abefd3c7fe81bb9a [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.
*/
#include "src/sksl/SkSLRehydrator.h"
#include "include/private/SkSLModifiers.h"
#include "include/private/SkSLProgramElement.h"
#include "include/private/SkSLProgramKind.h"
#include "include/private/SkSLStatement.h"
#include "include/private/SkSLSymbol.h"
#include "include/private/SkTArray.h"
#include "include/sksl/DSLCore.h"
#include "include/sksl/SkSLOperator.h"
#include "include/sksl/SkSLPosition.h"
#include "src/sksl/SkSLAnalysis.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLModifiersPool.h"
#include "src/sksl/SkSLParsedModule.h"
#include "src/sksl/SkSLPool.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/SkSLThreadContext.h"
#include "src/sksl/ir/SkSLBinaryExpression.h"
#include "src/sksl/ir/SkSLBlock.h"
#include "src/sksl/ir/SkSLBreakStatement.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
#include "src/sksl/ir/SkSLConstructorCompound.h"
#include "src/sksl/ir/SkSLConstructorCompoundCast.h"
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
#include "src/sksl/ir/SkSLConstructorMatrixResize.h"
#include "src/sksl/ir/SkSLConstructorScalarCast.h"
#include "src/sksl/ir/SkSLConstructorSplat.h"
#include "src/sksl/ir/SkSLConstructorStruct.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDiscardStatement.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLExpression.h"
#include "src/sksl/ir/SkSLExpressionStatement.h"
#include "src/sksl/ir/SkSLField.h"
#include "src/sksl/ir/SkSLFieldAccess.h"
#include "src/sksl/ir/SkSLForStatement.h"
#include "src/sksl/ir/SkSLFunctionCall.h"
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
#include "src/sksl/ir/SkSLFunctionPrototype.h"
#include "src/sksl/ir/SkSLIfStatement.h"
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLInterfaceBlock.h"
#include "src/sksl/ir/SkSLLiteral.h"
#include "src/sksl/ir/SkSLNop.h"
#include "src/sksl/ir/SkSLPostfixExpression.h"
#include "src/sksl/ir/SkSLPrefixExpression.h"
#include "src/sksl/ir/SkSLProgram.h"
#include "src/sksl/ir/SkSLReturnStatement.h"
#include "src/sksl/ir/SkSLSetting.h"
#include "src/sksl/ir/SkSLStructDefinition.h"
#include "src/sksl/ir/SkSLSwitchCase.h"
#include "src/sksl/ir/SkSLSwitchStatement.h"
#include "src/sksl/ir/SkSLSwizzle.h"
#include "src/sksl/ir/SkSLSymbolTable.h"
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLType.h"
#include "src/sksl/ir/SkSLUnresolvedFunction.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariable.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include <stdio.h>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
namespace SkSL {
class AutoRehydratorSymbolTable {
public:
AutoRehydratorSymbolTable(Rehydrator* rehydrator)
: fRehydrator(rehydrator)
, fOldSymbols(fRehydrator->fSymbolTable) {
std::shared_ptr<SymbolTable> symbols = fRehydrator->symbolTable();
if (symbols) {
fRehydrator->fSymbolTable = std::move(symbols);
}
}
~AutoRehydratorSymbolTable() {
fRehydrator->fSymbolTable = std::move(fOldSymbols);
}
private:
Rehydrator* fRehydrator;
std::shared_ptr<SymbolTable> fOldSymbols;
};
Rehydrator::Rehydrator(Compiler& compiler, const uint8_t* src, size_t length,
std::shared_ptr<SymbolTable> symbols)
: fCompiler(compiler)
, fSymbolTable(symbols ? std::move(symbols) : compiler.makeGLSLRootSymbolTable())
SkDEBUGCODE(, fEnd(src + length)) {
SkASSERT(fSymbolTable);
SkASSERT(fSymbolTable->isBuiltin());
fIP = src;
[[maybe_unused]] uint16_t version = this->readU16();
SkASSERTF(version == kVersion, "Dehydrated file is an unsupported version (current version is "
"%d, found version %d)", kVersion, version);
fStringStart = fIP;
// skip over string data
fIP += this->readU16();
}
#ifdef SK_DEBUG
Rehydrator::~Rehydrator() {
// ensure that we have read the expected number of bytes
SkASSERT(fIP == fEnd);
}
#endif
Context& Rehydrator::context() const {
return fCompiler.context();
}
Layout Rehydrator::layout() {
switch (this->readU8()) {
case kBuiltinLayout_Command: {
Layout result;
result.fBuiltin = this->readS16();
return result;
}
case kDefaultLayout_Command:
return Layout();
case kLayout_Command: {
int flags = this->readU32();
int location = this->readS8();
int offset = this->readS16();
int binding = this->readS16();
int index = this->readS8();
int set = this->readS8();
int builtin = this->readS16();
int inputAttachmentIndex = this->readS8();
return Layout(
flags, location, offset, binding, index, set, builtin, inputAttachmentIndex);
}
default:
SkASSERT(false);
return Layout();
}
}
Modifiers Rehydrator::modifiers() {
switch (this->readU8()) {
case kDefaultModifiers_Command:
return Modifiers();
case kModifiers8Bit_Command: {
Layout l = this->layout();
int flags = this->readU8();
return Modifiers(l, flags);
}
case kModifiers_Command: {
Layout l = this->layout();
int flags = this->readS32();
return Modifiers(l, flags);
}
default:
SkASSERT(false);
return Modifiers();
}
}
const Symbol* Rehydrator::symbol() {
int kind = this->readU8();
switch (kind) {
case kArrayType_Command: {
uint16_t id = this->readU16();
const Type* componentType = this->type();
int8_t count = this->readS8();
const std::string* arrayName =
fSymbolTable->takeOwnershipOfString(componentType->getArrayName(count));
const Type* result = fSymbolTable->takeOwnershipOfSymbol(
Type::MakeArrayType(*arrayName, *componentType, count));
this->addSymbol(id, result);
return result;
}
case kFunctionDeclaration_Command: {
uint16_t id = this->readU16();
Modifiers modifiers = this->modifiers();
std::string_view name = this->readString();
int parameterCount = this->readU8();
std::vector<const Variable*> parameters;
parameters.reserve(parameterCount);
for (int i = 0; i < parameterCount; ++i) {
parameters.push_back(this->symbolRef<Variable>());
}
const Type* returnType = this->type();
const FunctionDeclaration* result =
fSymbolTable->takeOwnershipOfSymbol(std::make_unique<FunctionDeclaration>(
Position(),
this->modifiersPool().add(modifiers),
name,
std::move(parameters),
returnType,
fSymbolTable->isBuiltin()));
this->addSymbol(id, result);
return result;
}
case kField_Command: {
const Variable* owner = this->symbolRef<Variable>();
uint8_t index = this->readU8();
const Field* result = fSymbolTable->takeOwnershipOfSymbol(
std::make_unique<Field>(Position(), owner, index));
return result;
}
case kStructType_Command: {
uint16_t id = this->readU16();
std::string name(this->readString());
uint8_t fieldCount = this->readU8();
std::vector<Type::Field> fields;
fields.reserve(fieldCount);
for (int i = 0; i < fieldCount; ++i) {
Modifiers m = this->modifiers();
std::string_view fieldName = this->readString();
const Type* type = this->type();
fields.emplace_back(Position(), m, fieldName, type);
}
bool interfaceBlock = this->readU8();
std::string_view nameChars(*fSymbolTable->takeOwnershipOfString(std::move(name)));
const Type* result = fSymbolTable->takeOwnershipOfSymbol(Type::MakeStructType(
Position(), nameChars, std::move(fields), interfaceBlock));
this->addSymbol(id, result);
return result;
}
case kSymbolRef_Command: {
return this->possiblyBuiltinSymbolRef();
}
case kUnresolvedFunction_Command: {
uint16_t id = this->readU16();
int length = this->readU8();
std::vector<const FunctionDeclaration*> functions;
functions.reserve(length);
for (int i = 0; i < length; ++i) {
const Symbol* f = this->symbol();
SkASSERT(f && f->kind() == Symbol::Kind::kFunctionDeclaration);
functions.push_back((const FunctionDeclaration*) f);
}
const UnresolvedFunction* result = fSymbolTable->takeOwnershipOfSymbol(
std::make_unique<UnresolvedFunction>(std::move(functions)));
this->addSymbol(id, result);
return result;
}
case kVariable_Command: {
uint16_t id = this->readU16();
const Modifiers* m = this->modifiersPool().add(this->modifiers());
std::string_view name = this->readString();
const Type* type = this->type();
Variable::Storage storage = (Variable::Storage) this->readU8();
const Variable* result = fSymbolTable->takeOwnershipOfSymbol(std::make_unique<Variable>(
/*pos=*/Position(), /*modifiersPosition=*/Position(), m, name, type,
fSymbolTable->isBuiltin(), storage));
this->addSymbol(id, result);
return result;
}
default:
printf("unsupported symbol %d\n", kind);
SkASSERT(false);
return nullptr;
}
}
const Type* Rehydrator::type() {
const Symbol* result = this->symbol();
SkASSERT(result->kind() == Symbol::Kind::kType);
return (const Type*) result;
}
std::unique_ptr<Program> Rehydrator::program() {
[[maybe_unused]] uint8_t command = this->readU8();
SkASSERT(command == kProgram_Command);
// Initialize the temporary config used to generate the complete program. We explicitly avoid
// enforcing ES2 restrictions when rehydrating a program, which we assume to be already
// well-formed when dehydrated.
auto config = std::make_unique<ProgramConfig>();
config->fKind = (ProgramKind)this->readU8();
config->fSettings.fEnforceES2Restrictions = false;
Context& context = this->context();
ProgramConfig* oldConfig = context.fConfig;
ModifiersPool* oldModifiersPool = context.fModifiersPool;
context.fConfig = config.get();
fSymbolTable = fCompiler.moduleForProgramKind(config->fKind).fSymbols;
dsl::Start(&fCompiler, config->fKind, config->fSettings);
auto modifiers = std::make_unique<ModifiersPool>();
context.fModifiersPool = modifiers.get();
this->symbolTable();
std::vector<std::unique_ptr<ProgramElement>> elements = this->elements();
context.fConfig = oldConfig;
context.fModifiersPool = oldModifiersPool;
Program::Inputs inputs;
inputs.fUseFlipRTUniform = this->readU8();
std::unique_ptr<Pool> pool = std::move(ThreadContext::MemoryPool());
pool->detachFromThread();
std::unique_ptr<Program> result = std::make_unique<Program>(nullptr, std::move(config),
fCompiler.fContext, std::move(elements),
/*sharedElements=*/std::vector<const ProgramElement*>(), std::move(modifiers),
fSymbolTable, std::move(pool), inputs);
fSymbolTable = fSymbolTable->fParent;
dsl::End();
return result;
}
std::vector<std::unique_ptr<ProgramElement>> Rehydrator::elements() {
SkDEBUGCODE(uint8_t command = )this->readU8();
SkASSERT(command == kElements_Command);
std::vector<std::unique_ptr<ProgramElement>> result;
while (std::unique_ptr<ProgramElement> elem = this->element()) {
result.push_back(std::move(elem));
}
return result;
}
std::unique_ptr<ProgramElement> Rehydrator::element() {
int kind = this->readU8();
switch (kind) {
case Rehydrator::kFunctionDefinition_Command: {
const FunctionDeclaration* decl = this->symbolRef<FunctionDeclaration>();
std::unique_ptr<Statement> body = this->statement();
auto result = FunctionDefinition::Convert(this->context(), Position(), *decl,
std::move(body), fSymbolTable->isBuiltin());
decl->setDefinition(result.get());
return std::move(result);
}
case Rehydrator::kFunctionPrototype_Command: {
const FunctionDeclaration* decl = this->symbolRef<FunctionDeclaration>();
// since we skip over builtin prototypes when dehydrating, we know that this
// builtin=false
return std::make_unique<FunctionPrototype>(Position(), decl, /*builtin=*/false);
}
case Rehydrator::kGlobalVar_Command: {
std::unique_ptr<Statement> decl = this->statement();
return std::make_unique<GlobalVarDeclaration>(std::move(decl));
}
case Rehydrator::kInterfaceBlock_Command: {
const Symbol* var = this->symbol();
SkASSERT(var && var->is<Variable>());
std::string_view typeName = this->readString();
std::string_view instanceName = this->readString();
int arraySize = this->readU8();
return std::make_unique<InterfaceBlock>(Position(), var->as<Variable>(), typeName,
instanceName, arraySize, nullptr);
}
case Rehydrator::kStructDefinition_Command: {
const Symbol* type = this->symbol();
SkASSERT(type && type->is<Type>());
return std::make_unique<StructDefinition>(Position(), type->as<Type>());
}
case Rehydrator::kSharedFunction_Command: {
int count = this->readU8();
for (int i = 0; i < count; ++i) {
[[maybe_unused]] const Symbol* param = this->symbol();
SkASSERT(param->is<Variable>());
}
[[maybe_unused]] const Symbol* decl = this->symbol();
SkASSERT(decl->is<FunctionDeclaration>());
std::unique_ptr<ProgramElement> result = this->element();
SkASSERT(result->is<FunctionDefinition>());
return result;
}
case Rehydrator::kElementsComplete_Command:
return nullptr;
default:
SkDEBUGFAILF("unsupported element %d\n", kind);
return nullptr;
}
}
std::unique_ptr<Statement> Rehydrator::statement() {
int kind = this->readU8();
switch (kind) {
case Rehydrator::kBlock_Command: {
AutoRehydratorSymbolTable symbols(this);
int count = this->readU8();
StatementArray statements;
statements.reserve_back(count);
for (int i = 0; i < count; ++i) {
statements.push_back(this->statement());
}
Block::Kind blockKind = (Block::Kind)this->readU8();
return Block::Make(Position(), std::move(statements), blockKind, fSymbolTable);
}
case Rehydrator::kBreak_Command:
return BreakStatement::Make(Position());
case Rehydrator::kContinue_Command:
return ContinueStatement::Make(Position());
case Rehydrator::kDiscard_Command:
return DiscardStatement::Make(Position());
case Rehydrator::kDo_Command: {
std::unique_ptr<Statement> stmt = this->statement();
std::unique_ptr<Expression> expr = this->expression();
return DoStatement::Make(this->context(), Position(), std::move(stmt), std::move(expr));
}
case Rehydrator::kExpressionStatement_Command: {
std::unique_ptr<Expression> expr = this->expression();
return ExpressionStatement::Make(this->context(), std::move(expr));
}
case Rehydrator::kFor_Command: {
AutoRehydratorSymbolTable symbols(this);
std::unique_ptr<Statement> initializer = this->statement();
std::unique_ptr<Expression> test = this->expression();
std::unique_ptr<Expression> next = this->expression();
std::unique_ptr<Statement> body = this->statement();
std::unique_ptr<LoopUnrollInfo> unrollInfo =
Analysis::GetLoopUnrollInfo(Position(), ForLoopPositions{},
initializer.get(), test.get(), next.get(), body.get(), /*errors=*/nullptr);
return ForStatement::Make(this->context(), Position(), ForLoopPositions{},
std::move(initializer), std::move(test), std::move(next),
std::move(body), std::move(unrollInfo), fSymbolTable);
}
case Rehydrator::kIf_Command: {
bool isStatic = this->readU8();
std::unique_ptr<Expression> test = this->expression();
std::unique_ptr<Statement> ifTrue = this->statement();
std::unique_ptr<Statement> ifFalse = this->statement();
return IfStatement::Make(this->context(), Position(), isStatic, std::move(test),
std::move(ifTrue), std::move(ifFalse));
}
case Rehydrator::kNop_Command:
return std::make_unique<SkSL::Nop>();
case Rehydrator::kReturn_Command: {
std::unique_ptr<Expression> expr = this->expression();
return ReturnStatement::Make(Position(), std::move(expr));
}
case Rehydrator::kSwitch_Command: {
bool isStatic = this->readU8();
AutoRehydratorSymbolTable symbols(this);
std::unique_ptr<Expression> expr = this->expression();
int caseCount = this->readU8();
StatementArray cases;
cases.reserve_back(caseCount);
for (int i = 0; i < caseCount; ++i) {
bool isDefault = this->readU8();
if (isDefault) {
std::unique_ptr<Statement> statement = this->statement();
cases.push_back(SwitchCase::MakeDefault(Position(), std::move(statement)));
} else {
SKSL_INT value = this->readS32();
std::unique_ptr<Statement> statement = this->statement();
cases.push_back(SwitchCase::Make(Position(), std::move(value),
std::move(statement)));
}
}
return SwitchStatement::Make(this->context(), Position(), isStatic, std::move(expr),
std::move(cases), fSymbolTable);
}
case Rehydrator::kVarDeclaration_Command: {
Variable* var = this->symbolRef<Variable>();
const Type* baseType = this->type();
int arraySize = this->readU8();
std::unique_ptr<Expression> value = this->expression();
return VarDeclaration::Make(this->context(), var, baseType, arraySize,
std::move(value));
}
case Rehydrator::kVoid_Command:
return nullptr;
default:
printf("unsupported statement %d\n", kind);
SkASSERT(false);
return nullptr;
}
}
ExpressionArray Rehydrator::expressionArray() {
uint8_t count = this->readU8();
ExpressionArray array;
array.reserve_back(count);
for (int i = 0; i < count; ++i) {
array.push_back(this->expression());
}
return array;
}
std::unique_ptr<Expression> Rehydrator::expression() {
Position pos;
int kind = this->readU8();
switch (kind) {
case Rehydrator::kBinary_Command: {
std::unique_ptr<Expression> left = this->expression();
Operator::Kind op = (Operator::Kind)this->readU8();
std::unique_ptr<Expression> right = this->expression();
return BinaryExpression::Make(this->context(), pos, std::move(left), op,
std::move(right));
}
case Rehydrator::kBoolLiteral_Command: {
bool value = this->readU8();
return Literal::MakeBool(this->context(), pos, value);
}
case Rehydrator::kConstructorArray_Command: {
const Type* type = this->type();
return ConstructorArray::Make(this->context(), pos, *type, this->expressionArray());
}
case Rehydrator::kConstructorArrayCast_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();
SkASSERT(args.size() == 1);
return ConstructorArrayCast::Make(this->context(), pos, *type, std::move(args[0]));
}
case Rehydrator::kConstructorCompound_Command: {
const Type* type = this->type();
return ConstructorCompound::Make(this->context(), pos, *type, this->expressionArray());
}
case Rehydrator::kConstructorDiagonalMatrix_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();
SkASSERT(args.size() == 1);
return ConstructorDiagonalMatrix::Make(this->context(), pos, *type, std::move(args[0]));
}
case Rehydrator::kConstructorMatrixResize_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();
SkASSERT(args.size() == 1);
return ConstructorMatrixResize::Make(this->context(), pos, *type, std::move(args[0]));
}
case Rehydrator::kConstructorScalarCast_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();
SkASSERT(args.size() == 1);
return ConstructorScalarCast::Make(this->context(), pos, *type, std::move(args[0]));
}
case Rehydrator::kConstructorSplat_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();
SkASSERT(args.size() == 1);
return ConstructorSplat::Make(this->context(), pos, *type, std::move(args[0]));
}
case Rehydrator::kConstructorStruct_Command: {
const Type* type = this->type();
return ConstructorStruct::Make(this->context(), pos, *type, this->expressionArray());
}
case Rehydrator::kConstructorCompoundCast_Command: {
const Type* type = this->type();
ExpressionArray args = this->expressionArray();
SkASSERT(args.size() == 1);
return ConstructorCompoundCast::Make(this->context(), pos, *type, std::move(args[0]));
}
case Rehydrator::kFieldAccess_Command: {
std::unique_ptr<Expression> base = this->expression();
int index = this->readU8();
FieldAccess::OwnerKind ownerKind = (FieldAccess::OwnerKind) this->readU8();
return FieldAccess::Make(this->context(), pos, std::move(base), index, ownerKind);
}
case Rehydrator::kFloatLiteral_Command: {
const Type* type = this->type();
int32_t floatBits = this->readS32();
float value;
memcpy(&value, &floatBits, sizeof(value));
return Literal::MakeFloat(pos, value, type);
}
case Rehydrator::kFunctionCall_Command: {
const Type* type = this->type();
const Symbol* symbol = this->possiblyBuiltinSymbolRef();
ExpressionArray args = this->expressionArray();
const FunctionDeclaration* f;
if (symbol->is<FunctionDeclaration>()) {
f = &symbol->as<FunctionDeclaration>();
} else if (symbol->is<UnresolvedFunction>()) {
const UnresolvedFunction& unresolved = symbol->as<UnresolvedFunction>();
f = FunctionCall::FindBestFunctionForCall(this->context(), unresolved.functions(),
args);
SkASSERT(f);
} else {
SkASSERT(false);
return nullptr;
}
return FunctionCall::Make(this->context(), pos, type, *f, std::move(args));
}
case Rehydrator::kIndex_Command: {
std::unique_ptr<Expression> base = this->expression();
std::unique_ptr<Expression> index = this->expression();
return IndexExpression::Make(this->context(), pos, std::move(base), std::move(index));
}
case Rehydrator::kIntLiteral_Command: {
const Type* type = this->type();
if (type->isUnsigned()) {
unsigned int value = this->readU32();
return Literal::MakeInt(pos, value, type);
} else {
int value = this->readS32();
return Literal::MakeInt(pos, value, type);
}
}
case Rehydrator::kPostfix_Command: {
Operator::Kind op = (Operator::Kind)this->readU8();
std::unique_ptr<Expression> operand = this->expression();
return PostfixExpression::Make(this->context(), pos, std::move(operand), op);
}
case Rehydrator::kPrefix_Command: {
Operator::Kind op = (Operator::Kind)this->readU8();
std::unique_ptr<Expression> operand = this->expression();
return PrefixExpression::Make(this->context(), pos, op, std::move(operand));
}
case Rehydrator::kSetting_Command: {
std::string name(this->readString());
return Setting::Convert(this->context(), pos, name);
}
case Rehydrator::kSwizzle_Command: {
std::unique_ptr<Expression> base = this->expression();
int count = this->readU8();
ComponentArray components;
for (int i = 0; i < count; ++i) {
components.push_back(this->readU8());
}
return Swizzle::Make(this->context(), pos, std::move(base), components);
}
case Rehydrator::kTernary_Command: {
std::unique_ptr<Expression> test = this->expression();
std::unique_ptr<Expression> ifTrue = this->expression();
std::unique_ptr<Expression> ifFalse = this->expression();
return TernaryExpression::Make(this->context(), pos, std::move(test),
std::move(ifTrue), std::move(ifFalse));
}
case Rehydrator::kVariableReference_Command: {
const Variable* var = &this->possiblyBuiltinSymbolRef()->as<Variable>();
VariableReference::RefKind refKind = (VariableReference::RefKind) this->readU8();
return VariableReference::Make(pos, var, refKind);
}
case Rehydrator::kVoid_Command:
return nullptr;
default:
printf("unsupported expression %d\n", kind);
SkASSERT(false);
return nullptr;
}
}
std::shared_ptr<SymbolTable> Rehydrator::symbolTable() {
int command = this->readU8();
if (command == kVoid_Command) {
return nullptr;
}
SkASSERT(command == kSymbolTable_Command);
bool builtin = this->readU8();
uint16_t ownedCount = this->readU16();
fSymbolTable = std::make_shared<SymbolTable>(std::move(fSymbolTable), builtin);
std::vector<const Symbol*> ownedSymbols;
ownedSymbols.reserve(ownedCount);
for (int i = 0; i < ownedCount; ++i) {
ownedSymbols.push_back(this->symbol());
}
uint16_t symbolCount = this->readU16();
std::vector<std::pair<std::string_view, int>> symbols;
symbols.reserve(symbolCount);
for (int i = 0; i < symbolCount; ++i) {
int index = this->readU16();
if (index != kBuiltin_Symbol) {
fSymbolTable->addWithoutOwnership(ownedSymbols[index]);
} else {
std::string_view name = this->readString();
SymbolTable* root = fSymbolTable.get();
while (root->fParent) {
root = root->fParent.get();
}
const Symbol* s = (*root)[name];
SkASSERT(s);
fSymbolTable->addWithoutOwnership(s);
}
}
return fSymbolTable;
}
} // namespace SkSL