| Index: pkg/fletchc/lib/incremental/reuser.dart | 
| diff --git a/pkg/fletchc/lib/incremental/reuser.dart b/pkg/fletchc/lib/incremental/reuser.dart | 
| deleted file mode 100644 | 
| index 776c6fca1847725e57757a9d36e48fce4d357c1d..0000000000000000000000000000000000000000 | 
| --- a/pkg/fletchc/lib/incremental/reuser.dart | 
| +++ /dev/null | 
| @@ -1,1185 +0,0 @@ | 
| -// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file | 
| -// for details. All rights reserved. Use of this source code is governed by a | 
| -// BSD-style license that can be found in the LICENSE file. | 
| - | 
| -library fletchc_incremental.reuser; | 
| - | 
| -import 'dart:async' show | 
| -    Future; | 
| - | 
| -import 'package:compiler/compiler.dart' as api; | 
| - | 
| -import 'package:compiler/src/compiler.dart' show | 
| -    Compiler; | 
| - | 
| -import 'package:compiler/src/diagnostics/messages.dart' show | 
| -    MessageKind; | 
| - | 
| -import 'package:compiler/src/script.dart' show | 
| -    Script; | 
| - | 
| -import 'package:compiler/src/elements/elements.dart' show | 
| -    ClassElement, | 
| -    CompilationUnitElement, | 
| -    Element, | 
| -    LibraryElement, | 
| -    STATE_NOT_STARTED, | 
| -    ScopeContainerElement, | 
| -    TypeDeclarationElement; | 
| - | 
| -import 'package:compiler/src/tokens/token_constants.dart' show | 
| -    EOF_TOKEN; | 
| - | 
| -import 'package:compiler/src/tokens/token.dart' show | 
| -    Token; | 
| - | 
| -import 'package:compiler/src/parser/partial_elements.dart' show | 
| -    PartialClassElement, | 
| -    PartialElement, | 
| -    PartialFieldList, | 
| -    PartialFunctionElement; | 
| - | 
| -import 'package:compiler/src/scanner/scanner.dart' show | 
| -    Scanner; | 
| - | 
| -import 'package:compiler/src/parser/parser.dart' show | 
| -    Parser; | 
| - | 
| -import 'package:compiler/src/parser/listener.dart' show | 
| -    Listener; | 
| - | 
| -import 'package:compiler/src/parser/node_listener.dart' show | 
| -    NodeListener; | 
| - | 
| -import 'package:compiler/src/io/source_file.dart' show | 
| -    CachingUtf8BytesSourceFile, | 
| -    SourceFile, | 
| -    StringSourceFile; | 
| - | 
| -import 'package:compiler/src/tree/tree.dart' show | 
| -    ClassNode, | 
| -    FunctionExpression, | 
| -    LibraryTag, | 
| -    NodeList, | 
| -    unparse; | 
| - | 
| -import 'package:compiler/src/util/util.dart' show | 
| -    Link; | 
| - | 
| -import 'package:compiler/src/elements/modelx.dart' show | 
| -    ClassElementX, | 
| -    CompilationUnitElementX, | 
| -    DeclarationSite, | 
| -    ElementX, | 
| -    FieldElementX, | 
| -    LibraryElementX; | 
| - | 
| -import 'package:compiler/src/constants/values.dart' show | 
| -    ConstantValue; | 
| - | 
| -import 'package:compiler/src/library_loader.dart' show | 
| -    TagState; | 
| - | 
| -import '../incremental_backend.dart' show | 
| -    IncrementalBackend; | 
| - | 
| -import 'diff.dart' show | 
| -    Difference, | 
| -    computeDifference; | 
| - | 
| -import 'fletchc_incremental.dart' show | 
| -    IncrementalCompilationFailed; | 
| - | 
| -typedef void Logger(message); | 
| - | 
| -typedef bool ReuseFunction( | 
| -    Token diffToken, | 
| -    PartialElement before, | 
| -    PartialElement after); | 
| - | 
| -class FailedUpdate { | 
| -  /// Either an [Element] or a [Difference]. | 
| -  final context; | 
| -  final String message; | 
| - | 
| -  FailedUpdate(this.context, this.message); | 
| - | 
| -  String toString() { | 
| -    if (context == null) return '$message'; | 
| -    return 'In $context:\n  $message'; | 
| -  } | 
| -} | 
| - | 
| -abstract class Reuser { | 
| -  final Compiler compiler; | 
| - | 
| -  final api.CompilerInputProvider inputProvider; | 
| - | 
| -  final Logger logTime; | 
| - | 
| -  final Logger logVerbose; | 
| - | 
| -  final List<Update> updates = <Update>[]; | 
| - | 
| -  final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; | 
| - | 
| -  final Set<ElementX> _elementsToInvalidate = new Set<ElementX>(); | 
| - | 
| -  final Set<ElementX> _removedElements = new Set<ElementX>(); | 
| - | 
| -  final Map<Uri, Future> _sources = <Uri, Future>{}; | 
| - | 
| -  /// Cached tokens of entry compilation units. | 
| -  final Map<LibraryElementX, Token> _entryUnitTokens = | 
| -      <LibraryElementX, Token>{}; | 
| - | 
| -  /// Cached source files for entry compilation units. | 
| -  final Map<LibraryElementX, SourceFile> _entrySourceFiles = | 
| -      <LibraryElementX, SourceFile>{}; | 
| - | 
| -  Reuser( | 
| -      this.compiler, | 
| -      this.inputProvider, | 
| -      this.logTime, | 
| -      this.logVerbose); | 
| - | 
| -  IncrementalBackend get backend; | 
| - | 
| -  /// When [true], updates must be applied (using [applyUpdates]) before the | 
| -  /// [compiler]'s state correctly reflects the updated program. | 
| -  bool get hasPendingUpdates => updates.isNotEmpty; | 
| - | 
| -  bool get failed => _failedUpdates.isNotEmpty; | 
| - | 
| -  /// Used as tear-off passed to [LibraryLoaderTask.resetLibraries]. | 
| -  Future<Iterable<LibraryElement>> reuseLibraries( | 
| -      Iterable<LibraryElement> libraries) async { | 
| -    List<LibraryElement> reusedLibraries = <LibraryElement>[]; | 
| -    for (LibraryElement library in libraries) { | 
| -      if (await _reuseLibrary(library)) { | 
| -        reusedLibraries.add(library); | 
| -      } | 
| -    } | 
| -    return reusedLibraries; | 
| -  } | 
| - | 
| -  Future<bool> _reuseLibrary(LibraryElement library) async { | 
| -    assert(compiler != null); | 
| -    if (library.isPlatformLibrary) { | 
| -      logTime('Reusing $library (assumed read-only).'); | 
| -      return true; | 
| -    } | 
| -    try { | 
| -      if (await _haveTagsChanged(library)) { | 
| -        cannotReuse( | 
| -            library, | 
| -            "Changes to library, import, export, or part declarations not" | 
| -            " supported."); | 
| -        // We return true to here to avoid that the library loader tries to | 
| -        // load a different version of this library. | 
| -        return true; | 
| -      } | 
| - | 
| -      bool isChanged = false; | 
| -      List<Script> scripts = <Script>[]; | 
| - | 
| -      for (CompilationUnitElementX unit in library.compilationUnits) { | 
| -        Uri uri = unit.script.resourceUri; | 
| -        if (uriHasUpdate(uri)) { | 
| -          isChanged = true; | 
| -          scripts.add(await _updatedScript(unit.script, library)); | 
| -        } else { | 
| -          scripts.add(unit.script); | 
| -        } | 
| -      } | 
| - | 
| -      if (!isChanged) { | 
| -        logTime("Reusing $library, source didn't change."); | 
| -        return true; | 
| -      } | 
| - | 
| -      return canReuseLibrary(library, scripts); | 
| -    } finally { | 
| -      _cleanUp(library); | 
| -    } | 
| -  } | 
| - | 
| -  void _cleanUp(LibraryElementX library) { | 
| -    _entryUnitTokens.remove(library); | 
| -    _entrySourceFiles.remove(library); | 
| -  } | 
| - | 
| -  Future<Script> _updatedScript(Script before, LibraryElementX library) { | 
| -    if (before == library.entryCompilationUnit.script && | 
| -        _entrySourceFiles.containsKey(library)) { | 
| -      return new Future.value(before.copyWithFile(_entrySourceFiles[library])); | 
| -    } | 
| - | 
| -    return _readUri(before.resourceUri).then((bytes) { | 
| -      Uri uri = before.file.uri; | 
| -      String filename = before.file.filename; | 
| -      SourceFile sourceFile = bytes is String | 
| -          ? new StringSourceFile(uri, filename, bytes) | 
| -          : new CachingUtf8BytesSourceFile(uri, filename, bytes); | 
| -      return before.copyWithFile(sourceFile); | 
| -    }); | 
| -  } | 
| - | 
| -  Future<bool> _haveTagsChanged(LibraryElementX library) { | 
| -    Script before = library.entryCompilationUnit.script; | 
| -    if (!uriHasUpdate(before.resourceUri)) { | 
| -      // The entry compilation unit hasn't been updated. So the tags aren't | 
| -      // changed. | 
| -      return new Future<bool>.value(false); | 
| -    } | 
| - | 
| -    return _updatedScript(before, library).then((Script script) { | 
| -      _entrySourceFiles[library] = script.file; | 
| -      Token token = new Scanner(_entrySourceFiles[library]).tokenize(); | 
| -      _entryUnitTokens[library] = token; | 
| -      // Using two parsers to only create the nodes we want ([LibraryTag]). | 
| -      Parser parser = new Parser(new Listener()); | 
| -      Element entryCompilationUnit = library.entryCompilationUnit; | 
| -      NodeListener listener = new NodeListener( | 
| -          compiler.resolution.parsing | 
| -              .getScannerOptionsFor(entryCompilationUnit), | 
| -          compiler.reporter, entryCompilationUnit); | 
| -      Parser nodeParser = new Parser(listener); | 
| -      Iterator<LibraryTag> tags = library.tags.iterator; | 
| -      while (token.kind != EOF_TOKEN) { | 
| -        token = parser.parseMetadataStar(token); | 
| -        if (parser.optional('library', token) || | 
| -            parser.optional('import', token) || | 
| -            parser.optional('export', token) || | 
| -            parser.optional('part', token)) { | 
| -          if (!tags.moveNext()) return true; | 
| -          token = nodeParser.parseTopLevelDeclaration(token); | 
| -          LibraryTag tag = listener.popNode(); | 
| -          assert(listener.nodes.isEmpty); | 
| -          if (unparse(tags.current) != unparse(tag)) { | 
| -            return true; | 
| -          } | 
| -        } else { | 
| -          break; | 
| -        } | 
| -      } | 
| -      return tags.moveNext(); | 
| -    }); | 
| -  } | 
| - | 
| -  Future _readUri(Uri uri) { | 
| -    return _sources.putIfAbsent(uri, () => inputProvider(uri)); | 
| -  } | 
| - | 
| -  /// Returns true if [library] can be reused. | 
| -  /// | 
| -  /// This methods also computes the [updates] (patches) needed to have | 
| -  /// [library] reflect the modifications in [scripts]. | 
| -  bool canReuseLibrary(LibraryElement library, List<Script> scripts) { | 
| -    logTime('Attempting to reuse ${library}.'); | 
| - | 
| -    Uri entryUri = library.entryCompilationUnit.script.resourceUri; | 
| -    Script entryScript = | 
| -        scripts.singleWhere((Script script) => script.resourceUri == entryUri); | 
| -    LibraryElementX newLibrary = | 
| -        new LibraryElementX(entryScript, library.canonicalUri); | 
| -    if (_entryUnitTokens.containsKey(library)) { | 
| -      compiler.dietParser.dietParse( | 
| -          newLibrary.entryCompilationUnit, _entryUnitTokens[library]); | 
| -    } else { | 
| -      compiler.scanner.scanLibrary(newLibrary); | 
| -    } | 
| - | 
| -    TagState tagState = new TagState(); | 
| -    for (LibraryTag tag in newLibrary.tags) { | 
| -      if (tag.isImport) { | 
| -        tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler.reporter); | 
| -      } else if (tag.isExport) { | 
| -        tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler.reporter); | 
| -      } else if (tag.isLibraryName) { | 
| -        tagState.checkTag(TagState.LIBRARY, tag, compiler.reporter); | 
| -        if (newLibrary.libraryTag == null) { | 
| -          // Use the first if there are multiple (which is reported as an | 
| -          // error in [TagState.checkTag]). | 
| -          newLibrary.libraryTag = tag; | 
| -        } | 
| -      } else if (tag.isPart) { | 
| -        tagState.checkTag(TagState.PART, tag, compiler.reporter); | 
| -      } | 
| -    } | 
| - | 
| -    // TODO(ahe): Process tags using TagState, not | 
| -    // LibraryLoaderTask.processLibraryTags. | 
| -    Link<CompilationUnitElement> units = library.compilationUnits; | 
| -    for (Script script in scripts) { | 
| -      CompilationUnitElementX unit = units.head; | 
| -      units = units.tail; | 
| -      if (script != entryScript) { | 
| -        // TODO(ahe): Copied from library_loader. | 
| -        CompilationUnitElement newUnit = | 
| -            new CompilationUnitElementX(script, newLibrary); | 
| -        compiler.reporter.withCurrentElement(newUnit, () { | 
| -          compiler.scanner.scan(newUnit); | 
| -          if (unit.partTag == null) { | 
| -            compiler.reporter.reportErrorMessage( | 
| -                unit, MessageKind.MISSING_PART_OF_TAG); | 
| -          } | 
| -        }); | 
| -      } | 
| -    } | 
| - | 
| -    logTime('New library synthesized.'); | 
| -    return canReuseScopeContainerElement(library, newLibrary); | 
| -  } | 
| - | 
| -  bool cannotReuse(context, String message) { | 
| -    _failedUpdates.add(new FailedUpdate(context, message)); | 
| -    logVerbose(message); | 
| -    return false; | 
| -  } | 
| - | 
| -  bool canReuseScopeContainerElement( | 
| -      ScopeContainerElement element, | 
| -      ScopeContainerElement newElement) { | 
| -    if (checkForGenericTypes(element)) return false; | 
| -    if (checkForGenericTypes(newElement)) return false; | 
| -    List<Difference> differences = computeDifference(element, newElement); | 
| -    logTime('Differences computed.'); | 
| -    for (Difference difference in differences) { | 
| -      logTime('Looking at difference: $difference'); | 
| - | 
| -      if (difference.before == null && difference.after is PartialElement) { | 
| -        canReuseAddedElement(difference.after, element, newElement); | 
| -        continue; | 
| -      } | 
| -      if (difference.after == null && difference.before is PartialElement) { | 
| -        canReuseRemovedElement(difference.before, element); | 
| -        continue; | 
| -      } | 
| -      Token diffToken = difference.token; | 
| -      if (diffToken == null) { | 
| -        cannotReuse(difference, "No difference token."); | 
| -        continue; | 
| -      } | 
| -      if (difference.after is! PartialElement && | 
| -          difference.before is! PartialElement) { | 
| -        cannotReuse(difference, "Don't know how to recompile."); | 
| -        continue; | 
| -      } | 
| -      PartialElement before = difference.before; | 
| -      PartialElement after = difference.after; | 
| - | 
| -      ReuseFunction reuser; | 
| - | 
| -      if (before is PartialFunctionElement && after is PartialFunctionElement) { | 
| -        reuser = canReuseFunction; | 
| -      } else if (before is PartialClassElement && | 
| -                 after is PartialClassElement) { | 
| -        reuser = canReuseClass; | 
| -      } else { | 
| -        reuser = unableToReuse; | 
| -      } | 
| -      if (!reuser(diffToken, before, after)) { | 
| -        assert(_failedUpdates.isNotEmpty); | 
| -        continue; | 
| -      } | 
| -    } | 
| - | 
| -    return _failedUpdates.isEmpty; | 
| -  } | 
| - | 
| -  bool canReuseAddedElement( | 
| -      PartialElement element, | 
| -      ScopeContainerElement container, | 
| -      ScopeContainerElement syntheticContainer) { | 
| -    if (!allowAddedElement(element)) return false; | 
| -    if (element is PartialFunctionElement) { | 
| -      addFunction(element, container); | 
| -      return true; | 
| -    } else if (element is PartialClassElement) { | 
| -      addClass(element, container); | 
| -      return true; | 
| -    } else if (element is PartialFieldList) { | 
| -      addFields(element, container, syntheticContainer); | 
| -      return true; | 
| -    } | 
| -    return cannotReuse(element, "Adding ${element.runtimeType} not supported."); | 
| -  } | 
| - | 
| -  void addFunction( | 
| -      PartialFunctionElement element, | 
| -      /* ScopeContainerElement */ container) { | 
| -    invalidateScopesAffectedBy(element, container); | 
| - | 
| -    addAddedFunctionUpdate(compiler, element, container); | 
| -  } | 
| - | 
| -  void addClass( | 
| -      PartialClassElement element, | 
| -      LibraryElementX library) { | 
| -    invalidateScopesAffectedBy(element, library); | 
| - | 
| -    addAddedClassUpdate(compiler, element, library); | 
| -  } | 
| - | 
| -  /// Called when a field in [definition] has changed. | 
| -  /// | 
| -  /// There's no direct link from a [PartialFieldList] to its implied | 
| -  /// [FieldElementX], so instead we use [syntheticContainer], the (synthetic) | 
| -  /// container created by [canReuseLibrary], or [canReuseClass] (through | 
| -  /// [PartialClassElement.parseNode]). This container is scanned looking for | 
| -  /// fields whose declaration site is [definition]. | 
| -  // TODO(ahe): It would be nice if [computeDifference] returned this | 
| -  // information directly. | 
| -  void addFields( | 
| -      PartialFieldList definition, | 
| -      ScopeContainerElement container, | 
| -      ScopeContainerElement syntheticContainer) { | 
| -    List<FieldElementX> fields = <FieldElementX>[]; | 
| -    syntheticContainer.forEachLocalMember((ElementX member) { | 
| -      if (member.declarationSite == definition) { | 
| -        fields.add(member); | 
| -      } | 
| -    }); | 
| -    for (FieldElementX field in fields) { | 
| -      // TODO(ahe): This only works when there's one field per | 
| -      // PartialFieldList. | 
| -      addField(field, container); | 
| -    } | 
| -  } | 
| - | 
| -  void addField(FieldElementX element, ScopeContainerElement container) { | 
| -    logVerbose("Add field $element to $container."); | 
| -    invalidateScopesAffectedBy(element, container); | 
| -    addAddedFieldUpdate(compiler, element, container); | 
| -  } | 
| - | 
| -  bool canReuseRemovedElement( | 
| -      PartialElement element, | 
| -      ScopeContainerElement container) { | 
| -    if (!allowRemovedElement(element)) return false; | 
| -    if (element is PartialFunctionElement) { | 
| -      removeFunction(element); | 
| -      return true; | 
| -    } else if (element is PartialClassElement) { | 
| -      removeClass(element); | 
| -      return true; | 
| -    } else if (element is PartialFieldList) { | 
| -      removeFields(element, container); | 
| -      return true; | 
| -    } | 
| -    return cannotReuse( | 
| -        element, "Removing ${element.runtimeType} not supported."); | 
| -  } | 
| - | 
| -  void removeFunction(PartialFunctionElement element) { | 
| -    logVerbose("Removed method $element."); | 
| - | 
| -    invalidateScopesAffectedBy(element, element.enclosingElement); | 
| - | 
| -    _removedElements.add(element); | 
| - | 
| -    addRemovedFunctionUpdate(compiler, element); | 
| -  } | 
| - | 
| -  void removeClass(PartialClassElement element) { | 
| -    logVerbose("Removed class $element."); | 
| - | 
| -    invalidateScopesAffectedBy(element, element.library); | 
| - | 
| -    _removedElements.add(element); | 
| -    element.forEachLocalMember((ElementX member) { | 
| -      _removedElements.add(member); | 
| -    }); | 
| - | 
| -    addRemovedClassUpdate(compiler, element); | 
| -  } | 
| - | 
| -  void removeFields( | 
| -      PartialFieldList definition, | 
| -      ScopeContainerElement container) { | 
| -    List<FieldElementX> fields = <FieldElementX>[]; | 
| -    container.forEachLocalMember((ElementX member) { | 
| -      if (member.declarationSite == definition) { | 
| -        fields.add(member); | 
| -      } | 
| -    }); | 
| -    for (FieldElementX field in fields) { | 
| -      // TODO(ahe): This only works when there's one field per | 
| -      // PartialFieldList. | 
| -      removeField(field); | 
| -    } | 
| -  } | 
| - | 
| -  void removeField(FieldElementX element) { | 
| -    logVerbose("Removed field $element."); | 
| -    if (!element.isInstanceMember) { | 
| -      cannotReuse(element, "Not an instance field."); | 
| -    } else { | 
| -      removeInstanceField(element); | 
| -    } | 
| -  } | 
| - | 
| -  void removeInstanceField(FieldElementX element) { | 
| -    PartialClassElement cls = element.enclosingClass; | 
| - | 
| -    invalidateScopesAffectedBy(element, cls); | 
| - | 
| -    _removedElements.add(element); | 
| - | 
| -    addRemovedFieldUpdate(compiler, element); | 
| -  } | 
| - | 
| -  /// Returns true if [element] has generic types (or if we cannot rule out | 
| -  /// that it has generic types). | 
| -  bool checkForGenericTypes(Element element) { | 
| -    if (element is TypeDeclarationElement) { | 
| -      if (!element.isResolved) { | 
| -        if (element is PartialClassElement) { | 
| -          ClassNode node = element.parseNode(compiler.parsing).asClassNode(); | 
| -          if (node == null) { | 
| -            cannotReuse( | 
| -                element, "Class body isn't a ClassNode on $element"); | 
| -            return true; | 
| -          } | 
| -          bool isGeneric = | 
| -              node.typeParameters != null && !node.typeParameters.isEmpty; | 
| -          if (isGeneric) { | 
| -            // TODO(ahe): Support generic types. | 
| -            cannotReuse( | 
| -                element, | 
| -                "Type variables not supported: '${node.typeParameters}'"); | 
| -            return true; | 
| -          } | 
| -        } else { | 
| -          cannotReuse( | 
| -              element, "Can't check for generic types on $element"); | 
| -          return true; | 
| -        } | 
| -      } else if (!element.thisType.isRaw) { | 
| -        cannotReuse( | 
| -            element, "Generic types not supported: '${element.thisType}'"); | 
| -        return true; | 
| -      } | 
| -    } | 
| -    return false; | 
| -  } | 
| - | 
| -  void invalidateScopesAffectedBy( | 
| -      ElementX element, | 
| -      /* ScopeContainerElement */ container) { | 
| -    if (checkForGenericTypes(element)) return; | 
| -    for (ScopeContainerElement scope in scopesAffectedBy(element, container)) { | 
| -      scanSites(scope, (Element member, DeclarationSite site) { | 
| -        // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. | 
| -        Set<String> names = qualifiedNamesIn(site); | 
| -        if (canNamesResolveStaticallyTo(names, element, container)) { | 
| -          if (checkForGenericTypes(member)) return; | 
| -          if (member is TypeDeclarationElement) { | 
| -            if (!member.isResolved) { | 
| -              // TODO(ahe): This is a bug in dart2js' forgetElement which | 
| -              // attempts to check if member is a generic type. | 
| -              cannotReuse(member, "Not resolved"); | 
| -              return; | 
| -            } | 
| -          } | 
| -          _elementsToInvalidate.add(member); | 
| -        } | 
| -      }); | 
| -    } | 
| -  } | 
| - | 
| -  void replaceFunctionInBackend( | 
| -      ElementX element, | 
| -      /* ScopeContainerElement */ container) { | 
| -    List<Element> elements = <Element>[]; | 
| -    if (checkForGenericTypes(element)) return; | 
| -    for (ScopeContainerElement scope in scopesAffectedBy(element, container)) { | 
| -      scanSites(scope, (Element member, DeclarationSite site) { | 
| -        // TODO(ahe): Cache qualifiedNamesIn to avoid quadratic behavior. | 
| -        Set<String> names = qualifiedNamesIn(site); | 
| -        if (canNamesResolveStaticallyTo(names, element, container)) { | 
| -          if (checkForGenericTypes(member)) return; | 
| -          if (member is TypeDeclarationElement) { | 
| -            if (!member.isResolved) { | 
| -              // TODO(ahe): This is a bug in dart2js' forgetElement which | 
| -              // attempts to check if member is a generic type. | 
| -              cannotReuse(member, "Not resolved"); | 
| -              return; | 
| -            } | 
| -          } | 
| -          elements.add(member); | 
| -        } | 
| -      }); | 
| -    } | 
| -    backend.replaceFunctionUsageElement(element, elements); | 
| -  } | 
| - | 
| -  /// Invoke [f] on each [DeclarationSite] in [element]. If [element] is a | 
| -  /// [ScopeContainerElement], invoke f on all local members as well. | 
| -  void scanSites( | 
| -      Element element, | 
| -      void f(ElementX element, DeclarationSite site)) { | 
| -    DeclarationSite site = declarationSite(element); | 
| -    if (site != null) { | 
| -      f(element, site); | 
| -    } | 
| -    if (element is ScopeContainerElement) { | 
| -      element.forEachLocalMember((member) { scanSites(member, f); }); | 
| -    } | 
| -  } | 
| - | 
| -  /// Assume [element] is either removed from or added to [container], and | 
| -  /// return all [ScopeContainerElement] that can see this change. | 
| -  List<ScopeContainerElement> scopesAffectedBy( | 
| -      Element element, | 
| -      /* ScopeContainerElement */ container) { | 
| -    // TODO(ahe): Use library export graph to compute this. | 
| -    // TODO(ahe): Should return all user-defined libraries and packages. | 
| -    LibraryElement library = container.library; | 
| -    List<ScopeContainerElement> result = <ScopeContainerElement>[library]; | 
| - | 
| -    if (!container.isClass) return result; | 
| - | 
| -    ClassElement cls = container; | 
| - | 
| -    if (!cls.declaration.isResolved) { | 
| -      // TODO(ahe): This test fails otherwise: experimental/add_static_field. | 
| -      throw new IncrementalCompilationFailed( | 
| -          "Unresolved class ${cls.declaration}"); | 
| -    } | 
| -    var externalSubtypes = | 
| -        compiler.world.subclassesOf(cls).where((e) => e.library != library); | 
| - | 
| -    return result..addAll(externalSubtypes); | 
| -  } | 
| - | 
| -  /// Returns true if function [before] can be reused to reflect the changes in | 
| -  /// [after]. | 
| -  /// | 
| -  /// If [before] can be reused, an update (patch) is added to [updates]. | 
| -  bool canReuseFunction( | 
| -      Token diffToken, | 
| -      PartialFunctionElement before, | 
| -      PartialFunctionElement after) { | 
| -    FunctionExpression node = | 
| -        after.parseNode(compiler.parsing).asFunctionExpression(); | 
| -    if (node == null) { | 
| -      return cannotReuse(after, "Not a function expression: '$node'"); | 
| -    } | 
| -    Token last = after.endToken; | 
| -    if (node.body != null) { | 
| -      last = node.body.getBeginToken(); | 
| -    } | 
| -    if (before.isMalformed || | 
| -        compiler.elementHasCompileTimeError(before) || | 
| -        isTokenBetween(diffToken, after.beginToken, last)) { | 
| -      removeFunction(before); | 
| -      addFunction(after, before.enclosingElement); | 
| -      if (compiler.mainFunction == before) { | 
| -        return cannotReuse( | 
| -            after, | 
| -            "Unable to handle when signature of '${after.name}' changes"); | 
| -      } | 
| -      return allowSignatureChanged(before, after); | 
| -    } | 
| -    logVerbose('Simple modification of ${after} detected'); | 
| -    if (!before.isInstanceMember) { | 
| -      if (!allowNonInstanceMemberModified(after)) return false; | 
| -    } | 
| -    updates.add(new FunctionUpdate(compiler, before, after)); | 
| -    return true; | 
| -  } | 
| - | 
| -  bool canReuseClass( | 
| -      Token diffToken, | 
| -      PartialClassElement before, | 
| -      PartialClassElement after) { | 
| -    ClassNode node = after.parseNode(compiler.parsing).asClassNode(); | 
| -    if (node == null) { | 
| -      return cannotReuse(after, "Not a ClassNode: '$node'"); | 
| -    } | 
| -    NodeList body = node.body; | 
| -    if (body == null) { | 
| -      return cannotReuse(after, "Class has no body."); | 
| -    } | 
| -    if (isTokenBetween(diffToken, node.beginToken, body.beginToken.next)) { | 
| -      if (!allowClassHeaderModified(after)) return false; | 
| -      logVerbose('Class header modified in ${after}'); | 
| -      addClassUpdate(compiler, before, after); | 
| -      before.forEachLocalMember((ElementX member) { | 
| -        // TODO(ahe): Quadratic. | 
| -        invalidateScopesAffectedBy(member, before); | 
| -      }); | 
| -    } else { | 
| -      logVerbose('Simple modification of ${after} detected'); | 
| -    } | 
| -    return canReuseScopeContainerElement(before, after); | 
| -  } | 
| - | 
| -  /// Returns true if [token] is found between [first] (included) and [last] | 
| -  /// (excluded). | 
| -  bool isTokenBetween(Token token, Token first, Token last) { | 
| -    Token current = first; | 
| -    while (current != last && current.kind != EOF_TOKEN) { | 
| -      if (current == token) { | 
| -        return true; | 
| -      } | 
| -      current = current.next; | 
| -    } | 
| -    return false; | 
| -  } | 
| - | 
| -  bool unableToReuse( | 
| -      Token diffToken, | 
| -      PartialElement before, | 
| -      PartialElement after) { | 
| -    return cannotReuse( | 
| -        after, | 
| -        'Unhandled change:' | 
| -        ' ${before} (${before.runtimeType} -> ${after.runtimeType}).'); | 
| -  } | 
| - | 
| -  /// Apply the collected [updates]. Return a list of elements that needs to be | 
| -  /// recompiled after applying the updates. | 
| -  List<Element> applyUpdates() { | 
| -    for (Update update in updates) { | 
| -      update.captureState(); | 
| -    } | 
| -    if (_failedUpdates.isNotEmpty) { | 
| -      throw new IncrementalCompilationFailed(_failedUpdates.join('\n\n')); | 
| -    } | 
| -    for (ElementX element in _elementsToInvalidate) { | 
| -      compiler.forgetElement(element); | 
| -      element.reuseElement(); | 
| -      if (element.isFunction) { | 
| -        replaceFunctionInBackend(element, element.enclosingElement); | 
| -      } | 
| -    } | 
| -    List<Element> elementsToInvalidate = <Element>[]; | 
| -    for (ElementX element in _elementsToInvalidate) { | 
| -      if (!_removedElements.contains(element)) { | 
| -        elementsToInvalidate.add(element); | 
| -      } | 
| -    } | 
| -    for (Update update in updates) { | 
| -      Element element = update.apply(backend); | 
| -      if (!update.isRemoval) { | 
| -        elementsToInvalidate.add(element); | 
| -      } | 
| -      if (update is FunctionUpdate) { | 
| -        replaceFunctionInBackend(element, element.enclosingElement); | 
| -      } | 
| -    } | 
| -    return elementsToInvalidate; | 
| -  } | 
| - | 
| -  void addClassUpdate( | 
| -      Compiler compiler, | 
| -      PartialClassElement before, | 
| -      PartialClassElement after); | 
| - | 
| -  void addAddedFunctionUpdate( | 
| -      Compiler compiler, | 
| -      PartialFunctionElement element, | 
| -      /* ScopeContainerElement */ container); | 
| - | 
| -  void addRemovedFunctionUpdate( | 
| -      Compiler compiler, | 
| -      PartialFunctionElement element); | 
| - | 
| -  void addRemovedFieldUpdate( | 
| -      Compiler compiler, | 
| -      FieldElementX element); | 
| - | 
| -  void addRemovedClassUpdate( | 
| -      Compiler compiler, | 
| -      PartialClassElement element); | 
| - | 
| -  void addAddedFieldUpdate( | 
| -      Compiler compiler, | 
| -      FieldElementX element, | 
| -      /* ScopeContainerElement */ container); | 
| - | 
| -  void addAddedClassUpdate( | 
| -      Compiler compiler, | 
| -      PartialClassElement element, | 
| -      LibraryElementX library); | 
| - | 
| -  bool uriHasUpdate(Uri uri); | 
| - | 
| -  bool allowClassHeaderModified(PartialClassElement after); | 
| - | 
| -  bool allowSignatureChanged( | 
| -      PartialFunctionElement before, | 
| -      PartialFunctionElement after); | 
| - | 
| -  bool allowNonInstanceMemberModified(PartialFunctionElement after); | 
| - | 
| -  bool allowRemovedElement(PartialElement element); | 
| - | 
| -  bool allowAddedElement(PartialElement element); | 
| -} | 
| - | 
| -/// Represents an update (aka patch) of [before] to [after]. We use the word | 
| -/// "update" to avoid confusion with the compiler feature of "patch" methods. | 
| -abstract class Update { | 
| -  final Compiler compiler; | 
| - | 
| -  PartialElement get before; | 
| - | 
| -  PartialElement get after; | 
| - | 
| -  Update(this.compiler); | 
| - | 
| -  /// Applies the update to [before] and returns that element. | 
| -  Element apply(IncrementalBackend backend); | 
| - | 
| -  bool get isRemoval => false; | 
| - | 
| -  /// Called before any patches are applied to capture any state that is needed | 
| -  /// later. | 
| -  void captureState() { | 
| -  } | 
| -} | 
| - | 
| -/// Represents an update of a function element. | 
| -class FunctionUpdate extends Update with ReuseFunctionElement { | 
| -  final PartialFunctionElement before; | 
| - | 
| -  final PartialFunctionElement after; | 
| - | 
| -  FunctionUpdate(Compiler compiler, this.before, this.after) | 
| -      : super(compiler); | 
| - | 
| -  PartialFunctionElement apply(IncrementalBackend backend) { | 
| -    patchElement(); | 
| -    reuseElement(); | 
| -    return before; | 
| -  } | 
| - | 
| -  /// Destructively change the tokens in [before] to match those of [after]. | 
| -  void patchElement() { | 
| -    before.beginToken = after.beginToken; | 
| -    before.endToken = after.endToken; | 
| -    before.getOrSet = after.getOrSet; | 
| -  } | 
| -} | 
| - | 
| -abstract class ReuseFunctionElement { | 
| -  Compiler get compiler; | 
| - | 
| -  PartialFunctionElement get before; | 
| - | 
| -  /// Reset various caches and remove this element from the compiler's internal | 
| -  /// state. | 
| -  void reuseElement() { | 
| -    compiler.forgetElement(before); | 
| -    before.reuseElement(); | 
| -  } | 
| -} | 
| - | 
| -abstract class RemovalUpdate extends Update { | 
| -  ElementX get element; | 
| - | 
| -  RemovalUpdate(Compiler compiler) | 
| -      : super(compiler); | 
| - | 
| -  bool get isRemoval => true; | 
| - | 
| -  void removeFromEnclosing() { | 
| -    // TODO(ahe): Need to recompute duplicated elements logic again. Simplest | 
| -    // solution is probably to remove all elements from enclosing scope and add | 
| -    // them back. | 
| -    if (element.isTopLevel) { | 
| -      removeFromLibrary(element.library); | 
| -    } else { | 
| -      removeFromEnclosingClass(element.enclosingClass); | 
| -    } | 
| -  } | 
| - | 
| -  void removeFromEnclosingClass(PartialClassElement cls) { | 
| -    cls.localMembersCache = null; | 
| -    cls.localMembersReversed = cls.localMembersReversed.copyWithout(element); | 
| -    cls.localScope.contents.remove(element.name); | 
| -  } | 
| - | 
| -  void removeFromLibrary(LibraryElementX library) { | 
| -    library.localMembers = library.localMembers.copyWithout(element); | 
| -    library.localScope.contents.remove(element.name); | 
| -  } | 
| -} | 
| - | 
| -abstract class RemovedFunctionUpdate extends RemovalUpdate | 
| -    with ReuseFunctionElement { | 
| -  final PartialFunctionElement element; | 
| - | 
| -  bool wasStateCaptured = false; | 
| - | 
| -  RemovedFunctionUpdate(Compiler compiler, this.element) | 
| -      : super(compiler); | 
| - | 
| -  PartialFunctionElement get before => element; | 
| - | 
| -  PartialFunctionElement get after => null; | 
| - | 
| -  void captureState() { | 
| -    if (wasStateCaptured) throw "captureState was called twice."; | 
| -    wasStateCaptured = true; | 
| -  } | 
| - | 
| -  PartialFunctionElement apply(IncrementalBackend backend) { | 
| -    if (!wasStateCaptured) throw "captureState must be called before apply."; | 
| -    removeFromEnclosing(); | 
| -    backend.removeFunction(element); | 
| -    reuseElement(); | 
| -    return null; | 
| -  } | 
| -} | 
| - | 
| -abstract class RemovedClassUpdate extends RemovalUpdate { | 
| -  final PartialClassElement element; | 
| - | 
| -  bool wasStateCaptured = false; | 
| - | 
| -  RemovedClassUpdate(Compiler compiler, this.element) | 
| -      : super(compiler); | 
| - | 
| -  PartialClassElement get before => element; | 
| - | 
| -  PartialClassElement get after => null; | 
| - | 
| -  void captureState() { | 
| -    if (wasStateCaptured) throw "captureState was called twice."; | 
| -    wasStateCaptured = true; | 
| -  } | 
| - | 
| -  PartialClassElement apply(IncrementalBackend backend) { | 
| -    if (!wasStateCaptured) { | 
| -      throw new StateError("captureState must be called before apply."); | 
| -    } | 
| - | 
| -    removeFromEnclosing(); | 
| - | 
| -    element.forEachLocalMember((ElementX member) { | 
| -      compiler.forgetElement(member); | 
| -      member.reuseElement(); | 
| -    }); | 
| - | 
| -    compiler.forgetElement(element); | 
| -    element.reuseElement(); | 
| - | 
| -    return null; | 
| -  } | 
| -} | 
| - | 
| -abstract class RemovedFieldUpdate extends RemovalUpdate { | 
| -  final FieldElementX element; | 
| - | 
| -  bool wasStateCaptured = false; | 
| - | 
| -  RemovedFieldUpdate(Compiler compiler, this.element) | 
| -      : super(compiler); | 
| - | 
| -  PartialFieldList get before => element.declarationSite; | 
| - | 
| -  PartialFieldList get after => null; | 
| - | 
| -  void captureState() { | 
| -    if (wasStateCaptured) throw "captureState was called twice."; | 
| -    wasStateCaptured = true; | 
| -  } | 
| - | 
| -  FieldElementX apply(IncrementalBackend backend) { | 
| -    if (!wasStateCaptured) { | 
| -      throw new StateError("captureState must be called before apply."); | 
| -    } | 
| - | 
| -    removeFromEnclosing(); | 
| -    backend.removeField(element); | 
| - | 
| -    return element; | 
| -  } | 
| -} | 
| - | 
| -abstract class AddedFunctionUpdate extends Update { | 
| -  final PartialFunctionElement element; | 
| - | 
| -  final /* ScopeContainerElement */ container; | 
| - | 
| -  AddedFunctionUpdate(Compiler compiler, this.element, this.container) | 
| -      : super(compiler) { | 
| -    if (container == null) { | 
| -      throw "container is null"; | 
| -    } | 
| -  } | 
| - | 
| -  PartialFunctionElement get before => null; | 
| - | 
| -  PartialFunctionElement get after => element; | 
| - | 
| -  PartialFunctionElement apply(IncrementalBackend backend) { | 
| -    Element enclosing = container; | 
| -    if (enclosing.isLibrary) { | 
| -      // TODO(ahe): Reuse compilation unit of element instead? | 
| -      enclosing = enclosing.compilationUnit; | 
| -    } | 
| -    PartialFunctionElement copy = element.copyWithEnclosing(enclosing); | 
| -    container.addMember(copy, compiler.reporter); | 
| -    return copy; | 
| -  } | 
| -} | 
| - | 
| -abstract class AddedClassUpdate extends Update { | 
| -  final PartialClassElement element; | 
| - | 
| -  final LibraryElementX library; | 
| - | 
| -  AddedClassUpdate(Compiler compiler, this.element, this.library) | 
| -      : super(compiler); | 
| - | 
| -  PartialClassElement get before => null; | 
| - | 
| -  PartialClassElement get after => element; | 
| - | 
| -  PartialClassElement apply(IncrementalBackend backend) { | 
| -    // TODO(ahe): Reuse compilation unit of element instead? | 
| -    CompilationUnitElementX compilationUnit = library.compilationUnit; | 
| -    PartialClassElement copy = element.copyWithEnclosing(compilationUnit); | 
| -    compilationUnit.addMember(copy, compiler.reporter); | 
| -    return copy; | 
| -  } | 
| -} | 
| - | 
| -abstract class AddedFieldUpdate extends Update { | 
| -  final FieldElementX element; | 
| - | 
| -  final /* ScopeContainerElement */ container; | 
| - | 
| -  AddedFieldUpdate(Compiler compiler, this.element, this.container) | 
| -      : super(compiler); | 
| - | 
| -  PartialFieldList get before => null; | 
| - | 
| -  PartialFieldList get after => element.declarationSite; | 
| - | 
| -  FieldElementX apply(IncrementalBackend backend) { | 
| -    Element enclosing = container; | 
| -    if (enclosing.isLibrary) { | 
| -      // TODO(ahe): Reuse compilation unit of element instead? | 
| -      enclosing = enclosing.compilationUnit; | 
| -    } | 
| -    FieldElementX copy = element.copyWithEnclosing(enclosing); | 
| -    container.addMember(copy, compiler.reporter); | 
| -    return copy; | 
| -  } | 
| -} | 
| - | 
| -abstract class ClassUpdate extends Update { | 
| -  final PartialClassElement before; | 
| - | 
| -  final PartialClassElement after; | 
| - | 
| -  ClassUpdate(Compiler compiler, this.before, this.after) | 
| -      : super(compiler); | 
| - | 
| -  PartialClassElement apply(IncrementalBackend backend) { | 
| -    patchElement(); | 
| -    reuseElement(); | 
| -    return before; | 
| -  } | 
| - | 
| -  /// Destructively change the tokens in [before] to match those of [after]. | 
| -  void patchElement() { | 
| -    before.cachedNode = after.cachedNode; | 
| -    before.beginToken = after.beginToken; | 
| -    before.endToken = after.endToken; | 
| -  } | 
| - | 
| -  void reuseElement() { | 
| -    before.supertype = null; | 
| -    before.interfaces = null; | 
| -    before.supertypeLoadState = STATE_NOT_STARTED; | 
| -    before.resolutionState = STATE_NOT_STARTED; | 
| -    before.isProxy = false; | 
| -    before.hasIncompleteHierarchy = false; | 
| -    before.backendMembers = const Link<Element>(); | 
| -    before.allSupertypesAndSelf = null; | 
| -  } | 
| -} | 
| - | 
| -/// Returns all qualified names in [element] with less than four identifiers. A | 
| -/// qualified name is an identifier followed by a sequence of dots and | 
| -/// identifiers, for example, "x", and "x.y.z". But not "x.y.z.w" ("w" is the | 
| -/// fourth identifier). | 
| -/// | 
| -/// The longest possible name that can be resolved is three identifiers, for | 
| -/// example, "prefix.MyClass.staticMethod". Since four or more identifiers | 
| -/// cannot resolve to anything statically, they're not included in the returned | 
| -/// value of this method. | 
| -Set<String> qualifiedNamesIn(PartialElement element) { | 
| -  Token beginToken = element.beginToken; | 
| -  Token endToken = element.endToken; | 
| -  Token token = beginToken; | 
| -  if (element is PartialClassElement) { | 
| -    ClassNode node = element.cachedNode; | 
| -    if (node != null) { | 
| -      NodeList body = node.body; | 
| -      if (body != null) { | 
| -        endToken = body.beginToken; | 
| -      } | 
| -    } | 
| -  } | 
| -  Set<String> names = new Set<String>(); | 
| -  do { | 
| -    if (token.isIdentifier()) { | 
| -      String name = token.value; | 
| -      // [name] is a single "identifier". | 
| -      names.add(name); | 
| -      if (identical('.', token.next.stringValue) && | 
| -          token.next.next.isIdentifier()) { | 
| -        token = token.next.next; | 
| -        name += '.${token.value}'; | 
| -        // [name] is "idenfifier.idenfifier". | 
| -        names.add(name); | 
| - | 
| -        if (identical('.', token.next.stringValue) && | 
| -            token.next.next.isIdentifier()) { | 
| -          token = token.next.next; | 
| -          name += '.${token.value}'; | 
| -          // [name] is "idenfifier.idenfifier.idenfifier". | 
| -          names.add(name); | 
| - | 
| -          while (identical('.', token.next.stringValue) && | 
| -                 token.next.next.isIdentifier()) { | 
| -            // Skip remaining identifiers, they cannot statically resolve to | 
| -            // anything, and must be dynamic sends. | 
| -            token = token.next.next; | 
| -          } | 
| -        } | 
| -      } | 
| -    } | 
| -    token = token.next; | 
| -  } while (token.kind != EOF_TOKEN && token != endToken); | 
| -  return names; | 
| -} | 
| - | 
| -/// Returns true if one of the qualified names in names (as computed by | 
| -/// [qualifiedNamesIn]) could be a static reference to [element]. | 
| -bool canNamesResolveStaticallyTo( | 
| -    Set<String> names, | 
| -    Element element, | 
| -    /* ScopeContainerElement */ container) { | 
| -  if (names.contains(element.name)) return true; | 
| -  if (container != null && container.isClass) { | 
| -    // [names] contains C.m, where C is the name of [container], and m is the | 
| -    // name of [element]. | 
| -    if (names.contains("${container.name}.${element.name}")) return true; | 
| -  } | 
| -  // TODO(ahe): Check for prefixes as well. | 
| -  return false; | 
| -} | 
| - | 
| -DeclarationSite declarationSite(Element element) { | 
| -  return element is ElementX ? element.declarationSite : null; | 
| -} | 
|  |