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