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