anchor.diff:    final SourceFactory _sourceFactory;
   
     /**
  -   * This [ContentCache] is consulted for a file content before reading
  -   * the content from the file.
  -   */
  -  final ContentCache _contentCache;
  -
  -  AnalysisDriver(this._byteStore, this._sourceFactory, this._contentCache);
  +   * The analysis options to analyze with.
  +   */
  +  final AnalysisOptions _analysisOptions;
  +
  +  /**
  +   * The combined unlinked and linked package for the SDK, extracted from
  +   * the given [_sourceFactory].
  +   */
  +  PackageBundle _sdkBundle;
  +
  +  /**
  +   * The mapping from the files for which analysis was requested using
  +   * [getResult] to the [Completer]s to report the result.
  +   */
  +  final _requestedFiles = <String, Completer<AnalysisResult>>{};
  +
  +  /**
  +   * The set of explicitly analyzed files.
  +   */
  +  final _explicitFiles = new LinkedHashSet<String>();
  +
  +  /**
  +   * The set of files that are currently scheduled for analysis.
  +   */
  +  final _filesToAnalyze = new LinkedHashSet<String>();
  +
  +  /**
  +   * The current file state.
  +   *
  +   * It maps file paths to MD5 hash of the file content.
  +   */
  +  final _fileContentHashMap = <String, String>{};
  +
  +  /**
  +   * TODO(scheglov) document and improve
  +   */
  +  final _hasWorkStreamController = new StreamController<String>();
  +
  +  AnalysisDriver(this._logger, this._resourceProvider, this._byteStore,
  +      this._contentCache, this._sourceFactory, this._analysisOptions) {
  +    _sdkBundle = _sourceFactory.dartSdk.getLinkedBundle();
  +  }
   
     /**
      * Set the list of files that the driver should try to analyze sooner.
  
positive.code:    * [changeFile] invocation.
     */
    void changeFile(String path) {
      // TODO(scheglov) implement
    }
  
    /**
  
negative.code:    * but does not guarantee this.
     */
    void removeFile(String path) {
      _explicitFiles.remove(path);
      _filesToAnalyze.remove(path);
    }
  
    /**
     * TODO(scheglov) replace with actual [AnalysisResult] computing.
     */
    List<String> _computeAndPrintErrors(_File file) {
      List<String> errorStrings = _logger.run('Compute errors $file', () {
        LibraryContext libraryContext = _createLibraryContext(file);
  
        String errorsKey;
        {
          ApiSignature signature = new ApiSignature();
          signature.addString(libraryContext.node.linkedHash);
          signature.addString(file.contentHash);
          errorsKey = '${signature.toHex()}.errors';
        }
  
        {
          List<int> bytes = _byteStore.get(errorsKey);
          if (bytes != null) {
            fb.BufferContext bp = new fb.BufferContext.fromBytes(bytes);
            int table = bp.derefObject(0);
            return const fb.ListReader<String>(const fb.StringReader())
                .vTableGet(bp, table, 0);
          }
        }
  
        AnalysisContext analysisContext = _createAnalysisContext(libraryContext);
        analysisContext.resolveCompilationUnit2(
            libraryContext.file.source, libraryContext.file.source);
        try {
          List<AnalysisError> errors;
          try {
            errors = _logger.runTimed('Computed errors', () {
              return analysisContext.computeErrors(file.source);
            });
          } catch (e, st) {
            // TODO(scheglov) why does it fail?
            // Caused by Bad state: Unmatched TypeParameterElementImpl T
            errors = [];
          }
          List<String> errorStrings = errors
              .where((error) => error.errorCode is! TodoCode)
              .map((error) => error.toString())
              .toList();
          {
            fb.Builder fbBuilder = new fb.Builder();
            var exportedOffset = fbBuilder.writeList(errorStrings
                .map((errorStr) => fbBuilder.writeString(errorStr))
                .toList());
            fbBuilder.startTable();
            fbBuilder.addOffset(0, exportedOffset);
            var offset = fbBuilder.endTable();
            List<int> bytes = fbBuilder.finish(offset, 'CErr');
            _byteStore.put(errorsKey, bytes);
          }
  
          return errorStrings;
        } finally {
          analysisContext.dispose();
        }
      });
  
      if (errorStrings.isNotEmpty) {
        errorStrings.forEach((errorString) => print('\t$errorString'));
      } else {
        print('\tNO ERRORS');
      }
      return errorStrings;
    }
  
    AnalysisContext _createAnalysisContext(LibraryContext libraryContext) {
      AnalysisContextImpl analysisContext =
          AnalysisEngine.instance.createAnalysisContext();
  
      analysisContext.sourceFactory =
          new SourceFactory((_sourceFactory as SourceFactoryImpl).resolvers);
      analysisContext.resultProvider =
          new InputPackagesResultProvider(analysisContext, libraryContext.store);
      analysisContext
          .applyChanges(new ChangeSet()..addedSource(libraryContext.file.source));
      return analysisContext;
    }
  
    /**
     * Return the content in which the library represented by the given
     * [libraryFile] should be analyzed it.
     *
     * TODO(scheglov) We often don't need [SummaryDataStore], only linked hash.
     */
    LibraryContext _createLibraryContext(_File libraryFile) {
      Map<String, _LibraryNode> nodes = <String, _LibraryNode>{};
  
      return _logger.run('Create library context', () {
        SummaryDataStore store = new SummaryDataStore(const <String>[]);
        store.addBundle(null, _sdkBundle);
  
        void createLibraryNodes(_File libraryFile) {
          Uri libraryUri = libraryFile.uri;
          if (libraryUri.scheme == 'dart') {
            return;
          }
          String uriStr = libraryUri.toString();
          if (!nodes.containsKey(uriStr)) {
            _LibraryNode node = new _LibraryNode(this, nodes, libraryUri);
            nodes[uriStr] = node;
            ReferencedUris referenced = _getReferencedUris(libraryFile);
  
            // Append unlinked bundles.
            for (String uri in referenced.parted) {
              _File file = libraryFile.resolveUri(uri);
              PackageBundle unlinked = _getUnlinked(file);
              node.unlinkedBundles.add(unlinked);
              store.addBundle(null, unlinked);
            }
  
            // Create nodes for referenced libraries.
            for (String uri in referenced.imported) {
              _File file = libraryFile.resolveUri(uri);
              createLibraryNodes(file);
            }
            for (String uri in referenced.exported) {
              _File file = libraryFile.resolveUri(uri);
              createLibraryNodes(file);
            }
          }
        }
  
        _logger.runTimed2(() {
          createLibraryNodes(libraryFile);
        }, () => 'Computed ${nodes.length} nodes');
        _LibraryNode libraryNode = nodes[libraryFile.uri.toString()];
  
        Set<String> libraryUrisToLink = new Set<String>();
        int numberOfNodesWithLinked = 0;
        _logger.runTimed2(() {
          for (_LibraryNode node in nodes.values) {
            String key = '${node.linkedHash}.linked';
            List<int> bytes = _byteStore.get(key);
            if (bytes != null) {
              PackageBundle linked = new PackageBundle.fromBuffer(bytes);
              node.linked = linked;
              store.addBundle(null, linked);
              numberOfNodesWithLinked++;
            } else {
              libraryUrisToLink.add(node.uri.toString());
            }
          }
        }, () => 'Loaded $numberOfNodesWithLinked linked bundles');
  
        Map<String, LinkedLibraryBuilder> linkedLibraries = {};
        _logger.runTimed2(() {
          linkedLibraries = link(libraryUrisToLink, (String uri) {
            LinkedLibrary linkedLibrary = store.linkedMap[uri];
            if (linkedLibrary == null) {
              throw new StateError('No linked library for: $uri');
            }
            return linkedLibrary;
          }, (String uri) {
            UnlinkedUnit unlinkedUnit = store.unlinkedMap[uri];
            if (unlinkedUnit == null) {
              throw new StateError('No unlinked unit for: $uri');
            }
            return unlinkedUnit;
          }, (_) => null, _analysisOptions.strongMode);
        }, () => 'Linked ${linkedLibraries.length} bundles');
  
        linkedLibraries.forEach((uri, linkedBuilder) {
          _LibraryNode node = nodes[uri];
          String key = '${node.linkedHash}.linked';
          List<int> bytes;
          {
            PackageBundleAssembler assembler = new PackageBundleAssembler();
            assembler.addLinkedLibrary(uri, linkedBuilder);
            bytes = assembler.assemble().toBuffer();
          }
          PackageBundle linked = new PackageBundle.fromBuffer(bytes);
          node.linked = linked;
          store.addBundle(null, linked);
          _byteStore.put(key, bytes);
        });
  
        return new LibraryContext(libraryFile, libraryNode, store);
      });
    }
  
    /**
     * Return the [_File] for the given [path] in [_sourceFactory].
     */
    _File _fileForPath(String path) {
      Source fileSource = _resourceProvider.getFile(path).createSource();
      Uri uri = _sourceFactory.restoreUri(fileSource);
      Source source = _resourceProvider.getFile(path).createSource(uri);
      return new _File(this, source);
    }
  
    /**
     * TODO(scheglov) It would be nice to get URIs of "parts" from unlinked.
     */
    ReferencedUris _getReferencedUris(_File file) {
      // Try to get from the store.
      {
        String key = '${file.contentHash}.uris';
        List<int> bytes = _byteStore.get(key);
        if (bytes != null) {
          fb.BufferContext bp = new fb.BufferContext.fromBytes(bytes);
          int table = bp.derefObject(0);
          const fb.ListReader<String> stringListReader =
              const fb.ListReader<String>(const fb.StringReader());
          bool isLibrary = const fb.BoolReader().vTableGet(bp, table, 0);
          List<String> imported = stringListReader.vTableGet(bp, table, 1);
          List<String> exported = stringListReader.vTableGet(bp, table, 2);
          List<String> parted = stringListReader.vTableGet(bp, table, 3);
          ReferencedUris referencedUris = new ReferencedUris();
          referencedUris.isLibrary = isLibrary;
          referencedUris.imported.addAll(imported);
          referencedUris.exported.addAll(exported);
          referencedUris.parted.addAll(parted);
          return referencedUris;
        }
      }
  
      // Compute URIs.
      ReferencedUris referencedUris = new ReferencedUris();
      referencedUris.parted.add(file.uri.toString());
      for (Directive directive in file.unit.directives) {
        if (directive is PartOfDirective) {
          referencedUris.isLibrary = false;
        } else if (directive is UriBasedDirective) {
          String uri = directive.uri.stringValue;
          if (directive is ImportDirective) {
            referencedUris.imported.add(uri);
          } else if (directive is ExportDirective) {
            referencedUris.exported.add(uri);
          } else if (directive is PartDirective) {
            referencedUris.parted.add(uri);
          }
        }
      }
  
      // Serialize into bytes.
      List<int> bytes;
      {
        fb.Builder fbBuilder = new fb.Builder();
        var importedOffset = fbBuilder.writeList(referencedUris.imported
            .map((uri) => fbBuilder.writeString(uri))
            .toList());
        var exportedOffset = fbBuilder.writeList(referencedUris.exported
            .map((uri) => fbBuilder.writeString(uri))
            .toList());
        var partedOffset = fbBuilder.writeList(referencedUris.parted
            .map((uri) => fbBuilder.writeString(uri))
            .toList());
        fbBuilder.startTable();
        fbBuilder.addBool(0, referencedUris.isLibrary);
        fbBuilder.addOffset(1, importedOffset);
        fbBuilder.addOffset(2, exportedOffset);
        fbBuilder.addOffset(3, partedOffset);
        var offset = fbBuilder.endTable();
        bytes = fbBuilder.finish(offset, 'SoRU');
      }
  
      // We read the content and recomputed the hash.
      // So, we need to update the key.
      String key = '${file.contentHash}.uris';
      _byteStore.put(key, bytes);
  
      return referencedUris;
    }
  
    /**
     * Return the unlinked bundle of [file] for the current file state.
     *
     * That is, if there is an existing bundle for the current content hash
     * of the [file] in the [_byteStore], then it is returned. Otherwise, the
     * [file] content is read, the content hash is computed and the current file
     * state is updated accordingly. That the content is parsed into the
     * [CompilationUnit] and serialized into a new unlinked bundle. The bundle
     * is then put into the [_byteStore] and returned.
     */
    PackageBundle _getUnlinked(_File file) {
      // Try to get bytes for file's unlinked bundle.
      List<int> bytes;
      {
        String key = '${file.contentHash}.unlinked';
        bytes = _byteStore.get(key);
      }
      // If no cached unlinked bundle, compute it.
      if (bytes == null) {
        _logger.runTimed('Create unlinked for $file', () {
          // We read the content and recomputed the hash.
          // So, we need to update the key.
          String key = '${file.contentHash}.unlinked';
          UnlinkedUnitBuilder unlinkedUnit = serializeAstUnlinked(file.unit);
          PackageBundleAssembler assembler = new PackageBundleAssembler();
          assembler.addUnlinkedUnitWithHash(
              file.uri.toString(), unlinkedUnit, key);
          bytes = assembler.assemble().toBuffer();
          _byteStore.put(key, bytes);
        });
      }
      return new PackageBundle.fromBuffer(bytes);
    }
  }
  
  