| Index: dart/pkg/analyzer/lib/src/generated/engine.dart
 | 
| ===================================================================
 | 
| --- dart/pkg/analyzer/lib/src/generated/engine.dart	(revision 29785)
 | 
| +++ dart/pkg/analyzer/lib/src/generated/engine.dart	(working copy)
 | 
| @@ -4054,7 +4054,7 @@
 | 
|    AnalysisTask get nextTaskAnalysisTask {
 | 
|      {
 | 
|        bool hintsEnabled = _options.hint;
 | 
| -      if (_incrementalAnalysisCache != null) {
 | 
| +      if (_incrementalAnalysisCache != null && _incrementalAnalysisCache.hasWork()) {
 | 
|          AnalysisTask task = new IncrementalAnalysisTask(this, _incrementalAnalysisCache);
 | 
|          _incrementalAnalysisCache = null;
 | 
|        }
 | 
| @@ -4596,6 +4596,7 @@
 | 
|        if (unit != null) {
 | 
|          ChangeNoticeImpl notice = getNotice(task.source);
 | 
|          notice.compilationUnit = unit;
 | 
| +        _incrementalAnalysisCache = IncrementalAnalysisCache.cacheResult(task.cache, unit);
 | 
|        }
 | 
|      }
 | 
|      return null;
 | 
| @@ -4646,6 +4647,7 @@
 | 
|            dartCopy.setValue(DartEntry.INCLUDED_PARTS, task.includedSources);
 | 
|            ChangeNoticeImpl notice = getNotice(source);
 | 
|            notice.setErrors(dartEntry.allErrors, lineInfo);
 | 
| +          _incrementalAnalysisCache = IncrementalAnalysisCache.verifyStructure(_incrementalAnalysisCache, source, task.compilationUnit);
 | 
|          } else {
 | 
|            dartCopy.recordParseError();
 | 
|          }
 | 
| @@ -5588,6 +5590,21 @@
 | 
|  class IncrementalAnalysisCache {
 | 
|  
 | 
|    /**
 | 
| +   * Determine if the incremental analysis result can be cached for the next incremental analysis.
 | 
| +   *
 | 
| +   * @param cache the prior incremental analysis cache
 | 
| +   * @param unit the incrementally updated compilation unit
 | 
| +   * @return the cache used for incremental analysis or `null` if incremental analysis results
 | 
| +   *         cannot be cached for the next incremental analysis
 | 
| +   */
 | 
| +  static IncrementalAnalysisCache cacheResult(IncrementalAnalysisCache cache, CompilationUnit unit) {
 | 
| +    if (cache != null && unit != null) {
 | 
| +      return new IncrementalAnalysisCache(cache.librarySource, cache.source, unit, cache.newContents, cache.newContents, 0, 0, 0);
 | 
| +    }
 | 
| +    return null;
 | 
| +  }
 | 
| +
 | 
| +  /**
 | 
|     * Determine if the cache should be cleared.
 | 
|     *
 | 
|     * @param cache the prior cache or `null` if none
 | 
| @@ -5617,20 +5634,19 @@
 | 
|     *         be performed
 | 
|     */
 | 
|    static IncrementalAnalysisCache update(IncrementalAnalysisCache cache, Source source, String oldContents, String newContents, int offset, int oldLength, int newLength, SourceEntry sourceEntry) {
 | 
| -    if (cache == null || cache.source != source) {
 | 
| -      if (sourceEntry is! DartEntryImpl) {
 | 
| -        return null;
 | 
| -      }
 | 
| +    Source librarySource = null;
 | 
| +    CompilationUnit unit = null;
 | 
| +    if (sourceEntry is DartEntryImpl) {
 | 
|        DartEntryImpl dartEntry = sourceEntry as DartEntryImpl;
 | 
|        List<Source> librarySources = dartEntry.librariesContaining;
 | 
| -      if (librarySources.length != 1) {
 | 
| -        return null;
 | 
| +      if (librarySources.length == 1) {
 | 
| +        librarySource = librarySources[0];
 | 
| +        if (librarySource != null) {
 | 
| +          unit = dartEntry.getValue2(DartEntry.RESOLVED_UNIT, librarySource);
 | 
| +        }
 | 
|        }
 | 
| -      Source librarySource = librarySources[0];
 | 
| -      if (librarySource == null) {
 | 
| -        return null;
 | 
| -      }
 | 
| -      CompilationUnit unit = dartEntry.getValue2(DartEntry.RESOLVED_UNIT, librarySource);
 | 
| +    }
 | 
| +    if (cache == null || cache.source != source || unit != null) {
 | 
|        if (unit == null) {
 | 
|          return null;
 | 
|        }
 | 
| @@ -5642,13 +5658,38 @@
 | 
|        }
 | 
|        return new IncrementalAnalysisCache(librarySource, source, unit, oldContents, newContents, offset, oldLength, newLength);
 | 
|      }
 | 
| -    if (cache.offset > offset || offset > cache.offset + cache.newLength) {
 | 
| -      return null;
 | 
| +    if (cache.oldLength == 0 && cache.newLength == 0) {
 | 
| +      cache.offset = offset;
 | 
| +      cache.oldLength = oldLength;
 | 
| +      cache.newLength = newLength;
 | 
| +    } else {
 | 
| +      if (cache.offset > offset || offset > cache.offset + cache.newLength) {
 | 
| +        return null;
 | 
| +      }
 | 
| +      cache.newLength += newLength - oldLength;
 | 
|      }
 | 
|      cache.newContents = newContents;
 | 
| -    cache.newLength += newLength - oldLength;
 | 
|      return cache;
 | 
|    }
 | 
| +
 | 
| +  /**
 | 
| +   * Verify that the incrementally parsed and resolved unit in the incremental cache is structurally
 | 
| +   * equivalent to the fully parsed unit.
 | 
| +   *
 | 
| +   * @param cache the prior cache or `null` if none
 | 
| +   * @param source the source of the compilation unit that was parsed (not `null`)
 | 
| +   * @param unit the compilation unit that was just parsed
 | 
| +   * @return the cache used for incremental analysis or `null` if incremental analysis results
 | 
| +   *         cannot be cached for the next incremental analysis
 | 
| +   */
 | 
| +  static IncrementalAnalysisCache verifyStructure(IncrementalAnalysisCache cache, Source source, CompilationUnit unit) {
 | 
| +    if (cache != null && unit != null && cache.source == source) {
 | 
| +      if (!ASTComparator.equals3(cache.resolvedUnit, unit)) {
 | 
| +        return null;
 | 
| +      }
 | 
| +    }
 | 
| +    return cache;
 | 
| +  }
 | 
|    Source librarySource;
 | 
|    Source source;
 | 
|    String oldContents;
 | 
| @@ -5667,6 +5708,13 @@
 | 
|      this.oldLength = oldLength;
 | 
|      this.newLength = newLength;
 | 
|    }
 | 
| +
 | 
| +  /**
 | 
| +   * Determine if the cache contains source changes that need to be analyzed
 | 
| +   *
 | 
| +   * @return `true` if the cache contains changes to be analyzed, else `false`
 | 
| +   */
 | 
| +  bool hasWork() => oldLength > 0 && newLength > 0;
 | 
|  }
 | 
|  /**
 | 
|   * Instances of the class `InstrumentedAnalysisContextImpl` implement an
 | 
| @@ -6841,7 +6889,7 @@
 | 
|    /**
 | 
|     * The information used to perform incremental analysis.
 | 
|     */
 | 
| -  IncrementalAnalysisCache _cache;
 | 
| +  IncrementalAnalysisCache cache;
 | 
|  
 | 
|    /**
 | 
|     * The compilation unit that was produced by incrementally updating the existing unit.
 | 
| @@ -6855,7 +6903,7 @@
 | 
|     * @param cache the incremental analysis cache used to perform the analysis
 | 
|     */
 | 
|    IncrementalAnalysisTask(InternalAnalysisContext context, IncrementalAnalysisCache cache) : super(context) {
 | 
| -    this._cache = cache;
 | 
| +    this.cache = cache;
 | 
|    }
 | 
|    accept(AnalysisTaskVisitor visitor) => visitor.visitIncrementalAnalysisTask(this);
 | 
|  
 | 
| @@ -6864,13 +6912,13 @@
 | 
|     *
 | 
|     * @return the source
 | 
|     */
 | 
| -  Source get source => _cache != null ? _cache.source : null;
 | 
| -  String get taskDescription => "incremental analysis ${(_cache != null ? _cache.source : "null")}";
 | 
| +  Source get source => cache != null ? cache.source : null;
 | 
| +  String get taskDescription => "incremental analysis ${(cache != null ? cache.source : "null")}";
 | 
|    void internalPerform() {
 | 
| -    if (_cache == null) {
 | 
| +    if (cache == null) {
 | 
|        return;
 | 
|      }
 | 
| -    compilationUnit = _cache.resolvedUnit;
 | 
| +    compilationUnit = cache.resolvedUnit;
 | 
|    }
 | 
|  }
 | 
|  /**
 | 
| 
 |