| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * This file contains code to generate serialization/deserialization logic for | 6 * This file contains code to generate serialization/deserialization logic for |
| 7 * summaries based on an "IDL" description of the summary format (written in | 7 * summaries based on an "IDL" description of the summary format (written in |
| 8 * stylized Dart). | 8 * stylized Dart). |
| 9 * | 9 * |
| 10 * For each class in the "IDL" input, two corresponding classes are generated: | 10 * For each class in the "IDL" input, two corresponding classes are generated: |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 checkIdl(); | 253 checkIdl(); |
| 254 out('// Copyright (c) 2015, the Dart project authors. Please see the AUTHOR
S file'); | 254 out('// Copyright (c) 2015, the Dart project authors. Please see the AUTHOR
S file'); |
| 255 out('// for details. All rights reserved. Use of this source code is governe
d by a'); | 255 out('// for details. All rights reserved. Use of this source code is governe
d by a'); |
| 256 out('// BSD-style license that can be found in the LICENSE file.'); | 256 out('// BSD-style license that can be found in the LICENSE file.'); |
| 257 out('//'); | 257 out('//'); |
| 258 out('// This file has been automatically generated. Please do not edit it m
anually.'); | 258 out('// This file has been automatically generated. Please do not edit it m
anually.'); |
| 259 out('// To regenerate the file, use the script "pkg/analyzer/tool/generate_f
iles".'); | 259 out('// To regenerate the file, use the script "pkg/analyzer/tool/generate_f
iles".'); |
| 260 out(); | 260 out(); |
| 261 out('library analyzer.src.summary.format;'); | 261 out('library analyzer.src.summary.format;'); |
| 262 out(); | 262 out(); |
| 263 out("import 'dart:convert';"); | |
| 264 out("import 'base.dart' as base;"); | 263 out("import 'base.dart' as base;"); |
| 264 out("import 'flat_buffers.dart' as fb;"); |
| 265 out(); | 265 out(); |
| 266 _idl.enums.forEach((String name, idlModel.EnumDeclaration enm) { | 266 _idl.enums.forEach((String name, idlModel.EnumDeclaration enm) { |
| 267 outDoc(enm.documentation); | 267 outDoc(enm.documentation); |
| 268 out('enum $name {'); | 268 out('enum $name {'); |
| 269 indent(() { | 269 indent(() { |
| 270 for (String value in enm.values) { | 270 for (String value in enm.values) { |
| 271 out('$value,'); | 271 out('$value,'); |
| 272 } | 272 } |
| 273 }); | 273 }); |
| 274 out('}'); | 274 out('}'); |
| 275 out(); | 275 out(); |
| 276 }); | 276 }); |
| 277 _idl.classes.forEach((String name, idlModel.ClassDeclaration cls) { | 277 for (var cls in _idl.classes.values) { |
| 278 outDoc(cls.documentation); | 278 List<String> builderParams = _generateBuilder(cls); |
| 279 out('class $name extends base.SummaryClass {'); | 279 out(); |
| 280 indent(() { | 280 _generateEncodeFunction(cls, builderParams); |
| 281 for (idlModel.FieldDeclaration field in cls.fields) { | 281 out(); |
| 282 String fieldName = field.name; | 282 _generateInterface(cls); |
| 283 idlModel.FieldType type = field.type; | 283 out(); |
| 284 out('${dartType(type)} _$fieldName;'); | 284 _generateReader(cls); |
| 285 } | 285 out(); |
| 286 out(); | 286 } |
| 287 out('$name.fromJson(Map json)'); | |
| 288 indent(() { | |
| 289 List<String> initializers = <String>[]; | |
| 290 for (idlModel.FieldDeclaration field in cls.fields) { | |
| 291 String fieldName = field.name; | |
| 292 idlModel.FieldType type = field.type; | |
| 293 String convert = 'json[${quoted(fieldName)}]'; | |
| 294 if (type.isList) { | |
| 295 if (type.typeName == 'int' || type.typeName == 'String') { | |
| 296 // No conversion necessary. | |
| 297 } else { | |
| 298 convert = | |
| 299 '$convert?.map((x) => new ${type.typeName}.fromJson(x))?.toL
ist()'; | |
| 300 } | |
| 301 } else if (_idl.classes.containsKey(type.typeName)) { | |
| 302 convert = | |
| 303 '$convert == null ? null : new ${type.typeName}.fromJson($conv
ert)'; | |
| 304 } else if (_idl.enums.containsKey(type.typeName)) { | |
| 305 convert = | |
| 306 '$convert == null ? null : ${type.typeName}.values[$convert]'; | |
| 307 } | |
| 308 initializers.add('_$fieldName = $convert'); | |
| 309 } | |
| 310 for (int i = 0; i < initializers.length; i++) { | |
| 311 String prefix = i == 0 ? ': ' : ' '; | |
| 312 String suffix = i == initializers.length - 1 ? ';' : ','; | |
| 313 out('$prefix${initializers[i]}$suffix'); | |
| 314 } | |
| 315 }); | |
| 316 out(); | |
| 317 out('@override'); | |
| 318 out('Map<String, Object> toMap() => {'); | |
| 319 indent(() { | |
| 320 for (idlModel.FieldDeclaration field in cls.fields) { | |
| 321 String fieldName = field.name; | |
| 322 out('${quoted(fieldName)}: $fieldName,'); | |
| 323 } | |
| 324 }); | |
| 325 out('};'); | |
| 326 out(); | |
| 327 if (cls.isTopLevel) { | |
| 328 out('$name.fromBuffer(List<int> buffer) : this.fromJson(JSON.decode(UT
F8.decode(buffer)));'); | |
| 329 out(); | |
| 330 } | |
| 331 cls.fields.asMap().forEach((index, field) { | |
| 332 String fieldName = field.name; | |
| 333 idlModel.FieldType type = field.type; | |
| 334 if (index != 0) { | |
| 335 out(); | |
| 336 } | |
| 337 String def = defaultValue(type); | |
| 338 String defaultSuffix = def == null ? '' : ' ?? $def'; | |
| 339 outDoc(field.documentation); | |
| 340 out('${dartType(type)} get $fieldName => _$fieldName$defaultSuffix;'); | |
| 341 }); | |
| 342 }); | |
| 343 out('}'); | |
| 344 out(); | |
| 345 List<String> builderParams = <String>[]; | |
| 346 out('class ${name}Builder {'); | |
| 347 indent(() { | |
| 348 out('final Map _json = {};'); | |
| 349 out(); | |
| 350 out('bool _finished = false;'); | |
| 351 out(); | |
| 352 out('${name}Builder(base.BuilderContext context);'); | |
| 353 for (idlModel.FieldDeclaration field in cls.fields) { | |
| 354 String fieldName = field.name; | |
| 355 idlModel.FieldType type = field.type; | |
| 356 out(); | |
| 357 outDoc(field.documentation); | |
| 358 String conversion = '_value'; | |
| 359 String condition = ''; | |
| 360 if (type.isList) { | |
| 361 if (_idl.classes.containsKey(type.typeName)) { | |
| 362 conversion = '$conversion.map((b) => b.finish()).toList()'; | |
| 363 } else { | |
| 364 conversion = '$conversion.toList()'; | |
| 365 } | |
| 366 condition = ' || _value.isEmpty'; | |
| 367 } else if (_idl.enums.containsKey(type.typeName)) { | |
| 368 conversion = '$conversion.index'; | |
| 369 condition = ' || _value == ${defaultValue(type)}'; | |
| 370 } else if (_idl.classes.containsKey(type.typeName)) { | |
| 371 conversion = '$conversion.finish()'; | |
| 372 } | |
| 373 builderParams.add('${encodedType(type)} $fieldName'); | |
| 374 out('void set $fieldName(${encodedType(type)} _value) {'); | |
| 375 indent(() { | |
| 376 out('assert(!_finished);'); | |
| 377 out('assert(!_json.containsKey(${quoted(fieldName)}));'); | |
| 378 if (condition.isEmpty) { | |
| 379 out('if (_value != null) {'); | |
| 380 } else { | |
| 381 out('if (!(_value == null$condition)) {'); | |
| 382 } | |
| 383 indent(() { | |
| 384 out('_json[${quoted(fieldName)}] = $conversion;'); | |
| 385 }); | |
| 386 out('}'); | |
| 387 }); | |
| 388 out('}'); | |
| 389 } | |
| 390 if (cls.isTopLevel) { | |
| 391 out(); | |
| 392 out('List<int> toBuffer() => UTF8.encode(JSON.encode(finish()));'); | |
| 393 } | |
| 394 out(); | |
| 395 out('Map finish() {'); | |
| 396 indent(() { | |
| 397 out('assert(!_finished);'); | |
| 398 out('_finished = true;'); | |
| 399 out('return _json;'); | |
| 400 }); | |
| 401 out('}'); | |
| 402 }); | |
| 403 out('}'); | |
| 404 out(); | |
| 405 out('${name}Builder encode$name(base.BuilderContext builderContext, {${bui
lderParams.join(', ')}}) {'); | |
| 406 indent(() { | |
| 407 out('${name}Builder builder = new ${name}Builder(builderContext);'); | |
| 408 for (idlModel.FieldDeclaration field in cls.fields) { | |
| 409 String fieldName = field.name; | |
| 410 out('builder.$fieldName = $fieldName;'); | |
| 411 } | |
| 412 out('return builder;'); | |
| 413 }); | |
| 414 out('}'); | |
| 415 out(); | |
| 416 }); | |
| 417 } | 287 } |
| 418 | 288 |
| 419 /** | 289 /** |
| 420 * Enclose [s] in quotes, escaping as necessary. | 290 * Enclose [s] in quotes, escaping as necessary. |
| 421 */ | 291 */ |
| 422 String quoted(String s) { | 292 String quoted(String s) { |
| 423 return JSON.encode(s); | 293 return JSON.encode(s); |
| 424 } | 294 } |
| 425 | 295 |
| 296 List<String> _generateBuilder(idlModel.ClassDeclaration cls) { |
| 297 String builderName = cls.name + 'Builder'; |
| 298 List<String> builderParams = <String>[]; |
| 299 out('class $builderName {'); |
| 300 indent(() { |
| 301 out('bool _finished = false;'); |
| 302 // Generate fields. |
| 303 out(); |
| 304 for (idlModel.FieldDeclaration field in cls.fields) { |
| 305 String fieldName = field.name; |
| 306 idlModel.FieldType type = field.type; |
| 307 String typeStr = encodedType(type); |
| 308 out('$typeStr _$fieldName;'); |
| 309 } |
| 310 out(); |
| 311 for (idlModel.FieldDeclaration field in cls.fields) { |
| 312 String fieldName = field.name; |
| 313 String isSetFieldName = '_' + fieldName + 'IsSet'; |
| 314 out('bool $isSetFieldName = false;'); |
| 315 } |
| 316 // Generate constructor. |
| 317 out(); |
| 318 out('$builderName(base.BuilderContext context);'); |
| 319 // Generate setters. |
| 320 for (idlModel.FieldDeclaration field in cls.fields) { |
| 321 String fieldName = field.name; |
| 322 String typeStr = encodedType(field.type); |
| 323 out(); |
| 324 outDoc(field.documentation); |
| 325 builderParams.add('$typeStr $fieldName'); |
| 326 out('void set $fieldName($typeStr _value) {'); |
| 327 indent(() { |
| 328 String stateFieldName = '_' + fieldName; |
| 329 String stateIsSetFieldName = stateFieldName + 'IsSet'; |
| 330 out('assert(!_finished);'); |
| 331 out('assert(!$stateIsSetFieldName);'); |
| 332 out('$stateIsSetFieldName = true;'); |
| 333 out('$stateFieldName = _value;'); |
| 334 }); |
| 335 out('}'); |
| 336 } |
| 337 // Generate finish. |
| 338 if (cls.isTopLevel) { |
| 339 out(); |
| 340 out('List<int> toBuffer() {'); |
| 341 indent(() { |
| 342 out('fb.Builder fbBuilder = new fb.Builder();'); |
| 343 out('return fbBuilder.finish(finish(fbBuilder));'); |
| 344 }); |
| 345 out('}'); |
| 346 } |
| 347 out(); |
| 348 out('fb.Offset finish(fb.Builder fbBuilder) {'); |
| 349 indent(() { |
| 350 out('assert(!_finished);'); |
| 351 out('_finished = true;'); |
| 352 // Write objects and remember Offset(s). |
| 353 cls.fields.asMap().forEach((index, idlModel.FieldDeclaration field) { |
| 354 idlModel.FieldType fieldType = field.type; |
| 355 String offsetName = 'offset_' + field.name; |
| 356 if (fieldType.isList || |
| 357 fieldType.typeName == 'String' || |
| 358 _idl.classes.containsKey(fieldType.typeName)) { |
| 359 out('fb.Offset $offsetName;'); |
| 360 } |
| 361 }); |
| 362 cls.fields.asMap().forEach((index, idlModel.FieldDeclaration field) { |
| 363 idlModel.FieldType fieldType = field.type; |
| 364 String valueName = '_' + field.name; |
| 365 String offsetName = 'offset_' + field.name; |
| 366 String condition; |
| 367 String writeCode; |
| 368 if (fieldType.isList) { |
| 369 condition = ' || $valueName.isEmpty'; |
| 370 if (_idl.classes.containsKey(fieldType.typeName)) { |
| 371 String itemCode = 'b.finish(fbBuilder)'; |
| 372 String listCode = '$valueName.map((b) => $itemCode).toList()'; |
| 373 writeCode = '$offsetName = fbBuilder.writeList($listCode);'; |
| 374 } else if (fieldType.typeName == 'int') { |
| 375 writeCode = '$offsetName = fbBuilder.writeListInt32($valueName);'; |
| 376 } else { |
| 377 assert(fieldType.typeName == 'String'); |
| 378 String itemCode = 'fbBuilder.writeString(b)'; |
| 379 String listCode = '$valueName.map((b) => $itemCode).toList()'; |
| 380 writeCode = '$offsetName = fbBuilder.writeList($listCode);'; |
| 381 } |
| 382 } else if (fieldType.typeName == 'String') { |
| 383 writeCode = '$offsetName = fbBuilder.writeString($valueName);'; |
| 384 } else if (_idl.classes.containsKey(fieldType.typeName)) { |
| 385 writeCode = '$offsetName = $valueName.finish(fbBuilder);'; |
| 386 } |
| 387 if (writeCode != null) { |
| 388 if (condition == null) { |
| 389 out('if ($valueName != null) {'); |
| 390 } else { |
| 391 out('if (!($valueName == null$condition)) {'); |
| 392 } |
| 393 indent(() { |
| 394 out(writeCode); |
| 395 }); |
| 396 out('}'); |
| 397 } |
| 398 }); |
| 399 // Write the table. |
| 400 out('fbBuilder.startTable();'); |
| 401 cls.fields.asMap().forEach((index, idlModel.FieldDeclaration field) { |
| 402 idlModel.FieldType fieldType = field.type; |
| 403 String valueName = '_' + field.name; |
| 404 String offsetName = 'offset_' + field.name; |
| 405 String condition = '$valueName != null'; |
| 406 String writeCode; |
| 407 if (fieldType.isList || |
| 408 fieldType.typeName == 'String' || |
| 409 _idl.classes.containsKey(fieldType.typeName)) { |
| 410 condition = '$offsetName != null'; |
| 411 writeCode = 'fbBuilder.addOffset($index, $offsetName);'; |
| 412 } else if (fieldType.typeName == 'bool') { |
| 413 // TODO(scheglov) implement booleans merging? |
| 414 condition = '$valueName == true'; |
| 415 writeCode = 'fbBuilder.addInt8($index, 1);'; |
| 416 } else if (fieldType.typeName == 'int') { |
| 417 condition += ' && $valueName != ${defaultValue(fieldType)}'; |
| 418 writeCode = 'fbBuilder.addInt32($index, $valueName);'; |
| 419 } else if (_idl.enums.containsKey(fieldType.typeName)) { |
| 420 condition += ' && $valueName != ${defaultValue(fieldType)}'; |
| 421 writeCode = 'fbBuilder.addInt32($index, $valueName.index);'; |
| 422 } |
| 423 if (writeCode == null) { |
| 424 throw new UnimplementedError('Writing type ${fieldType.typeName}'); |
| 425 } |
| 426 out('if ($condition) {'); |
| 427 indent(() { |
| 428 out(writeCode); |
| 429 }); |
| 430 out('}'); |
| 431 }); |
| 432 out('return fbBuilder.endTable();'); |
| 433 }); |
| 434 out('}'); |
| 435 }); |
| 436 out('}'); |
| 437 return builderParams; |
| 438 } |
| 439 |
| 440 void _generateEncodeFunction( |
| 441 idlModel.ClassDeclaration cls, List<String> builderParams) { |
| 442 String className = cls.name; |
| 443 String builderName = className + 'Builder'; |
| 444 out('$builderName encode$className(base.BuilderContext builderContext, {${bu
ilderParams.join(', ')}}) {'); |
| 445 indent(() { |
| 446 out('$builderName builder = new $builderName(builderContext);'); |
| 447 for (idlModel.FieldDeclaration field in cls.fields) { |
| 448 String fieldName = field.name; |
| 449 out('builder.$fieldName = $fieldName;'); |
| 450 } |
| 451 out('return builder;'); |
| 452 }); |
| 453 out('}'); |
| 454 } |
| 455 |
| 456 void _generateInterface(idlModel.ClassDeclaration cls) { |
| 457 String name = cls.name; |
| 458 outDoc(cls.documentation); |
| 459 out('abstract class $name extends base.SummaryClass {'); |
| 460 indent(() { |
| 461 if (cls.isTopLevel) { |
| 462 out('factory $name.fromBuffer(List<int> buffer) {'); |
| 463 indent(() { |
| 464 out('fb.BufferPointer rootRef = new fb.BufferPointer.fromBytes(buffer)
;'); |
| 465 out('return const _${name}Reader().read(rootRef);'); |
| 466 }); |
| 467 out('}'); |
| 468 } |
| 469 cls.fields.asMap().forEach((index, field) { |
| 470 String fieldName = field.name; |
| 471 idlModel.FieldType type = field.type; |
| 472 out(); |
| 473 outDoc(field.documentation); |
| 474 out('${dartType(type)} get $fieldName;'); |
| 475 }); |
| 476 }); |
| 477 out('}'); |
| 478 } |
| 479 |
| 480 void _generateReader(idlModel.ClassDeclaration cls) { |
| 481 String name = cls.name; |
| 482 String readerName = '_${name}Reader'; |
| 483 out('class $readerName extends fb.TableReader<$readerName> implements $name
{'); |
| 484 indent(() { |
| 485 out('final fb.BufferPointer _bp;'); |
| 486 out(); |
| 487 out('const $readerName([this._bp]);'); |
| 488 out(); |
| 489 out('@override'); |
| 490 out('$readerName createReader(fb.BufferPointer bp) => new $readerName(bp);
'); |
| 491 out(); |
| 492 out('@override'); |
| 493 out('Map<String, Object> toMap() => {};'); |
| 494 cls.fields.asMap().forEach((index, field) { |
| 495 String fieldName = field.name; |
| 496 idlModel.FieldType type = field.type; |
| 497 String typeName = type.typeName; |
| 498 // Prepare "readLines" or "readCode" + "def" + "readSuffix" |
| 499 List<String> readLines; |
| 500 String readCode; |
| 501 String def = defaultValue(type); |
| 502 String readSuffix = ''; |
| 503 if (type.isList) { |
| 504 if (typeName == 'int') { |
| 505 String itemCode = 'const fb.Int32Reader()'; |
| 506 readCode = 'const fb.ListReader<int>($itemCode)'; |
| 507 } else if (typeName == 'String') { |
| 508 String itemCode = 'const fb.StringReader()'; |
| 509 readCode = 'const fb.ListReader<String>($itemCode)'; |
| 510 } else { |
| 511 String itemCode = '$typeName>(const _${typeName}Reader()'; |
| 512 readCode = 'const fb.ListReader<$itemCode)'; |
| 513 } |
| 514 } else if (typeName == 'bool') { |
| 515 // TODO(scheglov) implement booleans merging? |
| 516 def = '0'; |
| 517 readCode = 'const fb.Int8Reader()'; |
| 518 readSuffix = ' == 1'; |
| 519 } else if (typeName == 'int') { |
| 520 readCode = 'const fb.Int32Reader()'; |
| 521 } else if (typeName == 'String') { |
| 522 readCode = 'const fb.StringReader()'; |
| 523 } else if (_idl.enums.containsKey(typeName)) { |
| 524 readLines = <String>[ |
| 525 'int index = const fb.Int32Reader().vTableGet(_bp, $index, 0);', |
| 526 'return $typeName.values[index];' |
| 527 ]; |
| 528 } else if (_idl.classes.containsKey(typeName)) { |
| 529 readCode = 'const _${typeName}Reader()'; |
| 530 } |
| 531 assert(readCode != null || readLines != null); |
| 532 // Write the getter implementation. |
| 533 out(); |
| 534 out('@override'); |
| 535 String returnType = dartType(type); |
| 536 if (readLines != null) { |
| 537 out('$returnType get $fieldName {'); |
| 538 indent(() { |
| 539 readLines.forEach(out); |
| 540 }); |
| 541 out('}'); |
| 542 } else { |
| 543 String expr = '$readCode.vTableGet(_bp, $index, $def)$readSuffix'; |
| 544 out('$returnType get $fieldName => $expr;'); |
| 545 } |
| 546 }); |
| 547 }); |
| 548 out('}'); |
| 549 } |
| 550 |
| 426 /** | 551 /** |
| 427 * Return the documentation text of the given [node], or `null` if the [node] | 552 * Return the documentation text of the given [node], or `null` if the [node] |
| 428 * does not have a comment. Each line is `\n` separated. | 553 * does not have a comment. Each line is `\n` separated. |
| 429 */ | 554 */ |
| 430 String _getNodeDoc(LineInfo lineInfo, AnnotatedNode node) { | 555 String _getNodeDoc(LineInfo lineInfo, AnnotatedNode node) { |
| 431 Comment comment = node.documentationComment; | 556 Comment comment = node.documentationComment; |
| 432 if (comment != null && | 557 if (comment != null && |
| 433 comment.isDocumentation && | 558 comment.isDocumentation && |
| 434 comment.tokens.length == 1 && | 559 comment.tokens.length == 1 && |
| 435 comment.tokens.first.type == TokenType.MULTI_LINE_COMMENT) { | 560 comment.tokens.first.type == TokenType.MULTI_LINE_COMMENT) { |
| 436 Token token = comment.tokens.first; | 561 Token token = comment.tokens.first; |
| 437 int column = lineInfo.getLocation(token.offset).columnNumber; | 562 int column = lineInfo.getLocation(token.offset).columnNumber; |
| 438 String indent = ' ' * (column - 1); | 563 String indent = ' ' * (column - 1); |
| 439 return token.lexeme.split('\n').map((String line) { | 564 return token.lexeme.split('\n').map((String line) { |
| 440 if (line.startsWith(indent)) { | 565 if (line.startsWith(indent)) { |
| 441 line = line.substring(indent.length); | 566 line = line.substring(indent.length); |
| 442 } | 567 } |
| 443 return line; | 568 return line; |
| 444 }).join('\n'); | 569 }).join('\n'); |
| 445 } | 570 } |
| 446 return null; | 571 return null; |
| 447 } | 572 } |
| 448 } | 573 } |
| OLD | NEW |