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 "src/sksl/ir/SkSLBinaryExpression.h"
14#include "src/sksl/ir/SkSLBreakStatement.h"
15#include "src/sksl/ir/SkSLContinueStatement.h"
16#include "src/sksl/ir/SkSLDiscardStatement.h"
17#include "src/sksl/ir/SkSLDoStatement.h"
18#include "src/sksl/ir/SkSLEnum.h"
19#include "src/sksl/ir/SkSLExpression.h"
20#include "src/sksl/ir/SkSLExpressionStatement.h"
21#include "src/sksl/ir/SkSLField.h"
22#include "src/sksl/ir/SkSLFieldAccess.h"
23#include "src/sksl/ir/SkSLFloatLiteral.h"
24#include "src/sksl/ir/SkSLForStatement.h"
25#include "src/sksl/ir/SkSLFunctionCall.h"
26#include "src/sksl/ir/SkSLFunctionDeclaration.h"
27#include "src/sksl/ir/SkSLFunctionDefinition.h"
28#include "src/sksl/ir/SkSLIfStatement.h"
29#include "src/sksl/ir/SkSLIndexExpression.h"
30#include "src/sksl/ir/SkSLIntLiteral.h"
31#include "src/sksl/ir/SkSLInterfaceBlock.h"
32#include "src/sksl/ir/SkSLModifiers.h"
33#include "src/sksl/ir/SkSLNullLiteral.h"
34#include "src/sksl/ir/SkSLPostfixExpression.h"
35#include "src/sksl/ir/SkSLPrefixExpression.h"
36#include "src/sksl/ir/SkSLProgramElement.h"
37#include "src/sksl/ir/SkSLReturnStatement.h"
38#include "src/sksl/ir/SkSLSetting.h"
39#include "src/sksl/ir/SkSLStatement.h"
40#include "src/sksl/ir/SkSLSwitchCase.h"
41#include "src/sksl/ir/SkSLSwitchStatement.h"
42#include "src/sksl/ir/SkSLSwizzle.h"
43#include "src/sksl/ir/SkSLSymbolTable.h"
44#include "src/sksl/ir/SkSLTernaryExpression.h"
45#include "src/sksl/ir/SkSLType.h"
46#include "src/sksl/ir/SkSLUnresolvedFunction.h"
47#include "src/sksl/ir/SkSLVarDeclarations.h"
48#include "src/sksl/ir/SkSLVarDeclarationsStatement.h"
49#include "src/sksl/ir/SkSLVariable.h"
50#include "src/sksl/ir/SkSLWhileStatement.h"
51
52namespace SkSL {
53
54class AutoRehydratorSymbolTable {
55public:
56 AutoRehydratorSymbolTable(Rehydrator* rehydrator)
57 : fRehydrator(rehydrator)
58 , fOldSymbols(fRehydrator->fSymbolTable) {
59 fRehydrator->fSymbolTable = fRehydrator->symbolTable();
60 }
61
62 ~AutoRehydratorSymbolTable() {
63 fRehydrator->fSymbolTable = std::move(fOldSymbols);
64 }
65
66private:
67 Rehydrator* fRehydrator;
68 std::shared_ptr<SymbolTable> fOldSymbols;
69};
70
71Layout Rehydrator::layout() {
72 switch (this->readU8()) {
73 case kBuiltinLayout_Command: {
74 Layout result;
75 result.fBuiltin = this->readS16();
76 return result;
77 }
78 case kDefaultLayout_Command:
79 return Layout();
80 case kLayout_Command: {
81 int flags = this->readU32();
82 int location = this->readS8();
83 int offset = this->readS8();
84 int binding = this->readS8();
85 int index = this->readS8();
86 int set = this->readS8();
87 int builtin = this->readS16();
88 int inputAttachmentIndex = this->readS8();
89 int format = this->readS8();
90 int primitive = this->readS8();
91 int maxVertices = this->readS8();
92 int invocations = this->readS8();
93 StringFragment marker = this->readString();
94 StringFragment when = this->readString();
95 int key = this->readS8();
96 int ctype = this->readS8();
97 return Layout(flags, location, offset, binding, index, set, builtin,
98 inputAttachmentIndex, (Layout::Format) format,
99 (Layout::Primitive) primitive, maxVertices, invocations, marker, when,
100 (Layout::Key) key, (Layout::CType) ctype);
101 }
102 default:
103 SkASSERT(false);
104 return Layout();
105 }
106}
107
108Modifiers Rehydrator::modifiers() {
109 switch (this->readU8()) {
110 case kDefaultModifiers_Command:
111 return Modifiers();
112 case kModifiers8Bit_Command: {
113 Layout l = this->layout();
114 int flags = this->readU8();
115 return Modifiers(l, flags);
116 }
117 case kModifiers_Command: {
118 Layout l = this->layout();
119 int flags = this->readS32();
120 return Modifiers(l, flags);
121 }
122 default:
123 SkASSERT(false);
124 return Modifiers();
125 }
126}
127
128const Symbol* Rehydrator::symbol() {
129 int kind = this->readU8();
130 switch (kind) {
131 case kArrayType_Command: {
132 uint16_t id = this->readU16();
133 const Type* componentType = this->type();
134 uint8_t count = this->readU8();
135 const Type* result = fSymbolTable->takeOwnershipOfSymbol(
136 std::make_unique<Type>(componentType->name() + "[" + to_string(count) + "]",
137 Type::kArray_Kind, *componentType, count));
138 this->addSymbol(id, result);
139 return result;
140 }
141 case kEnumType_Command: {
142 uint16_t id = this->readU16();
143 StringFragment name = this->readString();
144 const Type* result = fSymbolTable->takeOwnershipOfSymbol(
145 std::make_unique<Type>(name, Type::kEnum_Kind));
146 this->addSymbol(id, result);
147 return result;
148 }
149 case kFunctionDeclaration_Command: {
150 uint16_t id = this->readU16();
151 Modifiers modifiers = this->modifiers();
152 StringFragment name = this->readString();
153 int parameterCount = this->readU8();
154 std::vector<const Variable*> parameters;
155 parameters.reserve(parameterCount);
156 for (int i = 0; i < parameterCount; ++i) {
157 parameters.push_back(this->symbolRef<Variable>(Symbol::kVariable_Kind));
158 }
159 const Type* returnType = this->type();
160 const FunctionDeclaration* result =
161 fSymbolTable->takeOwnershipOfSymbol(std::make_unique<FunctionDeclaration>(
162 /*offset=*/-1, modifiers, name, std::move(parameters), *returnType,
163 /*builtin=*/true));
164 this->addSymbol(id, result);
165 return result;
166 }
167 case kField_Command: {
168 const Variable* owner = this->symbolRef<Variable>(Symbol::kVariable_Kind);
169 uint8_t index = this->readU8();
170 const Field* result = fSymbolTable->takeOwnershipOfSymbol(
171 std::make_unique<Field>(/*offset=*/-1, *owner, index));
172 return result;
173 }
174 case kNullableType_Command: {
175 uint16_t id = this->readU16();
176 const Type* base = this->type();
177 const Type* result = fSymbolTable->takeOwnershipOfSymbol(
178 std::make_unique<Type>(base->name() + "?", Type::kNullable_Kind, *base));
179 this->addSymbol(id, result);
180 return result;
181 }
182 case kStructType_Command: {
183 uint16_t id = this->readU16();
184 StringFragment 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 StringFragment name = this->readString();
191 const Type* type = this->type();
192 fields.emplace_back(m, name, type);
193 }
194 const Type* result = fSymbolTable->takeOwnershipOfSymbol(
195 std::make_unique<Type>(/*offset=*/-1, name, std::move(fields)));
196 this->addSymbol(id, result);
197 return result;
198 }
199 case kSymbolRef_Command: {
200 uint16_t id = this->readU16();
201 SkASSERT(fSymbols.size() > id);
202 return fSymbols[id];
203 }
204 case kSystemType_Command: {
205 uint16_t id = this->readU16();
206 StringFragment name = this->readString();
207 const Symbol* result = (*fSymbolTable)[name];
208 SkASSERT(result && result->fKind == Symbol::kType_Kind);
209 this->addSymbol(id, result);
210 return result;
211 }
212 case kUnresolvedFunction_Command: {
213 uint16_t id = this->readU16();
214 int length = this->readU8();
215 std::vector<const FunctionDeclaration*> functions;
216 functions.reserve(length);
217 for (int i = 0; i < length; ++i) {
218 const Symbol* f = this->symbol();
219 SkASSERT(f && f->fKind == Symbol::kFunctionDeclaration_Kind);
220 functions.push_back((const FunctionDeclaration*) f);
221 }
222 const UnresolvedFunction* result = fSymbolTable->takeOwnershipOfSymbol(
223 std::make_unique<UnresolvedFunction>(std::move(functions)));
224 this->addSymbol(id, result);
225 return result;
226 }
227 case kVariable_Command: {
228 uint16_t id = this->readU16();
229 Modifiers m = this->modifiers();
230 StringFragment name = this->readString();
231 const Type* type = this->type();
232 Variable::Storage storage = (Variable::Storage) this->readU8();
233 const Variable* result = fSymbolTable->takeOwnershipOfSymbol(
234 std::make_unique<Variable>(/*offset=*/-1, m, name, *type, storage));
235 this->addSymbol(id, result);
236 return result;
237 }
238 default:
239 printf("unsupported symbol %d\n", kind);
240 SkASSERT(false);
241 return nullptr;
242 }
243}
244
245const Type* Rehydrator::type() {
246 const Symbol* result = this->symbol();
247 SkASSERT(result->fKind == Symbol::kType_Kind);
248 return (const Type*) result;
249}
250
251std::vector<std::unique_ptr<ProgramElement>> Rehydrator::elements() {
252 SkDEBUGCODE(uint8_t command = )this->readU8();
253 SkASSERT(command == kElements_Command);
254 uint8_t count = this->readU8();
255 std::vector<std::unique_ptr<ProgramElement>> result;
256 result.reserve(count);
257 for (int i = 0; i < count; ++i) {
258 result.push_back(this->element());
259 }
260 return result;
261}
262
263std::unique_ptr<ProgramElement> Rehydrator::element() {
264 int kind = this->readU8();
265 switch (kind) {
266 case Rehydrator::kEnum_Command: {
267 StringFragment typeName = this->readString();
268 std::shared_ptr<SymbolTable> symbols = this->symbolTable();
269 for (auto& s : symbols->fOwnedSymbols) {
270 SkASSERT(s->fKind == Symbol::kVariable_Kind);
271 Variable& v = (Variable&) *s;
272 int value = this->readS32();
273 v.fInitialValue = symbols->takeOwnershipOfIRNode(
274 std::make_unique<IntLiteral>(fContext, /*offset=*/-1, value));
275 v.fWriteCount = 1;
276 }
277 return std::unique_ptr<ProgramElement>(new Enum(-1, typeName, std::move(symbols)));
278 }
279 case Rehydrator::kFunctionDefinition_Command: {
280 const FunctionDeclaration* decl = this->symbolRef<FunctionDeclaration>(
281 Symbol::kFunctionDeclaration_Kind);
282 std::unique_ptr<Statement> body = this->statement();
283 std::unordered_set<const FunctionDeclaration*> refs;
284 uint8_t refCount = this->readU8();
285 for (int i = 0; i < refCount; ++i) {
286 refs.insert(this->symbolRef<FunctionDeclaration>(
287 Symbol::kFunctionDeclaration_Kind));
288 }
289 FunctionDefinition* result = new FunctionDefinition(-1, *decl, std::move(body),
290 std::move(refs));
291 decl->fDefinition = result;
292 return std::unique_ptr<ProgramElement>(result);
293 }
294 case Rehydrator::kInterfaceBlock_Command: {
295 const Symbol* var = this->symbol();
296 SkASSERT(var && var->fKind == Symbol::kVariable_Kind);
297 StringFragment typeName = this->readString();
298 StringFragment instanceName = this->readString();
299 uint8_t sizeCount = this->readU8();
300 std::vector<std::unique_ptr<Expression>> sizes;
301 sizes.reserve(sizeCount);
302 for (int i = 0; i < sizeCount; ++i) {
303 sizes.push_back(this->expression());
304 }
305 return std::unique_ptr<ProgramElement>(new InterfaceBlock(-1, (Variable*) var, typeName,
306 instanceName,
307 std::move(sizes), nullptr));
308 }
309 case Rehydrator::kVarDeclarations_Command: {
310 const Type* baseType = this->type();
311 int count = this->readU8();
312 std::vector<std::unique_ptr<VarDeclaration>> vars;
313 vars.reserve(count);
314 for (int i = 0 ; i < count; ++i) {
315 std::unique_ptr<Statement> s = this->statement();
316 SkASSERT(s->fKind == Statement::kVarDeclaration_Kind);
317 vars.emplace_back((VarDeclaration*) s.release());
318 }
319 return std::unique_ptr<ProgramElement>(new VarDeclarations(-1, baseType,
320 std::move(vars)));
321 }
322 default:
323 printf("unsupported element %d\n", kind);
324 SkASSERT(false);
325 return nullptr;
326 }
327}
328
329std::unique_ptr<Statement> Rehydrator::statement() {
330 int kind = this->readU8();
331 switch (kind) {
332 case Rehydrator::kBlock_Command: {
333 AutoRehydratorSymbolTable symbols(this);
334 int count = this->readU8();
335 std::vector<std::unique_ptr<Statement>> statements;
336 statements.reserve(count);
337 for (int i = 0; i < count; ++i) {
338 statements.push_back(this->statement());
339 }
340 bool isScope = this->readU8();
341 return std::unique_ptr<Statement>(new Block(-1, std::move(statements), fSymbolTable,
342 isScope));
343 }
344 case Rehydrator::kBreak_Command:
345 return std::unique_ptr<Statement>(new BreakStatement(-1));
346 case Rehydrator::kContinue_Command:
347 return std::unique_ptr<Statement>(new ContinueStatement(-1));
348 case Rehydrator::kDiscard_Command:
349 return std::unique_ptr<Statement>(new DiscardStatement(-1));
350 case Rehydrator::kDo_Command: {
351 std::unique_ptr<Statement> stmt = this->statement();
352 std::unique_ptr<Expression> expr = this->expression();
353 return std::unique_ptr<Statement>(new DoStatement(-1, std::move(stmt),
354 std::move(expr)));
355 }
356 case Rehydrator::kExpressionStatement_Command: {
357 std::unique_ptr<Expression> expr = this->expression();
358 return std::unique_ptr<Statement>(new ExpressionStatement(std::move(expr)));
359 }
360 case Rehydrator::kFor_Command: {
361 std::unique_ptr<Statement> initializer = this->statement();
362 std::unique_ptr<Expression> test = this->expression();
363 std::unique_ptr<Expression> next = this->expression();
364 std::unique_ptr<Statement> body = this->statement();
365 std::shared_ptr<SymbolTable> symbols = this->symbolTable();
366 return std::unique_ptr<Statement>(new ForStatement(-1, std::move(initializer),
367 std::move(test), std::move(next),
368 std::move(body),
369 std::move(symbols)));
370 }
371 case Rehydrator::kIf_Command: {
372 bool isStatic = this->readU8();
373 std::unique_ptr<Expression> test = this->expression();
374 std::unique_ptr<Statement> ifTrue = this->statement();
375 std::unique_ptr<Statement> ifFalse = this->statement();
376 return std::unique_ptr<Statement>(new IfStatement(-1, isStatic, std::move(test),
377 std::move(ifTrue),
378 std::move(ifFalse)));
379 }
380 case Rehydrator::kReturn_Command: {
381 std::unique_ptr<Expression> expr = this->expression();
382 if (expr) {
383 return std::unique_ptr<Statement>(new ReturnStatement(std::move(expr)));
384 } else {
385 return std::unique_ptr<Statement>(new ReturnStatement(-1));
386 }
387 }
388 case Rehydrator::kSwitch_Command: {
389 bool isStatic = this->readU8();
390 AutoRehydratorSymbolTable symbols(this);
391 std::unique_ptr<Expression> expr = this->expression();
392 int caseCount = this->readU8();
393 std::vector<std::unique_ptr<SwitchCase>> cases;
394 cases.reserve(caseCount);
395 for (int i = 0; i < caseCount; ++i) {
396 std::unique_ptr<Expression> value = this->expression();
397 int statementCount = this->readU8();
398 std::vector<std::unique_ptr<Statement>> statements;
399 statements.reserve(statementCount);
400 for (int j = 0; j < statementCount; ++j) {
401 statements.push_back(this->statement());
402 }
403 cases.emplace_back(new SwitchCase(-1, std::move(value), std::move(statements)));
404 }
405 return std::unique_ptr<Statement>(new SwitchStatement(-1, isStatic, std::move(expr),
406 std::move(cases),
407 fSymbolTable));
408 }
409 case Rehydrator::kVarDeclaration_Command: {
410 Variable* var = this->symbolRef<Variable>(Symbol::kVariable_Kind);
411 uint8_t sizeCount = this->readU8();
412 std::vector<std::unique_ptr<Expression>> sizes;
413 sizes.reserve(sizeCount);
414 for (int i = 0; i < sizeCount; ++i) {
415 sizes.push_back(this->expression());
416 }
417 std::unique_ptr<Expression> value = this->expression();
418 if (value) {
419 var->fInitialValue = value.get();
420 SkASSERT(var->fWriteCount == 0);
421 ++var->fWriteCount;
422 }
423 return std::unique_ptr<Statement>(new VarDeclaration(var,
424 std::move(sizes),
425 std::move(value)));
426 }
427 case Rehydrator::kVarDeclarations_Command: {
428 const Type* baseType = this->type();
429 int count = this->readU8();
430 std::vector<std::unique_ptr<VarDeclaration>> vars;
431 vars.reserve(count);
432 for (int i = 0 ; i < count; ++i) {
433 std::unique_ptr<Statement> s = this->statement();
434 SkASSERT(s->fKind == Statement::kVarDeclaration_Kind);
435 vars.emplace_back((VarDeclaration*) s.release());
436 }
437 return std::make_unique<VarDeclarationsStatement>(
438 std::make_unique<VarDeclarations>(-1, baseType, std::move(vars)));
439 }
440 case Rehydrator::kVoid_Command:
441 return nullptr;
442 case Rehydrator::kWhile_Command: {
443 std::unique_ptr<Expression> expr = this->expression();
444 std::unique_ptr<Statement> stmt = this->statement();
445 return std::unique_ptr<Statement>(new WhileStatement(-1, std::move(expr),
446 std::move(stmt)));
447 }
448 default:
449 printf("unsupported statement %d\n", kind);
450 SkASSERT(false);
451 return nullptr;
452 }
453}
454
455std::unique_ptr<Expression> Rehydrator::expression() {
456 int kind = this->readU8();
457 switch (kind) {
458 case Rehydrator::kBinary_Command: {
459 std::unique_ptr<Expression> left = this->expression();
460 Token::Kind op = (Token::Kind) this->readU8();
461 std::unique_ptr<Expression> right = this->expression();
462 const Type* type = this->type();
463 return std::unique_ptr<Expression>(new BinaryExpression(-1, std::move(left), op,
464 std::move(right), *type));
465 }
466 case Rehydrator::kBoolLiteral_Command: {
467 bool value = this->readU8();
468 return std::unique_ptr<Expression>(new BoolLiteral(fContext, -1, value));
469 }
470 case Rehydrator::kConstructor_Command: {
471 const Type* type = this->type();
472 uint8_t argCount = this->readU8();
473 std::vector<std::unique_ptr<Expression>> args;
474 args.reserve(argCount);
475 for (int i = 0; i < argCount; ++i) {
476 args.push_back(this->expression());
477 }
478 return std::unique_ptr<Expression>(new Constructor(-1, *type, std::move(args)));
479 }
480 case Rehydrator::kFieldAccess_Command: {
481 std::unique_ptr<Expression> base = this->expression();
482 int index = this->readU8();
483 FieldAccess::OwnerKind ownerKind = (FieldAccess::OwnerKind) this->readU8();
484 return std::unique_ptr<Expression>(new FieldAccess(std::move(base), index, ownerKind));
485 }
486 case Rehydrator::kFloatLiteral_Command: {
487 FloatIntUnion u;
488 u.fInt = this->readS32();
489 return std::unique_ptr<Expression>(new FloatLiteral(fContext, -1, u.fFloat));
490 }
491 case Rehydrator::kFunctionCall_Command: {
492 const Type* type = this->type();
493 const FunctionDeclaration* f = this->symbolRef<FunctionDeclaration>(
494 Symbol::kFunctionDeclaration_Kind);
495 uint8_t argCount = this->readU8();
496 std::vector<std::unique_ptr<Expression>> args;
497 args.reserve(argCount);
498 for (int i = 0; i < argCount; ++i) {
499 args.push_back(this->expression());
500 }
501 return std::unique_ptr<Expression>(new FunctionCall(-1, *type, *f, std::move(args)));
502 }
503 case Rehydrator::kIndex_Command: {
504 std::unique_ptr<Expression> base = this->expression();
505 std::unique_ptr<Expression> index = this->expression();
506 return std::unique_ptr<Expression>(new IndexExpression(fContext, std::move(base),
507 std::move(index)));
508 }
509 case Rehydrator::kIntLiteral_Command: {
510 int value = this->readS32();
511 return std::unique_ptr<Expression>(new IntLiteral(fContext, -1, value));
512 }
513 case Rehydrator::kNullLiteral_Command:
514 return std::unique_ptr<Expression>(new NullLiteral(fContext, -1));
515 case Rehydrator::kPostfix_Command: {
516 Token::Kind op = (Token::Kind) this->readU8();
517 std::unique_ptr<Expression> operand = this->expression();
518 return std::unique_ptr<Expression>(new PostfixExpression(std::move(operand), op));
519 }
520 case Rehydrator::kPrefix_Command: {
521 Token::Kind op = (Token::Kind) this->readU8();
522 std::unique_ptr<Expression> operand = this->expression();
523 return std::unique_ptr<Expression>(new PrefixExpression(op, std::move(operand)));
524 }
525 case Rehydrator::kSetting_Command: {
526 StringFragment name = this->readString();
527 std::unique_ptr<Expression> value = this->expression();
528 return std::unique_ptr<Expression>(new Setting(-1, name, std::move(value)));
529 }
530 case Rehydrator::kSwizzle_Command: {
531 std::unique_ptr<Expression> base = this->expression();
532 int count = this->readU8();
533 std::vector<int> components;
534 components.reserve(count);
535 for (int i = 0; i < count; ++i) {
536 components.push_back(this->readU8());
537 }
538 return std::unique_ptr<Expression>(new Swizzle(fContext, std::move(base),
539 std::move(components)));
540 }
541 case Rehydrator::kTernary_Command: {
542 std::unique_ptr<Expression> test = this->expression();
543 std::unique_ptr<Expression> ifTrue = this->expression();
544 std::unique_ptr<Expression> ifFalse = this->expression();
545 return std::unique_ptr<Expression>(new TernaryExpression(-1, std::move(test),
546 std::move(ifFalse),
547 std::move(ifTrue)));
548 }
549 case Rehydrator::kVariableReference_Command: {
550 const Variable* var = this->symbolRef<Variable>(Symbol::kVariable_Kind);
551 VariableReference::RefKind refKind = (VariableReference::RefKind) this->readU8();
552 return std::unique_ptr<Expression>(new VariableReference(-1, *var, refKind));
553 }
554 case Rehydrator::kVoid_Command:
555 return nullptr;
556 default:
557 printf("unsupported expression %d\n", kind);
558 SkASSERT(false);
559 return nullptr;
560 }
561}
562
563std::shared_ptr<SymbolTable> Rehydrator::symbolTable() {
564 int command = this->readU8();
565 if (command == kVoid_Command) {
566 return nullptr;
567 }
568 SkASSERT(command == kSymbolTable_Command);
569 uint16_t ownedCount = this->readU16();
570 std::shared_ptr<SymbolTable> result(new SymbolTable(fSymbolTable));
571 fSymbolTable = result;
572 std::vector<const Symbol*> ownedSymbols;
573 ownedSymbols.reserve(ownedCount);
574 for (int i = 0; i < ownedCount; ++i) {
575 ownedSymbols.push_back(this->symbol());
576 }
577 uint16_t symbolCount = this->readU16();
578 std::vector<std::pair<StringFragment, int>> symbols;
579 symbols.reserve(symbolCount);
580 for (int i = 0; i < symbolCount; ++i) {
581 StringFragment name = this->readString();
582 int index = this->readU16();
583 fSymbolTable->addWithoutOwnership(name, ownedSymbols[index]);
584 }
585 fSymbolTable = fSymbolTable->fParent;
586 return result;
587}
588
589} // namespace SkSL
590