• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 Google LLC.
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 #include "src/sksl/SkSLRehydrator.h"
9 
10 #include <memory>
11 #include <unordered_set>
12 
13 #include "include/private/SkSLModifiers.h"
14 #include "include/private/SkSLProgramElement.h"
15 #include "include/private/SkSLStatement.h"
16 #include "src/sksl/SkSLAnalysis.h"
17 #include "src/sksl/ir/SkSLBinaryExpression.h"
18 #include "src/sksl/ir/SkSLBreakStatement.h"
19 #include "src/sksl/ir/SkSLConstructor.h"
20 #include "src/sksl/ir/SkSLConstructorArray.h"
21 #include "src/sksl/ir/SkSLConstructorCompound.h"
22 #include "src/sksl/ir/SkSLConstructorCompoundCast.h"
23 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
24 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
25 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
26 #include "src/sksl/ir/SkSLConstructorSplat.h"
27 #include "src/sksl/ir/SkSLConstructorStruct.h"
28 #include "src/sksl/ir/SkSLContinueStatement.h"
29 #include "src/sksl/ir/SkSLDiscardStatement.h"
30 #include "src/sksl/ir/SkSLDoStatement.h"
31 #include "src/sksl/ir/SkSLExpression.h"
32 #include "src/sksl/ir/SkSLExpressionStatement.h"
33 #include "src/sksl/ir/SkSLField.h"
34 #include "src/sksl/ir/SkSLFieldAccess.h"
35 #include "src/sksl/ir/SkSLForStatement.h"
36 #include "src/sksl/ir/SkSLFunctionCall.h"
37 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
38 #include "src/sksl/ir/SkSLFunctionDefinition.h"
39 #include "src/sksl/ir/SkSLIfStatement.h"
40 #include "src/sksl/ir/SkSLIndexExpression.h"
41 #include "src/sksl/ir/SkSLInlineMarker.h"
42 #include "src/sksl/ir/SkSLInterfaceBlock.h"
43 #include "src/sksl/ir/SkSLLiteral.h"
44 #include "src/sksl/ir/SkSLPostfixExpression.h"
45 #include "src/sksl/ir/SkSLPrefixExpression.h"
46 #include "src/sksl/ir/SkSLReturnStatement.h"
47 #include "src/sksl/ir/SkSLSetting.h"
48 #include "src/sksl/ir/SkSLStructDefinition.h"
49 #include "src/sksl/ir/SkSLSwitchCase.h"
50 #include "src/sksl/ir/SkSLSwitchStatement.h"
51 #include "src/sksl/ir/SkSLSwizzle.h"
52 #include "src/sksl/ir/SkSLSymbolAlias.h"
53 #include "src/sksl/ir/SkSLSymbolTable.h"
54 #include "src/sksl/ir/SkSLTernaryExpression.h"
55 #include "src/sksl/ir/SkSLType.h"
56 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
57 #include "src/sksl/ir/SkSLVarDeclarations.h"
58 #include "src/sksl/ir/SkSLVariable.h"
59 
60 namespace SkSL {
61 
62 class AutoRehydratorSymbolTable {
63 public:
AutoRehydratorSymbolTable(Rehydrator * rehydrator)64     AutoRehydratorSymbolTable(Rehydrator* rehydrator)
65         : fRehydrator(rehydrator)
66         , fOldSymbols(fRehydrator->fSymbolTable) {
67         fRehydrator->fSymbolTable = fRehydrator->symbolTable();
68     }
69 
~AutoRehydratorSymbolTable()70     ~AutoRehydratorSymbolTable() {
71         fRehydrator->fSymbolTable = std::move(fOldSymbols);
72     }
73 
74 private:
75     Rehydrator* fRehydrator;
76     std::shared_ptr<SymbolTable> fOldSymbols;
77 };
78 
Rehydrator(const Context * context,std::shared_ptr<SymbolTable> symbolTable,const uint8_t * src,size_t length)79 Rehydrator::Rehydrator(const Context* context,  std::shared_ptr<SymbolTable> symbolTable,
80                        const uint8_t* src, size_t length)
81                 : fContext(*context)
82                 , fSymbolTable(std::move(symbolTable))
83                 , fStart(src)
84     SkDEBUGCODE(, fEnd(fStart + length)) {
85     SkASSERT(fSymbolTable);
86     SkASSERT(fSymbolTable->isBuiltin());
87     // skip past string data
88     fIP = fStart;
89     fIP += this->readU16();
90 }
91 
layout()92 Layout Rehydrator::layout() {
93     switch (this->readU8()) {
94         case kBuiltinLayout_Command: {
95             Layout result;
96             result.fBuiltin = this->readS16();
97             return result;
98         }
99         case kDefaultLayout_Command:
100             return Layout();
101         case kLayout_Command: {
102             int flags = this->readU32();
103             int location = this->readS8();
104             int offset = this->readS8();
105             int binding = this->readS8();
106             int index = this->readS8();
107             int set = this->readS8();
108             int builtin = this->readS16();
109             int inputAttachmentIndex = this->readS8();
110             return Layout(
111                     flags, location, offset, binding, index, set, builtin, inputAttachmentIndex);
112         }
113         default:
114             SkASSERT(false);
115             return Layout();
116     }
117 }
118 
modifiers()119 Modifiers Rehydrator::modifiers() {
120     switch (this->readU8()) {
121         case kDefaultModifiers_Command:
122             return Modifiers();
123         case kModifiers8Bit_Command: {
124             Layout l = this->layout();
125             int flags = this->readU8();
126             return Modifiers(l, flags);
127         }
128         case kModifiers_Command: {
129             Layout l = this->layout();
130             int flags = this->readS32();
131             return Modifiers(l, flags);
132         }
133         default:
134             SkASSERT(false);
135             return Modifiers();
136     }
137 }
138 
symbol()139 const Symbol* Rehydrator::symbol() {
140     int kind = this->readU8();
141     switch (kind) {
142         case kArrayType_Command: {
143             uint16_t id = this->readU16();
144             const Type* componentType = this->type();
145             int8_t count = this->readS8();
146             const String* arrayName =
147                     fSymbolTable->takeOwnershipOfString(componentType->getArrayName(count));
148             const Type* result = fSymbolTable->takeOwnershipOfSymbol(
149                     Type::MakeArrayType(*arrayName, *componentType, count));
150             this->addSymbol(id, result);
151             return result;
152         }
153         case kFunctionDeclaration_Command: {
154             uint16_t id = this->readU16();
155             Modifiers modifiers = this->modifiers();
156             skstd::string_view name = this->readString();
157             int parameterCount = this->readU8();
158             std::vector<const Variable*> parameters;
159             parameters.reserve(parameterCount);
160             for (int i = 0; i < parameterCount; ++i) {
161                 parameters.push_back(this->symbolRef<Variable>(Symbol::Kind::kVariable));
162             }
163             const Type* returnType = this->type();
164             const FunctionDeclaration* result =
165                     fSymbolTable->takeOwnershipOfSymbol(std::make_unique<FunctionDeclaration>(
166                             /*line=*/-1,
167                             this->modifiersPool().add(modifiers),
168                             name,
169                             std::move(parameters),
170                             returnType,
171                             /*builtin=*/true));
172             this->addSymbol(id, result);
173             return result;
174         }
175         case kField_Command: {
176             const Variable* owner = this->symbolRef<Variable>(Symbol::Kind::kVariable);
177             uint8_t index = this->readU8();
178             const Field* result = fSymbolTable->takeOwnershipOfSymbol(
179                     std::make_unique<Field>(/*line=*/-1, owner, index));
180             return result;
181         }
182         case kStructType_Command: {
183             uint16_t id = this->readU16();
184             String name(this->readString());
185             uint8_t fieldCount = this->readU8();
186             std::vector<Type::Field> fields;
187             fields.reserve(fieldCount);
188             for (int i = 0; i < fieldCount; ++i) {
189                 Modifiers m = this->modifiers();
190                 skstd::string_view fieldName = this->readString();
191                 const Type* type = this->type();
192                 fields.emplace_back(m, fieldName, type);
193             }
194             skstd::string_view nameChars(*fSymbolTable->takeOwnershipOfString(std::move(name)));
195             const Type* result = fSymbolTable->takeOwnershipOfSymbol(
196                     Type::MakeStructType(/*line=*/-1, nameChars, std::move(fields)));
197             this->addSymbol(id, result);
198             return result;
199         }
200         case kSymbolRef_Command: {
201             uint16_t id = this->readU16();
202             SkASSERT(fSymbols.size() > id);
203             return fSymbols[id];
204         }
205         case kSymbolAlias_Command: {
206             uint16_t id = this->readU16();
207             skstd::string_view name = this->readString();
208             const Symbol* origSymbol = this->symbol();
209             const SymbolAlias* symbolAlias = fSymbolTable->takeOwnershipOfSymbol(
210                     std::make_unique<SymbolAlias>(/*line=*/-1, name, origSymbol));
211             this->addSymbol(id, symbolAlias);
212             return symbolAlias;
213         }
214         case kSystemType_Command: {
215             uint16_t id = this->readU16();
216             skstd::string_view name = this->readString();
217             const Symbol* result = (*fSymbolTable)[name];
218             SkASSERT(result && result->kind() == Symbol::Kind::kType);
219             this->addSymbol(id, result);
220             return result;
221         }
222         case kUnresolvedFunction_Command: {
223             uint16_t id = this->readU16();
224             int length = this->readU8();
225             std::vector<const FunctionDeclaration*> functions;
226             functions.reserve(length);
227             for (int i = 0; i < length; ++i) {
228                 const Symbol* f = this->symbol();
229                 SkASSERT(f && f->kind() == Symbol::Kind::kFunctionDeclaration);
230                 functions.push_back((const FunctionDeclaration*) f);
231             }
232             const UnresolvedFunction* result = fSymbolTable->takeOwnershipOfSymbol(
233                     std::make_unique<UnresolvedFunction>(std::move(functions)));
234             this->addSymbol(id, result);
235             return result;
236         }
237         case kVariable_Command: {
238             uint16_t id = this->readU16();
239             const Modifiers* m = this->modifiersPool().add(this->modifiers());
240             skstd::string_view name = this->readString();
241             const Type* type = this->type();
242             Variable::Storage storage = (Variable::Storage) this->readU8();
243             const Variable* result = fSymbolTable->takeOwnershipOfSymbol(std::make_unique<Variable>(
244                     /*line=*/-1, m, name, type, /*builtin=*/true, storage));
245             this->addSymbol(id, result);
246             return result;
247         }
248         default:
249             printf("unsupported symbol %d\n", kind);
250             SkASSERT(false);
251             return nullptr;
252     }
253 }
254 
type()255 const Type* Rehydrator::type() {
256     const Symbol* result = this->symbol();
257     SkASSERT(result->kind() == Symbol::Kind::kType);
258     return (const Type*) result;
259 }
260 
elements()261 std::vector<std::unique_ptr<ProgramElement>> Rehydrator::elements() {
262     SkDEBUGCODE(uint8_t command = )this->readU8();
263     SkASSERT(command == kElements_Command);
264     std::vector<std::unique_ptr<ProgramElement>> result;
265     while (std::unique_ptr<ProgramElement> elem = this->element()) {
266         result.push_back(std::move(elem));
267     }
268     return result;
269 }
270 
element()271 std::unique_ptr<ProgramElement> Rehydrator::element() {
272     int kind = this->readU8();
273     switch (kind) {
274         case Rehydrator::kFunctionDefinition_Command: {
275             const FunctionDeclaration* decl = this->symbolRef<FunctionDeclaration>(
276                                                                 Symbol::Kind::kFunctionDeclaration);
277             std::unique_ptr<Statement> body = this->statement();
278             auto result = FunctionDefinition::Convert(fContext, /*line=*/-1, *decl,
279                                                       std::move(body), /*builtin=*/true);
280             decl->setDefinition(result.get());
281             return std::move(result);
282         }
283         case Rehydrator::kInterfaceBlock_Command: {
284             const Symbol* var = this->symbol();
285             SkASSERT(var && var->is<Variable>());
286             skstd::string_view typeName = this->readString();
287             skstd::string_view instanceName = this->readString();
288             int arraySize = this->readS8();
289             return std::make_unique<InterfaceBlock>(/*line=*/-1, var->as<Variable>(), typeName,
290                                                     instanceName, arraySize, nullptr);
291         }
292         case Rehydrator::kVarDeclarations_Command: {
293             std::unique_ptr<Statement> decl = this->statement();
294             return std::make_unique<GlobalVarDeclaration>(std::move(decl));
295         }
296         case Rehydrator::kStructDefinition_Command: {
297             const Symbol* type = this->symbol();
298             SkASSERT(type && type->is<Type>());
299             return std::make_unique<StructDefinition>(/*line=*/-1, type->as<Type>());
300         }
301         case Rehydrator::kElementsComplete_Command:
302             return nullptr;
303         default:
304             SkDEBUGFAILF("unsupported element %d\n", kind);
305             return nullptr;
306     }
307 }
308 
statement()309 std::unique_ptr<Statement> Rehydrator::statement() {
310     int kind = this->readU8();
311     switch (kind) {
312         case Rehydrator::kBlock_Command: {
313             AutoRehydratorSymbolTable symbols(this);
314             int count = this->readU8();
315             StatementArray statements;
316             statements.reserve_back(count);
317             for (int i = 0; i < count; ++i) {
318                 statements.push_back(this->statement());
319             }
320             bool isScope = this->readU8();
321             return Block::Make(/*line=*/-1, std::move(statements), fSymbolTable, isScope);
322         }
323         case Rehydrator::kBreak_Command:
324             return BreakStatement::Make(/*line=*/-1);
325         case Rehydrator::kContinue_Command:
326             return ContinueStatement::Make(/*line=*/-1);
327         case Rehydrator::kDiscard_Command:
328             return DiscardStatement::Make(/*line=*/-1);
329         case Rehydrator::kDo_Command: {
330             std::unique_ptr<Statement> stmt = this->statement();
331             std::unique_ptr<Expression> expr = this->expression();
332             return DoStatement::Make(fContext, std::move(stmt), std::move(expr));
333         }
334         case Rehydrator::kExpressionStatement_Command: {
335             std::unique_ptr<Expression> expr = this->expression();
336             return ExpressionStatement::Make(fContext, std::move(expr));
337         }
338         case Rehydrator::kFor_Command: {
339             std::unique_ptr<Statement> initializer = this->statement();
340             std::unique_ptr<Expression> test = this->expression();
341             std::unique_ptr<Expression> next = this->expression();
342             std::unique_ptr<Statement> body = this->statement();
343             std::shared_ptr<SymbolTable> symbols = this->symbolTable();
344             std::unique_ptr<LoopUnrollInfo> unrollInfo =
345                     Analysis::GetLoopUnrollInfo(/*line=*/-1, initializer.get(), test.get(),
346                                                 next.get(), body.get(), /*errors=*/nullptr);
347             return ForStatement::Make(fContext, /*line=*/-1, std::move(initializer),
348                                       std::move(test), std::move(next), std::move(body),
349                                       std::move(unrollInfo), std::move(symbols));
350         }
351         case Rehydrator::kIf_Command: {
352             bool isStatic = this->readU8();
353             std::unique_ptr<Expression> test = this->expression();
354             std::unique_ptr<Statement> ifTrue = this->statement();
355             std::unique_ptr<Statement> ifFalse = this->statement();
356             return IfStatement::Make(fContext, /*line=*/-1, isStatic, std::move(test),
357                                      std::move(ifTrue), std::move(ifFalse));
358         }
359         case Rehydrator::kInlineMarker_Command: {
360             const FunctionDeclaration* funcDecl = this->symbolRef<FunctionDeclaration>(
361                                                           Symbol::Kind::kFunctionDeclaration);
362             return InlineMarker::Make(funcDecl);
363         }
364         case Rehydrator::kReturn_Command: {
365             std::unique_ptr<Expression> expr = this->expression();
366             return ReturnStatement::Make(/*line=*/-1, std::move(expr));
367         }
368         case Rehydrator::kSwitch_Command: {
369             bool isStatic = this->readU8();
370             AutoRehydratorSymbolTable symbols(this);
371             std::unique_ptr<Expression> expr = this->expression();
372             int caseCount = this->readU8();
373             StatementArray cases;
374             cases.reserve_back(caseCount);
375             for (int i = 0; i < caseCount; ++i) {
376                 std::unique_ptr<Expression> value = this->expression();
377                 std::unique_ptr<Statement> statement = this->statement();
378                 cases.push_back(std::make_unique<SwitchCase>(/*line=*/-1, std::move(value),
379                                                              std::move(statement)));
380             }
381             return SwitchStatement::Make(fContext, /*line=*/-1, isStatic, std::move(expr),
382                                          std::move(cases), fSymbolTable);
383         }
384         case Rehydrator::kVarDeclaration_Command: {
385             Variable* var = this->symbolRef<Variable>(Symbol::Kind::kVariable);
386             const Type* baseType = this->type();
387             int arraySize = this->readS8();
388             std::unique_ptr<Expression> value = this->expression();
389             return VarDeclaration::Make(fContext, var, baseType, arraySize, std::move(value));
390         }
391         case Rehydrator::kVoid_Command:
392             return nullptr;
393         default:
394             printf("unsupported statement %d\n", kind);
395             SkASSERT(false);
396             return nullptr;
397     }
398 }
399 
expressionArray()400 ExpressionArray Rehydrator::expressionArray() {
401     uint8_t count = this->readU8();
402     ExpressionArray array;
403     array.reserve_back(count);
404     for (int i = 0; i < count; ++i) {
405         array.push_back(this->expression());
406     }
407     return array;
408 }
409 
expression()410 std::unique_ptr<Expression> Rehydrator::expression() {
411     int kind = this->readU8();
412     switch (kind) {
413         case Rehydrator::kBinary_Command: {
414             std::unique_ptr<Expression> left = this->expression();
415             Token::Kind op = (Token::Kind) this->readU8();
416             std::unique_ptr<Expression> right = this->expression();
417             return BinaryExpression::Make(fContext, std::move(left), op, std::move(right));
418         }
419         case Rehydrator::kBoolLiteral_Command: {
420             bool value = this->readU8();
421             return Literal::MakeBool(fContext, /*line=*/-1, value);
422         }
423         case Rehydrator::kConstructorArray_Command: {
424             const Type* type = this->type();
425             return ConstructorArray::Make(fContext, /*line=*/-1, *type, this->expressionArray());
426         }
427         case Rehydrator::kConstructorCompound_Command: {
428             const Type* type = this->type();
429             return ConstructorCompound::Make(fContext, /*line=*/-1, *type,
430                                               this->expressionArray());
431         }
432         case Rehydrator::kConstructorDiagonalMatrix_Command: {
433             const Type* type = this->type();
434             ExpressionArray args = this->expressionArray();
435             SkASSERT(args.size() == 1);
436             return ConstructorDiagonalMatrix::Make(fContext, /*line=*/-1, *type,
437                                                    std::move(args[0]));
438         }
439         case Rehydrator::kConstructorMatrixResize_Command: {
440             const Type* type = this->type();
441             ExpressionArray args = this->expressionArray();
442             SkASSERT(args.size() == 1);
443             return ConstructorMatrixResize::Make(fContext, /*line=*/-1, *type,
444                                                  std::move(args[0]));
445         }
446         case Rehydrator::kConstructorScalarCast_Command: {
447             const Type* type = this->type();
448             ExpressionArray args = this->expressionArray();
449             SkASSERT(args.size() == 1);
450             return ConstructorScalarCast::Make(fContext, /*line=*/-1, *type, std::move(args[0]));
451         }
452         case Rehydrator::kConstructorSplat_Command: {
453             const Type* type = this->type();
454             ExpressionArray args = this->expressionArray();
455             SkASSERT(args.size() == 1);
456             return ConstructorSplat::Make(fContext, /*line=*/-1, *type, std::move(args[0]));
457         }
458         case Rehydrator::kConstructorStruct_Command: {
459             const Type* type = this->type();
460             return ConstructorStruct::Make(fContext, /*line=*/-1, *type, this->expressionArray());
461         }
462         case Rehydrator::kConstructorCompoundCast_Command: {
463             const Type* type = this->type();
464             ExpressionArray args = this->expressionArray();
465             SkASSERT(args.size() == 1);
466             return ConstructorCompoundCast::Make(fContext,/*line=*/-1, *type, std::move(args[0]));
467         }
468         case Rehydrator::kFieldAccess_Command: {
469             std::unique_ptr<Expression> base = this->expression();
470             int index = this->readU8();
471             FieldAccess::OwnerKind ownerKind = (FieldAccess::OwnerKind) this->readU8();
472             return FieldAccess::Make(fContext, std::move(base), index, ownerKind);
473         }
474         case Rehydrator::kFloatLiteral_Command: {
475             const Type* type = this->type();
476             int32_t floatBits = this->readS32();
477             float value;
478             memcpy(&value, &floatBits, sizeof(value));
479             return Literal::MakeFloat(/*line=*/-1, value, type);
480         }
481         case Rehydrator::kFunctionCall_Command: {
482             const Type* type = this->type();
483             const FunctionDeclaration* f = this->symbolRef<FunctionDeclaration>(
484                                                                 Symbol::Kind::kFunctionDeclaration);
485             ExpressionArray args = this->expressionArray();
486             return FunctionCall::Make(fContext, /*line=*/-1, type, *f, std::move(args));
487         }
488         case Rehydrator::kIndex_Command: {
489             std::unique_ptr<Expression> base = this->expression();
490             std::unique_ptr<Expression> index = this->expression();
491             return IndexExpression::Make(fContext, std::move(base), std::move(index));
492         }
493         case Rehydrator::kIntLiteral_Command: {
494             const Type* type = this->type();
495             int value = this->readS32();
496             return Literal::MakeInt(/*line=*/-1, value, type);
497         }
498         case Rehydrator::kPostfix_Command: {
499             Token::Kind op = (Token::Kind) this->readU8();
500             std::unique_ptr<Expression> operand = this->expression();
501             return PostfixExpression::Make(fContext, std::move(operand), op);
502         }
503         case Rehydrator::kPrefix_Command: {
504             Token::Kind op = (Token::Kind) this->readU8();
505             std::unique_ptr<Expression> operand = this->expression();
506             return PrefixExpression::Make(fContext, op, std::move(operand));
507         }
508         case Rehydrator::kSetting_Command: {
509             String name(this->readString());
510             return Setting::Convert(fContext, /*line=*/-1, name);
511         }
512         case Rehydrator::kSwizzle_Command: {
513             std::unique_ptr<Expression> base = this->expression();
514             int count = this->readU8();
515             ComponentArray components;
516             for (int i = 0; i < count; ++i) {
517                 components.push_back(this->readU8());
518             }
519             return Swizzle::Make(fContext, std::move(base), components);
520         }
521         case Rehydrator::kTernary_Command: {
522             std::unique_ptr<Expression> test = this->expression();
523             std::unique_ptr<Expression> ifTrue = this->expression();
524             std::unique_ptr<Expression> ifFalse = this->expression();
525             return TernaryExpression::Make(fContext, std::move(test),
526                                            std::move(ifTrue), std::move(ifFalse));
527         }
528         case Rehydrator::kVariableReference_Command: {
529             const Variable* var = this->symbolRef<Variable>(Symbol::Kind::kVariable);
530             VariableReference::RefKind refKind = (VariableReference::RefKind) this->readU8();
531             return VariableReference::Make(/*line=*/-1, var, refKind);
532         }
533         case Rehydrator::kVoid_Command:
534             return nullptr;
535         default:
536             printf("unsupported expression %d\n", kind);
537             SkASSERT(false);
538             return nullptr;
539     }
540 }
541 
symbolTable(bool inherit)542 std::shared_ptr<SymbolTable> Rehydrator::symbolTable(bool inherit) {
543     int command = this->readU8();
544     if (command == kVoid_Command) {
545         return nullptr;
546     }
547     SkASSERT(command == kSymbolTable_Command);
548     uint16_t ownedCount = this->readU16();
549     std::shared_ptr<SymbolTable> oldTable = fSymbolTable;
550     std::shared_ptr<SymbolTable> result =
551             inherit ? std::make_shared<SymbolTable>(fSymbolTable, /*builtin=*/true)
552                     : std::make_shared<SymbolTable>(fContext, /*builtin=*/true);
553     fSymbolTable = result;
554     std::vector<const Symbol*> ownedSymbols;
555     ownedSymbols.reserve(ownedCount);
556     for (int i = 0; i < ownedCount; ++i) {
557         ownedSymbols.push_back(this->symbol());
558     }
559     uint16_t symbolCount = this->readU16();
560     std::vector<std::pair<skstd::string_view, int>> symbols;
561     symbols.reserve(symbolCount);
562     for (int i = 0; i < symbolCount; ++i) {
563         int index = this->readU16();
564         fSymbolTable->addWithoutOwnership(ownedSymbols[index]);
565     }
566     fSymbolTable = oldTable;
567     return result;
568 }
569 
570 }  // namespace SkSL
571