anchor.diff:      });
       out('}');
     }
  -
  -  /// Generate a call to the appropriate method of [ApiSignature] for the type
  -  /// [typeName], using the data named by [ref].  If [couldBeNull] is `true`,
  -  /// generate code to handle the possibility that [ref] is `null` (substituting
  -  /// in the appropriate default value).
  -  void _generateSignatureCall(String typeName, String ref, bool couldBeNull) {
  -    if (_idl.enums.containsKey(typeName)) {
  -      if (couldBeNull) {
  -        out('signature.addInt($ref == null ? 0 : $ref.index);');
  -      } else {
  -        out('signature.addInt($ref.index);');
  -      }
  -    } else if (_idl.classes.containsKey(typeName)) {
  -      if (couldBeNull) {
  -        out('signature.addBool($ref != null);');
  -      }
  -      out('$ref?.collectApiSignature(signature);');
  -    } else {
  -      switch (typeName) {
  -        case 'String':
  -          if (couldBeNull) {
  -            ref += " ?? ''";
  -          }
  -          out("signature.addString($ref);");
  -          break;
  -        case 'int':
  -          if (couldBeNull) {
  -            ref += ' ?? 0';
  -          }
  -          out('signature.addInt($ref);');
  -          break;
  -        case 'bool':
  -          if (couldBeNull) {
  -            ref += ' == true';
  -          }
  -          out('signature.addBool($ref);');
  -          break;
  -        case 'double':
  -          if (couldBeNull) {
  -            ref += ' ?? 0.0';
  -          }
  -          out('signature.addDouble($ref);');
  -          break;
  -        default:
  -          throw "Don't know how to generate signature call for $typeName";
  -      }
  -    }
  -  }
  -
  -  /// Return the documentation text of the given [node], or `null` if the [node]
  -  /// does not have a comment.  Each line is `\n` separated.
  -  String _getNodeDoc(AnnotatedNode node) {
  -    Comment comment = node.documentationComment;
  -    if (comment != null && comment.isDocumentation) {
  -      if (comment.tokens.length == 1 &&
  -          comment.tokens.first.lexeme.startsWith('/*')) {
  -        Token token = comment.tokens.first;
  -        return token.lexeme.split('\n').map((String line) {
  -          line = line.trimLeft();
  -          if (line.startsWith('*')) line = ' ' + line;
  -          return line;
  -        }).join('\n');
  -      } else if (comment.tokens
  -          .every((token) => token.lexeme.startsWith('///'))) {
  -        return comment.tokens
  -            .map((token) => token.lexeme.trimLeft())
  -            .join('\n');
  -      }
  -    }
  -    return null;
  -  }
  -
  -  String _variantAssertStatement(
  -    idlModel.ClassDeclaration class_,
  -    List<String> variants,
  -  ) {
  -    var assertCondition = variants
  -        ?.map((key) => '${class_.variantField} == idl.$key')
  -        ?.join(' || ');
  -    return 'assert($assertCondition);';
  -  }
   }
  
positive.code:       return typeStr;
      }
    }
  
    /// Entry point to the code generator when generating the "format.fbs" file.
    void generateFlatBufferSchema() {
      outputHeader();
      for (idlModel.EnumDeclaration enm in _idl.enums.values) {
        out();
        outDoc(enm.documentation);
        out('enum ${enm.name} : byte {');
        indent(() {
          for (int i = 0; i < enm.values.length; i++) {
            idlModel.EnumValueDeclaration value = enm.values[i];
            if (i != 0) {
              out();
            }
            String suffix = i < enm.values.length - 1 ? ',' : '';
            outDoc(value.documentation);
            out('${value.name}$suffix');
          }
        });
        out('}');
      }
      for (idlModel.ClassDeclaration cls in _idl.classes.values) {
        out();
        outDoc(cls.documentation);
        out('table ${cls.name} {');
        indent(() {
          for (int i = 0; i < cls.allFields.length; i++) {
            idlModel.FieldDeclaration field = cls.allFields[i];
            if (i != 0) {
              out();
            }
            outDoc(field.documentation);
            List<String> attributes = <String>['id: ${field.id}'];
            if (field.isDeprecated) {
              attributes.add('deprecated');
            }
            String attrText = attributes.join(', ');
            out('${field.name}:${fbsType(field.type)} ($attrText);');
          }
        });
        out('}');
      }
      out();
      // Standard flatbuffers only support one root type.  We support multiple
      // root types.  For now work around this by forcing PackageBundle to be the
      // root type.  TODO(paulberry): come up with a better solution.
      idlModel.ClassDeclaration rootType = _idl.classes['PackageBundle'];
      out('root_type ${rootType.name};');
      if (rootType.fileIdentifier != null) {
        out();
        out('file_identifier ${quoted(rootType.fileIdentifier)};');
      }
    }
  
    /// Entry point to the code generator when generating the "format.dart" file.
    void generateFormatCode() {
      outputHeader();
      out('library analyzer.src.summary.format;');
      out();
      out("import 'dart:convert' as convert;");
      out();
      out("import 'package:analyzer/src/summary/api_signature.dart' as api_sig;");
      out("import 'package:analyzer/src/summary/flat_buffers.dart' as fb;");
      out();
      out("import 'idl.dart' as idl;");
      out();
      for (idlModel.EnumDeclaration enm in _idl.enums.values) {
        _generateEnumReader(enm);
        out();
      }
      for (idlModel.ClassDeclaration cls in _idl.classes.values) {
        if (!cls.isDeprecated) {
          _generateBuilder(cls);
          out();
        }
        if (cls.isTopLevel) {
          _generateReadFunction(cls);
          out();
        }
        if (!cls.isDeprecated) {
          _generateReader(cls);
          out();
          _generateImpl(cls);
          out();
          _generateMixin(cls);
          out();
        }
      }
    }
  
    /// Add the prefix `idl.` to a type name, unless that type name is the name of
    /// a built-in type.
    String idlPrefix(String s) {
      switch (s) {
        case 'bool':
        case 'double':
        case 'int':
        case 'String':
          return s;
        default:
          return 'idl.$s';
      }
    }
  
    /// Execute [callback] with two spaces added to [_indentation].
    void indent(void callback()) {
      String oldIndentation = _indentation;
      try {
        _indentation += '  ';
        callback();
      } finally {
        _indentation = oldIndentation;
      }
    }
  
    /// Add the string [s] to the output as a single line, indenting as
    /// appropriate.
    void out([String s = '']) {
      if (s == '') {
        _outBuffer.writeln('');
      } else {
        _outBuffer.writeln('$_indentation$s');
      }
    }
  
    void outDoc(String documentation) {
      if (documentation != null) {
        documentation.split('\n').forEach(out);
      }
    }
  
    void outputHeader() {
      out('// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file');
      out('// for details. All rights reserved. Use of this source code is governed by a');
      out('// BSD-style license that can be found in the LICENSE file.');
      out('//');
      out('// This file has been automatically generated.  Please do not edit it manually.');
      out('// To regenerate the file, use the SDK script');
      out('// "pkg/analyzer/tool/summary/generate.dart \$IDL_FILE_PATH",');
      out('// or "pkg/analyzer/tool/generate_files" for the analyzer package IDL/sources.');
      out();
    }
  
    /// Enclose [s] in quotes, escaping as necessary.
    String quoted(String s) {
      return json.encode(s);
    }
  
    void _addFieldForGetter(
      idlModel.ClassDeclaration cls,
      MethodDeclaration getter,
    ) {
      var desc = '${cls.name}.${getter.name}';
      if (getter.returnType == null) {
        throw new Exception('Getter needs a type: $desc');
      }
  
      var type = getter.returnType;
  
      var isList = false;
      if (type.name == 'List' &&
          type.typeArguments != null &&
          type.typeArguments.length == 1) {
        isList = true;
        type = type.typeArguments[0];
      }
      if (type.typeArguments != null) {
        throw new Exception('Cannot handle type arguments in `$type`');
      }
  
      int id;
      List<String> variants;
      bool isDeprecated = false;
      bool isInformative = false;
  
      for (Annotation annotation in getter.metadata) {
        if (annotation.name == 'Id') {
          if (id != null) {
            throw new Exception('Duplicate @id annotation ($getter)');
          }
          if (annotation.arguments == null) {
            throw Exception('@Id must be passed an argument ($desc)');
          }
          if (annotation.arguments.length != 1) {
            throw Exception('@Id must be passed exactly one argument ($desc)');
          }
  
          var idExpression = annotation.arguments[0];
          if (idExpression is IntegerLiteral) {
            id = idExpression.value;
          } else {
            throw new Exception(
              '@Id argument must be an integer literal ($desc)',
            );
          }
        } else if (annotation.name == 'deprecated') {
          if (annotation.arguments != null) {
            throw new Exception('@deprecated does not take args ($desc)');
          }
          isDeprecated = true;
        } else if (annotation.name == 'informative') {
          isInformative = true;
        } else if (annotation.name == 'VariantId') {
          if (id != null) {
            throw Exception('Cannot specify both @Id and @VariantId ($getter)');
          }
          if (variants != null) {
            throw Exception('Duplicate @VariantId annotation ($getter)');
          }
  
          if (annotation.arguments == null) {
            throw Exception('@VariantId must be given arguments ($desc)');
          }
          if (annotation.arguments.length != 2) {
            throw Exception(
              '@VariantId must be given exactly two arguments ($desc)',
            );
          }
  
          var idExpression = annotation.arguments[0];
          if (idExpression is IntegerLiteral) {
            id = idExpression.value;
          } else {
            throw Exception(
              '@VariantId argument must be an integer literal ($desc)',
            );
          }
  
          var variantExpression = annotation.arguments[1];
          if (variantExpression is NamedExpression) {
            if (variantExpression.name == 'variant') {
              variants = [variantExpression.expression.toCode()];
            } else if (variantExpression.name == 'variantList') {
              variants = (variantExpression.expression as ListLiteral)
                  .elements
                  .map((e) => e.toCode())
                  .toList();
            } else {
              throw Exception(
                'Only "key" or "keyList" expected in @VariantId ($desc)',
              );
            }
          } else {
            throw Exception(
              'The second argument of @VariantId must be named ($desc)',
            );
          }
        }
      }
      if (id == null) {
        throw new Exception('Missing @id annotation ($desc)');
      }
  
      var fieldType = new idlModel.FieldType(type.name, isList);
  
      String name = getter.name;
      Map<String, List<String>> variantMap;
      if (variants != null) {
        var fieldsWithSameId =
            cls.allFields.where((field) => field.id == id).toList();
        if (fieldsWithSameId.isNotEmpty) {
          var existingField = fieldsWithSameId.first;
          if (existingField.variantMap == null) {
            throw Exception('$desc: id $id is already used as a non-variant '
                'field: ${existingField.name}');
          }
  
          var map = existingField.variantMap;
          for (var variant in variants) {
            for (var logicalName in map.keys) {
              if (map[logicalName].contains(variant)) {
                throw Exception('$desc: id $id is already used for $logicalName');
              }
            }
          }
  
          if (existingField.type != fieldType) {
            throw Exception(
              '$desc: id $id is already used with type ${existingField.type}',
            );
          }
  
          map.putIfAbsent(getter.name, () => <String>[]).addAll(variants);
          return;
        } else {
          name = 'variantField_$id';
          variantMap = <String, List<String>>{getter.name: variants};
        }
      }
  
      cls.allFields.add(
        idlModel.FieldDeclaration(
          documentation: _getNodeDoc(getter),
          name: name,
          type: fieldType,
          id: id,
          isDeprecated: isDeprecated,
          isInformative: isInformative,
          variantMap: variantMap,
        ),
      );
    }
  
    Iterable<String> _computeVariants(idlModel.ClassDeclaration cls) {
      return cls.fields
          .map((f) => f.variantMap?.values ?? [])
          .expand((v) => v)
          .expand((v) => v)
          .toSet();
    }
  
    void _generateBuilder(idlModel.ClassDeclaration cls) {
      String name = cls.name;
      String builderName = name + 'Builder';
      String mixinName = '_${name}Mixin';
      List<String> constructorParams = <String>[];
      var implementsClause =
          cls.isDeprecated ? '' : ' implements ${idlPrefix(name)}';
      out('class $builderName extends Object with $mixinName$implementsClause {');
      indent(() {
        // Generate fields.
        for (idlModel.FieldDeclaration field in cls.fields) {
          String fieldName = field.name;
          idlModel.FieldType type = field.type;
          String typeStr = encodedType(type);
          out('$typeStr _$fieldName;');
        }
  
        // Generate getters and setters.
        for (idlModel.FieldDeclaration field in cls.allFields) {
          String fieldName = field.name;
          idlModel.FieldType fieldType = field.type;
          String typeStr = encodedType(fieldType);
          String def = defaultValue(fieldType, true);
          String defSuffix = def == null ? '' : ' ??= $def';
          out();
          if (field.isDeprecated) {
            out('@override');
            out('Null get $fieldName => $_throwDeprecated;');
          } else {
            if (field.variantMap != null) {
              for (var logicalName in field.variantMap.keys) {
                var variants = field.variantMap[logicalName];
                out('@override');
                out('$typeStr get $logicalName {');
                indent(() {
                  out(_variantAssertStatement(cls, variants));
                  out('return _${field.name}$defSuffix;');
                });
                out('}');
                out();
              }
            } else {
              out('@override');
              out('$typeStr get $fieldName => _$fieldName$defSuffix;');
            }
            out();
  
            constructorParams.add('$typeStr $fieldName');
  
            outDoc(field.documentation);
  
            if (field.variantMap != null) {
              for (var logicalName in field.variantMap.keys) {
                var variants = field.variantMap[logicalName];
                out('set $logicalName($typeStr value) {');
                indent(() {
                  out(_variantAssertStatement(cls, variants));
                  _generateNonNegativeInt(fieldType);
                  out('_variantField_${field.id} = value;');
                });
                out('}');
                out();
              }
            } else {
              out('set $fieldName($typeStr value) {');
              indent(() {
                _generateNonNegativeInt(fieldType);
                out('this._$fieldName = value;');
              });
              out('}');
            }
          }
        }
        // Generate constructor.
        out();
        if (cls.variantField != null) {
          for (var variant in _computeVariants(cls)) {
            var constructorName = variant.split('.')[1];
            out('$builderName.$constructorName({');
  
            for (var field in cls.fields) {
              if (field.variantMap != null) {
                for (var property in field.variantMap.keys) {
                  if (field.variantMap[property].contains(variant)) {
                    out('${encodedType(field.type)} $property,');
                  }
                }
              }
            }
  
            out('}) : ');
  
            out('_${cls.variantField} = idl.$variant');
  
            var separator = ',';
            for (var field in cls.fields) {
              if (field.variantMap != null) {
                for (var property in field.variantMap.keys) {
                  if (field.variantMap[property].contains(variant)) {
                    out('$separator _${field.name} = $property');
                    separator = ', ';
                  }
                }
              }
            }
  
            out(';');
            out();
          }
        } else {
          out('$builderName({${constructorParams.join(', ')}})');
          List<idlModel.FieldDeclaration> fields = cls.fields.toList();
          for (int i = 0; i < fields.length; i++) {
            idlModel.FieldDeclaration field = fields[i];
            String prefix = i == 0 ? '  : ' : '    ';
            String suffix = i == fields.length - 1 ? ';' : ',';
            out('${prefix}_${field.name} = ${field.name}$suffix');
          }
        }
  
        // Generate flushInformative().
        {
          out();
          out('/// Flush [informative] data recursively.');
          out('void flushInformative() {');
          indent(() {
            for (idlModel.FieldDeclaration field in cls.fields) {
              idlModel.FieldType fieldType = field.type;
              String valueName = '_' + field.name;
              if (field.isInformative) {
                out('$valueName = null;');
              } else if (_idl.classes.containsKey(fieldType.typeName)) {
                if (fieldType.isList) {
                  out('$valueName?.forEach((b) => b.flushInformative());');
                } else {
                  out('$valueName?.flushInformative();');
                }
              }
            }
          });
          out('}');
        }
        // Generate collectApiSignature().
        {
          out();
          out('/// Accumulate non-[informative] data into [signature].');
          out('void collectApiSignature(api_sig.ApiSignature signature) {');
          indent(() {
            List<idlModel.FieldDeclaration> sortedFields = cls.fields.toList()
              ..sort((idlModel.FieldDeclaration a, idlModel.FieldDeclaration b) =>
                  a.id.compareTo(b.id));
            for (idlModel.FieldDeclaration field in sortedFields) {
              if (field.isInformative) {
                continue;
              }
              String ref = 'this._${field.name}';
              if (field.type.isList) {
                out('if ($ref == null) {');
                indent(() {
                  out('signature.addInt(0);');
                });
                out('} else {');
                indent(() {
                  out('signature.addInt($ref.length);');
                  out('for (var x in $ref) {');
                  indent(() {
                    _generateSignatureCall(field.type.typeName, 'x', false);
                  });
                  out('}');
                });
                out('}');
              } else {
                _generateSignatureCall(field.type.typeName, ref, true);
              }
            }
          });
          out('}');
        }
        // Generate finish.
        if (cls.isTopLevel) {
          out();
          out('List<int> toBuffer() {');
          indent(() {
            out('fb.Builder fbBuilder = new fb.Builder();');
            String fileId = cls.fileIdentifier == null
                ? ''
                : ', ${quoted(cls.fileIdentifier)}';
            out('return fbBuilder.finish(finish(fbBuilder)$fileId);');
          });
          out('}');
        }
        out();
        out('fb.Offset finish(fb.Builder fbBuilder) {');
        indent(() {
          // Write objects and remember Offset(s).
          for (idlModel.FieldDeclaration field in cls.fields) {
            idlModel.FieldType fieldType = field.type;
            String offsetName = 'offset_' + field.name;
            if (fieldType.isList ||
                fieldType.typeName == 'String' ||
                _idl.classes.containsKey(fieldType.typeName)) {
              out('fb.Offset $offsetName;');
            }
          }
          for (idlModel.FieldDeclaration field in cls.fields) {
            idlModel.FieldType fieldType = field.type;
            String valueName = '_' + field.name;
            String offsetName = 'offset_' + field.name;
            String condition;
            String writeCode;
            if (fieldType.isList) {
              condition = ' || $valueName.isEmpty';
              if (_idl.classes.containsKey(fieldType.typeName)) {
                String itemCode = 'b.finish(fbBuilder)';
                String listCode = '$valueName.map((b) => $itemCode).toList()';
                writeCode = '$offsetName = fbBuilder.writeList($listCode);';
              } else if (_idl.enums.containsKey(fieldType.typeName)) {
                String itemCode = 'b.index';
                String listCode = '$valueName.map((b) => $itemCode).toList()';
                writeCode = '$offsetName = fbBuilder.writeListUint8($listCode);';
              } else if (fieldType.typeName == 'bool') {
                writeCode = '$offsetName = fbBuilder.writeListBool($valueName);';
              } else if (fieldType.typeName == 'int') {
                writeCode =
                    '$offsetName = fbBuilder.writeListUint32($valueName);';
              } else if (fieldType.typeName == 'double') {
                writeCode =
                    '$offsetName = fbBuilder.writeListFloat64($valueName);';
              } else {
                assert(fieldType.typeName == 'String');
                String itemCode = 'fbBuilder.writeString(b)';
                String listCode = '$valueName.map((b) => $itemCode).toList()';
                writeCode = '$offsetName = fbBuilder.writeList($listCode);';
              }
            } else if (fieldType.typeName == 'String') {
              writeCode = '$offsetName = fbBuilder.writeString($valueName);';
            } else if (_idl.classes.containsKey(fieldType.typeName)) {
              writeCode = '$offsetName = $valueName.finish(fbBuilder);';
            }
            if (writeCode != null) {
              if (condition == null) {
                out('if ($valueName != null) {');
              } else {
                out('if (!($valueName == null$condition)) {');
              }
              indent(() {
                out(writeCode);
              });
              out('}');
            }
          }
          // Write the table.
          out('fbBuilder.startTable();');
          for (idlModel.FieldDeclaration field in cls.fields) {
            int index = field.id;
            idlModel.FieldType fieldType = field.type;
            String valueName = '_' + field.name;
            String condition = '$valueName != null';
            String writeCode;
            if (fieldType.isList ||
                fieldType.typeName == 'String' ||
                _idl.classes.containsKey(fieldType.typeName)) {
              String offsetName = 'offset_' + field.name;
              condition = '$offsetName != null';
              writeCode = 'fbBuilder.addOffset($index, $offsetName);';
            } else if (fieldType.typeName == 'bool') {
              condition = '$valueName == true';
              writeCode = 'fbBuilder.addBool($index, true);';
            } else if (fieldType.typeName == 'double') {
              condition += ' && $valueName != ${defaultValue(fieldType, true)}';
              writeCode = 'fbBuilder.addFloat64($index, $valueName);';
            } else if (fieldType.typeName == 'int') {
              condition += ' && $valueName != ${defaultValue(fieldType, true)}';
              writeCode = 'fbBuilder.addUint32($index, $valueName);';
            } else if (_idl.enums.containsKey(fieldType.typeName)) {
              condition += ' && $valueName != ${defaultValue(fieldType, true)}';
              writeCode = 'fbBuilder.addUint8($index, $valueName.index);';
            }
            if (writeCode == null) {
              throw new UnimplementedError('Writing type ${fieldType.typeName}');
            }
            out('if ($condition) {');
            indent(() {
              out(writeCode);
            });
            out('}');
          }
          out('return fbBuilder.endTable();');
        });
        out('}');
      });
      out('}');
    }
  
    void _generateEnumReader(idlModel.EnumDeclaration enm) {
      String name = enm.name;
      String readerName = '_${name}Reader';
      String count = '${idlPrefix(name)}.values.length';
      String def = '${idlPrefix(name)}.${enm.values[0].name}';
      out('class $readerName extends fb.Reader<${idlPrefix(name)}> {');
      indent(() {
        out('const $readerName() : super();');
        out();
        out('@override');
        out('int get size => 1;');
        out();
        out('@override');
        out('${idlPrefix(name)} read(fb.BufferContext bc, int offset) {');
        indent(() {
          out('int index = const fb.Uint8Reader().read(bc, offset);');
          out('return index < $count ? ${idlPrefix(name)}.values[index] : $def;');
        });
        out('}');
      });
      out('}');
    }
  
    void _generateImpl(idlModel.ClassDeclaration cls) {
      String name = cls.name;
      String implName = '_${name}Impl';
      String mixinName = '_${name}Mixin';
  
negative.code:     });
      out('}');
    }
  }
  
  class _ReaderGenerator extends _BaseGenerator {
    final idlModel.ClassDeclaration cls;
  
    _ReaderGenerator(idlModel.Idl idl, StringBuffer outBuffer, this.cls)
        : super(idl, outBuffer);
  
    void generateReader() {
      String name = cls.name;
      String readerName = '_${name}Reader';
      String implName = '_${name}Impl';
  